Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
urllib.qc
Go to the documentation of this file.
1#include "urllib.qh"
2
3// files
4.float url_fh;
5const float URL_FH_CURL = -1;
6const float URL_FH_STDOUT = -2;
7
8// URLs
9.string url_url;
11.string url_verb;
12.float url_wbuf;
14.float url_rbuf;
16.float url_id;
17.url_ready_func url_ready;
19
20// for multi handles
23
26
28float url_URI_Get_Callback(int id, float status, string data)
29{
30 if (id < MIN_URL_ID) return 0;
31 id -= MIN_URL_ID;
32 if (id >= NUM_URL_ID) return 0;
33 entity e;
34 e = url_fromid[id];
35 if (!e) return 0;
36 if (e.url_rbuf >= 0 || e.url_wbuf >= 0)
37 {
38 LOG_INFOF("WARNING: handle %d (%s) has already received data?!?", id + NUM_URL_ID, e.url_url);
39 return 0;
40 }
41
42 // whatever happens, we will remove the URL from the list of IDs
43 url_fromid[id] = NULL;
44
45 // if we get here, we MUST have both buffers cleared
46 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");
47
48 if (status == 0)
49 {
50 // WE GOT DATA!
51 float n, i;
52 n = tokenizebyseparator(data, "\n");
53 e.url_rbuf = buf_create();
54 if (e.url_rbuf < 0)
55 {
56 LOG_INFO("url_URI_Get_Callback: out of memory in buf_create");
57 e.url_ready(e, e.url_ready_pass, URL_READY_ERROR);
58 strfree(e.url_url);
59 delete(e);
60 return 1;
61 }
62 e.url_rbufpos = 0;
63 if (e.url_rbuf < 0)
64 {
65 LOG_INFO("url_URI_Get_Callback: out of memory in buf_create");
66 e.url_ready(e, e.url_ready_pass, URL_READY_ERROR);
67 strfree(e.url_url);
68 delete(e);
69 return 1;
70 }
71 for (i = 0; i < n; ++i)
72 bufstr_set(e.url_rbuf, i, argv(i));
73 e.url_ready(e, e.url_ready_pass, URL_READY_CANREAD);
74 return 1;
75 }
76 else
77 {
78 // an ERROR
79 e.url_ready(e, e.url_ready_pass, -fabs(status));
80 strfree(e.url_url);
81 delete(e);
82 return 1;
83 }
84}
85
87void url_single_fopen(string url, int mode, url_ready_func rdy, entity pass)
88{
89 entity e;
90 int i;
91 if (strstrofs(url, "://", 0) >= 0)
92 {
93 switch (mode)
94 {
95 case FILE_WRITE:
96 case FILE_APPEND:
97 // collect data to a stringbuffer for a POST request
98 // attempts to close will result in a reading handle
99
100 // create a writing end that does nothing yet
101 e = new_pure(url_single_fopen_file);
102 e.url_url = strzone(url);
103 e.url_content_type = "text/plain";
104 e.url_verb = "";
105 e.url_fh = URL_FH_CURL;
106 e.url_wbuf = buf_create();
107 if (e.url_wbuf < 0)
108 {
109 LOG_INFO("url_single_fopen: out of memory in buf_create");
110 rdy(e, pass, URL_READY_ERROR);
111 strfree(e.url_url);
112 delete(e);
113 return;
114 }
115 e.url_wbufpos = 0;
116 e.url_rbuf = -1;
117 e.url_ready = rdy;
118 e.url_ready_pass = pass;
119 rdy(e, pass, URL_READY_CANWRITE);
120 break;
121
122 case FILE_READ:
123 // read data only
124
125 // get slot for HTTP request
126 for (i = autocvar__urllib_nextslot; i < NUM_URL_ID; ++i)
127 if (url_fromid[i] == NULL) break;
128 if (i >= NUM_URL_ID)
129 {
130 for (i = 0; i < autocvar__urllib_nextslot; ++i)
131 if (url_fromid[i] == NULL) break;
133 {
134 LOG_INFO("url_single_fopen: too many concurrent requests");
136 return;
137 }
138 }
139
140 // GET the data
141 if (!crypto_uri_postbuf(url, i + MIN_URL_ID, string_null, string_null, -1, 0))
142 {
143 LOG_INFO("url_single_fopen: failure in crypto_uri_postbuf");
145 return;
146 }
147
148 // Make a dummy handle object (no buffers at
149 // all). Wait for data to come from the
150 // server, then call the callback
151 e = new_pure(url_single_fopen_file);
152 e.url_url = strzone(url);
153 e.url_fh = URL_FH_CURL;
154 e.url_rbuf = -1;
155 e.url_wbuf = -1;
156 e.url_ready = rdy;
157 e.url_ready_pass = pass;
158 e.url_id = i;
159 url_fromid[i] = e;
160
161 // make sure this slot won't be reused quickly even on map change
162 cvar_set("_urllib_nextslot", ftos((i + 1) % NUM_URL_ID));
163 break;
164 }
165 }
166 else if (url == "-")
167 {
168 switch (mode)
169 {
170 case FILE_WRITE:
171 case FILE_APPEND:
172 e = new_pure(url_single_fopen_stdout);
173 e.url_fh = URL_FH_STDOUT;
174 e.url_ready = rdy;
175 e.url_ready_pass = pass;
176 rdy(e, pass, URL_READY_CANWRITE);
177 break;
178 case FILE_READ:
179 LOG_INFO("url_single_fopen: cannot open '-' for reading");
181 break;
182 }
183 }
184 else
185 {
186 float fh;
187 fh = fopen(url, mode);
188 if (fh < 0)
189 {
191 return;
192 }
193 else
194 {
195 e = new_pure(url_single_fopen_file);
196 e.url_fh = fh;
197 e.url_ready = rdy;
198 e.url_ready_pass = pass;
199 if (mode == FILE_READ) rdy(e, pass, URL_READY_CANREAD);
200 else rdy(e, pass, URL_READY_CANWRITE);
201 }
202 }
203}
204
205// close a file
208{
209 int i;
210
211 if (e.url_fh == URL_FH_CURL)
212 {
213 if (e.url_rbuf == -1 || e.url_wbuf != -1) // not(post GET/POST request)
214 if (e.url_rbuf != -1 || e.url_wbuf == -1) // not(pre POST request)
215 error("url_fclose: not closable in current state");
216
217 // closing an URL!
218 if (e.url_wbuf >= 0)
219 {
220 // we are closing the write end (HTTP POST request)
221
222 // get slot for HTTP request
223 for (i = autocvar__urllib_nextslot; i < NUM_URL_ID; ++i)
224 if (url_fromid[i] == NULL) break;
225 if (i >= NUM_URL_ID)
226 {
227 for (i = 0; i < autocvar__urllib_nextslot; ++i)
228 if (url_fromid[i] == NULL) break;
230 {
231 LOG_INFO("url_fclose: too many concurrent requests");
232 e.url_ready(e, e.url_ready_pass, URL_READY_ERROR);
233 buf_del(e.url_wbuf);
234 strfree(e.url_url);
235 delete(e);
236 return;
237 }
238 }
239
240 // POST the data
241 if (!crypto_uri_postbuf(e.url_url, i + MIN_URL_ID, e.url_content_type, e.url_verb, e.url_wbuf, 0))
242 {
243 LOG_INFO("url_fclose: failure in crypto_uri_postbuf");
244 e.url_ready(e, e.url_ready_pass, URL_READY_ERROR);
245 buf_del(e.url_wbuf);
246 strfree(e.url_url);
247 delete(e);
248 return;
249 }
250
251 // delete write end. File handle is now in unusable
252 // state. Wait for data to come from the server, then
253 // call the callback
254 buf_del(e.url_wbuf);
255 e.url_wbuf = -1;
256 e.url_id = i;
257 url_fromid[i] = e;
258
259 // make sure this slot won't be reused quickly even on map change
260 cvar_set("_urllib_nextslot", ftos((i + 1) % NUM_URL_ID));
261 }
262 else
263 {
264 // we have READ all data, just close
265 e.url_ready(e, e.url_ready_pass, URL_READY_CLOSED);
266 buf_del(e.url_rbuf);
267 strfree(e.url_url);
268 delete(e);
269 }
270 }
271 else if (e.url_fh == URL_FH_STDOUT)
272 {
273 e.url_ready(e, e.url_ready_pass, URL_READY_CLOSED); // closing creates no reading handle
274 delete(e);
275 }
276 else
277 {
278 // file
279 fclose(e.url_fh);
280 e.url_ready(e, e.url_ready_pass, URL_READY_CLOSED); // closing creates no reading handle
281 delete(e);
282 }
283}
284
285// with \n (blame FRIK_FILE)
288{
289 if (e.url_fh == URL_FH_CURL)
290 {
291 if (e.url_rbuf == -1) error("url_fgets: not readable in current state");
292 // curl
293 string s;
294 s = bufstr_get(e.url_rbuf, e.url_rbufpos);
295 e.url_rbufpos += 1;
296 return s;
297 }
298 else if (e.url_fh == URL_FH_STDOUT)
299 {
300 // stdout
301 return string_null;
302 }
303 else
304 {
305 // file
306 return fgets(e.url_fh);
307 }
308}
309
310// without \n (blame FRIK_FILE)
312void url_fputs(entity e, string s)
313{
314 if (e.url_fh == URL_FH_CURL)
315 {
316 if (e.url_wbuf == -1) error("url_fputs: not writable in current state");
317 // curl
318 bufstr_set(e.url_wbuf, e.url_wbufpos, s);
319 e.url_wbufpos += 1;
320 }
321 else if (e.url_fh == URL_FH_STDOUT)
322 {
323 // stdout
324 print(s);
325 }
326 else
327 {
328 // file
329 fputs(e.url_fh, s);
330 }
331}
332
333// multi URL object, tries URLs separated by space in sequence
335void url_multi_ready(entity fh, entity me, float status)
336{
337 float n;
338 if (status == URL_READY_ERROR || status < 0)
339 {
340 if (status == -422) // Unprocessable Entity
341 {
342 LOG_INFO("uri_multi_ready: got HTTP error 422, data is in unusable format - not continuing");
343 me.url_ready(fh, me.url_ready_pass, status);
344 strfree(me.url_url);
345 delete(me);
346 return;
347 }
348 me.url_attempt += 1;
349 n = tokenize_console(me.url_url);
350 if (n <= me.url_attempt)
351 {
352 me.url_ready(fh, me.url_ready_pass, status);
353 strfree(me.url_url);
354 delete(me);
355 return;
356 }
357 url_single_fopen(argv(me.url_attempt), me.url_mode, url_multi_ready, me);
358 return;
359 }
360 me.url_ready(fh, me.url_ready_pass, status);
361}
362
364void url_multi_fopen(string url, int mode, url_ready_func rdy, entity pass)
365{
366 float n;
367 n = tokenize_console(url);
368 if (n <= 0)
369 {
370 LOG_INFO("url_multi_fopen: need at least one URL");
372 return;
373 }
374
375 entity me = new_pure(url_multi);
376 me.url_url = strzone(url);
377 me.url_attempt = 0;
378 me.url_mode = mode;
379 me.url_ready = rdy;
380 me.url_ready_pass = pass;
382}
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
const float FILE_READ
const float FILE_WRITE
const float FILE_APPEND
#define strstrofs
#define tokenize_console
#define tokenizebyseparator
#define buf_create
#define pass(name, colormin, colormax)
#define ERASEABLE
Definition _all.inc:37
#define LOG_INFO(...)
Definition log.qh:65
#define LOG_INFOF(...)
Definition log.qh:66
void cvar_set(string name, string value)
string fgets(float fhandle)
void fclose(float fhandle)
void fputs(float fhandle, string s)
float fopen(string filename, float mode)
string ftos(float f)
float fabs(float f)
void print(string text,...)
string strzone(string s)
string argv(float n)
string string_null
Definition nil.qh:9
#define new_pure(class)
purely logical entities (not linked to the area grid)
Definition oo.qh:67
#define NULL
Definition post.qh:14
#define error
Definition pre.qh:6
#define strfree(this)
Definition string.qh:59
string url_url
Definition urllib.qc:9
url_ready_func url_ready
Definition urllib.qc:17
int autocvar__urllib_nextslot
Definition urllib.qc:25
const float URL_FH_CURL
Definition urllib.qc:5
ERASEABLE string url_fgets(entity e)
Definition urllib.qc:287
ERASEABLE void url_multi_fopen(string url, int mode, url_ready_func rdy, entity pass)
Definition urllib.qc:364
string url_content_type
Definition urllib.qc:10
entity url_fromid[NUM_URL_ID]
Definition urllib.qc:24
const float URL_FH_STDOUT
Definition urllib.qc:6
float url_wbuf
Definition urllib.qc:12
float url_rbuf
Definition urllib.qc:14
float url_fh
Definition urllib.qc:4
entity url_ready_pass
Definition urllib.qc:18
float url_rbufpos
Definition urllib.qc:15
ERASEABLE void url_multi_ready(entity fh, entity me, float status)
Definition urllib.qc:335
ERASEABLE void url_fputs(entity e, string s)
Definition urllib.qc:312
float url_wbufpos
Definition urllib.qc:13
int url_attempt
Definition urllib.qc:21
float url_id
Definition urllib.qc:16
ERASEABLE void url_single_fopen(string url, int mode, url_ready_func rdy, entity pass)
Definition urllib.qc:87
ERASEABLE float url_URI_Get_Callback(int id, float status, string data)
Definition urllib.qc:28
ERASEABLE void url_fclose(entity e)
Definition urllib.qc:207
string url_verb
Definition urllib.qc:11
int url_mode
Definition urllib.qc:22
const float URL_READY_CLOSED
Definition urllib.qh:15
const float URL_READY_ERROR
Definition urllib.qh:14
#define MIN_URL_ID
Definition urllib.qh:33
const float URL_READY_CANREAD
Definition urllib.qh:17
void(entity handle, entity pass, float status) url_ready_func
Definition urllib.qh:19
#define NUM_URL_ID
Definition urllib.qh:34
const float URL_READY_CANWRITE
Definition urllib.qh:16