diff --git a/bin/webs.c b/bin/webs.c index b5a30c0..9776b13 100644 --- a/bin/webs.c +++ b/bin/webs.c @@ -27,8 +27,16 @@ struct htts_ext_t }; typedef struct htts_ext_t htts_ext_t; +struct buff_t +{ + hio_uint8_t buf[4096]; + hio_oow_t len; +}; +typedef struct buff_t buff_t; -void untar (hio_t* hio, hio_dev_thr_iopair_t* iop, hio_svc_htts_thr_func_info_t* tfi, void* ctx) +/* ------------------------------------------------------------------------- */ + +static void untar (hio_t* hio, hio_dev_thr_iopair_t* iop, hio_svc_htts_thr_func_info_t* tfi, void* ctx) { FILE* wfp = HIO_NULL; htts_ext_t* ext; @@ -82,6 +90,70 @@ done: } } +/* ------------------------------------------------------------------------- */ + +static hio_oow_t write_all_to_fd (int fd, const hio_uint8_t* ptr, hio_oow_t len) +{ + hio_oow_t rem = len; + + while (rem > 0) + { + hio_ooi_t n; + n = write(fd, ptr, rem); + if (n <= -1) break; + rem -= n; + ptr += n; + } + + return len - rem; /* return the number of bytes written */ +} + +static HIO_INLINE void init_buff (buff_t* buf) +{ + buf->len = 0; +} + +static int flush_buff_to_fd (int fd, buff_t* buf) +{ + if (buf->len > 0) + { + hio_oow_t n; + n = write_all_to_fd(fd, buf->buf, buf->len); + buf->len -= n; + if (buf->len > 0) return -1; + } + return 0; +} + +static int write_buff_to_fd (int fd, buff_t* buf, const void* ptr, hio_oow_t len) +{ + hio_oow_t rcapa; + + rcapa = HIO_COUNTOF(buf->buf) - buf->len; + if (len >= HIO_COUNTOF(buf->buf) + rcapa) + { + if (flush_buff_to_fd(fd, buf) <= -1) return -1; + if (write_all_to_fd(fd, (const hio_uint8_t*)ptr, len) != len) return -1; + } + else if (len >= rcapa) + { + HIO_MEMCPY (&buf->buf[buf->len], ptr, rcapa); + buf->len += rcapa; + if (flush_buff_to_fd(fd, buf) <= -1) return -1; + HIO_MEMCPY (buf->buf, (const hio_uint8_t*)ptr + rcapa, len - rcapa); + buf->len = len - rcapa; + } + else + { + HIO_MEMCPY (&buf->buf[buf->len], ptr, len); + buf->len += len; + } + + return 0; +} + +/* ------------------------------------------------------------------------- */ + static const hio_bch_t* file_get_mime_type (hio_svc_htts_t* htts, const hio_bch_t* qpath, const hio_bch_t* file_path, void* ctx) { const hio_bch_t* mt = HIO_NULL; @@ -99,6 +171,7 @@ static int file_open_dir_list (hio_svc_htts_t* htts, const hio_bch_t* qpath, con hio_bch_t file_path[] = "/tmp/.XXXXXX"; int fd = -1; struct dirent* de; + buff_t buf; if (ext->ai->file_load_index_page) { @@ -134,42 +207,66 @@ static int file_open_dir_list (hio_svc_htts_t* htts, const hio_bch_t* qpath, con unlink (file_path); - write (fd, "", 12); - if (!(qpath[0] == '\0' || (qpath[0] == '/' && qpath[1] == '\0'))) - write (fd, "
  • ..", 23); + buf.len = 0; + init_buff (&buf); + + if (write_buff_to_fd(fd, &buf, "", 12) <= -1) goto oops; + if (!(qpath[0] == '\0' || (qpath[0] == '/' && qpath[1] == '\0')) && + write_buff_to_fd(fd, &buf,"
  • ..", 23) <= -1) goto oops; -/* TODO: sorting, other informatino like size, */ -/* TODO: error handling of write() error */ while ((de = readdir(dp))) { struct stat st; - hio_bch_t* tmp_path; + hio_bch_t* tptr, * dend; + hio_bch_t tmp[1024]; /* this must be at least 5 characters large for html escaping */ + hio_oow_t tml; int n; if ((de->d_name[0] == '.' && de->d_name[1] == '\0') || (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0')) continue; - tmp_path = hio_svc_htts_dupmergepaths(htts, dir_path, de->d_name); - if (HIO_UNLIKELY(!tmp_path)) continue; - n = stat(tmp_path, &st); - hio_freemem (hio, tmp_path); + tptr = hio_svc_htts_dupmergepaths(htts, dir_path, de->d_name); + if (HIO_UNLIKELY(!tptr)) continue; + n = stat(tptr, &st); + hio_freemem (hio, tptr); if (HIO_UNLIKELY(n <= -1)) continue; - write (fd, "
  • d_name + strlen(de->d_name); + + if (write_buff_to_fd(fd, &buf, "
  • d_name; tptr < dend; ) { - char tmp[1000]; /* TODO:use dynamic buffer?? */ - hio_perenc_http_bcstr(0, de->d_name, tmp, HIO_NULL); /* url encoding */ - //write (fd, de->d_name, strlen(de->d_name)); - write (fd, tmp, strlen(tmp)); + hio_oow_t dlen = dend - tptr; + if (dlen > HIO_COUNTOF(tmp) / 3) dlen = HIO_COUNTOF(tmp) / 3; /* it can grow upto 3 folds */ + HIO_ASSERT (hio, dlen >= 1); + tml = hio_perenc_http_bchars(tptr, dlen, tmp, HIO_COUNTOF(tmp), 0); /* feed a chunk that won't overflow the buffer */ + HIO_ASSERT (hio, tml <= HIO_COUNTOF(tmp)); /* the buffer 'tmp' must be large enough */ + if (write_buff_to_fd(fd, &buf, tmp, tml) <= -1) goto oops; + tptr += dlen; } - if (S_ISDIR(st.st_mode)) write (fd, "/", 1); - write (fd, "\">", 2); - write (fd, de->d_name, strlen(de->d_name)); /* TODO: html entity encoding */ - if (S_ISDIR(st.st_mode)) write (fd, "/", 1); - write (fd, "", 4); + + if (S_ISDIR(st.st_mode) && write_buff_to_fd(fd, &buf, "/", 1) <= -1) goto oops; + if (write_buff_to_fd(fd, &buf, "\">", 2) <= -1) goto oops; + + dend = de->d_name + strlen(de->d_name); + for (tptr = de->d_name; tptr < dend; ) + { + hio_oow_t dlen = dend - tptr; + if (dlen > HIO_COUNTOF(tmp) / 5) dlen = HIO_COUNTOF(tmp) / 5; /* it can grow upto 5 folds */ + HIO_ASSERT (hio, dlen >= 1); + tml = hio_escape_html_bchars(tptr, dlen, tmp, HIO_COUNTOF(tmp)); /* feed a chunk that won't overflow the buffer */ + HIO_ASSERT (hio, tml <= HIO_COUNTOF(tmp)); /* the buffer 'tmp' must be large enough */ + if (write_buff_to_fd(fd, &buf, tmp, tml) <= -1) goto oops; + tptr += dlen; + } + + if (S_ISDIR(st.st_mode) && write_buff_to_fd(fd, &buf, "/", 1) <= -1) goto oops; + if (write_buff_to_fd(fd, &buf, "", 4) <= -1) goto oops; } - write (fd, "\n", 15); + if (write_buff_to_fd(fd, &buf, "\n", 15) <= -1) goto oops; + if (flush_buff_to_fd(fd, &buf) <= -1) goto oops; closedir (dp); lseek (fd, SEEK_SET, 0); diff --git a/lib/hio-http.h b/lib/hio-http.h index 4cf8f04..45f9ca4 100644 --- a/lib/hio-http.h +++ b/lib/hio-http.h @@ -257,26 +257,32 @@ HIO_EXPORT hio_oow_t hio_perdec_http_bcs ( /** * The hio_perenc_http_bcstr() function performs percent-encoding over a string. - * The caller must ensure that the output buffer \a buf is large enough. - * If \a nencs is not #HIO_NULL, it is set to the number of characters - * encoded. 0 means no characters in the input string required encoding. - * \return the length of the output string. + * It returns the length of the encoded string if \a len is long enough to hold + * the resulting string and the terminating null. If the return value is equal to + * or greater than \a len, the buffer pointed to by \a buf of the length \a len + * is not large enough. + * \return the length of the output string encoded on success or the number of encoded + * string that would have been written if the buffer has been large enough. */ HIO_EXPORT hio_oow_t hio_perenc_http_bcstr ( - int opt, /**< 0 or bitwise-OR'ed of #hio_perenc_http_bcstr_opt_t */ const hio_bch_t* str, hio_bch_t* buf, - hio_oow_t* nencs + hio_oow_t len, + int opt /**< 0 or bitwise-OR'ed of #hio_perenc_http_bcstr_opt_t */ ); -#if 0 -/* TODO: rename this function according to the naming convension */ -HIO_EXPORT hio_bch_t* hio_perenc_http_bcstrdup ( - int opt, /**< 0 or bitwise-OR'ed of #hio_perenc_http_bcstr_opt_t */ - const hio_bch_t* str, - hio_mmgr_t* mmgr +/** + * The hio_perenc_http_bchars() function performs percent-ending over a length-bound string. + * It doesn't null-terminate the result. The buffer requirement is less than hio_perenc_http_bcstr() + * by 1. + */ +HIO_EXPORT hio_oow_t hio_perenc_http_bchars ( + const hio_bch_t* str, + hio_oow_t sln, + hio_bch_t* buf, + hio_oow_t len, + int opt /**< 0 or bitwise-OR'ed of #hio_perenc_http_bcstr_opt_t */ ); -#endif HIO_EXPORT int hio_scan_http_qparam ( hio_bch_t* qparam, @@ -284,6 +290,18 @@ HIO_EXPORT int hio_scan_http_qparam ( void* ctx ); +HIO_EXPORT hio_oow_t hio_escape_html_bchars ( + const hio_bch_t* str, + hio_oow_t sln, + hio_bch_t* buf, + hio_oow_t len +); + +HIO_EXPORT hio_oow_t hio_escape_html_bcstr ( + const hio_bch_t* str, + hio_bch_t* buf, + hio_oow_t len +); /* ------------------------------------------------------------------------- */ /* HTTP SERVER SERVICE */ /* ------------------------------------------------------------------------- */ diff --git a/lib/http.c b/lib/http.c index d0a969a..abef080 100644 --- a/lib/http.c +++ b/lib/http.c @@ -499,87 +499,73 @@ hio_oow_t hio_perdec_http_bcs (const hio_bcs_t* str, hio_bch_t* buf, hio_oow_t* #define TO_HEX(v) ("0123456789ABCDEF"[(v) & 15]) -hio_oow_t hio_perenc_http_bcstr (int opt, const hio_bch_t* str, hio_bch_t* buf, hio_oow_t* nencs) +hio_oow_t hio_perenc_http_bchars (const hio_bch_t* str, hio_oow_t sln, hio_bch_t* buf, hio_oow_t len, int opt) { - const hio_bch_t* p = str; + const hio_bch_t* ptr, * end = str + sln; hio_bch_t* out = buf; - hio_oow_t enc_count = 0; + hio_bch_t slash; + hio_oow_t reqlen = 0; - /* this function doesn't accept the size of the buffer. the caller must - * ensure that the buffer is large enough */ + slash = (opt & HIO_PERENC_HTTP_KEEP_SLASH)? '/': '\0'; - if (opt & HIO_PERENC_HTTP_KEEP_SLASH) + for (ptr = str; ptr < end; ptr++) { - while (*p != '\0') + reqlen += (IS_UNRESERVED(*ptr) || *ptr == slash)? 1: 3; + } + + if (len >= reqlen) + { + ptr = str; + while (ptr < end) { - if (IS_UNRESERVED(*p) || *p == '/') *out++ = *p; + if (IS_UNRESERVED(*ptr) || *ptr == slash) *out++ = *ptr; else { *out++ = '%'; - *out++ = TO_HEX (*p >> 4); - *out++ = TO_HEX (*p & 15); - enc_count++; + *out++ = TO_HEX(*ptr >> 4); + *out++ = TO_HEX(*ptr & 15); } - p++; + ptr++; } } - else - { - while (*p != '\0') - { - if (IS_UNRESERVED(*p)) *out++ = *p; - else - { - *out++ = '%'; - *out++ = TO_HEX (*p >> 4); - *out++ = TO_HEX (*p & 15); - enc_count++; - } - p++; - } - } - *out = '\0'; - if (nencs) *nencs = enc_count; - return out - buf; + + return reqlen; } -#if 0 -hio_bch_t* hio_perenc_http_bcstrdup (int opt, const hio_bch_t* str, hio_mmgr_t* mmgr) +hio_oow_t hio_perenc_http_bcstr (const hio_bch_t* str, hio_bch_t* buf, hio_oow_t len, int opt) { - hio_bch_t* buf; - hio_oow_t len = 0; - hio_oow_t count = 0; - - /* count the number of characters that should be encoded */ - if (opt & HIO_PERENC_HTTP_KEEP_SLASH) + const hio_bch_t* ptr = str; + hio_bch_t* out = buf; + hio_bch_t slash; + hio_oow_t reqlen = 0; + + slash = (opt & HIO_PERENC_HTTP_KEEP_SLASH)? '/': '\0'; + + for (ptr = str; *ptr != '\0'; ptr++) { - for (len = 0; str[len] != '\0'; len++) - { - if (!IS_UNRESERVED(str[len]) && str[len] != '/') count++; - } - } - else - { - for (len = 0; str[len] != '\0'; len++) - { - if (!IS_UNRESERVED(str[len])) count++; - } + reqlen += (IS_UNRESERVED(*ptr) || *ptr == slash)? 1: 3; } - /* if there are no characters to escape, just return the original string */ - if (count <= 0) return (hio_bch_t*)str; + if (len > reqlen) + { + ptr = str; + while (*ptr != '\0') + { + if (IS_UNRESERVED(*ptr) || *ptr == slash) *out++ = *ptr; + else + { + *out++ = '%'; + *out++ = TO_HEX(*ptr >> 4); + *out++ = TO_HEX(*ptr & 15); + } + ptr++; + } - /* allocate a buffer of an optimal size for escaping, otherwise */ - buf = HIO_MMGR_ALLOC(mmgr, (len + (count * 2) + 1) * HIO_SIZEOF(*buf)); - if (!buf) return HIO_NULL; + *out = '\0'; + } - /* perform actual escaping */ - hio_perenc_http_bcstr (opt, str, buf, HIO_NULL); - - return buf; + return reqlen; } -#endif - int hio_scan_http_qparam (hio_bch_t* qparam, int (*qparamcb) (hio_bcs_t* key, hio_bcs_t* val, void* ctx), void* ctx) { @@ -638,3 +624,116 @@ int hio_scan_http_qparam (hio_bch_t* qparam, int (*qparamcb) (hio_bcs_t* key, hi return 0; } + +hio_oow_t hio_escape_html_bchars (const hio_bch_t* str, hio_oow_t sln, hio_bch_t* buf, hio_oow_t len) +{ + hio_bch_t* ptr, * end = str + sln; + hio_oow_t reqlen = 0; + + for (ptr = (hio_bch_t*)str; ptr < end; ptr++) + { + switch (*ptr) + { + case '<': + case '>': + reqlen += 4; + break; + + case '&': + reqlen += 5; + break; + + default: + reqlen++; + break; + } + } + + if (len >= reqlen) + { + /* the buffer is large enough */ + ptr = buf; + while (str < end) + { + switch (*str) + { + case '<': + *ptr++ = '&'; *ptr++ = 'l'; *ptr++ = 't'; *ptr++ = ';'; + break; + + case '>': + *ptr++ = '&'; *ptr++ = 'g'; *ptr++ = 't'; *ptr++ = ';'; + break; + + case '&': + *ptr++ = '&'; *ptr++ = 'a'; *ptr++ = 'm'; *ptr++ = 'p'; *ptr++ = ';'; + break; + + default: + *ptr++ = *str; + break; + } + str++; + } + } + + /* NOTE no null termination */ + return reqlen; +} + +hio_oow_t hio_escape_html_bcstr (const hio_bch_t* str, hio_bch_t* buf, hio_oow_t len) +{ + hio_bch_t* ptr; + hio_oow_t reqlen = 0; + + for (ptr = (hio_bch_t*)str; *ptr != '\0'; ptr++) + { + switch (*ptr) + { + case '<': + case '>': + reqlen += 4; + break; + + case '&': + reqlen += 5; + break; + + default: + reqlen++; + break; + } + } + + if (len > reqlen) + { + /* the buffer is large enough */ + ptr = buf; + while (*str != '\0') + { + switch (*str) + { + case '<': + *ptr++ = '&'; *ptr++ = 'l'; *ptr++ = 't'; *ptr++ = ';'; + break; + + case '>': + *ptr++ = '&'; *ptr++ = 'g'; *ptr++ = 't'; *ptr++ = ';'; + break; + + case '&': + *ptr++ = '&'; *ptr++ = 'a'; *ptr++ = 'm'; *ptr++ = 'p'; *ptr++ = ';'; + break; + + default: + *ptr++ = *str; + break; + } + str++; + } + + *ptr = '\0'; + } + + return reqlen; +}