/* -*- 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. */ #include "xp.h" #include "netutils.h" #include "mkselect.h" #include "mktcp.h" #include "cvactive.h" #include "mkgeturl.h" #include "mkstream.h" #include "glhist.h" #include "merrors.h" extern int MK_OUT_OF_MEMORY; typedef struct _DataObject { int state; NET_StreamClass *next_stream; char *prev_buffer; int32 prev_buffer_len; MWContext *window_id; int format_out; URL_Struct *URL_s; XP_Bool signal_at_end_of_multipart; } DataObject; #define NORMAL_S 1 #define FOUND_BOUNDARY_S 2 #define MAX_MIME_LINE 200 /* parse a mime/multipart stream. The boundary is already * known and we start off in the content of the original * message/822 body. * * buffer data up to the size of the passed in data length * for efficiency of the upstream module. */ PRIVATE int net_MultipleDocumentWrite (NET_StreamClass *stream, CONST char* s, int32 l) { int32 i=0; int32 line_length=1; int rv=0; char *cp; char *line; char *push_buffer=NULL; int32 push_buffer_size=0; XP_Bool all_done=FALSE; DataObject *obj=stream->data_object; BlockAllocCat(obj->prev_buffer, obj->prev_buffer_len, s, l); obj->prev_buffer_len += l; if(!obj->prev_buffer) return(MK_OUT_OF_MEMORY); line = obj->prev_buffer; /* try and find a line */ for(cp=obj->prev_buffer; i < obj->prev_buffer_len; cp++, i++, line_length++) { if(*cp == '\n') { switch(obj->state) { case NORMAL_S: { char *cp2 = line; int blength = XP_STRLEN(obj->URL_s->boundary); /* look for boundary. We can rest assured that these XP_STRNCMP() calls are safe, because we know that the valid portion of the string starting at cp2 has a newline in it (at *cp), and we know that boundary strings never have newlines in them. */ if((!XP_STRNCMP(cp2, "--",2) && !XP_STRNCMP(cp2+2, obj->URL_s->boundary, blength)) || (!XP_STRNCMP(cp2, obj->URL_s->boundary, blength))) { TRACEMSG(("Found boundary: %s", obj->URL_s->boundary)); obj->state = FOUND_BOUNDARY_S; if(obj->next_stream) { if(push_buffer) { /* strip the last newline before the * boundary */ push_buffer_size--; /* if there is a CR as well as a LF * strip that too */ if(push_buffer_size > 0 && push_buffer[push_buffer_size-1] == CR) push_buffer_size--; rv = (*obj->next_stream->put_block) (obj->next_stream, push_buffer, push_buffer_size); XP_FREE(push_buffer); push_buffer = NULL; push_buffer_size = 0; } TRACEMSG(("Completeing an open stream")); /* if this stream is not the last one, set a flag before completion to let completion do special stuff */ XP_ASSERT(cp2 + blength <= cp); /* Because the above XP_STRCMP calls succeeded. Because this is true, we know the first call to XP_STRNCMP below is always safe, but we need to check lengths before we can be sure the other call is safe. */ if( (cp2 + blength + 2 < cp && !XP_STRNCMP(cp2+2+blength, "--",2)) || !XP_STRNCMP(cp2+blength, "--",2)) { /* very last boundary */ obj->next_stream->is_multipart = FALSE; /* set the all_done flag when * we have found the final boundary */ all_done = TRUE; } else { obj->next_stream->is_multipart = TRUE; } /* complete the last stream */ (*obj->next_stream->complete) (obj->next_stream); XP_FREE(obj->next_stream); obj->next_stream = NULL; } /* move the line ptr up to new data */ line = cp+1; line_length=1; /* reset */ if(all_done && obj->signal_at_end_of_multipart) return(MK_MULTIPART_MESSAGE_COMPLETED); break; } else { TRACEMSG(("Pushing line (actually buffering)")); /* didn't find the boundary */ if(obj->next_stream) { BlockAllocCat(push_buffer, push_buffer_size, line, line_length); push_buffer_size += line_length; if(!push_buffer) return(MK_OUT_OF_MEMORY); } /* move the line ptr up to new data */ line = cp+1; line_length=0; /* reset */ } break; } case FOUND_BOUNDARY_S: XP_ASSERT(*cp == '\n'); /* from the 'if' above */ /* terminate at the newline. * now 'line' points to a valid NULL terminated C string */ *cp = '\0'; TRACEMSG(("Parsing header: >%s<", line)); /* parse mime headers * stop when a blank line is encountered */ if(*line == '\0' || *line == '\r') { int format_out; obj->state = NORMAL_S; TRACEMSG(("Found end of headers")); if (obj->URL_s->content_type == NULL) StrAllocCopy(obj->URL_s->content_type, TEXT_PLAIN); /* abort all existing streams. We * have to do this to prevent the * image lib and other things from * continuing to load the page after * we left. * * don't abort other streams if it's * just a new inline image or if the * stream is not going to the screen */ if(CLEAR_CACHE_BIT(obj->format_out) != FO_INTERNAL_IMAGE && (!strncasecomp(obj->URL_s->content_type, "text", 4) || !strncasecomp(obj->URL_s->content_type, "image", 4)) ) { NET_SilentInterruptWindow(obj->window_id); format_out = obj->format_out; } else { /* don't cache image animations... */ format_out = CLEAR_CACHE_BIT(obj->format_out); } /* libimg and libplugin use the fe_data to store * urls data, so clear it only if its not them */ if( (CLEAR_CACHE_BIT(obj->format_out) != FO_INTERNAL_IMAGE) && (CLEAR_CACHE_BIT(obj->format_out) != FO_PLUGIN) && (CLEAR_CACHE_BIT(obj->format_out) != FO_BYTERANGE) && strncasecomp(obj->URL_s->content_type, "image", 5)) { obj->URL_s->fe_data = NULL; } /* build a stream */ obj->next_stream = NET_StreamBuilder(format_out, obj->URL_s, obj->window_id); if(!obj->next_stream) return(MK_UNABLE_TO_CONVERT); } else if(!strncasecomp(line, "CONTENT-TYPE:", 13)) { XP_STRTOK(line+13, ";"); /* terminate at ; */ StrAllocCopy(obj->URL_s->content_type, XP_StripLine(line+13)); if(!obj->URL_s->content_type || !*obj->URL_s->content_type) StrAllocCopy(obj->URL_s->content_type, TEXT_PLAIN); TRACEMSG(("found new content_type: %s", obj->URL_s->content_type)); } else { /* Pass all other headers to the MIME header parser */ char *value = XP_STRCHR(line, ':'); if(value) value++; NET_ParseMimeHeader(NET_AllowForeignCookies, obj->window_id, obj->URL_s, line, value, FALSE); } line = cp+1; line_length = 0; break; default: assert(0); break; } } /* end if */ } /* end for */ if(line_length > MAX_MIME_LINE * 2) { int32 new_size; TRACEMSG(("Line too long pushing it")); if(obj->next_stream) { if(push_buffer) { rv = (*obj->next_stream->put_block)(obj->next_stream, push_buffer, push_buffer_size); rv = (*obj->next_stream->put_block)(obj->next_stream, line, MAX_MIME_LINE); XP_FREE(push_buffer); push_buffer = 0; push_buffer_size = 0; } else { rv = (*obj->next_stream->put_block)(obj->next_stream, line, MAX_MIME_LINE); } if(rv < 0) return(rv); } /* newsize equals the old size minus the difference * between line and the start of the old buffer and * the MAX_MIME_LENGTH that we just wrote out */ new_size = obj->prev_buffer_len - ((line - obj->prev_buffer) + MAX_MIME_LINE); XP_MEMMOVE(obj->prev_buffer, line+MAX_MIME_LINE, new_size); obj->prev_buffer_len = new_size; return(0); } if(line != obj->prev_buffer) { /* some part of the line has been digested, get rid of * the part that has been used */ obj->prev_buffer_len -= (line - obj->prev_buffer); XP_MEMMOVE(obj->prev_buffer, line, obj->prev_buffer_len); } /* if there is anything in the push buffer send it now */ if(push_buffer) { if(obj->next_stream) { TRACEMSG(("Pushing buffered data")); rv = (*obj->next_stream->put_block)(obj->next_stream, push_buffer, push_buffer_size); } XP_FREE(push_buffer); if (rv < 0) return rv; } return(0); } /* is the stream ready for writeing? */ PRIVATE unsigned int net_MultipleDocumentWriteReady (NET_StreamClass * stream) { DataObject *obj=stream->data_object; if(obj->next_stream) return((*obj->next_stream->is_write_ready)(obj->next_stream)); else return(MAX_WRITE_READY); } PRIVATE void net_MultipleDocumentComplete (NET_StreamClass *stream) { DataObject *obj=stream->data_object; if(obj->next_stream) { (*obj->next_stream->complete)(obj->next_stream); XP_FREE(obj->next_stream); } XP_FREEIF(obj->prev_buffer); XP_FREE(obj); return; } PRIVATE void net_MultipleDocumentAbort (NET_StreamClass *stream, int status) { DataObject *obj=stream->data_object; if(obj->next_stream) { (*obj->next_stream->abort)(obj->next_stream, status); XP_FREE(obj->next_stream); } XP_FREEIF(obj->prev_buffer); XP_FREE(obj); return; } PUBLIC NET_StreamClass * CV_MakeMultipleDocumentStream (int format_out, void *data_object, URL_Struct *URL_s, MWContext *window_id) { DataObject* obj; NET_StreamClass* stream; TRACEMSG(("Setting up display stream. Have URL: %s\n", URL_s->address)); GH_UpdateGlobalHistory(URL_s); URL_s->is_active = TRUE; /* set to disable view source */ stream = XP_NEW(NET_StreamClass); if(stream == NULL) return(NULL); obj = XP_NEW(DataObject); if (obj == NULL) return(NULL); XP_MEMSET(obj, 0, sizeof(DataObject)); if(CVACTIVE_SIGNAL_AT_END_OF_MULTIPART == (int) data_object) obj->signal_at_end_of_multipart = TRUE; stream->name = "Multiple Document"; stream->complete = (MKStreamCompleteFunc) net_MultipleDocumentComplete; stream->abort = (MKStreamAbortFunc) net_MultipleDocumentAbort; stream->put_block = (MKStreamWriteFunc) net_MultipleDocumentWrite; stream->is_write_ready = (MKStreamWriteReadyFunc) net_MultipleDocumentWriteReady; stream->data_object = obj; /* document info object */ stream->window_id = window_id; /* don't cache these format_out = CLEAR_CACHE_BIT(format_out); */ /* enable clicking since it doesnt go through the cache * code */ FE_EnableClicking(window_id); obj->next_stream = NULL; obj->window_id = window_id; obj->format_out = format_out; obj->URL_s = URL_s; obj->state = NORMAL_S; /* make sure that we have a real boundary. * if not fill in '--' as a boundary */ if(!URL_s->boundary) StrAllocCopy(URL_s->boundary, "--"); /* overwrite the current content type with TEXT/PLAIN since * we want that as the default content-type for * body parts */ StrAllocCopy(URL_s->content_type, TEXT_PLAIN); TRACEMSG(("Returning stream from NET_MultipleDocumentConverter\n")); return stream; }