src/server/util/util.c

Mon, 12 Aug 2024 00:22:37 +0200

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Mon, 12 Aug 2024 00:22:37 +0200
changeset 543
3335f431a91b
parent 415
d938228c382e
child 548
40ecc0a6b280
permissions
-rw-r--r--

implement keep-alive timeout

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
 *
 * THE BSD LICENSE
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer. 
 * Redistributions in binary form must reproduce the above copyright notice, 
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution. 
 *
 * Neither the name of the  nor the names of its contributors may be
 * used to endorse or promote products derived from this software without 
 * specific prior written permission. 
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * util.c: A hodge podge of utility functions and standard functions which 
 *         are unavailable on certain systems
 * 
 * Rob McCool
 */

#ifdef XP_UNIX
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include "prthread.h"
#endif /* XP_UNIX */


//include "nspr.h"
#include <errno.h>

#include "../daemon/netsite.h"
#include "../public/nsapi.h"
#include <cx/string.h>
#include <cx/mempool.h>
#include <cx/utils.h>
#include <cx/printf.h>

#include "pblock.h"
#include "util.h"

#include <openssl/bio.h>
#include <openssl/buffer.h>
#include <openssl/evp.h>


/* ------------------------------ _uudecode ------------------------------- */

static const unsigned char pr2six[256] = {
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,62,64,64,64,63,
    52,53,54,55,56,57,58,59,60,61,64,64,64,64,64,64,64,0,1,2,3,4,5,6,7,8,9,
    10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,64,64,64,64,64,64,26,27,
    28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64
};

/** you MUST reserve at least 2 additional bytes for bufout */
size_t util_base64decode(char *bufcoded, size_t codedbytes, char *bufout) {
    register char *bufin = bufcoded;
    register int nprbytes;
    size_t nbytesdecoded;

    /* Find the length */
    nprbytes = (int) codedbytes;
    while(pr2six[(int)(bufin[nprbytes-1])] >= 64) {
      nprbytes--;
    }
    nbytesdecoded = ((nprbytes+3)/4) * 3;

    while (nprbytes > 0) {
        *(bufout++) = (unsigned char)
            (pr2six[(int)(*bufin)] << 2 | pr2six[(int)bufin[1]] >> 4);
        *(bufout++) = (unsigned char)
            (pr2six[(int)bufin[1]] << 4 | pr2six[(int)bufin[2]] >> 2);
        *(bufout++) = (unsigned char)
            (pr2six[(int)bufin[2]] << 6 | pr2six[(int)bufin[3]]);
        bufin += 4;
        nprbytes -= 4;
    }

    if(nprbytes & 03) {
        if(pr2six[(int)bufin[-2]] > 63)
            nbytesdecoded -= 2;
        else
            nbytesdecoded -= 1;
    }

    return nbytesdecoded;
}

char* util_base64encode(char *in, size_t len) { 
    BIO *b;
    BIO *e;
    BUF_MEM *mem;

    e = BIO_new(BIO_f_base64());
    b = BIO_new(BIO_s_mem());
    BIO_set_flags(e, BIO_FLAGS_BASE64_NO_NL);
    
    e = BIO_push(e, b);
    BIO_write(e, in, len);
    BIO_flush(e);
    
    BIO_get_mem_ptr(e, &mem);
    char *out = malloc(mem->length + 1);
    memcpy(out, mem->data, mem->length);
    out[mem->length] = '\0';

    BIO_free_all(e);

    return out;
}


/* --------------------------- util_env_create ---------------------------- */


NSAPI_PUBLIC char **util_env_create(char **env, int n, int *pos)
{
    int x;

    if(!env) {
        *pos = 0;
        return (char **) MALLOC((n + 1)*sizeof(char *));
    }
    else {
        for(x = 0; (env[x]); x++);
        env = (char **) REALLOC(env, (n + x + 1)*(sizeof(char *)));
        *pos = x;
        return env;
    }
}


/* ---------------------------- util_env_free ----------------------------- */


NSAPI_PUBLIC void util_env_free(char **env)
{
    register char **ep = env;

    for(ep = env; *ep; ep++)
        FREE(*ep);
    FREE(env);
}

/* ----------------------------- util_env_str ----------------------------- */


NSAPI_PUBLIC char *util_env_str(const char *name, const char *value) {
    char *t;
    
    size_t len = strlen(name) + strlen(value) + 2;
    t = (char *) MALLOC(len); /* 2: '=' and '\0' */

    snprintf(t, len, "%s=%s", name, value);

    return t;
}


/* --------------------------- util_env_replace --------------------------- */


NSAPI_PUBLIC void util_env_replace(char **env, const char *name, const char *value)
{
    int x, y, z;
    char *i;

    for(x = 0; env[x]; x++) {
        i = strchr(env[x], '=');
        *i = '\0';
        if(!strcmp(env[x], name)) {
            y = strlen(env[x]);
            z = strlen(value);

            env[x] = (char *) REALLOC(env[x], y + z + 2);
            util_sprintf(&env[x][y], "=%s", value);
            return;
        }
        *i = '=';
    }
}


/* ---------------------------- util_sh_escape ---------------------------- */


NSAPI_PUBLIC char *util_sh_escape(char *s)
{
    char *ns = (char *) MALLOC(strlen(s) * 2 + 1);   /* worst case */
    register char *t, *u;

    for(t = s, u = ns; *t; ++t, ++u) {
        if(strchr("&;`'\"|*?~<>^()[]{}$\\ #!", *t))
            *u++ = '\\';
        *u = *t;
    }
    *u = '\0';
    return ns;
}


/* ---------------------------- util_env_find ----------------------------- */


NSAPI_PUBLIC char *util_env_find(char **env, const char *name)
{
    char *i;
    int x, r;

    for(x = 0; env[x]; x++) {
        i = strchr(env[x], '=');
        *i = '\0';
        r = !strcmp(env[x], name);
        *i = '=';
        if(r)
            return i + 1;
    }
    return NULL;
}


/* ---------------------------- util_env_copy ----------------------------- */


NSAPI_PUBLIC char **util_env_copy(char **src, char **dst)
{
    char **src_ptr;
    int src_cnt;
    int index;

    if (!src)
        return NULL;

    for (src_cnt = 0, src_ptr = src; *src_ptr; src_ptr++, src_cnt++);

    if (!src_cnt)
        return NULL;

    dst = util_env_create(dst, src_cnt, &index);

    for (src_ptr = src; *src_ptr; index++, src_ptr++)
        dst[index] = STRDUP(*src_ptr);
    dst[index] = NULL;
    
    return dst;
}

/* ----------------------------- util_sprintf ----------------------------- */

NSAPI_PUBLIC int util_vsnprintf(char *s, int n, register const char *fmt, 
                                va_list args)
{
    return vsnprintf(s, n, fmt, args);
}

NSAPI_PUBLIC int util_snprintf(char *s, int n, const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    return vsnprintf(s, n, fmt, args);
}

NSAPI_PUBLIC int util_vasprintf(pool_handle_t *pool, char **s, const char *fmt, 
                                va_list args)
{
    CxAllocator *a = pool_allocator(pool);
    va_list ap;
    va_copy(ap, args);
    cxmutstr str = cx_vasprintf_a(a, fmt, ap);
    *s = str.ptr;
    return str.length;
}

NSAPI_PUBLIC int util_asprintf(pool_handle_t *pool, char **s, const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    return util_vasprintf(pool, s, fmt, args);
}

NSAPI_PUBLIC int util_vsprintf(char *s, register const char *fmt, va_list args)
{
    return vsprintf(s, fmt, args);
}

NSAPI_PUBLIC int util_sprintf(char *s, const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    return vsprintf(s, fmt, args);
}

// TODO: asprintf



/* -------------------------- util_uri_unescape --------------------------- */

NSAPI_PUBLIC void util_uri_unescape(char *s)
{
    char *t, *u;

    for(t = s, u = s; *t; ++t, ++u) {
        if((*t == '%') && t[1] && t[2]) {
            *u = ((t[1] >= 'A' ? ((t[1] & 0xdf) - 'A')+10 : (t[1] - '0'))*16) +
                  (t[2] >= 'A' ? ((t[2] & 0xdf) - 'A')+10 : (t[2] - '0'));
            t += 2;
        }
        else
            if(u != t)
                *u = *t;
    }
    *u = *t;
}

/*
 * Same as util_uri_unescape, but returns success/failure
 */
NSAPI_PUBLIC int util_uri_unescape_strict(char *s)
{
    char *t, *u, t1, t2;
    int rv = 1;

    for(t = s, u = s; *t; ++t, ++u) {
        if (*t == '%') {
            t1 = t[1] & 0xdf; /* [a-f] -> [A-F] */
            if ((t1 < 'A' || t1 > 'F') && (t[1] < '0' || t[1] > '9'))
                rv = 0;

            t2 = t[2] & 0xdf; /* [a-f] -> [A-F] */
            if ((t2 < 'A' || t2 > 'F') && (t[2] < '0' || t[2] > '9'))
                rv = 0;

            *u = ((t[1] >= 'A' ? ((t[1] & 0xdf) - 'A')+10 : (t[1] - '0'))*16) +
                  (t[2] >= 'A' ? ((t[2] & 0xdf) - 'A')+10 : (t[2] - '0'));
            t += 2;
        }
        else if (u != t)
            *u = *t;
    }
    *u = *t;

    return rv;
}


NSAPI_PUBLIC int
util_uri_unescape_plus (const char *src, char *trg, int len)
{
    const char *t = src;
    char *u = trg == NULL ? (char *)src : trg;
    int	rlen = 0;

    if (len == -1)
        len = strlen (src);

    for( ; len && *t; ++t, ++u, len--, rlen++)
    {
        if((*t == '%') && t[1] && t[2])
        {
            *u = ((t[1] >= 'A' ? ((t[1] & 0xdf) - 'A') + 10 : (t[1] - '0')) * 16) +
                  (t[2] >= 'A' ? ((t[2] & 0xdf) - 'A') + 10 : (t[2] - '0'));
            t  += 2;
            len-= 2;
        }
        else
            if (*t == '+')
                *u = ' ';
            else
                *u = *t;
    }
    *u = 0;
    return rlen;
}


NSAPI_PUBLIC int INTutil_getboolean(const char *v, int def) {
    if(v[0] == 'T' || v[0] == 't') {
        return 1;
    }
    if(v[0] == 'F' || v[0] == 'f') {
        return 0;
    }
    return def;
}

int util_getboolean_s(cxstring s, int def) {
    if(s.length == 0) {
        return def;
    }
    if(s.ptr[0] == 'T' || s.ptr[0] == 't') {
        return 1;
    }
    if(s.ptr[0] == 'F' || s.ptr[0] == 'f') {
        return 0;
    }
    return def;
}

NSAPI_PUBLIC int util_strtoint(const char *str, int64_t *value) {
    char *end;
    errno = 0;
    int64_t val = strtoll(str, &end, 0);
    if(errno == 0) {
        *value = val;
        return 1;
    } else {
        return 0;
    }
}

NSAPI_PUBLIC const char* util_resource_name(const char *url) {
    cxstring urlstr = cx_str(url);
    if(urlstr.ptr[urlstr.length-1] == '/') {
        urlstr.length--;
    }
    cxstring resname = cx_strrchr(urlstr, '/');
    if(resname.length > 1) {
        return resname.ptr+1;
    } else {
        return url;
    }
}

NSAPI_PUBLIC char* util_parent_path(const char *path) {
    char *name = (char*)util_resource_name((char*)path);
    size_t namelen = strlen(name);
    size_t pathlen = strlen(path);
    size_t parentlen = pathlen - namelen;
    char *parent = MALLOC(parentlen + 1);
    memcpy(parent, path, parentlen);
    parent[parentlen] = '\0';
    return parent;
}

NSAPI_PUBLIC char* util_parent_path_pool(pool_handle_t *pool, const char *path) {
    // maybe we can unify this function with util_parent_path
    char *name = (char*)util_resource_name((char*)path);
    size_t namelen = strlen(name);
    size_t pathlen = strlen(path);
    size_t parentlen = pathlen - namelen;
    char *parent = pool_malloc(pool, parentlen + 1);
    memcpy(parent, path, parentlen);
    parent[parentlen] = '\0';
    return parent;
}

/* ------------------------------ util_itoa ------------------------------- */
/*
NSAPI_PUBLIC int util_itoa(int i, char *a)
{
    int len = util_i64toa(i, a);

    PR_ASSERT(len < UTIL_ITOA_SIZE);

    return len;
}
*/
NSAPI_PUBLIC int INTutil_itoa(int i, char *a) {
    return INTutil_i64toa(i, a);
}


/* ----------------------------- util_i64toa ------------------------------ */

/*
 * Assumption: Reversing the digits will be faster in the general case
 * than doing a log10 or some nasty trick to find the # of digits.
 */

NSAPI_PUBLIC int INTutil_i64toa(int64_t i, char *a)
{
    register int x, y, p;
    register char c;
    int negative;

    negative = 0;
    if(i < 0) {
        *a++ = '-';
        negative = 1;
        i = -i;
    }
    p = 0;
    while(i > 9) {
        a[p++] = (i%10) + '0';
        i /= 10;
    }
    a[p++] = i + '0';

    if(p > 1) {
        for(x = 0, y = p - 1; x < y; ++x, --y) {
            c = a[x];
            a[x] = a[y];
            a[y] = c;
        }
    }
    a[p] = '\0';

    //PR_ASSERT(p + negative < UTIL_I64TOA_SIZE);

    return p + negative;
}



#ifndef XP_WIN32
NSAPI_PUBLIC struct passwd *
util_getpwnam(const char *name, struct passwd *result, char *buffer, 
	int buflen)
{
    struct passwd *rv;

    errno = getpwnam_r(name, result, buffer, buflen, &rv);
    if (errno != 0)
        rv = NULL;

    return rv;
}
#endif


#ifndef XP_WIN32
NSAPI_PUBLIC struct passwd *
util_getpwuid(uid_t uid, struct passwd *result, char *buffer, int buflen)
{
    struct passwd *rv;

    errno = getpwuid_r(uid, result, buffer, buflen, &rv);
    if (errno != 0)
        rv = NULL;

    return rv;
}
#endif


NSAPI_PUBLIC int util_errno2status(int errno_value) {
    switch(errno_value) {
        case 0: {
            return 200;
        }
        case EACCES: {
            return 403;
        }
        case ENOENT: {
            return 404;
            break;
        }
    }
    return 500;
}



NSAPI_PUBLIC
cxmutstr util_path_append(pool_handle_t *pool, char *path, char *ch) {
    cxmutstr parent = cx_mutstr(path);
    cxmutstr child = cx_mutstr(ch);
    cxmutstr newstr;
    
    CxAllocator *a = pool_allocator(pool); 
    if(parent.ptr[parent.length-1] == '/') {
        newstr = cx_strcat_a(a, 2, parent, child);
    } else {
        newstr = cx_strcat_a(a, 3, parent, cx_str("/"), child);
    }
    
    return newstr;
}

cxmutstr util_path_remove_last(cxmutstr path) {
    int i;
    for(i=path.length-1;i>=0;i--) {
        char c = path.ptr[i];
        if(c == '/') {
            path.ptr[i] = 0;
            path.length = i;
            break;
        }
    }
    if(i < 0) {
        path.ptr = NULL;
        path.length = 0;
    }
    return path;
}

void util_add_ppath(cxmutstr root, cxmutstr path, pblock *vars) {
    // concat path
    size_t length = root.length + path.length;
    char *translated_path = alloca(length);
    
    memcpy(translated_path, root.ptr, root.length);
    memcpy(translated_path + root.length, path.ptr, path.length);
    
    
    // add path to specified pblock
    pblock_kvinsert(
            pb_key_ppath,
            translated_path,
            length,
            vars);
}


// new - code from params.cpp
NSAPI_PUBLIC pblock* util_parse_param(pool_handle_t *pool, char *query) {
    pblock *pb = pblock_create_pool(pool, 32);
    if(!pb) {
        return NULL;
    }
    if(!query || !(*query)) {
        return pb;
    }
    
    int loopFlag = 1;
    int nl = 0;	// size of the name substring
    int vl = 0;	// size of the value substring
    int state = 0;
    const char *np = query;
    const char *vp = NULL;
    
    while (loopFlag) {
        char delim = *query++;
        switch (delim) {
            case '&':
            case '\0': {
                if(!delim) {
                    loopFlag = 0;
                }
                
                state = 0;
                
                if(nl > 0) {
                    util_uri_unescape_plus(np, NULL, nl);
                    util_uri_unescape_plus(vp, NULL, vl);
                    pblock_nvlinsert(np, nl, vp, vl, pb);
                }
                
                nl = 0;
                vl = 0;
                vp = NULL;
                np = query;
                break;
            }
            case '=': {
                state = 1;
                vp = query;
                break;
            }
            default: {
                if(state) {
                    vl++;
                } else {
                    nl++;
                }
            }
        } /* switch */
    } /* while */

    return pb;
}


/* ---------------------------- util_mstr2num ----------------------------- */

static const int MSTR2NUM_HT_MASK = 0xf;

struct mstr2num_ht {
    unsigned ucmstr; // Uppercase 3 character month string in a machine word
    int mnum; // 0-based month number for this month string
};

struct mstr2num_ht MSTR2NUM_HT[] = {
    { 'A' << 16 | 'P' << 8 | 'R', 3 },
    { 'S' << 16 | 'E' << 8 | 'P', 8 },
    { 'M' << 16 | 'A' << 8 | 'Y', 4 },
    { 0, -1 },
    { 'M' << 16 | 'A' << 8 | 'R', 2 },
    { 'F' << 16 | 'E' << 8 | 'B', 1 },
    { 0, -1 },
    { 'D' << 16 | 'E' << 8 | 'C', 11 },
    { 'O' << 16 | 'C' << 8 | 'T', 9 },
    { 'J' << 16 | 'U' << 8 | 'N', 5 },
    { 0, -1 },
    { 'A' << 16 | 'U' << 8 | 'G', 7 },
    { 'J' << 16 | 'A' << 8 | 'N', 0 },
    { 'J' << 16 | 'U' << 8 | 'L', 6 },
    { 0, -1 },
    { 'N' << 16 | 'O' << 8 | 'V', 10 }
};

static inline int _mstr2num(const char *s)
{
    const unsigned char *mstr = (const unsigned char *) s;

    /*
     * We compute ucmstr (an uppercase 3 character month string stored in a
     * machine word) and hash (a perfect hash based on the last 2 characters
     * of the 3 character uppercase month string) from the input string s.
     * Note that each character from the input string is masked by 0xdf; in
     * ASCII, this has the effect of converting alphabetic characters to
     * uppercase while 1. not changing any nonalphabetic characters into
     * alphabetic characters and 2. leaving any nul characters unchanged.
     *
     * The hash value is used as an index into the MSTR2NUM_HT[] hash table.
     * If the ucmstr at that index matches our computed ucmstr, the mnum at
     * that index is the 0-based month number corresponding to the input
     * string.
     *
     * Note that we never read past the end of the input string and always
     * return -1 if the input string doesn't begin with a valid 3 character
     * month string.
     */

    unsigned char ucmstr0 = mstr[0] & 0xdf;
    unsigned ucmstr = ucmstr0 << 16;
    if (ucmstr0 != '\0') {
        unsigned char ucmstr1 = mstr[1] & 0xdf;
        ucmstr |= ucmstr1 << 8;
        if (ucmstr1 != '\0') {
            unsigned char ucmstr2 = mstr[2] & 0xdf;
            ucmstr |= ucmstr2;

            unsigned hash = (ucmstr1 >> 2) ^ (ucmstr2 << 1);

            int i = hash & MSTR2NUM_HT_MASK;

            if (MSTR2NUM_HT[i].ucmstr == ucmstr)
                return MSTR2NUM_HT[i].mnum;
        }
    }

    return -1;
}

NSAPI_PUBLIC int util_mstr2num(const char *s)
{
    return _mstr2num(s);
}


/* ------------------------- util_str_time_equal -------------------------- */

/*
 * Function to compare if two time strings are equal
 *
 * Acceptable date formats:
 *     Sun, 06 Nov 1994 08:49:37 GMT  ; RFC 822, updated by RFC 1123
 *     Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
 *     Sun Nov  6 08:49:37 1994       ; ANSI C's asctime() format
 *
 * Return 0 if equal, -1 if not equal.
 */

static inline const char * _parse_day_month(const char *p, int *day, int *month)
{
    *day = 0;

    if (*p == ',') {
        // Parse day and month: ", 06 Nov", ", 06-Nov"
        p++;
        if (*p == ' ')
            p++;
        while (*p >= '0' && *p <= '9')
            *day = *day * 10 + (*p++ - '0');
        if (*p == ' ' || *p == '-')
            p++;
        *month = _mstr2num(p);
        if (*month != -1)
            p += 3;
    } else {
        // Parse month and day: " Nov  6"
        if (*p == ' ')
            p++;
        *month = _mstr2num(p);
        if (*month != -1)
            p += 3;
        while (*p == ' ')
            p++;
        while (*p >= '0' && *p <= '9')
            *day = *day * 10 + (*p++ - '0');
    }

    return p;
}

static inline void _parse_year_time(const char *p, int *year, const char ** time)
{
    int _year = 0;

    if (*p == '-') {
        // Parse year and time: "-94 08:49:37"
        p++;
        while (*p >= '0' && *p <= '9')
            _year = _year * 10 + (*p++ - '0');
        if (_year < 70) {
            _year += 2000;
        } else {
            _year += 1900;
        }
        if (*p == ' ')
            p++;
        *time = p;
    } else {
        // Parse year and time or time and year
        if (*p == ' ')
            p++;
        if (p[0] && p[1] && p[2] == ':') {
            // Parse time and year: "08:49:37 1994"
            *time = p;
            p += 3;
            while (*p && *p != ' ')
                p++;
            if (*p == ' ')
                p++;
            while (*p >= '0' && *p <= '9')
                _year = _year * 10 + (*p++ - '0');
        } else {
            // Parse year and time: "1994 08:49:37"
            while (*p >= '0' && *p <= '9')
                _year = _year * 10 + (*p++ - '0');
            if (*p == ' ')
                p++;
            *time = p;
        }
    }
    
    *year = _year;
}

NSAPI_PUBLIC int util_str_time_equal(const char *t1, const char *t2)
{
    // Skip leading whitespace and day of week
    while (isspace(*t1))
        t1++;
    while (isalpha(*t1))
        t1++;
    while (isspace(*t2))
        t2++;
    while (isalpha(*t2))
        t2++;

    // Day and month: ", 06 Nov", ", 06-Nov", or " Nov  6"
    int day1;
    int month1;
    t1 = _parse_day_month(t1, &day1, &month1);
    int day2;
    int month2;
    t2 = _parse_day_month(t2, &day2, &month2);
    if (day1 != day2)
        return -1;
    if (month1 != month2)
        return -1;

    // Year and time: " 1994 08:49:37", "-94 08:49:37", or " 08:49:37 1994"
    int year1;
    const char *time1;
    _parse_year_time(t1, &year1, &time1);
    int year2;
    const char *time2;
    _parse_year_time(t2, &year2, &time2);
    if (year1 != year2)
        return -1;
    while (*time1 && *time1 != ' ' && *time1 == *time2) {
        time1++;
        time2++;
    }
    if (*time2 && *time2 != ' ')
        return -1;

    return 0;
}


/* --------------------------- util_later_than ---------------------------- */

static int _time_compare(const struct tm *lms, const char *ims)
{
    while (isspace(*ims))
        ims++;
    while (isalpha(*ims))
        ims++;

    int day;
    int month;
    ims = _parse_day_month(ims, &day, &month);
    if (month == -1)
        return 1;

    int year;
    const char *time;
    _parse_year_time(ims, &year, &time);

    int rv;

    rv = (lms->tm_year + 1900) - year;
    if (rv)
        return rv;

    rv = lms->tm_mon - month;
    if (rv)
        return rv;

    rv = lms->tm_mday - day;
    if (rv)
        return rv;

    const char *p = time;

    int hour = 0;
    while (*p >= '0' && *p <= '9')
        hour = hour * 10 + (*p++ - '0');
    if (*p == ':')
        p++;

    rv = lms->tm_hour - hour;
    if (rv)
        return rv;

    int minutes = 0;
    while (*p >= '0' && *p <= '9')
        minutes = minutes * 10 + (*p++ - '0');
    if (*p == ':')
        p++;

    rv = lms->tm_min - minutes;
    if (rv)
        return rv;

    int seconds = 0;
    while (*p >= '0' && *p <= '9')
        seconds = seconds * 10 + (*p++ - '0');
    if (*p == ':')
        p++;

    rv = lms->tm_sec - seconds;
    if (rv)
        return rv;

    return 0;
}

NSAPI_PUBLIC int util_later_than(const struct tm *lms, const char *ims)
{
    /*
     * Returns 0 if lms later than ims
     *         0 if ims is malformed
     *         1 if ims later than lms
     *         1 if equal
     */

    return _time_compare(lms, ims) <= 0;
}

NSAPI_PUBLIC int util_time_equal(const struct tm *lms, const char *ims)
{
    return _time_compare(lms, ims) == 0;
}


NSAPI_PUBLIC struct tm *
util_gmtime(const time_t *clock, struct tm *res)
{
    return gmtime_r(clock, res);
}

int util_isdate(const char *str) {
    cxstring datestr = cx_str(str);  
    cxstring example = cx_str("Sun, 06 Nov 1994 08:49:37 GMT");
    
    if(datestr.length != example.length) {
        return 0;
    }
    
    for(int i=0;i<datestr.length;i++) {
        char e = example.ptr[i];
        if(isdigit(e)) {
            if(!isdigit(datestr.ptr[i])) {
                return 0;
            }
        } else if(e == ' ') {
            if(datestr.ptr[i] != ' ') {
                return 0;
            }
        } else if(e == ',') {
            if(datestr.ptr[i] != ',') {
                return 0;
            }
        } else if(e == ':') {
            if(datestr.ptr[i] != ':') {
                return 0;
            }
        }
    }
    
    if(!cx_strsuffix(datestr, cx_str("GMT"))) {
        return 0;
    }
    
    return 1;
}


/* ------------------------- util_mime_separator -------------------------- */


NSAPI_PUBLIC int util_mime_separator(char *sep)
{
    int size = 35; // documented in nsapi.h
    int pos = 0;

    sep[pos++] = CR;
    sep[pos++] = LF;
    sep[pos++] = '-';
    sep[pos++] = '-';
    
    int r[6];
    for(int i=0;i<6;i++) {
        r[i] = rand() % 10000;
    }
    pos += snprintf(
            sep+4,
            size-4,
            "X%04x%04x%04x%04x%04x%04xE",
            r[0],
            r[1],
            r[2],
            r[3],
            r[4],
            r[5]);

    return pos;
}


/* ------------------------- util_html_escape -------------------------- */

NSAPI_PUBLIC char *util_html_escape(const char *s)
{
    const char *in;

    int len = 0;
    for (in = s; *in; in++) {
        switch (*in) {
        case '<':
            len += 4; // &lt;
            break;
        case '>':
            len += 4; // &gt;
            break;
        case '&':
            len += 5; // &amp;
            break;
        case '"':
            len += 6; // &quot;
            break;
        case '\'':
            len += 6; // &apos;
            break;
        case '+':
            len += 5; // &#43;
            break;
        default:
            len++;
            break;
        }
    }

    char *ns = (char *) MALLOC(len + 1);
    if (!ns)
        return ns;

    char *out = ns;
    for (in = s; *in; in++) {
        switch (*in) {
        case '<':
            *out++ = '&';
            *out++ = 'l';
            *out++ = 't';
            *out++ = ';';
            break;
        case '>':
            *out++ = '&';
            *out++ = 'g';
            *out++ = 't';
            *out++ = ';';
            break;
        case '&':
            *out++ = '&';
            *out++ = 'a';
            *out++ = 'm';
            *out++ = 'p';
            *out++ = ';';
            break;
        case '"':
            *out++ = '&';
            *out++ = 'q';
            *out++ = 'u';
            *out++ = 'o';
            *out++ = 't';
            *out++ = ';';
            break;
        case '\'':
            *out++ = '&';
            *out++ = 'a';
            *out++ = 'p';
            *out++ = 'o';
            *out++ = 's';
            *out++ = ';';
            break;
        case '+':
            *out++ = '&';
            *out++ = '#';
            *out++ = '4';
            *out++ = '3';
            *out++ = ';';
            break;
        default:
            *out++ = *in;
            break;
        }
    }
    *out = '\0';

    return ns;
}

mercurial