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