gecko-dev/network/main/mkautocf.c

1728 строки
46 KiB
C

/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
/*
* mkautocf.c: Proxy auto-config parser and evaluator
* Ari Luotonen
*
* Updated and Documented by Judson Valeski 11/19/1997
*/
#include "mkutils.h" /* LF */
#include "mktcp.h"
#include "netutils.h"
#include "prsystem.h"
#include "mkpadpac.h"
#include "net_xp_file.h"
#include <time.h>
#include "mkautocf.h"
#include "xp_mem.h" /* PR_NEWZAP() */
#ifndef XP_MAC
#include <sys/types.h>
#endif
#include "libi18n.h"
#include "mkgeturl.h"
#include "mkstream.h"
#include "mkgeturl.h"
#include "mkformat.h"
#include "mime.h"
#include "cvactive.h"
#include "gui.h"
#include "msgcom.h"
#include "prefapi.h"
#define WANT_ENUM_STRING_IDS
#include "allxpstr.h"
#undef WANT_ENUM_STRING_IDS
#include "xp_regexp.h"
#include "prnetdb.h"
#include "net.h"
#include "libmocha.h"
#include "jsapi.h"
#include "jscompat.h"
#include "jspubtd.h"
#include "jsautocf.h"
/* for XP_GetString() */
#include "xpgetstr.h"
#ifdef NOT
extern int XP_BAD_KEYWORD_IN_PROXY_AUTOCFG;
extern int XP_RETRY_AGAIN_PROXY;
extern int XP_RETRY_AGAIN_SOCKS;
extern int XP_RETRY_AGAIN_PROXY_OR_SOCKS;
extern int XP_PROXY_UNAVAILABLE_TRY_AGAIN;
extern int XP_ALL_PROXIES_DOWN_TRY_AGAIN;
extern int XP_ALL_SOCKS_DOWN;
extern int XP_ALL_DOWN_MIX;
extern int XP_OVERRIDE_PROXY;
extern int XP_OVERRIDE_SOCKS;
extern int XP_OVERRIDE_MIX;
extern int XP_STILL_OVERRIDE_PROXY;
extern int XP_STILL_OVERRIDE_SOCKS;
extern int XP_STILL_OVERRIDE_MIX;
extern int XP_NO_CONFIG_RECIEVED;
extern int XP_NO_CONFIG_RECEIVED_NO_FAILOVER;
extern int XP_EMPTY_CONFIG_USE_PREV;
extern int XP_BAD_CONFIG_USE_PREV;
extern int XP_BAD_CONFIG_IGNORED;
extern int XP_BAD_TYPE_USE_PREV;
extern int XP_BAD_TYPE_CONFIG_IGNORED;
extern int XP_CONF_LOAD_FAILED_IGNORED;
extern int XP_CONF_LOAD_FAILED_NO_FAILOVER;
extern int XP_CONF_LOAD_FAILED_USE_PREV;
extern int XP_EVEN_SAVED_IS_BAD;
extern int XP_CONFIG_LOAD_ABORTED;
extern int XP_CONFIG_BLAST_WARNING;
extern int XP_RECEIVING_PROXY_AUTOCFG;
extern int XP_AUTOADMIN_MISSING;
#endif
#define PACF_MIN_RETRY_AFTER 300
#define PACF_MIN_RETRY_ASK 25
#define PACF_AUTO_RETRY_AFTER 1800
#define PACF_DIRECT_INTERVAL 1200
#define PACF_RETRY_OK_AFTER 30
/* Types of retrievals: retrieve directly, thru a proxy, or using socks. */
typedef enum _PACF_Type {
PACF_TYPE_INVALID = 0,
PACF_TYPE_DIRECT,
PACF_TYPE_PROXY,
PACF_TYPE_SOCKS
} PACF_Type;
/* Info about the proxy/socks server hostname/IP address and port.
* These nodes are kept in a list so we don't have to parse the pac file
* every time to gather proxy/socks info. */
typedef struct _PACF_Node {
PACF_Type type; /* direct | proxy | socks */
char * addr;
u_long socks_addr;
short socks_port;
int retries;
time_t retrying;
time_t last_try;
time_t down_since;
} PACF_Node;
/* Private object for the proxy config loader stream. */
typedef struct _PACF_Object {
MWContext * context;
int flag;
} PACF_Object;
/* A struct for holding queued state.
*
* The state is saved when NET_GetURL() is called for the first time,
* and the proxy autoconfig retrieve has to be started (and finished)
* before the first actual document can be loaded.
*
* A pre-exit function for the proxy autoconfig URL restarts the
* original URL retrieve by calling NET_GetURL() again with the
* same parameters. */
typedef struct _PACF_QueuedState {
URL_Struct * URL_s;
FO_Present_Types output_format;
MWContext * window_id;
Net_GetUrlExitFunc* exit_routine;
} PACF_QueuedState;
typedef enum {
PACF_SECONDARY_URL,
PACF_BINDINGS
} pc_slot;
/* Declared in mkgeturl.c. NET_GetURL uses these variables to determine
* whether or not the pac file has been loaded. */
extern XP_Bool NET_FindProxyInJSC(void);
extern XP_Bool NET_GlobalAcLoaded;
extern PRBool NET_ProxyAcLoaded;
/* Private proxy auto-config variables */
PRIVATE Bool pacf_do_failover = TRUE;
PRIVATE Bool pacf_loading = FALSE;
PRIVATE Bool pacf_ok = FALSE;
PRIVATE char * pacf_url = NULL;
PRIVATE char * pacf_src_buf = NULL;
PRIVATE int pacf_src_len = 0;
PRIVATE XP_List*pacf_all_nodes = NULL;
PRIVATE time_t pacf_direct_until = (time_t)0;
PRIVATE int pacf_direct_cnt = 0;
PRIVATE PACF_QueuedState *queued_state = NULL;
PRIVATE XP_List*pacf_bad_keywords = NULL;
PRIVATE Bool pacf_find_proxy_undefined = FALSE;
/* Javascript stuff. A javascript context does the interpretation and
* compilation of the pac file. */
PRIVATE JSPropertySpec pc_props[] = {
{"secondary", PACF_SECONDARY_URL},
{"bindings", PACF_BINDINGS},
{0}
};
PRIVATE JSClass pc_class = {
"ProxyConfig", 0,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
};
/* PRIVATE JSPropertySpec no_props[1] = { { NULL } }; */
PRIVATE JSObject *proxyConfig = NULL;
PRIVATE JSObject *globalConfig = NULL;
PRIVATE JSContext *configContext = NULL;
/* The javascript methods */
MODULE_PRIVATE JSBool PR_CALLBACK proxy_weekdayRange(JSContext *mc, JSObject *obj, unsigned int argc, jsval *argv, jsval *rval);
MODULE_PRIVATE JSBool PR_CALLBACK proxy_dateRange(JSContext *mc, JSObject *obj, unsigned int argc, jsval *argv, jsval *rval);
MODULE_PRIVATE JSBool PR_CALLBACK proxy_timeRange(JSContext *mc, JSObject *obj, unsigned int argc, jsval *argv, jsval *rval);
MODULE_PRIVATE JSBool PR_CALLBACK proxy_isPlainHostName(JSContext *mc, JSObject *obj, unsigned int argc, jsval *argv, jsval *rval);
MODULE_PRIVATE JSBool PR_CALLBACK proxy_dnsDomainLevels(JSContext *mc, JSObject *obj, unsigned int argc, jsval *argv, jsval *rval);
MODULE_PRIVATE JSBool PR_CALLBACK proxy_dnsDomainIs(JSContext *mc, JSObject *obj, unsigned int argc, jsval *argv, jsval *rval);
MODULE_PRIVATE JSBool PR_CALLBACK proxy_localHostOrDomainIs(JSContext *mc, JSObject *obj, unsigned int argc, jsval *argv, jsval *rval);
MODULE_PRIVATE JSBool PR_CALLBACK proxy_isResolvable(JSContext *mc, JSObject *obj, unsigned int argc, jsval *argv, jsval *rval);
MODULE_PRIVATE JSBool PR_CALLBACK proxy_dnsResolve(JSContext *mc, JSObject *obj, unsigned int argc, jsval *argv, jsval *rval);
MODULE_PRIVATE JSBool PR_CALLBACK proxy_isInNet(JSContext *mc, JSObject *obj, unsigned int argc, jsval *argv, jsval *rval);
MODULE_PRIVATE JSBool PR_CALLBACK proxy_myIpAddress(JSContext *mc, JSObject *obj, unsigned int argc, jsval *argv, jsval *rval);
MODULE_PRIVATE JSBool PR_CALLBACK proxy_regExpMatch(JSContext *mc, JSObject *obj, unsigned int argc, jsval *argv, jsval *rval);
PRIVATE JSFunctionSpec pc_methods[] = {
{ "weekdayRange", proxy_weekdayRange, 3},
{ "dateRange", proxy_dateRange, 7},
{ "timeRange", proxy_timeRange, 7},
{ "isPlainHostName", proxy_isPlainHostName, 1},
{ "dnsDomainLevels", proxy_dnsDomainLevels, 1},
{ "dnsDomainIs", proxy_dnsDomainIs, 2},
{ "localHostOrDomainIs", proxy_localHostOrDomainIs, 2},
{ "isResolvable", proxy_isResolvable, 1},
{ "dnsResolve", proxy_dnsResolve, 1},
{ "isInNet", proxy_isInNet, 3},
{ "myIpAddress", proxy_myIpAddress, 0},
{ "regExpMatch", proxy_regExpMatch, 2},
{ "shExpMatch", proxy_regExpMatch, 2},
{ NULL, NULL, 0}
};
PRIVATE char *mkMethodString( int method ) {
char *mstr= NULL;
switch ( method ) {
case URL_GET_METHOD:
mstr = PL_strdup("GET");
break;
case URL_POST_METHOD:
mstr = PL_strdup("POST");
break;
case URL_HEAD_METHOD:
mstr = PL_strdup("HEAD");
break;
case URL_PUT_METHOD:
mstr = PL_strdup("PUT");
break;
case URL_DELETE_METHOD:
mstr = PL_strdup("DELETE");
break;
case URL_MKDIR_METHOD :
mstr = PL_strdup("MKDIR");
break;
case URL_MOVE_METHOD:
mstr = PL_strdup("MOVE");
break;
case URL_INDEX_METHOD:
mstr = PL_strdup("INDEX");
break;
case URL_GETPROPERTIES_METHOD:
mstr = PL_strdup("GETPROPERTIES");
break;
}
return mstr;
}
PRIVATE char *msg2(char *fmt, char *prm) {
char *msg = (char *)PR_Malloc(PL_strlen(fmt) + 100 +
(prm ? PL_strlen(prm) : 0));
if (msg)
sprintf(msg, fmt, prm ? prm : "-");
return msg;
}
PRIVATE Bool confirm2(MWContext *context, char *fmt, char *prm) {
Bool rv = TRUE;
char *msg = msg2(fmt, prm);
if (msg) {
rv = FE_Confirm(context, msg);
PR_Free(msg);
}
return rv;
}
PRIVATE void alert2(MWContext *context, char *fmt, char *prm) {
char *msg = msg2(fmt, prm);
if (msg) {
FE_Alert(context, msg);
PR_Free(msg);
}
}
/* Returns a node representing a proxy/SOCKS server address or
* direct access, and guarantees to return the same node if one
* was already created for that address. */
PRIVATE PACF_Node *lookup_create_node(PACF_Type type, char *addr) {
if (!pacf_all_nodes)
pacf_all_nodes = XP_ListNew();
/* Truncate at 64 characters -- gethostbyname() is evil */
if (addr && PL_strlen(addr) > 64)
addr[64] = '\0';
if (!type || (!addr && (type == PACF_TYPE_PROXY ||
type == PACF_TYPE_SOCKS)))
{
return NULL;
}
else
{
XP_List *cur = pacf_all_nodes;
PACF_Node *node;
while ((node = (PACF_Node *)XP_ListNextObject(cur)) != NULL) {
if (node->type == type &&
((!node->addr && !addr) ||
( node->addr && addr && !strcmp(node->addr, addr))))
{
return node;
}
}
node = PR_NEWZAP(PACF_Node);
if (node) {
node->type = type;
node->addr = PL_strdup(addr);
XP_ListAddObject(pacf_all_nodes, node);
}
return node;
}
}
/* Called by netlib when it notices that a proxy is down. */
PRIVATE PACF_Node * pacf_proxy_is_down(MWContext *context,
char *proxy_addr,
u_long socks_addr,
short socks_port) {
XP_List *cur = pacf_all_nodes;
PACF_Node *node;
while ((node = (PACF_Node *)XP_ListNextObject(cur)) != NULL) {
if ((proxy_addr && node->addr && !PL_strcmp(node->addr, proxy_addr)) ||
(socks_addr &&
node->socks_addr == socks_addr &&
node->socks_port == socks_port))
{
time_t now = time(NULL);
if (!node->down_since) {
node->down_since = now;
}
node->last_try = now;
node->retrying = (time_t)0;
return node;
}
}
return NULL;
}
PRIVATE Bool fill_return_values(PACF_Type type,
PACF_Node * node,
char ** ret_proxy_addr,
u_long * ret_socks_addr,
short * ret_socks_port) {
*ret_proxy_addr = NULL;
*ret_socks_addr = 0;
*ret_socks_port = 0;
if(!node)
return FALSE;
if (type == PACF_TYPE_PROXY) {
*ret_proxy_addr = node->addr;
}
else if (!node->socks_addr) {
char *p, *host = NULL;
StrAllocCopy(host, node->addr);
p = PL_strchr(host, ':');
if (p) {
*p++ = '\0';
node->socks_port = (short)atoi(p);
}
else {
node->socks_port = 1080;
}
if (isdigit(*host)) {
/*
* Some systems return a number instead of a struct
*/
node->socks_addr = inet_addr(host);
}
else {
PRStatus rv;
PRHostEnt *hp;
PRHostEnt hpbuf;
char dbbuf[PR_NETDB_BUF_SIZE];
NET_InGetHostByName++; /* global semaphore */
rv = PR_GetHostByName(host, dbbuf, sizeof(dbbuf), &hpbuf);
hp = (rv == PR_SUCCESS ? &hpbuf : NULL);
NET_InGetHostByName--; /* global semaphore */
if (!hp)
return FALSE; /* Fail? */
memcpy(&node->socks_addr, hp->h_addr, hp->h_length);
}
}
*ret_socks_addr = node->socks_addr;
*ret_socks_port = node->socks_port;
return TRUE;
}
/* NET_SetNoProxyFailover
* Sets a flag that indicates that proxy failover should not
* be done. This is used by the Enterprise Kit code, where
* the customer might configure the client to use specific
* proxies. In these cases, failover can cause different
* proxies, or no proxies to be used. */
PUBLIC void
NET_SetNoProxyFailover(void) {
pacf_do_failover = FALSE;
}
PUBLIC PRBool
NET_LoadingPac(void) {
return pacf_loading;
}
#ifdef MOZILLA_CLIENT
/*
* NET_GetNoProxyFailover
*/
MODULE_PRIVATE Bool
NET_GetNoProxyFailover(void) {
return ( pacf_do_failover == FALSE );
}
#endif /* MOZILLA_CLIENT */
/* Called by netlib to get a single string of "host:port" format,
* given the string of possibilities returned by the Mocha script.
*
* This function will return an address to a proxy that is (to its
* knowledge) up and running. Netlib can later inform this module
* using the function pacf_proxy_is_down() that a proxy is down
* and should not be called for a few minutes.
*
* Returns FALSE if everything has failed, and an error should be
* displayed to the user.
*
* Returns TRUE if there is hope.
* If *ret is NULL, a direct connection should be attempted.
* If *ret is not null, it is the proxy address to use. */
MODULE_PRIVATE Bool
pacf_get_proxy_addr(MWContext *context, char *list, char **ret_proxy_addr,
u_long *ret_socks_addr, short *ret_socks_port) {
Bool rv = FALSE;
char *my_copy, *cur, *p, *addr;
PACF_Type type = PACF_TYPE_INVALID;
PACF_Node *node;
PACF_Node *sm_ptr = NULL;
PACF_Node *went_down = NULL;
time_t now = time(NULL);
time_t smallest = (time_t)0;
int proxy_cnt = 0;
int socks_cnt = 0;
int retry_now = 0;
if (!list || !*list || !ret_proxy_addr || !ret_socks_addr || !ret_socks_port)
return FALSE;
if (*ret_proxy_addr || *ret_socks_addr) {
/*
* We get called here because this proxy/SOCKS server was down.
*/
went_down = pacf_proxy_is_down(context, *ret_proxy_addr,
*ret_socks_addr, *ret_socks_port);
}
*ret_proxy_addr = NULL;
*ret_socks_addr = 0;
*ret_socks_port = 0;
cur = my_copy = PL_strdup(list);
TRACEMSG(("Getting proxy addr from config list: %s", list));
do {
p = PL_strchr(cur, ';');
if (p) {
do {
*p++ = '\0';
} while (*p && NET_IS_SPACE(*p));
}
for (addr=cur; *addr && !NET_IS_SPACE(*addr); addr++)
;
if (*addr) {
do {
*addr++ = '\0';
} while (*addr && NET_IS_SPACE(*addr));
}
type = ((!PL_strcasecmp(cur, "DIRECT")) ? PACF_TYPE_DIRECT :
(!PL_strcasecmp(cur, "PROXY")) ? PACF_TYPE_PROXY :
(!PL_strcasecmp(cur, "SOCKS")) ? PACF_TYPE_SOCKS :
PACF_TYPE_INVALID );
if (type == PACF_TYPE_DIRECT) /* don't use a proxy */
{
rv = TRUE;
break; /* done */
}
else if (type == PACF_TYPE_PROXY || /* use a proxy or... */
type == PACF_TYPE_SOCKS ) /* use SOCKS */
{
if (type == PACF_TYPE_PROXY)
proxy_cnt++;
else
socks_cnt++;
node = lookup_create_node(type, addr);
if (*addr && node) {
if (node->down_since && node->retrying &&
now - node->retrying > PACF_RETRY_OK_AFTER)
{
node->down_since = node->retrying = (time_t)0;
}
if (!node->down_since)
{
if (fill_return_values(type, node,
ret_proxy_addr,
ret_socks_addr,
ret_socks_port))
{
rv = TRUE;
break;
}
}
else if (now - node->last_try >
(node->retries + 1) * PACF_AUTO_RETRY_AFTER)
{
node->retries++;
if (fill_return_values(type, node,
ret_proxy_addr,
ret_socks_addr,
ret_socks_port))
{
node->retrying = now;
rv = TRUE;
break;
}
}
else if (node != went_down &&
(!smallest || smallest > node->last_try))
{
smallest = node->last_try;
sm_ptr = node;
}
}
}
else /* invalid entry */
{
char *key = NULL;
if (!pacf_bad_keywords)
{
pacf_bad_keywords = XP_ListNew();
}
else
{
XP_List *ptr = pacf_bad_keywords;
while ((key = (char *)XP_ListNextObject(ptr)) != NULL) {
if (!strcmp(key, cur))
break;
}
}
if (!key && XP_ListCount(pacf_bad_keywords) < 3)
{
key = PL_strdup(cur);
XP_ListAddObject(pacf_bad_keywords, key);
alert2(context, XP_GetString(XP_BAD_KEYWORD_IN_PROXY_AUTOCFG), cur);
}
}
cur = p;
} while (cur && *cur);
PR_Free(my_copy);
if (rv)
return TRUE;
/* If we didn't return just above here, and we're using a pad pac file,
* something went wrong with it so disable it for this session and return
* true so we go direct. */
if(NET_UsingPadPac()) {
net_UsePadPac(FALSE);
return TRUE;
}
if (pacf_do_failover && pacf_direct_until) {
if (now < pacf_direct_until)
{
return TRUE;
}
else if (!FE_Confirm(context,
!socks_cnt ? XP_GetString(XP_RETRY_AGAIN_PROXY) :
!proxy_cnt ? XP_GetString(XP_RETRY_AGAIN_SOCKS) : XP_GetString(XP_RETRY_AGAIN_PROXY_OR_SOCKS)))
{
pacf_direct_until = now + (++pacf_direct_cnt) * PACF_DIRECT_INTERVAL;
return TRUE;
}
else
{
pacf_direct_until = (time_t)0;
retry_now = 1;
}
}
if (smallest &&
(retry_now ||
now - smallest > PACF_MIN_RETRY_AFTER ||
(sm_ptr && !sm_ptr->retrying &&
now - smallest > PACF_MIN_RETRY_ASK &&
confirm2(context,
(!socks_cnt && proxy_cnt==1 ? XP_GetString(XP_PROXY_UNAVAILABLE_TRY_AGAIN) :
!socks_cnt && proxy_cnt >1 ? XP_GetString(XP_ALL_PROXIES_DOWN_TRY_AGAIN) :
!proxy_cnt ? XP_GetString(XP_ALL_SOCKS_DOWN) :
XP_GetString(XP_ALL_DOWN_MIX)),
sm_ptr->addr))) &&
fill_return_values(type, sm_ptr,
ret_proxy_addr, ret_socks_addr, ret_socks_port))
{
sm_ptr->retrying = now;
sm_ptr->retries++;
return TRUE;
}
else if (pacf_do_failover && FE_Confirm(context,
!pacf_direct_cnt ?
(!socks_cnt ? XP_GetString(XP_OVERRIDE_PROXY) :
!proxy_cnt ? XP_GetString(XP_OVERRIDE_SOCKS) : XP_GetString(XP_OVERRIDE_MIX)) :
(!socks_cnt ? XP_GetString(XP_STILL_OVERRIDE_PROXY) :
!proxy_cnt ? XP_GetString(XP_STILL_OVERRIDE_SOCKS) : XP_GetString(XP_STILL_OVERRIDE_SOCKS))))
{
pacf_direct_until = now + (++pacf_direct_cnt) * PACF_DIRECT_INTERVAL;
return TRUE;
}
else
{
return FALSE;
}
}
/* Saves out the proxy autoconfig file to disk, in case the server
* is down the next time.
*
* Returns 0 on success, -1 on failure. */
PRIVATE int pacf_save_config(void) {
XP_File fp;
int32 len = 0;
if (!pacf_do_failover || !pacf_src_buf || !*pacf_src_buf || pacf_src_len <= 0)
return -1;
if(!(fp = NET_XP_FileOpen("", xpProxyConfig, XP_FILE_WRITE)))
return -1;
len = NET_XP_FileWrite(pacf_src_buf, pacf_src_len, fp);
NET_XP_FileClose(fp);
if (len != pacf_src_len)
return -1;
return 0;
}
/* Reads the proxy autoconfig file from disk.
* This is called if the config server is not responding.
*
* returns 0 on success -1 on failure. */
PRIVATE int pacf_read_config(void) {
XP_StatStruct st;
XP_File fp;
if (NET_XP_Stat("", &st, xpProxyConfig) == -1)
return -1;
if (!(fp = NET_XP_FileOpen("", xpProxyConfig, XP_FILE_READ)))
return -1;
pacf_src_len = st.st_size;
pacf_src_buf = (char *)PR_Malloc(pacf_src_len + 1);
if (!pacf_src_buf) {
NET_XP_FileClose(fp);
pacf_src_len = 0;
return -1;
}
if ((pacf_src_len = NET_XP_FileRead(pacf_src_buf, pacf_src_len, fp)) > 0)
{
pacf_src_buf[pacf_src_len] = '\0';
}
else
{
PR_Free(pacf_src_buf);
pacf_src_buf = NULL;
pacf_src_len = 0;
}
NET_XP_FileClose(fp);
return 0;
}
/* Private stream object methods for receiving the proxy autoconfig file. */
PRIVATE int pacf_write(NET_StreamClass *stream, CONST char *buf, int32 len) {
PACF_Object *obj;
obj=stream->data_object;
if (len > 0) {
if (!pacf_src_buf)
pacf_src_buf = (char*)PR_Malloc(len + 1);
else
pacf_src_buf = (char*)PR_Realloc(pacf_src_buf,
pacf_src_len + len + 1);
if (!pacf_src_buf) { /* Out of memory */
pacf_src_len = 0;
return MK_DATA_LOADED;
}
memcpy(pacf_src_buf + pacf_src_len, buf, len);
pacf_src_len += len;
pacf_src_buf[pacf_src_len] = '\0';
}
return MK_DATA_LOADED;
}
PRIVATE unsigned int pacf_write_ready(NET_StreamClass *stream) {
return MAX_WRITE_READY;
}
PUBLIC XP_Bool
NET_InitPacfContext(void)
{
XP_Bool first_time = TRUE;
if ( first_time == FALSE ) return TRUE;
if ( (PREF_OK != PREF_GetConfigContext(&configContext))
||(PREF_OK != PREF_GetGlobalConfigObject(&globalConfig)) )
return FALSE;
JS_BeginRequest(configContext);
proxyConfig = JS_DefineObject(configContext, globalConfig,
"ProxyConfig",
&pc_class,
NULL,
JSPROP_ENUMERATE);
if (proxyConfig) {
if (!JS_DefineProperties(configContext,
proxyConfig,
pc_props)) {
JS_EndRequest(configContext);
return FALSE;
}
if (!JS_DefineFunctions(configContext,
proxyConfig,
pc_methods)) {
JS_EndRequest(configContext);
return FALSE;
}
}
JS_DefineObject(configContext, proxyConfig,
"bindings",
&pc_class,
NULL,
0);
JS_EndRequest(configContext);
first_time = FALSE;
return TRUE;
}
PRIVATE void pacf_complete(NET_StreamClass *stream) {
PACF_Object *obj=stream->data_object;
jsval result;
XP_StatStruct st;
JSBool ok;
retry:
if (!obj->flag || obj->flag == 2) {
pacf_loading = FALSE;
if ( !NET_InitPacfContext() ) return;
}
if (!pacf_src_buf) {
if ( pacf_do_failover == FALSE && !NET_UsingPadPac() ) {
/* Don't failover to using no proxies */
FE_Alert(obj->context, XP_GetString(XP_NO_CONFIG_RECEIVED_NO_FAILOVER));
}
else if (obj->flag != 2 && (NET_XP_Stat("", &st, xpProxyConfig) == -1) && !NET_UsingPadPac())
{
FE_Alert(obj->context, XP_GetString(XP_NO_CONFIG_RECIEVED));
}
else
{
if(NET_UsingPadPac()) {
pacf_read_config();
obj->flag = 1;
goto retry;
} else if (obj->flag == 2 ||
confirm2(obj->context, XP_GetString(XP_EMPTY_CONFIG_USE_PREV), pacf_url)) {
pacf_read_config();
obj->flag = 1;
goto retry;
}
}
pacf_ok = FALSE;
goto out;
}
JS_BeginRequest(configContext);
ok = JS_EvaluateScript(configContext, proxyConfig,
pacf_src_buf, pacf_src_len, pacf_url, 0,
&result);
JS_EndRequest(configContext);
if (!ok) {
/* Something went wrong with the js evaluation. If we're using a
* proxy auto-discovery pac file, don't use it again. */
if(NET_UsingPadPac()) {
net_UsePadPac(FALSE);
goto out;
}
if (obj->flag) {
FE_Alert(obj->context, XP_GetString(XP_EVEN_SAVED_IS_BAD));
} else if (NET_XP_Stat("", &st, xpProxyConfig) == -1) {
alert2(obj->context, XP_GetString(XP_BAD_CONFIG_IGNORED), pacf_url);
} else if (confirm2(obj->context, XP_GetString(XP_BAD_CONFIG_USE_PREV), pacf_url)) {
PR_Free(pacf_src_buf);
pacf_src_buf = NULL;
pacf_src_len = 0;
pacf_read_config();
obj->flag = 1; /* Uh (this is a horrid hack) */
goto retry;
}
pacf_ok = FALSE;
} else {
pacf_ok = TRUE;
if (!obj->flag) {
pacf_save_config();
}
}
PR_Free(obj);
out:
;
}
PRIVATE void pacf_abort(NET_StreamClass *stream, int status) {
PACF_Object *obj=stream->data_object;
pacf_loading = FALSE;
FE_Alert(obj->context, XP_GetString(XP_GLOBAL_CONFIG_LOAD_ABORTED));
PR_Free(obj);
}
/* A stream constructor function for application/x-ns-proxy-autoconfig.
* This is used by cvmime.c; it's registered as the stream converter for
* pac files. */
MODULE_PRIVATE NET_StreamClass *
NET_ProxyAutoConfig(int fmt, void *data_obj, URL_Struct *URL_s,
MWContext *w) {
PACF_Object *obj;
NET_StreamClass *stream;
if (!pacf_loading) {
/*
* The Navigator didn't start this config retrieve
* intentionally. Discarding the config.
*/
if(!URL_s)
return NULL;
alert2(w, XP_GetString(XP_CONFIG_BLAST_WARNING), URL_s->address);
return NULL;
}
else {
NET_Progress(w, XP_GetString( XP_RECEIVING_PROXY_AUTOCFG ) );
}
if (pacf_src_buf) {
PR_Free(pacf_src_buf);
pacf_src_buf = NULL;
pacf_src_len = 0;
}
if (!(stream = PR_NEWZAP(NET_StreamClass)))
return NULL;
if (!(obj = PR_NEWZAP(PACF_Object))) {
PR_Free(stream);
return NULL;
}
obj->context = w;
stream->data_object = obj;
stream->name = "ProxyAutoConfigLoader";
stream->complete = (MKStreamCompleteFunc) pacf_complete;
stream->abort = (MKStreamAbortFunc) pacf_abort;
stream->put_block = (MKStreamWriteFunc) pacf_write;
stream->is_write_ready = (MKStreamWriteReadyFunc)pacf_write_ready;
stream->window_id = w;
return stream;
}
/* calls NET_GetURL() to get the url that had been queued up behind the
* actual pac file url. */
static void pacf_restart_queued(URL_Struct *URL_s, int status,
MWContext *window_id) {
XP_StatStruct st;
/* We want to fail silently if we're loading a pad pac and fail,
* so fall through to bottom and load the queued url. */
if (pacf_loading && !(NET_UsingPadPac() || foundPADPAC) ) {
if ( pacf_do_failover == FALSE ) {
/* Don't failover to using no proxies */
FE_Alert(window_id, XP_GetString(XP_CONF_LOAD_FAILED_NO_FAILOVER));
}
else if (NET_XP_Stat("", &st, xpProxyConfig) == -1)
{
if (status < 0)
FE_Alert(window_id, XP_GetString(XP_CONF_LOAD_FAILED_IGNORED));
else {
alert2(window_id, XP_GetString(XP_BAD_TYPE_CONFIG_IGNORED), pacf_url);
}
}
else if (status == MK_INTERRUPTED)
{
/* silently fail and retry later */
NET_ProxyAcLoaded = FALSE;
NET_GlobalAcLoaded = FALSE;
}
else if (status < 0
? FE_Confirm(window_id, XP_GetString(XP_CONF_LOAD_FAILED_USE_PREV))
: confirm2(window_id, XP_GetString(XP_BAD_TYPE_USE_PREV), pacf_url))
{
PACF_Object *obj = PR_NEWZAP(PACF_Object);
NET_StreamClass stream;
stream.data_object=obj;
if (obj) {
obj->context = window_id;
obj->flag = 2;
pacf_complete(&stream);
}
}
pacf_loading = FALSE;
} else {
/* Call this only if everything succeeded -- otherwise
the netlib has already freed the URL_s, and there isn't
a clean fix, so for now we'll just forget the URL load
if proxy config load went foul.
*/
if(queued_state) {
NET_GetURL(queued_state->URL_s,
queued_state->output_format,
queued_state->window_id,
queued_state->exit_routine);
}
}
PR_FREEIF(queued_state);
queued_state = NULL;
}
/* Called by mkgeturl.c to originally retrieve, and re-retrieve
* the proxy autoconfig file.
*
* autoconfig_url is the URL pointing to the autoconfig.
*
* The rest of the parameters are what was passed to NET_GetURL(),
* and when the autoconfig load finishes NET_GetURL() will be called
* with those exact same parameters.
*
* This is because the proxy config is loaded when NET_GetURL() is
* called for the very first time, and the actual request must be put
* on hold when the proxy config is being loaded.
*
* When called from the explicit proxy config RE-load function
* NET_ReloadProxyConfig, the four last parameters are all zero,
* and no request gets restarted. */
MODULE_PRIVATE int NET_LoadProxyConfig(char *autoconf_url,
URL_Struct *URL_s,
FO_Present_Types output_format,
MWContext *window_id,
Net_GetUrlExitFunc *exit_routine) {
URL_Struct *my_url_s = NULL;
if (!autoconf_url)
return -1;
if (!PL_strcmp(autoconf_url,"BAD-NOAUTOADMNLIB")) {
FE_Alert(window_id, XP_GetString( XP_AUTOADMIN_MISSING ));
return -1;
}
StrAllocCopy(pacf_url, autoconf_url);
my_url_s = NET_CreateURLStruct(autoconf_url, NET_SUPER_RELOAD);
if (exit_routine) {
queued_state = PR_NEWZAP(PACF_QueuedState);
if(!queued_state)
return -1;
queued_state->URL_s = URL_s;
queued_state->output_format = output_format;
queued_state->window_id = window_id;
queued_state->exit_routine = exit_routine;
if(!my_url_s)
return -1;
my_url_s->pre_exit_fn = pacf_restart_queued;
}
/* Alert the proxy autoconfig module that config is coming */
pacf_loading = TRUE;
pacf_find_proxy_undefined = FALSE;
return NET_GetURL(my_url_s, FO_PRESENT, window_id, NULL);
}
/* Returns a pointer to a NULL-terminted buffer which contains
* the text of the proxy autoconfig file. */
PUBLIC char * NET_GetProxyConfigSource(void) {
return pacf_src_buf;
}
/* Given a URL, returns the proxy that should be used.
* The proxy is determined by the proxy autoconfig file,
* which is a JavaScript routine. */
MODULE_PRIVATE char *pacf_find_proxies_for_url(MWContext *context,
URL_Struct *URL_s ) {
jsval rv;
char *buf = NULL;
char *host = NULL;
char *p, *q, *r;
int i, len = 0;
char *safe_url = NULL;
char *orig_url = NULL;
char *method = NULL;
char *bad_url = NULL;
char *result = NULL;
JSBool ok;
if(!URL_s)
return NULL;
orig_url=URL_s->address;
method=mkMethodString(URL_s->method);
/* If proxy failover is not allowed, and we weren't
* able to autoload the proxy, return a string that
* pacf_get_proxy_addr will always fail with. */
if ( !pacf_do_failover && !pacf_loading && !pacf_ok && !NET_FindProxyInJSC()) {
return "";
}
if ( !NET_FindProxyInJSC() && (!orig_url || !pacf_ok || pacf_loading || pacf_find_proxy_undefined))
return NULL;
if (!(bad_url = PL_strdup(orig_url)))
goto out;
len = NET_UnEscapeCnt(bad_url);
if (!(safe_url = PR_Malloc(2 * len + 1))) /* worst case */
goto out;
p = bad_url;
q = safe_url;
for(i=0; i<len; i++, p++) {
switch (*p) {
case '\n':
*q++ = '\\';
*q++ = 'n';
break;
case '\r':
*q++ = '\\';
*q++ = 'r';
break;
case '\0':
*q++ = '\\';
*q++ = '0';
break;
case '"':
*q++ = '\\';
*q++ = '"';
break;
case '\\':
*q++ = '\\';
*q++ = '\\';
break;
default:
*q++ = *p;
}
}
*q = '\0';
len = (int)(q - safe_url);
if (!(host = PR_Malloc(len + 1)))
goto out;
if (!(buf = PR_Malloc(len*2 + 50)))
goto out;
host[0] = '\0';
p = PL_strstr(safe_url, "://");
if (p) {
p += 3;
q = PL_strchr(p, '/');
if (q)
*q = '\0';
r = PL_strchr(p, '@');
if (r)
p = r + 1;
PL_strcpy(host, p);
if (q)
*q = '/';
p = PL_strchr(host, ':');
if (p)
*p = '\0';
}
if ( NET_FindProxyInJSC() ) {
XP_SPRINTF(buf, "ProxyConfig.FindProxyForURL(\"%s\",\"%s\",\"%s\")", safe_url, host,
method ? method : "" );
} else {
XP_SPRINTF(buf, "FindProxyForURL(\"%s\",\"%s\",\"%s\")", safe_url, host,
method ? method : "" );
}
JS_BeginRequest(configContext);
if (!JS_AddRoot(configContext, &rv)) {
JS_EndRequest(configContext);
goto out;
}
if ( NET_FindProxyInJSC() ) {
ok = JS_EvaluateScript(configContext, globalConfig,
buf, strlen(buf), 0, 0, &rv);
} else {
ok = JS_EvaluateScript(configContext, proxyConfig,
buf, strlen(buf), 0, 0, &rv);
}
if (ok) {
if (JSVAL_IS_STRING(rv)) {
const char *name =
JS_GetStringBytes(JSVAL_TO_STRING(rv));
if (*name)
result = PL_strdup(name);
}
}
JS_RemoveRoot(configContext, &rv);
JS_EndRequest(configContext);
out:
FREEIF(method);
FREEIF(buf);
FREEIF(host);
FREEIF(safe_url);
FREEIF(bad_url);
return result;
}
/* Utility functions to be called from Javascript (aka Mocha).
* These are the actual implementations of the javascript fucntions that
* are in the javascript pac file.
*
* Get the number of DNS domain levels (number of dots):
*
* dnsDomainLevels(host)
*
*
* Host/URL based conditions:
*
* isPlainHostName(host)
* dnsDomainIs(host, name)
* localHostOrDomainIs(host, name)
* isResolvable(host)
* regExpMatch(host or URL, regexp)
*
*
* Date/time based conditions (limits inclusive):
*
* weekdayRange(wkday)
* weekdayRange(wkday, wkday)
*
* dateRange(day)
* dateRange(day, day)
* dateRange(mon)
* dateRange(mon, mon)
* dateRange(year)
* dateRange(year, year)
* dateRange(day, mon, day, mon)
* dateRange(mon, year, mon, year)
* dateRange(day, mon, year, day, mon, year)
*
* timeRange(hour)
* timeRange(hour, hour)
* timeRange(hour, min, hour, min)
* timeRange(hour, min, sec, hour, min, sec)
*
*
*
*/
/* Returns true if the hostname doesn't contain a dot
* (is a plain hostname, not an FQDN).
*
* Just a string operation, doesn't consult DNS. */
MODULE_PRIVATE JSBool PR_CALLBACK
proxy_isPlainHostName(JSContext *mc, JSObject *obj, unsigned int argc,
jsval *argv, jsval *rval) {
if (argc >= 1 && JSVAL_IS_STRING(argv[0])) {
const char *h = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
if (h && !PL_strchr(h, '.')) {
*rval = JSVAL_TRUE;
return JS_TRUE;
}
}
*rval = JSVAL_FALSE;
return JS_TRUE;
}
/* Returns the number of domain levels in the hostname
* (the number of dots, actually).
*
* Just a string operation, doesn't consult DNS. */
MODULE_PRIVATE JSBool PR_CALLBACK
proxy_dnsDomainLevels(JSContext *mc, JSObject *obj, unsigned int argc,
jsval *argv, jsval *rval) {
int i = 0;
if (argc >= 1 && JSVAL_IS_STRING(argv[0])) {
const char *h = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
if (h) {
while (*h) {
if (*h == '.')
i++;
h++;
}
}
}
*rval = INT_TO_JSVAL(i);
return JS_TRUE;
}
/* Checks if the hostname contains the given domain. */
MODULE_PRIVATE JSBool PR_CALLBACK
proxy_dnsDomainIs(JSContext *mc, JSObject *obj, unsigned int argc,
jsval *argv, jsval *rval) {
if (argc >= 2 && JSVAL_IS_STRING(argv[0]) && JSVAL_IS_STRING(argv[1])) {
const char *h = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
const char *p = JS_GetStringBytes(JSVAL_TO_STRING(argv[1]));
int len1, len2;
if (h && p && (len1 = PL_strlen(h)) >= (len2 = PL_strlen(p)) &&
(!PL_strcasecmp(h + len1 - len2, p))) {
*rval = JSVAL_TRUE;
return JS_TRUE;
}
}
*rval = JSVAL_FALSE;
return JS_TRUE;
}
/* Returns true if the hostname matches exactly the given
* pattern hostname, or if the hostname is just a local hostname
* and it matches the hostname in the pattern FQDN hostname. */
MODULE_PRIVATE JSBool PR_CALLBACK
proxy_localHostOrDomainIs(JSContext *mc, JSObject *obj, unsigned int argc,
jsval *argv, jsval *rval) {
if (argc >= 2 && JSVAL_IS_STRING(argv[0]) && JSVAL_IS_STRING(argv[1])) {
const char *h = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
const char *p = JS_GetStringBytes(JSVAL_TO_STRING(argv[1]));
char *hp, *pp;
if (h && p) {
hp = PL_strchr(h, '.');
pp = PL_strchr(p, '.');
if (hp || !pp) {
if (!PL_strcasecmp(h,p)) {
*rval = JSVAL_TRUE;
return JS_TRUE;
}
}
else if (!PL_strncasecmp(h, p, pp - p)) {
*rval = JSVAL_TRUE;
return JS_TRUE;
}
}
}
*rval = JSVAL_FALSE;
return JS_TRUE;
}
/* Attempts to resolve the DNS name, and returns TRUE if resolvable. */
MODULE_PRIVATE JSBool PR_CALLBACK
proxy_isResolvable(JSContext *mc, JSObject *obj, unsigned int argc,
jsval *argv, jsval *rval) {
if (argc >= 1 && JSVAL_IS_STRING(argv[0])) {
const char *h = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
PRHostEnt *hp = NULL;
PRStatus rv;
PRHostEnt hpbuf;
char dbbuf[PR_NETDB_BUF_SIZE];
if (h) {
char *safe = PL_strdup(h);
if (PL_strlen(safe) > 64)
safe[64] = '\0';
rv = PR_GetHostByName(safe, dbbuf, sizeof(dbbuf), &hpbuf);
hp = (rv == PR_SUCCESS ? &hpbuf : NULL);
PR_Free(safe);
}
if (hp) {
TRACEMSG(("~~~~~~~~~~~ isResolvable(%s) returns TRUE\n", h));
TRACEMSG(("~~~~~~~~~~~ hp = %p, hp->h_name = %s\n", hp, hp->h_name ? hp->h_name : "(null)"));
*rval = JSVAL_TRUE;
return JS_TRUE;
}
}
TRACEMSG(("~~~~~~~~~~~ isResolvable(%s) returns FALSE\n", argv[0]));
*rval = JSVAL_FALSE;
return JS_TRUE;
}
/* Resolves a DNS name, and returns the IP address string.
*
* Maintains a private cache for the last resolved address (so this
* function can be called multiple times with the same host argument
* without doing actual DNS queries every time). */
PRIVATE char *proxy_dns_resolve(const char *host) {
static char *cache_host = NULL;
static char *cache_ip = NULL;
PRStatus rv;
PRHostEnt *hp = NULL;
PRHostEnt hpbuf;
char dbbuf[PR_NETDB_BUF_SIZE];
if (host) {
const char *p;
char *safe = NULL;
XP_Bool is_numeric_ip = TRUE;
for(p=host; *p; p++) {
if (!NET_IS_DIGIT(*p) && *p != '.') {
is_numeric_ip = FALSE;
break;
}
}
if (is_numeric_ip) {
return PL_strdup(host);
}
if (cache_host && cache_ip && !PL_strcmp(cache_host, host)) {
return PL_strdup(cache_ip);
}
safe = PL_strdup(host);
if (safe) {
if (PL_strlen(safe) > 64)
safe[64] = '\0';
rv = PR_GetHostByName(safe, dbbuf, sizeof(dbbuf), &hpbuf);
hp = (rv == PR_SUCCESS ? &hpbuf : NULL);
PR_Free(safe);
}
if (hp) {
char *ip = NULL;
struct in_addr in;
memcpy(&in.s_addr, hp->h_addr, hp->h_length);
ip = inet_ntoa(in);
if (ip) {
StrAllocCopy(cache_host, host);
StrAllocCopy(cache_ip, ip);
return PL_strdup(ip);
}
}
}
return NULL;
}
MODULE_PRIVATE JSBool PR_CALLBACK
proxy_dnsResolve(JSContext *mc, JSObject *obj, unsigned int argc,
jsval *argv, jsval *rval) {
if (argc >= 1 && JSVAL_IS_STRING(argv[0])) {
const char *host = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
char *ip = proxy_dns_resolve(host);
if (ip) {
JSString * str = JS_NewString(mc, ip, strlen(ip));
if (!str) {
PR_Free(ip);
return JS_FALSE;
}
*rval = STRING_TO_JSVAL(str);
return JS_TRUE;
}
}
*rval = JSVAL_NULL;
return JS_TRUE;
}
/* Returns the IP address of the host machine as a string. */
MODULE_PRIVATE JSBool PR_CALLBACK
proxy_myIpAddress(JSContext *mc, JSObject *obj, unsigned int argc,
jsval *argv, jsval *rval) {
static XP_Bool initialized = FALSE;
static char *my_address = NULL;
if (!initialized) {
char name[100];
initialized = TRUE;
if (PR_GetSystemInfo(PR_SI_HOSTNAME, name, sizeof(name)) == PR_SUCCESS) {
my_address = proxy_dns_resolve(name);
}
}
TRACEMSG(("~~~~~~~~~~~~~~~~~~ myIpAddress() returns %s\n", my_address ? my_address : "(null)"));
if (my_address) {
JSString *str;
str = JS_NewStringCopyZ(mc, my_address);
if (!str)
return JS_FALSE;
*rval = STRING_TO_JSVAL(str);
return JS_TRUE;
}
*rval = JSVAL_NULL;
return JS_TRUE;
}
/* Determines if the host IP address belongs to the given network.
* Uses SOCKS style address pattern and mask:
*
* isInNet(hostname, "111.222.33.0", "255.255.255.0"); */
PRIVATE unsigned long convert_addr(const char *ip) {
char *p, *q, *buf = NULL;
int i;
unsigned char b[4];
unsigned long addr = 0L;
p = buf = PL_strdup(ip);
if (ip && p) {
for(i=0; p && i<4; i++) {
q = PL_strchr(p, '.');
if (q) {
*q = '\0';
}
b[i] = XP_ATOI(p) & 0xff;
if (q) {
p = q+1;
}
}
addr = (((unsigned long)b[0] << 24) |
((unsigned long)b[1] << 16) |
((unsigned long)b[2] << 8) |
((unsigned long)b[3]));
PR_Free(buf);
}
return htonl(addr);
}
MODULE_PRIVATE JSBool PR_CALLBACK
proxy_isInNet(JSContext *mc, JSObject *obj, unsigned int argc,
jsval *argv, jsval *rval) {
if (argc >= 3 &&
JSVAL_IS_STRING(argv[0]) &&
JSVAL_IS_STRING(argv[1]) &&
JSVAL_IS_STRING(argv[2]))
{
const char *ipstr = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
char *ip = proxy_dns_resolve(ipstr);
const char *patstr = JS_GetStringBytes(JSVAL_TO_STRING(argv[1]));
const char *maskstr = JS_GetStringBytes(JSVAL_TO_STRING(argv[2]));
if (ip) {
unsigned long host = convert_addr(ip);
unsigned long pat = convert_addr(patstr);
unsigned long mask = convert_addr(maskstr);
PR_Free(ip);
if ((mask & host) == (mask & pat)) {
TRACEMSG(("~~~~~~~~~~~~ isInNet(%s(%s), %s, %s) returns TRUE\n",
ipstr, ip, patstr, maskstr));
*rval = JSVAL_TRUE;
return JS_TRUE;
}
}
}
TRACEMSG(("~~~~~~~~~~~~ isInNet() returns FALSE\n"));
*rval = JSVAL_FALSE;
return JS_TRUE;
}
/* Does a regular expression match between the host/URL and the pattern.
*
* Returns true on match. */
MODULE_PRIVATE JSBool PR_CALLBACK
proxy_regExpMatch(JSContext *mc, JSObject *obj, unsigned int argc,
jsval *argv, jsval *rval) {
if (argc >= 2 && JSVAL_IS_STRING(argv[0]) && JSVAL_IS_STRING(argv[1])) {
const char *url = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
const char *pat = JS_GetStringBytes(JSVAL_TO_STRING(argv[1]));
if (url && pat && XP_RegExpValid((char *) pat) && /* XXX */
!XP_RegExpMatch((char *) url, (char *) pat, TRUE)) {/* XXX */
*rval = JSVAL_TRUE;
return JS_TRUE;
}
}
*rval = JSVAL_FALSE;
return JS_TRUE;
}
PRIVATE
struct tm * get_struct_tm(JSContext *mc,
unsigned int *argc, jsval *argv) {
time_t now = time(NULL);
if (*argc > 0 && JSVAL_IS_STRING(argv[*argc-1])) {
const char *laststr = JS_GetStringBytes(JSVAL_TO_STRING(argv[*argc-1]));
if (!PL_strcasecmp(laststr, "GMT")) {
(*argc)--;
return gmtime(&now);
}
}
return localtime(&now);
}
char *weekdays = "SUNMONTUEWEDTHUFRISAT";
char *monnames = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC";
PRIVATE int get_no(const char *nam, char *arr) {
char *p = PL_strcasestr(arr, nam);
return p ? (((int)(p - arr)) / 3) : -1;
}
PRIVATE int get_em(JSContext *mc, int argc, jsval *argv,
int *d, int *m, int *y) {
int i=0;
*m = *d = *y = -1;
for (i=0; i<argc; i++) {
if (JSVAL_IS_STRING(argv[i])) {
*m = get_no(JS_GetStringBytes(JSVAL_TO_STRING(argv[i])), monnames);
}
else if (JSVAL_IS_NUMBER(argv[i])) {
if (JSVAL_TO_INT(argv[i]) > 1900)
*y = JSVAL_TO_INT(argv[i]) - 1900;
else
*d = JSVAL_TO_INT(argv[i]);
}
else {
assert(0);
}
}
return ((*d > -1 ? 1 : 0) +
(*m > -1 ? 1 : 0) +
(*y > -1 ? 1 : 0));
}
PRIVATE int are_different(JSContext *mc, jsval *argv) {
int d1, d2, m1, m2, y1, y2;
get_em(mc, 1, argv, &d1, &m1, &y1);
get_em(mc, 1, &argv[1], &d2, &m2, &y2);
return ((d1 == -1 || d2 == -1) &&
(m1 == -1 || m2 == -1) &&
(y1 == -1 || y2 == -1));
}
PRIVATE long get_rel(int sel_d, int sel_m, int sel_y, int d, int m, int y) {
return ((sel_d > -1 ? d : 0) +
(sel_m > -1 ? m : 0) * 31 +
(sel_y > -1 ? y : 0) * 372);
}
PRIVATE int cmp_properly(int sel_y, long rel_lo, long rel, long rel_hi) {
if (sel_y || rel_lo < rel_hi)
return rel_lo <= rel && rel <= rel_hi;
else
return rel_lo <= rel || rel <= rel_hi;
}
MODULE_PRIVATE JSBool PR_CALLBACK
proxy_dateRange(JSContext *mc, JSObject *obj, unsigned int argc,
jsval *argv, jsval *rval) {
int d1, d2, m1, m2, y1, y2;
struct tm *tms = get_struct_tm(mc, &argc, argv);
d1 = d2 = m1 = m2 = y1 = y2 = -1;
if (argc == 1 || argc == 3 || (argc==2 && are_different(mc, argv)))
{
*rval = ((get_em(mc, argc, argv, &d1, &m1, &y1) &&
(d1 == -1 || d1 == tms->tm_mday) &&
(m1 == -1 || m1 == tms->tm_mon) &&
(y1 == -1 || y1 == tms->tm_year))
? JSVAL_TRUE : JSVAL_FALSE);
}
else
{
*rval = (((get_em(mc, argc/2, argv, &d1, &m1, &y1) ==
get_em(mc, argc/2, &argv[argc/2], &d2, &m2, &y2)) &&
cmp_properly(y1,
get_rel(d1, m1, y1, d1, m1, y1),
get_rel(d1, m1, y1, tms->tm_mday, tms->tm_mon, tms->tm_year),
get_rel(d1, m1, y1, d2, m2, y2)))
? JSVAL_TRUE : JSVAL_FALSE);
}
return JS_TRUE;
}
MODULE_PRIVATE JSBool PR_CALLBACK
proxy_weekdayRange(JSContext *mc, JSObject *obj, unsigned int argc,
jsval *argv, jsval *rval) {
struct tm *tms = get_struct_tm(mc, &argc, argv);
int i=0, j=0;
if (argc >= 1)
i = get_no(JS_GetStringBytes(JSVAL_TO_STRING(argv[0])), weekdays);
if (argc >= 2)
j = get_no(JS_GetStringBytes(JSVAL_TO_STRING(argv[1])), weekdays);
*rval = (((argc == 1 && i == tms->tm_wday) ||
(argc == 2 && ((i <= j && (i <= tms->tm_wday && j >= tms->tm_wday)) ||
(i > j && (i <= tms->tm_wday || j >= tms->tm_wday)))))
? JSVAL_TRUE : JSVAL_FALSE);
return JS_TRUE;
}
/* timeRange(<hr>)
* timeRange(<hr>, <hr>)
* timeRange(<hr>, <min>, <hr>, <min>)
* timeRange(<hr>, <min>, <sec>, <hr>, <min>, <sec>) */
MODULE_PRIVATE JSBool PR_CALLBACK
proxy_timeRange(JSContext *mc, JSObject *obj, unsigned int argc,
jsval *argv, jsval *rval) {
int32 secondsA=0, secondsB=0, secondsC=0;
struct tm *tms = get_struct_tm(mc, &argc, argv);
if(argc == 1) {
secondsA = JSVAL_TO_INT(argv[0])*60*60;
secondsB = tms->tm_hour*60*60;
*rval = (secondsA == secondsB) ? JSVAL_TRUE : JSVAL_FALSE;
return JS_TRUE;
}
if(argc == 2) {
secondsA = JSVAL_TO_INT(argv[0])*60*60;
secondsB = tms->tm_hour*60*60;
secondsC = JSVAL_TO_INT(argv[1])*60*60;
}
if(argc == 4) {
secondsA = JSVAL_TO_INT(argv[0])*60*60 + JSVAL_TO_INT(argv[1])*60;
secondsB = tms->tm_hour*60*60 + tms->tm_min*60;
secondsC = JSVAL_TO_INT(argv[2])*60*60 + JSVAL_TO_INT(argv[3])*60;
}
if(argc == 6) {
secondsA = JSVAL_TO_INT(argv[0])*60*60 + JSVAL_TO_INT(argv[1])*60 + JSVAL_TO_INT(argv[2]);
secondsB = tms->tm_hour*60*60 + tms->tm_min*60 + tms->tm_sec;
secondsC = JSVAL_TO_INT(argv[3])*60*60 + JSVAL_TO_INT(argv[4])*60 + JSVAL_TO_INT(argv[5]);
}
*rval = (secondsA <= secondsB && secondsB < secondsC) ? JSVAL_TRUE : JSVAL_FALSE;
return JS_TRUE;
}