Bug Summary

File:s/cmd/signtool/javascript.c
Warning:line 1288, column 20
Although the value stored to 'tail' is used in the enclosing expression, the value is never actually read from 'tail'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name javascript.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/var/lib/jenkins/workspace/nss-scan-build/nss/cmd/signtool -ffunction-sections -fdata-sections -fcoverage-compilation-dir=/var/lib/jenkins/workspace/nss-scan-build/nss/cmd/signtool -resource-dir /usr/lib/llvm-18/lib/clang/18 -D HAVE_STRERROR -D LINUX -D linux -D XP_UNIX -D XP_UNIX -D DEBUG -U NDEBUG -D _DEFAULT_SOURCE -D _BSD_SOURCE -D _POSIX_SOURCE -D SDB_MEASURE_USE_TEMP_DIR -D _REENTRANT -D DEBUG -U NDEBUG -D _DEFAULT_SOURCE -D _BSD_SOURCE -D _POSIX_SOURCE -D SDB_MEASURE_USE_TEMP_DIR -D _REENTRANT -D NSS_DISABLE_SSE3 -D NSS_NO_INIT_SUPPORT -D USE_UTIL_DIRECTLY -D NO_NSPR_10_SUPPORT -D SSL_DISABLE_DEPRECATED_CIPHER_SUITE_NAMES -I ../../../dist/Linux4.19_x86_64_gcc_glibc_PTH_64_DBG.OBJ/include -I ../../../dist/public/nss -I ../../../dist/private/nss -I ../../../dist/public/seccmd -internal-isystem /usr/lib/llvm-18/lib/clang/18/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -std=c99 -ferror-limit 19 -fgnuc-version=4.2.1 -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2024-05-18-082241-28900-1 -x c javascript.c
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/. */
4
5#include "signtool.h"
6#include <prmem.h>
7#include <prio.h>
8#include <prenv.h>
9
10static int javascript_fn(char *relpath, char *basedir, char *reldir,
11 char *filename, void *arg);
12static int extract_js(char *filename);
13static int copyinto(char *from, char *to);
14static PRStatus ensureExists(char *base, char *path);
15static int make_dirs(char *path, PRInt32 file_perms);
16
17static char *jartree = NULL((void*)0);
18static int idOrdinal;
19static PRBool dumpParse = PR_FALSE0;
20
21static 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};
46
47static int num_handlers = 23;
48
49/*
50 * I n l i n e J a v a S c r i p t
51 *
52 * Javascript signing. Instead of passing an archive to signtool,
53 * a directory containing html files is given. Archives are created
54 * from the archive= and src= tag attributes inside the html,
55 * as appropriate. Then the archives are signed.
56 *
57 */
58int
59InlineJavaScript(char *dir, PRBool recurse)
60{
61 jartree = dir;
62 if (verbosity >= 0) {
63 PR_fprintf(outputFD, "\nGenerating inline signatures from HTML files in: %s\n",
64 dir);
65 }
66 if (PR_GetEnvSecure("SIGNTOOL_DUMP_PARSE")) {
67 dumpParse = PR_TRUE1;
68 }
69
70 return foreach (dir, "", javascript_fn, recurse, PR_FALSE0 /*include dirs*/,
71 (void *)NULL((void*)0));
72}
73
74/************************************************************************
75 *
76 * j a v a s c r i p t _ f n
77 */
78static int
79javascript_fn(char *relpath, char *basedir, char *reldir, char *filename, void *arg)
80{
81 char fullname[FNSIZE256];
82
83 /* only process inline scripts from .htm, .html, and .shtml*/
84
85 if (!(PL_strcaserstr(filename, ".htm") == filename + strlen(filename) - 4) &&
86 !(PL_strcaserstr(filename, ".html") == filename + strlen(filename) - 5) &&
87 !(PL_strcaserstr(filename, ".shtml") == filename + strlen(filename) - 6)) {
88 return 0;
89 }
90
91 /* don't process scripts that signtool has already
92 extracted (those that are inside .arc directories) */
93
94 if (PL_strcaserstr(filename, ".arc") == filename + strlen(filename) - 4)
95 return 0;
96
97 if (verbosity >= 0) {
98 PR_fprintf(outputFD, "Processing HTML file: %s\n", relpath);
99 }
100
101 /* reset firstArchive at top of each HTML file */
102
103 /* skip directories that contain extracted scripts */
104
105 if (PL_strcaserstr(reldir, ".arc") == reldir + strlen(reldir) - 4)
106 return 0;
107
108 snprintf(fullname, sizeof(fullname), "%s/%s", basedir, relpath);
109 return extract_js(fullname);
110}
111
112/*===========================================================================
113 =
114 = D A T A S T R U C T U R E S
115 =
116*/
117typedef enum {
118 TEXT_HTML_STATE = 0,
119 SCRIPT_HTML_STATE
120}
121
122HTML_STATE;
123
124typedef enum {
125 /* we start in the start state */
126 START_STATE,
127
128 /* We are looking for or reading in an attribute */
129 GET_ATT_STATE,
130
131 /* We're burning ws before finding an attribute */
132 PRE_ATT_WS_STATE,
133
134 /* We're burning ws after an attribute. Looking for an '='. */
135 POST_ATT_WS_STATE,
136
137 /* We're burning ws after an '=', waiting for a value */
138 PRE_VAL_WS_STATE,
139
140 /* We're reading in a value */
141 GET_VALUE_STATE,
142
143 /* We're reading in a value that's inside quotes */
144 GET_QUOTED_VAL_STATE,
145
146 /* We've encountered the closing '>' */
147 DONE_STATE,
148
149 /* Error state */
150 ERR_STATE
151}
152
153TAG_STATE;
154
155typedef struct AVPair_Str {
156 char *attribute;
157 char *value;
158 unsigned int valueLine; /* the line that the value ends on */
159 struct AVPair_Str *next;
160} AVPair;
161
162typedef enum {
163 APPLET_TAG,
164 SCRIPT_TAG,
165 LINK_TAG,
166 STYLE_TAG,
167 COMMENT_TAG,
168 OTHER_TAG
169}
170
171TAG_TYPE;
172
173typedef struct {
174 TAG_TYPE type;
175 AVPair *attList;
176 AVPair *attListTail;
177 char *text;
178} TagItem;
179
180typedef enum {
181 TAG_ITEM,
182 TEXT_ITEM
183}
184
185ITEM_TYPE;
186
187typedef struct HTMLItem_Str {
188 unsigned int startLine;
189 unsigned int endLine;
190 ITEM_TYPE type;
191 union {
192 TagItem *tag;
193 char *text;
194 } item;
195 struct HTMLItem_Str *next;
196} HTMLItem;
197
198typedef struct {
199 PRFileDesc *fd;
200 PRInt32 curIndex;
201 PRBool IsEOF;
202#define FILE_BUFFER_BUFSIZE512 512
203 char buf[FILE_BUFFER_BUFSIZE512];
204 PRInt32 startOffset;
205 PRInt32 maxIndex;
206 unsigned int lineNum;
207} FileBuffer;
208
209/*===========================================================================
210 =
211 = F U N C T I O N S
212 =
213*/
214static HTMLItem *CreateTextItem(char *text, unsigned int startline,
215 unsigned int endline);
216static HTMLItem *CreateTagItem(TagItem *ti, unsigned int startline,
217 unsigned int endline);
218static TagItem *ProcessTag(FileBuffer *fb, char **errStr);
219static void DestroyHTMLItem(HTMLItem *item);
220static void DestroyTagItem(TagItem *ti);
221static TAG_TYPE GetTagType(char *att);
222static FileBuffer *FB_Create(PRFileDesc *fd);
223static int FB_GetChar(FileBuffer *fb);
224static PRInt32 FB_GetPointer(FileBuffer *fb);
225static PRInt32 FB_GetRange(FileBuffer *fb, PRInt32 start, PRInt32 end,
226 char **buf);
227static unsigned int FB_GetLineNum(FileBuffer *fb);
228static void FB_Destroy(FileBuffer *fb);
229static void PrintTagItem(PRFileDesc *fd, TagItem *ti);
230static void PrintHTMLStream(PRFileDesc *fd, HTMLItem *head);
231
232/************************************************************************
233 *
234 * C r e a t e T e x t I t e m
235 */
236static HTMLItem *
237CreateTextItem(char *text, unsigned int startline, unsigned int endline)
238{
239 HTMLItem *item;
240
241 item = PR_Malloc(sizeof(HTMLItem));
242 if (!item) {
243 return NULL((void*)0);
244 }
245
246 item->type = TEXT_ITEM;
247 item->item.text = text;
248 item->next = NULL((void*)0);
249 item->startLine = startline;
250 item->endLine = endline;
251
252 return item;
253}
254
255/************************************************************************
256 *
257 * C r e a t e T a g I t e m
258 */
259static HTMLItem *
260CreateTagItem(TagItem *ti, unsigned int startline, unsigned int endline)
261{
262 HTMLItem *item;
263
264 item = PR_Malloc(sizeof(HTMLItem));
265 if (!item) {
266 return NULL((void*)0);
267 }
268
269 item->type = TAG_ITEM;
270 item->item.tag = ti;
271 item->next = NULL((void*)0);
272 item->startLine = startline;
273 item->endLine = endline;
274
275 return item;
276}
277
278static PRBool
279isAttChar(int c)
280{
281 return (isalnum(c)((*__ctype_b_loc ())[(int) ((c))] & (unsigned short int) _ISalnum
)
|| c == '/' || c == '-');
282}
283
284/************************************************************************
285 *
286 * P r o c e s s T a g
287 */
288static TagItem *
289ProcessTag(FileBuffer *fb, char **errStr)
290{
291 TAG_STATE state;
292 PRInt32 startText, startID, curPos;
293 PRBool firstAtt;
294 int curchar;
295 TagItem *ti = NULL((void*)0);
296 AVPair *curPair = NULL((void*)0);
297 char quotechar = '\0';
298 unsigned int linenum;
299 unsigned int startline;
300
301 state = START_STATE;
302
303 startID = FB_GetPointer(fb);
304 startText = startID;
305 firstAtt = PR_TRUE1;
306
307 ti = (TagItem *)PR_Malloc(sizeof(TagItem));
308 if (!ti)
309 out_of_memory();
310 ti->type = OTHER_TAG;
311 ti->attList = NULL((void*)0);
312 ti->attListTail = NULL((void*)0);
313 ti->text = NULL((void*)0);
314
315 startline = FB_GetLineNum(fb);
316
317 while (state != DONE_STATE && state != ERR_STATE) {
318 linenum = FB_GetLineNum(fb);
319 curchar = FB_GetChar(fb);
320 if (curchar == EOF(-1)) {
321 *errStr = PR_smprintf(
322 "line %d: Unexpected end-of-file while parsing tag starting at line %d.\n",
323 linenum, startline);
324 state = ERR_STATE;
325 continue;
326 }
327
328 switch (state) {
329 case START_STATE:
330 if (curchar == '!') {
331 /*
332 * SGML tag or comment
333 * Here's the general rule for SGML tags. Everything from
334 * <! to > is the tag. Inside the tag, comments are
335 * delimited with --. So we are looking for the first '>'
336 * that is not commented out, that is, not inside a pair
337 * of --: <!DOCTYPE --this is a comment >(psyche!) -->
338 */
339
340 PRBool inComment = PR_FALSE0;
341 short hyphenCount = 0; /* number of consecutive hyphens */
342
343 while (1) {
344 linenum = FB_GetLineNum(fb);
345 curchar = FB_GetChar(fb);
346 if (curchar == EOF(-1)) {
347 /* Uh oh, EOF inside comment */
348 *errStr = PR_smprintf(
349 "line %d: Unexpected end-of-file inside comment starting at line %d.\n",
350 linenum, startline);
351 state = ERR_STATE;
352 break;
353 }
354 if (curchar == '-') {
355 if (hyphenCount == 1) {
356 /* This is a comment delimiter */
357 inComment = !inComment;
358 hyphenCount = 0;
359 } else {
360 /* beginning of a comment delimiter? */
361 hyphenCount = 1;
362 }
363 } else if (curchar == '>') {
364 if (!inComment) {
365 /* This is the end of the tag */
366 state = DONE_STATE;
367 break;
368 } else {
369 /* The > is inside a comment, so it's not
370 * really the end of the tag */
371 hyphenCount = 0;
372 }
373 } else {
374 hyphenCount = 0;
375 }
376 }
377 ti->type = COMMENT_TAG;
378 break;
379 }
380 /* fall through */
381 case GET_ATT_STATE:
382 if (isspace(curchar)((*__ctype_b_loc ())[(int) ((curchar))] & (unsigned short
int) _ISspace)
|| curchar == '=' || curchar == '>') {
383 /* end of the current attribute */
384 curPos = FB_GetPointer(fb) - 2;
385 if (curPos >= startID) {
386 /* We have an attribute */
387 curPair = (AVPair *)PR_Malloc(sizeof(AVPair));
388 if (!curPair)
389 out_of_memory();
390 curPair->value = NULL((void*)0);
391 curPair->next = NULL((void*)0);
392 FB_GetRange(fb, startID, curPos,
393 &curPair->attribute);
394
395 /* Stick this attribute on the list */
396 if (ti->attListTail) {
397 ti->attListTail->next = curPair;
398 ti->attListTail = curPair;
399 } else {
400 ti->attList = ti->attListTail =
401 curPair;
402 }
403
404 /* If this is the first attribute, find the type of tag
405 * based on it. Also, start saving the text of the tag. */
406 if (firstAtt) {
407 ti->type = GetTagType(curPair->attribute);
408 startText = FB_GetPointer(fb) -
409 1;
410 firstAtt = PR_FALSE0;
411 }
412 } else {
413 if (curchar == '=') {
414 /* If we don't have any attribute but we do have an
415 * equal sign, that's an error */
416 *errStr = PR_smprintf("line %d: Malformed tag starting at line %d.\n",
417 linenum, startline);
418 state = ERR_STATE;
419 break;
420 }
421 }
422
423 /* Compute next state */
424 if (curchar == '=') {
425 startID = FB_GetPointer(fb);
426 state = PRE_VAL_WS_STATE;
427 } else if (curchar == '>') {
428 state = DONE_STATE;
429 } else if (curPair) {
430 state = POST_ATT_WS_STATE;
431 } else {
432 state = PRE_ATT_WS_STATE;
433 }
434 } else if (isAttChar(curchar)) {
435 /* Just another char in the attribute. Do nothing */
436 state = GET_ATT_STATE;
437 } else {
438 /* bogus char */
439 *errStr = PR_smprintf("line %d: Bogus chararacter '%c' in tag.\n",
440 linenum, curchar);
441 state = ERR_STATE;
442 break;
443 }
444 break;
445 case PRE_ATT_WS_STATE:
446 if (curchar == '>') {
447 state = DONE_STATE;
448 } else if (isspace(curchar)((*__ctype_b_loc ())[(int) ((curchar))] & (unsigned short
int) _ISspace)
) {
449 /* more whitespace, do nothing */
450 } else if (isAttChar(curchar)) {
451 /* starting another attribute */
452 startID = FB_GetPointer(fb) - 1;
453 state = GET_ATT_STATE;
454 } else {
455 /* bogus char */
456 *errStr = PR_smprintf("line %d: Bogus character '%c' in tag.\n",
457 linenum, curchar);
458 state = ERR_STATE;
459 break;
460 }
461 break;
462 case POST_ATT_WS_STATE:
463 if (curchar == '>') {
464 state = DONE_STATE;
465 } else if (isspace(curchar)((*__ctype_b_loc ())[(int) ((curchar))] & (unsigned short
int) _ISspace)
) {
466 /* more whitespace, do nothing */
467 } else if (isAttChar(curchar)) {
468 /* starting another attribute */
469 startID = FB_GetPointer(fb) - 1;
470 state = GET_ATT_STATE;
471 } else if (curchar == '=') {
472 /* there was whitespace between the attribute and its equal
473 * sign, which means there's a value coming up */
474 state = PRE_VAL_WS_STATE;
475 } else {
476 /* bogus char */
477 *errStr = PR_smprintf("line %d: Bogus character '%c' in tag.\n",
478 linenum, curchar);
479 state = ERR_STATE;
480 break;
481 }
482 break;
483 case PRE_VAL_WS_STATE:
484 if (curchar == '>') {
485 /* premature end-of-tag (sounds like a personal problem). */
486 *errStr = PR_smprintf(
487 "line %d: End of tag while waiting for value.\n",
488 linenum);
489 state = ERR_STATE;
490 break;
491 } else if (isspace(curchar)((*__ctype_b_loc ())[(int) ((curchar))] & (unsigned short
int) _ISspace)
) {
492 /* more whitespace, do nothing */
493 break;
494 } else {
495 /* this must be some sort of value. Fall through
496 * to GET_VALUE_STATE */
497 startID = FB_GetPointer(fb) - 1;
498 state = GET_VALUE_STATE;
499 }
500 /* Fall through if we didn't break on '>' or whitespace */
501 case GET_VALUE_STATE:
502 if (isspace(curchar)((*__ctype_b_loc ())[(int) ((curchar))] & (unsigned short
int) _ISspace)
|| curchar == '>') {
503 /* end of value */
504 curPos = FB_GetPointer(fb) - 2;
505 if (curPos >= startID) {
506 /* Grab the value */
507 FB_GetRange(fb, startID, curPos,
508 &curPair->value);
509 curPair->valueLine = linenum;
510 } else {
511 /* empty value, leave as NULL */
512 }
513 if (isspace(curchar)((*__ctype_b_loc ())[(int) ((curchar))] & (unsigned short
int) _ISspace)
) {
514 state = PRE_ATT_WS_STATE;
515 } else {
516 state = DONE_STATE;
517 }
518 } else if (curchar == '\"' || curchar == '\'') {
519 /* quoted value. Start recording the value inside the quote*/
520 startID = FB_GetPointer(fb);
521 state = GET_QUOTED_VAL_STATE;
522 PORT_Assert(quotechar == '\0')((quotechar == '\0')?((void)0):PR_Assert("quotechar == '\\0'"
,"javascript.c",522))
;
523 quotechar = curchar; /* look for matching quote type */
524 } else {
525 /* just more value */
526 }
527 break;
528 case GET_QUOTED_VAL_STATE:
529 PORT_Assert(quotechar != '\0')((quotechar != '\0')?((void)0):PR_Assert("quotechar != '\\0'"
,"javascript.c",529))
;
530 if (curchar == quotechar) {
531 /* end of quoted value */
532 curPos = FB_GetPointer(fb) - 2;
533 if (curPos >= startID) {
534 /* Grab the value */
535 FB_GetRange(fb, startID, curPos,
536 &curPair->value);
537 curPair->valueLine = linenum;
538 } else {
539 /* empty value, leave it as NULL */
540 }
541 state = GET_ATT_STATE;
542 quotechar = '\0';
543 startID = FB_GetPointer(fb);
544 } else {
545 /* more quoted value, continue */
546 }
547 break;
548 case DONE_STATE:
549 case ERR_STATE:
550 default:; /* should never get here */
551 }
552 }
553
554 if (state == DONE_STATE) {
555 /* Get the text of the tag */
556 curPos = FB_GetPointer(fb) - 1;
557 FB_GetRange(fb, startText, curPos, &ti->text);
558
559 /* Return the tag */
560 return ti;
561 }
562
563 /* Uh oh, an error. Kill the tag item*/
564 DestroyTagItem(ti);
565 return NULL((void*)0);
566}
567
568/************************************************************************
569 *
570 * D e s t r o y H T M L I t e m
571 */
572static void
573DestroyHTMLItem(HTMLItem *item)
574{
575 if (item->type == TAG_ITEM) {
576 DestroyTagItem(item->item.tag);
577 } else {
578 if (item->item.text) {
579 PR_Free(item->item.text);
580 }
581 }
582}
583
584/************************************************************************
585 *
586 * D e s t r o y T a g I t e m
587 */
588static void
589DestroyTagItem(TagItem *ti)
590{
591 AVPair *temp;
592
593 if (ti->text) {
594 PR_Free(ti->text);
595 ti->text = NULL((void*)0);
596 }
597
598 while (ti->attList) {
599 temp = ti->attList;
600 ti->attList = ti->attList->next;
601
602 if (temp->attribute) {
603 PR_Free(temp->attribute);
604 temp->attribute = NULL((void*)0);
605 }
606 if (temp->value) {
607 PR_Free(temp->value);
608 temp->value = NULL((void*)0);
609 }
610 PR_Free(temp);
611 }
612
613 PR_Free(ti);
614}
615
616/************************************************************************
617 *
618 * G e t T a g T y p e
619 */
620static TAG_TYPE
621GetTagType(char *att)
622{
623 if (!PORT_StrcasecmpPL_strcasecmp(att, "APPLET")) {
624 return APPLET_TAG;
625 }
626 if (!PORT_StrcasecmpPL_strcasecmp(att, "SCRIPT")) {
627 return SCRIPT_TAG;
628 }
629 if (!PORT_StrcasecmpPL_strcasecmp(att, "LINK")) {
630 return LINK_TAG;
631 }
632 if (!PORT_StrcasecmpPL_strcasecmp(att, "STYLE")) {
633 return STYLE_TAG;
634 }
635 return OTHER_TAG;
636}
637
638/************************************************************************
639 *
640 * F B _ C r e a t e
641 */
642static FileBuffer *
643FB_Create(PRFileDesc *fd)
644{
645 FileBuffer *fb;
646 PRInt32 amountRead;
647 PRInt32 storedOffset;
648
649 fb = (FileBuffer *)PR_Malloc(sizeof(FileBuffer));
650 fb->fd = fd;
651 storedOffset = PR_Seek(fd, 0, PR_SEEK_CUR);
652 PR_Seek(fd, 0, PR_SEEK_SET);
653 fb->startOffset = 0;
654 amountRead = PR_Read(fd, fb->buf, FILE_BUFFER_BUFSIZE512);
655 if (amountRead == -1)
656 goto loser;
657 fb->maxIndex = amountRead - 1;
658 fb->curIndex = 0;
659 fb->IsEOF = (fb->curIndex > fb->maxIndex) ? PR_TRUE1 : PR_FALSE0;
660 fb->lineNum = 1;
661
662 PR_Seek(fd, storedOffset, PR_SEEK_SET);
663 return fb;
664loser:
665 PR_Seek(fd, storedOffset, PR_SEEK_SET);
666 PR_Free(fb);
667 return NULL((void*)0);
668}
669
670/************************************************************************
671 *
672 * F B _ G e t C h a r
673 */
674static int
675FB_GetChar(FileBuffer *fb)
676{
677 PRInt32 storedOffset;
678 PRInt32 amountRead;
679 int retval = -1;
680
681 if (fb->IsEOF) {
682 return EOF(-1);
683 }
684
685 storedOffset = PR_Seek(fb->fd, 0, PR_SEEK_CUR);
686
687 retval = (unsigned char)fb->buf[fb->curIndex++];
688 if (retval == '\n')
689 fb->lineNum++;
690
691 if (fb->curIndex > fb->maxIndex) {
692 /* We're at the end of the buffer. Try to get some new data from the
693 * file */
694 fb->startOffset += fb->maxIndex + 1;
695 PR_Seek(fb->fd, fb->startOffset, PR_SEEK_SET);
696 amountRead = PR_Read(fb->fd, fb->buf, FILE_BUFFER_BUFSIZE512);
697 if (amountRead == -1)
698 goto loser;
699 fb->maxIndex = amountRead - 1;
700 fb->curIndex = 0;
701 }
702
703 fb->IsEOF = (fb->curIndex > fb->maxIndex) ? PR_TRUE1 : PR_FALSE0;
704
705loser:
706 PR_Seek(fb->fd, storedOffset, PR_SEEK_SET);
707 return retval;
708}
709
710/************************************************************************
711 *
712 * F B _ G e t L i n e N u m
713 *
714 */
715static unsigned int
716FB_GetLineNum(FileBuffer *fb)
717{
718 return fb->lineNum;
719}
720
721/************************************************************************
722 *
723 * F B _ G e t P o i n t e r
724 *
725 */
726static PRInt32
727FB_GetPointer(FileBuffer *fb)
728{
729 return fb->startOffset + fb->curIndex;
730}
731
732/************************************************************************
733 *
734 * F B _ G e t R a n g e
735 *
736 */
737static PRInt32
738FB_GetRange(FileBuffer *fb, PRInt32 start, PRInt32 end, char **buf)
739{
740 PRInt32 amountRead;
741 PRInt32 storedOffset;
742
743 *buf = PR_Malloc(end - start + 2);
744 if (*buf == NULL((void*)0)) {
745 return 0;
746 }
747
748 storedOffset = PR_Seek(fb->fd, 0, PR_SEEK_CUR);
749 PR_Seek(fb->fd, start, PR_SEEK_SET);
750 amountRead = PR_Read(fb->fd, *buf, end - start + 1);
751 PR_Seek(fb->fd, storedOffset, PR_SEEK_SET);
752 if (amountRead == -1) {
753 PR_Free(*buf);
754 *buf = NULL((void*)0);
755 return 0;
756 }
757
758 (*buf)[end - start + 1] = '\0';
759 return amountRead;
760}
761
762/************************************************************************
763 *
764 * F B _ D e s t r o y
765 *
766 */
767static void
768FB_Destroy(FileBuffer *fb)
769{
770 if (fb) {
771 PR_Free(fb);
772 }
773}
774
775/************************************************************************
776 *
777 * P r i n t T a g I t e m
778 *
779 */
780static void
781PrintTagItem(PRFileDesc *fd, TagItem *ti)
782{
783 AVPair *pair;
784
785 PR_fprintf(fd, "TAG:\n----\nType: ");
786 switch (ti->type) {
787 case APPLET_TAG:
788 PR_fprintf(fd, "applet\n");
789 break;
790 case SCRIPT_TAG:
791 PR_fprintf(fd, "script\n");
792 break;
793 case LINK_TAG:
794 PR_fprintf(fd, "link\n");
795 break;
796 case STYLE_TAG:
797 PR_fprintf(fd, "style\n");
798 break;
799 case COMMENT_TAG:
800 PR_fprintf(fd, "comment\n");
801 break;
802 case OTHER_TAG:
803 default:
804 PR_fprintf(fd, "other\n");
805 break;
806 }
807
808 PR_fprintf(fd, "Attributes:\n");
809 for (pair = ti->attList; pair; pair = pair->next) {
810 PR_fprintf(fd, "\t%s=%s\n", pair->attribute,
811 pair->value ? pair->value : "");
812 }
813 PR_fprintf(fd, "Text:%s\n", ti->text ? ti->text : "");
814
815 PR_fprintf(fd, "---End of tag---\n");
816}
817
818/************************************************************************
819 *
820 * P r i n t H T M L S t r e a m
821 *
822 */
823static void
824PrintHTMLStream(PRFileDesc *fd, HTMLItem *head)
825{
826 while (head) {
827 if (head->type == TAG_ITEM) {
828 PrintTagItem(fd, head->item.tag);
829 } else {
830 PR_fprintf(fd, "\nTEXT:\n-----\n%s\n-----\n\n", head->item.text);
831 }
832 head = head->next;
833 }
834}
835
836/************************************************************************
837 *
838 * S a v e I n l i n e S c r i p t
839 *
840 */
841static int
842SaveInlineScript(char *text, char *id, char *basedir, char *archiveDir)
843{
844 char *filename = NULL((void*)0);
845 PRFileDesc *fd = NULL((void*)0);
846 int retval = -1;
847 PRInt32 writeLen;
848 char *ilDir = NULL((void*)0);
849
850 if (!text || !id || !archiveDir) {
851 return -1;
852 }
853
854 if (dumpParse) {
855 PR_fprintf(outputFD, "SaveInlineScript: text=%s, id=%s, \n"
856 "basedir=%s, archiveDir=%s\n",
857 text, id, basedir, archiveDir);
858 }
859
860 /* Make sure the archive directory is around */
861 if (ensureExists(basedir, archiveDir) != PR_SUCCESS) {
862 PR_fprintf(errorFD,
863 "ERROR: Unable to create archive directory %s.\n", archiveDir);
864 errorCount++;
865 return -1;
866 }
867
868 /* Make sure the inline script directory is around */
869 ilDir = PR_smprintf("%s/inlineScripts", archiveDir);
870 scriptdir = "inlineScripts";
871 if (ensureExists(basedir, ilDir) != PR_SUCCESS) {
872 PR_fprintf(errorFD,
873 "ERROR: Unable to create directory %s.\n", ilDir);
874 errorCount++;
875 return -1;
876 }
877
878 filename = PR_smprintf("%s/%s/%s", basedir, ilDir, id);
879
880 /* If the file already exists, give a warning, then blow it away */
881 if (PR_Access(filename, PR_ACCESS_EXISTS) == PR_SUCCESS) {
882 PR_fprintf(errorFD,
883 "warning: file \"%s\" already exists--will overwrite.\n",
884 filename);
885 warningCount++;
886 if (rm_dash_r(filename)) {
887 PR_fprintf(errorFD, "ERROR: Unable to delete %s.\n", filename);
888 errorCount++;
889 goto finish;
890 }
891 }
892
893 /* Write text into file with name id */
894 fd = PR_Open(filename, PR_WRONLY0x02 | PR_CREATE_FILE0x08 | PR_TRUNCATE0x20, 0777);
895 if (!fd) {
896 PR_fprintf(errorFD, "ERROR: Unable to create file \"%s\".\n",
897 filename);
898 errorCount++;
899 goto finish;
900 }
901 writeLen = strlen(text);
902 if (PR_Write(fd, text, writeLen) != writeLen) {
903 PR_fprintf(errorFD, "ERROR: Unable to write to file \"%s\".\n",
904 filename);
905 errorCount++;
906 goto finish;
907 }
908
909 retval = 0;
910finish:
911 if (filename) {
912 PR_smprintf_free(filename);
913 }
914 if (ilDir) {
915 PR_smprintf_free(ilDir);
916 }
917 if (fd) {
918 PR_Close(fd);
919 }
920 return retval;
921}
922
923/************************************************************************
924 *
925 * S a v e U n n a m a b l e S c r i p t
926 *
927 */
928static int
929SaveUnnamableScript(char *text, char *basedir, char *archiveDir,
930 char *HTMLfilename)
931{
932 char *id = NULL((void*)0);
933 char *ext = NULL((void*)0);
934 char *start = NULL((void*)0);
935 int retval = -1;
936
937 if (!text || !archiveDir || !HTMLfilename) {
938 return -1;
939 }
940
941 if (dumpParse) {
942 PR_fprintf(outputFD, "SaveUnnamableScript: text=%s, basedir=%s,\n"
943 "archiveDir=%s, filename=%s\n",
944 text, basedir, archiveDir,
945 HTMLfilename);
946 }
947
948 /* Construct the filename */
949 ext = PL_strrchr(HTMLfilename, '.');
950 if (ext) {
951 *ext = '\0';
952 }
953 for (start = HTMLfilename; strpbrk(start, "/\\");
954 start = strpbrk(start, "/\\") + 1)
955 /* do nothing */;
956 if (*start == '\0')
957 start = HTMLfilename;
958 id = PR_smprintf("_%s%d", start, idOrdinal++);
959 if (ext) {
960 *ext = '.';
961 }
962
963 /* Now call SaveInlineScript to do the work */
964 retval = SaveInlineScript(text, id, basedir, archiveDir);
965
966 PR_Free(id);
967
968 return retval;
969}
970
971/************************************************************************
972 *
973 * S a v e S o u r c e
974 *
975 */
976static int
977SaveSource(char *src, char *codebase, char *basedir, char *archiveDir)
978{
979 char *from = NULL((void*)0), *to = NULL((void*)0);
980 int retval = -1;
981 char *arcDir = NULL((void*)0);
982
983 if (!src || !archiveDir) {
984 return -1;
985 }
986
987 if (dumpParse) {
988 PR_fprintf(outputFD, "SaveSource: src=%s, codebase=%s, basedir=%s,\n"
989 "archiveDir=%s\n",
990 src, codebase, basedir, archiveDir);
991 }
992
993 if (codebase) {
994 arcDir = PR_smprintf("%s/%s/%s/", basedir, codebase, archiveDir);
995 } else {
996 arcDir = PR_smprintf("%s/%s/", basedir, archiveDir);
997 }
998
999 if (codebase) {
1000 from = PR_smprintf("%s/%s/%s", basedir, codebase, src);
1001 to = PR_smprintf("%s%s", arcDir, src);
1002 } else {
1003 from = PR_smprintf("%s/%s", basedir, src);
1004 to = PR_smprintf("%s%s", arcDir, src);
1005 }
1006
1007 if (make_dirs(to, 0777)) {
1008 PR_fprintf(errorFD,
1009 "ERROR: Unable to create archive directory %s.\n", archiveDir);
1010 errorCount++;
1011 goto finish;
1012 }
1013
1014 retval = copyinto(from, to);
1015finish:
1016 if (from)
1017 PR_Free(from);
1018 if (to)
1019 PR_Free(to);
1020 if (arcDir)
1021 PR_Free(arcDir);
1022 return retval;
1023}
1024
1025/************************************************************************
1026 *
1027 * T a g T y p e T o S t r i n g
1028 *
1029 */
1030char *
1031TagTypeToString(TAG_TYPE type)
1032{
1033 switch (type) {
1034 case APPLET_TAG:
1035 return "APPLET";
1036 case SCRIPT_TAG:
1037 return "SCRIPT";
1038 case LINK_TAG:
1039 return "LINK";
1040 case STYLE_TAG:
1041 return "STYLE";
1042 default:
1043 break;
1044 }
1045 return "unknown";
1046}
1047
1048/************************************************************************
1049 *
1050 * e x t r a c t _ j s
1051 *
1052 */
1053static int
1054extract_js(char *filename)
1055{
1056 PRFileDesc *fd = NULL((void*)0);
1057 FileBuffer *fb = NULL((void*)0);
1058 HTMLItem *head = NULL((void*)0);
1059 HTMLItem *tail = NULL((void*)0);
1060 HTMLItem *curitem = NULL((void*)0);
1061 HTMLItem *styleList = NULL((void*)0);
1062 HTMLItem *styleListTail = NULL((void*)0);
1063 HTMLItem *entityList = NULL((void*)0);
1064 HTMLItem *entityListTail = NULL((void*)0);
1065 TagItem *tagp = NULL((void*)0);
1066 char *text = NULL((void*)0);
1067 char *tagerr = NULL((void*)0);
1068 char *archiveDir = NULL((void*)0);
1069 char *firstArchiveDir = NULL((void*)0);
1070 char *basedir = NULL((void*)0);
1071 PRInt32 textStart;
1072 PRInt32 curOffset;
1073 HTML_STATE state;
1074 int curchar;
1075 int retval = -1;
1076 unsigned int linenum, startLine;
1077
1078 /* Initialize the implicit ID counter for each file */
1079 idOrdinal = 0;
1080
1081 /*
1082 * First, parse the HTML into a stream of tags and text.
1083 */
1084
1085 fd = PR_Open(filename, PR_RDONLY0x01, 0);
1086 if (!fd) {
1087 PR_fprintf(errorFD, "Unable to open %s for reading.\n", filename);
1088 errorCount++;
1089 return -1;
1090 }
1091
1092 /* Construct base directory of filename. */
1093 {
1094 char *cp;
1095
1096 basedir = PL_strdup(filename);
1097
1098 /* Remove trailing slashes */
1099 while ((cp = PL_strprbrk(basedir, "/\\")) ==
1100 (basedir + strlen(basedir) - 1)) {
1101 *cp = '\0';
1102 }
1103
1104 /* Now remove everything from the last slash (which will be followed
1105 * by a filename) to the end */
1106 cp = PL_strprbrk(basedir, "/\\");
1107 if (cp) {
1108 *cp = '\0';
1109 }
1110 }
1111
1112 state = TEXT_HTML_STATE;
1113
1114 fb = FB_Create(fd);
1115
1116 textStart = 0;
1117 startLine = 0;
1118 while (linenum = FB_GetLineNum(fb), (curchar = FB_GetChar(fb)) != EOF(-1)) {
1119 switch (state) {
1120 case TEXT_HTML_STATE:
1121 if (curchar == '<') {
1122 /*
1123 * Found a tag
1124 */
1125 /* Save the text so far to a new text item */
1126 curOffset = FB_GetPointer(fb) - 2;
1127 if (curOffset >= textStart) {
1128 if (FB_GetRange(fb, textStart, curOffset,
1129 &text) !=
1130 curOffset - textStart + 1) {
1131 PR_fprintf(errorFD,
1132 "Unable to read from %s.\n",
1133 filename);
1134 errorCount++;
1135 goto loser;
1136 }
1137 /* little fudge here. If the first character on a line
1138 * is '<', meaning a new tag, the preceding text item
1139 * actually ends on the previous line. In this case
1140 * we will be saying that the text segment ends on the
1141 * next line. I don't think this matters for text items. */
1142 curitem = CreateTextItem(text, startLine,
1143 linenum);
1144 text = NULL((void*)0);
1145 if (tail == NULL((void*)0)) {
1146 head = tail = curitem;
1147 } else {
1148 tail->next = curitem;
1149 tail = curitem;
1150 }
1151 }
1152
1153 /* Process the tag */
1154 tagp = ProcessTag(fb, &tagerr);
1155 if (!tagp) {
1156 if (tagerr) {
1157 PR_fprintf(errorFD, "Error in file %s: %s\n",
1158 filename, tagerr);
1159 errorCount++;
1160 } else {
1161 PR_fprintf(errorFD,
1162 "Error in file %s, in tag starting at line %d\n",
1163 filename, linenum);
1164 errorCount++;
1165 }
1166 goto loser;
1167 }
1168 /* Add the tag to the list */
1169 curitem = CreateTagItem(tagp, linenum, FB_GetLineNum(fb));
1170 if (tail == NULL((void*)0)) {
1171 head = tail = curitem;
1172 } else {
1173 tail->next = curitem;
1174 tail = curitem;
1175 }
1176
1177 /* What's the next state */
1178 if (tagp->type == SCRIPT_TAG) {
1179 state = SCRIPT_HTML_STATE;
1180 }
1181
1182 /* Start recording text from the new offset */
1183 textStart = FB_GetPointer(fb);
1184 startLine = FB_GetLineNum(fb);
1185 } else {
1186 /* regular character. Next! */
1187 }
1188 break;
1189 case SCRIPT_HTML_STATE:
1190 if (curchar == '<') {
1191 char *cp;
1192 /*
1193 * If this is a </script> tag, then we're at the end of the
1194 * script. Otherwise, ignore
1195 */
1196 curOffset = FB_GetPointer(fb) - 1;
1197 cp = NULL((void*)0);
1198 if (FB_GetRange(fb, curOffset, curOffset + 8, &cp) != 9) {
1199 if (cp) {
1200 PR_Free(cp);
1201 cp = NULL((void*)0);
1202 }
1203 } else {
1204 /* compare the strings */
1205 if (!PORT_StrncasecmpPL_strncasecmp(cp, "</script>", 9)) {
1206 /* This is the end of the script. Record the text. */
1207 curOffset--;
1208 if (curOffset >= textStart) {
1209 if (FB_GetRange(fb, textStart, curOffset, &text) !=
1210 curOffset - textStart + 1) {
1211 PR_fprintf(errorFD, "Unable to read from %s.\n",
1212 filename);
1213 errorCount++;
1214 goto loser;
1215 }
1216 curitem = CreateTextItem(text, startLine, linenum);
1217 text = NULL((void*)0);
1218 if (tail == NULL((void*)0)) {
1219 head = tail = curitem;
1220 } else {
1221 tail->next = curitem;
1222 tail = curitem;
1223 }
1224 }
1225
1226 /* Now parse the /script tag and put it on the list */
1227 tagp = ProcessTag(fb, &tagerr);
1228 if (!tagp) {
1229 if (tagerr) {
1230 PR_fprintf(errorFD, "Error in file %s: %s\n",
1231 filename, tagerr);
1232 } else {
1233 PR_fprintf(errorFD,
1234 "Error in file %s, in tag starting at"
1235 " line %d\n",
1236 filename, linenum);
1237 }
1238 errorCount++;
1239 goto loser;
1240 }
1241 curitem = CreateTagItem(tagp, linenum,
1242 FB_GetLineNum(fb));
1243 if (tail == NULL((void*)0)) {
1244 head = tail = curitem;
1245 } else {
1246 tail->next = curitem;
1247 tail = curitem;
1248 }
1249
1250 /* go back to text state */
1251 state = TEXT_HTML_STATE;
1252
1253 textStart = FB_GetPointer(fb);
1254 startLine = FB_GetLineNum(fb);
1255 }
1256 }
1257 }
1258 break;
1259 }
1260 }
1261
1262 /* End of the file. Wrap up any remaining text */
1263 if (state == SCRIPT_HTML_STATE) {
1264 if (tail && tail->type == TAG_ITEM) {
1265 PR_fprintf(errorFD, "ERROR: <SCRIPT> tag at %s:%d is not followed "
1266 "by a </SCRIPT> tag.\n",
1267 filename, tail->startLine);
1268 } else {
1269 PR_fprintf(errorFD, "ERROR: <SCRIPT> tag in file %s is not followed"
1270 " by a </SCRIPT tag.\n",
1271 filename);
1272 }
1273 errorCount++;
1274 goto loser;
1275 }
1276 curOffset = FB_GetPointer(fb) - 1;
1277 if (curOffset >= textStart) {
1278 text = NULL((void*)0);
1279 if (FB_GetRange(fb, textStart, curOffset, &text) !=
1280 curOffset - textStart + 1) {
1281 PR_fprintf(errorFD, "Unable to read from %s.\n", filename);
1282 errorCount++;
1283 goto loser;
1284 }
1285 curitem = CreateTextItem(text, startLine, linenum);
1286 text = NULL((void*)0);
1287 if (tail == NULL((void*)0)) {
1288 head = tail = curitem;
Although the value stored to 'tail' is used in the enclosing expression, the value is never actually read from 'tail'
1289 } else {
1290 tail->next = curitem;
1291 tail = curitem;
1292 }
1293 }
1294
1295 if (dumpParse) {
1296 PrintHTMLStream(outputFD, head);
1297 }
1298
1299 /*
1300 * Now we have a stream of tags and text. Go through and deal with each.
1301 */
1302 for (curitem = head; curitem; curitem = curitem->next) {
1303 AVPair *pairp = NULL((void*)0);
1304 char *src = NULL((void*)0), *id = NULL((void*)0), *codebase = NULL((void*)0);
1305 PRBool hasEventHandler = PR_FALSE0;
1306 int i;
1307
1308 /* Reset archive directory for each tag */
1309 if (archiveDir) {
1310 PR_Free(archiveDir);
1311 archiveDir = NULL((void*)0);
1312 }
1313
1314 /* We only analyze tags */
1315 if (curitem->type != TAG_ITEM) {
1316 continue;
1317 }
1318
1319 tagp = curitem->item.tag;
1320
1321 /* go through the attributes to get information */
1322 for (pairp = tagp->attList; pairp; pairp = pairp->next) {
1323
1324 /* ARCHIVE= */
1325 if (!PL_strcasecmp(pairp->attribute, "archive")) {
1326 if (archiveDir) {
1327 /* Duplicate attribute. Print warning */
1328 PR_fprintf(errorFD,
1329 "warning: \"%s\" attribute overwrites previous attribute"
1330 " in tag starting at %s:%d.\n",
1331 pairp->attribute, filename, curitem->startLine);
1332 warningCount++;
1333 PR_Free(archiveDir);
1334 }
1335 archiveDir = PL_strdup(pairp->value);
1336
1337 /* Substiture ".arc" for ".jar" */
1338 if ((PL_strlen(archiveDir) < 4) ||
1339 PL_strcasecmp((archiveDir + strlen(archiveDir) - 4),
1340 ".jar")) {
1341 char *newArchiveDir = NULL((void*)0);
1342 PR_fprintf(errorFD,
1343 "warning: ARCHIVE attribute should end in \".jar\" in tag"
1344 " starting on %s:%d.\n",
1345 filename, curitem->startLine);
1346 warningCount++;
1347 newArchiveDir = PR_smprintf("%s.arc", archiveDir);
1348 PR_Free(archiveDir);
1349 archiveDir = newArchiveDir;
1350 } else {
1351 PL_strcpy(archiveDir + strlen(archiveDir) - 4, ".arc");
1352 }
1353
1354 /* Record the first archive. This will be used later if
1355 * the archive is not specified */
1356 if (firstArchiveDir == NULL((void*)0)) {
1357 firstArchiveDir = PL_strdup(archiveDir);
1358 }
1359 }
1360 /* CODEBASE= */
1361 else if (!PL_strcasecmp(pairp->attribute, "codebase")) {
1362 if (codebase) {
1363 /* Duplicate attribute. Print warning */
1364 PR_fprintf(errorFD,
1365 "warning: \"%s\" attribute overwrites previous attribute"
1366 " in tag staring at %s:%d.\n",
1367 pairp->attribute, filename, curitem->startLine);
1368 warningCount++;
1369 }
1370 codebase = pairp->value;
1371 }
1372 /* SRC= and HREF= */
1373 else if (!PORT_StrcasecmpPL_strcasecmp(pairp->attribute, "src") ||
1374 !PORT_StrcasecmpPL_strcasecmp(pairp->attribute, "href")) {
1375 if (src) {
1376 /* Duplicate attribute. Print warning */
1377 PR_fprintf(errorFD,
1378 "warning: \"%s\" attribute overwrites previous attribute"
1379 " in tag staring at %s:%d.\n",
1380 pairp->attribute, filename, curitem->startLine);
1381 warningCount++;
1382 }
1383 src = pairp->value;
1384 }
1385 /* CODE= */
1386 else if (!PORT_StrcasecmpPL_strcasecmp(pairp->attribute, "code")) {
1387 /*!!!XXX Change PORT to PL all over this code !!! */
1388 if (src) {
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++;
1395 }
1396 src = pairp->value;
1397
1398 /* Append a .class if one is not already present */
1399 if ((PL_strlen(src) < 6) ||
1400 PL_strcasecmp((src + PL_strlen(src) - 6), ".class")) {
1401 src = PR_smprintf("%s.class", src);
1402 /* Put this string back into the data structure so it
1403 * will be deallocated properly */
1404 PR_Free(pairp->value);
1405 pairp->value = src;
1406 }
1407 }
1408 /* ID= */
1409 else if (!PL_strcasecmp(pairp->attribute, "id")) {
1410 if (id) {
1411 /* Duplicate attribute. Print warning */
1412 PR_fprintf(errorFD,
1413 "warning: \"%s\" attribute overwrites previous attribute"
1414 " in tag staring at %s:%d.\n",
1415 pairp->attribute, filename, curitem->startLine);
1416 warningCount++;
1417 }
1418 id = pairp->value;
1419 }
1420
1421 /* STYLE= */
1422 /* style= attributes, along with JS entities, are stored into
1423 * files with dynamically generated names. The filenames are
1424 * based on the order in which the text is found in the file.
1425 * All JS entities on all lines up to and including the line
1426 * containing the end of the tag that has this style= attribute
1427 * will be processed before this style=attribute. So we need
1428 * to record the line that this _tag_ (not the attribute) ends on.
1429 */
1430 else if (!PL_strcasecmp(pairp->attribute, "style") && pairp->value) {
1431 HTMLItem *styleItem;
1432 /* Put this item on the style list */
1433 styleItem = CreateTextItem(PL_strdup(pairp->value),
1434 curitem->startLine, curitem->endLine);
1435 if (styleListTail == NULL((void*)0)) {
1436 styleList = styleListTail = styleItem;
1437 } else {
1438 styleListTail->next = styleItem;
1439 styleListTail = styleItem;
1440 }
1441 }
1442 /* Event handlers */
1443 else {
1444 for (i = 0; i < num_handlers; i++) {
1445 if (!PL_strcasecmp(event_handlers[i], pairp->attribute)) {
1446 hasEventHandler = PR_TRUE1;
1447 break;
1448 }
1449 }
1450 }
1451
1452 /* JS Entity */
1453 {
1454 char *entityStart, *entityEnd;
1455 HTMLItem *entityItem;
1456
1457 /* go through each JavaScript entity ( &{...}; ) and store it
1458 * in the entityList. The important thing is to record what
1459 * line number it's on, so we can get it in the right order
1460 * in relation to style= attributes.
1461 * Apparently, these can't flow across lines, so the start and
1462 * end line will be the same. That helps matters.
1463 */
1464 entityEnd = pairp->value;
1465 while (entityEnd &&
1466 (entityStart = PL_strstr(entityEnd, "&{")) /*}*/ != NULL((void*)0)) {
1467 entityStart += 2; /* point at beginning of actual entity */
1468 entityEnd = PL_strchr(entityStart, '}');
1469 if (entityEnd) {
1470 /* Put this item on the entity list */
1471 *entityEnd = '\0';
1472 entityItem = CreateTextItem(PL_strdup(entityStart),
1473 pairp->valueLine, pairp->valueLine);
1474 *entityEnd = /* { */ '}';
1475 if (entityListTail) {
1476 entityListTail->next = entityItem;
1477 entityListTail = entityItem;
1478 } else {
1479 entityList = entityListTail = entityItem;
1480 }
1481 }
1482 }
1483 }
1484 }
1485
1486 /* If no archive was supplied, we use the first one of the file */
1487 if (!archiveDir && firstArchiveDir) {
1488 archiveDir = PL_strdup(firstArchiveDir);
1489 }
1490
1491 /* If we have an event handler, we need to archive this tag */
1492 if (hasEventHandler) {
1493 if (!id) {
1494 PR_fprintf(errorFD,
1495 "warning: tag starting at %s:%d has event handler but"
1496 " no ID attribute. The tag will not be signed.\n",
1497 filename, curitem->startLine);
1498 warningCount++;
1499 } else if (!archiveDir) {
1500 PR_fprintf(errorFD,
1501 "warning: tag starting at %s:%d has event handler but"
1502 " no ARCHIVE attribute. The tag will not be signed.\n",
1503 filename, curitem->startLine);
1504 warningCount++;
1505 } else {
1506 if (SaveInlineScript(tagp->text, id, basedir, archiveDir)) {
1507 goto loser;
1508 }
1509 }
1510 }
1511
1512 switch (tagp->type) {
1513 case APPLET_TAG:
1514 if (!src) {
1515 PR_fprintf(errorFD,
1516 "error: APPLET tag starting on %s:%d has no CODE "
1517 "attribute.\n",
1518 filename, curitem->startLine);
1519 errorCount++;
1520 goto loser;
1521 } else if (!archiveDir) {
1522 PR_fprintf(errorFD,
1523 "error: APPLET tag starting on %s:%d has no ARCHIVE "
1524 "attribute.\n",
1525 filename, curitem->startLine);
1526 errorCount++;
1527 goto loser;
1528 } else {
1529 if (SaveSource(src, codebase, basedir, archiveDir)) {
1530 goto loser;
1531 }
1532 }
1533 break;
1534 case SCRIPT_TAG:
1535 case LINK_TAG:
1536 case STYLE_TAG:
1537 if (!archiveDir) {
1538 PR_fprintf(errorFD,
1539 "error: %s tag starting on %s:%d has no ARCHIVE "
1540 "attribute.\n",
1541 TagTypeToString(tagp->type),
1542 filename, curitem->startLine);
1543 errorCount++;
1544 goto loser;
1545 } else if (src) {
1546 if (SaveSource(src, codebase, basedir, archiveDir)) {
1547 goto loser;
1548 }
1549 } else if (id) {
1550 /* Save the next text item */
1551 if (!curitem->next || (curitem->next->type !=
1552 TEXT_ITEM)) {
1553 PR_fprintf(errorFD,
1554 "warning: %s tag starting on %s:%d is not followed"
1555 " by script text.\n",
1556 TagTypeToString(tagp->type),
1557 filename, curitem->startLine);
1558 warningCount++;
1559 /* just create empty file */
1560 if (SaveInlineScript("", id, basedir, archiveDir)) {
1561 goto loser;
1562 }
1563 } else {
1564 curitem = curitem->next;
1565 if (SaveInlineScript(curitem->item.text,
1566 id, basedir,
1567 archiveDir)) {
1568 goto loser;
1569 }
1570 }
1571 } else {
1572 /* No src or id tag--warning */
1573 PR_fprintf(errorFD,
1574 "warning: %s tag starting on %s:%d has no SRC or"
1575 " ID attributes. Will not sign.\n",
1576 TagTypeToString(tagp->type), filename, curitem->startLine);
1577 warningCount++;
1578 }
1579 break;
1580 default:
1581 /* do nothing for other tags */
1582 break;
1583 }
1584 }
1585
1586 /* Now deal with all the unnamable scripts */
1587 if (firstArchiveDir) {
1588 HTMLItem *style, *entity;
1589
1590 /* Go through the lists of JS entities and style attributes. Do them
1591 * in chronological order within a list. Pick the list with the lower
1592 * endLine. In case of a tie, entities come first.
1593 */
1594 style = styleList;
1595 entity = entityList;
1596 while (style || entity) {
1597 if (!entity || (style && (style->endLine < entity->endLine))) {
1598 /* Process style */
1599 SaveUnnamableScript(style->item.text, basedir, firstArchiveDir,
1600 filename);
1601 style = style->next;
1602 } else {
1603 /* Process entity */
1604 SaveUnnamableScript(entity->item.text, basedir, firstArchiveDir,
1605 filename);
1606 entity = entity->next;
1607 }
1608 }
1609 }
1610
1611 retval = 0;
1612loser:
1613 /* Blow away the stream */
1614 while (head) {
1615 curitem = head;
1616 head = head->next;
1617 DestroyHTMLItem(curitem);
1618 }
1619 while (styleList) {
1620 curitem = styleList;
1621 styleList = styleList->next;
1622 DestroyHTMLItem(curitem);
1623 }
1624 while (entityList) {
1625 curitem = entityList;
1626 entityList = entityList->next;
1627 DestroyHTMLItem(curitem);
1628 }
1629 if (text) {
1630 PR_Free(text);
1631 text = NULL((void*)0);
1632 }
1633 if (fb) {
1634 FB_Destroy(fb);
1635 fb = NULL((void*)0);
1636 }
1637 if (fd) {
1638 PR_Close(fd);
1639 }
1640 if (tagerr) {
1641 PR_smprintf_free(tagerr);
1642 tagerr = NULL((void*)0);
1643 }
1644 if (archiveDir) {
1645 PR_Free(archiveDir);
1646 archiveDir = NULL((void*)0);
1647 }
1648 if (firstArchiveDir) {
1649 PR_Free(firstArchiveDir);
1650 firstArchiveDir = NULL((void*)0);
1651 }
1652 if (entityListTail) {
1653 PR_Free(entityListTail);
1654 }
1655 if (basedir) {
1656 PR_Free(basedir);
1657 }
1658 return retval;
1659}
1660
1661/**********************************************************************
1662 *
1663 * e n s u r e E x i s t s
1664 *
1665 * Check for existence of indicated directory. If it doesn't exist,
1666 * it will be created.
1667 * Returns PR_SUCCESS if the directory is present, PR_FAILURE otherwise.
1668 */
1669static PRStatus
1670ensureExists(char *basepath, char *path)
1671{
1672 char fn[FNSIZE256];
1673 PRDir *dir;
1674 int c = snprintf(fn, sizeof(fn), "%s/%s", basepath, path);
1675 if (c >= sizeof(fn)) {
1676 return PR_FAILURE;
1677 }
1678
1679 /*PR_fprintf(outputFD, "Trying to open directory %s.\n", fn);*/
1680
1681 if ((dir = PR_OpenDir(fn))) {
1682 PR_CloseDir(dir);
1683 return PR_SUCCESS;
1684 }
1685 return PR_MkDir(fn, 0777);
1686}
1687
1688/***************************************************************************
1689 *
1690 * m a k e _ d i r s
1691 *
1692 * Ensure that the directory portion of the path exists. This may require
1693 * making the directory, and its parent, and its parent's parent, etc.
1694 */
1695static int
1696make_dirs(char *path, int file_perms)
1697{
1698 char *Path;
1699 char *start;
1700 char *sep;
1701 int ret = 0;
1702 PRFileInfo info;
1703
1704 if (!path) {
1705 return 0;
1706 }
1707
1708 Path = PL_strdup(path);
1709 if (!Path) {
1710 return 0;
1711 }
1712
1713 start = strpbrk(Path, "/\\");
1714 if (!start) {
1715 goto loser;
1716 }
1717 start++; /* start right after first slash */
1718
1719 /* Each time through the loop add one more directory. */
1720 while ((sep = strpbrk(start, "/\\"))) {
1721 *sep = '\0';
1722
1723 if (PR_GetFileInfo(Path, &info) != PR_SUCCESS) {
1724 /* No such dir, we have to create it */
1725 if (PR_MkDir(Path, file_perms) != PR_SUCCESS) {
1726 PR_fprintf(errorFD, "ERROR: Unable to create directory %s.\n",
1727 Path);
1728 errorCount++;
1729 ret = -1;
1730 goto loser;
1731 }
1732 } else {
1733 /* something exists by this name, make sure it's a directory */
1734 if (info.type != PR_FILE_DIRECTORY) {
1735 PR_fprintf(errorFD, "ERROR: Unable to create directory %s.\n",
1736 Path);
1737 errorCount++;
1738 ret = -1;
1739 goto loser;
1740 }
1741 }
1742
1743 start = sep + 1; /* start after the next slash */
1744 *sep = '/';
1745 }
1746
1747loser:
1748 PR_Free(Path);
1749 return ret;
1750}
1751
1752/*
1753 * c o p y i n t o
1754 *
1755 * Function to copy file "from" to path "to".
1756 *
1757 */
1758static int
1759copyinto(char *from, char *to)
1760{
1761 PRInt32 num;
1762 char buf[BUFSIZ8192];
1763 PRFileDesc *infp = NULL((void*)0), *outfp = NULL((void*)0);
1764 int retval = -1;
1765
1766 if ((infp = PR_Open(from, PR_RDONLY0x01, 0777)) == NULL((void*)0)) {
1767 PR_fprintf(errorFD, "ERROR: Unable to open \"%s\" for reading.\n",
1768 from);
1769 errorCount++;
1770 goto finish;
1771 }
1772
1773 /* If to already exists, print a warning before deleting it */
1774 if (PR_Access(to, PR_ACCESS_EXISTS) == PR_SUCCESS) {
1775 PR_fprintf(errorFD, "warning: %s already exists--will overwrite\n", to);
1776 warningCount++;
1777 if (rm_dash_r(to)) {
1778 PR_fprintf(errorFD,
1779 "ERROR: Unable to remove %s.\n", to);
1780 errorCount++;
1781 goto finish;
1782 }
1783 }
1784
1785 if ((outfp = PR_Open(to, PR_WRONLY0x02 | PR_CREATE_FILE0x08 | PR_TRUNCATE0x20, 0777)) ==
1786 NULL((void*)0)) {
1787 char *errBuf = NULL((void*)0);
1788
1789 errBuf = PR_Malloc(PR_GetErrorTextLength() + 1);
1790 PR_fprintf(errorFD, "ERROR: Unable to open \"%s\" for writing.\n", to);
1791 if (PR_GetErrorText(errBuf)) {
1792 PR_fprintf(errorFD, "Cause: %s\n", errBuf);
1793 }
1794 if (errBuf) {
1795 PR_Free(errBuf);
1796 }
1797 errorCount++;
1798 goto finish;
1799 }
1800
1801 while ((num = PR_Read(infp, buf, BUFSIZ8192)) > 0) {
1802 if (PR_Write(outfp, buf, num) != num) {
1803 PR_fprintf(errorFD, "ERROR: Error writing to %s.\n", to);
1804 errorCount++;
1805 goto finish;
1806 }
1807 }
1808
1809 retval = 0;
1810finish:
1811 if (infp)
1812 PR_Close(infp);
1813 if (outfp)
1814 PR_Close(outfp);
1815
1816 return retval;
1817}