| /* |
| * formdata.c -- x-www-form-urlencoded and multipart/form-data |
| * (file upload) handling for websh3 |
| * nca-073-9 |
| * |
| * Copyright (c) 1996-2000 by Netcetera AG. |
| * Copyright (c) 2001 by Apache Software Foundation. |
| * All rights reserved. |
| * |
| * See the file "license.terms" for information on usage and |
| * redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES. |
| * |
| * @(#) $Id$ |
| * |
| */ |
| |
| #include <tcl.h> |
| #include "request.h" |
| #include "paramlist.h" |
| #include "webutl.h" |
| #include "log.h" |
| #include "macros.h" |
| #include "varchannel.h" |
| |
| |
| /* ---------------------------------------------------------------------------- |
| * parseUrlEncodedFormData -- |
| * parse "k1=v1&k2=v2" kind of data, and store in web::formvar structure |
| * ------------------------------------------------------------------------- */ |
| int parseUrlEncodedFormData(RequestData * requestData, Tcl_Interp * interp, |
| char *channelName, Tcl_Obj * len) |
| { |
| Tcl_Obj *tclo = NULL; |
| int tRes = 0; |
| int listLen = -1; |
| Tcl_Obj *cmdList[2]; |
| Tcl_Obj *uriCmd = NULL; |
| Tcl_Obj *formData = NULL; |
| Tcl_Channel channel; |
| int mode; |
| int readToEnd = 0; |
| int content_length = 0; |
| Tcl_DString translation; |
| Tcl_DString encoding; |
| |
| channel = Web_GetChannelOrVarChannel(interp, channelName, &mode); |
| if (channel == NULL) { |
| LOG_MSG(interp, WRITE_LOG, __FILE__, __LINE__, |
| "web::dispatch -postdata", WEBLOG_WARNING, |
| "error getting channel \"", channelName, "\"", NULL); |
| return TCL_ERROR; |
| } |
| |
| if ((mode & TCL_READABLE) == 0) { |
| |
| LOG_MSG(interp, WRITE_LOG, __FILE__, __LINE__, |
| "web::dispatch -postdata", WEBLOG_WARNING, |
| "channel \"", channelName, "\" not open for reading", NULL); |
| |
| /* unregister if was a varchannel */ |
| Web_UnregisterVarChannel(interp, channelName, channel); |
| |
| return TCL_ERROR; |
| } |
| |
| Tcl_DStringInit(&translation); |
| Tcl_DStringInit(&encoding); |
| Tcl_GetChannelOption(interp, channel, "-translation", &translation); |
| Tcl_GetChannelOption(interp, channel, "-encoding", &encoding); |
| Tcl_SetChannelOption(interp, channel, "-translation", "binary"); |
| |
| /* ------------------------------------------------------------------------ |
| * how much to read ? |
| * --------------------------------------------------------------------- */ |
| if (len == NULL) { |
| |
| readToEnd = 1; |
| |
| } |
| else { |
| |
| if (strcmp(Tcl_GetString(len), "end") == 0) { |
| |
| readToEnd = 1; |
| |
| } |
| else { |
| |
| readToEnd = 0; |
| |
| if (Tcl_GetIntFromObj(interp, len, &content_length) != TCL_OK) { |
| |
| Tcl_SetChannelOption(interp, channel, "-translation", Tcl_DStringValue(&translation)); |
| Tcl_SetChannelOption(interp, channel, "-encoding", Tcl_DStringValue(&encoding)); |
| Tcl_DStringFree(&translation); |
| Tcl_DStringFree(&encoding); |
| /* unregister if was a varchannel */ |
| Web_UnregisterVarChannel(interp, channelName, channel); |
| return TCL_ERROR; |
| } |
| } |
| } |
| |
| /* ------------------------------------------------------------------------ |
| * ok, read |
| * --------------------------------------------------------------------- */ |
| formData = Tcl_NewObj(); |
| Tcl_IncrRefCount(formData); |
| |
| if (readToEnd) { |
| |
| /* try to read to the end */ |
| /* append flag */ |
| while (Tcl_ReadChars(channel, formData, 4096, 1) != -1) { |
| if (Tcl_Eof(channel)) |
| break; |
| } |
| |
| } |
| else { |
| |
| if (Tcl_ReadChars(channel, formData, content_length, 1) == TCL_ERROR) { |
| |
| LOG_MSG(interp, WRITE_LOG, __FILE__, __LINE__, |
| "web::dispatch -postdata", WEBLOG_WARNING, |
| "error reading from \"", channelName, "\"", NULL); |
| |
| Tcl_DecrRefCount(formData); |
| |
| Tcl_SetChannelOption(interp, channel, "-translation", Tcl_DStringValue(&translation)); |
| Tcl_SetChannelOption(interp, channel, "-encoding", Tcl_DStringValue(&encoding)); |
| Tcl_DStringFree(&translation); |
| Tcl_DStringFree(&encoding); |
| /* unregister if was a varchannel */ |
| Web_UnregisterVarChannel(interp, channelName, channel); |
| |
| return TCL_ERROR; |
| } |
| } |
| |
| Tcl_SetChannelOption(interp, channel, "-translation", Tcl_DStringValue(&translation)); |
| Tcl_SetChannelOption(interp, channel, "-encoding", Tcl_DStringValue(&encoding)); |
| Tcl_DStringFree(&translation); |
| Tcl_DStringFree(&encoding); |
| /* unregister if was a varchannel */ |
| Web_UnregisterVarChannel(interp, channelName, channel); |
| |
| cmdList[0] = Tcl_NewStringObj("web::uri2list", -1); |
| cmdList[1] = Tcl_DuplicateObj(formData); |
| Tcl_IncrRefCount(cmdList[0]); |
| Tcl_IncrRefCount(cmdList[1]); |
| uriCmd = Tcl_NewListObj(2, cmdList); |
| Tcl_IncrRefCount(uriCmd); |
| tRes = Tcl_EvalObjEx(interp, uriCmd, TCL_EVAL_DIRECT); |
| Tcl_DecrRefCount(uriCmd); |
| Tcl_DecrRefCount(cmdList[0]); |
| Tcl_DecrRefCount(cmdList[1]); |
| |
| |
| Tcl_DecrRefCount(formData); |
| |
| if (tRes == TCL_ERROR) { |
| LOG_MSG(interp, WRITE_LOG, __FILE__, __LINE__, |
| "web::dispatch -postdata", WEBLOG_WARNING, |
| "error parsing formdata", NULL); |
| |
| return TCL_ERROR; |
| } |
| |
| tclo = Tcl_GetObjResult(interp); |
| Tcl_IncrRefCount(tclo); |
| Tcl_ResetResult(interp); |
| |
| /* -------------------------------------------------------------------------- |
| * only add if list length > 0 |
| * ----------------------------------------------------------------------- */ |
| if ((listLen = tclGetListLength(interp, tclo)) == -1) { |
| Tcl_DecrRefCount(tclo); |
| return TCL_ERROR; |
| } |
| |
| if (listLen > 0) { |
| /* ------------------------------------------------------------------------ |
| * add list to requestData |
| * --------------------------------------------------------------------- */ |
| tRes = listObjAsParamList(tclo, requestData->formVarList); |
| Tcl_DecrRefCount(tclo); |
| return tRes; |
| } |
| |
| /* -------------------------------------------------------------------------- |
| * done |
| * ----------------------------------------------------------------------- */ |
| Tcl_DecrRefCount(tclo); |
| |
| return TCL_OK; |
| } |
| |
| |
| /* ---------------------------------------------------------------------------- |
| * parseMultipartFormData |
| * ------------------------------------------------------------------------- */ |
| int parseMultipartFormData(RequestData * requestData, Tcl_Interp * interp, |
| char *channelName, char *content_type) |
| { |
| |
| Tcl_Channel channel; |
| int mode; |
| char *boundary = mimeGetParamFromContDisp(content_type, "boundary"); |
| int res = 0; |
| Tcl_DString translation; |
| Tcl_DString encoding; |
| |
| /* printf("DBG parseMultipartFormData - starting\n"); fflush(stdout); */ |
| |
| if (boundary == NULL) { |
| LOG_MSG(interp, WRITE_LOG, __FILE__, __LINE__, |
| "web::dispatch -postdata", WEBLOG_WARNING, |
| "error accessing boundary from\"", content_type, "\"", NULL); |
| return TCL_ERROR; |
| } |
| |
| channel = Web_GetChannelOrVarChannel(interp, channelName, &mode); |
| if (channel == NULL) { |
| LOG_MSG(interp, WRITE_LOG, __FILE__, __LINE__, |
| "web::dispatch -postdata", WEBLOG_WARNING, |
| "error getting channel \"", channelName, "\"", NULL); |
| WebFreeIfNotNull(boundary); |
| return TCL_ERROR; |
| } |
| |
| if ((mode & TCL_READABLE) == 0) { |
| LOG_MSG(interp, WRITE_LOG, __FILE__, __LINE__, |
| "web::dispatch -postdata", WEBLOG_WARNING, |
| "channel \"", channelName, "\" not open for reading", NULL); |
| |
| /* unregister if was a varchannel */ |
| Web_UnregisterVarChannel(interp, channelName, channel); |
| WebFreeIfNotNull(boundary); |
| return TCL_ERROR; |
| } |
| |
| Tcl_DStringInit(&translation); |
| Tcl_DStringInit(&encoding); |
| Tcl_GetChannelOption(interp, channel, "-translation", &translation); |
| Tcl_GetChannelOption(interp, channel, "-encoding", &encoding); |
| Tcl_SetChannelOption(interp, channel, "-translation", "binary"); |
| |
| res = mimeSplitMultipart(interp, channel, boundary, requestData); |
| |
| Tcl_SetChannelOption(interp, channel, "-translation", Tcl_DStringValue(&translation)); |
| Tcl_SetChannelOption(interp, channel, "-encoding", Tcl_DStringValue(&encoding)); |
| Tcl_DStringFree(&translation); |
| Tcl_DStringFree(&encoding); |
| /* unregister if was a varchannel */ |
| Web_UnregisterVarChannel(interp, channelName, channel); |
| |
| WebFreeIfNotNull(boundary); |
| return res; |
| } |
| |
| |
| /* ---------------------------------------------------------------------------- |
| * mimeSplitMultipart |
| * ------------------------------------------------------------------------- */ |
| int mimeSplitMultipart(Tcl_Interp * interp, Tcl_Channel channel, |
| const char *boundary, RequestData * requestData) |
| { |
| |
| Tcl_Obj *pro = NULL; |
| Tcl_Obj *hdr = NULL; |
| Tcl_Obj *bdy = NULL; |
| int isLast = TCL_ERROR; |
| long upLoadFileSize = 0; |
| long bytesSkipped = 0; |
| Tcl_Obj *tmpFileName = NULL; |
| |
| MimeContDispData *mimeContDispData = NULL; |
| |
| isLast = TCL_ERROR; |
| |
| /* printf("DBG mimeSplitMultipart - starting\n"); fflush(stdout); */ |
| |
| /* -------------------------------------------------------------------------- |
| * prolog |
| * ----------------------------------------------------------------------- */ |
| pro = Tcl_NewObj(); |
| if (pro == NULL) |
| return TCL_ERROR; |
| Tcl_IncrRefCount(pro); |
| mimeReadBody(channel, pro, boundary, &isLast); |
| /* printf("DBG prolog: %s\n",Tcl_GetString(pro)); fflush(stdout); */ |
| Tcl_DecrRefCount(pro); |
| |
| /* -------------------------------------------------------------------------- |
| * read until last |
| * ----------------------------------------------------------------------- */ |
| |
| /* fixme: only read content_length bytes ... */ |
| /* For now (3.5) we will add this to the documentation. */ |
| while (isLast == TCL_ERROR) { |
| |
| /* ------------------------------------------------------------------------ |
| * header |
| * --------------------------------------------------------------------- */ |
| hdr = Tcl_NewObj(); |
| if (hdr == NULL) |
| return TCL_ERROR; |
| Tcl_IncrRefCount(hdr); |
| mimeReadHeader(channel, hdr); |
| mimeContDispData = |
| mimeGetContDispFromHeader(interp, Tcl_GetString(hdr)); |
| Tcl_DecrRefCount(hdr); |
| |
| /* ------------------------------------------------------------------------ |
| * body |
| * --------------------------------------------------------------------- */ |
| if (mimeContDispData == NULL) { |
| LOG_MSG(interp, WRITE_LOG, __FILE__, __LINE__, |
| "web::dispatch -postdata", |
| WEBLOG_ERROR, |
| "error accessing 'Content-Disposition'. Check boundary", |
| NULL); |
| return TCL_ERROR; |
| } |
| |
| if ((mimeContDispData->name == NULL) || |
| (mimeContDispData->type == NULL)) { |
| |
| destroyMimeContDispData(mimeContDispData); |
| return TCL_ERROR; |
| } |
| |
| if (STRCASECMP(mimeContDispData->type, "form-data") == 0) { |
| |
| /* ---------------------------------------------------------------------- |
| * file upload ? |
| * ------------------------------------------------------------------- */ |
| |
| if (mimeContDispData->fileName != NULL) { |
| |
| int fileNameLen = strlen(mimeContDispData->fileName); |
| int flag = TCL_OK; |
| Tcl_Obj *lobjv[4]; |
| Tcl_Obj *fileUploadList = NULL; |
| |
| WebGetLong(interp, requestData->upLoadFileSize, |
| upLoadFileSize, flag); |
| if (flag == TCL_ERROR) { |
| destroyMimeContDispData(mimeContDispData); |
| return TCL_ERROR; |
| } |
| |
| bytesSkipped = 0; |
| |
| tmpFileName = tempFileName(interp, requestData, NULL, NULL); |
| if (tmpFileName == NULL) { |
| LOG_MSG(interp, WRITE_LOG, __FILE__, __LINE__, |
| "web::dispatch -postdata", |
| WEBLOG_ERROR, |
| "cannot request name for temporary file", NULL); |
| destroyMimeContDispData(mimeContDispData); |
| return TCL_ERROR; |
| } |
| |
| readAndDumpBody(interp, channel, boundary, &isLast, |
| tmpFileName, upLoadFileSize, |
| requestData->filePermissions, &bytesSkipped); |
| |
| if (fileNameLen > 0) { |
| |
| lobjv[0] = tmpFileName; |
| lobjv[1] = |
| Tcl_NewStringObj(mimeContDispData->fileName, -1); |
| if (upLoadFileSize == 0) |
| lobjv[2] = Tcl_NewIntObj(-1); |
| else |
| lobjv[2] = Tcl_NewLongObj(bytesSkipped); |
| lobjv[3] = |
| Tcl_NewStringObj(mimeContDispData->content, -1); |
| |
| } |
| else { |
| |
| lobjv[0] = Tcl_NewStringObj("", -1); |
| lobjv[1] = Tcl_NewStringObj("", -1); |
| lobjv[2] = Tcl_NewIntObj(-2); |
| lobjv[3] = Tcl_NewStringObj("", -1); |
| } |
| |
| fileUploadList = Tcl_NewObj(); |
| Tcl_IncrRefCount(fileUploadList); |
| Tcl_ListObjReplace(interp, fileUploadList, 0, 0, 4, lobjv); |
| |
| if (paramListAdd(requestData->formVarList, |
| mimeContDispData->name, fileUploadList) |
| == TCL_ERROR) { |
| LOG_MSG(interp, WRITE_LOG, __FILE__, __LINE__, |
| "web::dispatch -postdata", |
| WEBLOG_ERROR, |
| "cannot add \"", |
| mimeContDispData->name, ", ", |
| Tcl_GetString(fileUploadList), |
| "\" to web::formvar data", NULL); |
| |
| Tcl_ListObjReplace(interp, fileUploadList, 0, 3, 0, NULL); |
| Tcl_DecrRefCount(fileUploadList); |
| |
| destroyMimeContDispData(mimeContDispData); |
| return TCL_ERROR; |
| } |
| Tcl_DecrRefCount(fileUploadList); |
| } |
| else { |
| |
| /* -------------------------------------------------------------------- |
| * no filename. is normal field |
| * ----------------------------------------------------------------- */ |
| bdy = Tcl_NewObj(); |
| if (bdy == NULL) { |
| destroyMimeContDispData(mimeContDispData); |
| return TCL_ERROR; |
| } |
| Tcl_IncrRefCount(bdy); |
| mimeReadBody(channel, bdy, boundary, &isLast); |
| |
| /* printf("DBG mimeSplitMultipart - '%s' -> '%s'\n",mimeContDispData->name,Tcl_GetString(bdy)); fflush(stdout); */ |
| |
| if (paramListAdd(requestData->formVarList, |
| mimeContDispData->name, bdy) |
| == TCL_ERROR) { |
| LOG_MSG(interp, WRITE_LOG, __FILE__, __LINE__, |
| "web::dispatch -postdata", WEBLOG_ERROR, |
| "cannot add \"", |
| mimeContDispData->name, ", ", |
| Tcl_GetString(bdy), |
| "\" to web::formvar data", NULL); |
| destroyMimeContDispData(mimeContDispData); |
| Tcl_DecrRefCount(bdy); |
| return TCL_ERROR; |
| } |
| Tcl_DecrRefCount(bdy); |
| } |
| } |
| destroyMimeContDispData(mimeContDispData); |
| } |
| return TCL_OK; |
| } |
| |
| |
| /* ---------------------------------------------------------------------------- |
| * mimeSplitIsBoundary |
| * ------------------------------------------------------------------------- */ |
| int mimeSplitIsBoundary(Tcl_Obj * cur, Tcl_Obj * prev, |
| const char *boundary, int *isLast) |
| { |
| |
| int bLen = 0; |
| int cLen = 0; |
| int pLen = 0; |
| char *line = NULL; |
| |
| if ((cur == NULL) || (boundary == NULL)) |
| return TCL_ERROR; |
| |
| /* check for CR on prev line */ |
| if (prev != NULL) { |
| line = Tcl_GetStringFromObj(prev, &pLen); |
| |
| if (pLen > 0) |
| if (line[pLen - 1] != '\r') |
| return TCL_ERROR; |
| } |
| |
| bLen = strlen(boundary); |
| line = Tcl_GetStringFromObj(cur, &cLen); |
| *isLast = TCL_ERROR; |
| |
| /* printf("DBG mimeSplitIsBoundary - checking %s\n",line); fflush(stdout); */ |
| |
| if (cLen >= 2 + bLen) |
| if (strncmp(line, "--", 2) == 0) |
| if (strncmp(&(line[2]), boundary, bLen) == 0) { |
| if (cLen >= (4 + bLen)) |
| if (strncmp(&(line[2 + bLen]), "--", 2) == 0) |
| *isLast = TCL_OK; |
| |
| if (prev != NULL) { |
| /* cut CR from line if it was a boundary */ |
| Tcl_SetObjLength(prev, pLen - 1); |
| } |
| |
| return TCL_OK; |
| } |
| return TCL_ERROR; |
| } |
| |
| /* ---------------------------------------------------------------------------- |
| * mimeReadHeader |
| * ------------------------------------------------------------------------- */ |
| void mimeReadHeader(Tcl_Channel channel, Tcl_Obj * hdr) |
| { |
| |
| Tcl_Obj *tclo = NULL; |
| int first = 0; |
| char *cline = NULL; |
| int len = 0; |
| |
| if ((channel == NULL) || (hdr == NULL)) |
| return; |
| |
| /* printf("DBG mimeReadHeader - starting\n"); fflush(stdout); */ |
| |
| |
| tclo = Tcl_NewObj(); |
| first = TCL_OK; |
| Tcl_IncrRefCount(tclo); |
| |
| while (Tcl_GetsObj(channel, tclo) != -1) { |
| |
| cline = Tcl_GetStringFromObj(tclo, &len); |
| |
| if (strcmp(cline, "\r") == 0) |
| break; |
| |
| /* cut CR from line */ |
| if (len > 0) |
| if (cline[len - 1] == '\r') |
| Tcl_SetObjLength(tclo, len - 1); |
| |
| |
| if (first == TCL_ERROR) |
| Tcl_AppendToObj(hdr, "\n", 1); |
| else |
| first = TCL_ERROR; |
| |
| Tcl_AppendObjToObj(hdr, tclo); |
| |
| Tcl_DecrRefCount(tclo); |
| tclo = Tcl_NewObj(); |
| Tcl_IncrRefCount(tclo); |
| } |
| |
| Tcl_DecrRefCount(tclo); |
| |
| /* printf("DBG mimeReadHeader - hdr: %s\n",Tcl_GetString(hdr)); fflush(stdout); */ |
| } |
| |
| /* ---------------------------------------------------------------------------- |
| * readAndDumpBody -- POST/file upload: |
| * get data from channel (typically stdin) and dump to file |
| * note: if the first chunk that is read is bigger than upLoadFileSize, |
| * note: no bytes will be written, and bytesSkipped = readBytes. |
| * ------------------------------------------------------------------------- */ |
| long readAndDumpBody(Tcl_Interp * interp, Tcl_Channel in, |
| const char *boundary, int *isLast, |
| Tcl_Obj * tmpFileName, long upLoadFileSize, |
| int filePermissions, long *bytesSkipped) |
| { |
| |
| Tcl_Channel out; |
| Tcl_Obj *curline = NULL; |
| Tcl_Obj *prevline = NULL; |
| long tmp = 0; |
| long readBytes = 0; |
| long writtenBytes = 0; |
| long rBytes = 0; |
| long rBytesPrev = 0; |
| long wBytes = 0; |
| |
| /* -------------------------------------------------------------------------- |
| * sanity |
| * ----------------------------------------------------------------------- */ |
| if ((in == NULL) || (boundary == NULL) || (tmpFileName == NULL)) |
| return 0; |
| |
| /* -------------------------------------------------------------------------- |
| * open file |
| * ----------------------------------------------------------------------- */ |
| if ((out = Tcl_OpenFileChannel(NULL, Tcl_GetString(tmpFileName), |
| "w", filePermissions)) == NULL) |
| return 0; |
| |
| /* -------------------------------------------------------------------------- |
| * switch output to "binary" |
| * ----------------------------------------------------------------------- */ |
| if (Tcl_SetChannelOption(interp, out, "-translation", "binary") |
| == TCL_ERROR) { |
| |
| LOG_MSG(interp, WRITE_LOG, __FILE__, __LINE__, |
| "web::dispatch (file upload)", |
| WEBLOG_INFO, "error setting translation to binary ", NULL); |
| return 0; |
| } |
| |
| /* -------------------------------------------------------------------------- |
| * first line |
| * ----------------------------------------------------------------------- */ |
| prevline = Tcl_NewObj(); |
| Tcl_IncrRefCount(prevline); |
| rBytesPrev = Tcl_GetsObj(in, prevline); |
| |
| if (rBytesPrev != -1) { |
| |
| /* ------------------------------------------------------------------------ |
| * read line-by-line, write delayed |
| * --------------------------------------------------------------------- */ |
| curline = Tcl_NewObj(); |
| Tcl_IncrRefCount(curline); |
| |
| while ((rBytes = Tcl_GetsObj(in, curline)) != -1) { |
| |
| int isBoundary = 0; |
| |
| /* ---------------------------------------------------------------------- |
| * test for boundary |
| * ------------------------------------------------------------------- */ |
| if (mimeSplitIsBoundary(curline, prevline, boundary, isLast) == |
| TCL_OK) { |
| |
| isBoundary = 1; |
| readBytes += rBytesPrev - 1; /* we truncated CR */ |
| |
| } |
| else { |
| |
| readBytes += rBytesPrev + 1; /* add newline */ |
| } |
| |
| /* ---------------------------------------------------------------------- |
| * test for upload limit |
| * ------------------------------------------------------------------- */ |
| if ((upLoadFileSize > 0) && (writtenBytes <= upLoadFileSize)) { |
| |
| /* -------------------------------------------------------------------- |
| * partial or full write ? |
| * ----------------------------------------------------------------- */ |
| if ((writtenBytes + rBytesPrev) <= upLoadFileSize) { |
| |
| /* still room to write all we read */ |
| |
| if ((wBytes = Tcl_WriteObj(out, prevline)) != -1) { |
| |
| writtenBytes += wBytes; |
| |
| if (!isBoundary) { |
| |
| if (writtenBytes <= (upLoadFileSize - 1)) { |
| |
| /* still room for newline */ |
| |
| if (Tcl_Eof(in) == 0) /* not yet at end-of-file */ |
| if ((tmp = |
| Tcl_WriteChars(out, "\n", 1)) != -1) |
| writtenBytes += tmp; |
| } |
| } |
| } |
| } |
| else { |
| |
| /* just dump part of input */ |
| |
| if ((wBytes = Tcl_WriteChars(out, Tcl_GetString(prevline), |
| upLoadFileSize - |
| writtenBytes)) != -1) { |
| |
| writtenBytes += wBytes; |
| } |
| } |
| } |
| |
| if (isBoundary) |
| break; |
| |
| /* ---------------------------------------------------------------------- |
| * update prevline |
| * ------------------------------------------------------------------- */ |
| Tcl_DecrRefCount(prevline); |
| prevline = curline; |
| curline = Tcl_NewObj(); |
| Tcl_IncrRefCount(curline); |
| rBytesPrev = rBytes; |
| } |
| Tcl_DecrRefCount(curline); |
| } |
| |
| Tcl_Close(NULL, out); |
| |
| Tcl_DecrRefCount(prevline); |
| |
| *bytesSkipped = (readBytes - writtenBytes); |
| |
| return writtenBytes; |
| } |
| |
| /* ---------------------------------------------------------------------------- |
| * mimeReadBody |
| * ------------------------------------------------------------------------- */ |
| void mimeReadBody(Tcl_Channel in, Tcl_Obj * bdy, const char *boundary, |
| int *isLast) |
| { |
| |
| Tcl_Obj *prevline = NULL; |
| Tcl_Obj *curline = NULL; |
| int first; |
| |
| /* printf("DBG mimeReadBody - starting\n"); fflush(stdout); */ |
| |
| first = 1; |
| |
| prevline = Tcl_NewObj(); |
| Tcl_IncrRefCount(prevline); |
| if (Tcl_GetsObj(in, prevline) != -1) { |
| |
| /* printf("DBG mimeReadBody - first line: %s\n",Tcl_GetString(prevline)); fflush(stdout); */ |
| |
| if (mimeSplitIsBoundary(prevline, NULL, boundary, isLast) == TCL_OK) { |
| |
| Tcl_DecrRefCount(prevline); |
| return; |
| } |
| |
| curline = Tcl_NewObj(); |
| Tcl_IncrRefCount(curline); |
| |
| while (Tcl_GetsObj(in, curline) != -1) { |
| |
| int isBoundary = 0; |
| |
| /* printf("DBG mimeReadBody - prevline: %s\n",Tcl_GetString(prevline)); fflush(stdout); */ |
| |
| /* ---------------------------------------------------------------------- |
| * test for boundary |
| * ------------------------------------------------------------------- */ |
| if (mimeSplitIsBoundary(curline, prevline, boundary, isLast) == |
| TCL_OK) { |
| isBoundary = 1; |
| } |
| |
| if (!first) { |
| Tcl_AppendToObj(bdy, "\n", 1); |
| } |
| else { |
| first = 0; |
| } |
| |
| Tcl_AppendObjToObj(bdy, prevline); |
| |
| |
| /* printf("DBG mimeReadBody - bdy now: %s\n",Tcl_GetString(bdy)); fflush(stdout); */ |
| |
| Tcl_DecrRefCount(prevline); |
| prevline = curline; |
| curline = Tcl_NewObj(); |
| Tcl_IncrRefCount(curline); |
| |
| if (isBoundary) |
| break; |
| } |
| Tcl_DecrRefCount(curline); |
| } |
| Tcl_DecrRefCount(prevline); |
| } |
| |
| /* ---------------------------------------------------------------------------- |
| * mimeGetParamFromContDisp -- |
| * searches for parameter of name <name> in a string of the form: |
| * Content-Disposition: form-data; name="i1"; filename="test.dat" |
| * actually, it searches case-insensitive for <name> in <contentDisp> |
| * and takes what follows after '=', discarding optional '"' |
| * N o t e: user needs to free memory for char that is returned. |
| * ------------------------------------------------------------------------- */ |
| char *mimeGetParamFromContDisp(const char *contentDisp, const char *name) |
| { |
| |
| char *tmp = NULL; |
| char *val = NULL; |
| char *semicolon = NULL; |
| char tag = -1; |
| int namLen = 0; |
| |
| if ((contentDisp == NULL) || (name == NULL)) |
| return NULL; |
| |
| namLen = strlen(name); |
| tmp = myUtfStrStr(contentDisp, name); |
| |
| if (tmp == NULL) |
| return NULL; |
| |
| if (strlen(tmp) <= (unsigned int) namLen) |
| return NULL; |
| |
| tmp = &(tmp[namLen]); |
| |
| if (strlen(tmp) > 2) { |
| |
| if (tmp[0] == '=') { |
| |
| tmp = &(tmp[1]); |
| |
| if (tmp[0] == '"') |
| tmp = &(tmp[1]); |
| |
| semicolon = strchrchr(tmp, ';', '\n', &tag); |
| if (semicolon == NULL) |
| semicolon = tmp + strlen(tmp) + 1; |
| |
| if (strlen(tmp) > 0) { |
| |
| val = allocAndSetN(tmp, semicolon - tmp); |
| |
| if (val != NULL) { |
| if (val[strlen(val) - 1] == '"') |
| val[strlen(val) - 1] = 0; |
| |
| val = strWithoutLinebreak(val); |
| |
| return val; |
| } |
| } |
| } |
| } |
| return NULL; |
| } |
| |
| |
| /* ---------------------------------------------------------------------------- |
| * mimeGetContDispFromHeader |
| * ------------------------------------------------------------------------- */ |
| MimeContDispData *mimeGetContDispFromHeader(Tcl_Interp * interp, |
| const char *header) |
| { |
| |
| char *start = NULL; |
| int len = 0; |
| char *semicolon = NULL; |
| char tag; |
| |
| char *type = NULL; |
| char *name = NULL; |
| char *fileName = NULL; |
| char *content = NULL; |
| |
| MimeContDispData *mimeContDispData = NULL; |
| |
| if (header == NULL) { |
| LOG_MSG(interp, WRITE_LOG, __FILE__, __LINE__, |
| "web::dispatch -postdata", |
| WEBLOG_ERROR, |
| "error accessing Content-Disposition from multipart/formdata data: no header", |
| NULL); |
| return NULL; |
| } |
| |
| /* 1 2 */ |
| /* 012345678901234567890 */ |
| start = myUtfStrStr(header, "Content-Disposition: "); |
| if (start == NULL) { |
| LOG_MSG(interp, WRITE_LOG, __FILE__, __LINE__, |
| "web::dispatch -postdata", |
| WEBLOG_ERROR, |
| "error accessing Content-Disposition from multipart/formdata data: 'Content-Disposition' not found in '", |
| header, "'", NULL); |
| return NULL; |
| } |
| |
| len = strlen(start); |
| if (len < 20) { |
| LOG_MSG(interp, WRITE_LOG, __FILE__, __LINE__, |
| "web::dispatch -postdata", |
| WEBLOG_ERROR, |
| "error accessing Content-Disposition from multipart/formdata data: empty 'Content-Disposition'", |
| NULL); |
| return NULL; |
| } |
| |
| semicolon = strchrchr(start, ';', '\n', &tag); |
| if (semicolon == NULL) { |
| LOG_MSG(interp, WRITE_LOG, __FILE__, __LINE__, |
| "web::dispatch -postdata", |
| WEBLOG_ERROR, |
| "error accessing Content-Disposition from multipart/formdata data: 'Content-Disposition' not properly terminated in '", |
| start, "'", NULL); |
| return NULL; |
| } |
| |
| start = webEat(' ', &start[21]); |
| type = allocAndSetN(start, semicolon - start); |
| |
| name = mimeGetParamFromContDisp(start, "name"); |
| fileName = mimeGetParamFromContDisp(start, "filename"); |
| |
| mimeContDispData = newMimeContDispData(); |
| if (mimeContDispData == NULL) { |
| |
| WebFreeIfNotNull(type); |
| WebFreeIfNotNull(name); |
| WebFreeIfNotNull(fileName); |
| |
| LOG_MSG(interp, WRITE_LOG, __FILE__, __LINE__, |
| "web::dispatch -postdata", |
| WEBLOG_ERROR, "error getting memory", NULL); |
| return NULL; |
| } |
| |
| mimeContDispData->type = type; |
| mimeContDispData->name = name; |
| mimeContDispData->fileName = fileName; |
| |
| /* -------------------------------------------------------------------------- |
| * and get the Mime Content-type |
| * patch provided by david@raster.onthe.net.au |
| * ----------------------------------------------------------------------- */ |
| |
| /* 1 2 */ |
| /* 012345678901234567890 */ |
| start = myUtfStrStr(header, "Content-Type: "); |
| if (start) { |
| |
| len = strlen(start); |
| if (len >= 13) { |
| semicolon = strchr(start, '\n'); |
| if (semicolon == NULL) |
| semicolon = &start[len]; |
| |
| start = webEat(' ', &start[14]); |
| content = allocAndSetN(start, semicolon - start); |
| mimeContDispData->content = content; |
| } |
| } |
| return mimeContDispData; |
| } |
| |
| |
| /* ---------------------------------------------------------------------------- |
| * newMimeContDispData |
| * ------------------------------------------------------------------------- */ |
| MimeContDispData *newMimeContDispData(void) |
| { |
| |
| MimeContDispData *data = NULL; |
| |
| data = WebAllocInternalData(MimeContDispData); |
| |
| if (data != NULL) { |
| |
| data->type = NULL; |
| data->name = NULL; |
| data->fileName = NULL; |
| data->content = NULL; |
| } |
| return data; |
| } |
| |
| /* ---------------------------------------------------------------------------- |
| * destroyMimeContDispData |
| * ------------------------------------------------------------------------- */ |
| void destroyMimeContDispData(MimeContDispData * data) |
| { |
| |
| if (data != NULL) { |
| |
| WebFreeIfNotNull(data->type); |
| WebFreeIfNotNull(data->name); |
| WebFreeIfNotNull(data->fileName); |
| WebFreeIfNotNull(data->content); |
| } |
| WebFreeIfNotNull(data); |
| } |