]> git.lizzy.rs Git - plan9front.git/blob - sys/src/libhttpd/hio.c
upas/Mail: avoid showing empty To: and CC: lines in compose windows
[plan9front.git] / sys / src / libhttpd / hio.c
1 #include <u.h>
2 #include <libc.h>
3 #include <httpd.h>
4
5 static  char    hstates[] = "nrewE";
6 static  char    hxfers[] = " x";
7 static int _hflush(Hio*, int, int);
8
9 int
10 hinit(Hio *h, int fd, int mode)
11 {
12         if(fd == -1 || mode != Hread && mode != Hwrite)
13                 return -1;
14         h->hh = nil;
15         h->fd = fd;
16         h->seek = 0;
17         h->state = mode;
18         h->start = h->buf + 16;         /* leave space for chunk length */
19         h->stop = h->pos = h->start;
20         if(mode == Hread){
21                 h->bodylen = ~0UL;
22                 *h->pos = '\0';
23         }else
24                 h->stop = h->start + Hsize;
25         return 0;
26 }
27
28 int
29 hiserror(Hio *h)
30 {
31         return h->state == Herr;
32 }
33
34 int
35 hgetc(Hio *h)
36 {
37         uchar *p;
38
39         p = h->pos;
40         if(p < h->stop){
41                 h->pos = p + 1;
42                 return *p;
43         }
44         p -= UTFmax;
45         if(p < h->start)
46                 p = h->start;
47         if(!hreadbuf(h, p) || h->pos == h->stop)
48                 return -1;
49         return *h->pos++;
50 }
51
52 int
53 hungetc(Hio *h)
54 {
55         if(h->state == Hend)
56                 h->state = Hread;
57         else if(h->state == Hread)
58                 h->pos--;
59         if(h->pos < h->start || h->state != Hread){
60                 h->state = Herr;
61                 h->pos = h->stop;
62                 return -1;
63         }
64         return 0;
65 }
66
67 /*
68  * fill the buffer, saving contents from vsave onwards.
69  * nothing is saved if vsave is nil.
70  * returns the beginning of the buffer.
71  *
72  * understands message body sizes and chunked transfer encoding
73  */
74 void *
75 hreadbuf(Hio *h, void *vsave)
76 {
77         Hio *hh;
78         uchar *save;
79         int c, in, cpy, dpos;
80
81         save = vsave;
82         if(save && (save < h->start || save > h->stop)
83         || h->state != Hread && h->state != Hend){
84                 h->state = Herr;
85                 h->pos = h->stop;
86                 return nil;
87         }
88
89         dpos = 0;
90         if(save && h->pos > save)
91                 dpos = h->pos - save;
92         cpy = 0;
93         if(save){
94                 cpy = h->stop - save;
95                 memmove(h->start, save, cpy);
96         }
97         h->seek += h->stop - h->start - cpy;
98         h->pos = h->start + dpos;
99
100         in = Hsize - cpy;
101         if(h->state == Hend)
102                 in = 0;
103         else if(in > h->bodylen)
104                 in = h->bodylen;
105
106         /*
107          * for chunked encoding, fill buffer,
108          * then read in new chunk length and wipe out that line
109          */
110         hh = h->hh;
111         if(hh != nil){
112                 if(!in && h->xferenc && h->state != Hend){
113                         if(h->xferenc == 2){
114                                 c = hgetc(hh);
115                                 if(c == '\r')
116                                         c = hgetc(hh);
117                                 if(c != '\n'){
118                                         h->pos = h->stop;
119                                         h->state = Herr;
120                                         return nil;
121                                 }
122                         }
123                         h->xferenc = 2;
124                         in = 0;
125                         while((c = hgetc(hh)) != '\n'){
126                                 if(c >= '0' && c <= '9')
127                                         c -= '0';
128                                 else if(c >= 'a' && c <= 'f')
129                                         c -= 'a' - 10;
130                                 else if(c >= 'A' && c <= 'F')
131                                         c -= 'A' - 10;
132                                 else
133                                         break;
134                                 in = in * 16 + c;
135                         }
136                         while(c != '\n'){
137                                 if(c < 0){
138                                         h->pos = h->stop;
139                                         h->state = Herr;
140                                         return nil;
141                                 }
142                                 c = hgetc(hh);
143                         }
144                         h->bodylen = in;
145
146                         in = Hsize - cpy;
147                         if(in > h->bodylen)
148                                 in = h->bodylen;
149                 }
150                 if(in){
151                         while(hh->pos + in > hh->stop){
152                                 if(hreadbuf(hh, hh->pos) == nil){
153                                         h->pos = h->stop;
154                                         h->state = Herr;
155                                         return nil;
156                                 }
157                         }
158                         memmove(h->start + cpy, hh->pos, in);
159                         hh->pos += in;
160                 }
161         }else if(in){
162                 if((in = read(h->fd, h->start + cpy, in)) < 0){
163                         h->state = Herr;
164                         h->pos = h->stop;
165                         return nil;
166                 }
167         }
168         if(in == 0)
169                 h->state = Hend;
170
171         h->bodylen -= in;
172
173         h->stop = h->start + cpy + in;
174         *h->stop = '\0';
175         if(h->pos == h->stop)
176                 return nil;
177         return h->start;
178 }
179
180 int
181 hbuflen(Hio *h, void *p)
182 {
183         return h->stop - (uchar*)p;
184 }
185
186 /*
187  * prepare to receive a message body
188  * len is the content length (~0 => unspecified)
189  * te is the transfer encoding
190  * returns < 0 if setup failed
191  */
192 Hio*
193 hbodypush(Hio *hh, ulong len, HFields *te)
194 {
195         Hio *h;
196         int xe;
197
198         if(hh->state != Hread)
199                 return nil;
200         xe = 0;
201         if(te != nil){
202                 if(te->params != nil || te->next != nil)
203                         return nil;
204                 if(cistrcmp(te->s, "chunked") == 0){
205                         xe = 1;
206                         len = 0;
207                 }else if(cistrcmp(te->s, "identity") == 0){
208                         ;
209                 }else
210                         return nil;
211         }
212
213         h = malloc(sizeof *h);
214         if(h == nil)
215                 return nil;
216
217         h->hh = hh;
218         h->fd = -1;
219         h->seek = 0;
220         h->state = Hread;
221         h->xferenc = xe;
222         h->start = h->buf + 16;         /* leave space for chunk length */
223         h->stop = h->pos = h->start;
224         *h->pos = '\0';
225         h->bodylen = len;
226         return h;
227 }
228
229 /*
230  * dump the state of the io buffer into a string
231  */
232 char *
233 hunload(Hio *h)
234 {
235         uchar *p, *t, *stop, *buf;
236         int ne, n, c;
237
238         stop = h->stop;
239         ne = 0;
240         for(p = h->pos; p < stop; p++){
241                 c = *p;
242                 if(c == 0x80)
243                         ne++;
244         }
245         p = h->pos;
246
247         n = (stop - p) + ne + 3;
248         buf = mallocz(n, 1);
249         if(buf == nil)
250                 return nil;
251         buf[0] = hstates[h->state];
252         buf[1] = hxfers[h->xferenc];
253
254         t = &buf[2];
255         for(; p < stop; p++){
256                 c = *p;
257                 if(c == 0 || c == 0x80){
258                         *t++ = 0x80;
259                         if(c == 0x80)
260                                 *t++ = 0x80;
261                 }else
262                         *t++ = c;
263         }
264         *t++ = '\0';
265         if(t != buf + n)
266                 return nil;
267         return (char*)buf;
268 }
269
270 /*
271  * read the io buffer state from a string
272  */
273 int
274 hload(Hio *h, char *buf)
275 {
276         uchar *p, *t, *stop;
277         char *s;
278         int c;
279
280         s = strchr(hstates, buf[0]);
281         if(s == nil)
282                 return -1;
283         h->state = s - hstates;
284
285         s = strchr(hxfers, buf[1]);
286         if(s == nil)
287                 return -1;
288         h->xferenc = s - hxfers;
289
290         t = h->start;
291         stop = t + Hsize;
292         for(p = (uchar*)&buf[2]; c = *p; p++){
293                 if(c == 0x80){
294                         if(p[1] != 0x80)
295                                 c = 0;
296                         else
297                                 p++;
298                 }
299                 *t++ = c;
300                 if(t >= stop)
301                         return -1;
302         }
303         *t = '\0';
304         h->pos = h->start;
305         h->stop = t;
306         h->seek = 0;
307         return 0;
308 }
309
310 void
311 hclose(Hio *h)
312 {
313         if(h->fd >= 0){
314                 if(h->state == Hwrite)
315                         hxferenc(h, 0);
316                 close(h->fd);
317         }
318         h->stop = h->pos = nil;
319         h->fd = -1;
320 }
321
322 /*
323  * flush the buffer and possibly change encoding modes
324  */
325 int
326 hxferenc(Hio *h, int on)
327 {
328         if(h->xferenc && !on && h->pos != h->start)
329                 hflush(h);
330         if(_hflush(h, 1, 0) < 0)
331                 return -1;
332         h->xferenc = !!on;
333         return 0;
334 }
335
336 int
337 hputc(Hio *h, int c)
338 {
339         uchar *p;
340
341         p = h->pos;
342         if(p < h->stop){
343                 h->pos = p + 1;
344                 return *p = c;
345         }
346         if(hflush(h) < 0)
347                 return -1;
348         return *h->pos++ = c;
349 }
350
351 static int
352 fmthflush(Fmt *f)
353 {
354         Hio *h;
355
356         h = f->farg;
357         h->pos = f->to;
358         if(hflush(h) < 0)
359                 return 0;
360         f->stop = h->stop;
361         f->to = h->pos;
362         f->start = h->pos;
363         return 1;
364 }
365
366 int
367 hvprint(Hio *h, char *fmt, va_list args)
368 {
369         int n;
370         Fmt f;
371
372         f.runes = 0;
373         f.stop = h->stop;
374         f.to = h->pos;
375         f.start = h->pos;
376         f.flush = fmthflush;
377         f.farg = h;
378         f.nfmt = 0;
379 //      fmtlocaleinit(&f, nil, nil, nil);
380         n = fmtvprint(&f, fmt, args);
381         h->pos = f.to;
382         return n;
383 }
384
385 int
386 hprint(Hio *h, char *fmt, ...)
387 {
388         int n;
389         va_list arg;
390
391         va_start(arg, fmt);
392         n = hvprint(h, fmt, arg);
393         va_end(arg);
394         return n;
395 }
396
397 static int
398 _hflush(Hio *h, int force, int dolength)
399 {
400         uchar *s;
401         int w;
402
403         if(h == nil)
404                 return -1;
405         if(h->state != Hwrite){
406                 h->state = Herr;
407                 h->stop = h->pos;
408                 return -1;
409         }
410         s = h->start;
411         w = h->pos - s;
412         if(w == 0 && !force)
413                 return 0;
414         if(h->xferenc){
415                 *--s = '\n';
416                 *--s = '\r';
417                 do{
418                         *--s = "0123456789abcdef"[w & 0xf];
419                         w >>= 4;
420                 }while(w);
421                 h->pos[0] = '\r';
422                 h->pos[1] = '\n';
423                 w = &h->pos[2] - s;
424         }
425         if(dolength)
426                 fprint(h->fd, "Content-Length: %d\r\n\r\n", w);
427         if(write(h->fd, s, w) != w){
428                 h->state = Herr;
429                 h->stop = h->pos;
430                 return -1;
431         }
432         h->seek += w;
433         h->pos = h->start;
434         return 0;
435 }
436
437 int
438 hflush(Hio *h)
439 {
440         return _hflush(h, 0, 0);
441 }
442
443 int
444 hlflush(Hio* h)
445 {
446         return _hflush(h, 0, 1);
447 }
448
449 int
450 hwrite(Hio *h, void *vbuf, int len)
451 {
452         uchar *buf;
453         int n, m;
454
455         buf = vbuf;
456         n = len;
457         if(n < 0 || h->state != Hwrite){
458                 h->state = Herr;
459                 h->stop = h->pos;
460                 return -1;
461         }
462         if(h->pos + n >= h->stop){
463                 if(h->start != h->pos)
464                         if(hflush(h) < 0)
465                                 return -1;
466                 while(h->pos + n >= h->stop){
467                         m = h->stop - h->pos;
468                         if(h->xferenc){
469                                 memmove(h->pos, buf, m);
470                                 h->pos += m;
471                                 if(hflush(h) < 0)
472                                         return -1;
473                         }else{
474                                 if(write(h->fd, buf, m) != m){
475                                         h->state = Herr;
476                                         h->stop = h->pos;
477                                         return -1;
478                                 }
479                                 h->seek += m;
480                         }
481                         n -= m;
482                         buf += m;
483                 }
484         }
485         memmove(h->pos, buf, n);
486         h->pos += n;
487         return len;
488 }