dual-license fix
[platform/upstream/curl.git] / lib / formdata.c
1 /*****************************************************************************
2  *                                  _   _ ____  _     
3  *  Project                     ___| | | |  _ \| |    
4  *                             / __| | | | |_) | |    
5  *                            | (__| |_| |  _ <| |___ 
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * In order to be useful for every potential user, curl and libcurl are
11  * dual-licensed under the MPL and the MIT/X-derivate licenses.
12  *
13  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
14  * copies of the Software, and permit persons to whom the Software is
15  * furnished to do so, under the terms of the MPL or the MIT/X-derivate
16  * licenses. You may pick one of these licenses.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  * $Id$
22  *****************************************************************************/
23
24 /*
25   Debug the form generator stand-alone by compiling this source file with:
26
27   gcc -DHAVE_CONFIG_H -I../ -g -D_FORM_DEBUG -o formdata -I../include formdata.c
28
29   run the 'formdata' executable and make sure the output is ok!
30
31   try './formdata "name=Daniel" "poo=noo" "foo=bar"' and similarly
32
33  */
34
35 #include "setup.h"
36
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <stdarg.h>
41
42 #include <time.h>
43
44 #include <curl/curl.h>
45 #include "formdata.h"
46
47 #include "strequal.h"
48
49 /* The last #include file should be: */
50 #ifdef MALLOCDEBUG
51 #include "memdebug.h"
52 #endif
53
54 /* Length of the random boundary string. The risk of this being used
55    in binary data is very close to zero, 64^32 makes
56    6277101735386680763835789423207666416102355444464034512896
57    combinations... */
58 #define BOUNDARY_LENGTH 32
59
60 /* What kind of Content-Type to use on un-specified files with unrecognized
61    extensions. */
62 #define HTTPPOST_CONTENTTYPE_DEFAULT "text/plain"
63
64 /* This is a silly duplicate of the function in main.c to enable this source
65    to compile stand-alone for better debugging */
66 static void GetStr(char **string,
67                    char *value)
68 {
69   if(*string)
70     free(*string);
71   *string = strdup(value);
72 }
73
74 /***************************************************************************
75  *
76  * FormParse()
77  *      
78  * Reads a 'name=value' paramter and builds the appropriate linked list.
79  *
80  * Specify files to upload with 'name=@filename'. Supports specified
81  * given Content-Type of the files. Such as ';type=<content-type>'.
82  *
83  * You may specify more than one file for a single name (field). Specify
84  * multiple files by writing it like:
85  *
86  * 'name=@filename,filename2,filename3'
87  *
88  * If you want content-types specified for each too, write them like:
89  *
90  * 'name=@filename;type=image/gif,filename2,filename3'
91  *
92  ***************************************************************************/
93
94 int curl_formparse(char *input,
95                    struct HttpPost **httppost,
96                    struct HttpPost **last_post)
97 {
98   return FormParse(input, httppost, last_post);
99 }
100
101 #define FORM_FILE_SEPARATOR ','
102 #define FORM_TYPE_SEPARATOR ';'
103
104 int FormParse(char *input,
105               struct HttpPost **httppost,
106               struct HttpPost **last_post)
107 {
108   /* nextarg MUST be a string in the format 'name=contents' and we'll
109      build a linked list with the info */
110   char name[256];
111   char contents[4096]="";
112   char major[128];
113   char minor[128];
114   long flags = 0;
115   char *contp;
116   char *type = NULL;
117   char *prevtype = NULL;
118   char *sep;
119   char *sep2;
120   struct HttpPost *post;
121   struct HttpPost *subpost; /* a sub-node */
122   unsigned int i;
123
124   if(1 <= sscanf(input, "%255[^ =] = %4095[^\n]", name, contents)) {
125     /* the input was using the correct format */
126     contp = contents;
127
128     if('@' == contp[0]) {
129       /* we use the @-letter to indicate file name(s) */
130       
131       flags = HTTPPOST_FILENAME;
132       contp++;
133
134       post=NULL;
135
136       do {
137         /* since this was a file, it may have a content-type specifier
138            at the end too */
139
140         sep=strchr(contp, FORM_TYPE_SEPARATOR);
141         sep2=strchr(contp, FORM_FILE_SEPARATOR);
142
143         /* pick the closest */
144         if(sep2 && (sep2 < sep)) {
145           sep = sep2;
146
147           /* no type was specified! */
148         }
149         if(sep) {
150
151           /* if we got here on a comma, don't do much */
152           if(FORM_FILE_SEPARATOR != *sep)
153             type = strstr(sep+1, "type=");
154           else
155             type=NULL;
156
157           *sep=0; /* terminate file name at separator */
158
159           if(type) {
160             type += strlen("type=");
161             
162             if(2 != sscanf(type, "%127[^/]/%127[^,\n]",
163                            major, minor)) {
164               fprintf(stderr, "Illegally formatted content-type field!\n");
165               return 2; /* illegal content-type syntax! */
166             }
167             /* now point beyond the content-type specifier */
168             sep = type + strlen(major)+strlen(minor)+1;
169
170             /* find the following comma */
171             sep=strchr(sep, FORM_FILE_SEPARATOR);
172           }
173         }
174         else {
175           type=NULL;
176           sep=strchr(contp, FORM_FILE_SEPARATOR);
177         }
178         if(sep) {
179           /* the next file name starts here */
180           *sep =0;
181           sep++;
182         }
183         if(!type) {
184           /*
185            * No type was specified, we scan through a few well-known
186            * extensions and pick the first we match!
187            */
188           struct ContentType {
189             char *extension;
190             char *type;
191           };
192           static struct ContentType ctts[]={
193             {".gif",  "image/gif"},
194             {".jpg",  "image/jpeg"},
195             {".jpeg", "image/jpeg"},
196             {".txt",  "text/plain"},
197             {".html", "text/plain"}
198           };
199
200           if(prevtype)
201             /* default to the previously set/used! */
202             type = prevtype;
203           else
204             /* It seems RFC1867 defines no Content-Type to default to
205                text/plain so we don't actually need to set this: */
206             type = HTTPPOST_CONTENTTYPE_DEFAULT;
207
208           for(i=0; i<sizeof(ctts)/sizeof(ctts[0]); i++) {
209             if(strlen(contp) >= strlen(ctts[i].extension)) {
210               if(strequal(contp +
211                           strlen(contp) - strlen(ctts[i].extension),
212                           ctts[i].extension)) {
213                 type = ctts[i].type;
214                 break;
215               }       
216             }
217           }
218           /* we have a type by now */
219         }
220
221         if(NULL == post) {
222           /* For the first file name, we allocate and initiate the main list
223              node */
224
225           post = (struct HttpPost *)malloc(sizeof(struct HttpPost));
226           if(post) {
227             memset(post, 0, sizeof(struct HttpPost));
228             GetStr(&post->name, name);      /* get the name */
229             GetStr(&post->contents, contp); /* get the contents */
230             post->flags = flags;
231             if(type) {
232               GetStr(&post->contenttype, type); /* get type */
233               prevtype=post->contenttype; /* point to the allocated string! */
234             }
235             /* make the previous point to this */
236             if(*last_post)
237               (*last_post)->next = post;
238             else
239               (*httppost) = post;
240
241             (*last_post) = post;          
242           }
243
244         }
245         else {
246           /* we add a file name to the previously allocated node, known as
247              'post' now */
248           subpost =(struct HttpPost *)malloc(sizeof(struct HttpPost));
249           if(subpost) {
250              memset(subpost, 0, sizeof(struct HttpPost));
251              GetStr(&subpost->name, name);      /* get the name */
252              GetStr(&subpost->contents, contp); /* get the contents */
253              subpost->flags = flags;
254              if(type) {
255                GetStr(&subpost->contenttype, type); /* get type */
256                prevtype=subpost->contenttype; /* point to the allocated string! */
257              }
258              /* now, point our 'more' to the original 'more' */
259              subpost->more = post->more;
260
261              /* then move the original 'more' to point to ourselves */
262              post->more = subpost;           
263           }
264         }
265         contp = sep; /* move the contents pointer to after the separator */
266       } while(sep && *sep); /* loop if there's another file name */
267     }
268     else {
269       post = (struct HttpPost *)malloc(sizeof(struct HttpPost));
270       if(post) {
271         memset(post, 0, sizeof(struct HttpPost));
272         GetStr(&post->name, name);      /* get the name */
273         if( contp[0]=='<' ) {
274           GetStr(&post->contents, contp+1); /* get the contents */
275           post->flags = HTTPPOST_READFILE;
276         }
277         else {
278           GetStr(&post->contents, contp); /* get the contents */
279           post->flags = 0;
280         }
281
282         /* make the previous point to this */
283         if(*last_post)
284           (*last_post)->next = post;
285         else
286           (*httppost) = post;
287
288         (*last_post) = post;      
289       }
290
291     }
292
293   }
294   else {
295     fprintf(stderr, "Illegally formatted input field!\n");
296     return 1;
297   }
298   return 0;
299 }
300
301 static int AddFormData(struct FormData **formp,
302                         void *line,
303                         long length)
304 {
305   struct FormData *newform = (struct FormData *)
306     malloc(sizeof(struct FormData));
307   newform->next = NULL;
308
309   /* we make it easier for plain strings: */
310   if(!length)
311     length = strlen((char *)line);
312
313   newform->line = (char *)malloc(length+1);
314   memcpy(newform->line, line, length+1);
315   newform->length = length;
316   newform->line[length]=0; /* zero terminate for easier debugging */
317   
318   if(*formp) {
319     (*formp)->next = newform;
320     *formp = newform;
321   }
322   else
323     *formp = newform;
324
325   return length;
326 }
327
328
329 static int AddFormDataf(struct FormData **formp,
330                          char *fmt, ...)
331 {
332   char s[4096];
333   va_list ap;
334   va_start(ap, fmt);
335   vsprintf(s, fmt, ap);
336   va_end(ap);
337
338   return AddFormData(formp, s, 0);
339 }
340
341
342 char *MakeFormBoundary(void)
343 {
344   char *retstring;
345   static int randomizer=0; /* this is just so that two boundaries within
346                               the same form won't be identical */
347   int i;
348
349   static char table64[]=
350     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
351
352   retstring = (char *)malloc(BOUNDARY_LENGTH);
353
354   if(!retstring)
355     return NULL; /* failed */
356
357   srand(time(NULL)+randomizer++); /* seed */
358
359   strcpy(retstring, "curl"); /* bonus commercials 8*) */
360
361   for(i=4; i<(BOUNDARY_LENGTH-1); i++) {
362     retstring[i] = table64[rand()%64];
363   }
364   retstring[BOUNDARY_LENGTH-1]=0; /* zero terminate */
365
366   return retstring;
367 }
368
369 /* Used from http.c */ 
370 void FormFree(struct FormData *form)
371 {
372   struct FormData *next;
373   do {
374     next=form->next;  /* the following form line */
375     free(form->line); /* free the line */
376     free(form);       /* free the struct */
377   
378   } while((form=next)); /* continue */
379 }
380
381 /* external function to free up a whole form post chain */
382 void curl_formfree(struct HttpPost *form)
383 {
384   struct HttpPost *next;
385   do {
386     next=form->next;  /* the following form line */
387
388     /* recurse to sub-contents */
389     if(form->more)
390       curl_formfree(form->more);
391
392     if(form->name)
393       free(form->name); /* free the name */
394     if(form->contents)
395       free(form->contents); /* free the contents */
396     if(form->contenttype)
397       free(form->contenttype); /* free the content type */
398     free(form);       /* free the struct */
399
400   } while((form=next)); /* continue */
401 }
402
403 struct FormData *getFormData(struct HttpPost *post,
404                              int *sizep)
405 {
406   struct FormData *form = NULL;
407   struct FormData *firstform;
408
409   struct HttpPost *file;
410
411   int size =0;
412   char *boundary;
413   char *fileboundary=NULL;
414
415   if(!post)
416     return NULL; /* no input => no output! */
417
418   boundary = MakeFormBoundary();
419   
420   /* Make the first line of the output */
421   AddFormDataf(&form,
422                "Content-Type: multipart/form-data;"
423                " boundary=%s\r\n",
424                boundary);
425   /* we DO NOT count that line since that'll be part of the header! */
426
427   firstform = form;
428   
429   do {
430
431     /* boundary */
432     size += AddFormDataf(&form, "\r\n--%s\r\n", boundary);
433
434     size += AddFormDataf(&form,
435                          "Content-Disposition: form-data; name=\"%s\"",
436                          post->name);
437
438     if(post->more) {
439       /* If used, this is a link to more file names, we must then do
440          the magic to include several files with the same field name */
441
442       fileboundary = MakeFormBoundary();
443
444       size += AddFormDataf(&form,
445                            "\r\nContent-Type: multipart/mixed,"
446                            " boundary=%s\r\n",
447                            fileboundary);
448     }
449
450     file = post;
451
452     do {
453       if(post->more) {
454         /* if multiple-file */
455         size += AddFormDataf(&form,
456                              "\r\n--%s\r\nContent-Disposition: attachment; filename=\"%s\"",
457                              fileboundary, file->contents);
458       }
459       else if(post->flags & HTTPPOST_FILENAME) {
460         size += AddFormDataf(&form,
461                              "; filename=\"%s\"",
462                              post->contents);
463       }
464       
465       if(file->contenttype) {
466         /* we have a specified type */
467         size += AddFormDataf(&form,
468                              "\r\nContent-Type: %s",
469                              file->contenttype);
470       }
471
472 #if 0
473       /* The header Content-Transfer-Encoding: seems to confuse some receivers
474        * (like the built-in PHP engine). While I can't see any reason why it
475        * should, I can just as well skip this to the benefit of the users who
476        * are using such confused receivers.
477        */
478       
479       if(file->contenttype &&
480          !strnequal("text/", file->contenttype, 5)) {
481         /* this is not a text content, mention our binary encoding */
482         size += AddFormData(&form, "\r\nContent-Transfer-Encoding: binary", 0);
483       }
484 #endif
485
486       size += AddFormData(&form, "\r\n\r\n", 0);
487
488       if((post->flags & HTTPPOST_FILENAME) ||
489          (post->flags & HTTPPOST_READFILE)) {
490         /* we should include the contents from the specified file */
491         FILE *fileread;
492         char buffer[1024];
493         int nread;
494
495         fileread = strequal("-", file->contents)?stdin:
496           /* binary read for win32 crap */
497           fopen(file->contents, "rb");
498         if(fileread) {
499           while((nread = fread(buffer, 1, 1024, fileread))) {
500             size += AddFormData(&form,
501                                 buffer,
502                                 nread);
503           }
504           if(fileread != stdin)
505             fclose(fileread);
506         } else {
507           size += AddFormData(&form, "[File wasn't found by client]", 0);
508         }
509       } else {
510         /* include the contents we got */
511         size += AddFormData(&form, post->contents, 0);
512       }
513     } while((file = file->more)); /* for each specified file for this field */
514
515     if(post->more) {
516       /* this was a multiple-file inclusion, make a termination file
517          boundary: */
518       size += AddFormDataf(&form,
519                            "\r\n--%s--",
520                            fileboundary);     
521       free(fileboundary);
522     }
523
524   } while((post=post->next)); /* for each field */
525
526   /* end-boundary for everything */
527   size += AddFormDataf(&form,
528                        "\r\n--%s--\r\n",
529                        boundary);
530
531   *sizep = size;
532
533   free(boundary);
534
535   return firstform;
536 }
537
538 int FormInit(struct Form *form, struct FormData *formdata )
539 {
540   if(!formdata)
541     return 1; /* error */
542
543 #if 0  
544   struct FormData *lastnode=formdata;
545
546   /* find the last node in the list */
547   while(lastnode->next) {
548     lastnode = lastnode->next;
549   }
550
551   /* Now, make sure that we'll send a nice terminating sequence at the end
552    * of the post. We *DONT* add this string to the size of the data since this
553    * is actually AFTER the data. */
554   AddFormDataf(&lastnode, "\r\n\r\n");
555 #endif
556   form->data = formdata;
557   form->sent = 0;
558
559   return 0;
560 }
561
562 /* fread() emulation */
563 int FormReader(char *buffer,
564                size_t size,
565                size_t nitems,
566                FILE *mydata)
567 {
568   struct Form *form;
569   int wantedsize;
570   int gotsize;
571
572   form=(struct Form *)mydata;
573
574   wantedsize = size * nitems;
575
576   if(!form->data)
577     return -1; /* nothing, error, empty */
578
579   do {
580   
581     if( (form->data->length - form->sent ) > wantedsize ) {
582
583       memcpy(buffer, form->data->line + form->sent, wantedsize);
584
585       form->sent += wantedsize;
586
587       return wantedsize;
588     }
589
590     memcpy(buffer,
591            form->data->line + form->sent,
592            gotsize = (form->data->length - form->sent) );
593
594     form->sent = 0;
595
596     form->data = form->data->next; /* advance */
597
598   } while(!gotsize && form->data);
599   /* If we got an empty line and we have more data, we proceed to the next
600      line immediately to avoid returning zero before we've reached the end.
601      This is the bug reported November 22 1999 on curl 6.3. (Daniel) */
602
603   return gotsize;
604 }
605
606
607 #ifdef _FORM_DEBUG
608
609 int main(int argc, char **argv)
610 {
611 #if 0
612   char *testargs[]={
613     "name1 = data in number one",
614     "name2 = number two data",
615     "test = @upload"
616   };
617 #endif
618   int i;
619   char *nextarg;
620   struct HttpPost *httppost=NULL;
621   struct HttpPost *last_post=NULL;
622   struct HttpPost *post;
623   int size;
624   int nread;
625   char buffer[4096];
626
627   struct FormData *form;
628   struct Form formread;
629
630   for(i=1; i<argc; i++) {
631
632     if( FormParse( argv[i],
633                    &httppost,
634                    &last_post)) {
635       fprintf(stderr, "Illegally formatted input field: '%s'!\n",
636               argv[i]);
637       return 1;
638     }
639   }
640
641   form=getFormData(httppost, &size);
642
643   FormInit(&formread, form);
644
645   while(nread = FormReader(buffer, 1, sizeof(buffer), (FILE *)&formread)) {
646     fwrite(buffer, nread, 1, stderr);
647   }
648
649   fprintf(stderr, "size: %d\n", size);
650
651   return 0;
652 }
653
654 #endif