2 #include "../dpdefs/csprogsdefs.qh"
3 #include "constants.qh"
8 #include "../dpdefs/progsdefs.qh"
9 #include "../dpdefs/dpextensions.qh"
10 #include "constants.qh"
17 const float URL_FH_CURL = -1;
18 const float URL_FH_STDOUT = -2;
27 .url_ready_func url_ready;
28 .entity url_ready_pass;
34 entity url_fromid[NUM_URL_ID];
35 int autocvar__urllib_nextslot;
37 float url_URI_Get_Callback(int id, float status, string data)
48 if(e.url_rbuf >= 0 || e.url_wbuf >= 0)
50 printf("WARNING: handle %d (%s) has already received data?!?\n", id + NUM_URL_ID, e.url_url);
54 // whatever happens, we will remove the URL from the list of IDs
55 url_fromid[id] = world;
57 // if we get here, we MUST have both buffers cleared
58 if(e.url_rbuf != -1 || e.url_wbuf != -1 || e.url_fh != URL_FH_CURL)
59 error("url_URI_Get_Callback: not a request waiting for data");
65 n = tokenizebyseparator(data, "\n");
66 e.url_rbuf = buf_create();
69 print("url_URI_Get_Callback: out of memory in buf_create\n");
70 e.url_ready(e, e.url_ready_pass, URL_READY_ERROR);
78 print("url_URI_Get_Callback: out of memory in buf_create\n");
79 e.url_ready(e, e.url_ready_pass, URL_READY_ERROR);
84 for(i = 0; i < n; ++i)
85 bufstr_set(e.url_rbuf, i, argv(i));
86 e.url_ready(e, e.url_ready_pass, URL_READY_CANREAD);
92 e.url_ready(e, e.url_ready_pass, -fabs(status));
99 void url_single_fopen(string url, int mode, url_ready_func rdy, entity pass)
103 if(strstrofs(url, "://", 0) >= 0)
109 // collect data to a stringbuffer for a POST request
110 // attempts to close will result in a reading handle
112 // create a writing end that does nothing yet
114 e.classname = "url_single_fopen_file";
115 e.url_url = strzone(url);
116 e.url_fh = URL_FH_CURL;
117 e.url_wbuf = buf_create();
120 print("url_single_fopen: out of memory in buf_create\n");
121 rdy(e, pass, URL_READY_ERROR);
122 strunzone(e.url_url);
129 e.url_ready_pass = pass;
130 rdy(e, pass, URL_READY_CANWRITE);
136 // get slot for HTTP request
137 for(i = autocvar__urllib_nextslot; i < NUM_URL_ID; ++i)
138 if(url_fromid[i] == world)
142 for(i = 0; i < autocvar__urllib_nextslot; ++i)
143 if(url_fromid[i] == world)
145 if(i >= autocvar__urllib_nextslot)
147 print("url_single_fopen: too many concurrent requests\n");
148 rdy(world, pass, URL_READY_ERROR);
154 if(!crypto_uri_postbuf(url, i + MIN_URL_ID, string_null, string_null, -1, 0))
156 print("url_single_fopen: failure in crypto_uri_postbuf\n");
157 rdy(world, pass, URL_READY_ERROR);
161 // Make a dummy handle object (no buffers at
162 // all). Wait for data to come from the
163 // server, then call the callback
165 e.classname = "url_single_fopen_file";
166 e.url_url = strzone(url);
167 e.url_fh = URL_FH_CURL;
171 e.url_ready_pass = pass;
175 // make sure this slot won't be reused quickly even on map change
176 cvar_set("_urllib_nextslot", ftos((i + 1) % NUM_URL_ID));
187 e.classname = "url_single_fopen_stdout";
188 e.url_fh = URL_FH_STDOUT;
190 e.url_ready_pass = pass;
191 rdy(e, pass, URL_READY_CANWRITE);
194 print("url_single_fopen: cannot open '-' for reading\n");
195 rdy(world, pass, URL_READY_ERROR);
202 fh = fopen(url, mode);
205 rdy(world, pass, URL_READY_ERROR);
211 e.classname = "url_single_fopen_file";
214 e.url_ready_pass = pass;
215 if(mode == FILE_READ)
216 rdy(e, pass, URL_READY_CANREAD);
218 rdy(e, pass, URL_READY_CANWRITE);
224 void url_fclose(entity e)
228 if(e.url_fh == URL_FH_CURL)
230 if(e.url_rbuf == -1 || e.url_wbuf != -1) // not(post GET/POST request)
231 if(e.url_rbuf != -1 || e.url_wbuf == -1) // not(pre POST request)
232 error("url_fclose: not closable in current state");
237 // we are closing the write end (HTTP POST request)
239 // get slot for HTTP request
240 for(i = autocvar__urllib_nextslot; i < NUM_URL_ID; ++i)
241 if(url_fromid[i] == world)
245 for(i = 0; i < autocvar__urllib_nextslot; ++i)
246 if(url_fromid[i] == world)
248 if(i >= autocvar__urllib_nextslot)
250 print("url_fclose: too many concurrent requests\n");
251 e.url_ready(e,e.url_ready_pass, URL_READY_ERROR);
253 strunzone(e.url_url);
260 if(!crypto_uri_postbuf(e.url_url, i + MIN_URL_ID, "text/plain", "", e.url_wbuf, 0))
262 print("url_fclose: failure in crypto_uri_postbuf\n");
263 e.url_ready(e, e.url_ready_pass, URL_READY_ERROR);
265 strunzone(e.url_url);
270 // delete write end. File handle is now in unusable
271 // state. Wait for data to come from the server, then
278 // make sure this slot won't be reused quickly even on map change
279 cvar_set("_urllib_nextslot", ftos((i + 1) % NUM_URL_ID));
283 // we have READ all data, just close
284 e.url_ready(e, e.url_ready_pass, URL_READY_CLOSED);
286 strunzone(e.url_url);
290 else if(e.url_fh == URL_FH_STDOUT)
292 e.url_ready(e, e.url_ready_pass, URL_READY_CLOSED); // closing creates no reading handle
299 e.url_ready(e, e.url_ready_pass, URL_READY_CLOSED); // closing creates no reading handle
304 // with \n (blame FRIK_FILE)
305 string url_fgets(entity e)
307 if(e.url_fh == URL_FH_CURL)
310 error("url_fgets: not readable in current state");
313 s = bufstr_get(e.url_rbuf, e.url_rbufpos);
317 else if(e.url_fh == URL_FH_STDOUT)
325 return fgets(e.url_fh);
329 // without \n (blame FRIK_FILE)
330 void url_fputs(entity e, string s)
332 if(e.url_fh == URL_FH_CURL)
335 error("url_fputs: not writable in current state");
337 bufstr_set(e.url_wbuf, e.url_wbufpos, s);
340 else if(e.url_fh == URL_FH_STDOUT)
352 // multi URL object, tries URLs separated by space in sequence
353 void url_multi_ready(entity fh, entity me, float status)
356 if(status == URL_READY_ERROR || status < 0)
358 if(status == -422) // Unprocessable Entity
360 print("uri_multi_ready: got HTTP error 422, data is in unusable format - not continuing\n");
361 me.url_ready(fh, me.url_ready_pass, status);
362 strunzone(me.url_url);
367 n = tokenize_console(me.url_url);
368 if(n <= me.url_attempt)
370 me.url_ready(fh, me.url_ready_pass, status);
371 strunzone(me.url_url);
375 url_single_fopen(argv(me.url_attempt), me.url_mode, url_multi_ready, me);
378 me.url_ready(fh, me.url_ready_pass, status);
380 void url_multi_fopen(string url, int mode, url_ready_func rdy, entity pass)
383 n = tokenize_console(url);
386 print("url_multi_fopen: need at least one URL\n");
387 rdy(world, pass, URL_READY_ERROR);
393 me.classname = "url_multi";
394 me.url_url = strzone(url);
398 me.url_ready_pass = pass;
399 url_single_fopen(argv(0), mode, url_multi_ready, me);