Skip to content
Snippets Groups Projects
formdata.c 45.9 KiB
Newer Older
  • Learn to ignore specific revisions
  •   *finalform=NULL; /* default form is empty */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
      if(!post)
    
        return result; /* no input => no output! */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
      if(!boundary)
        return CURLE_OUT_OF_MEMORY;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      /* Make the first line of the output */
    
      result = AddFormDataf(&form, NULL,
                            "Content-Type: multipart/form-data;"
                            " boundary=%s\r\n",
                            boundary);
      if (result) {
    
      /* we DO NOT include that line in the total size of the POST, since it'll be
         part of the header! */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
      firstform = form;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      do {
    
    
        if(size) {
          result = AddFormDataf(&form, &size, "\r\n");
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        /* boundary */
    
        result = AddFormDataf(&form, &size, "--%s\r\n", boundary);
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
        result = AddFormDataf(&form, &size,
                              "Content-Disposition: form-data; name=\"");
    
        result = AddFormData(&form, FORM_DATA, post->name, post->namelength,
                             &size);
    
        result = AddFormDataf(&form, &size, "\"");
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
        if(post->more) {
          /* If used, this is a link to more file names, we must then do
             the magic to include several files with the same field name */
    
    
          fileboundary = Curl_FormBoundary();
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
          result = AddFormDataf(&form, &size,
    
                                "\r\nContent-Type: multipart/mixed,"
                                " boundary=%s\r\n",
                                fileboundary);
          if (result)
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        }
    
        file = post;
    
        do {
    
    
          /* If 'showfilename' is set, that is a faked name passed on to us
             to use to in the formpost. If that is not set, the actually used
             local file name should be added. */
    
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
          if(post->more) {
    
            char *filebasename=
              (!file->showfilename)?strippath(file->contents):NULL;
    
    
            result = AddFormDataf(&form, &size,
    
                                  "\r\n--%s\r\nContent-Disposition: "
                                  "attachment; filename=\"%s\"",
                                  fileboundary,
                                  (file->showfilename?file->showfilename:
    
                                   filebasename));
            if (filebasename)
              free(filebasename);
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
          }
    
          else if((post->flags & HTTPPOST_FILENAME) ||
                  (post->flags & HTTPPOST_BUFFER)) {
    
    
            char *filebasename=
              (!post->showfilename)?strippath(post->contents):NULL;
    
    
            result = AddFormDataf(&form, &size,
    
                                  "; filename=\"%s\"",
                                  (post->showfilename?post->showfilename:
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
          }
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
          if(file->contenttype) {
    
            result = AddFormDataf(&form, &size,
    
                                  "\r\nContent-Type: %s",
                                  file->contenttype);
            if (result)
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
          }
    
          curList = file->contentheader;
          while( curList ) {
            /* Process the additional headers specified for this form */
    
            result = AddFormDataf( &form, &size, "\r\n%s", curList->data );
    
            Curl_formclean(firstform);
            free(boundary);
            return result;
          }
    
    #if 0
          /* The header Content-Transfer-Encoding: seems to confuse some receivers
           * (like the built-in PHP engine). While I can't see any reason why it
           * should, I can just as well skip this to the benefit of the users who
           * are using such confused receivers.
           */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
          if(file->contenttype &&
    
             !checkprefix("text/", file->contenttype)) {
    
            /* this is not a text content, mention our binary encoding */
            size += AddFormData(&form, "\r\nContent-Transfer-Encoding: binary", 0);
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
          }
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
          result = AddFormDataf(&form, &size, "\r\n\r\n");
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
          if((post->flags & HTTPPOST_FILENAME) ||
    
             (post->flags & HTTPPOST_READFILE)) {
            /* we should include the contents from the specified file */
            FILE *fileread;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
            fileread = strequal("-", file->contents)?
              stdin:fopen(file->contents, "rb"); /* binary read for win32  */
    
            /*
             * VMS: This only allows for stream files on VMS.  Stream files are
             * OK, as are FIXED & VAR files WITHOUT implied CC For implied CC,
             * every record needs to have a \n appended & 1 added to SIZE
             */
    
    
              if(fileread != stdin) {
                /* close the file again */
    
                /* add the file name only - for later reading from this */
                result = AddFormData(&form, FORM_FILE, file->contents, 0, &size);
              }
              else {
                /* When uploading from stdin, we can't know the size of the file,
                 * thus must read the full file as before. We *could* use chunked
                 * transfer-encoding, but that only works for HTTP 1.1 and we
                 * can't be sure we work with such a server.
                 */
                size_t nread;
                char buffer[512];
                while((nread = fread(buffer, 1, sizeof(buffer), fileread))) {
                  result = AddFormData(&form, FORM_DATA, buffer, nread, &size);
                  if (result)
                    break;
                }
              }
    
    
                Curl_formclean(firstform);
                free(boundary);
                return result;
              }
    
              Curl_formclean(firstform);
              free(boundary);
              *finalform = NULL;
              return CURLE_READ_ERROR;
    
          else if (post->flags & HTTPPOST_BUFFER) {
    
            /* include contents of buffer */
    
            result = AddFormData(&form, FORM_DATA, post->buffer,
                                 post->bufferlength, &size);
    
            result = AddFormData(&form, FORM_DATA, post->contents,
                                 post->contentslength, &size);
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
          }
        } while((file = file->more)); /* for each specified file for this field */
    
          Curl_formclean(firstform);
          free(boundary);
          return result;
        }
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
        if(post->more) {
          /* this was a multiple-file inclusion, make a termination file
             boundary: */
    
          result = AddFormDataf(&form, &size,
    
                               fileboundary);
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
          free(fileboundary);
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        }
    
      } while((post=post->next)); /* for each field */
    
        Curl_formclean(firstform);
        free(boundary);
        return result;
      }
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
      /* end-boundary for everything */
    
      result = AddFormDataf(&form, &size,
    
        Curl_formclean(firstform);
        free(boundary);
        return result;
      }
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
      *sizep = size;
    
      free(boundary);
    
    
    /*
     * Curl_FormInit() inits the struct 'form' points to with the 'formdata'
     * and resets the 'sent' counter.
     */
    
    int Curl_FormInit(struct Form *form, struct FormData *formdata )
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    {
      if(!formdata)
        return 1; /* error */
    
      form->data = formdata;
      form->sent = 0;
    
    static size_t readfromfile(struct Form *form, char *buffer, size_t size)
    {
      size_t nread;
      if(!form->fp) {
        /* this file hasn't yet been opened */
        form->fp = fopen(form->data->line, "rb"); /* b is for binary */
        if(!form->fp)
          return -1; /* failure */
      }
      nread = fread(buffer, 1, size, form->fp);
    
      if(nread != size) {
        /* this is the last chunk form the file, move on */
        fclose(form->fp);
        form->fp = NULL;
        form->data = form->data->next;
      }
    
      return nread;
    }
    
    
    /*
     * Curl_FormReader() is the fread() emulation function that will be used to
     * deliver the formdata to the transfer loop and then sent away to the peer.
     */
    
    size_t Curl_FormReader(char *buffer,
                           size_t size,
                           size_t nitems,
                           FILE *mydata)
    
      size_t wantedsize;
      size_t gotsize = 0;
    
    
      form=(struct Form *)mydata;
    
      wantedsize = size * nitems;
    
      if(!form->data)
    
      if(form->data->type == FORM_FILE) {
        gotsize = readfromfile(form, buffer, wantedsize);
    
        if(gotsize)
          /* If positive or -1, return. If zero, continue! */
          return gotsize;
      }
    
        if( (form->data->length - form->sent ) > wantedsize - gotsize) {
    
          memcpy(buffer + gotsize , form->data->line + form->sent,
                 wantedsize - gotsize);
    
          form->sent += wantedsize-gotsize;
    
          return wantedsize;
        }
    
        memcpy(buffer+gotsize,
               form->data->line + form->sent,
               (form->data->length - form->sent) );
        gotsize += form->data->length - form->sent;
    
        form->sent = 0;
    
        form->data = form->data->next; /* advance */
    
    
      } while(form->data && (form->data->type == FORM_DATA));
    
      /* If we got an empty line and we have more data, we proceed to the next
         line immediately to avoid returning zero before we've reached the end.
         This is the bug reported November 22 1999 on curl 6.3. (Daniel) */
    
      return gotsize;
    }
    
    
    /*
     * Curl_formpostheader() returns the first line of the formpost, the
     * request-header part (which is not part of the request-body like the rest of
     * the post).
     */
    char *Curl_formpostheader(void *formp, size_t *len)
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    {
    
      char *header;
      struct Form *form=(struct Form *)formp;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
      if(!form->data)
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
      header = form->data->line;
      *len = form->data->length;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
      form->data = form->data->next; /* advance */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    }
    
    
    #ifdef _FORM_DEBUG
    
    int FormAddTest(const char * errormsg,
    
                     struct curl_httppost **httppost,
                     struct curl_httppost **last_post,
    
                     ...)
    {
      int result;
      va_list arg;
      va_start(arg, last_post);
      if ((result = FormAdd(httppost, last_post, arg)))
        fprintf (stderr, "ERROR doing FormAdd ret: %d action: %s\n", result,
                 errormsg);
      va_end(arg);
      return result;
    }
    
    
    int main()
    {
      char name1[] = "simple_COPYCONTENTS";
      char name2[] = "COPYCONTENTS_+_CONTENTTYPE";
    
      char name3[] = "PTRNAME_+_NAMELENGTH_+_COPYNAME_+_CONTENTSLENGTH";
      char name4[] = "simple_PTRCONTENTS";
      char name5[] = "PTRCONTENTS_+_CONTENTSLENGTH";
      char name6[] = "PTRCONTENTS_+_CONTENTSLENGTH_+_CONTENTTYPE";
      char name7[] = "FILE1_+_CONTENTTYPE";
      char name8[] = "FILE1_+_FILE2";
      char name9[] = "FILE1_+_FILE2_+_FILE3";
    
      char name10[] = "ARRAY: FILE1_+_FILE2_+_FILE3";
      char name11[] = "FILECONTENT";
    
      char value1[] = "value for simple COPYCONTENTS";
      char value2[] = "value for COPYCONTENTS + CONTENTTYPE";
    
      char value3[] = "value for PTRNAME + NAMELENGTH + COPYNAME + CONTENTSLENGTH";
      char value4[] = "value for simple PTRCONTENTS";
      char value5[] = "value for PTRCONTENTS + CONTENTSLENGTH";
      char value6[] = "value for PTRCOTNENTS + CONTENTSLENGTH + CONTENTTYPE";
      char value7[] = "inet_ntoa_r.h";
      char value8[] = "Makefile.b32.resp";
    
      char type2[] = "image/gif";
    
      char type6[] = "text/plain";
      char type7[] = "text/html";
      int name3length = strlen(name3);
      int value3length = strlen(value3);
      int value5length = strlen(value4);
      int value6length = strlen(value5);
    
      struct curl_httppost *httppost=NULL;
      struct curl_httppost *last_post=NULL;
    
      struct curl_forms forms[4];
    
    
      struct FormData *form;
      struct Form formread;
    
      if (FormAddTest("simple COPYCONTENTS test", &httppost, &last_post,
                      CURLFORM_COPYNAME, name1, CURLFORM_COPYCONTENTS, value1,
                      CURLFORM_END))
        ++errors;
      if (FormAddTest("COPYCONTENTS  + CONTENTTYPE test", &httppost, &last_post,
                      CURLFORM_COPYNAME, name2, CURLFORM_COPYCONTENTS, value2,
                      CURLFORM_CONTENTTYPE, type2, CURLFORM_END))
        ++errors;
    
      /* make null character at start to check that contentslength works
         correctly */
      name3[1] = '\0';
      value3[1] = '\0';
      if (FormAddTest("PTRNAME + NAMELENGTH + COPYNAME + CONTENTSLENGTH test",
    
                      &httppost, &last_post,
    
                      CURLFORM_PTRNAME, name3, CURLFORM_COPYCONTENTS, value3,
                      CURLFORM_CONTENTSLENGTH, value3length,
    
                      CURLFORM_NAMELENGTH, name3length, CURLFORM_END))
    
      if (FormAddTest("simple PTRCONTENTS test", &httppost, &last_post,
    
                      CURLFORM_COPYNAME, name4, CURLFORM_PTRCONTENTS, value4,
    
                      CURLFORM_END))
        ++errors;
      /* make null character at start to check that contentslength works
         correctly */
    
      if (FormAddTest("PTRCONTENTS + CONTENTSLENGTH test", &httppost, &last_post,
    
                      CURLFORM_COPYNAME, name5, CURLFORM_PTRCONTENTS, value5,
                      CURLFORM_CONTENTSLENGTH, value5length, CURLFORM_END))
    
        ++errors;
      /* make null character at start to check that contentslength works
         correctly */
    
      if (FormAddTest("PTRCONTENTS + CONTENTSLENGTH + CONTENTTYPE test",
                      &httppost, &last_post,
    
                      CURLFORM_COPYNAME, name6, CURLFORM_PTRCONTENTS, value6,
                      CURLFORM_CONTENTSLENGTH, value6length,
                      CURLFORM_CONTENTTYPE, type6, CURLFORM_END))
    
        ++errors;
      if (FormAddTest("FILE + CONTENTTYPE test", &httppost, &last_post,
    
                      CURLFORM_COPYNAME, name7, CURLFORM_FILE, value7,
                      CURLFORM_CONTENTTYPE, type7, CURLFORM_END))
    
        ++errors;
      if (FormAddTest("FILE1 + FILE2 test", &httppost, &last_post,
    
                      CURLFORM_COPYNAME, name8, CURLFORM_FILE, value7,
                      CURLFORM_FILE, value8, CURLFORM_END))
        ++errors;
      if (FormAddTest("FILE1 + FILE2 + FILE3 test", &httppost, &last_post,
                      CURLFORM_COPYNAME, name9, CURLFORM_FILE, value7,
                      CURLFORM_FILE, value8, CURLFORM_FILE, value7, CURLFORM_END))
    
      forms[0].option = CURLFORM_FILE;
      forms[0].value  = value7;
      forms[1].option = CURLFORM_FILE;
      forms[1].value  = value8;
      forms[2].option = CURLFORM_FILE;
      forms[2].value  = value7;
      forms[3].option  = CURLFORM_END;
      if (FormAddTest("FILE1 + FILE2 + FILE3 ARRAY test", &httppost, &last_post,
                      CURLFORM_COPYNAME, name10, CURLFORM_ARRAY, forms,
                      CURLFORM_END))
        ++errors;
      if (FormAddTest("FILECONTENT test", &httppost, &last_post,
                      CURLFORM_COPYNAME, name11, CURLFORM_FILECONTENT, value7,
                      CURLFORM_END))
        ++errors;
    
    
      form=Curl_getFormData(httppost, &size);
    
      Curl_FormInit(&formread, form);
    
      do {
        nread = Curl_FormReader(buffer, 1, sizeof(buffer),
                                (FILE *)&formread);
    
        if(-1 == nread)
          break;
    
        fwrite(buffer, nread, 1, stdout);
    
      fprintf(stdout, "size: %d\n", size);
    
        fprintf(stdout, "\n==> %d Test(s) failed!\n", errors);
    
      else
        fprintf(stdout, "\nAll Tests seem to have worked (please check output)\n");
    
      return 0;
    }
    
    
    #else  /* CURL_DISABLE_HTTP */
    CURLFORMcode curl_formadd(struct curl_httppost **httppost,
                              struct curl_httppost **last_post,
                              ...)
    {
      (void)httppost;
      (void)last_post;
      return CURL_FORMADD_DISABLED;
    }
    
    
    void curl_formfree(struct curl_httppost *form)
    {
      (void)form;
      /* does nothing HTTP is disabled */
    }
    
    
    #if !defined(CURL_DISABLE_HTTP) || defined(USE_SSLEAY)
    
    
    /*
     * Curl_FormBoundary() creates a suitable boundary string and returns an
     * allocated one. This is also used by SSL-code so it must be present even
     * if HTTP is disabled!
     */
    char *Curl_FormBoundary(void)
    {
      char *retstring;
    
      static int randomizer;   /* this is just so that two boundaries within
    
                                  the same form won't be identical */
    
      static const char table16[]="abcdef0123456789";
    
    
      retstring = (char *)malloc(BOUNDARY_LENGTH+1);
    
      if(!retstring)
        return NULL; /* failed */
    
    
      srand((unsigned int)time(NULL)+randomizer++); /* seed */
    
    
      strcpy(retstring, "----------------------------");
    
      for(i=strlen(retstring); i<BOUNDARY_LENGTH; i++)
        retstring[i] = table16[rand()%16];
    
      /* 28 dashes and 12 hexadecimal digits makes 12^16 (184884258895036416)
         combinations */
      retstring[BOUNDARY_LENGTH]=0; /* zero terminate */
    
      return retstring;
    }
    
    
    #endif  /* !defined(CURL_DISABLE_HTTP) || defined(USE_SSLEAY) */