/* -*- 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 #include "mkautocf.h" #include "xp_mem.h" /* PR_NEWZAP() */ #ifndef XP_MAC #include #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=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; char * global_url = 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= 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 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(
) * timeRange(
,
) * timeRange(
, ,
, ) * timeRange(
, , ,
, , ) */ 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; }