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