Beispiel #1
0
/**
 * Create a `struct MHD_PostProcessor`.
 *
 * A `struct MHD_PostProcessor` can be used to (incrementally) parse
 * the data portion of a POST request.  Note that some buggy browsers
 * fail to set the encoding type.  If you want to support those, you
 * may have to call #MHD_set_connection_value with the proper encoding
 * type before creating a post processor (if no supported encoding
 * type is set, this function will fail).
 *
 * @param connection the connection on which the POST is
 *        happening (used to determine the POST format)
 * @param buffer_size maximum number of bytes to use for
 *        internal buffering (used only for the parsing,
 *        specifically the parsing of the keys).  A
 *        tiny value (256-1024) should be sufficient.
 *        Do NOT use a value smaller than 256.  For good
 *        performance, use 32 or 64k (i.e. 65536).
 * @param iter iterator to be called with the parsed data,
 *        Must NOT be NULL.
 * @param iter_cls first argument to @a iter
 * @return NULL on error (out of memory, unsupported encoding),
 *         otherwise a PP handle
 * @ingroup request
 */
struct MHD_PostProcessor *
MHD_create_post_processor (struct MHD_Connection *connection,
                           size_t buffer_size,
                           MHD_PostDataIterator iter, void *iter_cls)
{
  struct MHD_PostProcessor *ret;
  const char *encoding;
  const char *boundary;
  size_t blen;

  if ((buffer_size < 256) || (connection == NULL) || (iter == NULL))
    mhd_panic (mhd_panic_cls, __FILE__, __LINE__, NULL);
  encoding = MHD_lookup_connection_value (connection,
                                          MHD_HEADER_KIND,
                                          MHD_HTTP_HEADER_CONTENT_TYPE);
  if (encoding == NULL)
    return NULL;
  boundary = NULL;
  if (!MHD_str_equal_caseless_n_ (MHD_HTTP_POST_ENCODING_FORM_URLENCODED, encoding,
                        strlen (MHD_HTTP_POST_ENCODING_FORM_URLENCODED)))
    {
      if (!MHD_str_equal_caseless_n_ (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA, encoding,
                       strlen (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA)))
        return NULL;
      boundary =
        &encoding[strlen (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA)];
      /* Q: should this be "strcasestr"? */
      boundary = strstr (boundary, "boundary=");
      if (NULL == boundary)
	return NULL; /* failed to determine boundary */
      boundary += strlen ("boundary=");
      blen = strlen (boundary);
      if ((blen == 0) || (blen * 2 + 2 > buffer_size))
        return NULL;            /* (will be) out of memory or invalid boundary */
      if ( (boundary[0] == '"') && (boundary[blen - 1] == '"') )
	{
	  /* remove enclosing quotes */
	  ++boundary;
	  blen -= 2;
	}
    }
  else
    blen = 0;
  buffer_size += 4; /* round up to get nice block sizes despite boundary search */

  /* add +1 to ensure we ALWAYS have a zero-termination at the end */
  if (NULL == (ret = malloc (sizeof (struct MHD_PostProcessor) + buffer_size + 1)))
    return NULL;
  memset (ret, 0, sizeof (struct MHD_PostProcessor) + buffer_size + 1);
  ret->connection = connection;
  ret->ikvi = iter;
  ret->cls = iter_cls;
  ret->encoding = encoding;
  ret->buffer_size = buffer_size;
  ret->state = PP_Init;
  ret->blen = blen;
  ret->boundary = boundary;
  ret->skip_rn = RN_Inactive;
  return ret;
}
Beispiel #2
0
/**
 * Decode multipart POST data.
 *
 * @param pp post processor context
 */
static int
post_process_multipart (struct MHD_PostProcessor *pp,
                        const char *post_data,
			size_t post_data_len)
{
  char *buf;
  size_t max;
  size_t ioff;
  size_t poff;
  int state_changed;

  buf = (char *) &pp[1];
  ioff = 0;
  poff = 0;
  state_changed = 1;
  while ((poff < post_data_len) ||
         ((pp->buffer_pos > 0) && (state_changed != 0)))
    {
      /* first, move as much input data
         as possible to our internal buffer */
      max = pp->buffer_size - pp->buffer_pos;
      if (max > post_data_len - poff)
        max = post_data_len - poff;
      memcpy (&buf[pp->buffer_pos], &post_data[poff], max);
      poff += max;
      pp->buffer_pos += max;
      if ((max == 0) && (state_changed == 0) && (poff < post_data_len))
        {
          pp->state = PP_Error;
          return MHD_NO;        /* out of memory */
        }
      state_changed = 0;

      /* first state machine for '\r'-'\n' and '--' handling */
      switch (pp->skip_rn)
        {
        case RN_Inactive:
          break;
        case RN_OptN:
          if (buf[0] == '\n')
            {
              ioff++;
              pp->skip_rn = RN_Inactive;
              goto AGAIN;
            }
          /* fall-through! */
        case RN_Dash:
          if (buf[0] == '-')
            {
              ioff++;
              pp->skip_rn = RN_Dash2;
              goto AGAIN;
            }
          pp->skip_rn = RN_Full;
          /* fall-through! */
        case RN_Full:
          if (buf[0] == '\r')
            {
              if ((pp->buffer_pos > 1) && (buf[1] == '\n'))
                {
                  pp->skip_rn = RN_Inactive;
                  ioff += 2;
                }
              else
                {
                  pp->skip_rn = RN_OptN;
                  ioff++;
                }
              goto AGAIN;
            }
          if (buf[0] == '\n')
            {
              ioff++;
              pp->skip_rn = RN_Inactive;
              goto AGAIN;
            }
          pp->skip_rn = RN_Inactive;
          pp->state = PP_Error;
          return MHD_NO;        /* no '\r\n' */
        case RN_Dash2:
          if (buf[0] == '-')
            {
              ioff++;
              pp->skip_rn = RN_Full;
              pp->state = pp->dash_state;
              goto AGAIN;
            }
          pp->state = PP_Error;
          break;
        }

      /* main state engine */
      switch (pp->state)
        {
        case PP_Error:
          return MHD_NO;
        case PP_Done:
          /* did not expect to receive more data */
          pp->state = PP_Error;
          return MHD_NO;
        case PP_Init:
          /**
           * Per RFC2046 5.1.1 NOTE TO IMPLEMENTORS, consume anything
           * prior to the first multipart boundary:
           *
           * > There appears to be room for additional information prior
           * > to the first boundary delimiter line and following the
           * > final boundary delimiter line.  These areas should
           * > generally be left blank, and implementations must ignore
           * > anything that appears before the first boundary delimiter
           * > line or after the last one.
           */
          (void) find_boundary (pp,
				pp->boundary,
				pp->blen,
				&ioff,
				PP_ProcessEntryHeaders, PP_Done);
          break;
        case PP_NextBoundary:
          if (MHD_NO == find_boundary (pp,
                                       pp->boundary,
                                       pp->blen,
                                       &ioff,
                                       PP_ProcessEntryHeaders, PP_Done))
            {
              if (pp->state == PP_Error)
                return MHD_NO;
              goto END;
            }
          break;
        case PP_ProcessEntryHeaders:
	  pp->must_ikvi = MHD_YES;
          if (MHD_NO ==
              process_multipart_headers (pp, &ioff, PP_PerformCheckMultipart))
            {
              if (pp->state == PP_Error)
                return MHD_NO;
              else
                goto END;
            }
          state_changed = 1;
          break;
        case PP_PerformCheckMultipart:
          if ((pp->content_type != NULL) &&
              (0 == strncasecmp (pp->content_type,
                                 "multipart/mixed",
                                 strlen ("multipart/mixed"))))
            {
              pp->nested_boundary = strstr (pp->content_type, "boundary=");
              if (pp->nested_boundary == NULL)
                {
                  pp->state = PP_Error;
                  return MHD_NO;
                }
              pp->nested_boundary =
                strdup (&pp->nested_boundary[strlen ("boundary=")]);
              if (pp->nested_boundary == NULL)
                {
                  /* out of memory */
                  pp->state = PP_Error;
                  return MHD_NO;
                }
              /* free old content type, we will need that field
                 for the content type of the nested elements */
              free (pp->content_type);
              pp->content_type = NULL;
              pp->nlen = strlen (pp->nested_boundary);
              pp->state = PP_Nested_Init;
              state_changed = 1;
              break;
            }
          pp->state = PP_ProcessValueToBoundary;
          pp->value_offset = 0;
          state_changed = 1;
          break;
        case PP_ProcessValueToBoundary:
          if (MHD_NO == process_value_to_boundary (pp,
                                                   &ioff,
                                                   pp->boundary,
                                                   pp->blen,
                                                   PP_PerformCleanup,
                                                   PP_Done))
            {
              if (pp->state == PP_Error)
                return MHD_NO;
              break;
            }
          break;
        case PP_PerformCleanup:
          /* clean up state of one multipart form-data element! */
          pp->have = NE_none;
          free_unmarked (pp);
          if (pp->nested_boundary != NULL)
            {
              free (pp->nested_boundary);
              pp->nested_boundary = NULL;
            }
          pp->state = PP_ProcessEntryHeaders;
          state_changed = 1;
          break;
        case PP_Nested_Init:
          if (pp->nested_boundary == NULL)
            {
              pp->state = PP_Error;
              return MHD_NO;
            }
          if (MHD_NO == find_boundary (pp,
                                       pp->nested_boundary,
                                       pp->nlen,
                                       &ioff,
                                       PP_Nested_PerformMarking,
                                       PP_NextBoundary /* or PP_Error? */ ))
            {
              if (pp->state == PP_Error)
                return MHD_NO;
              goto END;
            }
          break;
        case PP_Nested_PerformMarking:
          /* remember what headers were given
             globally */
          pp->have = NE_none;
          if (pp->content_name != NULL)
            pp->have |= NE_content_name;
          if (pp->content_type != NULL)
            pp->have |= NE_content_type;
          if (pp->content_filename != NULL)
            pp->have |= NE_content_filename;
          if (pp->content_transfer_encoding != NULL)
            pp->have |= NE_content_transfer_encoding;
          pp->state = PP_Nested_ProcessEntryHeaders;
          state_changed = 1;
          break;
        case PP_Nested_ProcessEntryHeaders:
          pp->value_offset = 0;
          if (MHD_NO ==
              process_multipart_headers (pp, &ioff,
                                         PP_Nested_ProcessValueToBoundary))
            {
              if (pp->state == PP_Error)
                return MHD_NO;
              else
                goto END;
            }
          state_changed = 1;
          break;
        case PP_Nested_ProcessValueToBoundary:
          if (MHD_NO == process_value_to_boundary (pp,
                                                   &ioff,
                                                   pp->nested_boundary,
                                                   pp->nlen,
                                                   PP_Nested_PerformCleanup,
                                                   PP_NextBoundary))
            {
              if (pp->state == PP_Error)
                return MHD_NO;
              break;
            }
          break;
        case PP_Nested_PerformCleanup:
          free_unmarked (pp);
          pp->state = PP_Nested_ProcessEntryHeaders;
          state_changed = 1;
          break;
        default:
          mhd_panic (mhd_panic_cls, __FILE__, __LINE__, NULL);          /* should never happen! */
        }
    AGAIN:
      if (ioff > 0)
        {
          memmove (buf, &buf[ioff], pp->buffer_pos - ioff);
          pp->buffer_pos -= ioff;
          ioff = 0;
          state_changed = 1;
        }
    }
END:
  if (ioff != 0)
    {
      memmove (buf, &buf[ioff], pp->buffer_pos - ioff);
      pp->buffer_pos -= ioff;
    }
  if (poff < post_data_len)
    {
      pp->state = PP_Error;
      return MHD_NO;            /* serious error */
    }
  return MHD_YES;
}
Beispiel #3
0
/**
 * Process url-encoded POST data.
 *
 * @param pp post processor context
 * @param post_data upload data
 * @param post_data_len number of bytes in upload_data
 * @return MHD_YES on success, MHD_NO if there was an error processing the data
 */
static int
post_process_urlencoded (struct MHD_PostProcessor *pp,
                         const char *post_data,
			 size_t post_data_len)
{
  size_t equals;
  size_t amper;
  size_t poff;
  size_t xoff;
  size_t delta;
  int end_of_value_found;
  char *buf;
  char xbuf[XBUF_SIZE + 1];

  buf = (char *) &pp[1];
  poff = 0;
  while (poff < post_data_len)
    {
      switch (pp->state)
        {
        case PP_Error:
          return MHD_NO;
        case PP_Done:
          /* did not expect to receive more data */
          pp->state = PP_Error;
          return MHD_NO;
        case PP_Init:
          equals = 0;
          while ((equals + poff < post_data_len) &&
                 (post_data[equals + poff] != '='))
            equals++;
          if (equals + pp->buffer_pos > pp->buffer_size)
            {
              pp->state = PP_Error;     /* out of memory */
              return MHD_NO;
            }
          memcpy (&buf[pp->buffer_pos], &post_data[poff], equals);
          pp->buffer_pos += equals;
          if (equals + poff == post_data_len)
            return MHD_YES;     /* no '=' yet */
          buf[pp->buffer_pos] = '\0';   /* 0-terminate key */
          pp->buffer_pos = 0;   /* reset for next key */
          MHD_http_unescape (NULL, NULL, buf);
          poff += equals + 1;
          pp->state = PP_ProcessValue;
          pp->value_offset = 0;
          break;
        case PP_ProcessValue:
          /* obtain rest of value from previous iteration */
          memcpy (xbuf, pp->xbuf, pp->xbuf_pos);
          xoff = pp->xbuf_pos;
          pp->xbuf_pos = 0;

          /* find last position in input buffer that is part of the value */
          amper = 0;
          while ((amper + poff < post_data_len) &&
                 (amper < XBUF_SIZE) &&
                 (post_data[amper + poff] != '&') &&
                 (post_data[amper + poff] != '\n') &&
                 (post_data[amper + poff] != '\r'))
            amper++;
          end_of_value_found = ((amper + poff < post_data_len) &&
                                ((post_data[amper + poff] == '&') ||
                                 (post_data[amper + poff] == '\n') ||
                                 (post_data[amper + poff] == '\r')));
          /* compute delta, the maximum number of bytes that we will be able to
             process right now (either amper-limited of xbuf-size limited) */
          delta = amper;
          if (delta > XBUF_SIZE - xoff)
            delta = XBUF_SIZE - xoff;

          /* move input into processing buffer */
          memcpy (&xbuf[xoff], &post_data[poff], delta);
          xoff += delta;
          poff += delta;

          /* find if escape sequence is at the end of the processing buffer;
             if so, exclude those from processing (reduce delta to point at
             end of processed region) */
          delta = xoff;
          if ((delta > 0) && (xbuf[delta - 1] == '%'))
            delta--;
          else if ((delta > 1) && (xbuf[delta - 2] == '%'))
            delta -= 2;

          /* if we have an incomplete escape sequence, save it to
             pp->xbuf for later */
          if (delta < xoff)
            {
              memcpy (pp->xbuf, &xbuf[delta], xoff - delta);
              pp->xbuf_pos = xoff - delta;
              xoff = delta;
            }

          /* If we have nothing to do (delta == 0) and
             not just because the value is empty (are
             waiting for more data), go for next iteration */
          if ((xoff == 0) && (poff == post_data_len))
            continue;

          /* unescape */
          xbuf[xoff] = '\0';    /* 0-terminate in preparation */
          xoff = MHD_http_unescape (NULL, NULL, xbuf);
          /* finally: call application! */
	  pp->must_ikvi = MHD_NO;
          if (MHD_NO == pp->ikvi (pp->cls, MHD_POSTDATA_KIND, (const char *) &pp[1],    /* key */
                                  NULL, NULL, NULL, xbuf, pp->value_offset,
                                  xoff))
            {
              pp->state = PP_Error;
              return MHD_NO;
            }
          pp->value_offset += xoff;

          /* are we done with the value? */
          if (end_of_value_found)
            {
              /* we found the end of the value! */
              if ((post_data[poff] == '\n') || (post_data[poff] == '\r'))
                {
                  pp->state = PP_ExpectNewLine;
                }
              else if (post_data[poff] == '&')
                {
                  poff++;       /* skip '&' */
                  pp->state = PP_Init;
                }
            }
          break;
        case PP_ExpectNewLine:
          if ((post_data[poff] == '\n') || (post_data[poff] == '\r'))
            {
              poff++;
              /* we are done, report error if we receive any more... */
              pp->state = PP_Done;
              return MHD_YES;
            }
          return MHD_NO;
        default:
          mhd_panic (mhd_panic_cls, __FILE__, __LINE__, NULL);          /* should never happen! */
        }
    }
  return MHD_YES;
}