#include #include #include #include #include #include #include #include #include #include #include #define STDIN 0 #define STDOUT 1 #define BUF_LEN 4096 #define MAXCONTENTLEN 50*1024*1024 /* 50 megs */ #define PATH_INCOMING "/srv/http/intra.ogris.net/incoming" #define URL_ERR_TECH_PROBS "../errors/err_upl_tech.html" #define URL_ERR_FILE2BIG "../errors/err_upl_f2b.html" #define URL_ERR_INVALID_METHOD "../errors/err_upl_inv.html" #define URL_UPLOAD_OK "../upload-ok.html" size_t str_len (char *s) { register size_t len; for (len = 0; *s; ++len, ++s) ;; return len; } int str_eq (char *s1, char *s2) { for (; *s1 && *s2; ++s1, ++s2) if (*s1 != *s2) return 0; return 1; } uint64_t str2uint (char *s) { uint64_t ret; for (ret = 0; *s; ++s) { if (*s < '0' || *s > '9') break; ret = ret * 10 + *s - '0'; } return ret; } char *uint2str (uint64_t i, char *s) { register uint64_t tmp; char *ret; tmp = i; do { tmp /= 10; ++s; } while (tmp); ret = s; *s-- = 0; tmp = i; do { *s-- = (tmp % 10) + '0'; tmp /= 10; } while (tmp); return ret; } char *str_find_x (char *needle, size_t len2, char *haystack, size_t len1) { size_t len_tmp, len_tmp2; ssize_t len_diff; char *tmp1, *tmp2; len_diff = len1 - len2; tmp1 = haystack; for (len_tmp = 0; (ssize_t) len_tmp <= len_diff; ++tmp1, ++len_tmp) { for (tmp2 = tmp1, len_tmp2 = 0; len_tmp2 < len2; ++tmp2, ++len_tmp2) { if (*tmp2 != *(needle + len_tmp2)) break; } if (len_tmp2 == len2) return tmp2; } return 0; } char *str_find (char *needle, char *haystack) { return str_find_x(needle, str_len(needle), haystack, str_len(haystack)); } size_t str_addchar (char *s, char c) { size_t ret; ret = str_len(s); *(s + ret++) = c; *(s + ret) = 0; return ret; } char *str_addstr_x (char *dest, size_t len, char *s) { len -= str_len(dest) + 1; while (*dest) ++dest; if (s) while (len && *s) { *dest++ = *s++; --len; } *dest = 0; return dest; } void print (char *s) { write(STDOUT, s, str_len(s)); } void location (char *url) { print("Location: "); print(url); print("\n\n"); } #define err(url,format,args...) { \ syslog(LOG_ERR, format, ## args); \ _err(url); \ } void _err(char *url) { closelog(); location((url)?(url):(URL_ERR_TECH_PROBS)); exit(1); } char *add_date_pos (char *c, int i) { str_addchar(c++, '.'); if (i < 10) str_addchar(c++, '0'); return uint2str(i, c); } int main (int argc, char **argv, char **env) { uint64_t boundary_len, content_len, hdr_len, data_len, len, speed; ssize_t ret; time_t diff, start; pid_t pid; struct tm *now; int post, fh; char **key, *value, *boundary, *content, *hdr, *data, *end, fn_buf[BUF_LEN], *tmp, *rh, *ra, *req_fn, *fn, *proctitle, *content_length, *cookie; fh = 0; pid = 1; len = str_len(argv[0]); if (argc < 1) fh = 1; else fh = (len < BUF_LEN - 1) || (argc > 1); if (fh) { pid = fork(); if (pid > 0) return 0; if (!pid) { for (tmp = str_addstr_x(fn_buf, BUF_LEN, argv[0]); len < BUF_LEN - 1; ++len) *tmp++ = ' '; fn_buf[BUF_LEN - 1] = '\0'; closelog(); execve(argv[0], (char*[]) { fn_buf, NULL }, env); } fh = errno; } openlog(argv[0], LOG_PID, LOG_DAEMON); if (pid <= 0) err(NULL, (pid < 0)?("fork(): %s"):("execve(): %s"), strerror(fh)); close(2); umask(0133); if (chdir(PATH_INCOMING)) err(NULL, PATH_INCOMING ": %s", strerror(errno)); content_len = (uint64_t) -1; post = 0; boundary = NULL; content = NULL; content_length = NULL; ra = NULL; rh = NULL; cookie = NULL; proctitle = argv[0] + 6; *proctitle = '\0'; for (key = env; *key; ++key) { for (value = *key; *value; ++value) { if (*value == '=') { *value++ = '\0'; break; } } if (str_eq("QUERY_STRING", *key)) cookie = value; if (str_eq("REMOTE_ADDR", *key)) ra = value; if (str_eq("REMOTE_HOST", *key)) rh = value; if (str_eq("REQUEST_METHOD", *key) && str_eq("POST", value)) post = 1; if (str_eq("CONTENT_LENGTH", *key)) { content_len = str2uint(value); content_length = value; } if (str_eq("CONTENT_TYPE", *key)) { boundary = str_find("boundary=", value); *(--boundary) = '-'; *(--boundary) = '-'; } } syslog(LOG_NOTICE, "addr:%s host:%s clen:%s", (ra)?(ra):("?"), (rh)?(rh):("?"), (content_length)?(content_length):("?")); if (!post) err(URL_ERR_INVALID_METHOD, "invalid request method"); if (content_len == (uint64_t) -1) err(NULL, "no content length"); if (!boundary) err(NULL, "no boundary"); boundary_len = str_len(boundary); if (content_len > MAXCONTENTLEN) { close(STDIN); err(URL_ERR_FILE2BIG, "content too big:%s", content_length); } content = malloc(content_len); if (!content) err(NULL, "no memory!"); str_addstr_x(proctitle, BUF_LEN, ": cookie:"); if (cookie) str_addstr_x(proctitle, BUF_LEN, cookie); str_addstr_x(proctitle, BUF_LEN, " addr:"); if (ra) str_addstr_x(proctitle, BUF_LEN, ra); str_addstr_x(proctitle, BUF_LEN, " host:"); if (rh) str_addstr_x(proctitle, BUF_LEN, rh); str_addstr_x(proctitle, BUF_LEN, " total:"); str_addstr_x(proctitle, BUF_LEN, content_length); fh = 4080 - str_len(proctitle); proctitle = str_addstr_x(proctitle, BUF_LEN, " read:"); start = time(NULL); for (len = 0; len < content_len; len += ret) { ret = read(STDIN, content + len, content_len - len); if (ret < 0) err(NULL, "read(): %s", strerror(errno)); uint2str(len, proctitle); uint2str((len * 100) / content_len, str_addstr_x(proctitle, fh, " percent:")); diff = time(NULL) - start; if (diff <= 0) continue; uint2str(diff, str_addstr_x(proctitle, fh, " running:")); speed = len / diff; uint2str(speed, str_addstr_x(proctitle, fh, " speed:")); if (len <= 0) continue; uint2str((content_len - len) / speed, str_addstr_x(proctitle, fh, " eta:")); } close(STDIN); hdr = str_find_x(boundary, boundary_len, content, content_len); if (!hdr) err(NULL, "no starting boundary"); fn = fn_buf; now = localtime(&start); now->tm_year += 1900; now->tm_mon++; fn = uint2str(now->tm_year, fn); fn = add_date_pos(fn, now->tm_mon); fn = add_date_pos(fn, now->tm_mday); fn = add_date_pos(fn, now->tm_hour); fn = add_date_pos(fn, now->tm_min); fn = add_date_pos(fn, now->tm_sec); str_addchar(fn++, '+'); fn = uint2str(getpid(), fn); str_addchar(fn++, '+'); fn = str_addstr_x(fn_buf, BUF_LEN, ra); str_addchar(fn++, '+'); fn = str_addstr_x(fn_buf, BUF_LEN, rh); str_addchar(fn++, '+'); for (post = 0;; hdr = end, ++post) { /* strip "\r\n" after starting boundary */ hdr += 2; len = content_len - (hdr - content); if (len <= 0) err(NULL, "length of content smaller than length of boundary"); end = str_find_x(boundary, boundary_len, hdr, len); if (!end) err(NULL, "no trailing boundary"); if (end >= content + content_len - 1) err(NULL, "trailing boundary exceeds content"); /* strip "\r\n" before closing boundary */ len = (end - hdr) - boundary_len - 2; if (len <= 0) err(NULL, "length of content smaller than trailing boundary"); data = str_find_x("\r\n\r\n", 4, hdr, len); if (!data) continue; data_len = len - (data - hdr); hdr_len = data - hdr - 4; req_fn = str_find_x("ename=\"", 7, hdr, hdr_len); if (req_fn) { for (tmp = req_fn; *tmp && (*tmp != '"'); ++tmp) { if (tmp >= end) err(NULL, "filename exceeds content"); if (!(*tmp >= '0' && *tmp <= '9') && !(*tmp >= 'A' && *tmp <= 'Z') && !(*tmp >= 'a' && *tmp <= 'z') && *tmp != '.' && *tmp != '-' && *tmp != '_' && *tmp != '(' && *tmp != ')') *tmp = '_'; } *tmp = '\0'; str_addstr_x(str_addstr_x(uint2str(post, fn), 4076, "+"), 4075 - (fn - fn_buf), req_fn); } else uint2str(post, fn); if (data_len) { fh = open(fn_buf, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (fh < 0) err(NULL, "open(): %s: %s", fn_buf, strerror(errno)); ret = write(fh, data, data_len); if (ret == -1) err(NULL, "write(): %s: %s", fn_buf, strerror(errno)); if (ret != (signed) data_len) err(NULL, "write(): %s: could not write whole content", fn_buf); if (close(fh) == -1) err(NULL, "close(): %s: %s", fn_buf, strerror(errno)); syslog(LOG_NOTICE, "%s: %llu bytes written", fn_buf, data_len); } if (*end == '-' && *(end + 1) == '-') break; } closelog(); location(URL_UPLOAD_OK); return 0; }