]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/urllib.qc
prevent early urllib slot reuse
[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 #define MIN_URL_ID 128
15 #define NUM_URL_ID 64
16 entity url_fromid[NUM_URL_ID];
17 float autocvar__urllib_nextslot;
18
19 float url_URI_Get_Callback(float id, float status, string data)
20 {
21         if(id < MIN_URL_ID)
22                 return 0;
23         id -= MIN_URL_ID;
24         if(id >= NUM_URL_ID)
25                 return 0;
26         entity e;
27         e = url_fromid[id];
28         if(!e)
29                 return 0;
30         if(e.url_rbuf >= 0 || e.url_wbuf >= 0)
31         {
32                 print(sprintf("WARNING: handle %d (%s) has already received data?!?\n", id + NUM_URL_ID, e.url_url));
33                 return 0;
34         }
35
36         // whatever happens, we will remove the URL from the list of IDs
37         url_fromid[id] = world;
38
39         if(status == 0)
40         {
41                 // WE GOT DATA!
42                 float n, i;
43                 n = tokenizebyseparator(data, "\n");
44                 e.url_rbuf = buf_create();
45                 e.url_rbufpos = 0;
46                 if(e.url_rbuf < 0)
47                 {
48                         print("buf_create: out of memory\n");
49                         e.url_ready(e, e.url_ready_pass, URL_READY_ERROR);
50                         strunzone(e.url_url);
51                         remove(e);
52                 }
53                 for(i = 0; i < n; ++i)
54                         bufstr_set(e.url_rbuf, i, argv(i));
55                 e.url_ready(e, e.url_ready_pass, URL_READY_CANREAD);
56                 return 1;
57         }
58         else
59         {
60                 // an ERROR
61                 e.url_ready(e, e.url_ready_pass, -status);
62                 strunzone(e.url_url);
63                 remove(e);
64                 return 1;
65         }
66 }
67
68 void url_fopen(string url, float mode, url_ready_func rdy, entity pass)
69 {
70         entity e;
71         float i;
72         if(strstrofs(url, "://", -1))
73         {
74                 switch(mode)
75                 {
76                         case FILE_WRITE:
77                         case FILE_APPEND:
78                                 // collect data to a stringbuffer for a POST request
79                                 // attempts to close will result in a reading handle
80                                 e = spawn();
81                                 e.classname = "url_fopen_file";
82                                 e.url_url = strzone(url);
83                                 e.url_fh = -1;
84                                 e.url_wbuf = buf_create();
85                                 if(e.url_wbuf < 0)
86                                 {
87                                         print("buf_create: out of memory\n");
88                                         rdy(e, pass, URL_READY_ERROR);
89                                         strunzone(e.url_url);
90                                         remove(e);
91                                         return;
92                                 }
93                                 e.url_wbufpos = 0;
94                                 e.url_rbuf = -1;
95                                 rdy(e, pass, URL_READY_CANWRITE);
96                                 break;
97
98                         case FILE_READ:
99                                 // read data only
100                                 for(i = autocvar__urllib_nextslot; i < NUM_URL_ID; ++i)
101                                         if(url_fromid[i] == world)
102                                                 break;
103                                 if(i >= NUM_URL_ID)
104                                 {
105                                         for(i = 0; i < autocvar__urllib_nextslot; ++i)
106                                                 if(url_fromid[i] == world)
107                                                         break;
108                                         if(i >= autocvar__urllib_nextslot)
109                                         {
110                                                 rdy(world, pass, URL_READY_ERROR);
111                                                 return;
112                                         }
113                                 }
114
115                                 e = spawn();
116                                 e.classname = "url_fopen_file";
117                                 e.url_url = strzone(url);
118                                 e.url_fh = -1;
119                                 e.url_rbuf = -1;
120                                 e.url_wbuf = -1;
121                                 if(!uri_get(url, i + MIN_URL_ID))
122                                 {
123                                         rdy(e, pass, URL_READY_ERROR);
124                                         strunzone(e.url_url);
125                                         remove(e);
126                                         return;
127                                 }
128                                 e.url_ready = rdy;
129                                 e.url_ready_pass = pass;
130                                 e.url_id = i;
131                                 url_fromid[i] = e;
132                                 cvar_set("_urllib_nextslot", ftos(mod(i + 1, NUM_URL_ID)));
133                                 break;
134                 }
135         }
136         else
137         {
138                 float fh;
139                 fh = fopen(url, mode);
140                 if(fh < 0)
141                 {
142                         rdy(world, pass, URL_READY_ERROR);
143                         return;
144                 }
145                 else
146                 {
147                         e = spawn();
148                         e.classname = "url_fopen_file";
149                         e.url_fh = fh;
150                         if(mode == FILE_READ)
151                                 rdy(e, pass, URL_READY_CANREAD);
152                         else
153                                 rdy(e, pass, URL_READY_CANWRITE);
154                 }
155         }
156 }
157
158 void url_fclose(entity e, url_ready_func rdy, entity pass)
159 {
160         float i;
161
162         if(e.url_fh < 0)
163         {
164                 if(e.url_wbuf >= 0)
165                 {
166                         for(i = autocvar__urllib_nextslot; i < NUM_URL_ID; ++i)
167                                 if(url_fromid[i] == world)
168                                         break;
169                         if(i >= NUM_URL_ID)
170                         {
171                                 for(i = 0; i < autocvar__urllib_nextslot; ++i)
172                                         if(url_fromid[i] == world)
173                                                 break;
174                                 if(i >= autocvar__urllib_nextslot)
175                                 {
176                                         buf_del(e.url_wbuf);
177                                         rdy(e, pass, URL_READY_ERROR);
178                                         strunzone(e.url_url);
179                                         remove(e);
180                                         return;
181                                 }
182                         }
183
184                         if(!uri_postbuf(e.url_url, e.url_id + MIN_URL_ID, "text/plain", "\n", e.url_wbuf))
185                         {
186                                 buf_del(e.url_wbuf);
187                                 rdy(e, pass, URL_READY_ERROR);
188                                 strunzone(e.url_url);
189                                 remove(e);
190                                 return;
191                         }
192
193                         buf_del(e.url_wbuf);
194                         e.url_wbuf = -1;
195                         e.url_ready = rdy;
196                         e.url_ready_pass = pass;
197                         e.url_id = i;
198                         url_fromid[i] = e;
199                         cvar_set("_urllib_nextslot", ftos(mod(i + 1, NUM_URL_ID)));
200                 }
201                 else
202                 {
203                         // we have READ all data
204                         rdy(e, pass, URL_READY_CLOSED);
205                         buf_del(e.url_rbuf);
206                         strunzone(e.url_url);
207                         remove(e);
208                 }
209         }
210         else
211         {
212                 // file
213                 fclose(e.url_fh);
214                 rdy(e, pass, URL_READY_CLOSED); // closing creates no reading handle
215                 remove(e);
216         }
217 }
218
219 // with \n
220 string url_fgets(entity e)
221 {
222         if(e.url_fh < 0)
223         {
224                 // curl
225                 string s;
226                 s = bufstr_get(e.url_rbuf, e.url_rbufpos);
227                 e.url_rbufpos += 1;
228                 return s;
229         }
230         else
231         {
232                 // file
233                 return fgets(e.url_fh);
234         }
235 }
236
237 // without \n
238 void url_fputs(entity e, string s)
239 {
240         if(e.url_fh < 0)
241         {
242                 // curl
243                 bufstr_set(e.url_wbuf, e.url_wbufpos, s);
244                 e.url_wbufpos += 1;
245         }
246         else
247         {
248                 // file
249                 fputs(e.url_fh, s);
250         }
251 }