5 const float URL_FH_CURL = -1;
6 const float URL_FH_STDOUT = -2;
15 .url_ready_func url_ready;
16 .entity url_ready_pass;
22 entity url_fromid[NUM_URL_ID];
23 int autocvar__urllib_nextslot;
25 float url_URI_Get_Callback(int id, float status, string data)
27 if (id < MIN_URL_ID) return 0;
29 if (id >= NUM_URL_ID) return 0;
33 if (e.url_rbuf >= 0 || e.url_wbuf >= 0)
35 LOG_INFOF("WARNING: handle %d (%s) has already received data?!?\n", id + NUM_URL_ID, e.url_url);
39 // whatever happens, we will remove the URL from the list of IDs
40 url_fromid[id] = NULL;
42 // if we get here, we MUST have both buffers cleared
43 if (e.url_rbuf != -1 || e.url_wbuf != -1 || e.url_fh != URL_FH_CURL) error("url_URI_Get_Callback: not a request waiting for data");
49 n = tokenizebyseparator(data, "\n");
50 e.url_rbuf = buf_create();
53 LOG_INFO("url_URI_Get_Callback: out of memory in buf_create\n");
54 e.url_ready(e, e.url_ready_pass, URL_READY_ERROR);
62 LOG_INFO("url_URI_Get_Callback: out of memory in buf_create\n");
63 e.url_ready(e, e.url_ready_pass, URL_READY_ERROR);
68 for (i = 0; i < n; ++i)
69 bufstr_set(e.url_rbuf, i, argv(i));
70 e.url_ready(e, e.url_ready_pass, URL_READY_CANREAD);
76 e.url_ready(e, e.url_ready_pass, -fabs(status));
83 void url_single_fopen(string url, int mode, url_ready_func rdy, entity pass)
87 if (strstrofs(url, "://", 0) >= 0)
93 // collect data to a stringbuffer for a POST request
94 // attempts to close will result in a reading handle
96 // create a writing end that does nothing yet
97 e = new_pure(url_single_fopen_file);
98 e.url_url = strzone(url);
99 e.url_fh = URL_FH_CURL;
100 e.url_wbuf = buf_create();
103 LOG_INFO("url_single_fopen: out of memory in buf_create\n");
104 rdy(e, pass, URL_READY_ERROR);
105 strunzone(e.url_url);
112 e.url_ready_pass = pass;
113 rdy(e, pass, URL_READY_CANWRITE);
119 // get slot for HTTP request
120 for (i = autocvar__urllib_nextslot; i < NUM_URL_ID; ++i)
121 if (url_fromid[i] == NULL) break;
124 for (i = 0; i < autocvar__urllib_nextslot; ++i)
125 if (url_fromid[i] == NULL) break;
126 if (i >= autocvar__urllib_nextslot)
128 LOG_INFO("url_single_fopen: too many concurrent requests\n");
129 rdy(NULL, pass, URL_READY_ERROR);
135 if (!crypto_uri_postbuf(url, i + MIN_URL_ID, string_null, string_null, -1, 0))
137 LOG_INFO("url_single_fopen: failure in crypto_uri_postbuf\n");
138 rdy(NULL, pass, URL_READY_ERROR);
142 // Make a dummy handle object (no buffers at
143 // all). Wait for data to come from the
144 // server, then call the callback
145 e = new_pure(url_single_fopen_file);
146 e.url_url = strzone(url);
147 e.url_fh = URL_FH_CURL;
151 e.url_ready_pass = pass;
155 // make sure this slot won't be reused quickly even on map change
156 cvar_set("_urllib_nextslot", ftos((i + 1) % NUM_URL_ID));
166 e = new_pure(url_single_fopen_stdout);
167 e.url_fh = URL_FH_STDOUT;
169 e.url_ready_pass = pass;
170 rdy(e, pass, URL_READY_CANWRITE);
173 LOG_INFO("url_single_fopen: cannot open '-' for reading\n");
174 rdy(NULL, pass, URL_READY_ERROR);
181 fh = fopen(url, mode);
184 rdy(NULL, pass, URL_READY_ERROR);
189 e = new_pure(url_single_fopen_file);
192 e.url_ready_pass = pass;
193 if (mode == FILE_READ) rdy(e, pass, URL_READY_CANREAD);
194 else rdy(e, pass, URL_READY_CANWRITE);
200 void url_fclose(entity e)
204 if (e.url_fh == URL_FH_CURL)
206 if (e.url_rbuf == -1 || e.url_wbuf != -1) // not(post GET/POST request)
207 if (e.url_rbuf != -1 || e.url_wbuf == -1) // not(pre POST request)
208 error("url_fclose: not closable in current state");
213 // we are closing the write end (HTTP POST request)
215 // get slot for HTTP request
216 for (i = autocvar__urllib_nextslot; i < NUM_URL_ID; ++i)
217 if (url_fromid[i] == NULL) break;
220 for (i = 0; i < autocvar__urllib_nextslot; ++i)
221 if (url_fromid[i] == NULL) break;
222 if (i >= autocvar__urllib_nextslot)
224 LOG_INFO("url_fclose: too many concurrent requests\n");
225 e.url_ready(e, e.url_ready_pass, URL_READY_ERROR);
227 strunzone(e.url_url);
234 if (!crypto_uri_postbuf(e.url_url, i + MIN_URL_ID, "text/plain", "", e.url_wbuf, 0))
236 LOG_INFO("url_fclose: failure in crypto_uri_postbuf\n");
237 e.url_ready(e, e.url_ready_pass, URL_READY_ERROR);
239 strunzone(e.url_url);
244 // delete write end. File handle is now in unusable
245 // state. Wait for data to come from the server, then
252 // make sure this slot won't be reused quickly even on map change
253 cvar_set("_urllib_nextslot", ftos((i + 1) % NUM_URL_ID));
257 // we have READ all data, just close
258 e.url_ready(e, e.url_ready_pass, URL_READY_CLOSED);
260 strunzone(e.url_url);
264 else if (e.url_fh == URL_FH_STDOUT)
266 e.url_ready(e, e.url_ready_pass, URL_READY_CLOSED); // closing creates no reading handle
273 e.url_ready(e, e.url_ready_pass, URL_READY_CLOSED); // closing creates no reading handle
278 // with \n (blame FRIK_FILE)
279 string url_fgets(entity e)
281 if (e.url_fh == URL_FH_CURL)
283 if (e.url_rbuf == -1) error("url_fgets: not readable in current state");
286 s = bufstr_get(e.url_rbuf, e.url_rbufpos);
290 else if (e.url_fh == URL_FH_STDOUT)
298 return fgets(e.url_fh);
302 // without \n (blame FRIK_FILE)
303 void url_fputs(entity e, string s)
305 if (e.url_fh == URL_FH_CURL)
307 if (e.url_wbuf == -1) error("url_fputs: not writable in current state");
309 bufstr_set(e.url_wbuf, e.url_wbufpos, s);
312 else if (e.url_fh == URL_FH_STDOUT)
324 // multi URL object, tries URLs separated by space in sequence
325 void url_multi_ready(entity fh, entity me, float status)
328 if (status == URL_READY_ERROR || status < 0)
330 if (status == -422) // Unprocessable Entity
332 LOG_INFO("uri_multi_ready: got HTTP error 422, data is in unusable format - not continuing\n");
333 me.url_ready(fh, me.url_ready_pass, status);
334 strunzone(me.url_url);
339 n = tokenize_console(me.url_url);
340 if (n <= me.url_attempt)
342 me.url_ready(fh, me.url_ready_pass, status);
343 strunzone(me.url_url);
347 url_single_fopen(argv(me.url_attempt), me.url_mode, url_multi_ready, me);
350 me.url_ready(fh, me.url_ready_pass, status);
352 void url_multi_fopen(string url, int mode, url_ready_func rdy, entity pass)
355 n = tokenize_console(url);
358 LOG_INFO("url_multi_fopen: need at least one URL\n");
359 rdy(NULL, pass, URL_READY_ERROR);
363 entity me = new_pure(url_multi);
364 me.url_url = strzone(url);
368 me.url_ready_pass = pass;
369 url_single_fopen(argv(0), mode, url_multi_ready, me);