michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "signtool.h" michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: static int javascript_fn(char *relpath, char *basedir, char *reldir, michael@0: char *filename, void *arg); michael@0: static int extract_js (char *filename); michael@0: static int copyinto (char *from, char *to); michael@0: static PRStatus ensureExists (char *base, char *path); michael@0: static int make_dirs(char *path, PRInt32 file_perms); michael@0: michael@0: static char *jartree = NULL; michael@0: static int idOrdinal; michael@0: static PRBool dumpParse = PR_FALSE; michael@0: michael@0: static char *event_handlers[] = { michael@0: "onAbort", michael@0: "onBlur", michael@0: "onChange", michael@0: "onClick", michael@0: "onDblClick", michael@0: "onDragDrop", michael@0: "onError", michael@0: "onFocus", michael@0: "onKeyDown", michael@0: "onKeyPress", michael@0: "onKeyUp", michael@0: "onLoad", michael@0: "onMouseDown", michael@0: "onMouseMove", michael@0: "onMouseOut", michael@0: "onMouseOver", michael@0: "onMouseUp", michael@0: "onMove", michael@0: "onReset", michael@0: "onResize", michael@0: "onSelect", michael@0: "onSubmit", michael@0: "onUnload" michael@0: }; michael@0: michael@0: michael@0: static int num_handlers = 23; michael@0: michael@0: /* michael@0: * I n l i n e J a v a S c r i p t michael@0: * michael@0: * Javascript signing. Instead of passing an archive to signtool, michael@0: * a directory containing html files is given. Archives are created michael@0: * from the archive= and src= tag attributes inside the html, michael@0: * as appropriate. Then the archives are signed. michael@0: * michael@0: */ michael@0: int michael@0: InlineJavaScript(char *dir, PRBool recurse) michael@0: { michael@0: jartree = dir; michael@0: if (verbosity >= 0) { michael@0: PR_fprintf(outputFD, "\nGenerating inline signatures from HTML files in: %s\n", michael@0: dir); michael@0: } michael@0: if (PR_GetEnv("SIGNTOOL_DUMP_PARSE")) { michael@0: dumpParse = PR_TRUE; michael@0: } michael@0: michael@0: return foreach(dir, "", javascript_fn, recurse, PR_FALSE /*include dirs*/, michael@0: (void * )NULL); michael@0: michael@0: } michael@0: michael@0: michael@0: /************************************************************************ michael@0: * michael@0: * j a v a s c r i p t _ f n michael@0: */ michael@0: static int javascript_fn michael@0: (char *relpath, char *basedir, char *reldir, char *filename, void *arg) michael@0: { michael@0: char fullname [FNSIZE]; michael@0: michael@0: /* only process inline scripts from .htm, .html, and .shtml*/ michael@0: michael@0: if (!(PL_strcaserstr(filename, ".htm") == filename + strlen(filename) - michael@0: 4) && michael@0: !(PL_strcaserstr(filename, ".html") == filename + strlen(filename) - michael@0: 5) && michael@0: !(PL_strcaserstr(filename, ".shtml") == filename + strlen(filename) michael@0: -6)) { michael@0: return 0; michael@0: } michael@0: michael@0: /* don't process scripts that signtool has already michael@0: extracted (those that are inside .arc directories) */ michael@0: michael@0: if (PL_strcaserstr(filename, ".arc") == filename + strlen(filename) - 4) michael@0: return 0; michael@0: michael@0: if (verbosity >= 0) { michael@0: PR_fprintf(outputFD, "Processing HTML file: %s\n", relpath); michael@0: } michael@0: michael@0: /* reset firstArchive at top of each HTML file */ michael@0: michael@0: /* skip directories that contain extracted scripts */ michael@0: michael@0: if (PL_strcaserstr(reldir, ".arc") == reldir + strlen(reldir) - 4) michael@0: return 0; michael@0: michael@0: sprintf (fullname, "%s/%s", basedir, relpath); michael@0: return extract_js (fullname); michael@0: } michael@0: michael@0: michael@0: /*=========================================================================== michael@0: = michael@0: = D A T A S T R U C T U R E S michael@0: = michael@0: */ michael@0: typedef enum { michael@0: TEXT_HTML_STATE = 0, michael@0: SCRIPT_HTML_STATE michael@0: } michael@0: michael@0: michael@0: HTML_STATE ; michael@0: michael@0: typedef enum { michael@0: /* we start in the start state */ michael@0: START_STATE, michael@0: michael@0: /* We are looking for or reading in an attribute */ michael@0: GET_ATT_STATE, michael@0: michael@0: /* We're burning ws before finding an attribute */ michael@0: PRE_ATT_WS_STATE, michael@0: michael@0: /* We're burning ws after an attribute. Looking for an '='. */ michael@0: POST_ATT_WS_STATE, michael@0: michael@0: /* We're burning ws after an '=', waiting for a value */ michael@0: PRE_VAL_WS_STATE, michael@0: michael@0: /* We're reading in a value */ michael@0: GET_VALUE_STATE, michael@0: michael@0: /* We're reading in a value that's inside quotes */ michael@0: GET_QUOTED_VAL_STATE, michael@0: michael@0: /* We've encountered the closing '>' */ michael@0: DONE_STATE, michael@0: michael@0: /* Error state */ michael@0: ERR_STATE michael@0: } michael@0: michael@0: michael@0: TAG_STATE ; michael@0: michael@0: typedef struct AVPair_Str { michael@0: char *attribute; michael@0: char *value; michael@0: unsigned int valueLine; /* the line that the value ends on */ michael@0: struct AVPair_Str *next; michael@0: } AVPair; michael@0: michael@0: typedef enum { michael@0: APPLET_TAG, michael@0: SCRIPT_TAG, michael@0: LINK_TAG, michael@0: STYLE_TAG, michael@0: COMMENT_TAG, michael@0: OTHER_TAG michael@0: } michael@0: michael@0: michael@0: TAG_TYPE ; michael@0: michael@0: typedef struct { michael@0: TAG_TYPE type; michael@0: AVPair * attList; michael@0: AVPair * attListTail; michael@0: char *text; michael@0: } TagItem; michael@0: michael@0: typedef enum { michael@0: TAG_ITEM, michael@0: TEXT_ITEM michael@0: } michael@0: michael@0: michael@0: ITEM_TYPE ; michael@0: michael@0: typedef struct HTMLItem_Str { michael@0: unsigned int startLine; michael@0: unsigned int endLine; michael@0: ITEM_TYPE type; michael@0: union { michael@0: TagItem *tag; michael@0: char *text; michael@0: } item; michael@0: struct HTMLItem_Str *next; michael@0: } HTMLItem; michael@0: michael@0: typedef struct { michael@0: PRFileDesc *fd; michael@0: PRInt32 curIndex; michael@0: PRBool IsEOF; michael@0: #define FILE_BUFFER_BUFSIZE 512 michael@0: char buf[FILE_BUFFER_BUFSIZE]; michael@0: PRInt32 startOffset; michael@0: PRInt32 maxIndex; michael@0: unsigned int lineNum; michael@0: } FileBuffer; michael@0: michael@0: /*=========================================================================== michael@0: = michael@0: = F U N C T I O N S michael@0: = michael@0: */ michael@0: static HTMLItem*CreateTextItem(char *text, unsigned int startline, michael@0: unsigned int endline); michael@0: static HTMLItem*CreateTagItem(TagItem*ti, unsigned int startline, michael@0: unsigned int endline); michael@0: static TagItem*ProcessTag(FileBuffer*fb, char **errStr); michael@0: static void DestroyHTMLItem(HTMLItem *item); michael@0: static void DestroyTagItem(TagItem*ti); michael@0: static TAG_TYPE GetTagType(char *att); michael@0: static FileBuffer*FB_Create(PRFileDesc*fd); michael@0: static int FB_GetChar(FileBuffer *fb); michael@0: static PRInt32 FB_GetPointer(FileBuffer *fb); michael@0: static PRInt32 FB_GetRange(FileBuffer *fb, PRInt32 start, PRInt32 end, michael@0: char **buf); michael@0: static unsigned int FB_GetLineNum(FileBuffer *fb); michael@0: static void FB_Destroy(FileBuffer *fb); michael@0: static void PrintTagItem(PRFileDesc *fd, TagItem *ti); michael@0: static void PrintHTMLStream(PRFileDesc *fd, HTMLItem *head); michael@0: michael@0: /************************************************************************ michael@0: * michael@0: * C r e a t e T e x t I t e m michael@0: */ michael@0: static HTMLItem* michael@0: CreateTextItem(char *text, unsigned int startline, unsigned int endline) michael@0: { michael@0: HTMLItem * item; michael@0: michael@0: item = PR_Malloc(sizeof(HTMLItem)); michael@0: if (!item) { michael@0: return NULL; michael@0: } michael@0: michael@0: item->type = TEXT_ITEM; michael@0: item->item.text = text; michael@0: item->next = NULL; michael@0: item->startLine = startline; michael@0: item->endLine = endline; michael@0: michael@0: return item; michael@0: } michael@0: michael@0: michael@0: /************************************************************************ michael@0: * michael@0: * C r e a t e T a g I t e m michael@0: */ michael@0: static HTMLItem* michael@0: CreateTagItem(TagItem*ti, unsigned int startline, unsigned int endline) michael@0: { michael@0: HTMLItem * item; michael@0: michael@0: item = PR_Malloc(sizeof(HTMLItem)); michael@0: if (!item) { michael@0: return NULL; michael@0: } michael@0: michael@0: item->type = TAG_ITEM; michael@0: item->item.tag = ti; michael@0: item->next = NULL; michael@0: item->startLine = startline; michael@0: item->endLine = endline; michael@0: michael@0: return item; michael@0: } michael@0: michael@0: michael@0: static PRBool michael@0: isAttChar(int c) michael@0: { michael@0: return (isalnum(c) || c == '/' || c == '-'); michael@0: } michael@0: michael@0: michael@0: /************************************************************************ michael@0: * michael@0: * P r o c e s s T a g michael@0: */ michael@0: static TagItem* michael@0: ProcessTag(FileBuffer*fb, char **errStr) michael@0: { michael@0: TAG_STATE state; michael@0: PRInt32 startText, startID, curPos; michael@0: PRBool firstAtt; michael@0: int curchar; michael@0: TagItem * ti = NULL; michael@0: AVPair * curPair = NULL; michael@0: char quotechar = '\0'; michael@0: unsigned int linenum; michael@0: unsigned int startline; michael@0: michael@0: state = START_STATE; michael@0: michael@0: startID = FB_GetPointer(fb); michael@0: startText = startID; michael@0: firstAtt = PR_TRUE; michael@0: michael@0: ti = (TagItem * ) PR_Malloc(sizeof(TagItem)); michael@0: if (!ti) michael@0: out_of_memory(); michael@0: ti->type = OTHER_TAG; michael@0: ti->attList = NULL; michael@0: ti->attListTail = NULL; michael@0: ti->text = NULL; michael@0: michael@0: startline = FB_GetLineNum(fb); michael@0: michael@0: while (state != DONE_STATE && state != ERR_STATE) { michael@0: linenum = FB_GetLineNum(fb); michael@0: curchar = FB_GetChar(fb); michael@0: if (curchar == EOF) { michael@0: *errStr = PR_smprintf( michael@0: "line %d: Unexpected end-of-file while parsing tag starting at line %d.\n", michael@0: linenum, startline); michael@0: state = ERR_STATE; michael@0: continue; michael@0: } michael@0: michael@0: switch (state) { michael@0: case START_STATE: michael@0: if (curchar == '!') { michael@0: /* michael@0: * SGML tag or comment michael@0: * Here's the general rule for SGML tags. Everything from michael@0: * is the tag. Inside the tag, comments are michael@0: * delimited with --. So we are looking for the first '>' michael@0: * that is not commented out, that is, not inside a pair michael@0: * of --: (psyche!) --> michael@0: */ michael@0: michael@0: PRBool inComment = PR_FALSE; michael@0: short hyphenCount = 0; /* number of consecutive hyphens */ michael@0: michael@0: while (1) { michael@0: linenum = FB_GetLineNum(fb); michael@0: curchar = FB_GetChar(fb); michael@0: if (curchar == EOF) { michael@0: /* Uh oh, EOF inside comment */ michael@0: *errStr = PR_smprintf( michael@0: "line %d: Unexpected end-of-file inside comment starting at line %d.\n", michael@0: linenum, startline); michael@0: state = ERR_STATE; michael@0: break; michael@0: } michael@0: if (curchar == '-') { michael@0: if (hyphenCount == 1) { michael@0: /* This is a comment delimiter */ michael@0: inComment = !inComment; michael@0: hyphenCount = 0; michael@0: } else { michael@0: /* beginning of a comment delimiter? */ michael@0: hyphenCount = 1; michael@0: } michael@0: } else if (curchar == '>') { michael@0: if (!inComment) { michael@0: /* This is the end of the tag */ michael@0: state = DONE_STATE; michael@0: break; michael@0: } else { michael@0: /* The > is inside a comment, so it's not michael@0: * really the end of the tag */ michael@0: hyphenCount = 0; michael@0: } michael@0: } else { michael@0: hyphenCount = 0; michael@0: } michael@0: } michael@0: ti->type = COMMENT_TAG; michael@0: break; michael@0: } michael@0: /* fall through */ michael@0: case GET_ATT_STATE: michael@0: if (isspace(curchar) || curchar == '=' || curchar michael@0: == '>') { michael@0: /* end of the current attribute */ michael@0: curPos = FB_GetPointer(fb) - 2; michael@0: if (curPos >= startID) { michael@0: /* We have an attribute */ michael@0: curPair = (AVPair * )PR_Malloc(sizeof(AVPair)); michael@0: if (!curPair) michael@0: out_of_memory(); michael@0: curPair->value = NULL; michael@0: curPair->next = NULL; michael@0: FB_GetRange(fb, startID, curPos, michael@0: &curPair->attribute); michael@0: michael@0: /* Stick this attribute on the list */ michael@0: if (ti->attListTail) { michael@0: ti->attListTail->next = curPair; michael@0: ti->attListTail = curPair; michael@0: } else { michael@0: ti->attList = ti->attListTail = michael@0: curPair; michael@0: } michael@0: michael@0: /* If this is the first attribute, find the type of tag michael@0: * based on it. Also, start saving the text of the tag. */ michael@0: if (firstAtt) { michael@0: ti->type = GetTagType(curPair->attribute); michael@0: startText = FB_GetPointer(fb) michael@0: -1; michael@0: firstAtt = PR_FALSE; michael@0: } michael@0: } else { michael@0: if (curchar == '=') { michael@0: /* If we don't have any attribute but we do have an michael@0: * equal sign, that's an error */ michael@0: *errStr = PR_smprintf("line %d: Malformed tag starting at line %d.\n", michael@0: linenum, startline); michael@0: state = ERR_STATE; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: /* Compute next state */ michael@0: if (curchar == '=') { michael@0: startID = FB_GetPointer(fb); michael@0: state = PRE_VAL_WS_STATE; michael@0: } else if (curchar == '>') { michael@0: state = DONE_STATE; michael@0: } else if (curPair) { michael@0: state = POST_ATT_WS_STATE; michael@0: } else { michael@0: state = PRE_ATT_WS_STATE; michael@0: } michael@0: } else if (isAttChar(curchar)) { michael@0: /* Just another char in the attribute. Do nothing */ michael@0: state = GET_ATT_STATE; michael@0: } else { michael@0: /* bogus char */ michael@0: *errStr = PR_smprintf("line %d: Bogus chararacter '%c' in tag.\n", michael@0: linenum, curchar); michael@0: state = ERR_STATE; michael@0: break; michael@0: } michael@0: break; michael@0: case PRE_ATT_WS_STATE: michael@0: if (curchar == '>') { michael@0: state = DONE_STATE; michael@0: } else if (isspace(curchar)) { michael@0: /* more whitespace, do nothing */ michael@0: } else if (isAttChar(curchar)) { michael@0: /* starting another attribute */ michael@0: startID = FB_GetPointer(fb) - 1; michael@0: state = GET_ATT_STATE; michael@0: } else { michael@0: /* bogus char */ michael@0: *errStr = PR_smprintf("line %d: Bogus character '%c' in tag.\n", michael@0: linenum, curchar); michael@0: state = ERR_STATE; michael@0: break; michael@0: } michael@0: break; michael@0: case POST_ATT_WS_STATE: michael@0: if (curchar == '>') { michael@0: state = DONE_STATE; michael@0: } else if (isspace(curchar)) { michael@0: /* more whitespace, do nothing */ michael@0: } else if (isAttChar(curchar)) { michael@0: /* starting another attribute */ michael@0: startID = FB_GetPointer(fb) - 1; michael@0: state = GET_ATT_STATE; michael@0: } else if (curchar == '=') { michael@0: /* there was whitespace between the attribute and its equal michael@0: * sign, which means there's a value coming up */ michael@0: state = PRE_VAL_WS_STATE; michael@0: } else { michael@0: /* bogus char */ michael@0: *errStr = PR_smprintf("line %d: Bogus character '%c' in tag.\n", michael@0: linenum, curchar); michael@0: state = ERR_STATE; michael@0: break; michael@0: } michael@0: break; michael@0: case PRE_VAL_WS_STATE: michael@0: if (curchar == '>') { michael@0: /* premature end-of-tag (sounds like a personal problem). */ michael@0: *errStr = PR_smprintf( michael@0: "line %d: End of tag while waiting for value.\n", michael@0: linenum); michael@0: state = ERR_STATE; michael@0: break; michael@0: } else if (isspace(curchar)) { michael@0: /* more whitespace, do nothing */ michael@0: break; michael@0: } else { michael@0: /* this must be some sort of value. Fall through michael@0: * to GET_VALUE_STATE */ michael@0: startID = FB_GetPointer(fb) - 1; michael@0: state = GET_VALUE_STATE; michael@0: } michael@0: /* Fall through if we didn't break on '>' or whitespace */ michael@0: case GET_VALUE_STATE: michael@0: if (isspace(curchar) || curchar == '>') { michael@0: /* end of value */ michael@0: curPos = FB_GetPointer(fb) - 2; michael@0: if (curPos >= startID) { michael@0: /* Grab the value */ michael@0: FB_GetRange(fb, startID, curPos, michael@0: &curPair->value); michael@0: curPair->valueLine = linenum; michael@0: } else { michael@0: /* empty value, leave as NULL */ michael@0: } michael@0: if (isspace(curchar)) { michael@0: state = PRE_ATT_WS_STATE; michael@0: } else { michael@0: state = DONE_STATE; michael@0: } michael@0: } else if (curchar == '\"' || curchar == '\'') { michael@0: /* quoted value. Start recording the value inside the quote*/ michael@0: startID = FB_GetPointer(fb); michael@0: state = GET_QUOTED_VAL_STATE; michael@0: PORT_Assert(quotechar == '\0'); michael@0: quotechar = curchar; /* look for matching quote type */ michael@0: } else { michael@0: /* just more value */ michael@0: } michael@0: break; michael@0: case GET_QUOTED_VAL_STATE: michael@0: PORT_Assert(quotechar != '\0'); michael@0: if (curchar == quotechar) { michael@0: /* end of quoted value */ michael@0: curPos = FB_GetPointer(fb) - 2; michael@0: if (curPos >= startID) { michael@0: /* Grab the value */ michael@0: FB_GetRange(fb, startID, curPos, michael@0: &curPair->value); michael@0: curPair->valueLine = linenum; michael@0: } else { michael@0: /* empty value, leave it as NULL */ michael@0: } michael@0: state = GET_ATT_STATE; michael@0: quotechar = '\0'; michael@0: startID = FB_GetPointer(fb); michael@0: } else { michael@0: /* more quoted value, continue */ michael@0: } michael@0: break; michael@0: case DONE_STATE: michael@0: case ERR_STATE: michael@0: default: michael@0: ; /* should never get here */ michael@0: } michael@0: } michael@0: michael@0: if (state == DONE_STATE) { michael@0: /* Get the text of the tag */ michael@0: curPos = FB_GetPointer(fb) - 1; michael@0: FB_GetRange(fb, startText, curPos, &ti->text); michael@0: michael@0: /* Return the tag */ michael@0: return ti; michael@0: } michael@0: michael@0: /* Uh oh, an error. Kill the tag item*/ michael@0: DestroyTagItem(ti); michael@0: return NULL; michael@0: } michael@0: michael@0: michael@0: /************************************************************************ michael@0: * michael@0: * D e s t r o y H T M L I t e m michael@0: */ michael@0: static void michael@0: DestroyHTMLItem(HTMLItem *item) michael@0: { michael@0: if (item->type == TAG_ITEM) { michael@0: DestroyTagItem(item->item.tag); michael@0: } else { michael@0: if (item->item.text) { michael@0: PR_Free(item->item.text); michael@0: } michael@0: } michael@0: } michael@0: michael@0: michael@0: /************************************************************************ michael@0: * michael@0: * D e s t r o y T a g I t e m michael@0: */ michael@0: static void michael@0: DestroyTagItem(TagItem*ti) michael@0: { michael@0: AVPair * temp; michael@0: michael@0: if (ti->text) { michael@0: PR_Free(ti->text); michael@0: ti->text = NULL; michael@0: } michael@0: michael@0: while (ti->attList) { michael@0: temp = ti->attList; michael@0: ti->attList = ti->attList->next; michael@0: michael@0: if (temp->attribute) { michael@0: PR_Free(temp->attribute); michael@0: temp->attribute = NULL; michael@0: } michael@0: if (temp->value) { michael@0: PR_Free(temp->value); michael@0: temp->value = NULL; michael@0: } michael@0: PR_Free(temp); michael@0: } michael@0: michael@0: PR_Free(ti); michael@0: } michael@0: michael@0: michael@0: /************************************************************************ michael@0: * michael@0: * G e t T a g T y p e michael@0: */ michael@0: static TAG_TYPE michael@0: GetTagType(char *att) michael@0: { michael@0: if (!PORT_Strcasecmp(att, "APPLET")) { michael@0: return APPLET_TAG; michael@0: } michael@0: if (!PORT_Strcasecmp(att, "SCRIPT")) { michael@0: return SCRIPT_TAG; michael@0: } michael@0: if (!PORT_Strcasecmp(att, "LINK")) { michael@0: return LINK_TAG; michael@0: } michael@0: if (!PORT_Strcasecmp(att, "STYLE")) { michael@0: return STYLE_TAG; michael@0: } michael@0: return OTHER_TAG; michael@0: } michael@0: michael@0: michael@0: /************************************************************************ michael@0: * michael@0: * F B _ C r e a t e michael@0: */ michael@0: static FileBuffer* michael@0: FB_Create(PRFileDesc*fd) michael@0: { michael@0: FileBuffer * fb; michael@0: PRInt32 amountRead; michael@0: PRInt32 storedOffset; michael@0: michael@0: fb = (FileBuffer * ) PR_Malloc(sizeof(FileBuffer)); michael@0: fb->fd = fd; michael@0: storedOffset = PR_Seek(fd, 0, PR_SEEK_CUR); michael@0: PR_Seek(fd, 0, PR_SEEK_SET); michael@0: fb->startOffset = 0; michael@0: amountRead = PR_Read(fd, fb->buf, FILE_BUFFER_BUFSIZE); michael@0: if (amountRead == -1) michael@0: goto loser; michael@0: fb->maxIndex = amountRead - 1; michael@0: fb->curIndex = 0; michael@0: fb->IsEOF = (fb->curIndex > fb->maxIndex) ? PR_TRUE : PR_FALSE; michael@0: fb->lineNum = 1; michael@0: michael@0: PR_Seek(fd, storedOffset, PR_SEEK_SET); michael@0: return fb; michael@0: loser: michael@0: PR_Seek(fd, storedOffset, PR_SEEK_SET); michael@0: PR_Free(fb); michael@0: return NULL; michael@0: } michael@0: michael@0: michael@0: /************************************************************************ michael@0: * michael@0: * F B _ G e t C h a r michael@0: */ michael@0: static int michael@0: FB_GetChar(FileBuffer *fb) michael@0: { michael@0: PRInt32 storedOffset; michael@0: PRInt32 amountRead; michael@0: int retval = -1; michael@0: michael@0: if (fb->IsEOF) { michael@0: return EOF; michael@0: } michael@0: michael@0: storedOffset = PR_Seek(fb->fd, 0, PR_SEEK_CUR); michael@0: michael@0: retval = (unsigned char) fb->buf[fb->curIndex++]; michael@0: if (retval == '\n') michael@0: fb->lineNum++; michael@0: michael@0: if (fb->curIndex > fb->maxIndex) { michael@0: /* We're at the end of the buffer. Try to get some new data from the michael@0: * file */ michael@0: fb->startOffset += fb->maxIndex + 1; michael@0: PR_Seek(fb->fd, fb->startOffset, PR_SEEK_SET); michael@0: amountRead = PR_Read(fb->fd, fb->buf, FILE_BUFFER_BUFSIZE); michael@0: if (amountRead == -1) michael@0: goto loser; michael@0: fb->maxIndex = amountRead - 1; michael@0: fb->curIndex = 0; michael@0: } michael@0: michael@0: fb->IsEOF = (fb->curIndex > fb->maxIndex) ? PR_TRUE : PR_FALSE; michael@0: michael@0: loser: michael@0: PR_Seek(fb->fd, storedOffset, PR_SEEK_SET); michael@0: return retval; michael@0: } michael@0: michael@0: michael@0: /************************************************************************ michael@0: * michael@0: * F B _ G e t L i n e N u m michael@0: * michael@0: */ michael@0: static unsigned int michael@0: FB_GetLineNum(FileBuffer *fb) michael@0: { michael@0: return fb->lineNum; michael@0: } michael@0: michael@0: michael@0: /************************************************************************ michael@0: * michael@0: * F B _ G e t P o i n t e r michael@0: * michael@0: */ michael@0: static PRInt32 michael@0: FB_GetPointer(FileBuffer *fb) michael@0: { michael@0: return fb->startOffset + fb->curIndex; michael@0: } michael@0: michael@0: michael@0: /************************************************************************ michael@0: * michael@0: * F B _ G e t R a n g e michael@0: * michael@0: */ michael@0: static PRInt32 michael@0: FB_GetRange(FileBuffer *fb, PRInt32 start, PRInt32 end, char **buf) michael@0: { michael@0: PRInt32 amountRead; michael@0: PRInt32 storedOffset; michael@0: michael@0: *buf = PR_Malloc(end - start + 2); michael@0: if (*buf == NULL) { michael@0: return 0; michael@0: } michael@0: michael@0: storedOffset = PR_Seek(fb->fd, 0, PR_SEEK_CUR); michael@0: PR_Seek(fb->fd, start, PR_SEEK_SET); michael@0: amountRead = PR_Read(fb->fd, *buf, end - start + 1); michael@0: PR_Seek(fb->fd, storedOffset, PR_SEEK_SET); michael@0: if (amountRead == -1) { michael@0: PR_Free(*buf); michael@0: *buf = NULL; michael@0: return 0; michael@0: } michael@0: michael@0: (*buf)[end-start+1] = '\0'; michael@0: return amountRead; michael@0: } michael@0: michael@0: michael@0: /************************************************************************ michael@0: * michael@0: * F B _ D e s t r o y michael@0: * michael@0: */ michael@0: static void michael@0: FB_Destroy(FileBuffer *fb) michael@0: { michael@0: if (fb) { michael@0: PR_Free(fb); michael@0: } michael@0: } michael@0: michael@0: michael@0: /************************************************************************ michael@0: * michael@0: * P r i n t T a g I t e m michael@0: * michael@0: */ michael@0: static void michael@0: PrintTagItem(PRFileDesc *fd, TagItem *ti) michael@0: { michael@0: AVPair * pair; michael@0: michael@0: PR_fprintf(fd, "TAG:\n----\nType: "); michael@0: switch (ti->type) { michael@0: case APPLET_TAG: michael@0: PR_fprintf(fd, "applet\n"); michael@0: break; michael@0: case SCRIPT_TAG: michael@0: PR_fprintf(fd, "script\n"); michael@0: break; michael@0: case LINK_TAG: michael@0: PR_fprintf(fd, "link\n"); michael@0: break; michael@0: case STYLE_TAG: michael@0: PR_fprintf(fd, "style\n"); michael@0: break; michael@0: case COMMENT_TAG: michael@0: PR_fprintf(fd, "comment\n"); michael@0: break; michael@0: case OTHER_TAG: michael@0: default: michael@0: PR_fprintf(fd, "other\n"); michael@0: break; michael@0: } michael@0: michael@0: PR_fprintf(fd, "Attributes:\n"); michael@0: for (pair = ti->attList; pair; pair = pair->next) { michael@0: PR_fprintf(fd, "\t%s=%s\n", pair->attribute, michael@0: pair->value ? pair->value : ""); michael@0: } michael@0: PR_fprintf(fd, "Text:%s\n", ti->text ? ti->text : ""); michael@0: michael@0: PR_fprintf(fd, "---End of tag---\n"); michael@0: } michael@0: michael@0: michael@0: /************************************************************************ michael@0: * michael@0: * P r i n t H T M L S t r e a m michael@0: * michael@0: */ michael@0: static void michael@0: PrintHTMLStream(PRFileDesc *fd, HTMLItem *head) michael@0: { michael@0: while (head) { michael@0: if (head->type == TAG_ITEM) { michael@0: PrintTagItem(fd, head->item.tag); michael@0: } else { michael@0: PR_fprintf(fd, "\nTEXT:\n-----\n%s\n-----\n\n", head->item.text); michael@0: } michael@0: head = head->next; michael@0: } michael@0: } michael@0: michael@0: michael@0: /************************************************************************ michael@0: * michael@0: * S a v e I n l i n e S c r i p t michael@0: * michael@0: */ michael@0: static int michael@0: SaveInlineScript(char *text, char *id, char *basedir, char *archiveDir) michael@0: { michael@0: char *filename = NULL; michael@0: PRFileDesc * fd = NULL; michael@0: int retval = -1; michael@0: PRInt32 writeLen; michael@0: char *ilDir = NULL; michael@0: michael@0: if (!text || !id || !archiveDir) { michael@0: return - 1; michael@0: } michael@0: michael@0: if (dumpParse) { michael@0: PR_fprintf(outputFD, "SaveInlineScript: text=%s, id=%s, \n" michael@0: "basedir=%s, archiveDir=%s\n", michael@0: text, id, basedir, archiveDir); michael@0: } michael@0: michael@0: /* Make sure the archive directory is around */ michael@0: if (ensureExists(basedir, archiveDir) != PR_SUCCESS) { michael@0: PR_fprintf(errorFD, michael@0: "ERROR: Unable to create archive directory %s.\n", archiveDir); michael@0: errorCount++; michael@0: return - 1; michael@0: } michael@0: michael@0: /* Make sure the inline script directory is around */ michael@0: ilDir = PR_smprintf("%s/inlineScripts", archiveDir); michael@0: scriptdir = "inlineScripts"; michael@0: if (ensureExists(basedir, ilDir) != PR_SUCCESS) { michael@0: PR_fprintf(errorFD, michael@0: "ERROR: Unable to create directory %s.\n", ilDir); michael@0: errorCount++; michael@0: return - 1; michael@0: } michael@0: michael@0: filename = PR_smprintf("%s/%s/%s", basedir, ilDir, id); michael@0: michael@0: /* If the file already exists, give a warning, then blow it away */ michael@0: if (PR_Access(filename, PR_ACCESS_EXISTS) == PR_SUCCESS) { michael@0: PR_fprintf(errorFD, michael@0: "warning: file \"%s\" already exists--will overwrite.\n", michael@0: filename); michael@0: warningCount++; michael@0: if (rm_dash_r(filename)) { michael@0: PR_fprintf(errorFD, "ERROR: Unable to delete %s.\n", filename); michael@0: errorCount++; michael@0: goto finish; michael@0: } michael@0: } michael@0: michael@0: /* Write text into file with name id */ michael@0: fd = PR_Open(filename, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0777); michael@0: if (!fd) { michael@0: PR_fprintf(errorFD, "ERROR: Unable to create file \"%s\".\n", michael@0: filename); michael@0: errorCount++; michael@0: goto finish; michael@0: } michael@0: writeLen = strlen(text); michael@0: if ( PR_Write(fd, text, writeLen) != writeLen) { michael@0: PR_fprintf(errorFD, "ERROR: Unable to write to file \"%s\".\n", michael@0: filename); michael@0: errorCount++; michael@0: goto finish; michael@0: } michael@0: michael@0: retval = 0; michael@0: finish: michael@0: if (filename) { michael@0: PR_smprintf_free(filename); michael@0: } michael@0: if (ilDir) { michael@0: PR_smprintf_free(ilDir); michael@0: } michael@0: if (fd) { michael@0: PR_Close(fd); michael@0: } michael@0: return retval; michael@0: } michael@0: michael@0: michael@0: /************************************************************************ michael@0: * michael@0: * S a v e U n n a m a b l e S c r i p t michael@0: * michael@0: */ michael@0: static int michael@0: SaveUnnamableScript(char *text, char *basedir, char *archiveDir, michael@0: char *HTMLfilename) michael@0: { michael@0: char *id = NULL; michael@0: char *ext = NULL; michael@0: char *start = NULL; michael@0: int retval = -1; michael@0: michael@0: if (!text || !archiveDir || !HTMLfilename) { michael@0: return - 1; michael@0: } michael@0: michael@0: if (dumpParse) { michael@0: PR_fprintf(outputFD, "SaveUnnamableScript: text=%s, basedir=%s,\n" michael@0: "archiveDir=%s, filename=%s\n", text, basedir, archiveDir, michael@0: HTMLfilename); michael@0: } michael@0: michael@0: /* Construct the filename */ michael@0: ext = PL_strrchr(HTMLfilename, '.'); michael@0: if (ext) { michael@0: *ext = '\0'; michael@0: } michael@0: for (start = HTMLfilename; strpbrk(start, "/\\"); michael@0: start = strpbrk(start, "/\\") + 1) michael@0: /* do nothing */; michael@0: if (*start == '\0') michael@0: start = HTMLfilename; michael@0: id = PR_smprintf("_%s%d", start, idOrdinal++); michael@0: if (ext) { michael@0: *ext = '.'; michael@0: } michael@0: michael@0: /* Now call SaveInlineScript to do the work */ michael@0: retval = SaveInlineScript(text, id, basedir, archiveDir); michael@0: michael@0: PR_Free(id); michael@0: michael@0: return retval; michael@0: } michael@0: michael@0: michael@0: /************************************************************************ michael@0: * michael@0: * S a v e S o u r c e michael@0: * michael@0: */ michael@0: static int michael@0: SaveSource(char *src, char *codebase, char *basedir, char *archiveDir) michael@0: { michael@0: char *from = NULL, *to = NULL; michael@0: int retval = -1; michael@0: char *arcDir = NULL; michael@0: michael@0: if (!src || !archiveDir) { michael@0: return - 1; michael@0: } michael@0: michael@0: if (dumpParse) { michael@0: PR_fprintf(outputFD, "SaveSource: src=%s, codebase=%s, basedir=%s,\n" michael@0: "archiveDir=%s\n", src, codebase, basedir, archiveDir); michael@0: } michael@0: michael@0: if (codebase) { michael@0: arcDir = PR_smprintf("%s/%s/%s/", basedir, codebase, archiveDir); michael@0: } else { michael@0: arcDir = PR_smprintf("%s/%s/", basedir, archiveDir); michael@0: } michael@0: michael@0: if (codebase) { michael@0: from = PR_smprintf("%s/%s/%s", basedir, codebase, src); michael@0: to = PR_smprintf("%s%s", arcDir, src); michael@0: } else { michael@0: from = PR_smprintf("%s/%s", basedir, src); michael@0: to = PR_smprintf("%s%s", arcDir, src); michael@0: } michael@0: michael@0: if (make_dirs(to, 0777)) { michael@0: PR_fprintf(errorFD, michael@0: "ERROR: Unable to create archive directory %s.\n", archiveDir); michael@0: errorCount++; michael@0: goto finish; michael@0: } michael@0: michael@0: retval = copyinto(from, to); michael@0: finish: michael@0: if (from) michael@0: PR_Free(from); michael@0: if (to) michael@0: PR_Free(to); michael@0: if (arcDir) michael@0: PR_Free(arcDir); michael@0: return retval; michael@0: } michael@0: michael@0: michael@0: /************************************************************************ michael@0: * michael@0: * T a g T y p e T o S t r i n g michael@0: * michael@0: */ michael@0: char * michael@0: TagTypeToString(TAG_TYPE type) michael@0: { michael@0: switch (type) { michael@0: case APPLET_TAG: michael@0: return "APPLET"; michael@0: case SCRIPT_TAG: michael@0: return "SCRIPT"; michael@0: case LINK_TAG: michael@0: return "LINK"; michael@0: case STYLE_TAG: michael@0: return "STYLE"; michael@0: default: michael@0: break; michael@0: } michael@0: return "unknown"; michael@0: } michael@0: michael@0: michael@0: /************************************************************************ michael@0: * michael@0: * e x t r a c t _ j s michael@0: * michael@0: */ michael@0: static int michael@0: extract_js(char *filename) michael@0: { michael@0: PRFileDesc * fd = NULL; michael@0: FileBuffer * fb = NULL; michael@0: HTMLItem * head = NULL; michael@0: HTMLItem * tail = NULL; michael@0: HTMLItem * curitem = NULL; michael@0: HTMLItem * styleList = NULL; michael@0: HTMLItem * styleListTail = NULL; michael@0: HTMLItem * entityList = NULL; michael@0: HTMLItem * entityListTail = NULL; michael@0: TagItem * tagp = NULL; michael@0: char *text = NULL; michael@0: char *tagerr = NULL; michael@0: char *archiveDir = NULL; michael@0: char *firstArchiveDir = NULL; michael@0: char *basedir = NULL; michael@0: PRInt32 textStart; michael@0: PRInt32 curOffset; michael@0: HTML_STATE state; michael@0: int curchar; michael@0: int retval = -1; michael@0: unsigned int linenum, startLine; michael@0: michael@0: /* Initialize the implicit ID counter for each file */ michael@0: idOrdinal = 0; michael@0: michael@0: /* michael@0: * First, parse the HTML into a stream of tags and text. michael@0: */ michael@0: michael@0: fd = PR_Open(filename, PR_RDONLY, 0); michael@0: if (!fd) { michael@0: PR_fprintf(errorFD, "Unable to open %s for reading.\n", filename); michael@0: errorCount++; michael@0: return - 1; michael@0: } michael@0: michael@0: /* Construct base directory of filename. */ michael@0: { michael@0: char *cp; michael@0: michael@0: basedir = PL_strdup(filename); michael@0: michael@0: /* Remove trailing slashes */ michael@0: while ( (cp = PL_strprbrk(basedir, "/\\")) == michael@0: (basedir + strlen(basedir) - 1)) { michael@0: *cp = '\0'; michael@0: } michael@0: michael@0: /* Now remove everything from the last slash (which will be followed michael@0: * by a filename) to the end */ michael@0: cp = PL_strprbrk(basedir, "/\\"); michael@0: if (cp) { michael@0: *cp = '\0'; michael@0: } michael@0: } michael@0: michael@0: state = TEXT_HTML_STATE; michael@0: michael@0: fb = FB_Create(fd); michael@0: michael@0: textStart = 0; michael@0: startLine = 0; michael@0: while (linenum = FB_GetLineNum(fb), (curchar = FB_GetChar(fb)) != michael@0: EOF) { michael@0: switch (state) { michael@0: case TEXT_HTML_STATE: michael@0: if (curchar == '<') { michael@0: /* michael@0: * Found a tag michael@0: */ michael@0: /* Save the text so far to a new text item */ michael@0: curOffset = FB_GetPointer(fb) - 2; michael@0: if (curOffset >= textStart) { michael@0: if (FB_GetRange(fb, textStart, curOffset, michael@0: &text) != michael@0: curOffset - textStart + 1) { michael@0: PR_fprintf(errorFD, michael@0: "Unable to read from %s.\n", michael@0: filename); michael@0: errorCount++; michael@0: goto loser; michael@0: } michael@0: /* little fudge here. If the first character on a line michael@0: * is '<', meaning a new tag, the preceding text item michael@0: * actually ends on the previous line. In this case michael@0: * we will be saying that the text segment ends on the michael@0: * next line. I don't think this matters for text items. */ michael@0: curitem = CreateTextItem(text, startLine, michael@0: linenum); michael@0: text = NULL; michael@0: if (tail == NULL) { michael@0: head = tail = curitem; michael@0: } else { michael@0: tail->next = curitem; michael@0: tail = curitem; michael@0: } michael@0: } michael@0: michael@0: /* Process the tag */ michael@0: tagp = ProcessTag(fb, &tagerr); michael@0: if (!tagp) { michael@0: if (tagerr) { michael@0: PR_fprintf(errorFD, "Error in file %s: %s\n", michael@0: filename, tagerr); michael@0: errorCount++; michael@0: } else { michael@0: PR_fprintf(errorFD, michael@0: "Error in file %s, in tag starting at line %d\n", michael@0: filename, linenum); michael@0: errorCount++; michael@0: } michael@0: goto loser; michael@0: } michael@0: /* Add the tag to the list */ michael@0: curitem = CreateTagItem(tagp, linenum, FB_GetLineNum(fb)); michael@0: if (tail == NULL) { michael@0: head = tail = curitem; michael@0: } else { michael@0: tail->next = curitem; michael@0: tail = curitem; michael@0: } michael@0: michael@0: /* What's the next state */ michael@0: if (tagp->type == SCRIPT_TAG) { michael@0: state = SCRIPT_HTML_STATE; michael@0: } michael@0: michael@0: /* Start recording text from the new offset */ michael@0: textStart = FB_GetPointer(fb); michael@0: startLine = FB_GetLineNum(fb); michael@0: } else { michael@0: /* regular character. Next! */ michael@0: } michael@0: break; michael@0: case SCRIPT_HTML_STATE: michael@0: if (curchar == '<') { michael@0: char *cp; michael@0: /* michael@0: * If this is a tag, then we're at the end of the michael@0: * script. Otherwise, ignore michael@0: */ michael@0: curOffset = FB_GetPointer(fb) - 1; michael@0: cp = NULL; michael@0: if (FB_GetRange(fb, curOffset, curOffset + 8, &cp) != 9) { michael@0: if (cp) { michael@0: PR_Free(cp); michael@0: cp = NULL; michael@0: } michael@0: } else { michael@0: /* compare the strings */ michael@0: if ( !PORT_Strncasecmp(cp, "", 9) ) { michael@0: /* This is the end of the script. Record the text. */ michael@0: curOffset--; michael@0: if (curOffset >= textStart) { michael@0: if (FB_GetRange(fb, textStart, curOffset, &text) != michael@0: curOffset - textStart + 1) { michael@0: PR_fprintf(errorFD, "Unable to read from %s.\n", michael@0: filename); michael@0: errorCount++; michael@0: goto loser; michael@0: } michael@0: curitem = CreateTextItem(text, startLine, linenum); michael@0: text = NULL; michael@0: if (tail == NULL) { michael@0: head = tail = curitem; michael@0: } else { michael@0: tail->next = curitem; michael@0: tail = curitem; michael@0: } michael@0: } michael@0: michael@0: /* Now parse the /script tag and put it on the list */ michael@0: tagp = ProcessTag(fb, &tagerr); michael@0: if (!tagp) { michael@0: if (tagerr) { michael@0: PR_fprintf(errorFD, "Error in file %s: %s\n", michael@0: filename, tagerr); michael@0: } else { michael@0: PR_fprintf(errorFD, michael@0: "Error in file %s, in tag starting at" michael@0: " line %d\n", filename, linenum); michael@0: } michael@0: errorCount++; michael@0: goto loser; michael@0: } michael@0: curitem = CreateTagItem(tagp, linenum, michael@0: FB_GetLineNum(fb)); michael@0: if (tail == NULL) { michael@0: head = tail = curitem; michael@0: } else { michael@0: tail->next = curitem; michael@0: tail = curitem; michael@0: } michael@0: michael@0: /* go back to text state */ michael@0: state = TEXT_HTML_STATE; michael@0: michael@0: textStart = FB_GetPointer(fb); michael@0: startLine = FB_GetLineNum(fb); michael@0: } michael@0: } michael@0: } michael@0: break; michael@0: } michael@0: } michael@0: michael@0: /* End of the file. Wrap up any remaining text */ michael@0: if (state == SCRIPT_HTML_STATE) { michael@0: if (tail && tail->type == TAG_ITEM) { michael@0: PR_fprintf(errorFD, "ERROR: tag.\n", filename, tail->startLine); michael@0: } else { michael@0: PR_fprintf(errorFD, "ERROR: = textStart) { michael@0: text = NULL; michael@0: if ( FB_GetRange(fb, textStart, curOffset, &text) != michael@0: curOffset - textStart + 1) { michael@0: PR_fprintf(errorFD, "Unable to read from %s.\n", filename); michael@0: errorCount++; michael@0: goto loser; michael@0: } michael@0: curitem = CreateTextItem(text, startLine, linenum); michael@0: text = NULL; michael@0: if (tail == NULL) { michael@0: head = tail = curitem; michael@0: } else { michael@0: tail->next = curitem; michael@0: tail = curitem; michael@0: } michael@0: } michael@0: michael@0: if (dumpParse) { michael@0: PrintHTMLStream(outputFD, head); michael@0: } michael@0: michael@0: /* michael@0: * Now we have a stream of tags and text. Go through and deal with each. michael@0: */ michael@0: for (curitem = head; curitem; curitem = curitem->next) { michael@0: TagItem * tagp = NULL; michael@0: AVPair * pairp = NULL; michael@0: char *src = NULL, *id = NULL, *codebase = NULL; michael@0: PRBool hasEventHandler = PR_FALSE; michael@0: int i; michael@0: michael@0: /* Reset archive directory for each tag */ michael@0: if (archiveDir) { michael@0: PR_Free(archiveDir); michael@0: archiveDir = NULL; michael@0: } michael@0: michael@0: /* We only analyze tags */ michael@0: if (curitem->type != TAG_ITEM) { michael@0: continue; michael@0: } michael@0: michael@0: tagp = curitem->item.tag; michael@0: michael@0: /* go through the attributes to get information */ michael@0: for (pairp = tagp->attList; pairp; pairp = pairp->next) { michael@0: michael@0: /* ARCHIVE= */ michael@0: if ( !PL_strcasecmp(pairp->attribute, "archive")) { michael@0: if (archiveDir) { michael@0: /* Duplicate attribute. Print warning */ michael@0: PR_fprintf(errorFD, michael@0: "warning: \"%s\" attribute overwrites previous attribute" michael@0: " in tag starting at %s:%d.\n", michael@0: pairp->attribute, filename, curitem->startLine); michael@0: warningCount++; michael@0: PR_Free(archiveDir); michael@0: } michael@0: archiveDir = PL_strdup(pairp->value); michael@0: michael@0: /* Substiture ".arc" for ".jar" */ michael@0: if ( (PL_strlen(archiveDir) < 4) || michael@0: PL_strcasecmp((archiveDir + strlen(archiveDir) -4), michael@0: ".jar")) { michael@0: PR_fprintf(errorFD, michael@0: "warning: ARCHIVE attribute should end in \".jar\" in tag" michael@0: " starting on %s:%d.\n", filename, curitem->startLine); michael@0: warningCount++; michael@0: PR_Free(archiveDir); michael@0: archiveDir = PR_smprintf("%s.arc", archiveDir); michael@0: } else { michael@0: PL_strcpy(archiveDir + strlen(archiveDir) -4, ".arc"); michael@0: } michael@0: michael@0: /* Record the first archive. This will be used later if michael@0: * the archive is not specified */ michael@0: if (firstArchiveDir == NULL) { michael@0: firstArchiveDir = PL_strdup(archiveDir); michael@0: } michael@0: } michael@0: /* CODEBASE= */ michael@0: else if ( !PL_strcasecmp(pairp->attribute, "codebase")) { michael@0: if (codebase) { michael@0: /* Duplicate attribute. Print warning */ michael@0: PR_fprintf(errorFD, michael@0: "warning: \"%s\" attribute overwrites previous attribute" michael@0: " in tag staring at %s:%d.\n", michael@0: pairp->attribute, filename, curitem->startLine); michael@0: warningCount++; michael@0: } michael@0: codebase = pairp->value; michael@0: } michael@0: /* SRC= and HREF= */ michael@0: else if ( !PORT_Strcasecmp(pairp->attribute, "src") || michael@0: !PORT_Strcasecmp(pairp->attribute, "href") ) { michael@0: if (src) { michael@0: /* Duplicate attribute. Print warning */ michael@0: PR_fprintf(errorFD, michael@0: "warning: \"%s\" attribute overwrites previous attribute" michael@0: " in tag staring at %s:%d.\n", michael@0: pairp->attribute, filename, curitem->startLine); michael@0: warningCount++; michael@0: } michael@0: src = pairp->value; michael@0: } michael@0: /* CODE= */ michael@0: else if (!PORT_Strcasecmp(pairp->attribute, "code") ) { michael@0: /*!!!XXX Change PORT to PL all over this code !!! */ michael@0: if (src) { michael@0: /* Duplicate attribute. Print warning */ michael@0: PR_fprintf(errorFD, michael@0: "warning: \"%s\" attribute overwrites previous attribute" michael@0: " ,in tag staring at %s:%d.\n", michael@0: pairp->attribute, filename, curitem->startLine); michael@0: warningCount++; michael@0: } michael@0: src = pairp->value; michael@0: michael@0: /* Append a .class if one is not already present */ michael@0: if ( (PL_strlen(src) < 6) || michael@0: PL_strcasecmp( (src + PL_strlen(src) - 6), ".class") ) { michael@0: src = PR_smprintf("%s.class", src); michael@0: /* Put this string back into the data structure so it michael@0: * will be deallocated properly */ michael@0: PR_Free(pairp->value); michael@0: pairp->value = src; michael@0: } michael@0: } michael@0: /* ID= */ michael@0: else if (!PL_strcasecmp(pairp->attribute, "id") ) { michael@0: if (id) { michael@0: /* Duplicate attribute. Print warning */ michael@0: PR_fprintf(errorFD, michael@0: "warning: \"%s\" attribute overwrites previous attribute" michael@0: " in tag staring at %s:%d.\n", michael@0: pairp->attribute, filename, curitem->startLine); michael@0: warningCount++; michael@0: } michael@0: id = pairp->value; michael@0: } michael@0: michael@0: /* STYLE= */ michael@0: /* style= attributes, along with JS entities, are stored into michael@0: * files with dynamically generated names. The filenames are michael@0: * based on the order in which the text is found in the file. michael@0: * All JS entities on all lines up to and including the line michael@0: * containing the end of the tag that has this style= attribute michael@0: * will be processed before this style=attribute. So we need michael@0: * to record the line that this _tag_ (not the attribute) ends on. michael@0: */ michael@0: else if (!PL_strcasecmp(pairp->attribute, "style") && pairp->value) michael@0: { michael@0: HTMLItem * styleItem; michael@0: /* Put this item on the style list */ michael@0: styleItem = CreateTextItem(PL_strdup(pairp->value), michael@0: curitem->startLine, curitem->endLine); michael@0: if (styleListTail == NULL) { michael@0: styleList = styleListTail = styleItem; michael@0: } else { michael@0: styleListTail->next = styleItem; michael@0: styleListTail = styleItem; michael@0: } michael@0: } michael@0: /* Event handlers */ michael@0: else { michael@0: for (i = 0; i < num_handlers; i++) { michael@0: if (!PL_strcasecmp(event_handlers[i], pairp->attribute)) { michael@0: hasEventHandler = PR_TRUE; michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: michael@0: /* JS Entity */ michael@0: { michael@0: char *entityStart, *entityEnd; michael@0: HTMLItem * entityItem; michael@0: michael@0: /* go through each JavaScript entity ( &{...}; ) and store it michael@0: * in the entityList. The important thing is to record what michael@0: * line number it's on, so we can get it in the right order michael@0: * in relation to style= attributes. michael@0: * Apparently, these can't flow across lines, so the start and michael@0: * end line will be the same. That helps matters. michael@0: */ michael@0: entityEnd = pairp->value; michael@0: while ( entityEnd && michael@0: (entityStart = PL_strstr(entityEnd, "&{")) /*}*/ != NULL) { michael@0: entityStart += 2; /* point at beginning of actual entity */ michael@0: entityEnd = PL_strchr(entityStart, '}'); michael@0: if (entityEnd) { michael@0: /* Put this item on the entity list */ michael@0: *entityEnd = '\0'; michael@0: entityItem = CreateTextItem(PL_strdup(entityStart), michael@0: pairp->valueLine, pairp->valueLine); michael@0: *entityEnd = /* { */ '}'; michael@0: if (entityListTail) { michael@0: entityListTail->next = entityItem; michael@0: entityListTail = entityItem; michael@0: } else { michael@0: entityList = entityListTail = entityItem; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: /* If no archive was supplied, we use the first one of the file */ michael@0: if (!archiveDir && firstArchiveDir) { michael@0: archiveDir = PL_strdup(firstArchiveDir); michael@0: } michael@0: michael@0: /* If we have an event handler, we need to archive this tag */ michael@0: if (hasEventHandler) { michael@0: if (!id) { michael@0: PR_fprintf(errorFD, michael@0: "warning: tag starting at %s:%d has event handler but" michael@0: " no ID attribute. The tag will not be signed.\n", michael@0: filename, curitem->startLine); michael@0: warningCount++; michael@0: } else if (!archiveDir) { michael@0: PR_fprintf(errorFD, michael@0: "warning: tag starting at %s:%d has event handler but" michael@0: " no ARCHIVE attribute. The tag will not be signed.\n", michael@0: filename, curitem->startLine); michael@0: warningCount++; michael@0: } else { michael@0: if (SaveInlineScript(tagp->text, id, basedir, archiveDir)) { michael@0: goto loser; michael@0: } michael@0: } michael@0: } michael@0: michael@0: switch (tagp->type) { michael@0: case APPLET_TAG: michael@0: if (!src) { michael@0: PR_fprintf(errorFD, michael@0: "error: APPLET tag starting on %s:%d has no CODE " michael@0: "attribute.\n", filename, curitem->startLine); michael@0: errorCount++; michael@0: goto loser; michael@0: } else if (!archiveDir) { michael@0: PR_fprintf(errorFD, michael@0: "error: APPLET tag starting on %s:%d has no ARCHIVE " michael@0: "attribute.\n", filename, curitem->startLine); michael@0: errorCount++; michael@0: goto loser; michael@0: } else { michael@0: if (SaveSource(src, codebase, basedir, archiveDir)) { michael@0: goto loser; michael@0: } michael@0: } michael@0: break; michael@0: case SCRIPT_TAG: michael@0: case LINK_TAG: michael@0: case STYLE_TAG: michael@0: if (!archiveDir) { michael@0: PR_fprintf(errorFD, michael@0: "error: %s tag starting on %s:%d has no ARCHIVE " michael@0: "attribute.\n", TagTypeToString(tagp->type), michael@0: filename, curitem->startLine); michael@0: errorCount++; michael@0: goto loser; michael@0: } else if (src) { michael@0: if (SaveSource(src, codebase, basedir, archiveDir)) { michael@0: goto loser; michael@0: } michael@0: } else if (id) { michael@0: /* Save the next text item */ michael@0: if (!curitem->next || (curitem->next->type != michael@0: TEXT_ITEM)) { michael@0: PR_fprintf(errorFD, michael@0: "warning: %s tag starting on %s:%d is not followed" michael@0: " by script text.\n", TagTypeToString(tagp->type), michael@0: filename, curitem->startLine); michael@0: warningCount++; michael@0: /* just create empty file */ michael@0: if (SaveInlineScript("", id, basedir, archiveDir)) { michael@0: goto loser; michael@0: } michael@0: } else { michael@0: curitem = curitem->next; michael@0: if (SaveInlineScript(curitem->item.text, michael@0: id, basedir, michael@0: archiveDir)) { michael@0: goto loser; michael@0: } michael@0: } michael@0: } else { michael@0: /* No src or id tag--warning */ michael@0: PR_fprintf(errorFD, michael@0: "warning: %s tag starting on %s:%d has no SRC or" michael@0: " ID attributes. Will not sign.\n", michael@0: TagTypeToString(tagp->type), filename, curitem->startLine); michael@0: warningCount++; michael@0: } michael@0: break; michael@0: default: michael@0: /* do nothing for other tags */ michael@0: break; michael@0: } michael@0: michael@0: } michael@0: michael@0: /* Now deal with all the unnamable scripts */ michael@0: if (firstArchiveDir) { michael@0: HTMLItem * style, *entity; michael@0: michael@0: /* Go through the lists of JS entities and style attributes. Do them michael@0: * in chronological order within a list. Pick the list with the lower michael@0: * endLine. In case of a tie, entities come first. michael@0: */ michael@0: style = styleList; michael@0: entity = entityList; michael@0: while (style || entity) { michael@0: if (!entity || (style && (style->endLine < entity->endLine))) { michael@0: /* Process style */ michael@0: SaveUnnamableScript(style->item.text, basedir, firstArchiveDir, michael@0: filename); michael@0: style = style->next; michael@0: } else { michael@0: /* Process entity */ michael@0: SaveUnnamableScript(entity->item.text, basedir, firstArchiveDir, michael@0: filename); michael@0: entity = entity->next; michael@0: } michael@0: } michael@0: } michael@0: michael@0: michael@0: retval = 0; michael@0: loser: michael@0: /* Blow away the stream */ michael@0: while (head) { michael@0: curitem = head; michael@0: head = head->next; michael@0: DestroyHTMLItem(curitem); michael@0: } michael@0: while (styleList) { michael@0: curitem = styleList; michael@0: styleList = styleList->next; michael@0: DestroyHTMLItem(curitem); michael@0: } michael@0: while (entityList) { michael@0: curitem = entityList; michael@0: entityList = entityList->next; michael@0: DestroyHTMLItem(curitem); michael@0: } michael@0: if (text) { michael@0: PR_Free(text); michael@0: text = NULL; michael@0: } michael@0: if (fb) { michael@0: FB_Destroy(fb); michael@0: fb = NULL; michael@0: } michael@0: if (fd) { michael@0: PR_Close(fd); michael@0: } michael@0: if (tagerr) { michael@0: PR_smprintf_free(tagerr); michael@0: tagerr = NULL; michael@0: } michael@0: if (archiveDir) { michael@0: PR_Free(archiveDir); michael@0: archiveDir = NULL; michael@0: } michael@0: if (firstArchiveDir) { michael@0: PR_Free(firstArchiveDir); michael@0: firstArchiveDir = NULL; michael@0: } michael@0: return retval; michael@0: } michael@0: michael@0: michael@0: /********************************************************************** michael@0: * michael@0: * e n s u r e E x i s t s michael@0: * michael@0: * Check for existence of indicated directory. If it doesn't exist, michael@0: * it will be created. michael@0: * Returns PR_SUCCESS if the directory is present, PR_FAILURE otherwise. michael@0: */ michael@0: static PRStatus michael@0: ensureExists (char *base, char *path) michael@0: { michael@0: char fn [FNSIZE]; michael@0: PRDir * dir; michael@0: sprintf (fn, "%s/%s", base, path); michael@0: michael@0: /*PR_fprintf(outputFD, "Trying to open directory %s.\n", fn);*/ michael@0: michael@0: if ( (dir = PR_OpenDir(fn)) ) { michael@0: PR_CloseDir(dir); michael@0: return PR_SUCCESS; michael@0: } michael@0: return PR_MkDir(fn, 0777); michael@0: } michael@0: michael@0: michael@0: /*************************************************************************** michael@0: * michael@0: * m a k e _ d i r s michael@0: * michael@0: * Ensure that the directory portion of the path exists. This may require michael@0: * making the directory, and its parent, and its parent's parent, etc. michael@0: */ michael@0: static int michael@0: make_dirs(char *path, int file_perms) michael@0: { michael@0: char *Path; michael@0: char *start; michael@0: char *sep; michael@0: int ret = 0; michael@0: PRFileInfo info; michael@0: michael@0: if (!path) { michael@0: return 0; michael@0: } michael@0: michael@0: Path = PL_strdup(path); michael@0: start = strpbrk(Path, "/\\"); michael@0: if (!start) { michael@0: return 0; michael@0: } michael@0: start++; /* start right after first slash */ michael@0: michael@0: /* Each time through the loop add one more directory. */ michael@0: while ( (sep = strpbrk(start, "/\\")) ) { michael@0: *sep = '\0'; michael@0: michael@0: if ( PR_GetFileInfo(Path, &info) != PR_SUCCESS) { michael@0: /* No such dir, we have to create it */ michael@0: if ( PR_MkDir(Path, file_perms) != PR_SUCCESS) { michael@0: PR_fprintf(errorFD, "ERROR: Unable to create directory %s.\n", michael@0: Path); michael@0: errorCount++; michael@0: ret = -1; michael@0: goto loser; michael@0: } michael@0: } else { michael@0: /* something exists by this name, make sure it's a directory */ michael@0: if ( info.type != PR_FILE_DIRECTORY ) { michael@0: PR_fprintf(errorFD, "ERROR: Unable to create directory %s.\n", michael@0: Path); michael@0: errorCount++; michael@0: ret = -1; michael@0: goto loser; michael@0: } michael@0: } michael@0: michael@0: start = sep + 1; /* start after the next slash */ michael@0: *sep = '/'; michael@0: } michael@0: michael@0: loser: michael@0: PR_Free(Path); michael@0: return ret; michael@0: } michael@0: michael@0: michael@0: /* michael@0: * c o p y i n t o michael@0: * michael@0: * Function to copy file "from" to path "to". michael@0: * michael@0: */ michael@0: static int michael@0: copyinto (char *from, char *to) michael@0: { michael@0: PRInt32 num; michael@0: char buf [BUFSIZ]; michael@0: PRFileDesc * infp = NULL, *outfp = NULL; michael@0: int retval = -1; michael@0: michael@0: if ((infp = PR_Open(from, PR_RDONLY, 0777)) == NULL) { michael@0: PR_fprintf(errorFD, "ERROR: Unable to open \"%s\" for reading.\n", michael@0: from); michael@0: errorCount++; michael@0: goto finish; michael@0: } michael@0: michael@0: /* If to already exists, print a warning before deleting it */ michael@0: if (PR_Access(to, PR_ACCESS_EXISTS) == PR_SUCCESS) { michael@0: PR_fprintf(errorFD, "warning: %s already exists--will overwrite\n", to); michael@0: warningCount++; michael@0: if (rm_dash_r(to)) { michael@0: PR_fprintf(errorFD, michael@0: "ERROR: Unable to remove %s.\n", to); michael@0: errorCount++; michael@0: goto finish; michael@0: } michael@0: } michael@0: michael@0: if ((outfp = PR_Open(to, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0777)) michael@0: == NULL) { michael@0: char *errBuf = NULL; michael@0: michael@0: errBuf = PR_Malloc(PR_GetErrorTextLength() + 1); michael@0: PR_fprintf(errorFD, "ERROR: Unable to open \"%s\" for writing.\n", to); michael@0: if (PR_GetErrorText(errBuf)) { michael@0: PR_fprintf(errorFD, "Cause: %s\n", errBuf); michael@0: } michael@0: if (errBuf) { michael@0: PR_Free(errBuf); michael@0: } michael@0: errorCount++; michael@0: goto finish; michael@0: } michael@0: michael@0: while ( (num = PR_Read(infp, buf, BUFSIZ)) > 0) { michael@0: if (PR_Write(outfp, buf, num) != num) { michael@0: PR_fprintf(errorFD, "ERROR: Error writing to %s.\n", to); michael@0: errorCount++; michael@0: goto finish; michael@0: } michael@0: } michael@0: michael@0: retval = 0; michael@0: finish: michael@0: if (infp) michael@0: PR_Close(infp); michael@0: if (outfp) michael@0: PR_Close(outfp); michael@0: michael@0: return retval; michael@0: } michael@0: michael@0: