1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
14 * The Original Code is the Netscape security libraries.
16 * The Initial Developer of the Original Code is
17 * Netscape Communications Corporation.
18 * Portions created by the Initial Developer are Copyright (C) 1994-2000
19 * the Initial Developer. All Rights Reserved.
23 * Alternatively, the contents of this file may be used under the terms of
24 * either the GNU General Public License Version 2 or later (the "GPL"), or
25 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 * in which case the provisions of the GPL or the LGPL are applicable instead
27 * of those above. If you wish to allow use of your version of this file only
28 * under the terms of either the GPL or the LGPL, and not to allow others to
29 * use your version of this file under the terms of the MPL, indicate your
30 * decision by deleting the provisions above and replace them with the notice
31 * and other provisions required by the GPL or the LGPL. If you do not delete
32 * the provisions above, a recipient may use your version of this file under
33 * the terms of any one of the MPL, the GPL or the LGPL.
35 * ***** END LICENSE BLOCK ***** */
42 static int javascript_fn(char *relpath
, char *basedir
, char *reldir
,
43 char *filename
, void *arg
);
44 static int extract_js (char *filename
);
45 static int copyinto (char *from
, char *to
);
46 static PRStatus
ensureExists (char *base
, char *path
);
47 static int make_dirs(char *path
, PRInt32 file_perms
);
49 static char *jartree
= NULL
;
51 static PRBool dumpParse
= PR_FALSE
;
53 static char *event_handlers
[] = {
80 static int num_handlers
= 23;
83 * I n l i n e J a v a S c r i p t
85 * Javascript signing. Instead of passing an archive to signtool,
86 * a directory containing html files is given. Archives are created
87 * from the archive= and src= tag attributes inside the html,
88 * as appropriate. Then the archives are signed.
92 InlineJavaScript(char *dir
, PRBool recurse
)
96 PR_fprintf(outputFD
, "\nGenerating inline signatures from HTML files in: %s\n",
99 if (PR_GetEnv("SIGNTOOL_DUMP_PARSE")) {
103 return foreach(dir
, "", javascript_fn
, recurse
, PR_FALSE
/*include dirs*/,
109 /************************************************************************
111 * j a v a s c r i p t _ f n
113 static int javascript_fn
114 (char *relpath
, char *basedir
, char *reldir
, char *filename
, void *arg
)
116 char fullname
[FNSIZE
];
118 /* only process inline scripts from .htm, .html, and .shtml*/
120 if (!(PL_strcaserstr(filename
, ".htm") == filename
+ strlen(filename
) -
122 !(PL_strcaserstr(filename
, ".html") == filename
+ strlen(filename
) -
124 !(PL_strcaserstr(filename
, ".shtml") == filename
+ strlen(filename
)
129 /* don't process scripts that signtool has already
130 extracted (those that are inside .arc directories) */
132 if (PL_strcaserstr(filename
, ".arc") == filename
+ strlen(filename
) - 4)
135 if (verbosity
>= 0) {
136 PR_fprintf(outputFD
, "Processing HTML file: %s\n", relpath
);
139 /* reset firstArchive at top of each HTML file */
141 /* skip directories that contain extracted scripts */
143 if (PL_strcaserstr(reldir
, ".arc") == reldir
+ strlen(reldir
) - 4)
146 sprintf (fullname
, "%s/%s", basedir
, relpath
);
147 return extract_js (fullname
);
151 /*===========================================================================
153 = D A T A S T R U C T U R E S
165 /* we start in the start state */
168 /* We are looking for or reading in an attribute */
171 /* We're burning ws before finding an attribute */
174 /* We're burning ws after an attribute. Looking for an '='. */
177 /* We're burning ws after an '=', waiting for a value */
180 /* We're reading in a value */
183 /* We're reading in a value that's inside quotes */
184 GET_QUOTED_VAL_STATE
,
186 /* We've encountered the closing '>' */
196 typedef struct AVPair_Str
{
199 unsigned int valueLine
; /* the line that the value ends on */
200 struct AVPair_Str
*next
;
218 AVPair
* attListTail
;
230 typedef struct HTMLItem_Str
{
231 unsigned int startLine
;
232 unsigned int endLine
;
238 struct HTMLItem_Str
*next
;
245 #define FILE_BUFFER_BUFSIZE 512
246 char buf
[FILE_BUFFER_BUFSIZE
];
249 unsigned int lineNum
;
252 /*===========================================================================
257 static HTMLItem
*CreateTextItem(char *text
, unsigned int startline
,
258 unsigned int endline
);
259 static HTMLItem
*CreateTagItem(TagItem
*ti
, unsigned int startline
,
260 unsigned int endline
);
261 static TagItem
*ProcessTag(FileBuffer
*fb
, char **errStr
);
262 static void DestroyHTMLItem(HTMLItem
*item
);
263 static void DestroyTagItem(TagItem
*ti
);
264 static TAG_TYPE
GetTagType(char *att
);
265 static FileBuffer
*FB_Create(PRFileDesc
*fd
);
266 static int FB_GetChar(FileBuffer
*fb
);
267 static PRInt32
FB_GetPointer(FileBuffer
*fb
);
268 static PRInt32
FB_GetRange(FileBuffer
*fb
, PRInt32 start
, PRInt32 end
,
270 static unsigned int FB_GetLineNum(FileBuffer
*fb
);
271 static void FB_Destroy(FileBuffer
*fb
);
272 static void PrintTagItem(PRFileDesc
*fd
, TagItem
*ti
);
273 static void PrintHTMLStream(PRFileDesc
*fd
, HTMLItem
*head
);
275 /************************************************************************
277 * C r e a t e T e x t I t e m
280 CreateTextItem(char *text
, unsigned int startline
, unsigned int endline
)
284 item
= PR_Malloc(sizeof(HTMLItem
));
289 item
->type
= TEXT_ITEM
;
290 item
->item
.text
= text
;
292 item
->startLine
= startline
;
293 item
->endLine
= endline
;
299 /************************************************************************
301 * C r e a t e T a g I t e m
304 CreateTagItem(TagItem
*ti
, unsigned int startline
, unsigned int endline
)
308 item
= PR_Malloc(sizeof(HTMLItem
));
313 item
->type
= TAG_ITEM
;
316 item
->startLine
= startline
;
317 item
->endLine
= endline
;
326 return (isalnum(c
) || c
== '/' || c
== '-');
330 /************************************************************************
332 * P r o c e s s T a g
335 ProcessTag(FileBuffer
*fb
, char **errStr
)
338 PRInt32 startText
, startID
, curPos
;
342 AVPair
* curPair
= NULL
;
343 char quotechar
= '\0';
344 unsigned int linenum
;
345 unsigned int startline
;
349 startID
= FB_GetPointer(fb
);
353 ti
= (TagItem
* ) PR_Malloc(sizeof(TagItem
));
356 ti
->type
= OTHER_TAG
;
358 ti
->attListTail
= NULL
;
361 startline
= FB_GetLineNum(fb
);
363 while (state
!= DONE_STATE
&& state
!= ERR_STATE
) {
364 linenum
= FB_GetLineNum(fb
);
365 curchar
= FB_GetChar(fb
);
366 if (curchar
== EOF
) {
367 *errStr
= PR_smprintf(
368 "line %d: Unexpected end-of-file while parsing tag starting at line %d.\n",
376 if (curchar
== '!') {
378 * SGML tag or comment
379 * Here's the general rule for SGML tags. Everything from
380 * <! to > is the tag. Inside the tag, comments are
381 * delimited with --. So we are looking for the first '>'
382 * that is not commented out, that is, not inside a pair
383 * of --: <!DOCTYPE --this is a comment >(psyche!) -->
386 PRBool inComment
= PR_FALSE
;
387 short hyphenCount
= 0; /* number of consecutive hyphens */
390 linenum
= FB_GetLineNum(fb
);
391 curchar
= FB_GetChar(fb
);
392 if (curchar
== EOF
) {
393 /* Uh oh, EOF inside comment */
394 *errStr
= PR_smprintf(
395 "line %d: Unexpected end-of-file inside comment starting at line %d.\n",
400 if (curchar
== '-') {
401 if (hyphenCount
== 1) {
402 /* This is a comment delimiter */
403 inComment
= !inComment
;
406 /* beginning of a comment delimiter? */
409 } else if (curchar
== '>') {
411 /* This is the end of the tag */
415 /* The > is inside a comment, so it's not
416 * really the end of the tag */
423 ti
->type
= COMMENT_TAG
;
428 if (isspace(curchar
) || curchar
== '=' || curchar
430 /* end of the current attribute */
431 curPos
= FB_GetPointer(fb
) - 2;
432 if (curPos
>= startID
) {
433 /* We have an attribute */
434 curPair
= (AVPair
* )PR_Malloc(sizeof(AVPair
));
437 curPair
->value
= NULL
;
438 curPair
->next
= NULL
;
439 FB_GetRange(fb
, startID
, curPos
,
440 &curPair
->attribute
);
442 /* Stick this attribute on the list */
443 if (ti
->attListTail
) {
444 ti
->attListTail
->next
= curPair
;
445 ti
->attListTail
= curPair
;
447 ti
->attList
= ti
->attListTail
=
451 /* If this is the first attribute, find the type of tag
452 * based on it. Also, start saving the text of the tag. */
454 ti
->type
= GetTagType(curPair
->attribute
);
455 startText
= FB_GetPointer(fb
)
460 if (curchar
== '=') {
461 /* If we don't have any attribute but we do have an
462 * equal sign, that's an error */
463 *errStr
= PR_smprintf("line %d: Malformed tag starting at line %d.\n",
470 /* Compute next state */
471 if (curchar
== '=') {
472 startID
= FB_GetPointer(fb
);
473 state
= PRE_VAL_WS_STATE
;
474 } else if (curchar
== '>') {
476 } else if (curPair
) {
477 state
= POST_ATT_WS_STATE
;
479 state
= PRE_ATT_WS_STATE
;
481 } else if (isAttChar(curchar
)) {
482 /* Just another char in the attribute. Do nothing */
483 state
= GET_ATT_STATE
;
486 *errStr
= PR_smprintf("line %d: Bogus chararacter '%c' in tag.\n",
492 case PRE_ATT_WS_STATE
:
493 if (curchar
== '>') {
495 } else if (isspace(curchar
)) {
496 /* more whitespace, do nothing */
497 } else if (isAttChar(curchar
)) {
498 /* starting another attribute */
499 startID
= FB_GetPointer(fb
) - 1;
500 state
= GET_ATT_STATE
;
503 *errStr
= PR_smprintf("line %d: Bogus character '%c' in tag.\n",
509 case POST_ATT_WS_STATE
:
510 if (curchar
== '>') {
512 } else if (isspace(curchar
)) {
513 /* more whitespace, do nothing */
514 } else if (isAttChar(curchar
)) {
515 /* starting another attribute */
516 startID
= FB_GetPointer(fb
) - 1;
517 state
= GET_ATT_STATE
;
518 } else if (curchar
== '=') {
519 /* there was whitespace between the attribute and its equal
520 * sign, which means there's a value coming up */
521 state
= PRE_VAL_WS_STATE
;
524 *errStr
= PR_smprintf("line %d: Bogus character '%c' in tag.\n",
530 case PRE_VAL_WS_STATE
:
531 if (curchar
== '>') {
532 /* premature end-of-tag (sounds like a personal problem). */
533 *errStr
= PR_smprintf(
534 "line %d: End of tag while waiting for value.\n",
538 } else if (isspace(curchar
)) {
539 /* more whitespace, do nothing */
542 /* this must be some sort of value. Fall through
543 * to GET_VALUE_STATE */
544 startID
= FB_GetPointer(fb
) - 1;
545 state
= GET_VALUE_STATE
;
547 /* Fall through if we didn't break on '>' or whitespace */
548 case GET_VALUE_STATE
:
549 if (isspace(curchar
) || curchar
== '>') {
551 curPos
= FB_GetPointer(fb
) - 2;
552 if (curPos
>= startID
) {
554 FB_GetRange(fb
, startID
, curPos
,
556 curPair
->valueLine
= linenum
;
558 /* empty value, leave as NULL */
560 if (isspace(curchar
)) {
561 state
= PRE_ATT_WS_STATE
;
565 } else if (curchar
== '\"' || curchar
== '\'') {
566 /* quoted value. Start recording the value inside the quote*/
567 startID
= FB_GetPointer(fb
);
568 state
= GET_QUOTED_VAL_STATE
;
569 PORT_Assert(quotechar
== '\0');
570 quotechar
= curchar
; /* look for matching quote type */
572 /* just more value */
575 case GET_QUOTED_VAL_STATE
:
576 PORT_Assert(quotechar
!= '\0');
577 if (curchar
== quotechar
) {
578 /* end of quoted value */
579 curPos
= FB_GetPointer(fb
) - 2;
580 if (curPos
>= startID
) {
582 FB_GetRange(fb
, startID
, curPos
,
584 curPair
->valueLine
= linenum
;
586 /* empty value, leave it as NULL */
588 state
= GET_ATT_STATE
;
590 startID
= FB_GetPointer(fb
);
592 /* more quoted value, continue */
598 ; /* should never get here */
602 if (state
== DONE_STATE
) {
603 /* Get the text of the tag */
604 curPos
= FB_GetPointer(fb
) - 1;
605 FB_GetRange(fb
, startText
, curPos
, &ti
->text
);
611 /* Uh oh, an error. Kill the tag item*/
617 /************************************************************************
619 * D e s t r o y H T M L I t e m
622 DestroyHTMLItem(HTMLItem
*item
)
624 if (item
->type
== TAG_ITEM
) {
625 DestroyTagItem(item
->item
.tag
);
627 if (item
->item
.text
) {
628 PR_Free(item
->item
.text
);
634 /************************************************************************
636 * D e s t r o y T a g I t e m
639 DestroyTagItem(TagItem
*ti
)
648 while (ti
->attList
) {
650 ti
->attList
= ti
->attList
->next
;
652 if (temp
->attribute
) {
653 PR_Free(temp
->attribute
);
654 temp
->attribute
= NULL
;
657 PR_Free(temp
->value
);
667 /************************************************************************
669 * G e t T a g T y p e
672 GetTagType(char *att
)
674 if (!PORT_Strcasecmp(att
, "APPLET")) {
677 if (!PORT_Strcasecmp(att
, "SCRIPT")) {
680 if (!PORT_Strcasecmp(att
, "LINK")) {
683 if (!PORT_Strcasecmp(att
, "STYLE")) {
690 /************************************************************************
695 FB_Create(PRFileDesc
*fd
)
699 PRInt32 storedOffset
;
701 fb
= (FileBuffer
* ) PR_Malloc(sizeof(FileBuffer
));
703 storedOffset
= PR_Seek(fd
, 0, PR_SEEK_CUR
);
704 PR_Seek(fd
, 0, PR_SEEK_SET
);
706 amountRead
= PR_Read(fd
, fb
->buf
, FILE_BUFFER_BUFSIZE
);
707 if (amountRead
== -1)
709 fb
->maxIndex
= amountRead
- 1;
711 fb
->IsEOF
= (fb
->curIndex
> fb
->maxIndex
) ? PR_TRUE
: PR_FALSE
;
714 PR_Seek(fd
, storedOffset
, PR_SEEK_SET
);
717 PR_Seek(fd
, storedOffset
, PR_SEEK_SET
);
723 /************************************************************************
725 * F B _ G e t C h a r
728 FB_GetChar(FileBuffer
*fb
)
730 PRInt32 storedOffset
;
738 storedOffset
= PR_Seek(fb
->fd
, 0, PR_SEEK_CUR
);
740 retval
= (unsigned char) fb
->buf
[fb
->curIndex
++];
744 if (fb
->curIndex
> fb
->maxIndex
) {
745 /* We're at the end of the buffer. Try to get some new data from the
747 fb
->startOffset
+= fb
->maxIndex
+ 1;
748 PR_Seek(fb
->fd
, fb
->startOffset
, PR_SEEK_SET
);
749 amountRead
= PR_Read(fb
->fd
, fb
->buf
, FILE_BUFFER_BUFSIZE
);
750 if (amountRead
== -1)
752 fb
->maxIndex
= amountRead
- 1;
756 fb
->IsEOF
= (fb
->curIndex
> fb
->maxIndex
) ? PR_TRUE
: PR_FALSE
;
759 PR_Seek(fb
->fd
, storedOffset
, PR_SEEK_SET
);
764 /************************************************************************
766 * F B _ G e t L i n e N u m
770 FB_GetLineNum(FileBuffer
*fb
)
776 /************************************************************************
778 * F B _ G e t P o i n t e r
782 FB_GetPointer(FileBuffer
*fb
)
784 return fb
->startOffset
+ fb
->curIndex
;
788 /************************************************************************
790 * F B _ G e t R a n g e
794 FB_GetRange(FileBuffer
*fb
, PRInt32 start
, PRInt32 end
, char **buf
)
797 PRInt32 storedOffset
;
799 *buf
= PR_Malloc(end
- start
+ 2);
804 storedOffset
= PR_Seek(fb
->fd
, 0, PR_SEEK_CUR
);
805 PR_Seek(fb
->fd
, start
, PR_SEEK_SET
);
806 amountRead
= PR_Read(fb
->fd
, *buf
, end
- start
+ 1);
807 PR_Seek(fb
->fd
, storedOffset
, PR_SEEK_SET
);
808 if (amountRead
== -1) {
814 (*buf
)[end
-start
+1] = '\0';
819 /************************************************************************
821 * F B _ D e s t r o y
825 FB_Destroy(FileBuffer
*fb
)
833 /************************************************************************
835 * P r i n t T a g I t e m
839 PrintTagItem(PRFileDesc
*fd
, TagItem
*ti
)
843 PR_fprintf(fd
, "TAG:\n----\nType: ");
846 PR_fprintf(fd
, "applet\n");
849 PR_fprintf(fd
, "script\n");
852 PR_fprintf(fd
, "link\n");
855 PR_fprintf(fd
, "style\n");
858 PR_fprintf(fd
, "comment\n");
862 PR_fprintf(fd
, "other\n");
866 PR_fprintf(fd
, "Attributes:\n");
867 for (pair
= ti
->attList
; pair
; pair
= pair
->next
) {
868 PR_fprintf(fd
, "\t%s=%s\n", pair
->attribute
,
869 pair
->value
? pair
->value
: "");
871 PR_fprintf(fd
, "Text:%s\n", ti
->text
? ti
->text
: "");
873 PR_fprintf(fd
, "---End of tag---\n");
877 /************************************************************************
879 * P r i n t H T M L S t r e a m
883 PrintHTMLStream(PRFileDesc
*fd
, HTMLItem
*head
)
886 if (head
->type
== TAG_ITEM
) {
887 PrintTagItem(fd
, head
->item
.tag
);
889 PR_fprintf(fd
, "\nTEXT:\n-----\n%s\n-----\n\n", head
->item
.text
);
896 /************************************************************************
898 * S a v e I n l i n e S c r i p t
902 SaveInlineScript(char *text
, char *id
, char *basedir
, char *archiveDir
)
904 char *filename
= NULL
;
905 PRFileDesc
* fd
= NULL
;
910 if (!text
|| !id
|| !archiveDir
) {
915 PR_fprintf(outputFD
, "SaveInlineScript: text=%s, id=%s, \n"
916 "basedir=%s, archiveDir=%s\n",
917 text
, id
, basedir
, archiveDir
);
920 /* Make sure the archive directory is around */
921 if (ensureExists(basedir
, archiveDir
) != PR_SUCCESS
) {
923 "ERROR: Unable to create archive directory %s.\n", archiveDir
);
928 /* Make sure the inline script directory is around */
929 ilDir
= PR_smprintf("%s/inlineScripts", archiveDir
);
930 scriptdir
= "inlineScripts";
931 if (ensureExists(basedir
, ilDir
) != PR_SUCCESS
) {
933 "ERROR: Unable to create directory %s.\n", ilDir
);
938 filename
= PR_smprintf("%s/%s/%s", basedir
, ilDir
, id
);
940 /* If the file already exists, give a warning, then blow it away */
941 if (PR_Access(filename
, PR_ACCESS_EXISTS
) == PR_SUCCESS
) {
943 "warning: file \"%s\" already exists--will overwrite.\n",
946 if (rm_dash_r(filename
)) {
947 PR_fprintf(errorFD
, "ERROR: Unable to delete %s.\n", filename
);
953 /* Write text into file with name id */
954 fd
= PR_Open(filename
, PR_WRONLY
| PR_CREATE_FILE
| PR_TRUNCATE
, 0777);
956 PR_fprintf(errorFD
, "ERROR: Unable to create file \"%s\".\n",
961 writeLen
= strlen(text
);
962 if ( PR_Write(fd
, text
, writeLen
) != writeLen
) {
963 PR_fprintf(errorFD
, "ERROR: Unable to write to file \"%s\".\n",
972 PR_smprintf_free(filename
);
975 PR_smprintf_free(ilDir
);
984 /************************************************************************
986 * S a v e U n n a m a b l e S c r i p t
990 SaveUnnamableScript(char *text
, char *basedir
, char *archiveDir
,
998 if (!text
|| !archiveDir
|| !HTMLfilename
) {
1003 PR_fprintf(outputFD
, "SaveUnnamableScript: text=%s, basedir=%s,\n"
1004 "archiveDir=%s, filename=%s\n", text
, basedir
, archiveDir
,
1008 /* Construct the filename */
1009 ext
= PL_strrchr(HTMLfilename
, '.');
1013 for (start
= HTMLfilename
; strpbrk(start
, "/\\");
1014 start
= strpbrk(start
, "/\\") + 1)
1017 start
= HTMLfilename
;
1018 id
= PR_smprintf("_%s%d", start
, idOrdinal
++);
1023 /* Now call SaveInlineScript to do the work */
1024 retval
= SaveInlineScript(text
, id
, basedir
, archiveDir
);
1032 /************************************************************************
1034 * S a v e S o u r c e
1038 SaveSource(char *src
, char *codebase
, char *basedir
, char *archiveDir
)
1040 char *from
= NULL
, *to
= NULL
;
1042 char *arcDir
= NULL
;
1044 if (!src
|| !archiveDir
) {
1049 PR_fprintf(outputFD
, "SaveSource: src=%s, codebase=%s, basedir=%s,\n"
1050 "archiveDir=%s\n", src
, codebase
, basedir
, archiveDir
);
1054 arcDir
= PR_smprintf("%s/%s/%s/", basedir
, codebase
, archiveDir
);
1056 arcDir
= PR_smprintf("%s/%s/", basedir
, archiveDir
);
1060 from
= PR_smprintf("%s/%s/%s", basedir
, codebase
, src
);
1061 to
= PR_smprintf("%s%s", arcDir
, src
);
1063 from
= PR_smprintf("%s/%s", basedir
, src
);
1064 to
= PR_smprintf("%s%s", arcDir
, src
);
1067 if (make_dirs(to
, 0777)) {
1069 "ERROR: Unable to create archive directory %s.\n", archiveDir
);
1074 retval
= copyinto(from
, to
);
1086 /************************************************************************
1088 * T a g T y p e T o S t r i n g
1092 TagTypeToString(TAG_TYPE type
)
1110 /************************************************************************
1112 * e x t r a c t _ j s
1116 extract_js(char *filename
)
1118 PRFileDesc
* fd
= NULL
;
1119 FileBuffer
* fb
= NULL
;
1120 HTMLItem
* head
= NULL
;
1121 HTMLItem
* tail
= NULL
;
1122 HTMLItem
* curitem
= NULL
;
1123 HTMLItem
* styleList
= NULL
;
1124 HTMLItem
* styleListTail
= NULL
;
1125 HTMLItem
* entityList
= NULL
;
1126 HTMLItem
* entityListTail
= NULL
;
1127 TagItem
* tagp
= NULL
;
1129 char *tagerr
= NULL
;
1130 char *archiveDir
= NULL
;
1131 char *firstArchiveDir
= NULL
;
1132 char *basedir
= NULL
;
1138 unsigned int linenum
, startLine
;
1140 /* Initialize the implicit ID counter for each file */
1144 * First, parse the HTML into a stream of tags and text.
1147 fd
= PR_Open(filename
, PR_RDONLY
, 0);
1149 PR_fprintf(errorFD
, "Unable to open %s for reading.\n", filename
);
1154 /* Construct base directory of filename. */
1158 basedir
= PL_strdup(filename
);
1160 /* Remove trailing slashes */
1161 while ( (cp
= PL_strprbrk(basedir
, "/\\")) ==
1162 (basedir
+ strlen(basedir
) - 1)) {
1166 /* Now remove everything from the last slash (which will be followed
1167 * by a filename) to the end */
1168 cp
= PL_strprbrk(basedir
, "/\\");
1174 state
= TEXT_HTML_STATE
;
1180 while (linenum
= FB_GetLineNum(fb
), (curchar
= FB_GetChar(fb
)) !=
1183 case TEXT_HTML_STATE
:
1184 if (curchar
== '<') {
1188 /* Save the text so far to a new text item */
1189 curOffset
= FB_GetPointer(fb
) - 2;
1190 if (curOffset
>= textStart
) {
1191 if (FB_GetRange(fb
, textStart
, curOffset
,
1193 curOffset
- textStart
+ 1) {
1195 "Unable to read from %s.\n",
1200 /* little fudge here. If the first character on a line
1201 * is '<', meaning a new tag, the preceding text item
1202 * actually ends on the previous line. In this case
1203 * we will be saying that the text segment ends on the
1204 * next line. I don't think this matters for text items. */
1205 curitem
= CreateTextItem(text
, startLine
,
1209 head
= tail
= curitem
;
1211 tail
->next
= curitem
;
1216 /* Process the tag */
1217 tagp
= ProcessTag(fb
, &tagerr
);
1220 PR_fprintf(errorFD
, "Error in file %s: %s\n",
1225 "Error in file %s, in tag starting at line %d\n",
1231 /* Add the tag to the list */
1232 curitem
= CreateTagItem(tagp
, linenum
, FB_GetLineNum(fb
));
1234 head
= tail
= curitem
;
1236 tail
->next
= curitem
;
1240 /* What's the next state */
1241 if (tagp
->type
== SCRIPT_TAG
) {
1242 state
= SCRIPT_HTML_STATE
;
1245 /* Start recording text from the new offset */
1246 textStart
= FB_GetPointer(fb
);
1247 startLine
= FB_GetLineNum(fb
);
1249 /* regular character. Next! */
1252 case SCRIPT_HTML_STATE
:
1253 if (curchar
== '<') {
1256 * If this is a </script> tag, then we're at the end of the
1257 * script. Otherwise, ignore
1259 curOffset
= FB_GetPointer(fb
) - 1;
1261 if (FB_GetRange(fb
, curOffset
, curOffset
+ 8, &cp
) != 9) {
1267 /* compare the strings */
1268 if ( !PORT_Strncasecmp(cp
, "</script>", 9) ) {
1269 /* This is the end of the script. Record the text. */
1271 if (curOffset
>= textStart
) {
1272 if (FB_GetRange(fb
, textStart
, curOffset
, &text
) !=
1273 curOffset
- textStart
+ 1) {
1274 PR_fprintf(errorFD
, "Unable to read from %s.\n",
1279 curitem
= CreateTextItem(text
, startLine
, linenum
);
1282 head
= tail
= curitem
;
1284 tail
->next
= curitem
;
1289 /* Now parse the /script tag and put it on the list */
1290 tagp
= ProcessTag(fb
, &tagerr
);
1293 PR_fprintf(errorFD
, "Error in file %s: %s\n",
1297 "Error in file %s, in tag starting at"
1298 " line %d\n", filename
, linenum
);
1303 curitem
= CreateTagItem(tagp
, linenum
,
1306 head
= tail
= curitem
;
1308 tail
->next
= curitem
;
1312 /* go back to text state */
1313 state
= TEXT_HTML_STATE
;
1315 textStart
= FB_GetPointer(fb
);
1316 startLine
= FB_GetLineNum(fb
);
1324 /* End of the file. Wrap up any remaining text */
1325 if (state
== SCRIPT_HTML_STATE
) {
1326 if (tail
&& tail
->type
== TAG_ITEM
) {
1327 PR_fprintf(errorFD
, "ERROR: <SCRIPT> tag at %s:%d is not followed "
1328 "by a </SCRIPT> tag.\n", filename
, tail
->startLine
);
1330 PR_fprintf(errorFD
, "ERROR: <SCRIPT> tag in file %s is not followed"
1331 " by a </SCRIPT tag.\n", filename
);
1336 curOffset
= FB_GetPointer(fb
) - 1;
1337 if (curOffset
>= textStart
) {
1339 if ( FB_GetRange(fb
, textStart
, curOffset
, &text
) !=
1340 curOffset
- textStart
+ 1) {
1341 PR_fprintf(errorFD
, "Unable to read from %s.\n", filename
);
1345 curitem
= CreateTextItem(text
, startLine
, linenum
);
1348 head
= tail
= curitem
;
1350 tail
->next
= curitem
;
1356 PrintHTMLStream(outputFD
, head
);
1360 * Now we have a stream of tags and text. Go through and deal with each.
1362 for (curitem
= head
; curitem
; curitem
= curitem
->next
) {
1363 TagItem
* tagp
= NULL
;
1364 AVPair
* pairp
= NULL
;
1365 char *src
= NULL
, *id
= NULL
, *codebase
= NULL
;
1366 PRBool hasEventHandler
= PR_FALSE
;
1369 /* Reset archive directory for each tag */
1371 PR_Free(archiveDir
);
1375 /* We only analyze tags */
1376 if (curitem
->type
!= TAG_ITEM
) {
1380 tagp
= curitem
->item
.tag
;
1382 /* go through the attributes to get information */
1383 for (pairp
= tagp
->attList
; pairp
; pairp
= pairp
->next
) {
1386 if ( !PL_strcasecmp(pairp
->attribute
, "archive")) {
1388 /* Duplicate attribute. Print warning */
1390 "warning: \"%s\" attribute overwrites previous attribute"
1391 " in tag starting at %s:%d.\n",
1392 pairp
->attribute
, filename
, curitem
->startLine
);
1394 PR_Free(archiveDir
);
1396 archiveDir
= PL_strdup(pairp
->value
);
1398 /* Substiture ".arc" for ".jar" */
1399 if ( (PL_strlen(archiveDir
) < 4) ||
1400 PL_strcasecmp((archiveDir
+ strlen(archiveDir
) -4),
1403 "warning: ARCHIVE attribute should end in \".jar\" in tag"
1404 " starting on %s:%d.\n", filename
, curitem
->startLine
);
1406 PR_Free(archiveDir
);
1407 archiveDir
= PR_smprintf("%s.arc", archiveDir
);
1409 PL_strcpy(archiveDir
+ strlen(archiveDir
) -4, ".arc");
1412 /* Record the first archive. This will be used later if
1413 * the archive is not specified */
1414 if (firstArchiveDir
== NULL
) {
1415 firstArchiveDir
= PL_strdup(archiveDir
);
1419 else if ( !PL_strcasecmp(pairp
->attribute
, "codebase")) {
1421 /* Duplicate attribute. Print warning */
1423 "warning: \"%s\" attribute overwrites previous attribute"
1424 " in tag staring at %s:%d.\n",
1425 pairp
->attribute
, filename
, curitem
->startLine
);
1428 codebase
= pairp
->value
;
1430 /* SRC= and HREF= */
1431 else if ( !PORT_Strcasecmp(pairp
->attribute
, "src") ||
1432 !PORT_Strcasecmp(pairp
->attribute
, "href") ) {
1434 /* Duplicate attribute. Print warning */
1436 "warning: \"%s\" attribute overwrites previous attribute"
1437 " in tag staring at %s:%d.\n",
1438 pairp
->attribute
, filename
, curitem
->startLine
);
1444 else if (!PORT_Strcasecmp(pairp
->attribute
, "code") ) {
1445 /*!!!XXX Change PORT to PL all over this code !!! */
1447 /* Duplicate attribute. Print warning */
1449 "warning: \"%s\" attribute overwrites previous attribute"
1450 " ,in tag staring at %s:%d.\n",
1451 pairp
->attribute
, filename
, curitem
->startLine
);
1456 /* Append a .class if one is not already present */
1457 if ( (PL_strlen(src
) < 6) ||
1458 PL_strcasecmp( (src
+ PL_strlen(src
) - 6), ".class") ) {
1459 src
= PR_smprintf("%s.class", src
);
1460 /* Put this string back into the data structure so it
1461 * will be deallocated properly */
1462 PR_Free(pairp
->value
);
1467 else if (!PL_strcasecmp(pairp
->attribute
, "id") ) {
1469 /* Duplicate attribute. Print warning */
1471 "warning: \"%s\" attribute overwrites previous attribute"
1472 " in tag staring at %s:%d.\n",
1473 pairp
->attribute
, filename
, curitem
->startLine
);
1480 /* style= attributes, along with JS entities, are stored into
1481 * files with dynamically generated names. The filenames are
1482 * based on the order in which the text is found in the file.
1483 * All JS entities on all lines up to and including the line
1484 * containing the end of the tag that has this style= attribute
1485 * will be processed before this style=attribute. So we need
1486 * to record the line that this _tag_ (not the attribute) ends on.
1488 else if (!PL_strcasecmp(pairp
->attribute
, "style") && pairp
->value
)
1490 HTMLItem
* styleItem
;
1491 /* Put this item on the style list */
1492 styleItem
= CreateTextItem(PL_strdup(pairp
->value
),
1493 curitem
->startLine
, curitem
->endLine
);
1494 if (styleListTail
== NULL
) {
1495 styleList
= styleListTail
= styleItem
;
1497 styleListTail
->next
= styleItem
;
1498 styleListTail
= styleItem
;
1501 /* Event handlers */
1503 for (i
= 0; i
< num_handlers
; i
++) {
1504 if (!PL_strcasecmp(event_handlers
[i
], pairp
->attribute
)) {
1505 hasEventHandler
= PR_TRUE
;
1514 char *entityStart
, *entityEnd
;
1515 HTMLItem
* entityItem
;
1517 /* go through each JavaScript entity ( &{...}; ) and store it
1518 * in the entityList. The important thing is to record what
1519 * line number it's on, so we can get it in the right order
1520 * in relation to style= attributes.
1521 * Apparently, these can't flow across lines, so the start and
1522 * end line will be the same. That helps matters.
1524 entityEnd
= pairp
->value
;
1525 while ( entityEnd
&&
1526 (entityStart
= PL_strstr(entityEnd
, "&{")) /*}*/ != NULL
) {
1527 entityStart
+= 2; /* point at beginning of actual entity */
1528 entityEnd
= PL_strstr(entityStart
, /*{*/ "}");
1530 /* Put this item on the entity list */
1532 entityItem
= CreateTextItem(PL_strdup(entityStart
),
1533 pairp
->valueLine
, pairp
->valueLine
);
1534 *entityEnd
= /* { */ '}';
1535 if (entityListTail
) {
1536 entityListTail
->next
= entityItem
;
1537 entityListTail
= entityItem
;
1539 entityList
= entityListTail
= entityItem
;
1546 /* If no archive was supplied, we use the first one of the file */
1547 if (!archiveDir
&& firstArchiveDir
) {
1548 archiveDir
= PL_strdup(firstArchiveDir
);
1551 /* If we have an event handler, we need to archive this tag */
1552 if (hasEventHandler
) {
1555 "warning: tag starting at %s:%d has event handler but"
1556 " no ID attribute. The tag will not be signed.\n",
1557 filename
, curitem
->startLine
);
1559 } else if (!archiveDir
) {
1561 "warning: tag starting at %s:%d has event handler but"
1562 " no ARCHIVE attribute. The tag will not be signed.\n",
1563 filename
, curitem
->startLine
);
1566 if (SaveInlineScript(tagp
->text
, id
, basedir
, archiveDir
)) {
1572 switch (tagp
->type
) {
1576 "error: APPLET tag starting on %s:%d has no CODE "
1577 "attribute.\n", filename
, curitem
->startLine
);
1580 } else if (!archiveDir
) {
1582 "error: APPLET tag starting on %s:%d has no ARCHIVE "
1583 "attribute.\n", filename
, curitem
->startLine
);
1587 if (SaveSource(src
, codebase
, basedir
, archiveDir
)) {
1597 "error: %s tag starting on %s:%d has no ARCHIVE "
1598 "attribute.\n", TagTypeToString(tagp
->type
),
1599 filename
, curitem
->startLine
);
1603 if (SaveSource(src
, codebase
, basedir
, archiveDir
)) {
1607 /* Save the next text item */
1608 if (!curitem
->next
|| (curitem
->next
->type
!=
1611 "warning: %s tag starting on %s:%d is not followed"
1612 " by script text.\n", TagTypeToString(tagp
->type
),
1613 filename
, curitem
->startLine
);
1615 /* just create empty file */
1616 if (SaveInlineScript("", id
, basedir
, archiveDir
)) {
1620 curitem
= curitem
->next
;
1621 if (SaveInlineScript(curitem
->item
.text
,
1628 /* No src or id tag--warning */
1630 "warning: %s tag starting on %s:%d has no SRC or"
1631 " ID attributes. Will not sign.\n",
1632 TagTypeToString(tagp
->type
), filename
, curitem
->startLine
);
1637 /* do nothing for other tags */
1643 /* Now deal with all the unnamable scripts */
1644 if (firstArchiveDir
) {
1645 HTMLItem
* style
, *entity
;
1647 /* Go through the lists of JS entities and style attributes. Do them
1648 * in chronological order within a list. Pick the list with the lower
1649 * endLine. In case of a tie, entities come first.
1652 entity
= entityList
;
1653 while (style
|| entity
) {
1654 if (!entity
|| (style
&& (style
->endLine
< entity
->endLine
))) {
1656 SaveUnnamableScript(style
->item
.text
, basedir
, firstArchiveDir
,
1658 style
= style
->next
;
1660 /* Process entity */
1661 SaveUnnamableScript(entity
->item
.text
, basedir
, firstArchiveDir
,
1663 entity
= entity
->next
;
1671 /* Blow away the stream */
1675 DestroyHTMLItem(curitem
);
1678 curitem
= styleList
;
1679 styleList
= styleList
->next
;
1680 DestroyHTMLItem(curitem
);
1682 while (entityList
) {
1683 curitem
= entityList
;
1684 entityList
= entityList
->next
;
1685 DestroyHTMLItem(curitem
);
1699 PR_smprintf_free(tagerr
);
1703 PR_Free(archiveDir
);
1706 if (firstArchiveDir
) {
1707 PR_Free(firstArchiveDir
);
1708 firstArchiveDir
= NULL
;
1714 /**********************************************************************
1716 * e n s u r e E x i s t s
1718 * Check for existence of indicated directory. If it doesn't exist,
1719 * it will be created.
1720 * Returns PR_SUCCESS if the directory is present, PR_FAILURE otherwise.
1723 ensureExists (char *base
, char *path
)
1727 sprintf (fn
, "%s/%s", base
, path
);
1729 /*PR_fprintf(outputFD, "Trying to open directory %s.\n", fn);*/
1731 if ( (dir
= PR_OpenDir(fn
)) ) {
1735 return PR_MkDir(fn
, 0777);
1739 /***************************************************************************
1743 * Ensure that the directory portion of the path exists. This may require
1744 * making the directory, and its parent, and its parent's parent, etc.
1747 make_dirs(char *path
, int file_perms
)
1759 Path
= PL_strdup(path
);
1760 start
= strpbrk(Path
, "/\\");
1764 start
++; /* start right after first slash */
1766 /* Each time through the loop add one more directory. */
1767 while ( (sep
= strpbrk(start
, "/\\")) ) {
1770 if ( PR_GetFileInfo(Path
, &info
) != PR_SUCCESS
) {
1771 /* No such dir, we have to create it */
1772 if ( PR_MkDir(Path
, file_perms
) != PR_SUCCESS
) {
1773 PR_fprintf(errorFD
, "ERROR: Unable to create directory %s.\n",
1780 /* something exists by this name, make sure it's a directory */
1781 if ( info
.type
!= PR_FILE_DIRECTORY
) {
1782 PR_fprintf(errorFD
, "ERROR: Unable to create directory %s.\n",
1790 start
= sep
+ 1; /* start after the next slash */
1803 * Function to copy file "from" to path "to".
1807 copyinto (char *from
, char *to
)
1811 PRFileDesc
* infp
= NULL
, *outfp
= NULL
;
1814 if ((infp
= PR_Open(from
, PR_RDONLY
, 0777)) == NULL
) {
1815 PR_fprintf(errorFD
, "ERROR: Unable to open \"%s\" for reading.\n",
1821 /* If to already exists, print a warning before deleting it */
1822 if (PR_Access(to
, PR_ACCESS_EXISTS
) == PR_SUCCESS
) {
1823 PR_fprintf(errorFD
, "warning: %s already exists--will overwrite\n", to
);
1825 if (rm_dash_r(to
)) {
1827 "ERROR: Unable to remove %s.\n", to
);
1833 if ((outfp
= PR_Open(to
, PR_WRONLY
| PR_CREATE_FILE
| PR_TRUNCATE
, 0777))
1835 char *errBuf
= NULL
;
1837 errBuf
= PR_Malloc(PR_GetErrorTextLength());
1838 PR_fprintf(errorFD
, "ERROR: Unable to open \"%s\" for writing.\n", to
);
1839 if (PR_GetErrorText(errBuf
)) {
1840 PR_fprintf(errorFD
, "Cause: %s\n", errBuf
);
1849 while ( (num
= PR_Read(infp
, buf
, BUFSIZ
)) > 0) {
1850 if (PR_Write(outfp
, buf
, num
) != num
) {
1851 PR_fprintf(errorFD
, "ERROR: Error writing to %s.\n", to
);