upload.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  1. /*
  2. upload.c -- File upload handler
  3. Copyright (c) All Rights Reserved. See details at the end of the file.
  4. */
  5. /*********************************** Includes *********************************/
  6. #include "goahead.h"
  7. #if ME_GOAHEAD_UPLOAD
  8. /************************************ Locals **********************************/
  9. /*
  10. Upload states
  11. */
  12. #define UPLOAD_REQUEST_HEADER 1 /* Request header */
  13. #define UPLOAD_BOUNDARY 2 /* Boundary divider */
  14. #define UPLOAD_CONTENT_HEADER 3 /* Content part header */
  15. #define UPLOAD_CONTENT_DATA 4 /* Content encoded data */
  16. #define UPLOAD_CONTENT_END 5 /* End of multipart message */
  17. static char *uploadDir;
  18. /*********************************** Forwards *********************************/
  19. static void defineUploadVars(Webs *wp);
  20. static char *getBoundary(Webs *wp, char *buf, ssize bufLen);
  21. static void initUpload(Webs *wp);
  22. static void processContentBoundary(Webs *wp, char *line);
  23. static bool processContentData(Webs *wp);
  24. static void processUploadHeader(Webs *wp, char *line);
  25. /************************************* Code ***********************************/
  26. /*
  27. The upload handler functions as a filter.
  28. Return false because the upload handler is not a terminal handler.
  29. */
  30. static bool uploadHandler(Webs *wp)
  31. {
  32. return 0;
  33. }
  34. static void initUpload(Webs *wp)
  35. {
  36. char *boundary;
  37. if (wp->uploadState == 0) {
  38. wp->uploadState = UPLOAD_BOUNDARY;
  39. if ((boundary = strstr(wp->contentType, "boundary=")) != 0) {
  40. boundary += 9;
  41. wfree(wp->boundary);
  42. wp->boundary = sfmt("--%s", boundary);
  43. wp->boundaryLen = strlen(wp->boundary);
  44. }
  45. if (wp->boundaryLen == 0 || *wp->boundary == '\0') {
  46. websError(wp, HTTP_CODE_BAD_REQUEST, "Bad boundary");
  47. } else {
  48. websSetVar(wp, "UPLOAD_DIR", uploadDir);
  49. wp->files = hashCreate(11);
  50. }
  51. }
  52. }
  53. static void freeUploadFile(WebsUpload *up)
  54. {
  55. if (up) {
  56. if (up->filename) {
  57. unlink(up->filename);
  58. wfree(up->filename);
  59. }
  60. wfree(up->clientFilename);
  61. wfree(up->contentType);
  62. wfree(up);
  63. }
  64. }
  65. PUBLIC void websFreeUpload(Webs *wp)
  66. {
  67. WebsUpload *up;
  68. WebsKey *s;
  69. if (wp->files >= 0) {
  70. for (s = hashFirst(wp->files); s; s = hashNext(wp->files, s)) {
  71. up = s->content.value.symbol;
  72. freeUploadFile(up);
  73. if (up == wp->currentFile) {
  74. wp->currentFile = 0;
  75. }
  76. }
  77. hashFree(wp->files);
  78. }
  79. if (wp->currentFile) {
  80. freeUploadFile(wp->currentFile);
  81. wp->currentFile = 0;
  82. }
  83. if (wp->upfd >= 0) {
  84. close(wp->upfd);
  85. wp->upfd = -1;
  86. }
  87. }
  88. PUBLIC bool websProcessUploadData(Webs *wp)
  89. {
  90. char *line, *nextTok;
  91. ssize len, nbytes;
  92. bool canProceed;
  93. line = 0;
  94. canProceed = 1;
  95. while (canProceed && !wp->finalized && wp->uploadState != UPLOAD_CONTENT_END) {
  96. if (wp->uploadState == UPLOAD_BOUNDARY || wp->uploadState == UPLOAD_CONTENT_HEADER) {
  97. /*
  98. Parse the next input line
  99. */
  100. line = wp->input.servp;
  101. if ((nextTok = memchr(line, '\n', bufLen(&wp->input))) == 0) {
  102. /* Incomplete line */
  103. canProceed = 0;
  104. break;
  105. }
  106. *nextTok++ = '\0';
  107. nbytes = nextTok - line;
  108. websConsumeInput(wp, nbytes);
  109. strim(line, "\r", WEBS_TRIM_END);
  110. len = strlen(line);
  111. if (line[len - 1] == '\r') {
  112. line[len - 1] = '\0';
  113. }
  114. }
  115. switch (wp->uploadState) {
  116. printf("wp->uploadState = %d\n",wp->uploadState);
  117. case 0:
  118. initUpload(wp);
  119. break;
  120. case UPLOAD_BOUNDARY:
  121. processContentBoundary(wp, line);
  122. break;
  123. case UPLOAD_CONTENT_HEADER:
  124. processUploadHeader(wp, line);
  125. break;
  126. case UPLOAD_CONTENT_DATA:
  127. canProceed = processContentData(wp);
  128. if (bufLen(&wp->input) < wp->boundaryLen) {
  129. /* Incomplete boundary - return to get more data */
  130. canProceed = 0;
  131. }
  132. break;
  133. case UPLOAD_CONTENT_END:
  134. break;
  135. }
  136. }
  137. bufCompact(&wp->input);
  138. return canProceed;
  139. }
  140. static void processContentBoundary(Webs *wp, char *line)
  141. {
  142. /*
  143. Expecting a multipart boundary string
  144. */
  145. if (strncmp(wp->boundary, line, wp->boundaryLen) != 0) {
  146. websError(wp, HTTP_CODE_BAD_REQUEST, "Bad upload state. Incomplete boundary");
  147. } else if (line[wp->boundaryLen] && strcmp(&line[wp->boundaryLen], "--") == 0) {
  148. wp->uploadState = UPLOAD_CONTENT_END;
  149. } else {
  150. wp->uploadState = UPLOAD_CONTENT_HEADER;
  151. }
  152. }
  153. static void processUploadHeader(Webs *wp, char *line)
  154. {
  155. WebsUpload *file;
  156. char *key, *headerTok, *rest, *nextPair, *value;
  157. if (line[0] == '\0') {
  158. wp->uploadState = UPLOAD_CONTENT_DATA;
  159. return;
  160. }
  161. trace(7, "Header line: %s", line);
  162. headerTok = line;
  163. stok(line, ": ", &rest);
  164. if (scaselesscmp(headerTok, "Content-Disposition") == 0) {
  165. /*
  166. The content disposition header describes either a form variable or an uploaded file.
  167. Content-Disposition: form-data; name="field1"
  168. >>blank line
  169. Field Data
  170. ---boundary
  171. Content-Disposition: form-data; name="field1" filename="user.file"
  172. >>blank line
  173. File data
  174. ---boundary
  175. */
  176. key = rest;
  177. wfree(wp->uploadVar);
  178. wfree(wp->clientFilename);
  179. wp->uploadVar = wp->clientFilename = 0;
  180. while (key && stok(key, ";\r\n", &nextPair)) {
  181. key = strim(key, " ", WEBS_TRIM_BOTH);
  182. ssplit(key, "= ", &value);
  183. value = strim(value, "\"", WEBS_TRIM_BOTH);
  184. if (scaselesscmp(key, "form-data") == 0) {
  185. /* Nothing to do */
  186. } else if (scaselesscmp(key, "name") == 0) {
  187. wfree(wp->uploadVar);
  188. wp->uploadVar = sclone(value);
  189. printf("log1: name: %s\n", wp->uploadVar);
  190. } else if (scaselesscmp(key, "filename") == 0) {
  191. if (wp->uploadVar == 0) {
  192. websError(wp, HTTP_CODE_BAD_REQUEST, "Bad upload state. Missing name field");
  193. return;
  194. }
  195. value = websNormalizeUriPath(value);
  196. if (*value == '.' || !websValidUriChars(value) || strpbrk(value, "\\/:*?<>|~\"'%`^\n\r\t\f")) {
  197. websError(wp, HTTP_CODE_INTERNAL_SERVER_ERROR, "Bad upload client filename");
  198. wfree(value);
  199. return;
  200. }
  201. wfree(wp->clientFilename);
  202. wp->clientFilename = value;
  203. /*
  204. Create the file to hold the uploaded data
  205. */
  206. wfree(wp->uploadTmp);
  207. if ((wp->uploadTmp = websTempFile(uploadDir, "tmp")) == 0) {
  208. websError(wp, HTTP_CODE_INTERNAL_SERVER_ERROR,
  209. "Cannot create upload temp file %s. Check upload temp dir %s", wp->uploadTmp, uploadDir);
  210. return;
  211. }
  212. trace(5, "File upload of: %s stored as %s", wp->clientFilename, wp->uploadTmp);
  213. if ((wp->upfd = open(wp->uploadTmp, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0600)) < 0) {
  214. websError(wp, HTTP_CODE_INTERNAL_SERVER_ERROR, "Cannot open upload temp file %s", wp->uploadTmp);
  215. return;
  216. }
  217. /*
  218. Create the files[id]
  219. */
  220. freeUploadFile(wp->currentFile);
  221. file = wp->currentFile = walloc(sizeof(WebsUpload));
  222. memset(file, 0, sizeof(WebsUpload));
  223. file->clientFilename = sclone(wp->clientFilename);
  224. file->filename = sclone(wp->uploadTmp);
  225. }
  226. key = nextPair;
  227. }
  228. } else if (scaselesscmp(headerTok, "Content-Type") == 0) {
  229. if (wp->clientFilename) {
  230. trace(5, "Set files[%s][CONTENT_TYPE] = %s", wp->uploadVar, rest);
  231. wp->currentFile->contentType = sclone(rest);
  232. }
  233. }
  234. }
  235. static void defineUploadVars(Webs *wp)
  236. {
  237. WebsUpload *file;
  238. char key[64];
  239. file = wp->currentFile;
  240. fmt(key, sizeof(key), "FILE_CLIENT_FILENAME_%s", wp->uploadVar);
  241. websSetVar(wp, key, file->clientFilename);
  242. fmt(key, sizeof(key), "FILE_CONTENT_TYPE_%s", wp->uploadVar);
  243. websSetVar(wp, key, file->contentType);
  244. fmt(key, sizeof(key), "FILE_FILENAME_%s", wp->uploadVar);
  245. websSetVar(wp, key, file->filename);
  246. fmt(key, sizeof(key), "FILE_SIZE_%s", wp->uploadVar);
  247. websSetVarFmt(wp, key, "%d", (int) file->size);
  248. }
  249. static int writeToFile(Webs *wp, char *data, ssize len)
  250. {
  251. WebsUpload *file;
  252. ssize rc;
  253. file = wp->currentFile;
  254. //printf("===file->size %d >>>len %d >> ME_GOAHEAD_LIMIT_UPLOAD %d ====\n", file->size, len, ME_GOAHEAD_LIMIT_UPLOAD);
  255. //printf("- %d\n", file->size);
  256. if ((file->size + len) > ME_GOAHEAD_LIMIT_UPLOAD) {
  257. websError(wp, HTTP_CODE_REQUEST_TOO_LARGE, "Uploaded file exceeds maximum %d", (int) ME_GOAHEAD_LIMIT_UPLOAD);
  258. return -1;
  259. }
  260. if (len > 0) {
  261. /*
  262. File upload. Write the file data.
  263. */
  264. if ((rc = write(wp->upfd, data, (int) len)) != len) {
  265. websError(wp, HTTP_CODE_INTERNAL_SERVER_ERROR, "Cannot write to upload temp file %s, rc %d", wp->uploadTmp, rc);
  266. return -1;
  267. }
  268. file->size += len;
  269. trace(7, "uploadFilter: Wrote %d bytes to %s", len, wp->uploadTmp);
  270. }
  271. return 0;
  272. }
  273. static bool processContentData(Webs *wp)
  274. {
  275. WebsUpload *file;
  276. WebsBuf *content;
  277. ssize size, nbytes;
  278. char *data, *bp;
  279. content = &wp->input;
  280. file = wp->currentFile;
  281. // printf("---> filename: %s, clientFilename: %s, contentType: %s, size: %d\n",
  282. // file->filename, file->clientFilename, file->contentType, size);
  283. // printf("---> buf: %p, servp: %p, endp: %p, endbuf: %p, buflen: %d, maxsize: %d\n",
  284. // content->buf, content->servp, content->endp, content->endbuf, content->buflen, content->maxsize);
  285. size = bufLen(content);
  286. if (size < wp->boundaryLen) {
  287. /* Incomplete boundary. Return and get more data */
  288. return 0;
  289. }
  290. if ((bp = getBoundary(wp, content->servp, size)) == 0) {
  291. trace(7, "uploadFilter: Got boundary filename %x", wp->clientFilename);
  292. if (wp->clientFilename) {
  293. /*
  294. No signature found yet. probably more data to come. Must handle split boundaries.
  295. */
  296. data = content->servp;
  297. nbytes = ((int) (content->endp - data)) - (wp->boundaryLen - 1);
  298. if (writeToFile(wp, content->servp, nbytes) < 0) {
  299. /* Proceed to handle error */
  300. printf("write >>>>>>>>>>>>>hander error\n");
  301. return 1;
  302. }
  303. websConsumeInput(wp, nbytes);
  304. /* Get more data */
  305. return 0;
  306. }
  307. }
  308. data = content->servp;
  309. nbytes = (bp) ? (bp - data) : bufLen(content);
  310. if (nbytes > 0) {
  311. websConsumeInput(wp, nbytes);
  312. /*
  313. This is the CRLF before the boundary
  314. */
  315. if (nbytes >= 2 && data[nbytes - 2] == '\r' && data[nbytes - 1] == '\n') {
  316. nbytes -= 2;
  317. }
  318. if (wp->clientFilename) {
  319. /*
  320. Write the last bit of file data and add to the list of files and define environment variables
  321. */
  322. if (writeToFile(wp, data, nbytes) < 0) {
  323. /* Proceed to handle error */
  324. return 1;
  325. }
  326. hashEnter(wp->files, wp->uploadVar, valueSymbol(file), 0);
  327. defineUploadVars(wp);
  328. } else {
  329. /*
  330. Normal string form data variables
  331. */
  332. data[nbytes] = '\0';
  333. trace(5, "uploadFilter: form[%s] = %s", wp->uploadVar, data);
  334. websDecodeUrl(wp->uploadVar, wp->uploadVar, -1);
  335. websDecodeUrl(data, data, -1);
  336. websSetVar(wp, wp->uploadVar, data);
  337. }
  338. }
  339. if (wp->clientFilename) {
  340. /*
  341. Now have all the data (we've seen the boundary)
  342. */
  343. close(wp->upfd);
  344. wp->upfd = -1;
  345. wfree(wp->clientFilename);
  346. wp->clientFilename = 0;
  347. wfree(wp->uploadTmp);
  348. wp->uploadTmp = 0;
  349. }
  350. wp->uploadState = UPLOAD_BOUNDARY;
  351. return 1;
  352. }
  353. /*
  354. Find the boundary signature in memory. Returns pointer to the first match.
  355. */
  356. static char *getBoundary(Webs *wp, char *buf, ssize bufLen)
  357. {
  358. char *cp, *endp;
  359. char first;
  360. assert(buf);
  361. first = *wp->boundary;
  362. cp = buf;
  363. if (bufLen < wp->boundaryLen) {
  364. return 0;
  365. }
  366. endp = cp + (bufLen - wp->boundaryLen) + 1;
  367. while (cp < endp) {
  368. cp = (char *) memchr(cp, first, endp - cp);
  369. if (!cp) {
  370. return 0;
  371. }
  372. if (memcmp(cp, wp->boundary, wp->boundaryLen) == 0) {
  373. return cp;
  374. }
  375. cp++;
  376. }
  377. return 0;
  378. }
  379. WebsUpload *websLookupUpload(Webs *wp, char *key)
  380. {
  381. WebsKey *sp;
  382. if (wp->files >= 0) {
  383. if ((sp = hashLookup(wp->files, key)) == 0) {
  384. return 0;
  385. }
  386. return sp->content.value.symbol;
  387. }
  388. return 0;
  389. }
  390. WebsHash websGetUpload(Webs *wp)
  391. {
  392. return wp->files;
  393. }
  394. PUBLIC void websUploadOpen()
  395. {
  396. uploadDir = ME_GOAHEAD_UPLOAD_DIR;
  397. if (*uploadDir == '\0') {
  398. #if ME_WIN_LIKE
  399. uploadDir = getenv("TEMP");
  400. #else
  401. uploadDir = "/tmp";
  402. #endif
  403. }
  404. trace(4, "Upload directory is %s", uploadDir);
  405. websDefineHandler("upload", 0, uploadHandler, 0, 0);
  406. }
  407. #endif /* ME_GOAHEAD_UPLOAD */
  408. /*
  409. Copyright (c) Embedthis Software. All Rights Reserved.
  410. This software is distributed under commercial and open source licenses.
  411. You may use the Embedthis GoAhead open source license or you may acquire
  412. a commercial license from Embedthis Software. You agree to be fully bound
  413. by the terms of either license. Consult the LICENSE.md distributed with
  414. this software for full details and other copyrights.
  415. */