Skip to content
formdata.c 45.6 KiB
Newer Older
Daniel Stenberg's avatar
Daniel Stenberg committed

  newform->line = (char *)malloc(length+1);
Daniel Stenberg's avatar
Daniel Stenberg committed
  newform->length = length;
  newform->line[length]=0; /* zero terminate for easier debugging */
Daniel Stenberg's avatar
Daniel Stenberg committed
  
  if(*formp) {
    (*formp)->next = newform;
    *formp = newform;
  }
  else
    *formp = newform;

  return length;
}


static size_t AddFormDataf(struct FormData **formp,
Daniel Stenberg's avatar
Daniel Stenberg committed
{
  char s[4096];
Daniel Stenberg's avatar
Daniel Stenberg committed
  va_list ap;
  va_start(ap, fmt);
  vsprintf(s, fmt, ap);
  va_end(ap);

  return AddFormData(formp, s, 0);
}


Daniel Stenberg's avatar
Daniel Stenberg committed
{
  char *retstring;
  static int randomizer=0; /* this is just so that two boundaries within
			      the same form won't be identical */
Daniel Stenberg's avatar
Daniel Stenberg committed

  static char table16[]="abcdef0123456789";
Daniel Stenberg's avatar
Daniel Stenberg committed

  retstring = (char *)malloc(BOUNDARY_LENGTH+1);
Daniel Stenberg's avatar
Daniel Stenberg committed

  if(!retstring)
    return NULL; /* failed */

  srand(time(NULL)+randomizer++); /* seed */

  strcpy(retstring, "----------------------------");
Daniel Stenberg's avatar
Daniel Stenberg committed

  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 */
Daniel Stenberg's avatar
Daniel Stenberg committed

  return retstring;
}

/* Used from http.c, this cleans a built FormData linked list */ 
void Curl_formclean(struct FormData *form)
Daniel Stenberg's avatar
Daniel Stenberg committed
{
  struct FormData *next;
Daniel Stenberg's avatar
Daniel Stenberg committed
  do {
    next=form->next;  /* the following form line */
    free(form->line); /* free the line */
    free(form);       /* free the struct */
Daniel Stenberg's avatar
Daniel Stenberg committed
  
  } while((form=next)); /* continue */
}

/* external function to free up a whole form post chain */
void curl_formfree(struct curl_httppost *form)

  if(!form)
    /* no form to free, just get out of this */
    return;

Daniel Stenberg's avatar
Daniel Stenberg committed
  do {
    next=form->next;  /* the following form line */

    /* recurse to sub-contents */
    if(form->more)
      curl_formfree(form->more);

    if( !(form->flags & HTTPPOST_PTRNAME) && form->name)
Daniel Stenberg's avatar
Daniel Stenberg committed
      free(form->name); /* free the name */
    if( !(form->flags & HTTPPOST_PTRCONTENTS) && form->contents)
Daniel Stenberg's avatar
Daniel Stenberg committed
      free(form->contents); /* free the contents */
    if(form->contenttype)
      free(form->contenttype); /* free the content type */
    if(form->showfilename)
      free(form->showfilename); /* free the faked file name */
Daniel Stenberg's avatar
Daniel Stenberg committed
    free(form);       /* free the struct */
Daniel Stenberg's avatar
Daniel Stenberg committed

  } while((form=next)); /* continue */
CURLcode Curl_getFormData(struct FormData **finalform,
                          struct curl_httppost *post,
Daniel Stenberg's avatar
Daniel Stenberg committed
{
  struct FormData *form = NULL;
  struct FormData *firstform;
Daniel Stenberg's avatar
Daniel Stenberg committed

Daniel Stenberg's avatar
Daniel Stenberg committed
  char *boundary;
  char *fileboundary=NULL;
  *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

Daniel Stenberg's avatar
Daniel Stenberg committed
  
  /* Make the first line of the output */
  AddFormDataf(&form,
               "Content-Type: multipart/form-data;"
               " boundary=%s\r\n",
               boundary);
  /* we DO NOT count that line since that'll be part of the header! */

  firstform = form;
  
  do {

    if(size)
      size += AddFormDataf(&form, "\r\n");

Daniel Stenberg's avatar
Daniel Stenberg committed
    /* boundary */
    size += AddFormDataf(&form, "--%s\r\n", boundary);
Daniel Stenberg's avatar
Daniel Stenberg committed

    size += AddFormData(&form,
                        "Content-Disposition: form-data; name=\"", 0);

    size += AddFormData(&form, post->name, post->namelength);

    size += AddFormData(&form, "\"", 0);
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

      size += AddFormDataf(&form,
                           "\r\nContent-Type: multipart/mixed,"
                           " boundary=%s\r\n",
                           fileboundary);
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) {
        /* if multiple-file */
        size += AddFormDataf(&form,
                             "\r\n--%s\r\nContent-Disposition: "
                             (file->showfilename?file->showfilename:
                              file->contents));
Daniel Stenberg's avatar
Daniel Stenberg committed
      }
      else if((post->flags & HTTPPOST_FILENAME) ||
              (post->flags & HTTPPOST_BUFFER)) {

        size += AddFormDataf(&form,
                             "; filename=\"%s\"",
                             (post->showfilename?post->showfilename:
Daniel Stenberg's avatar
Daniel Stenberg committed
      }
      
      if(file->contenttype) {
        /* we have a specified type */
        size += AddFormDataf(&form,
                             "\r\nContent-Type: %s",
                             file->contenttype);
Daniel Stenberg's avatar
Daniel Stenberg committed
      }
      curList = file->contentheader;
      while( curList ) {
        /* Process the additional headers specified for this form */
        size += AddFormDataf( &form, "\r\n%s", curList->data );
        curList = curList->next;
      }

#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

      size += AddFormData(&form, "\r\n\r\n", 0);
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;
        char buffer[1024];
Daniel Stenberg's avatar
Daniel Stenberg committed

        fileread = strequal("-", file->contents)?stdin:
Daniel Stenberg's avatar
Daniel Stenberg committed
          /* binary read for win32 crap */
          /*VMS??*/ fopen(file->contents, "rb");  /* ONLY ALLOWS FOR STREAM FILES ON VMS */
        /*VMS?? Stream files are OK, as are FIXED & VAR files WITHOUT implied CC */
        /*VMS?? For implied CC, every record needs to have a \n appended & 1 added to SIZE */
        if(fileread) {
          while((nread = fread(buffer, 1, 1024, fileread)))
            size += AddFormData(&form, buffer, nread);
Daniel Stenberg's avatar
Daniel Stenberg committed
          if(fileread != stdin)
            fclose(fileread);
          /* File wasn't found, add a nothing field! */
#endif
          Curl_formclean(firstform);
          free(boundary);
          *finalform = NULL;
          return CURLE_READ_ERROR;
      } 
      else if (post->flags & HTTPPOST_BUFFER) {
          /* include contents of buffer */
          size += AddFormData(&form, post->buffer, post->bufferlength);
        /* include the contents we got */
        size += AddFormData(&form, post->contents, post->contentslength);
Daniel Stenberg's avatar
Daniel Stenberg committed
      }
    } while((file = file->more)); /* for each specified file for this field */

    if(post->more) {
      /* this was a multiple-file inclusion, make a termination file
         boundary: */
      size += AddFormDataf(&form,
Daniel Stenberg's avatar
Daniel Stenberg committed
      free(fileboundary);
    }

  } while((post=post->next)); /* for each field */

  /* end-boundary for everything */
  size += AddFormDataf(&form,
Daniel Stenberg's avatar
Daniel Stenberg committed

  *sizep = size;

  free(boundary);

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;
Daniel Stenberg's avatar
Daniel Stenberg committed

  return 0;
}

/* fread() emulation */
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)

  do {
  
    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);
  /* 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;
}

/* possible (old) fread() emulation that copies at most one line */
size_t Curl_FormReadOneLine(char *buffer,
                            size_t size,
                            size_t nitems,
                            FILE *mydata)
Daniel Stenberg's avatar
Daniel Stenberg committed
{
  struct Form *form;
  size_t wantedsize;
  size_t gotsize;
Daniel Stenberg's avatar
Daniel Stenberg committed

  form=(struct Form *)mydata;

  wantedsize = size * nitems;

  if(!form->data)
    return 0; /* nothing, error, empty */
Daniel Stenberg's avatar
Daniel Stenberg committed

  do {
  
    if( (form->data->length - form->sent ) > wantedsize ) {

      memcpy(buffer, form->data->line + form->sent, wantedsize);

      form->sent += wantedsize;

      return wantedsize;
    }

    memcpy(buffer,
           form->data->line + form->sent,
           gotsize = (form->data->length - form->sent) );

    form->sent = 0;

    form->data = form->data->next; /* advance */

  } while(!gotsize && 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;
}


#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))
    ++errors;
  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;
}

#endif

#ifdef _OLD_FORM_DEBUG
Daniel Stenberg's avatar
Daniel Stenberg committed

int main(int argc, char **argv)
{
#if 0
  char *testargs[]={
    "name1 = data in number one",
    "name2 = number two data",
    "test = @upload"
  };
#endif
  int i;
  char *nextarg;
  struct curl_httppost *httppost=NULL;
  struct curl_httppost *last_post=NULL;
  struct curl_httppost *post;
Daniel Stenberg's avatar
Daniel Stenberg committed
  int size;
  int nread;
  char buffer[4096];

  struct FormData *form;
  struct Form formread;

  for(i=1; i<argc; i++) {

    if( FormParse( argv[i],
		   &httppost,
		   &last_post)) {
      fprintf(stderr, "Illegally formatted input field: '%s'!\n",
	      argv[i]);
      return 1;
    }
  }

  form=Curl_getFormData(httppost, &size);
Daniel Stenberg's avatar
Daniel Stenberg committed

Daniel Stenberg's avatar
Daniel Stenberg committed

  do {
    nread = Curl_FormReader(buffer, 1, sizeof(buffer),
                            (FILE *)&formread);

    if(-1 == nread)
      break;
Daniel Stenberg's avatar
Daniel Stenberg committed
    fwrite(buffer, nread, 1, stderr);
Daniel Stenberg's avatar
Daniel Stenberg committed

  fprintf(stderr, "size: %d\n", size);

  return 0;
}

#endif