]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/urllib.qc
Merge remote-tracking branch 'origin/terencehill/arena_stuff'
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / urllib.qc
1 // files (-1 for URL)
2 .float url_fh;
3
4 // URLs
5 .string url_url;
6 .float url_wbuf;
7 .float url_wbufpos;
8 .float url_rbuf;
9 .float url_rbufpos;
10 .float url_id;
11 .url_ready_func url_ready;
12 .entity url_ready_pass;
13
14 entity url_fromid[NUM_URL_ID];
15 float autocvar__urllib_nextslot;
16
17 float url_URI_Get_Callback(float id, float status, string data)
18 {
19         if(id < MIN_URL_ID)
20                 return 0;
21         id -= MIN_URL_ID;
22         if(id >= NUM_URL_ID)
23                 return 0;
24         entity e;
25         e = url_fromid[id];
26         if(!e)
27                 return 0;
28         if(e.url_rbuf >= 0 || e.url_wbuf >= 0)
29         {
30                 print(sprintf("WARNING: handle %d (%s) has already received data?!?\n", id + NUM_URL_ID, e.url_url));
31                 return 0;
32         }
33
34         // whatever happens, we will remove the URL from the list of IDs
35         url_fromid[id] = world;
36
37         // if we get here, we MUST have both buffers cleared
38         if(e.url_rbuf != -1 || e.url_wbuf != -1 || e.url_fh != -1)
39                 error("url_URI_Get_Callback: not a request waiting for data");
40
41         if(status == 0)
42         {
43                 // WE GOT DATA!
44                 float n, i;
45                 n = tokenizebyseparator(data, "\n");
46                 e.url_rbuf = buf_create();
47                 if(e.url_rbuf < 0)
48                 {
49                         print("url_URI_Get_Callback: out of memory in buf_create\n");
50                         e.url_ready(e, e.url_ready_pass, URL_READY_ERROR);
51                         strunzone(e.url_url);
52                         remove(e);
53                         return 1;
54                 }
55                 e.url_rbufpos = 0;
56                 if(e.url_rbuf < 0)
57                 {
58                         print("url_URI_Get_Callback: out of memory in buf_create\n");
59                         e.url_ready(e, e.url_ready_pass, URL_READY_ERROR);
60                         strunzone(e.url_url);
61                         remove(e);
62                         return 1;
63                 }
64                 for(i = 0; i < n; ++i)
65                         bufstr_set(e.url_rbuf, i, argv(i));
66                 e.url_ready(e, e.url_ready_pass, URL_READY_CANREAD);
67                 return 1;
68         }
69         else
70         {
71                 // an ERROR
72                 e.url_ready(e, e.url_ready_pass, -fabs(status));
73                 strunzone(e.url_url);
74                 remove(e);
75                 return 1;
76         }
77 }
78
79 void url_fopen(string url, float mode, url_ready_func rdy, entity pass)
80 {
81         entity e;
82         float i;
83         if(strstrofs(url, "://", 0) >= 0)
84         {
85                 switch(mode)
86                 {
87                         case FILE_WRITE:
88                         case FILE_APPEND:
89                                 // collect data to a stringbuffer for a POST request
90                                 // attempts to close will result in a reading handle
91
92                                 // create a writing end that does nothing yet
93                                 e = spawn();
94                                 e.classname = "url_fopen_file";
95                                 e.url_url = strzone(url);
96                                 e.url_fh = -1;
97                                 e.url_wbuf = buf_create();
98                                 if(e.url_wbuf < 0)
99                                 {
100                                         print("url_fopen: out of memory in buf_create\n");
101                                         rdy(e, pass, URL_READY_ERROR);
102                                         strunzone(e.url_url);
103                                         remove(e);
104                                         return;
105                                 }
106                                 e.url_wbufpos = 0;
107                                 e.url_rbuf = -1;
108                                 rdy(e, pass, URL_READY_CANWRITE);
109                                 break;
110
111                         case FILE_READ:
112                                 // read data only
113
114                                 // get slot for HTTP request
115                                 for(i = autocvar__urllib_nextslot; i < NUM_URL_ID; ++i)
116                                         if(url_fromid[i] == world)
117                                                 break;
118                                 if(i >= NUM_URL_ID)
119                                 {
120                                         for(i = 0; i < autocvar__urllib_nextslot; ++i)
121                                                 if(url_fromid[i] == world)
122                                                         break;
123                                         if(i >= autocvar__urllib_nextslot)
124                                         {
125                                                 print("url_fopen: too many concurrent requests\n");
126                                                 rdy(world, pass, URL_READY_ERROR);
127                                                 return;
128                                         }
129                                 }
130
131                                 // GET the data
132                                 if(!crypto_uri_postbuf(url, i + MIN_URL_ID, string_null, string_null, -1, 0))
133                                 {
134                                         print("url_fopen: failure in crypto_uri_postbuf\n");
135                                         rdy(world, pass, URL_READY_ERROR);
136                                         return;
137                                 }
138
139                                 // Make a dummy handle object (no buffers at
140                                 // all). Wait for data to come from the
141                                 // server, then call the callback
142                                 e = spawn();
143                                 e.classname = "url_fopen_file";
144                                 e.url_url = strzone(url);
145                                 e.url_fh = -1;
146                                 e.url_rbuf = -1;
147                                 e.url_wbuf = -1;
148                                 e.url_ready = rdy;
149                                 e.url_ready_pass = pass;
150                                 e.url_id = i;
151                                 url_fromid[i] = e;
152
153                                 // make sure this slot won't be reused quickly even on map change
154                                 cvar_set("_urllib_nextslot", ftos(mod(i + 1, NUM_URL_ID)));
155                                 break;
156                 }
157         }
158         else
159         {
160                 float fh;
161                 fh = fopen(url, mode);
162                 if(fh < 0)
163                 {
164                         rdy(world, pass, URL_READY_ERROR);
165                         return;
166                 }
167                 else
168                 {
169                         e = spawn();
170                         e.classname = "url_fopen_file";
171                         e.url_fh = fh;
172                         if(mode == FILE_READ)
173                                 rdy(e, pass, URL_READY_CANREAD);
174                         else
175                                 rdy(e, pass, URL_READY_CANWRITE);
176                 }
177         }
178 }
179
180 // close a file
181 void url_fclose(entity e, url_ready_func rdy, entity pass)
182 {
183         float i;
184
185         if(e.url_fh < 0)
186         {
187                 if(e.url_rbuf == -1 || e.url_wbuf != -1) // not(post GET/POST request)
188                 if(e.url_rbuf != -1 || e.url_wbuf == -1) // not(pre POST request)
189                         error("url_fclose: not closable in current state");
190
191                 // closing an URL!
192                 if(e.url_wbuf >= 0)
193                 {
194                         // we are closing the write end (HTTP POST request)
195
196                         // get slot for HTTP request
197                         for(i = autocvar__urllib_nextslot; i < NUM_URL_ID; ++i)
198                                 if(url_fromid[i] == world)
199                                         break;
200                         if(i >= NUM_URL_ID)
201                         {
202                                 for(i = 0; i < autocvar__urllib_nextslot; ++i)
203                                         if(url_fromid[i] == world)
204                                                 break;
205                                 if(i >= autocvar__urllib_nextslot)
206                                 {
207                                         print("url_fclose: too many concurrent requests\n");
208                                         rdy(e, pass, URL_READY_ERROR);
209                                         buf_del(e.url_wbuf);
210                                         strunzone(e.url_url);
211                                         remove(e);
212                                         return;
213                                 }
214                         }
215
216                         // POST the data
217                         if(!crypto_uri_postbuf(e.url_url, i + MIN_URL_ID, "text/plain", "", e.url_wbuf, 0))
218                         {
219                                 print("url_fclose: failure in crypto_uri_postbuf\n");
220                                 rdy(e, pass, URL_READY_ERROR);
221                                 buf_del(e.url_wbuf);
222                                 strunzone(e.url_url);
223                                 remove(e);
224                                 return;
225                         }
226
227                         // delete write end. File handle is now in unusable
228                         // state. Wait for data to come from the server, then
229                         // call the callback
230                         buf_del(e.url_wbuf);
231                         e.url_wbuf = -1;
232                         e.url_ready = rdy;
233                         e.url_ready_pass = pass;
234                         e.url_id = i;
235                         url_fromid[i] = e;
236
237                         // make sure this slot won't be reused quickly even on map change
238                         cvar_set("_urllib_nextslot", ftos(mod(i + 1, NUM_URL_ID)));
239                 }
240                 else
241                 {
242                         // we have READ all data, just close
243                         rdy(e, pass, URL_READY_CLOSED);
244                         buf_del(e.url_rbuf);
245                         strunzone(e.url_url);
246                         remove(e);
247                 }
248         }
249         else
250         {
251                 // file
252                 fclose(e.url_fh);
253                 rdy(e, pass, URL_READY_CLOSED); // closing creates no reading handle
254                 remove(e);
255         }
256 }
257
258 // with \n (blame FRIK_FILE)
259 string url_fgets(entity e)
260 {
261         if(e.url_fh < 0)
262         {
263                 if(e.url_rbuf == -1)
264                         error("url_fgets: not readable in current state");
265                 // curl
266                 string s;
267                 s = bufstr_get(e.url_rbuf, e.url_rbufpos);
268                 e.url_rbufpos += 1;
269                 return s;
270         }
271         else
272         {
273                 // file
274                 return fgets(e.url_fh);
275         }
276 }
277
278 // without \n (blame FRIK_FILE)
279 void url_fputs(entity e, string s)
280 {
281         if(e.url_fh < 0)
282         {
283                 if(e.url_wbuf == -1)
284                         error("url_fputs: not writable in current state");
285                 // curl
286                 bufstr_set(e.url_wbuf, e.url_wbufpos, s);
287                 e.url_wbufpos += 1;
288         }
289         else
290         {
291                 // file
292                 fputs(e.url_fh, s);
293         }
294 }