security/nss/cmd/signtool/javascript.c

Wed, 31 Dec 2014 07:16:47 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:16:47 +0100
branch
TOR_BUG_9701
changeset 3
141e0f1194b1
permissions
-rw-r--r--

Revert simplistic fix pending revisit of Mozilla integration attempt.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 #include "signtool.h"
     6 #include <prmem.h>
     7 #include <prio.h>
     8 #include <prenv.h>
    10 static int	javascript_fn(char *relpath, char *basedir, char *reldir,
    11 char *filename, void *arg);
    12 static int	extract_js (char *filename);
    13 static int	copyinto (char *from, char *to);
    14 static PRStatus ensureExists (char *base, char *path);
    15 static int	make_dirs(char *path, PRInt32 file_perms);
    17 static char	*jartree = NULL;
    18 static int	idOrdinal;
    19 static PRBool dumpParse = PR_FALSE;
    21 static char	*event_handlers[] = {
    22     "onAbort",
    23     "onBlur",
    24     "onChange",
    25     "onClick",
    26     "onDblClick",
    27     "onDragDrop",
    28     "onError",
    29     "onFocus",
    30     "onKeyDown",
    31     "onKeyPress",
    32     "onKeyUp",
    33     "onLoad",
    34     "onMouseDown",
    35     "onMouseMove",
    36     "onMouseOut",
    37     "onMouseOver",
    38     "onMouseUp",
    39     "onMove",
    40     "onReset",
    41     "onResize",
    42     "onSelect",
    43     "onSubmit",
    44     "onUnload"
    45 };
    48 static int	num_handlers = 23;
    50 /*
    51  *  I n l i n e J a v a S c r i p t
    52  *
    53  *  Javascript signing. Instead of passing an archive to signtool,
    54  *  a directory containing html files is given. Archives are created
    55  *  from the archive= and src= tag attributes inside the html,
    56  *  as appropriate. Then the archives are signed.
    57  *
    58  */
    59 int
    60 InlineJavaScript(char *dir, PRBool recurse)
    61 {
    62     jartree = dir;
    63     if (verbosity >= 0) {
    64 	PR_fprintf(outputFD, "\nGenerating inline signatures from HTML files in: %s\n",
    65 	     dir);
    66     }
    67     if (PR_GetEnv("SIGNTOOL_DUMP_PARSE")) {
    68 	dumpParse = PR_TRUE;
    69     }
    71     return foreach(dir, "", javascript_fn, recurse, PR_FALSE /*include dirs*/,
    72          		(void * )NULL);
    74 }
    77 /************************************************************************
    78  *
    79  * j a v a s c r i p t _ f n
    80  */
    81 static int	javascript_fn
    82 (char *relpath, char *basedir, char *reldir, char *filename, void *arg)
    83 {
    84     char	fullname [FNSIZE];
    86     /* only process inline scripts from .htm, .html, and .shtml*/
    88     if (!(PL_strcaserstr(filename, ".htm") == filename + strlen(filename) -
    89         4) && 
    90         !(PL_strcaserstr(filename, ".html") == filename + strlen(filename) -
    91         5) && 
    92         !(PL_strcaserstr(filename, ".shtml") == filename + strlen(filename)
    93         -6)) {
    94 	return 0;
    95     }
    97     /* don't process scripts that signtool has already
    98      extracted (those that are inside .arc directories) */
   100     if (PL_strcaserstr(filename, ".arc") == filename + strlen(filename) - 4)
   101 	return 0;
   103     if (verbosity >= 0) {
   104 	PR_fprintf(outputFD, "Processing HTML file: %s\n", relpath);
   105     }
   107     /* reset firstArchive at top of each HTML file */
   109     /* skip directories that contain extracted scripts */
   111     if (PL_strcaserstr(reldir, ".arc") == reldir + strlen(reldir) - 4)
   112 	return 0;
   114     sprintf (fullname, "%s/%s", basedir, relpath);
   115     return extract_js (fullname);
   116 }
   119 /*===========================================================================
   120  =
   121  = D A T A   S T R U C T U R E S
   122  =
   123 */
   124 typedef enum {
   125     TEXT_HTML_STATE = 0,
   126     SCRIPT_HTML_STATE
   127 } 
   130 HTML_STATE ;
   132 typedef enum {
   133     /* we start in the start state */
   134     START_STATE,
   136     /* We are looking for or reading in an attribute */
   137     GET_ATT_STATE,
   139     /* We're burning ws before finding an attribute */
   140     PRE_ATT_WS_STATE,
   142     /* We're burning ws after an attribute.  Looking for an '='. */
   143     POST_ATT_WS_STATE,
   145     /* We're burning ws after an '=', waiting for a value */
   146     PRE_VAL_WS_STATE,
   148     /* We're reading in a value */
   149     GET_VALUE_STATE,
   151     /* We're reading in a value that's inside quotes */
   152     GET_QUOTED_VAL_STATE,
   154     /* We've encountered the closing '>' */
   155     DONE_STATE,
   157     /* Error state */
   158     ERR_STATE
   159 } 
   162 TAG_STATE ;
   164 typedef struct AVPair_Str {
   165     char	*attribute;
   166     char	*value;
   167     unsigned int	valueLine; /* the line that the value ends on */
   168     struct AVPair_Str *next;
   169 } AVPair;
   171 typedef enum {
   172     APPLET_TAG,
   173     SCRIPT_TAG,
   174     LINK_TAG,
   175     STYLE_TAG,
   176     COMMENT_TAG,
   177     OTHER_TAG
   178 } 
   181 TAG_TYPE ;
   183 typedef struct {
   184     TAG_TYPE type;
   185     AVPair * attList;
   186     AVPair * attListTail;
   187     char	*text;
   188 } TagItem;
   190 typedef enum {
   191     TAG_ITEM,
   192     TEXT_ITEM
   193 } 
   196 ITEM_TYPE ;
   198 typedef struct HTMLItem_Str {
   199     unsigned int	startLine;
   200     unsigned int	endLine;
   201     ITEM_TYPE type;
   202     union {
   203 	TagItem *tag;
   204 	char	*text;
   205     } item;
   206     struct HTMLItem_Str *next;
   207 } HTMLItem;
   209 typedef struct {
   210     PRFileDesc *fd;
   211     PRInt32 curIndex;
   212     PRBool IsEOF;
   213 #define FILE_BUFFER_BUFSIZE 512
   214     char	buf[FILE_BUFFER_BUFSIZE];
   215     PRInt32 startOffset;
   216     PRInt32 maxIndex;
   217     unsigned int	lineNum;
   218 } FileBuffer;
   220 /*===========================================================================
   221  =
   222  = F U N C T I O N S
   223  =
   224 */
   225 static HTMLItem*CreateTextItem(char *text, unsigned int startline,
   226 unsigned int endline);
   227 static HTMLItem*CreateTagItem(TagItem*ti, unsigned int startline,
   228 unsigned int endline);
   229 static TagItem*ProcessTag(FileBuffer*fb, char **errStr);
   230 static void	DestroyHTMLItem(HTMLItem *item);
   231 static void	DestroyTagItem(TagItem*ti);
   232 static TAG_TYPE GetTagType(char *att);
   233 static FileBuffer*FB_Create(PRFileDesc*fd);
   234 static int	FB_GetChar(FileBuffer *fb);
   235 static PRInt32 FB_GetPointer(FileBuffer *fb);
   236 static PRInt32 FB_GetRange(FileBuffer *fb, PRInt32 start, PRInt32 end,
   237 char **buf);
   238 static unsigned int	FB_GetLineNum(FileBuffer *fb);
   239 static void	FB_Destroy(FileBuffer *fb);
   240 static void	PrintTagItem(PRFileDesc *fd, TagItem *ti);
   241 static void	PrintHTMLStream(PRFileDesc *fd, HTMLItem *head);
   243 /************************************************************************
   244  *
   245  * C r e a t e T e x t I t e m
   246  */
   247 static HTMLItem*
   248 CreateTextItem(char *text, unsigned int startline, unsigned int endline)
   249 {
   250     HTMLItem * item;
   252     item = PR_Malloc(sizeof(HTMLItem));
   253     if (!item) {
   254 	return NULL;
   255     }
   257     item->type = TEXT_ITEM;
   258     item->item.text = text;
   259     item->next = NULL;
   260     item->startLine = startline;
   261     item->endLine = endline;
   263     return item;
   264 }
   267 /************************************************************************
   268  *
   269  * C r e a t e T a g I t e m
   270  */
   271 static HTMLItem*
   272 CreateTagItem(TagItem*ti, unsigned int startline, unsigned int endline)
   273 {
   274     HTMLItem * item;
   276     item = PR_Malloc(sizeof(HTMLItem));
   277     if (!item) {
   278 	return NULL;
   279     }
   281     item->type = TAG_ITEM;
   282     item->item.tag = ti;
   283     item->next = NULL;
   284     item->startLine = startline;
   285     item->endLine = endline;
   287     return item;
   288 }
   291 static PRBool
   292 isAttChar(int c)
   293 {
   294     return (isalnum(c) || c == '/' || c == '-');
   295 }
   298 /************************************************************************
   299  *
   300  * P r o c e s s T a g
   301  */
   302 static TagItem*
   303 ProcessTag(FileBuffer*fb, char **errStr)
   304 {
   305     TAG_STATE state;
   306     PRInt32 startText, startID, curPos;
   307     PRBool firstAtt;
   308     int	curchar;
   309     TagItem * ti = NULL;
   310     AVPair * curPair = NULL;
   311     char	quotechar = '\0';
   312     unsigned int	linenum;
   313     unsigned int	startline;
   315     state = START_STATE;
   317     startID = FB_GetPointer(fb);
   318     startText = startID;
   319     firstAtt = PR_TRUE;
   321     ti = (TagItem * ) PR_Malloc(sizeof(TagItem));
   322     if (!ti) 
   323 	out_of_memory();
   324     ti->type = OTHER_TAG;
   325     ti->attList = NULL;
   326     ti->attListTail = NULL;
   327     ti->text = NULL;
   329     startline = FB_GetLineNum(fb);
   331     while (state != DONE_STATE && state != ERR_STATE) {
   332 	linenum = FB_GetLineNum(fb);
   333 	curchar = FB_GetChar(fb);
   334 	if (curchar == EOF) {
   335 	    *errStr = PR_smprintf(
   336 	        "line %d: Unexpected end-of-file while parsing tag starting at line %d.\n",
   337 	         linenum, startline);
   338 	    state = ERR_STATE;
   339 	    continue;
   340 	}
   342 	switch (state) {
   343 	case START_STATE:
   344 	    if (curchar == '!') {
   345 		/*
   346 		 * SGML tag or comment
   347 		 * Here's the general rule for SGML tags.  Everything from
   348 		 * <! to > is the tag.  Inside the tag, comments are
   349 		 * delimited with --.  So we are looking for the first '>'
   350 		 * that is not commented out, that is, not inside a pair
   351 		 * of --: <!DOCTYPE --this is a comment >(psyche!)   -->
   352 		 */
   354 		PRBool inComment = PR_FALSE;
   355 		short	hyphenCount = 0; /* number of consecutive hyphens */
   357 		while (1) {
   358 		    linenum = FB_GetLineNum(fb);
   359 		    curchar = FB_GetChar(fb);
   360 		    if (curchar == EOF) {
   361 			/* Uh oh, EOF inside comment */
   362 			*errStr = PR_smprintf(
   363     "line %d: Unexpected end-of-file inside comment starting at line %d.\n",
   364   						linenum, startline);
   365 			state = ERR_STATE;
   366 			break;
   367 		    }
   368 		    if (curchar == '-') {
   369 			if (hyphenCount == 1) {
   370 			    /* This is a comment delimiter */
   371 			    inComment = !inComment;
   372 			    hyphenCount = 0;
   373 			} else {
   374 			    /* beginning of a comment delimiter? */
   375 			    hyphenCount = 1;
   376 			}
   377 		    } else if (curchar == '>') {
   378 			if (!inComment) {
   379 			    /* This is the end of the tag */
   380 			    state = DONE_STATE;
   381 			    break;
   382 			} else {
   383 			    /* The > is inside a comment, so it's not
   384 							 * really the end of the tag */
   385 			    hyphenCount = 0;
   386 			}
   387 		    } else {
   388 			hyphenCount = 0;
   389 		    }
   390 		}
   391 		ti->type = COMMENT_TAG;
   392 		break;
   393 	    }
   394 	    /* fall through */
   395 	case GET_ATT_STATE:
   396 	    if (isspace(curchar) || curchar == '=' || curchar
   397 	        == '>') {
   398 		/* end of the current attribute */
   399 		curPos = FB_GetPointer(fb) - 2;
   400 		if (curPos >= startID) {
   401 		    /* We have an attribute */
   402 		    curPair = (AVPair * )PR_Malloc(sizeof(AVPair));
   403 		    if (!curPair) 
   404 			out_of_memory();
   405 		    curPair->value = NULL;
   406 		    curPair->next = NULL;
   407 		    FB_GetRange(fb, startID, curPos,
   408 		        &curPair->attribute);
   410 		    /* Stick this attribute on the list */
   411 		    if (ti->attListTail) {
   412 			ti->attListTail->next = curPair;
   413 			ti->attListTail = curPair;
   414 		    } else {
   415 			ti->attList = ti->attListTail =
   416 			    curPair;
   417 		    }
   419 		    /* If this is the first attribute, find the type of tag
   420 		     * based on it. Also, start saving the text of the tag. */
   421 		    if (firstAtt) {
   422 			ti->type = GetTagType(curPair->attribute);
   423 			startText = FB_GetPointer(fb)
   424 			    -1;
   425 			firstAtt = PR_FALSE;
   426 		    }
   427 		} else {
   428 		    if (curchar == '=') {
   429 			/* If we don't have any attribute but we do have an
   430 			 * equal sign, that's an error */
   431 			*errStr = PR_smprintf("line %d: Malformed tag starting at line %d.\n",
   432 			     linenum, startline);
   433 			state = ERR_STATE;
   434 			break;
   435 		    }
   436 		}
   438 		/* Compute next state */
   439 		if (curchar == '=') {
   440 		    startID = FB_GetPointer(fb);
   441 		    state = PRE_VAL_WS_STATE;
   442 		} else if (curchar == '>') {
   443 		    state = DONE_STATE;
   444 		} else if (curPair) {
   445 		    state = POST_ATT_WS_STATE;
   446 		} else {
   447 		    state = PRE_ATT_WS_STATE;
   448 		}
   449 	    } else if (isAttChar(curchar)) {
   450 		/* Just another char in the attribute. Do nothing */
   451 		state = GET_ATT_STATE;
   452 	    } else {
   453 		/* bogus char */
   454 		*errStr = PR_smprintf("line %d: Bogus chararacter '%c' in tag.\n",
   455 		     			linenum, curchar);
   456 		state = ERR_STATE;
   457 		break;
   458 	    }
   459 	    break;
   460 	case PRE_ATT_WS_STATE:
   461 	    if (curchar == '>') {
   462 		state = DONE_STATE;
   463 	    } else if (isspace(curchar)) {
   464 		/* more whitespace, do nothing */
   465 	    } else if (isAttChar(curchar)) {
   466 		/* starting another attribute */
   467 		startID = FB_GetPointer(fb) - 1;
   468 		state = GET_ATT_STATE;
   469 	    } else {
   470 		/* bogus char */
   471 		*errStr = PR_smprintf("line %d: Bogus character '%c' in tag.\n",
   472 		     			linenum, curchar);
   473 		state = ERR_STATE;
   474 		break;
   475 	    }
   476 	    break;
   477 	case POST_ATT_WS_STATE:
   478 	    if (curchar == '>') {
   479 		state = DONE_STATE;
   480 	    } else if (isspace(curchar)) {
   481 		/* more whitespace, do nothing */
   482 	    } else if (isAttChar(curchar)) {
   483 		/* starting another attribute */
   484 		startID = FB_GetPointer(fb) - 1;
   485 		state = GET_ATT_STATE;
   486 	    } else if (curchar == '=') {
   487 		/* there was whitespace between the attribute and its equal
   488 		 * sign, which means there's a value coming up */
   489 		state = PRE_VAL_WS_STATE;
   490 	    } else {
   491 		/* bogus char */
   492 		*errStr = PR_smprintf("line %d: Bogus character '%c' in tag.\n",
   493 		     					linenum, curchar);
   494 		state = ERR_STATE;
   495 		break;
   496 	    }
   497 	    break;
   498 	case PRE_VAL_WS_STATE:
   499 	    if (curchar == '>') {
   500 		/* premature end-of-tag (sounds like a personal problem). */
   501 		*errStr = PR_smprintf(
   502 		    "line %d: End of tag while waiting for value.\n",
   503 		     linenum);
   504 		state = ERR_STATE;
   505 		break;
   506 	    } else if (isspace(curchar)) {
   507 		/* more whitespace, do nothing */
   508 		break;
   509 	    } else {
   510 		/* this must be some sort of value. Fall through
   511 				 * to GET_VALUE_STATE */
   512 		startID = FB_GetPointer(fb) - 1;
   513 		state = GET_VALUE_STATE;
   514 	    }
   515 	    /* Fall through if we didn't break on '>' or whitespace */
   516 	case GET_VALUE_STATE:
   517 	    if (isspace(curchar) || curchar == '>') {
   518 		/* end of value */
   519 		curPos = FB_GetPointer(fb) - 2;
   520 		if (curPos >= startID) {
   521 		    /* Grab the value */
   522 		    FB_GetRange(fb, startID, curPos,
   523 		        &curPair->value);
   524 		    curPair->valueLine = linenum;
   525 		} else {
   526 		    /* empty value, leave as NULL */
   527 		}
   528 		if (isspace(curchar)) {
   529 		    state = PRE_ATT_WS_STATE;
   530 		} else {
   531 		    state = DONE_STATE;
   532 		}
   533 	    } else if (curchar == '\"' || curchar == '\'') {
   534 		/* quoted value.  Start recording the value inside the quote*/
   535 		startID = FB_GetPointer(fb);
   536 		state = GET_QUOTED_VAL_STATE;
   537 		PORT_Assert(quotechar == '\0');
   538 		quotechar = curchar; /* look for matching quote type */
   539 	    } else {
   540 		/* just more value */
   541 	    }
   542 	    break;
   543 	case GET_QUOTED_VAL_STATE:
   544 	    PORT_Assert(quotechar != '\0');
   545 	    if (curchar == quotechar) {
   546 		/* end of quoted value */
   547 		curPos = FB_GetPointer(fb) - 2;
   548 		if (curPos >= startID) {
   549 		    /* Grab the value */
   550 		    FB_GetRange(fb, startID, curPos,
   551 		        &curPair->value);
   552 		    curPair->valueLine = linenum;
   553 		} else {
   554 		    /* empty value, leave it as NULL */
   555 		}
   556 		state = GET_ATT_STATE;
   557 		quotechar = '\0';
   558 		startID = FB_GetPointer(fb);
   559 	    } else {
   560 		/* more quoted value, continue */
   561 	    }
   562 	    break;
   563 	case DONE_STATE:
   564 	case ERR_STATE:
   565 	default:
   566 	    ; /* should never get here */
   567 	}
   568     }
   570     if (state == DONE_STATE) {
   571 	/* Get the text of the tag */
   572 	curPos = FB_GetPointer(fb) - 1;
   573 	FB_GetRange(fb, startText, curPos, &ti->text);
   575 	/* Return the tag */
   576 	return ti;
   577     }
   579     /* Uh oh, an error.  Kill the tag item*/
   580     DestroyTagItem(ti);
   581     return NULL;
   582 }
   585 /************************************************************************
   586  *
   587  * D e s t r o y H T M L I t e m
   588  */
   589 static void	
   590 DestroyHTMLItem(HTMLItem *item)
   591 {
   592     if (item->type == TAG_ITEM) {
   593 	DestroyTagItem(item->item.tag);
   594     } else {
   595 	if (item->item.text) {
   596 	    PR_Free(item->item.text);
   597 	}
   598     }
   599 }
   602 /************************************************************************
   603  *
   604  * D e s t r o y T a g I t e m
   605  */
   606 static void	
   607 DestroyTagItem(TagItem*ti)
   608 {
   609     AVPair * temp;
   611     if (ti->text) {
   612 	PR_Free(ti->text); 
   613 	ti->text = NULL;
   614     }
   616     while (ti->attList) {
   617 	temp = ti->attList;
   618 	ti->attList = ti->attList->next;
   620 	if (temp->attribute) {
   621 	    PR_Free(temp->attribute); 
   622 	    temp->attribute = NULL;
   623 	}
   624 	if (temp->value) {
   625 	    PR_Free(temp->value); 
   626 	    temp->value = NULL;
   627 	}
   628 	PR_Free(temp);
   629     }
   631     PR_Free(ti);
   632 }
   635 /************************************************************************
   636  *
   637  * G e t T a g T y p e
   638  */
   639 static TAG_TYPE
   640 GetTagType(char *att)
   641 {
   642     if (!PORT_Strcasecmp(att, "APPLET")) {
   643 	return APPLET_TAG;
   644     }
   645     if (!PORT_Strcasecmp(att, "SCRIPT")) {
   646 	return SCRIPT_TAG;
   647     }
   648     if (!PORT_Strcasecmp(att, "LINK")) {
   649 	return LINK_TAG;
   650     }
   651     if (!PORT_Strcasecmp(att, "STYLE")) {
   652 	return STYLE_TAG;
   653     }
   654     return OTHER_TAG;
   655 }
   658 /************************************************************************
   659  *
   660  * F B _ C r e a t e
   661  */
   662 static FileBuffer*
   663 FB_Create(PRFileDesc*fd)
   664 {
   665     FileBuffer * fb;
   666     PRInt32 amountRead;
   667     PRInt32 storedOffset;
   669     fb = (FileBuffer * ) PR_Malloc(sizeof(FileBuffer));
   670     fb->fd = fd;
   671     storedOffset = PR_Seek(fd, 0, PR_SEEK_CUR);
   672     PR_Seek(fd, 0, PR_SEEK_SET);
   673     fb->startOffset = 0;
   674     amountRead = PR_Read(fd, fb->buf, FILE_BUFFER_BUFSIZE);
   675     if (amountRead == -1) 
   676 	goto loser;
   677     fb->maxIndex = amountRead - 1;
   678     fb->curIndex = 0;
   679     fb->IsEOF = (fb->curIndex > fb->maxIndex) ? PR_TRUE : PR_FALSE;
   680     fb->lineNum = 1;
   682     PR_Seek(fd, storedOffset, PR_SEEK_SET);
   683     return fb;
   684 loser:
   685     PR_Seek(fd, storedOffset, PR_SEEK_SET);
   686     PR_Free(fb);
   687     return NULL;
   688 }
   691 /************************************************************************
   692  *
   693  * F B _ G e t C h a r
   694  */
   695 static int	
   696 FB_GetChar(FileBuffer *fb)
   697 {
   698     PRInt32 storedOffset;
   699     PRInt32 amountRead;
   700     int	retval = -1;
   702     if (fb->IsEOF) {
   703 	return EOF;
   704     }
   706     storedOffset = PR_Seek(fb->fd, 0, PR_SEEK_CUR);
   708     retval = (unsigned char) fb->buf[fb->curIndex++];
   709     if (retval == '\n') 
   710 	fb->lineNum++;
   712     if (fb->curIndex > fb->maxIndex) {
   713 	/* We're at the end of the buffer. Try to get some new data from the
   714 		 * file */
   715 	fb->startOffset += fb->maxIndex + 1;
   716 	PR_Seek(fb->fd, fb->startOffset, PR_SEEK_SET);
   717 	amountRead = PR_Read(fb->fd, fb->buf, FILE_BUFFER_BUFSIZE);
   718 	if (amountRead == -1)  
   719 	    goto loser;
   720 	fb->maxIndex = amountRead - 1;
   721 	fb->curIndex = 0;
   722     }
   724     fb->IsEOF = (fb->curIndex > fb->maxIndex) ? PR_TRUE : PR_FALSE;
   726 loser:
   727     PR_Seek(fb->fd, storedOffset, PR_SEEK_SET);
   728     return retval;
   729 }
   732 /************************************************************************
   733  *
   734  * F B _ G e t L i n e N u m
   735  *
   736  */
   737 static unsigned int	
   738 FB_GetLineNum(FileBuffer *fb)
   739 {
   740     return fb->lineNum;
   741 }
   744 /************************************************************************
   745  *
   746  * F B _ G e t P o i n t e r
   747  *
   748  */
   749 static PRInt32
   750 FB_GetPointer(FileBuffer *fb)
   751 {
   752     return fb->startOffset + fb->curIndex;
   753 }
   756 /************************************************************************
   757  *
   758  * F B _ G e t R a n g e
   759  *
   760  */
   761 static PRInt32
   762 FB_GetRange(FileBuffer *fb, PRInt32 start, PRInt32 end, char **buf)
   763 {
   764     PRInt32 amountRead;
   765     PRInt32 storedOffset;
   767     *buf = PR_Malloc(end - start + 2);
   768     if (*buf == NULL) {
   769 	return 0;
   770     }
   772     storedOffset = PR_Seek(fb->fd, 0, PR_SEEK_CUR);
   773     PR_Seek(fb->fd, start, PR_SEEK_SET);
   774     amountRead = PR_Read(fb->fd, *buf, end - start + 1);
   775     PR_Seek(fb->fd, storedOffset, PR_SEEK_SET);
   776     if (amountRead == -1) {
   777 	PR_Free(*buf);
   778 	*buf = NULL;
   779 	return 0;
   780     }
   782     (*buf)[end-start+1] = '\0';
   783     return amountRead;
   784 }
   787 /************************************************************************
   788  *
   789  * F B _ D e s t r o y
   790  *
   791  */
   792 static void	
   793 FB_Destroy(FileBuffer *fb)
   794 {
   795     if (fb) {
   796 	PR_Free(fb);
   797     }
   798 }
   801 /************************************************************************
   802  *
   803  * P r i n t T a g I t e m
   804  *
   805  */
   806 static void	
   807 PrintTagItem(PRFileDesc *fd, TagItem *ti)
   808 {
   809     AVPair * pair;
   811     PR_fprintf(fd, "TAG:\n----\nType: ");
   812     switch (ti->type) {
   813     case APPLET_TAG:
   814 	PR_fprintf(fd, "applet\n");
   815 	break;
   816     case SCRIPT_TAG:
   817 	PR_fprintf(fd, "script\n");
   818 	break;
   819     case LINK_TAG:
   820 	PR_fprintf(fd, "link\n");
   821 	break;
   822     case STYLE_TAG:
   823 	PR_fprintf(fd, "style\n");
   824 	break;
   825     case COMMENT_TAG:
   826 	PR_fprintf(fd, "comment\n");
   827 	break;
   828     case OTHER_TAG:
   829     default:
   830 	PR_fprintf(fd, "other\n");
   831 	break;
   832     }
   834     PR_fprintf(fd, "Attributes:\n");
   835     for (pair = ti->attList; pair; pair = pair->next) {
   836 	PR_fprintf(fd, "\t%s=%s\n", pair->attribute,
   837 	    pair->value ? pair->value : "");
   838     }
   839     PR_fprintf(fd, "Text:%s\n", ti->text ? ti->text : "");
   841     PR_fprintf(fd, "---End of tag---\n");
   842 }
   845 /************************************************************************
   846  *
   847  * P r i n t H T M L S t r e a m
   848  *
   849  */
   850 static void	
   851 PrintHTMLStream(PRFileDesc *fd, HTMLItem *head)
   852 {
   853     while (head) {
   854 	if (head->type == TAG_ITEM) {
   855 	    PrintTagItem(fd, head->item.tag);
   856 	} else {
   857 	    PR_fprintf(fd, "\nTEXT:\n-----\n%s\n-----\n\n", head->item.text);
   858 	}
   859 	head = head->next;
   860     }
   861 }
   864 /************************************************************************
   865  *
   866  * S a v e I n l i n e S c r i p t
   867  *
   868  */
   869 static int	
   870 SaveInlineScript(char *text, char *id, char *basedir, char *archiveDir)
   871 {
   872     char	*filename = NULL;
   873     PRFileDesc * fd = NULL;
   874     int	retval = -1;
   875     PRInt32 writeLen;
   876     char	*ilDir = NULL;
   878     if (!text || !id || !archiveDir) {
   879 	return - 1;
   880     }
   882     if (dumpParse) {
   883 	PR_fprintf(outputFD, "SaveInlineScript: text=%s, id=%s, \n"
   884 	    "basedir=%s, archiveDir=%s\n",
   885 	    text, id, basedir, archiveDir);
   886     }
   888     /* Make sure the archive directory is around */
   889     if (ensureExists(basedir, archiveDir) != PR_SUCCESS) {
   890 	PR_fprintf(errorFD,
   891 	    "ERROR: Unable to create archive directory %s.\n", archiveDir);
   892 	errorCount++;
   893 	return - 1;
   894     }
   896     /* Make sure the inline script directory is around */
   897     ilDir = PR_smprintf("%s/inlineScripts", archiveDir);
   898     scriptdir = "inlineScripts";
   899     if (ensureExists(basedir, ilDir) != PR_SUCCESS) {
   900 	PR_fprintf(errorFD,
   901 	    "ERROR: Unable to create directory %s.\n", ilDir);
   902 	errorCount++;
   903 	return - 1;
   904     }
   906     filename = PR_smprintf("%s/%s/%s", basedir, ilDir, id);
   908     /* If the file already exists, give a warning, then blow it away */
   909     if (PR_Access(filename, PR_ACCESS_EXISTS) == PR_SUCCESS) {
   910 	PR_fprintf(errorFD,
   911 	    "warning: file \"%s\" already exists--will overwrite.\n",
   912 	     			filename);
   913 	warningCount++;
   914 	if (rm_dash_r(filename)) {
   915 	    PR_fprintf(errorFD, "ERROR: Unable to delete %s.\n", filename);
   916 	    errorCount++;
   917 	    goto finish;
   918 	}
   919     }
   921     /* Write text into file with name id */
   922     fd = PR_Open(filename, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0777);
   923     if (!fd) {
   924 	PR_fprintf(errorFD, "ERROR: Unable to create file \"%s\".\n",
   925 	     			filename);
   926 	errorCount++;
   927 	goto finish;
   928     }
   929     writeLen = strlen(text);
   930     if ( PR_Write(fd, text, writeLen) != writeLen) {
   931 	PR_fprintf(errorFD, "ERROR: Unable to write to file \"%s\".\n",
   932 	     			filename);
   933 	errorCount++;
   934 	goto finish;
   935     }
   937     retval = 0;
   938 finish:
   939     if (filename) {
   940 	PR_smprintf_free(filename);
   941     }
   942     if (ilDir) {
   943 	PR_smprintf_free(ilDir);
   944     }
   945     if (fd) {
   946 	PR_Close(fd);
   947     }
   948     return retval;
   949 }
   952 /************************************************************************
   953  *
   954  * S a v e U n n a m a b l e S c r i p t
   955  *
   956  */
   957 static int	
   958 SaveUnnamableScript(char *text, char *basedir, char *archiveDir,
   959 char *HTMLfilename)
   960 {
   961     char	*id = NULL;
   962     char	*ext = NULL;
   963     char	*start = NULL;
   964     int	retval = -1;
   966     if (!text || !archiveDir || !HTMLfilename) {
   967 	return - 1;
   968     }
   970     if (dumpParse) {
   971 	PR_fprintf(outputFD, "SaveUnnamableScript: text=%s, basedir=%s,\n"
   972 	    "archiveDir=%s, filename=%s\n", text, basedir, archiveDir,
   973 	     			HTMLfilename);
   974     }
   976     /* Construct the filename */
   977     ext = PL_strrchr(HTMLfilename, '.');
   978     if (ext) {
   979 	*ext = '\0';
   980     }
   981     for (start = HTMLfilename; strpbrk(start, "/\\"); 
   982          start = strpbrk(start, "/\\") + 1)
   983 	/* do nothing */;
   984     if (*start == '\0') 
   985 	start = HTMLfilename;
   986     id = PR_smprintf("_%s%d", start, idOrdinal++);
   987     if (ext) {
   988 	*ext = '.';
   989     }
   991     /* Now call SaveInlineScript to do the work */
   992     retval = SaveInlineScript(text, id, basedir, archiveDir);
   994     PR_Free(id);
   996     return retval;
   997 }
  1000 /************************************************************************
  1002  * S a v e S o u r c e
  1004  */
  1005 static int	
  1006 SaveSource(char *src, char *codebase, char *basedir, char *archiveDir)
  1008     char	*from = NULL, *to = NULL;
  1009     int	retval = -1;
  1010     char	*arcDir = NULL;
  1012     if (!src || !archiveDir) {
  1013 	return - 1;
  1016     if (dumpParse) {
  1017 	PR_fprintf(outputFD, "SaveSource: src=%s, codebase=%s, basedir=%s,\n"
  1018 	    "archiveDir=%s\n", src, codebase, basedir, archiveDir);
  1021     if (codebase) {
  1022 	arcDir = PR_smprintf("%s/%s/%s/", basedir, codebase, archiveDir);
  1023     } else {
  1024 	arcDir = PR_smprintf("%s/%s/", basedir, archiveDir);
  1027     if (codebase) {
  1028 	from = PR_smprintf("%s/%s/%s", basedir, codebase, src);
  1029 	to = PR_smprintf("%s%s", arcDir, src);
  1030     } else {
  1031 	from = PR_smprintf("%s/%s", basedir, src);
  1032 	to = PR_smprintf("%s%s", arcDir, src);
  1035     if (make_dirs(to, 0777)) {
  1036 	PR_fprintf(errorFD,
  1037 	    "ERROR: Unable to create archive directory %s.\n", archiveDir);
  1038 	errorCount++;
  1039 	goto finish;
  1042     retval = copyinto(from, to);
  1043 finish:
  1044     if (from) 
  1045 	PR_Free(from);
  1046     if (to) 
  1047 	PR_Free(to);
  1048     if (arcDir) 
  1049 	PR_Free(arcDir);
  1050     return retval;
  1054 /************************************************************************
  1056  * T a g T y p e T o S t r i n g
  1058  */
  1059 char	*
  1060 TagTypeToString(TAG_TYPE type)
  1062     switch (type) {
  1063     case APPLET_TAG:
  1064 	return "APPLET";
  1065     case SCRIPT_TAG:
  1066 	return "SCRIPT";
  1067     case LINK_TAG:
  1068 	return "LINK";
  1069     case STYLE_TAG:
  1070 	return "STYLE";
  1071     default:
  1072 	break;
  1074     return "unknown";
  1078 /************************************************************************
  1080  * e x t r a c t _ j s
  1082  */
  1083 static int	
  1084 extract_js(char *filename)
  1086     PRFileDesc * fd = NULL;
  1087     FileBuffer * fb = NULL;
  1088     HTMLItem * head = NULL;
  1089     HTMLItem * tail = NULL;
  1090     HTMLItem * curitem = NULL;
  1091     HTMLItem * styleList	= NULL;
  1092     HTMLItem * styleListTail	= NULL;
  1093     HTMLItem * entityList	= NULL;
  1094     HTMLItem * entityListTail	= NULL;
  1095     TagItem * tagp = NULL;
  1096     char	*text = NULL;
  1097     char	*tagerr = NULL;
  1098     char	*archiveDir = NULL;
  1099     char	*firstArchiveDir = NULL;
  1100     char	*basedir = NULL;
  1101     PRInt32    textStart;
  1102     PRInt32    curOffset;
  1103     HTML_STATE state;
  1104     int	       curchar;
  1105     int	       retval = -1;
  1106     unsigned int linenum, startLine;
  1108     /* Initialize the implicit ID counter for each file */
  1109     idOrdinal = 0;
  1111     /*
  1112      * First, parse the HTML into a stream of tags and text.
  1113      */
  1115     fd = PR_Open(filename, PR_RDONLY, 0);
  1116     if (!fd) {
  1117 	PR_fprintf(errorFD, "Unable to open %s for reading.\n", filename);
  1118 	errorCount++;
  1119 	return - 1;
  1122     /* Construct base directory of filename. */
  1124 	char	*cp;
  1126 	basedir = PL_strdup(filename);
  1128 	/* Remove trailing slashes */
  1129 	while ( (cp = PL_strprbrk(basedir, "/\\")) == 
  1130 	    (basedir + strlen(basedir) - 1)) {
  1131 	    *cp = '\0';
  1134 	/* Now remove everything from the last slash (which will be followed
  1135 	 * by a filename) to the end */
  1136 	cp = PL_strprbrk(basedir, "/\\");
  1137 	if (cp) {
  1138 	    *cp = '\0';
  1142     state = TEXT_HTML_STATE;
  1144     fb = FB_Create(fd);
  1146     textStart = 0;
  1147     startLine = 0;
  1148     while (linenum = FB_GetLineNum(fb), (curchar = FB_GetChar(fb)) !=
  1149         EOF) {
  1150 	switch (state) {
  1151 	case TEXT_HTML_STATE:
  1152 	    if (curchar == '<') {
  1153 		/*
  1154 		 * Found a tag
  1155 		 */
  1156 		/* Save the text so far to a new text item */
  1157 		curOffset = FB_GetPointer(fb) - 2;
  1158 		if (curOffset >= textStart) {
  1159 		    if (FB_GetRange(fb, textStart, curOffset,
  1160 		         &text) != 
  1161 		        curOffset - textStart + 1)  {
  1162 			PR_fprintf(errorFD,
  1163 			    "Unable to read from %s.\n",
  1164 			     filename);
  1165 			errorCount++;
  1166 			goto loser;
  1168 		    /* little fudge here.  If the first character on a line
  1169 		     * is '<', meaning a new tag, the preceding text item
  1170 		     * actually ends on the previous line.  In this case
  1171 		     * we will be saying that the text segment ends on the
  1172 		     * next line. I don't think this matters for text items. */
  1173 		    curitem = CreateTextItem(text, startLine,
  1174 		         linenum);
  1175 		    text = NULL;
  1176 		    if (tail == NULL) {
  1177 			head = tail = curitem;
  1178 		    } else {
  1179 			tail->next = curitem;
  1180 			tail = curitem;
  1184 		/* Process the tag */
  1185 		tagp = ProcessTag(fb, &tagerr);
  1186 		if (!tagp) {
  1187 		    if (tagerr) {
  1188 			PR_fprintf(errorFD, "Error in file %s: %s\n",
  1189 						  filename, tagerr);
  1190 			errorCount++;
  1191 		    } else {
  1192 			PR_fprintf(errorFD,
  1193 			    "Error in file %s, in tag starting at line %d\n",
  1194 						  filename, linenum);
  1195 			errorCount++;
  1197 		    goto loser;
  1199 		/* Add the tag to the list */
  1200 		curitem = CreateTagItem(tagp, linenum, FB_GetLineNum(fb));
  1201 		if (tail == NULL) {
  1202 		    head = tail = curitem;
  1203 		} else {
  1204 		    tail->next = curitem;
  1205 		    tail = curitem;
  1208 		/* What's the next state */
  1209 		if (tagp->type == SCRIPT_TAG) {
  1210 		    state = SCRIPT_HTML_STATE;
  1213 		/* Start recording text from the new offset */
  1214 		textStart = FB_GetPointer(fb);
  1215 		startLine = FB_GetLineNum(fb);
  1216 	    } else {
  1217 		/* regular character.  Next! */
  1219 	    break;
  1220 	case SCRIPT_HTML_STATE:
  1221 	    if (curchar == '<') {
  1222 		char	*cp;
  1223 		/*
  1224 		 * If this is a </script> tag, then we're at the end of the
  1225 		 * script.  Otherwise, ignore
  1226 		 */
  1227 		curOffset = FB_GetPointer(fb) - 1;
  1228 		cp = NULL;
  1229 		if (FB_GetRange(fb, curOffset, curOffset + 8, &cp) != 9) {
  1230 		    if (cp) { 
  1231 			PR_Free(cp); 
  1232 			cp = NULL; 
  1234 		} else {
  1235 		    /* compare the strings */
  1236 		    if ( !PORT_Strncasecmp(cp, "</script>", 9) ) {
  1237 			/* This is the end of the script. Record the text. */
  1238 			curOffset--;
  1239 			if (curOffset >= textStart) {
  1240 			    if (FB_GetRange(fb, textStart, curOffset, &text) != 
  1241 			        curOffset - textStart + 1) {
  1242 				PR_fprintf(errorFD, "Unable to read from %s.\n",
  1243 				     filename);
  1244 				errorCount++;
  1245 				goto loser;
  1247 			    curitem = CreateTextItem(text, startLine, linenum);
  1248 			    text = NULL;
  1249 			    if (tail == NULL) {
  1250 				head = tail = curitem;
  1251 			    } else {
  1252 				tail->next = curitem;
  1253 				tail = curitem;
  1257 			/* Now parse the /script tag and put it on the list */
  1258 			tagp = ProcessTag(fb, &tagerr);
  1259 			if (!tagp) {
  1260 			    if (tagerr) {
  1261 				PR_fprintf(errorFD, "Error in file %s: %s\n",
  1262 				     filename, tagerr);
  1263 			    } else {
  1264 				PR_fprintf(errorFD, 
  1265 				    "Error in file %s, in tag starting at"
  1266 				    " line %d\n", filename, linenum);
  1268 			    errorCount++;
  1269 			    goto loser;
  1271 			curitem = CreateTagItem(tagp, linenum,
  1272 						FB_GetLineNum(fb));
  1273 			if (tail == NULL) {
  1274 			    head = tail = curitem;
  1275 			} else {
  1276 			    tail->next = curitem;
  1277 			    tail = curitem;
  1280 			/* go back to text state */
  1281 			state = TEXT_HTML_STATE;
  1283 			textStart = FB_GetPointer(fb);
  1284 			startLine = FB_GetLineNum(fb);
  1288 	    break;
  1292     /* End of the file.  Wrap up any remaining text */
  1293     if (state == SCRIPT_HTML_STATE) {
  1294 	if (tail && tail->type == TAG_ITEM) {
  1295 	    PR_fprintf(errorFD, "ERROR: <SCRIPT> tag at %s:%d is not followed "
  1296 	        "by a </SCRIPT> tag.\n", filename, tail->startLine);
  1297 	} else {
  1298 	    PR_fprintf(errorFD, "ERROR: <SCRIPT> tag in file %s is not followed"
  1299 	        " by a </SCRIPT tag.\n", filename);
  1301 	errorCount++;
  1302 	goto loser;
  1304     curOffset = FB_GetPointer(fb) - 1;
  1305     if (curOffset >= textStart) {
  1306 	text = NULL;
  1307 	if ( FB_GetRange(fb, textStart, curOffset, &text) != 
  1308 	    curOffset - textStart + 1) {
  1309 	    PR_fprintf(errorFD, "Unable to read from %s.\n", filename);
  1310 	    errorCount++;
  1311 	    goto loser;
  1313 	curitem = CreateTextItem(text, startLine, linenum);
  1314 	text = NULL;
  1315 	if (tail == NULL) {
  1316 	    head = tail = curitem;
  1317 	} else {
  1318 	    tail->next = curitem;
  1319 	    tail = curitem;
  1323     if (dumpParse) {
  1324 	PrintHTMLStream(outputFD, head);
  1327     /*
  1328      * Now we have a stream of tags and text.  Go through and deal with each.
  1329      */
  1330     for (curitem = head; curitem; curitem = curitem->next) {
  1331 	TagItem * tagp = NULL;
  1332 	AVPair * pairp = NULL;
  1333 	char	*src = NULL, *id = NULL, *codebase = NULL;
  1334 	PRBool hasEventHandler = PR_FALSE;
  1335 	int	i;
  1337 	/* Reset archive directory for each tag */
  1338 	if (archiveDir) {
  1339 	    PR_Free(archiveDir); 
  1340 	    archiveDir = NULL;
  1343 	/* We only analyze tags */
  1344 	if (curitem->type != TAG_ITEM) {
  1345 	    continue;
  1348 	tagp = curitem->item.tag;
  1350 	/* go through the attributes to get information */
  1351 	for (pairp = tagp->attList; pairp; pairp = pairp->next) {
  1353 	    /* ARCHIVE= */
  1354 	    if ( !PL_strcasecmp(pairp->attribute, "archive")) {
  1355 		if (archiveDir) {
  1356 		    /* Duplicate attribute.  Print warning */
  1357 		    PR_fprintf(errorFD,
  1358 		        "warning: \"%s\" attribute overwrites previous attribute"
  1359 		        " in tag starting at %s:%d.\n",
  1360 		        pairp->attribute, filename, curitem->startLine);
  1361 		    warningCount++;
  1362 		    PR_Free(archiveDir);
  1364 		archiveDir = PL_strdup(pairp->value);
  1366 		/* Substiture ".arc" for ".jar" */
  1367 		if ( (PL_strlen(archiveDir) < 4) || 
  1368 		    PL_strcasecmp((archiveDir + strlen(archiveDir) -4), 
  1369 			".jar")) {
  1370 		    PR_fprintf(errorFD,
  1371 		        "warning: ARCHIVE attribute should end in \".jar\" in tag"
  1372 		        " starting on %s:%d.\n", filename, curitem->startLine);
  1373 		    warningCount++;
  1374 		    PR_Free(archiveDir);
  1375 		    archiveDir = PR_smprintf("%s.arc", archiveDir);
  1376 		} else {
  1377 		    PL_strcpy(archiveDir + strlen(archiveDir) -4, ".arc");
  1380 		/* Record the first archive.  This will be used later if
  1381 		 * the archive is not specified */
  1382 		if (firstArchiveDir == NULL) {
  1383 		    firstArchiveDir = PL_strdup(archiveDir);
  1386 	    /* CODEBASE= */
  1387 	    else if ( !PL_strcasecmp(pairp->attribute, "codebase")) {
  1388 		if (codebase) {
  1389 		    /* Duplicate attribute.  Print warning */
  1390 		    PR_fprintf(errorFD,
  1391 		        "warning: \"%s\" attribute overwrites previous attribute"
  1392 		        " in tag staring at %s:%d.\n",
  1393 		        pairp->attribute, filename, curitem->startLine);
  1394 		    warningCount++;
  1396 		codebase = pairp->value;
  1398 	    /* SRC= and HREF= */
  1399 	    else if ( !PORT_Strcasecmp(pairp->attribute, "src") ||
  1400 	        !PORT_Strcasecmp(pairp->attribute, "href") ) {
  1401 		if (src) {
  1402 		    /* Duplicate attribute.  Print warning */
  1403 		    PR_fprintf(errorFD,
  1404 		        "warning: \"%s\" attribute overwrites previous attribute"
  1405 		        " in tag staring at %s:%d.\n",
  1406 		        pairp->attribute, filename, curitem->startLine);
  1407 		    warningCount++;
  1409 		src = pairp->value;
  1411 	    /* CODE= */
  1412 	    else if (!PORT_Strcasecmp(pairp->attribute, "code") ) {
  1413 		/*!!!XXX Change PORT to PL all over this code !!! */
  1414 		if (src) {
  1415 		    /* Duplicate attribute.  Print warning */
  1416 		    PR_fprintf(errorFD,
  1417 		        "warning: \"%s\" attribute overwrites previous attribute"
  1418 		        " ,in tag staring at %s:%d.\n",
  1419 		        pairp->attribute, filename, curitem->startLine);
  1420 		    warningCount++;
  1422 		src = pairp->value;
  1424 		/* Append a .class if one is not already present */
  1425 		if ( (PL_strlen(src) < 6) || 
  1426 		    PL_strcasecmp( (src + PL_strlen(src) - 6), ".class") ) {
  1427 		    src = PR_smprintf("%s.class", src);
  1428 		    /* Put this string back into the data structure so it
  1429 		     * will be deallocated properly */
  1430 		    PR_Free(pairp->value);
  1431 		    pairp->value = src;
  1434 	    /* ID= */
  1435 	    else if (!PL_strcasecmp(pairp->attribute, "id") ) {
  1436 		if (id) {
  1437 		    /* Duplicate attribute.  Print warning */
  1438 		    PR_fprintf(errorFD,
  1439 		        "warning: \"%s\" attribute overwrites previous attribute"
  1440 		        " in tag staring at %s:%d.\n",
  1441 		        pairp->attribute, filename, curitem->startLine);
  1442 		    warningCount++;
  1444 		id = pairp->value;
  1447 	    /* STYLE= */
  1448 	    /* style= attributes, along with JS entities, are stored into
  1449 	     * files with dynamically generated names. The filenames are
  1450 	     * based on the order in which the text is found in the file.
  1451 	     * All JS entities on all lines up to and including the line
  1452 	     * containing the end of the tag that has this style= attribute
  1453 	     * will be processed before this style=attribute.  So we need
  1454 	     * to record the line that this _tag_ (not the attribute) ends on.
  1455 	     */
  1456 	    else if (!PL_strcasecmp(pairp->attribute, "style") && pairp->value) 
  1458 		HTMLItem * styleItem;
  1459 		/* Put this item on the style list */
  1460 		styleItem = CreateTextItem(PL_strdup(pairp->value),
  1461 		    curitem->startLine, curitem->endLine);
  1462 		if (styleListTail == NULL) {
  1463 		    styleList = styleListTail = styleItem;
  1464 		} else {
  1465 		    styleListTail->next = styleItem;
  1466 		    styleListTail = styleItem;
  1469 	    /* Event handlers */
  1470 	    else {
  1471 		for (i = 0; i < num_handlers; i++) {
  1472 		    if (!PL_strcasecmp(event_handlers[i], pairp->attribute)) {
  1473 			hasEventHandler = PR_TRUE;
  1474 			break;
  1480 	    /* JS Entity */
  1482 		char	*entityStart, *entityEnd;
  1483 		HTMLItem * entityItem;
  1485 		/* go through each JavaScript entity ( &{...}; ) and store it
  1486 		 * in the entityList.  The important thing is to record what
  1487 		 * line number it's on, so we can get it in the right order
  1488 		 * in relation to style= attributes.
  1489 		 * Apparently, these can't flow across lines, so the start and
  1490 		 * end line will be the same.  That helps matters.
  1491 		 */
  1492 		entityEnd = pairp->value;
  1493 		while ( entityEnd && 
  1494 		    (entityStart = PL_strstr(entityEnd, "&{")) /*}*/ != NULL) {
  1495 		    entityStart += 2; /* point at beginning of actual entity */
  1496 		    entityEnd = PL_strchr(entityStart, '}');
  1497 		    if (entityEnd) {
  1498 			/* Put this item on the entity list */
  1499 			*entityEnd = '\0';
  1500 			entityItem = CreateTextItem(PL_strdup(entityStart),
  1501 					    pairp->valueLine, pairp->valueLine);
  1502 			*entityEnd = /* { */ '}';
  1503 			if (entityListTail) {
  1504 			    entityListTail->next = entityItem;
  1505 			    entityListTail = entityItem;
  1506 			} else {
  1507 			    entityList = entityListTail = entityItem;
  1514 	/* If no archive was supplied, we use the first one of the file */
  1515 	if (!archiveDir && firstArchiveDir) {
  1516 	    archiveDir = PL_strdup(firstArchiveDir);
  1519 	/* If we have an event handler, we need to archive this tag */
  1520 	if (hasEventHandler) {
  1521 	    if (!id) {
  1522 		PR_fprintf(errorFD,
  1523 		    "warning: tag starting at %s:%d has event handler but"
  1524 		    " no ID attribute.  The tag will not be signed.\n",
  1525 					filename, curitem->startLine);
  1526 		warningCount++;
  1527 	    } else if (!archiveDir) {
  1528 		PR_fprintf(errorFD,
  1529 		    "warning: tag starting at %s:%d has event handler but"
  1530 		    " no ARCHIVE attribute.  The tag will not be signed.\n",
  1531 					    filename, curitem->startLine);
  1532 		warningCount++;
  1533 	    } else {
  1534 		if (SaveInlineScript(tagp->text, id, basedir, archiveDir)) {
  1535 		    goto loser;
  1540 	switch (tagp->type) {
  1541 	case APPLET_TAG:
  1542 	    if (!src) {
  1543 		PR_fprintf(errorFD,
  1544 		    "error: APPLET tag starting on %s:%d has no CODE "
  1545 		    "attribute.\n", filename, curitem->startLine);
  1546 		errorCount++;
  1547 		goto loser;
  1548 	    } else if (!archiveDir) {
  1549 		PR_fprintf(errorFD,
  1550 		    "error: APPLET tag starting on %s:%d has no ARCHIVE "
  1551 		    "attribute.\n", filename, curitem->startLine);
  1552 		errorCount++;
  1553 		goto loser;
  1554 	    } else {
  1555 		if (SaveSource(src, codebase, basedir, archiveDir)) {
  1556 		    goto loser;
  1559 	    break;
  1560 	case SCRIPT_TAG:
  1561 	case LINK_TAG:
  1562 	case STYLE_TAG:
  1563 	    if (!archiveDir) {
  1564 		PR_fprintf(errorFD,
  1565 		    "error: %s tag starting on %s:%d has no ARCHIVE "
  1566 		    "attribute.\n", TagTypeToString(tagp->type),
  1567 					    filename, curitem->startLine);
  1568 		errorCount++;
  1569 		goto loser;
  1570 	    } else if (src) {
  1571 		if (SaveSource(src, codebase, basedir, archiveDir)) {
  1572 		    goto loser;
  1574 	    } else if (id) {
  1575 		/* Save the next text item */
  1576 		if (!curitem->next || (curitem->next->type !=
  1577 		    TEXT_ITEM)) {
  1578 		    PR_fprintf(errorFD,
  1579 		        "warning: %s tag starting on %s:%d is not followed"
  1580 		        " by script text.\n", TagTypeToString(tagp->type),
  1581 					    filename, curitem->startLine);
  1582 		    warningCount++;
  1583 		    /* just create empty file */
  1584 		    if (SaveInlineScript("", id, basedir, archiveDir)) {
  1585 			goto loser;
  1587 		} else {
  1588 		    curitem = curitem->next;
  1589 		    if (SaveInlineScript(curitem->item.text,
  1590 		         id, basedir,
  1591 		        archiveDir)) {
  1592 			goto loser;
  1595 	    } else {
  1596 		/* No src or id tag--warning */
  1597 		PR_fprintf(errorFD,
  1598 		    "warning: %s tag starting on %s:%d has no SRC or"
  1599 		    " ID attributes.  Will not sign.\n",
  1600 		    TagTypeToString(tagp->type), filename, curitem->startLine);
  1601 		warningCount++;
  1603 	    break;
  1604 	default:
  1605 	    /* do nothing for other tags */
  1606 	    break;
  1611     /* Now deal with all the unnamable scripts */
  1612     if (firstArchiveDir) {
  1613 	HTMLItem * style, *entity;
  1615 	/* Go through the lists of JS entities and style attributes.  Do them
  1616 	 * in chronological order within a list.  Pick the list with the lower
  1617 	 * endLine. In case of a tie, entities come first.
  1618 	 */
  1619 	style = styleList; 
  1620 	entity = entityList;
  1621 	while (style || entity) {
  1622 	    if (!entity || (style && (style->endLine < entity->endLine))) {
  1623 		/* Process style */
  1624 		SaveUnnamableScript(style->item.text, basedir, firstArchiveDir,
  1625 				    filename);
  1626 		style = style->next;
  1627 	    } else {
  1628 		/* Process entity */
  1629 		SaveUnnamableScript(entity->item.text, basedir, firstArchiveDir,
  1630 				    filename);
  1631 		entity = entity->next;
  1637     retval = 0;
  1638 loser:
  1639     /* Blow away the stream */
  1640     while (head) {
  1641 	curitem = head;
  1642 	head = head->next;
  1643 	DestroyHTMLItem(curitem);
  1645     while (styleList) {
  1646 	curitem = styleList;
  1647 	styleList = styleList->next;
  1648 	DestroyHTMLItem(curitem);
  1650     while (entityList) {
  1651 	curitem = entityList;
  1652 	entityList = entityList->next;
  1653 	DestroyHTMLItem(curitem);
  1655     if (text) {
  1656 	PR_Free(text); 
  1657 	text = NULL;
  1659     if (fb) {
  1660 	FB_Destroy(fb); 
  1661 	fb = NULL;
  1663     if (fd) {
  1664 	PR_Close(fd);
  1666     if (tagerr) {
  1667 	PR_smprintf_free(tagerr); 
  1668 	tagerr = NULL;
  1670     if (archiveDir) {
  1671 	PR_Free(archiveDir); 
  1672 	archiveDir = NULL;
  1674     if (firstArchiveDir) {
  1675 	PR_Free(firstArchiveDir); 
  1676 	firstArchiveDir = NULL;
  1678     return retval;
  1682 /**********************************************************************
  1684  * e n s u r e E x i s t s
  1686  * Check for existence of indicated directory.  If it doesn't exist,
  1687  * it will be created.
  1688  * Returns PR_SUCCESS if the directory is present, PR_FAILURE otherwise.
  1689  */
  1690 static PRStatus
  1691 ensureExists (char *base, char *path)
  1693     char	fn [FNSIZE];
  1694     PRDir * dir;
  1695     sprintf (fn, "%s/%s", base, path);
  1697     /*PR_fprintf(outputFD, "Trying to open directory %s.\n", fn);*/
  1699     if ( (dir = PR_OpenDir(fn)) ) {
  1700 	PR_CloseDir(dir);
  1701 	return PR_SUCCESS;
  1703     return PR_MkDir(fn, 0777);
  1707 /***************************************************************************
  1709  * m a k e _ d i r s
  1711  * Ensure that the directory portion of the path exists.  This may require
  1712  * making the directory, and its parent, and its parent's parent, etc.
  1713  */
  1714 static int	
  1715 make_dirs(char *path, int file_perms)
  1717     char	*Path;
  1718     char	*start;
  1719     char	*sep;
  1720     int	ret = 0;
  1721     PRFileInfo info;
  1723     if (!path) {
  1724 	return 0;
  1727     Path = PL_strdup(path);
  1728     start = strpbrk(Path, "/\\");
  1729     if (!start) {
  1730 	return 0;
  1732     start++; /* start right after first slash */
  1734     /* Each time through the loop add one more directory. */
  1735     while ( (sep = strpbrk(start, "/\\")) ) {
  1736 	*sep = '\0';
  1738 	if ( PR_GetFileInfo(Path, &info) != PR_SUCCESS) {
  1739 	    /* No such dir, we have to create it */
  1740 	    if ( PR_MkDir(Path, file_perms) != PR_SUCCESS) {
  1741 		PR_fprintf(errorFD, "ERROR: Unable to create directory %s.\n",
  1742 		     					Path);
  1743 		errorCount++;
  1744 		ret = -1;
  1745 		goto loser;
  1747 	} else {
  1748 	    /* something exists by this name, make sure it's a directory */
  1749 	    if ( info.type != PR_FILE_DIRECTORY ) {
  1750 		PR_fprintf(errorFD, "ERROR: Unable to create directory %s.\n",
  1751 		     					Path);
  1752 		errorCount++;
  1753 		ret = -1;
  1754 		goto loser;
  1758 	start = sep + 1; /* start after the next slash */
  1759 	*sep = '/';
  1762 loser:
  1763     PR_Free(Path);
  1764     return ret;
  1768 /*
  1769  *  c o p y i n t o
  1771  *  Function to copy file "from" to path "to".
  1773  */
  1774 static int	
  1775 copyinto (char *from, char *to)
  1777     PRInt32 num;
  1778     char	buf [BUFSIZ];
  1779     PRFileDesc * infp = NULL, *outfp = NULL;
  1780     int	retval = -1;
  1782     if ((infp = PR_Open(from, PR_RDONLY, 0777)) == NULL) {
  1783 	PR_fprintf(errorFD, "ERROR: Unable to open \"%s\" for reading.\n",
  1784 	     			from);
  1785 	errorCount++;
  1786 	goto finish;
  1789     /* If to already exists, print a warning before deleting it */
  1790     if (PR_Access(to, PR_ACCESS_EXISTS) == PR_SUCCESS) {
  1791 	PR_fprintf(errorFD, "warning: %s already exists--will overwrite\n", to);
  1792 	warningCount++;
  1793 	if (rm_dash_r(to)) {
  1794 	    PR_fprintf(errorFD,
  1795 	        "ERROR: Unable to remove %s.\n", to);
  1796 	    errorCount++;
  1797 	    goto finish;
  1801     if ((outfp = PR_Open(to, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0777))
  1802          == NULL) {
  1803 	char	*errBuf = NULL;
  1805 	errBuf = PR_Malloc(PR_GetErrorTextLength() + 1);
  1806 	PR_fprintf(errorFD, "ERROR: Unable to open \"%s\" for writing.\n", to);
  1807 	if (PR_GetErrorText(errBuf)) {
  1808 	    PR_fprintf(errorFD, "Cause: %s\n", errBuf);
  1810 	if (errBuf) {
  1811 	    PR_Free(errBuf);
  1813 	errorCount++;
  1814 	goto finish;
  1817     while ( (num = PR_Read(infp, buf, BUFSIZ)) > 0) {
  1818 	if (PR_Write(outfp, buf, num) != num) {
  1819 	    PR_fprintf(errorFD, "ERROR: Error writing to %s.\n", to);
  1820 	    errorCount++;
  1821 	    goto finish;
  1825     retval = 0;
  1826 finish:
  1827     if (infp) 
  1828 	PR_Close(infp);
  1829     if (outfp) 
  1830 	PR_Close(outfp);
  1832     return retval;

mercurial