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