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