4 #define URL_FH_STDOUT -2
13 .url_ready_func url_ready;
14 .entity url_ready_pass;
16 entity url_fromid[NUM_URL_ID];
17 float autocvar__urllib_nextslot;
19 float url_URI_Get_Callback(float id, float status, string data)
30 if(e.url_rbuf >= 0 || e.url_wbuf >= 0)
32 print(sprintf("WARNING: handle %d (%s) has already received data?!?\n", id + NUM_URL_ID, e.url_url));
36 // whatever happens, we will remove the URL from the list of IDs
37 url_fromid[id] = world;
39 // if we get here, we MUST have both buffers cleared
40 if(e.url_rbuf != -1 || e.url_wbuf != -1 || e.url_fh != URL_FH_CURL)
41 error("url_URI_Get_Callback: not a request waiting for data");
47 n = tokenizebyseparator(data, "\n");
48 e.url_rbuf = buf_create();
51 print("url_URI_Get_Callback: out of memory in buf_create\n");
52 e.url_ready(e, e.url_ready_pass, URL_READY_ERROR);
60 print("url_URI_Get_Callback: out of memory in buf_create\n");
61 e.url_ready(e, e.url_ready_pass, URL_READY_ERROR);
66 for(i = 0; i < n; ++i)
67 bufstr_set(e.url_rbuf, i, argv(i));
68 e.url_ready(e, e.url_ready_pass, URL_READY_CANREAD);
74 e.url_ready(e, e.url_ready_pass, -fabs(status));
81 void url_single_fopen(string url, float mode, url_ready_func rdy, entity pass)
85 if(strstrofs(url, "://", 0) >= 0)
91 // collect data to a stringbuffer for a POST request
92 // attempts to close will result in a reading handle
94 // create a writing end that does nothing yet
96 e.classname = "url_single_fopen_file";
97 e.url_url = strzone(url);
98 e.url_fh = URL_FH_CURL;
99 e.url_wbuf = buf_create();
102 print("url_single_fopen: out of memory in buf_create\n");
103 rdy(e, pass, URL_READY_ERROR);
104 strunzone(e.url_url);
111 e.url_ready_pass = pass;
112 rdy(e, pass, URL_READY_CANWRITE);
118 // get slot for HTTP request
119 for(i = autocvar__urllib_nextslot; i < NUM_URL_ID; ++i)
120 if(url_fromid[i] == world)
124 for(i = 0; i < autocvar__urllib_nextslot; ++i)
125 if(url_fromid[i] == world)
127 if(i >= autocvar__urllib_nextslot)
129 print("url_single_fopen: too many concurrent requests\n");
130 rdy(world, pass, URL_READY_ERROR);
136 if(!crypto_uri_postbuf(url, i + MIN_URL_ID, string_null, string_null, -1, 0))
138 print("url_single_fopen: failure in crypto_uri_postbuf\n");
139 rdy(world, pass, URL_READY_ERROR);
143 // Make a dummy handle object (no buffers at
144 // all). Wait for data to come from the
145 // server, then call the callback
147 e.classname = "url_single_fopen_file";
148 e.url_url = strzone(url);
149 e.url_fh = URL_FH_CURL;
153 e.url_ready_pass = pass;
157 // make sure this slot won't be reused quickly even on map change
158 cvar_set("_urllib_nextslot", ftos(mod(i + 1, NUM_URL_ID)));
169 e.classname = "url_single_fopen_stdout";
170 e.url_fh = URL_FH_STDOUT;
172 e.url_ready_pass = pass;
173 rdy(e, pass, URL_READY_CANWRITE);
176 print("url_single_fopen: cannot open '-' for reading\n");
177 rdy(world, pass, URL_READY_ERROR);
184 fh = fopen(url, mode);
187 rdy(world, pass, URL_READY_ERROR);
193 e.classname = "url_single_fopen_file";
196 e.url_ready_pass = pass;
197 if(mode == FILE_READ)
198 rdy(e, pass, URL_READY_CANREAD);
200 rdy(e, pass, URL_READY_CANWRITE);
206 void url_fclose(entity e)
210 if(e.url_fh == URL_FH_CURL)
212 if(e.url_rbuf == -1 || e.url_wbuf != -1) // not(post GET/POST request)
213 if(e.url_rbuf != -1 || e.url_wbuf == -1) // not(pre POST request)
214 error("url_fclose: not closable in current state");
219 // we are closing the write end (HTTP POST request)
221 // get slot for HTTP request
222 for(i = autocvar__urllib_nextslot; i < NUM_URL_ID; ++i)
223 if(url_fromid[i] == world)
227 for(i = 0; i < autocvar__urllib_nextslot; ++i)
228 if(url_fromid[i] == world)
230 if(i >= autocvar__urllib_nextslot)
232 print("url_fclose: too many concurrent requests\n");
233 e.url_ready(e,e.url_ready_pass, URL_READY_ERROR);
235 strunzone(e.url_url);
242 if(!crypto_uri_postbuf(e.url_url, i + MIN_URL_ID, "text/plain", "", e.url_wbuf, 0))
244 print("url_fclose: failure in crypto_uri_postbuf\n");
245 e.url_ready(e, e.url_ready_pass, URL_READY_ERROR);
247 strunzone(e.url_url);
252 // delete write end. File handle is now in unusable
253 // state. Wait for data to come from the server, then
260 // make sure this slot won't be reused quickly even on map change
261 cvar_set("_urllib_nextslot", ftos(mod(i + 1, NUM_URL_ID)));
265 // we have READ all data, just close
266 e.url_ready(e, e.url_ready_pass, URL_READY_CLOSED);
268 strunzone(e.url_url);
272 else if(e.url_fh == URL_FH_STDOUT)
274 e.url_ready(e, e.url_ready_pass, URL_READY_CLOSED); // closing creates no reading handle
281 e.url_ready(e, e.url_ready_pass, URL_READY_CLOSED); // closing creates no reading handle
286 // with \n (blame FRIK_FILE)
287 string url_fgets(entity e)
289 if(e.url_fh == URL_FH_CURL)
292 error("url_fgets: not readable in current state");
295 s = bufstr_get(e.url_rbuf, e.url_rbufpos);
299 else if(e.url_fh == URL_FH_STDOUT)
307 return fgets(e.url_fh);
311 // without \n (blame FRIK_FILE)
312 void url_fputs(entity e, string s)
314 if(e.url_fh == URL_FH_CURL)
317 error("url_fputs: not writable in current state");
319 bufstr_set(e.url_wbuf, e.url_wbufpos, s);
322 else if(e.url_fh == URL_FH_STDOUT)
334 // multi URL object, tries URLs separated by space in sequence
335 void url_multi_ready(entity fh, entity me, float status)
338 if(status == URL_READY_ERROR)
341 n = tokenize_console(me.url_url);
344 me.url_ready(fh, me.url_ready_pass, status);
345 strunzone(me.url_url);
349 url_single_fopen(argv(me.cnt), me.lip, url_multi_ready, me);
352 me.url_ready(fh, me.url_ready_pass, status);
354 void url_multi_fopen(string url, float mode, url_ready_func rdy, entity pass)
357 n = tokenize_console(url);
360 print("url_multi_fopen: need at least one URL\n");
361 rdy(world, pass, URL_READY_ERROR);
367 me.classname = "url_multi";
368 me.url_url = strzone(url);
372 me.url_ready_pass = pass;
373 url_single_fopen(argv(0), mode, url_multi_ready, me);