mirror of
https://github.com/rn10950/RetroZilla.git
synced 2024-11-09 09:20:15 +01:00
1868 lines
45 KiB
C
1868 lines
45 KiB
C
|
/* ***** BEGIN LICENSE BLOCK *****
|
||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||
|
*
|
||
|
* 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 the Initial Developer are Copyright (C) 1994-2000
|
||
|
* the Initial Developer. All Rights Reserved.
|
||
|
*
|
||
|
* Contributor(s):
|
||
|
*
|
||
|
* Alternatively, the contents of this file may be used under the terms of
|
||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||
|
* of those above. If you wish to allow use of your version of this file only
|
||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||
|
* use your version of this file under the terms of the MPL, indicate your
|
||
|
* decision by deleting the provisions above and replace them with the notice
|
||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||
|
* the provisions above, a recipient may use your version of this file under
|
||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||
|
*
|
||
|
* ***** END LICENSE BLOCK ***** */
|
||
|
|
||
|
#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(int 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 = '\0';
|
||
|
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;
|
||
|
PORT_Assert(quotechar == '\0');
|
||
|
quotechar = curchar; /* look for matching quote type */
|
||
|
} else {
|
||
|
/* just more value */
|
||
|
}
|
||
|
break;
|
||
|
case GET_QUOTED_VAL_STATE:
|
||
|
PORT_Assert(quotechar != '\0');
|
||
|
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;
|
||
|
quotechar = '\0';
|
||
|
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 = (unsigned char) 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)
|
||
|
/* do nothing */;
|
||
|
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;
|
||
|
HTMLItem * head = NULL;
|
||
|
HTMLItem * tail = NULL;
|
||
|
HTMLItem * curitem = NULL;
|
||
|
HTMLItem * styleList = NULL;
|
||
|
HTMLItem * styleListTail = NULL;
|
||
|
HTMLItem * entityList = NULL;
|
||
|
HTMLItem * entityListTail = NULL;
|
||
|
TagItem * tagp = NULL;
|
||
|
char *text = NULL;
|
||
|
char *tagerr = NULL;
|
||
|
char *archiveDir = NULL;
|
||
|
char *firstArchiveDir = NULL;
|
||
|
char *basedir = NULL;
|
||
|
PRInt32 textStart;
|
||
|
PRInt32 curOffset;
|
||
|
HTML_STATE state;
|
||
|
int curchar;
|
||
|
int retval = -1;
|
||
|
unsigned int linenum, startLine;
|
||
|
|
||
|
/* 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;
|
||
|
}
|
||
|
|
||
|
|