|  | #include <u.h> | 
|  | #include <libc.h> | 
|  | #include <httpd.h> | 
|  |  | 
|  | static	char	hstates[] = "nrewE"; | 
|  | static	char	hxfers[] = " x"; | 
|  | static int _hflush(Hio*, int, int); | 
|  |  | 
|  | int | 
|  | hinit(Hio *h, int fd, int mode) | 
|  | { | 
|  | if(fd == -1 || mode != Hread && mode != Hwrite) | 
|  | return -1; | 
|  | h->hh = nil; | 
|  | h->fd = fd; | 
|  | h->seek = 0; | 
|  | h->state = mode; | 
|  | h->start = h->buf + 16;		/* leave space for chunk length */ | 
|  | h->stop = h->pos = h->start; | 
|  | if(mode == Hread){ | 
|  | h->bodylen = ~0UL; | 
|  | *h->pos = '\0'; | 
|  | }else | 
|  | h->stop = h->start + Hsize; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int | 
|  | hiserror(Hio *h) | 
|  | { | 
|  | return h->state == Herr; | 
|  | } | 
|  |  | 
|  | int | 
|  | hgetc(Hio *h) | 
|  | { | 
|  | uchar *p; | 
|  |  | 
|  | p = h->pos; | 
|  | if(p < h->stop){ | 
|  | h->pos = p + 1; | 
|  | return *p; | 
|  | } | 
|  | p -= UTFmax; | 
|  | if(p < h->start) | 
|  | p = h->start; | 
|  | if(!hreadbuf(h, p) || h->pos == h->stop) | 
|  | return -1; | 
|  | return *h->pos++; | 
|  | } | 
|  |  | 
|  | int | 
|  | hungetc(Hio *h) | 
|  | { | 
|  | if(h->state == Hend) | 
|  | h->state = Hread; | 
|  | else if(h->state == Hread) | 
|  | h->pos--; | 
|  | if(h->pos < h->start || h->state != Hread){ | 
|  | h->state = Herr; | 
|  | h->pos = h->stop; | 
|  | return -1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * fill the buffer, saving contents from vsave onwards. | 
|  | * nothing is saved if vsave is nil. | 
|  | * returns the beginning of the buffer. | 
|  | * | 
|  | * understands message body sizes and chunked transfer encoding | 
|  | */ | 
|  | void * | 
|  | hreadbuf(Hio *h, void *vsave) | 
|  | { | 
|  | Hio *hh; | 
|  | uchar *save; | 
|  | int c, in, cpy, dpos; | 
|  |  | 
|  | save = vsave; | 
|  | if(save && (save < h->start || save > h->stop) | 
|  | || h->state != Hread && h->state != Hend){ | 
|  | h->state = Herr; | 
|  | h->pos = h->stop; | 
|  | return nil; | 
|  | } | 
|  |  | 
|  | dpos = 0; | 
|  | if(save && h->pos > save) | 
|  | dpos = h->pos - save; | 
|  | cpy = 0; | 
|  | if(save){ | 
|  | cpy = h->stop - save; | 
|  | memmove(h->start, save, cpy); | 
|  | } | 
|  | h->seek += h->stop - h->start - cpy; | 
|  | h->pos = h->start + dpos; | 
|  |  | 
|  | in = Hsize - cpy; | 
|  | if(h->state == Hend) | 
|  | in = 0; | 
|  | else if(in > h->bodylen) | 
|  | in = h->bodylen; | 
|  |  | 
|  | /* | 
|  | * for chunked encoding, fill buffer, | 
|  | * then read in new chunk length and wipe out that line | 
|  | */ | 
|  | hh = h->hh; | 
|  | if(hh != nil){ | 
|  | if(!in && h->xferenc && h->state != Hend){ | 
|  | if(h->xferenc == 2){ | 
|  | c = hgetc(hh); | 
|  | if(c == '\r') | 
|  | c = hgetc(hh); | 
|  | if(c != '\n'){ | 
|  | h->pos = h->stop; | 
|  | h->state = Herr; | 
|  | return nil; | 
|  | } | 
|  | } | 
|  | h->xferenc = 2; | 
|  | in = 0; | 
|  | while((c = hgetc(hh)) != '\n'){ | 
|  | if(c >= '0' && c <= '9') | 
|  | c -= '0'; | 
|  | else if(c >= 'a' && c <= 'f') | 
|  | c -= 'a' - 10; | 
|  | else if(c >= 'A' && c <= 'F') | 
|  | c -= 'A' - 10; | 
|  | else | 
|  | break; | 
|  | in = in * 16 + c; | 
|  | } | 
|  | while(c != '\n'){ | 
|  | if(c < 0){ | 
|  | h->pos = h->stop; | 
|  | h->state = Herr; | 
|  | return nil; | 
|  | } | 
|  | c = hgetc(hh); | 
|  | } | 
|  | h->bodylen = in; | 
|  |  | 
|  | in = Hsize - cpy; | 
|  | if(in > h->bodylen) | 
|  | in = h->bodylen; | 
|  | } | 
|  | if(in){ | 
|  | while(hh->pos + in > hh->stop){ | 
|  | if(hreadbuf(hh, hh->pos) == nil){ | 
|  | h->pos = h->stop; | 
|  | h->state = Herr; | 
|  | return nil; | 
|  | } | 
|  | } | 
|  | memmove(h->start + cpy, hh->pos, in); | 
|  | hh->pos += in; | 
|  | } | 
|  | }else if(in){ | 
|  | if((in = read(h->fd, h->start + cpy, in)) < 0){ | 
|  | h->state = Herr; | 
|  | h->pos = h->stop; | 
|  | return nil; | 
|  | } | 
|  | } | 
|  | if(in == 0) | 
|  | h->state = Hend; | 
|  |  | 
|  | h->bodylen -= in; | 
|  |  | 
|  | h->stop = h->start + cpy + in; | 
|  | *h->stop = '\0'; | 
|  | if(h->pos == h->stop) | 
|  | return nil; | 
|  | return h->start; | 
|  | } | 
|  |  | 
|  | int | 
|  | hbuflen(Hio *h, void *p) | 
|  | { | 
|  | return h->stop - (uchar*)p; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * prepare to receive a message body | 
|  | * len is the content length (~0 => unspecified) | 
|  | * te is the transfer encoding | 
|  | * returns < 0 if setup failed | 
|  | */ | 
|  | Hio* | 
|  | hbodypush(Hio *hh, ulong len, HFields *te) | 
|  | { | 
|  | Hio *h; | 
|  | int xe; | 
|  |  | 
|  | if(hh->state != Hread) | 
|  | return nil; | 
|  | xe = 0; | 
|  | if(te != nil){ | 
|  | if(te->params != nil || te->next != nil) | 
|  | return nil; | 
|  | if(cistrcmp(te->s, "chunked") == 0){ | 
|  | xe = 1; | 
|  | len = 0; | 
|  | }else if(cistrcmp(te->s, "identity") == 0){ | 
|  | ; | 
|  | }else | 
|  | return nil; | 
|  | } | 
|  |  | 
|  | h = malloc(sizeof *h); | 
|  | if(h == nil) | 
|  | return nil; | 
|  |  | 
|  | h->hh = hh; | 
|  | h->fd = -1; | 
|  | h->seek = 0; | 
|  | h->state = Hread; | 
|  | h->xferenc = xe; | 
|  | h->start = h->buf + 16;		/* leave space for chunk length */ | 
|  | h->stop = h->pos = h->start; | 
|  | *h->pos = '\0'; | 
|  | h->bodylen = len; | 
|  | return h; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * dump the state of the io buffer into a string | 
|  | */ | 
|  | char * | 
|  | hunload(Hio *h) | 
|  | { | 
|  | uchar *p, *t, *stop, *buf; | 
|  | int ne, n, c; | 
|  |  | 
|  | stop = h->stop; | 
|  | ne = 0; | 
|  | for(p = h->pos; p < stop; p++){ | 
|  | c = *p; | 
|  | if(c == 0x80) | 
|  | ne++; | 
|  | } | 
|  | p = h->pos; | 
|  |  | 
|  | n = (stop - p) + ne + 3; | 
|  | buf = mallocz(n, 1); | 
|  | if(buf == nil) | 
|  | return nil; | 
|  | buf[0] = hstates[h->state]; | 
|  | buf[1] = hxfers[h->xferenc]; | 
|  |  | 
|  | t = &buf[2]; | 
|  | for(; p < stop; p++){ | 
|  | c = *p; | 
|  | if(c == 0 || c == 0x80){ | 
|  | *t++ = 0x80; | 
|  | if(c == 0x80) | 
|  | *t++ = 0x80; | 
|  | }else | 
|  | *t++ = c; | 
|  | } | 
|  | *t++ = '\0'; | 
|  | if(t != buf + n) | 
|  | return nil; | 
|  | return (char*)buf; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * read the io buffer state from a string | 
|  | */ | 
|  | int | 
|  | hload(Hio *h, char *buf) | 
|  | { | 
|  | uchar *p, *t, *stop; | 
|  | char *s; | 
|  | int c; | 
|  |  | 
|  | s = strchr(hstates, buf[0]); | 
|  | if(s == nil) | 
|  | return -1; | 
|  | h->state = s - hstates; | 
|  |  | 
|  | s = strchr(hxfers, buf[1]); | 
|  | if(s == nil) | 
|  | return -1; | 
|  | h->xferenc = s - hxfers; | 
|  |  | 
|  | t = h->start; | 
|  | stop = t + Hsize; | 
|  | for(p = (uchar*)&buf[2]; c = *p; p++){ | 
|  | if(c == 0x80){ | 
|  | if(p[1] != 0x80) | 
|  | c = 0; | 
|  | else | 
|  | p++; | 
|  | } | 
|  | *t++ = c; | 
|  | if(t >= stop) | 
|  | return -1; | 
|  | } | 
|  | *t = '\0'; | 
|  | h->pos = h->start; | 
|  | h->stop = t; | 
|  | h->seek = 0; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void | 
|  | hclose(Hio *h) | 
|  | { | 
|  | if(h->fd >= 0){ | 
|  | if(h->state == Hwrite) | 
|  | hxferenc(h, 0); | 
|  | close(h->fd); | 
|  | } | 
|  | h->stop = h->pos = nil; | 
|  | h->fd = -1; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * flush the buffer and possibly change encoding modes | 
|  | */ | 
|  | int | 
|  | hxferenc(Hio *h, int on) | 
|  | { | 
|  | if(h->xferenc && !on && h->pos != h->start) | 
|  | hflush(h); | 
|  | if(_hflush(h, 1, 0) < 0) | 
|  | return -1; | 
|  | h->xferenc = !!on; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int | 
|  | hputc(Hio *h, int c) | 
|  | { | 
|  | uchar *p; | 
|  |  | 
|  | p = h->pos; | 
|  | if(p < h->stop){ | 
|  | h->pos = p + 1; | 
|  | return *p = c; | 
|  | } | 
|  | if(hflush(h) < 0) | 
|  | return -1; | 
|  | return *h->pos++ = c; | 
|  | } | 
|  |  | 
|  | static int | 
|  | fmthflush(Fmt *f) | 
|  | { | 
|  | Hio *h; | 
|  |  | 
|  | h = f->farg; | 
|  | h->pos = f->to; | 
|  | if(hflush(h) < 0) | 
|  | return 0; | 
|  | f->stop = h->stop; | 
|  | f->to = h->pos; | 
|  | f->start = h->pos; | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | int | 
|  | hvprint(Hio *h, char *fmt, va_list args) | 
|  | { | 
|  | int n; | 
|  | Fmt f; | 
|  |  | 
|  | f.runes = 0; | 
|  | f.stop = h->stop; | 
|  | f.to = h->pos; | 
|  | f.start = h->pos; | 
|  | f.flush = fmthflush; | 
|  | f.farg = h; | 
|  | f.nfmt = 0; | 
|  | fmtlocaleinit(&f, nil, nil, nil); | 
|  | n = fmtvprint(&f, fmt, args); | 
|  | h->pos = f.to; | 
|  | return n; | 
|  | } | 
|  |  | 
|  | int | 
|  | hprint(Hio *h, char *fmt, ...) | 
|  | { | 
|  | int n; | 
|  | va_list arg; | 
|  |  | 
|  | va_start(arg, fmt); | 
|  | n = hvprint(h, fmt, arg); | 
|  | va_end(arg); | 
|  | return n; | 
|  | } | 
|  |  | 
|  | static int | 
|  | _hflush(Hio *h, int force, int dolength) | 
|  | { | 
|  | uchar *s; | 
|  | int w; | 
|  |  | 
|  | if(h->state != Hwrite){ | 
|  | h->state = Herr; | 
|  | h->stop = h->pos; | 
|  | return -1; | 
|  | } | 
|  | s = h->start; | 
|  | w = h->pos - s; | 
|  | if(w == 0 && !force) | 
|  | return 0; | 
|  | if(h->xferenc){ | 
|  | *--s = '\n'; | 
|  | *--s = '\r'; | 
|  | do{ | 
|  | *--s = "0123456789abcdef"[w & 0xf]; | 
|  | w >>= 4; | 
|  | }while(w); | 
|  | h->pos[0] = '\r'; | 
|  | h->pos[1] = '\n'; | 
|  | w = &h->pos[2] - s; | 
|  | } | 
|  | if(dolength) | 
|  | fprint(h->fd, "Content-Length: %d\r\n\r\n", w); | 
|  | if(write(h->fd, s, w) != w){ | 
|  | h->state = Herr; | 
|  | h->stop = h->pos; | 
|  | return -1; | 
|  | } | 
|  | h->seek += w; | 
|  | h->pos = h->start; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int | 
|  | hflush(Hio *h) | 
|  | { | 
|  | return _hflush(h, 0, 0); | 
|  | } | 
|  |  | 
|  | int | 
|  | hlflush(Hio* h) | 
|  | { | 
|  | return _hflush(h, 0, 1); | 
|  | } | 
|  |  | 
|  | int | 
|  | hwrite(Hio *h, void *vbuf, int len) | 
|  | { | 
|  | uchar *buf; | 
|  | int n, m; | 
|  |  | 
|  | buf = vbuf; | 
|  | n = len; | 
|  | if(n < 0 || h->state != Hwrite){ | 
|  | h->state = Herr; | 
|  | h->stop = h->pos; | 
|  | return -1; | 
|  | } | 
|  | if(h->pos + n >= h->stop){ | 
|  | if(h->start != h->pos) | 
|  | if(hflush(h) < 0) | 
|  | return -1; | 
|  | while(h->pos + n >= h->stop){ | 
|  | m = h->stop - h->pos; | 
|  | if(h->xferenc){ | 
|  | memmove(h->pos, buf, m); | 
|  | h->pos += m; | 
|  | if(hflush(h) < 0) | 
|  | return -1; | 
|  | }else{ | 
|  | if(write(h->fd, buf, m) != m){ | 
|  | h->state = Herr; | 
|  | h->stop = h->pos; | 
|  | return -1; | 
|  | } | 
|  | h->seek += m; | 
|  | } | 
|  | n -= m; | 
|  | buf += m; | 
|  | } | 
|  | } | 
|  | memmove(h->pos, buf, n); | 
|  | h->pos += n; | 
|  | return len; | 
|  | } |