gecko-dev/network/cnvts/cvchunk.c
ebina%netscape.com ccbcd41b1d Fix bug #773
Apply patch to not infinite loop if Chunking in STRIP_CRLF
state, and no LF is provided.
1999-03-12 19:45:24 +00:00

330 lines
8.7 KiB
C

/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
/* Please leave outside of ifdef for windows precompiled headers */
#include "xp.h"
#include "plstr.h"
#include "prmem.h"
#include "netutils.h"
#include "mkselect.h"
#include "mktcp.h"
#include "mkgeturl.h"
#ifdef MOZILLA_CLIENT
#include "cvchunk.h" /* prototype */
extern int MK_OUT_OF_MEMORY;
typedef enum {
FIND_CHUNK_SIZE,
READ_CHUNK,
STRIP_CRLF,
PARSE_FOOTER
} States;
typedef struct _DataObject {
NET_StreamClass *next_stream;
char *in_buf;
uint32 in_buf_size;
uint32 chunk_size;
uint32 amount_of_chunk_parsed;
States cur_state;
FO_Present_Types format_out;
MWContext *context;
URL_Struct *URL_s;
} DataObject;
/* unchunk the message and return MK_MULTIPART_MESSAGE_COMPLETED
* when end detected
*/
PRIVATE int net_ChunkedWrite (NET_StreamClass *stream, char* s, int32 l)
{
DataObject *obj=stream->data_object;
BlockAllocCat(obj->in_buf, obj->in_buf_size, s, l);
if(!obj->in_buf)
return MK_OUT_OF_MEMORY;
obj->in_buf_size += l;
while(obj->in_buf_size > 0)
{
if(obj->cur_state == FIND_CHUNK_SIZE)
{
char *line_feed;
char *semicolon;
char *end;
/* we don't have a current chunk size
* look for a new chunk size
*
* make sure the line has a CRLF
*/
if((line_feed = PL_strnchr(obj->in_buf, LF, obj->in_buf_size)) == NULL)
{
return 1; /* need more data */
}
*line_feed = '\0';
semicolon = PL_strnchr(obj->in_buf, ';', line_feed-obj->in_buf);
if(semicolon)
*semicolon = '\0';
end = semicolon ? semicolon : line_feed;
/* read the first integer and ignore any thing
* else on the line. Extensions are allowed
*/
obj->chunk_size = strtol(obj->in_buf, &end, 16);
/* strip everything up to the line feed */
obj->in_buf_size -= (line_feed+1) - obj->in_buf;
if(obj->in_buf_size)
memmove(obj->in_buf,
line_feed+1,
obj->in_buf_size);
if(obj->chunk_size == 0)
{
/* the stream should be done now */
obj->cur_state = PARSE_FOOTER;
}
else
{
obj->cur_state = READ_CHUNK;
}
}
else if(obj->cur_state == READ_CHUNK)
{
uint32 data_size;
int32 status;
/* take as much data as we have and push it up the stream
*/
data_size = MIN(obj->in_buf_size, obj->chunk_size-obj->amount_of_chunk_parsed);
status = (obj->next_stream->put_block)(obj->next_stream,
obj->in_buf,
data_size);
if(status < 0)
return status;
/* remove the part that has been pushed */
obj->in_buf_size -= data_size;
if(obj->in_buf_size)
memmove(obj->in_buf,
obj->in_buf+data_size,
obj->in_buf_size);
obj->amount_of_chunk_parsed += data_size;
if(obj->amount_of_chunk_parsed >= obj->chunk_size)
{
PR_ASSERT(obj->amount_of_chunk_parsed == obj->chunk_size);
/* reinit */
obj->amount_of_chunk_parsed = 0;
obj->cur_state = STRIP_CRLF;
}
}
else if(obj->cur_state == STRIP_CRLF)
{
if(PL_strnchr(obj->in_buf, LF, obj->in_buf_size) == NULL)
{
return 1; /* No LF, need more data. */
}
if(obj->in_buf_size > 1 && obj->in_buf[0] == CR && obj->in_buf[1] == LF)
{
/* strip two bytes */
obj->in_buf_size -= 2;
if(obj->in_buf_size)
memmove(obj->in_buf,
obj->in_buf+2,
obj->in_buf_size);
obj->cur_state = FIND_CHUNK_SIZE;
}
else if(obj->in_buf[0] == LF)
{
/* strip one bytes */
obj->in_buf_size -= 1;
if(obj->in_buf_size)
memmove(obj->in_buf,
obj->in_buf+1,
obj->in_buf_size);
obj->cur_state = FIND_CHUNK_SIZE;
}
else
{
if(obj->in_buf_size >= 2)
{
int status;
/* a fatal parse error */
PR_ASSERT(0);
/* just spew the buf to the screen */
status = (obj->next_stream->put_block)(obj->next_stream,
obj->in_buf,
obj->in_buf_size);
if(status < 0)
return status;
/* remove the part that has been pushed */
obj->in_buf_size = 0;
}
}
}
else if(obj->cur_state == PARSE_FOOTER)
{
char *line_feed;
char *value;
/* parse until we see two CRLF's in a row */
if((line_feed = PL_strnchr(obj->in_buf, LF, obj->in_buf_size)) == NULL)
{
return 1; /* need more data */
}
*line_feed = '\0';
/* strip the CR */
if(line_feed != obj->in_buf && *(line_feed-1) == CR)
*(line_feed-1) = '\0';
if(*obj->in_buf == '\0')
{
/* end of parse stream */
return MK_MULTIPART_MESSAGE_COMPLETED;
}
/* names are separated from values with a colon
*/
value = PL_strchr(obj->in_buf, ':');
if(value)
value++;
/* otherwise parse the line as a mime header */
NET_ParseMimeHeader(obj->format_out,
obj->context,
obj->URL_s,
obj->in_buf,
value,
FALSE);
/* strip the line from the buffer */
obj->in_buf_size -= (line_feed+1) - obj->in_buf;
if(obj->in_buf_size)
memmove(obj->in_buf,
line_feed+1,
obj->in_buf_size);
}
}
PR_ASSERT(obj->in_buf_size == 0);
return(1);
}
/* is the stream ready for writeing?
*/
PRIVATE unsigned int net_ChunkedWriteReady (NET_StreamClass * stream)
{
DataObject *obj=stream->data_object;
return (*obj->next_stream->is_write_ready)(obj->next_stream);
}
PRIVATE void net_ChunkedComplete (NET_StreamClass *stream)
{
DataObject *obj=stream->data_object;
(*obj->next_stream->complete)(obj->next_stream);
PR_Free(obj);
return;
}
PRIVATE void net_ChunkedAbort (NET_StreamClass *stream, int status)
{
DataObject *obj=stream->data_object;
(*obj->next_stream->abort)(obj->next_stream, status);
return;
}
MODULE_PRIVATE NET_StreamClass *
NET_ChunkedDecoderStream (int format_out,
void *data_obj,
URL_Struct *URL_s,
MWContext *window_id)
{
DataObject* obj;
NET_StreamClass* stream;
TRACEMSG(("Setting up display stream. Have URL: %s\n", URL_s->address));
stream = PR_NEW(NET_StreamClass);
if(stream == NULL)
return(NULL);
obj = PR_NEWZAP(DataObject);
if (obj == NULL)
return(NULL);
stream->name = "Chunked decoder";
stream->complete = (MKStreamCompleteFunc) net_ChunkedComplete;
stream->abort = (MKStreamAbortFunc) net_ChunkedAbort;
stream->put_block = (MKStreamWriteFunc) net_ChunkedWrite;
stream->is_write_ready = (MKStreamWriteReadyFunc) net_ChunkedWriteReady;
stream->data_object = obj; /* document info object */
stream->window_id = window_id;
/* clear the "chunked" encoding */
if(URL_s->transfer_encoding)
{
PR_FREEIF(URL_s->transfer_encoding);
URL_s->transfer_encoding = NULL;
}
else
{
PR_FREEIF(URL_s->content_encoding);
URL_s->content_encoding = NULL;
}
obj->next_stream = NET_StreamBuilder(format_out, URL_s, window_id);
if(!obj->next_stream)
{
PR_Free(obj);
PR_Free(stream);
return NULL;
}
obj->context = window_id;
obj->format_out = format_out;
obj->URL_s = URL_s;
TRACEMSG(("Returning stream from NET_ChunkedConverter\n"));
return stream;
}
#endif /* MOZILLA_CLIENT */