123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488 |
- /*
- upload.c -- File upload handler
- Copyright (c) All Rights Reserved. See details at the end of the file.
- */
- /*********************************** Includes *********************************/
- #include "goahead.h"
- #if ME_GOAHEAD_UPLOAD
- /************************************ Locals **********************************/
- /*
- Upload states
- */
- #define UPLOAD_REQUEST_HEADER 1 /* Request header */
- #define UPLOAD_BOUNDARY 2 /* Boundary divider */
- #define UPLOAD_CONTENT_HEADER 3 /* Content part header */
- #define UPLOAD_CONTENT_DATA 4 /* Content encoded data */
- #define UPLOAD_CONTENT_END 5 /* End of multipart message */
- static char *uploadDir;
- /*********************************** Forwards *********************************/
- static void defineUploadVars(Webs *wp);
- static char *getBoundary(Webs *wp, char *buf, ssize bufLen);
- static void initUpload(Webs *wp);
- static void processContentBoundary(Webs *wp, char *line);
- static bool processContentData(Webs *wp);
- static void processUploadHeader(Webs *wp, char *line);
- /************************************* Code ***********************************/
- /*
- The upload handler functions as a filter.
- Return false because the upload handler is not a terminal handler.
- */
- static bool uploadHandler(Webs *wp)
- {
- return 0;
- }
- static void initUpload(Webs *wp)
- {
- char *boundary;
- if (wp->uploadState == 0) {
- wp->uploadState = UPLOAD_BOUNDARY;
- if ((boundary = strstr(wp->contentType, "boundary=")) != 0) {
- boundary += 9;
- wfree(wp->boundary);
- wp->boundary = sfmt("--%s", boundary);
- wp->boundaryLen = strlen(wp->boundary);
- }
- if (wp->boundaryLen == 0 || *wp->boundary == '\0') {
- websError(wp, HTTP_CODE_BAD_REQUEST, "Bad boundary");
- } else {
- websSetVar(wp, "UPLOAD_DIR", uploadDir);
- wp->files = hashCreate(11);
- }
- }
- }
- static void freeUploadFile(WebsUpload *up)
- {
- if (up) {
- if (up->filename) {
- unlink(up->filename);
- wfree(up->filename);
- }
- wfree(up->clientFilename);
- wfree(up->contentType);
- wfree(up);
- }
- }
- PUBLIC void websFreeUpload(Webs *wp)
- {
- WebsUpload *up;
- WebsKey *s;
- if (wp->files >= 0) {
- for (s = hashFirst(wp->files); s; s = hashNext(wp->files, s)) {
- up = s->content.value.symbol;
- freeUploadFile(up);
- if (up == wp->currentFile) {
- wp->currentFile = 0;
- }
- }
- hashFree(wp->files);
- }
- if (wp->currentFile) {
- freeUploadFile(wp->currentFile);
- wp->currentFile = 0;
- }
- if (wp->upfd >= 0) {
- close(wp->upfd);
- wp->upfd = -1;
- }
- }
- PUBLIC bool websProcessUploadData(Webs *wp)
- {
- char *line, *nextTok;
- ssize len, nbytes;
- bool canProceed;
- line = 0;
- canProceed = 1;
- while (canProceed && !wp->finalized && wp->uploadState != UPLOAD_CONTENT_END) {
- if (wp->uploadState == UPLOAD_BOUNDARY || wp->uploadState == UPLOAD_CONTENT_HEADER) {
- /*
- Parse the next input line
- */
- line = wp->input.servp;
- if ((nextTok = memchr(line, '\n', bufLen(&wp->input))) == 0) {
- /* Incomplete line */
- canProceed = 0;
- break;
- }
- *nextTok++ = '\0';
- nbytes = nextTok - line;
- websConsumeInput(wp, nbytes);
- strim(line, "\r", WEBS_TRIM_END);
- len = strlen(line);
- if (line[len - 1] == '\r') {
- line[len - 1] = '\0';
- }
- }
- switch (wp->uploadState) {
- printf("wp->uploadState = %d\n",wp->uploadState);
- case 0:
- initUpload(wp);
- break;
- case UPLOAD_BOUNDARY:
- processContentBoundary(wp, line);
- break;
- case UPLOAD_CONTENT_HEADER:
- processUploadHeader(wp, line);
- break;
- case UPLOAD_CONTENT_DATA:
- canProceed = processContentData(wp);
- if (bufLen(&wp->input) < wp->boundaryLen) {
- /* Incomplete boundary - return to get more data */
- canProceed = 0;
- }
- break;
- case UPLOAD_CONTENT_END:
- break;
- }
- }
- bufCompact(&wp->input);
- return canProceed;
- }
- static void processContentBoundary(Webs *wp, char *line)
- {
- /*
- Expecting a multipart boundary string
- */
- if (strncmp(wp->boundary, line, wp->boundaryLen) != 0) {
- websError(wp, HTTP_CODE_BAD_REQUEST, "Bad upload state. Incomplete boundary");
- } else if (line[wp->boundaryLen] && strcmp(&line[wp->boundaryLen], "--") == 0) {
- wp->uploadState = UPLOAD_CONTENT_END;
- } else {
- wp->uploadState = UPLOAD_CONTENT_HEADER;
- }
- }
- static void processUploadHeader(Webs *wp, char *line)
- {
- WebsUpload *file;
- char *key, *headerTok, *rest, *nextPair, *value;
- if (line[0] == '\0') {
- wp->uploadState = UPLOAD_CONTENT_DATA;
- return;
- }
- trace(7, "Header line: %s", line);
- headerTok = line;
- stok(line, ": ", &rest);
- if (scaselesscmp(headerTok, "Content-Disposition") == 0) {
- /*
- The content disposition header describes either a form variable or an uploaded file.
- Content-Disposition: form-data; name="field1"
- >>blank line
- Field Data
- ---boundary
- Content-Disposition: form-data; name="field1" filename="user.file"
- >>blank line
- File data
- ---boundary
- */
- key = rest;
- wfree(wp->uploadVar);
- wfree(wp->clientFilename);
- wp->uploadVar = wp->clientFilename = 0;
- while (key && stok(key, ";\r\n", &nextPair)) {
- key = strim(key, " ", WEBS_TRIM_BOTH);
- ssplit(key, "= ", &value);
- value = strim(value, "\"", WEBS_TRIM_BOTH);
- if (scaselesscmp(key, "form-data") == 0) {
- /* Nothing to do */
- } else if (scaselesscmp(key, "name") == 0) {
- wfree(wp->uploadVar);
- wp->uploadVar = sclone(value);
- printf("log1: name: %s\n", wp->uploadVar);
- } else if (scaselesscmp(key, "filename") == 0) {
- if (wp->uploadVar == 0) {
- websError(wp, HTTP_CODE_BAD_REQUEST, "Bad upload state. Missing name field");
- return;
- }
- value = websNormalizeUriPath(value);
- if (*value == '.' || !websValidUriChars(value) || strpbrk(value, "\\/:*?<>|~\"'%`^\n\r\t\f")) {
- websError(wp, HTTP_CODE_INTERNAL_SERVER_ERROR, "Bad upload client filename");
- wfree(value);
- return;
- }
- wfree(wp->clientFilename);
- wp->clientFilename = value;
- /*
- Create the file to hold the uploaded data
- */
- wfree(wp->uploadTmp);
- if ((wp->uploadTmp = websTempFile(uploadDir, "tmp")) == 0) {
- websError(wp, HTTP_CODE_INTERNAL_SERVER_ERROR,
- "Cannot create upload temp file %s. Check upload temp dir %s", wp->uploadTmp, uploadDir);
- return;
- }
- trace(5, "File upload of: %s stored as %s", wp->clientFilename, wp->uploadTmp);
- if ((wp->upfd = open(wp->uploadTmp, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0600)) < 0) {
- websError(wp, HTTP_CODE_INTERNAL_SERVER_ERROR, "Cannot open upload temp file %s", wp->uploadTmp);
- return;
- }
- /*
- Create the files[id]
- */
- freeUploadFile(wp->currentFile);
- file = wp->currentFile = walloc(sizeof(WebsUpload));
- memset(file, 0, sizeof(WebsUpload));
- file->clientFilename = sclone(wp->clientFilename);
- file->filename = sclone(wp->uploadTmp);
- }
- key = nextPair;
- }
- } else if (scaselesscmp(headerTok, "Content-Type") == 0) {
- if (wp->clientFilename) {
- trace(5, "Set files[%s][CONTENT_TYPE] = %s", wp->uploadVar, rest);
- wp->currentFile->contentType = sclone(rest);
- }
- }
- }
- static void defineUploadVars(Webs *wp)
- {
- WebsUpload *file;
- char key[64];
- file = wp->currentFile;
- fmt(key, sizeof(key), "FILE_CLIENT_FILENAME_%s", wp->uploadVar);
- websSetVar(wp, key, file->clientFilename);
- fmt(key, sizeof(key), "FILE_CONTENT_TYPE_%s", wp->uploadVar);
- websSetVar(wp, key, file->contentType);
- fmt(key, sizeof(key), "FILE_FILENAME_%s", wp->uploadVar);
- websSetVar(wp, key, file->filename);
- fmt(key, sizeof(key), "FILE_SIZE_%s", wp->uploadVar);
- websSetVarFmt(wp, key, "%d", (int) file->size);
- }
- static int writeToFile(Webs *wp, char *data, ssize len)
- {
- WebsUpload *file;
- ssize rc;
- file = wp->currentFile;
- //printf("===file->size %d >>>len %d >> ME_GOAHEAD_LIMIT_UPLOAD %d ====\n", file->size, len, ME_GOAHEAD_LIMIT_UPLOAD);
- //printf("- %d\n", file->size);
- if ((file->size + len) > ME_GOAHEAD_LIMIT_UPLOAD) {
- websError(wp, HTTP_CODE_REQUEST_TOO_LARGE, "Uploaded file exceeds maximum %d", (int) ME_GOAHEAD_LIMIT_UPLOAD);
- return -1;
- }
- if (len > 0) {
- /*
- File upload. Write the file data.
- */
- if ((rc = write(wp->upfd, data, (int) len)) != len) {
- websError(wp, HTTP_CODE_INTERNAL_SERVER_ERROR, "Cannot write to upload temp file %s, rc %d", wp->uploadTmp, rc);
- return -1;
- }
- file->size += len;
- trace(7, "uploadFilter: Wrote %d bytes to %s", len, wp->uploadTmp);
- }
- return 0;
- }
- static bool processContentData(Webs *wp)
- {
- WebsUpload *file;
- WebsBuf *content;
- ssize size, nbytes;
- char *data, *bp;
- content = &wp->input;
- file = wp->currentFile;
- // printf("---> filename: %s, clientFilename: %s, contentType: %s, size: %d\n",
- // file->filename, file->clientFilename, file->contentType, size);
- // printf("---> buf: %p, servp: %p, endp: %p, endbuf: %p, buflen: %d, maxsize: %d\n",
- // content->buf, content->servp, content->endp, content->endbuf, content->buflen, content->maxsize);
- size = bufLen(content);
- if (size < wp->boundaryLen) {
- /* Incomplete boundary. Return and get more data */
- return 0;
- }
- if ((bp = getBoundary(wp, content->servp, size)) == 0) {
- trace(7, "uploadFilter: Got boundary filename %x", wp->clientFilename);
- if (wp->clientFilename) {
- /*
- No signature found yet. probably more data to come. Must handle split boundaries.
- */
- data = content->servp;
- nbytes = ((int) (content->endp - data)) - (wp->boundaryLen - 1);
- if (writeToFile(wp, content->servp, nbytes) < 0) {
- /* Proceed to handle error */
- printf("write >>>>>>>>>>>>>hander error\n");
- return 1;
- }
- websConsumeInput(wp, nbytes);
- /* Get more data */
- return 0;
- }
- }
- data = content->servp;
- nbytes = (bp) ? (bp - data) : bufLen(content);
- if (nbytes > 0) {
- websConsumeInput(wp, nbytes);
- /*
- This is the CRLF before the boundary
- */
- if (nbytes >= 2 && data[nbytes - 2] == '\r' && data[nbytes - 1] == '\n') {
- nbytes -= 2;
- }
- if (wp->clientFilename) {
- /*
- Write the last bit of file data and add to the list of files and define environment variables
- */
- if (writeToFile(wp, data, nbytes) < 0) {
- /* Proceed to handle error */
- return 1;
- }
- hashEnter(wp->files, wp->uploadVar, valueSymbol(file), 0);
- defineUploadVars(wp);
- } else {
- /*
- Normal string form data variables
- */
- data[nbytes] = '\0';
- trace(5, "uploadFilter: form[%s] = %s", wp->uploadVar, data);
- websDecodeUrl(wp->uploadVar, wp->uploadVar, -1);
- websDecodeUrl(data, data, -1);
- websSetVar(wp, wp->uploadVar, data);
- }
- }
- if (wp->clientFilename) {
- /*
- Now have all the data (we've seen the boundary)
- */
- close(wp->upfd);
- wp->upfd = -1;
- wfree(wp->clientFilename);
- wp->clientFilename = 0;
- wfree(wp->uploadTmp);
- wp->uploadTmp = 0;
- }
- wp->uploadState = UPLOAD_BOUNDARY;
- return 1;
- }
- /*
- Find the boundary signature in memory. Returns pointer to the first match.
- */
- static char *getBoundary(Webs *wp, char *buf, ssize bufLen)
- {
- char *cp, *endp;
- char first;
- assert(buf);
- first = *wp->boundary;
- cp = buf;
- if (bufLen < wp->boundaryLen) {
- return 0;
- }
- endp = cp + (bufLen - wp->boundaryLen) + 1;
- while (cp < endp) {
- cp = (char *) memchr(cp, first, endp - cp);
- if (!cp) {
- return 0;
- }
- if (memcmp(cp, wp->boundary, wp->boundaryLen) == 0) {
- return cp;
- }
- cp++;
- }
- return 0;
- }
- WebsUpload *websLookupUpload(Webs *wp, char *key)
- {
- WebsKey *sp;
- if (wp->files >= 0) {
- if ((sp = hashLookup(wp->files, key)) == 0) {
- return 0;
- }
- return sp->content.value.symbol;
- }
- return 0;
- }
- WebsHash websGetUpload(Webs *wp)
- {
- return wp->files;
- }
- PUBLIC void websUploadOpen()
- {
- uploadDir = ME_GOAHEAD_UPLOAD_DIR;
- if (*uploadDir == '\0') {
- #if ME_WIN_LIKE
- uploadDir = getenv("TEMP");
- #else
- uploadDir = "/tmp";
- #endif
- }
- trace(4, "Upload directory is %s", uploadDir);
- websDefineHandler("upload", 0, uploadHandler, 0, 0);
- }
- #endif /* ME_GOAHEAD_UPLOAD */
- /*
- Copyright (c) Embedthis Software. All Rights Reserved.
- This software is distributed under commercial and open source licenses.
- You may use the Embedthis GoAhead open source license or you may acquire
- a commercial license from Embedthis Software. You agree to be fully bound
- by the terms of either license. Consult the LICENSE.md distributed with
- this software for full details and other copyrights.
- */
|