netscape-revival
lib/libnet/mkautocf.c
/*
* mkautocf.c: Proxy auto-config parser and evaluator
*
*
*
*
*
* Ari Luotonen
* Copyright (c) 1995 Netscape Communications Corporation
*
*/
#include "mkutils.h" /* LF */
#include <time.h>
#include "mkautocf.h"
#include "xp_mem.h" /* XP_NEW_ZAP() */
#ifndef XP_MAC
#include <sys/types.h>
#endif
#include "libi18n.h"
#include "mkstream.h"
#include "mkgeturl.h"
#include "cvextcon.h"
#include "mkformat.h"
#include "il.h"
#include "mime.h"
#include "cvactive.h"
#include "gui.h"
#include "msgcom.h"
#include "xp_reg.h"
#if defined(XP_UNIX) || defined(XP_WIN32)
#include "prnetdb.h"
#else
#define PRHostEnt struct hostent
#endif
#ifdef MOCHA
#include "libmocha.h"
# ifdef XP_MAC
# include "mochaapi.h"
# else
# include "mocha/mochaapi.h"
# endif
#endif /* MOCHA */
/* for XP_GetString() */
#include "xpgetstr.h"
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;
#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
#if 0
#define PACF_MIN_RETRY_AFTER 300
#define PACF_MIN_RETRY_ASK 15
#define PACF_AUTO_RETRY_AFTER 60
#define PACF_DIRECT_INTERVAL 30
#define PACF_RETRY_OK_AFTER 30
#endif
#define EMPTY_CONFIG_USE_PREV XP_GetString( XP_EMPTY_CONFIG_USE_PREV )
#define BAD_CONFIG_USE_PREV XP_GetString( XP_BAD_CONFIG_USE_PREV )
#define BAD_CONFIG_IGNORED XP_GetString( XP_BAD_CONFIG_IGNORED )
#define BAD_TYPE_USE_PREV XP_GetString( XP_BAD_TYPE_USE_PREV )
#define BAD_TYPE_CONFIG_IGNORED XP_GetString( XP_BAD_TYPE_CONFIG_IGNORED )
#define CONF_LOAD_FAILED_IGNORED XP_GetString( XP_CONF_LOAD_FAILED_IGNORED )
#define CONF_LOAD_FAILED_NO_FAILOVER XP_GetString( XP_CONF_LOAD_FAILED_NO_FAILOVER )
#define CONF_LOAD_FAILED_USE_PREV XP_GetString( XP_CONF_LOAD_FAILED_USE_PREV )
#define BAD_KEYWORD_MSG XP_GetString( XP_BAD_KEYWORD_IN_PROXY_AUTOCFG )
#define NO_CONFIG_RECEIVED XP_GetString( XP_NO_CONFIG_RECIEVED )
#define NO_CONFIG_RECEIVED_NO_FAILOVER XP_GetString( XP_NO_CONFIG_RECEIVED_NO_FAILOVER )
#define EVEN_SAVED_IS_BAD XP_GetString( XP_EVEN_SAVED_IS_BAD )
#define CONFIG_LOAD_ABORTED XP_GetString( XP_CONFIG_LOAD_ABORTED )
#define CONFIG_BLAST_WARNING XP_GetString( XP_CONFIG_BLAST_WARNING )
#define RETRY_AGAIN_PROXY XP_GetString( XP_RETRY_AGAIN_PROXY )
#define RETRY_AGAIN_SOCKS XP_GetString( XP_RETRY_AGAIN_SOCKS )
#define RETRY_AGAIN_MIX XP_GetString( XP_RETRY_AGAIN_PROXY_OR_SOCKS )
#define ONE_DOWN_PROXY XP_GetString( XP_PROXY_UNAVAILABLE_TRY_AGAIN )
#define ALL_DOWN_PROXY XP_GetString( XP_ALL_PROXIES_DOWN_TRY_AGAIN )
#define ALL_DOWN_SOCKS XP_GetString( XP_ALL_SOCKS_DOWN )
#define ALL_DOWN_MIX XP_GetString( XP_ALL_DOWN_MIX )
#define OVERRIDE_PROXY XP_GetString( XP_OVERRIDE_PROXY )
#define OVERRIDE_SOCKS XP_GetString( XP_OVERRIDE_SOCKS )
#define OVERRIDE_MIX XP_GetString( XP_OVERRIDE_MIX )
#define STILL_OVERRIDE_PROXY XP_GetString( XP_STILL_OVERRIDE_PROXY )
#define STILL_OVERRIDE_SOCKS XP_GetString( XP_STILL_OVERRIDE_SOCKS )
#define STILL_OVERRIDE_MIX XP_GetString( XP_STILL_OVERRIDE_SOCKS )
/*
* Types of retrievals:
* retrieve directly, thru a proxy, or using SOKCS.
*
*/
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.
*
*/
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;
/*
* Private proxy auto-config variables
*
*/
#ifdef MOCHA
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 MochaPropertySpec pc_props[] = {
{"secondary", PACF_SECONDARY_URL},
{"bindings", PACF_BINDINGS},
{0}
};
PRIVATE MochaClass pc_class = {
"ProxyConfig",
MOCHA_PropertyStub, MOCHA_PropertyStub, MOCHA_ListPropStub,
MOCHA_ResolveStub, MOCHA_ConvertStub, MOCHA_FinalizeStub
};
PRIVATE MochaPropertySpec no_props[1] = { { NULL } };
PRIVATE MochaObject *proxyConfig = NULL;
PRIVATE MochaBoolean proxy_weekdayRange(MochaContext *mc, MochaObject *obj, unsigned int argc, MochaDatum *argv, MochaDatum *rval);
PRIVATE MochaBoolean proxy_dateRange(MochaContext *mc, MochaObject *obj, unsigned int argc, MochaDatum *argv, MochaDatum *rval);
PRIVATE MochaBoolean proxy_timeRange(MochaContext *mc, MochaObject *obj, unsigned int argc, MochaDatum *argv, MochaDatum *rval);
PRIVATE MochaBoolean proxy_isPlainHostName(MochaContext *mc, MochaObject *obj, unsigned int argc, MochaDatum *argv, MochaDatum *rval);
PRIVATE MochaBoolean proxy_dnsDomainLevels(MochaContext *mc, MochaObject *obj, unsigned int argc, MochaDatum *argv, MochaDatum *rval);
PRIVATE MochaBoolean proxy_dnsDomainIs(MochaContext *mc, MochaObject *obj, unsigned int argc, MochaDatum *argv, MochaDatum *rval);
PRIVATE MochaBoolean proxy_localHostOrDomainIs(MochaContext *mc, MochaObject *obj, unsigned int argc, MochaDatum *argv, MochaDatum *rval);
PRIVATE MochaBoolean proxy_isResolvable(MochaContext *mc, MochaObject *obj, unsigned int argc, MochaDatum *argv, MochaDatum *rval);
PRIVATE MochaBoolean proxy_dnsResolve(MochaContext *mc, MochaObject *obj, unsigned int argc, MochaDatum *argv, MochaDatum *rval);
PRIVATE MochaBoolean proxy_isInNet(MochaContext *mc, MochaObject *obj, unsigned int argc, MochaDatum *argv, MochaDatum *rval);
PRIVATE MochaBoolean proxy_myIpAddress(MochaContext *mc, MochaObject *obj, unsigned int argc, MochaDatum *argv, MochaDatum *rval);
PRIVATE MochaBoolean proxy_regExpMatch(MochaContext *mc, MochaObject *obj, unsigned int argc, MochaDatum *argv, MochaDatum *rval);
PRIVATE MochaFunctionSpec 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 *msg2(char *fmt, char *prm)
{
char *msg = (char *)XP_ALLOC(XP_STRLEN(fmt) + 100 +
(prm ? XP_STRLEN(prm) : 0));
if (msg)
XP_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);
XP_FREE(msg);
}
return rv;
}
PRIVATE void alert2(MWContext *context, char *fmt, char *prm)
{
char *msg = msg2(fmt, prm);
if (msg) {
FE_Alert(context, msg);
XP_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 && XP_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))) {
if (node->type == type &&
((!node->addr && !addr) ||
( node->addr && addr && !strcmp(node->addr, addr))))
{
return node;
}
}
node = XP_NEW_ZAP(PACF_Node);
if (node) {
node->type = type;
node->addr = XP_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))) {
if ((proxy_addr && node->addr && !XP_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) {
#if 0
alert2(context, "Proxy at %s is down.", addr);
#endif
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 (type == PACF_TYPE_PROXY) {
*ret_proxy_addr = node->addr;
}
else if (!node->socks_addr) {
char *p, *host = NULL;
StrAllocCopy(host, node->addr);
if ((p = XP_STRCHR(host, ':'))) {
*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 {
struct hostent *hp;
#if defined(XP_UNIX) || defined(XP_WIN32)
struct hostent hpbuf;
char dbbuf[PR_NETDB_BUF_SIZE];
#endif
NET_InGetHostByName++; /* global semaphore */
#if defined(XP_UNIX) || defined(XP_WIN32)
hp = PR_gethostbyname(host, &hpbuf, dbbuf, sizeof(dbbuf), 0);
#else
hp = gethostbyname(host);
#endif
NET_InGetHostByName--; /* global semaphore */
if (!hp)
return FALSE; /* Fail? */
XP_MEMCPY(&node->socks_addr, hp->h_addr, hp->h_length);
}
}
*ret_socks_addr = node->socks_addr;
*ret_socks_port = node->socks_port;
return TRUE;
}
#endif /* MOCHA */
/*
* 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)
{
#ifdef MOCHA
pacf_do_failover = FALSE;
#endif
}
#ifdef MOZILLA_CLIENT
/*
* NET_GetNoProxyFailover
*/
MODULE_PRIVATE Bool
NET_GetNoProxyFailover(void)
{
#ifdef MOCHA /* added by jwz */
return ( pacf_do_failover == FALSE );
#else /* !MOCHA -- added by jwz */
return FALSE;
#endif /* !MOCHA -- added by jwz */
}
#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)
{
#ifdef MOCHA
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 = XP_STRDUP(list);
TRACEMSG(("Getting proxy addr from config list: %s", list));
do {
if ((p = XP_STRCHR(cur, ';'))) {
do {
*p++ = '\0';
} while (*p && XP_IS_SPACE(*p));
}
for(addr=cur; *addr && !XP_IS_SPACE(*addr); addr++);
if (*addr) {
do {
*addr++ = '\0';
} while (*addr && XP_IS_SPACE(*addr));
}
type = ((!strcasecomp(cur, "DIRECT")) ? PACF_TYPE_DIRECT :
(!strcasecomp(cur, "PROXY")) ? PACF_TYPE_PROXY :
(!strcasecomp(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++;
if (*addr && (node = lookup_create_node(type, addr))) {
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))) {
if (!strcmp(key, cur))
break;
}
}
if (!key && XP_ListCount(pacf_bad_keywords) < 3)
{
key = XP_STRDUP(cur);
XP_ListAddObject(pacf_bad_keywords, key);
alert2(context, BAD_KEYWORD_MSG, cur);
}
}
cur = p;
} while (cur && *cur);
XP_FREE(my_copy);
if (rv)
{
return TRUE;
}
if (pacf_do_failover && pacf_direct_until)
{
if (now < pacf_direct_until)
{
return TRUE;
}
else if (!FE_Confirm(context,
!socks_cnt ? RETRY_AGAIN_PROXY :
!proxy_cnt ? RETRY_AGAIN_SOCKS : RETRY_AGAIN_MIX))
{
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->retrying &&
now - smallest > PACF_MIN_RETRY_ASK &&
confirm2(context,
(!socks_cnt && proxy_cnt==1 ? ONE_DOWN_PROXY :
!socks_cnt && proxy_cnt >1 ? ALL_DOWN_PROXY :
!proxy_cnt ? ALL_DOWN_SOCKS :
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 ? OVERRIDE_PROXY :
!proxy_cnt ? OVERRIDE_SOCKS : OVERRIDE_MIX) :
(!socks_cnt ? STILL_OVERRIDE_PROXY :
!proxy_cnt ? STILL_OVERRIDE_SOCKS : STILL_OVERRIDE_MIX)))
{
pacf_direct_until = now + (++pacf_direct_cnt) * PACF_DIRECT_INTERVAL;
return TRUE;
}
else
{
return FALSE;
}
#else
return FALSE;
#endif /* MOCHA */
}
#ifdef MOCHA
/*
* 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;
if (!pacf_do_failover || !pacf_src_buf || !*pacf_src_buf || pacf_src_len <= 0)
return -1;
if(!(fp = XP_FileOpen("", xpProxyConfig, XP_FILE_WRITE)))
return -1;
XP_FileWrite(pacf_src_buf, pacf_src_len, fp);
XP_FileClose(fp);
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 (XP_Stat("", &st, xpProxyConfig) == -1)
return -1;
if (!(fp = XP_FileOpen("", xpProxyConfig, XP_FILE_READ)))
return -1;
pacf_src_len = st.st_size;
pacf_src_buf = (char *)XP_ALLOC(pacf_src_len + 1);
if (!pacf_src_buf) {
XP_FileClose(fp);
pacf_src_len = 0;
return -1;
}
if ((pacf_src_len = XP_FileRead(pacf_src_buf, pacf_src_len, fp)) > 0)
{
pacf_src_buf[pacf_src_len] = '\0';
}
else
{
XP_FREE(pacf_src_buf);
pacf_src_buf = NULL;
pacf_src_len = 0;
}
XP_FileClose(fp);
return 0;
}
/*
* Private stream object methods for receiving the proxy autoconfig
* file.
*
*
*/
PRIVATE int pacf_write(PACF_Object *obj, CONST char *buf, int32 len)
{
if (len > 0) {
if (!pacf_src_buf)
pacf_src_buf = (char*)XP_ALLOC(len + 1);
else
pacf_src_buf = (char*)XP_REALLOC(pacf_src_buf,
pacf_src_len + len + 1);
if (!pacf_src_buf) { /* Out of memory */
pacf_src_len = 0;
return MK_DATA_LOADED;
}
XP_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(PACF_Object *obj)
{
return MAX_WRITE_READY;
}
PRIVATE void pacf_complete(PACF_Object *obj)
{
MochaDecoder *decoder;
MochaDatum result;
XP_StatStruct st;
MochaBoolean ok;
decoder = LM_GetGlobalMochaDecoder(obj->context);
if (!decoder) {
if (pacf_src_buf) {
XP_FREE(pacf_src_buf);
pacf_src_buf = NULL;
pacf_src_len = 0;
}
pacf_ok = FALSE;
return;
}
retry:
if (!obj->flag || obj->flag == 2) {
pacf_loading = FALSE;
proxyConfig =
MOCHA_DefineNewObject(decoder->mocha_context, decoder->window_object,
"ProxyConfig", &pc_class, NULL, NULL, 0,
pc_props, pc_methods);
MOCHA_DefineNewObject(decoder->mocha_context, proxyConfig, "bindings",
&pc_class, NULL, NULL, 0, no_props, 0);
}
if (!pacf_src_buf) {
if ( pacf_do_failover == FALSE ) {
/* Don't failover to using no proxies */
FE_Alert(obj->context, NO_CONFIG_RECEIVED_NO_FAILOVER);
}
else if (obj->flag != 2 && XP_Stat("", &st, xpProxyConfig) == -1)
{
FE_Alert(obj->context, NO_CONFIG_RECEIVED);
}
else if (obj->flag == 2 ||
confirm2(obj->context, EMPTY_CONFIG_USE_PREV, pacf_url))
{
pacf_read_config();
obj->flag = 1;
goto retry;
}
pacf_ok = FALSE;
goto out;
}
ok = MOCHA_EvaluateBuffer(decoder->mocha_context, proxyConfig,
pacf_src_buf, pacf_src_len, pacf_url, 0,
&result);
if (!ok) {
if (obj->flag)
{
FE_Alert(obj->context, EVEN_SAVED_IS_BAD);
}
else if (XP_Stat("", &st, xpProxyConfig) == -1)
{
alert2(obj->context, BAD_CONFIG_IGNORED, pacf_url);
}
else if (confirm2(obj->context, BAD_CONFIG_USE_PREV, pacf_url))
{
XP_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 {
MOCHA_DropRef(decoder->mocha_context, &result);
pacf_ok = TRUE;
if (!obj->flag) {
#if 0
FE_Alert(obj->context,
"Proxy auto-configuration\nsuccessfully loaded.");
#endif
pacf_save_config();
}
}
XP_FREE(obj);
out:
LM_PutGlobalMochaDecoder(decoder);
}
PRIVATE void pacf_abort(PACF_Object *obj, int status)
{
pacf_loading = FALSE;
FE_Alert(obj->context, CONFIG_LOAD_ABORTED);
XP_FREE(obj);
}
#endif /* MOCHA */
/*
* A stream constructor function for application/x-ns-proxy-autoconfig.
*
*
*
*/
MODULE_PRIVATE NET_StreamClass *
NET_ProxyAutoConfig(int fmt, void *data_obj, URL_Struct *URL_s, MWContext *w)
{
#ifdef MOCHA
PACF_Object *obj;
NET_StreamClass *stream;
if (!pacf_loading) {
/*
* The Navigator didn't start this config retrieve
* intentionally. Discarding the config.
*/
alert2(w, CONFIG_BLAST_WARNING, URL_s->address);
return NULL;
}
else {
NET_Progress(w, XP_GetString( XP_RECEIVING_PROXY_AUTOCFG ) );
}
if (pacf_src_buf) {
XP_FREE(pacf_src_buf);
pacf_src_buf = NULL;
pacf_src_len = 0;
}
if (!(stream = XP_NEW_ZAP(NET_StreamClass)))
return NULL;
if (!(obj = XP_NEW_ZAP(PACF_Object))) {
XP_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;
#else /* ! MOCHA */
return NULL;
#endif /* ! MOCHA */
}
#ifdef MOCHA
void pacf_exit_routine(URL_Struct *URL_s, int status, MWContext *window_id)
{}
static void pacf_restart_queued(URL_Struct *URL_s, int status,
MWContext *window_id)
{
XP_StatStruct st;
if (pacf_loading) {
if ( pacf_do_failover == FALSE ) {
/* Don't failover to using no proxies */
FE_Alert(window_id, CONF_LOAD_FAILED_NO_FAILOVER);
}
else if (XP_Stat("", &st, xpProxyConfig) == -1)
{
if (status < 0)
FE_Alert(window_id, CONF_LOAD_FAILED_IGNORED);
else
alert2(window_id, BAD_TYPE_CONFIG_IGNORED, pacf_url);
}
else if (status == MK_INTERRUPTED)
{
/* silently fail and retry later */
NET_ProxyAcLoaded = FALSE;
}
else if (status < 0
? FE_Confirm(window_id, CONF_LOAD_FAILED_USE_PREV)
: confirm2(window_id, BAD_TYPE_USE_PREV, pacf_url))
{
PACF_Object *obj = XP_NEW_ZAP(PACF_Object);
if (obj) {
obj->context = window_id;
obj->flag = 2;
pacf_complete(obj);
}
}
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.
*/
NET_GetURL(queued_state->URL_s,
queued_state->output_format,
queued_state->window_id,
queued_state->exit_routine);
}
XP_FREE(queued_state);
queued_state = NULL;
}
#endif /* MOCHA */
/*
* 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)
{
#ifdef MOCHA
URL_Struct *my_url_s = NULL;
if (!autoconf_url)
return -1;
StrAllocCopy(pacf_url, autoconf_url);
my_url_s = NET_CreateURLStruct(autoconf_url, NET_SUPER_RELOAD);
if (exit_routine) {
queued_state = XP_NEW_ZAP(PACF_QueuedState);
queued_state->URL_s = URL_s;
queued_state->output_format = output_format;
queued_state->window_id = window_id;
queued_state->exit_routine = exit_routine;
my_url_s->pre_exit_fn = pacf_restart_queued;
}
/* Alert the proxy autoconfig module that config is coming */
pacf_loading = TRUE;
#if 0
FE_Alert(window_id, "Loading proxy autoconfig...");
NET_Progress(window_id, "Loading proxy auto-config...");
#endif
return FE_GetURL(window_id, my_url_s);
#else
return -1;
#endif
}
/*
* Returns a pointer to a NULL-terminted buffer which contains
* the text of the proxy autoconfig file.
*
*
*/
PUBLIC char * NET_GetProxyConfigSource(void)
{
#ifdef MOCHA
return pacf_src_buf;
#else
return 0;
#endif
}
/*
* pacf_find_proxies_for_url
* 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, char *orig_url)
{
#ifdef MOCHA
MochaDecoder *decoder;
MochaDatum rv;
char *buf = NULL;
char *host = NULL;
char *p, *q, *r;
int i, len = 0;
char *safe_url = NULL;
char *bad_url = NULL;
char *result = NULL;
MochaBoolean ok;
/*
* 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 ) {
return "";
}
if (!orig_url || !pacf_ok || pacf_loading)
return NULL;
decoder = LM_GetGlobalMochaDecoder(context);
if (!decoder)
return NULL;
if (!(bad_url = XP_STRDUP(orig_url)))
goto out;
len = NET_UnEscapeCnt(bad_url);
if (!(safe_url = XP_ALLOC(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 = XP_ALLOC(len + 1)))
goto out;
if (!(buf = XP_ALLOC(len*2 + 50)))
goto out;
host[0] = '\0';
if ((p = XP_STRSTR(safe_url, "://"))) {
p += 3;
if ((q = XP_STRCHR(p, '/')))
*q = '\0';
if ((r = XP_STRCHR(p, '@')))
p = r + 1;
XP_STRCPY(host, p);
if (q)
*q = '/';
if ((p = XP_STRCHR(host, ':')))
*p = '\0';
}
XP_SPRINTF(buf, "FindProxyForURL(\"%s\",\"%s\")", safe_url, host);
ok = MOCHA_EvaluateBuffer(decoder->mocha_context, proxyConfig,
buf, strlen(buf), 0, 0, &rv);
if (ok) {
if (rv.tag == MOCHA_STRING) {
const char *name =
MOCHA_GetAtomName(decoder->mocha_context, rv.u.atom);
if (*name)
result = XP_STRDUP(name);
}
MOCHA_DropRef(decoder->mocha_context, &rv);
}
out:
FREEIF(buf);
FREEIF(host);
FREEIF(safe_url);
FREEIF(bad_url);
LM_PutGlobalMochaDecoder(decoder);
return result;
#else /* ! MOCHA */
return NULL;
#endif /* ! MOCHA */
}
#ifdef MOCHA
/*
* Utility functions to be called from Mocha.
*
*
* 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.
*
*/
PRIVATE MochaBoolean
proxy_isPlainHostName(MochaContext *mc, MochaObject *obj, unsigned int argc, MochaDatum *argv, MochaDatum *rval)
{
if (argc >= 1 && argv[0].tag == MOCHA_STRING) {
const char *h = MOCHA_GetAtomName(mc, argv[0].u.atom);
if (h && !XP_STRCHR(h, '.')) {
*rval = MOCHA_true;
return MOCHA_TRUE;
}
}
*rval = MOCHA_false;
return MOCHA_TRUE;
}
/*
* Returns the number of domain levels in the hostname
* (the number of dots, actually).
*
* Just a string operation, doesn't consult DNS.
*
*/
PRIVATE MochaBoolean
proxy_dnsDomainLevels(MochaContext *mc, MochaObject *obj, unsigned int argc, MochaDatum *argv, MochaDatum *rval)
{
int i = 0;
if (argc >= 1 && argv[0].tag == MOCHA_STRING) {
const char *h = MOCHA_GetAtomName(mc, argv[0].u.atom);
if (h) {
while (*h) {
if (*h == '.')
i++;
h++;
}
}
}
MOCHA_INIT_DATUM(mc, rval, MOCHA_NUMBER, u.fval, i);
return MOCHA_TRUE;
}
/*
* Checks if the hostname contains the given domain.
*
*
*
*
*/
PRIVATE MochaBoolean
proxy_dnsDomainIs(MochaContext *mc, MochaObject *obj, unsigned int argc, MochaDatum *argv, MochaDatum *rval)
{
if (argc >= 2 && argv[0].tag == MOCHA_STRING && argv[1].tag == MOCHA_STRING) {
const char *h = MOCHA_GetAtomName(mc, argv[0].u.atom);
const char *p = MOCHA_GetAtomName(mc, argv[1].u.atom);
int len1, len2;
if (h && p && (len1 = XP_STRLEN(h)) >= (len2 = XP_STRLEN(p)) &&
(!strcasecomp(h + len1 - len2, p))) {
*rval = MOCHA_true;
return MOCHA_TRUE;
}
}
*rval = MOCHA_false;
return MOCHA_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.
*
*
*/
PRIVATE MochaBoolean
proxy_localHostOrDomainIs(MochaContext *mc, MochaObject *obj, unsigned int argc, MochaDatum *argv, MochaDatum *rval)
{
if (argc >= 2 && argv[0].tag == MOCHA_STRING && argv[1].tag == MOCHA_STRING) {
const char *h = MOCHA_GetAtomName(mc, argv[0].u.atom);
const char *p = MOCHA_GetAtomName(mc, argv[1].u.atom);
char *hp, *pp;
if (h && p) {
hp = XP_STRCHR(h, '.');
pp = XP_STRCHR(p, '.');
if (hp || !pp) {
if (!strcasecomp(h,p)) {
*rval = MOCHA_true;
return MOCHA_TRUE;
}
}
else if (!strncasecomp(h, p, pp - p)) {
*rval = MOCHA_true;
return MOCHA_TRUE;
}
}
}
*rval = MOCHA_false;
return MOCHA_TRUE;
}
/*
* Attempts to resolve the DNS name, and returns TRUE if resolvable.
*
*
*/
PRIVATE MochaBoolean
proxy_isResolvable(MochaContext *mc, MochaObject *obj, unsigned int argc, MochaDatum *argv, MochaDatum *rval)
{
if (argc >= 1 && argv[0].tag == MOCHA_STRING) {
const char *h = MOCHA_GetAtomName(mc, argv[0].u.atom);
PRHostEnt *hp = NULL;
#if defined(XP_UNIX) || defined(XP_WIN32)
PRHostEnt hpbuf;
char dbbuf[PR_NETDB_BUF_SIZE];
#endif
if (h) {
char *safe = XP_STRDUP(h);
if (XP_STRLEN(safe) > 64)
safe[64] = '\0';
#if defined(XP_UNIX) || defined(XP_WIN32)
hp = PR_gethostbyname(safe, &hpbuf, dbbuf, sizeof(dbbuf), 0);
#else
hp = gethostbyname(safe);
#endif
XP_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 = MOCHA_true;
return MOCHA_TRUE;
}
}
TRACEMSG(("~~~~~~~~~~~ isResolvable(%s) returns FALSE\n", argv[0]));
*rval = MOCHA_false;
return MOCHA_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;
struct hostent *hp = NULL;
#if defined(XP_UNIX) || defined(XP_WIN32)
struct hostent hpbuf;
char dbbuf[PR_NETDB_BUF_SIZE];
#endif
if (host) {
const char *p;
char *safe = NULL;
XP_Bool is_numeric_ip = TRUE;
for(p=host; *p; p++) {
if (!XP_IS_DIGIT(*p) && *p != '.') {
is_numeric_ip = FALSE;
break;
}
}
if (is_numeric_ip) {
return XP_STRDUP(host);
}
if (cache_host && cache_ip && !XP_STRCMP(cache_host, host)) {
return XP_STRDUP(cache_ip);
}
if ((safe = XP_STRDUP(host))) {
if (XP_STRLEN(safe) > 64)
safe[64] = '\0';
#if defined(XP_UNIX) || defined(XP_WIN32)
hp = PR_gethostbyname(safe, &hpbuf, dbbuf, sizeof(dbbuf), 0);
#else
hp = gethostbyname(safe);
#endif
XP_FREE(safe);
}
if (hp) {
char *ip = NULL;
struct in_addr in;
XP_MEMCPY(&in.s_addr, hp->h_addr, hp->h_length);
if ((ip = inet_ntoa(in))) {
StrAllocCopy(cache_host, host);
StrAllocCopy(cache_ip, ip);
return XP_STRDUP(ip);
}
}
}
return NULL;
}
PRIVATE MochaBoolean
proxy_dnsResolve(MochaContext *mc, MochaObject *obj, unsigned int argc, MochaDatum *argv, MochaDatum *rval)
{
if (argc >= 1 && argv[0].tag == MOCHA_STRING) {
const char *host = MOCHA_GetAtomName(mc, argv[0].u.atom);
char *ip = proxy_dns_resolve(host);
if (ip) {
MOCHA_INIT_DATUM(mc, rval, MOCHA_STRING, u.atom,
MOCHA_Atomize(mc, ip));
return MOCHA_TRUE;
}
}
*rval = MOCHA_null;
return MOCHA_TRUE;
}
/*
* Returns the IP address of the host machine as a string.
*
*
*/
PRIVATE MochaBoolean
proxy_myIpAddress(MochaContext *mc, MochaObject *obj, unsigned int argc, MochaDatum *argv, MochaDatum *rval)
{
static XP_Bool initialized = FALSE;
static char *my_address = NULL;
if (!initialized) {
char name[100];
initialized = TRUE;
if (gethostname(name, sizeof(name)) == 0) {
my_address = proxy_dns_resolve(name);
}
}
TRACEMSG(("~~~~~~~~~~~~~~~~~~ myIpAddress() returns %s\n", my_address ? my_address : "(null)"));
if (my_address) {
MOCHA_INIT_DATUM(mc, rval, MOCHA_STRING, u.atom,
MOCHA_Atomize(mc, my_address));
return MOCHA_TRUE;
}
*rval = MOCHA_null;
return MOCHA_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;
if (ip && (p = buf = XP_STRDUP(ip))) {
for(i=0; p && i<4; i++) {
if ((q = XP_STRCHR(p, '.'))) {
*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]));
XP_FREE(buf);
}
return htonl(addr);
}
PRIVATE MochaBoolean
proxy_isInNet(MochaContext *mc, MochaObject *obj, unsigned int argc, MochaDatum *argv, MochaDatum *rval)
{
if (argc >= 3 &&
argv[0].tag == MOCHA_STRING &&
argv[1].tag == MOCHA_STRING &&
argv[2].tag == MOCHA_STRING)
{
const char *ipstr = MOCHA_GetAtomName(mc, argv[0].u.atom);
char *ip = proxy_dns_resolve(ipstr);
const char *patstr = MOCHA_GetAtomName(mc, argv[1].u.atom);
const char *maskstr = MOCHA_GetAtomName(mc, argv[2].u.atom);
if (ip) {
unsigned long host = convert_addr(ip);
unsigned long pat = convert_addr(patstr);
unsigned long mask = convert_addr(maskstr);
XP_FREE(ip);
if ((mask & host) == (mask & pat)) {
TRACEMSG(("~~~~~~~~~~~~ isInNet(%s(%s), %s, %s) returns TRUE\n",
ipstr, ip, patstr, maskstr));
*rval = MOCHA_true;
return MOCHA_TRUE;
}
}
}
TRACEMSG(("~~~~~~~~~~~~ isInNet() returns FALSE\n"));
*rval = MOCHA_false;
return MOCHA_TRUE;
}
/*
* Does a regular expression match between the host/URL and the pattern.
*
* Returns true on match.
*
*/
PRIVATE MochaBoolean
proxy_regExpMatch(MochaContext *mc, MochaObject *obj, unsigned int argc, MochaDatum *argv, MochaDatum *rval)
{
if (argc >= 2 && argv[0].tag == MOCHA_STRING && argv[1].tag == MOCHA_STRING) {
const char *url = MOCHA_GetAtomName(mc, argv[0].u.atom);
const char *pat = MOCHA_GetAtomName(mc, argv[1].u.atom);
if (url && pat && XP_RegExpValid((char *) pat) && /* XXX */
!XP_RegExpMatch((char *) url, (char *) pat, TRUE)) {/* XXX */
*rval = MOCHA_true;
return MOCHA_TRUE;
}
}
*rval = MOCHA_false;
return MOCHA_TRUE;
}
struct tm * get_struct_tm(MochaContext *mc,
unsigned int *argc, MochaDatum *argv)
{
time_t now = time(NULL);
if (*argc > 0 && argv[*argc-1].tag == MOCHA_STRING) {
const char *laststr = MOCHA_GetAtomName(mc, argv[*argc-1].u.atom);
if (!strcasecomp(laststr, "GMT")) {
(*argc)--;
return gmtime(&now);
}
}
return localtime(&now);
}
char *weekdays = "SUNMONTUEWEDTHUFRISAT";
char *monnames = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC";
int get_no(const char *nam, char *arr)
{
char *p = strcasestr(arr, nam);
return p ? (((int)(p - arr)) / 3) : -1;
}
PRIVATE int get_em(MochaContext *mc, int argc, MochaDatum *argv,
int *d, int *m, int *y)
{
int i=0;
*m = *d = *y = -1;
for (i=0; i<argc; i++) {
switch (argv[i].tag) {
case MOCHA_STRING:
*m = get_no(MOCHA_GetAtomName(mc, argv[i].u.atom), monnames);
break;
case MOCHA_NUMBER:
if (argv[i].u.fval > 1900)
*y = argv[i].u.fval - 1900;
else
*d = argv[i].u.fval;
break;
default:
assert(0);
}
}
return ((*d > -1 ? 1 : 0) +
(*m > -1 ? 1 : 0) +
(*y > -1 ? 1 : 0));
}
PRIVATE int are_different(MochaContext *mc, MochaDatum *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;
}
PRIVATE MochaBoolean
proxy_dateRange(MochaContext *mc, MochaObject *obj, unsigned int argc, MochaDatum *argv, MochaDatum *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))
? MOCHA_true : MOCHA_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)))
? MOCHA_true : MOCHA_false);
}
return MOCHA_TRUE;
}
PRIVATE MochaBoolean
proxy_weekdayRange(MochaContext *mc, MochaObject *obj, unsigned int argc, MochaDatum *argv, MochaDatum *rval)
{
struct tm *tms = get_struct_tm(mc, &argc, argv);
int i, j;
if (argc >= 1)
i = get_no(MOCHA_GetAtomName(mc, argv[0].u.atom), weekdays);
if (argc >= 2)
j = get_no(MOCHA_GetAtomName(mc, argv[1].u.atom), 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)))))
? MOCHA_true : MOCHA_false);
return MOCHA_TRUE;
}
/*
* timeRange(<hr>)
* timeRange(<hr>, <hr>)
* timeRange(<hr>, <min>, <hr>, <min>)
* timeRange(<hr>, <min>, <sec>, <hr>, <min>, <sec>)
*
*/
PRIVATE MochaBoolean
proxy_timeRange(MochaContext *mc, MochaObject *obj, unsigned int argc, MochaDatum *argv, MochaDatum *rval)
{
struct tm *tms = get_struct_tm(mc, &argc, argv);
*rval = (((argc == 1 && argv[0].u.fval == tms->tm_hour) ||
(argc == 2 && (argv[0].u.fval <= tms->tm_hour &&
argv[1].u.fval >= tms->tm_hour)) ||
(argc == 4 && (argv[0].u.fval <= tms->tm_hour &&
argv[1].u.fval <= tms->tm_min &&
argv[2].u.fval >= tms->tm_hour &&
argv[3].u.fval >= tms->tm_min)) ||
(argc == 6 && (argv[0].u.fval <= tms->tm_hour &&
argv[1].u.fval <= tms->tm_min &&
argv[2].u.fval <= tms->tm_sec &&
argv[3].u.fval >= tms->tm_hour &&
argv[4].u.fval >= tms->tm_min &&
argv[5].u.fval >= tms->tm_sec)))
? MOCHA_true : MOCHA_false);
return MOCHA_TRUE;
}
#endif /* MOCHA */