2 #include "../dpdefs/csprogsdefs.qh"
3 #include "constants.qh"
8 #include "../dpdefs/progsdefs.qh"
9 #include "../dpdefs/dpextensions.qh"
10 #include "../server/sys-post.qh"
11 #include "constants.qh"
18 const float URL_FH_CURL = -1;
19 const float URL_FH_STDOUT = -2;
28 .url_ready_func url_ready;
29 .entity url_ready_pass;
35 entity url_fromid[NUM_URL_ID];
36 float autocvar__urllib_nextslot;
38 float url_URI_Get_Callback(float id, float status, string data)
49 if(e.url_rbuf >= 0 || e.url_wbuf >= 0)
51 printf("WARNING: handle %d (%s) has already received data?!?\n", id + NUM_URL_ID, e.url_url);
55 // whatever happens, we will remove the URL from the list of IDs
56 url_fromid[id] = world;
58 // if we get here, we MUST have both buffers cleared
59 if(e.url_rbuf != -1 || e.url_wbuf != -1 || e.url_fh != URL_FH_CURL)
60 error("url_URI_Get_Callback: not a request waiting for data");
66 n = tokenizebyseparator(data, "\n");
67 e.url_rbuf = buf_create();
70 print("url_URI_Get_Callback: out of memory in buf_create\n");
71 e.url_ready(e, e.url_ready_pass, URL_READY_ERROR);
79 print("url_URI_Get_Callback: out of memory in buf_create\n");
80 e.url_ready(e, e.url_ready_pass, URL_READY_ERROR);
85 for(i = 0; i < n; ++i)
86 bufstr_set(e.url_rbuf, i, argv(i));
87 e.url_ready(e, e.url_ready_pass, URL_READY_CANREAD);
93 e.url_ready(e, e.url_ready_pass, -fabs(status));
100 void url_single_fopen(string url, float mode, url_ready_func rdy, entity pass)
104 if(strstrofs(url, "://", 0) >= 0)
110 // collect data to a stringbuffer for a POST request
111 // attempts to close will result in a reading handle
113 // create a writing end that does nothing yet
115 e.classname = "url_single_fopen_file";
116 e.url_url = strzone(url);
117 e.url_fh = URL_FH_CURL;
118 e.url_wbuf = buf_create();
121 print("url_single_fopen: out of memory in buf_create\n");
122 rdy(e, pass, URL_READY_ERROR);
123 strunzone(e.url_url);
130 e.url_ready_pass = pass;
131 rdy(e, pass, URL_READY_CANWRITE);
137 // get slot for HTTP request
138 for(i = autocvar__urllib_nextslot; i < NUM_URL_ID; ++i)
139 if(url_fromid[i] == world)
143 for(i = 0; i < autocvar__urllib_nextslot; ++i)
144 if(url_fromid[i] == world)
146 if(i >= autocvar__urllib_nextslot)
148 print("url_single_fopen: too many concurrent requests\n");
149 rdy(world, pass, URL_READY_ERROR);
155 if(!crypto_uri_postbuf(url, i + MIN_URL_ID, string_null, string_null, -1, 0))
157 print("url_single_fopen: failure in crypto_uri_postbuf\n");
158 rdy(world, pass, URL_READY_ERROR);
162 // Make a dummy handle object (no buffers at
163 // all). Wait for data to come from the
164 // server, then call the callback
166 e.classname = "url_single_fopen_file";
167 e.url_url = strzone(url);
168 e.url_fh = URL_FH_CURL;
172 e.url_ready_pass = pass;
176 // make sure this slot won't be reused quickly even on map change
177 cvar_set("_urllib_nextslot", ftos((i + 1) % NUM_URL_ID));
188 e.classname = "url_single_fopen_stdout";
189 e.url_fh = URL_FH_STDOUT;
191 e.url_ready_pass = pass;
192 rdy(e, pass, URL_READY_CANWRITE);
195 print("url_single_fopen: cannot open '-' for reading\n");
196 rdy(world, pass, URL_READY_ERROR);
203 fh = fopen(url, mode);
206 rdy(world, pass, URL_READY_ERROR);
212 e.classname = "url_single_fopen_file";
215 e.url_ready_pass = pass;
216 if(mode == FILE_READ)
217 rdy(e, pass, URL_READY_CANREAD);
219 rdy(e, pass, URL_READY_CANWRITE);
225 void url_fclose(entity e)
229 if(e.url_fh == URL_FH_CURL)
231 if(e.url_rbuf == -1 || e.url_wbuf != -1) // not(post GET/POST request)
232 if(e.url_rbuf != -1 || e.url_wbuf == -1) // not(pre POST request)
233 error("url_fclose: not closable in current state");
238 // we are closing the write end (HTTP POST request)
240 // get slot for HTTP request
241 for(i = autocvar__urllib_nextslot; i < NUM_URL_ID; ++i)
242 if(url_fromid[i] == world)
246 for(i = 0; i < autocvar__urllib_nextslot; ++i)
247 if(url_fromid[i] == world)
249 if(i >= autocvar__urllib_nextslot)
251 print("url_fclose: too many concurrent requests\n");
252 e.url_ready(e,e.url_ready_pass, URL_READY_ERROR);
254 strunzone(e.url_url);
261 if(!crypto_uri_postbuf(e.url_url, i + MIN_URL_ID, "text/plain", "", e.url_wbuf, 0))
263 print("url_fclose: failure in crypto_uri_postbuf\n");
264 e.url_ready(e, e.url_ready_pass, URL_READY_ERROR);
266 strunzone(e.url_url);
271 // delete write end. File handle is now in unusable
272 // state. Wait for data to come from the server, then
279 // make sure this slot won't be reused quickly even on map change
280 cvar_set("_urllib_nextslot", ftos((i + 1) % NUM_URL_ID));
284 // we have READ all data, just close
285 e.url_ready(e, e.url_ready_pass, URL_READY_CLOSED);
287 strunzone(e.url_url);
291 else if(e.url_fh == URL_FH_STDOUT)
293 e.url_ready(e, e.url_ready_pass, URL_READY_CLOSED); // closing creates no reading handle
300 e.url_ready(e, e.url_ready_pass, URL_READY_CLOSED); // closing creates no reading handle
305 // with \n (blame FRIK_FILE)
306 string url_fgets(entity e)
308 if(e.url_fh == URL_FH_CURL)
311 error("url_fgets: not readable in current state");
314 s = bufstr_get(e.url_rbuf, e.url_rbufpos);
318 else if(e.url_fh == URL_FH_STDOUT)
326 return fgets(e.url_fh);
330 // without \n (blame FRIK_FILE)
331 void url_fputs(entity e, string s)
333 if(e.url_fh == URL_FH_CURL)
336 error("url_fputs: not writable in current state");
338 bufstr_set(e.url_wbuf, e.url_wbufpos, s);
341 else if(e.url_fh == URL_FH_STDOUT)
353 // multi URL object, tries URLs separated by space in sequence
354 void url_multi_ready(entity fh, entity me, float status)
357 if(status == URL_READY_ERROR || status < 0)
359 if(status == -422) // Unprocessable Entity
361 print("uri_multi_ready: got HTTP error 422, data is in unusable format - not continuing\n");
362 me.url_ready(fh, me.url_ready_pass, status);
363 strunzone(me.url_url);
368 n = tokenize_console(me.url_url);
369 if(n <= me.url_attempt)
371 me.url_ready(fh, me.url_ready_pass, status);
372 strunzone(me.url_url);
376 url_single_fopen(argv(me.url_attempt), me.url_mode, url_multi_ready, me);
379 me.url_ready(fh, me.url_ready_pass, status);
381 void url_multi_fopen(string url, float mode, url_ready_func rdy, entity pass)
384 n = tokenize_console(url);
387 print("url_multi_fopen: need at least one URL\n");
388 rdy(world, pass, URL_READY_ERROR);
394 me.classname = "url_multi";
395 me.url_url = strzone(url);
399 me.url_ready_pass = pass;
400 url_single_fopen(argv(0), mode, url_multi_ready, me);