/** BEGIN COPYRIGHT BLOCK
 * Copyright (C) 2001 Sun Microsystems, Inc.  Used by permission.
 * Copyright (C) 2005 Red Hat, Inc.
 * All rights reserved.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation version
 * 2.1 of the License.
 *                                                                                  
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *                                                                                  
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 * END COPYRIGHT BLOCK **/
/*
 * form_post.c - Functions to help with handling POSTs
 *
 * All blame to Mike McCool
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "libadminutil/admutil.h"
#include "dbtadmutil.h"
#if defined(USE_OPENLDAP)
#include <unicode/utf8.h>
static char *
my_ldap_utf8inc(char *s) {
    int utf8index = 0;
    UChar32 utf8char = 0;
    U8_NEXT_UNSAFE(s,utf8index,utf8char);
    return s + utf8index;
}

static int
my_ldap_utf8copy(char *d, const char *s) {
    int utf8index = 0;
    UChar32 utf8char = 0;

    U8_GET_UNSAFE(s,0,utf8char);
    U8_APPEND_UNSAFE(d,utf8index,utf8char);
    return utf8index;
}

#ifndef LDAP_UTF8INC
#define LDAP_UTF8INC(s) ((0x80 & *(unsigned char*)(s)) ? s = my_ldap_utf8inc(s) : ++s) 
#endif
#ifndef LDAP_UTF8COPY
#define LDAP_UTF8COPY(d,s) ((0x80 & *(unsigned char*)(s)) ? my_ldap_utf8copy(d,s) : ((*(d) = *(s)), 1)) 
#endif

#endif /* USE_OPENLDAP */

extern Resource *admutil_i18nResource;
extern char *admutil_acceptLang;

#ifndef BIG_LINE
#define BIG_LINE 1024
#endif

static char **input;

/* returns the unencoded char pointed at by
   the given string which may be url encoded
*/
static char
urlchar2char(char *str, size_t *pos, size_t len)
{
    char thechar;

    if((str[*pos] == '%') && (len > 2) && (*pos < (len - 2)))  {
        ++(*pos);
        thechar = (str[*pos] >= 'A' ? 
                   ((str[*pos] & 0xdf) - 'A')+10 : (str[*pos] - '0'));
        thechar *= 16;

        ++(*pos);
        thechar += (str[*pos] >= 'A' ? 
                    ((str[*pos] & 0xdf) - 'A')+10 : (str[*pos] - '0'));
    } 
    else if(str[*pos] == '+')  {
        thechar = ' ';
    } else {
        thechar = str[*pos];
    }

    return thechar;
}

#ifdef XP_WIN32
PR_IMPLEMENT(char *)
Get_QueryNT(void)
{
    char *qs = getenv("QUERY_STRING");
    if(qs && (*qs == '\0'))
        qs = NULL;
    return qs;
}
#endif  /* XP_WIN32 */
 
PR_IMPLEMENT(char **)
get_input_ptr(void)
{
    return input;
}

#define ADMINUTIL_MAX_ENTITY_LEN			6	/* &quot; */
static char	*specials = "&\"<>\'";
static char	*entities[] = { "&amp;", "&quot;", "&lt;", "&gt;", "&#39;" };
static int entitylen[] = { 5, 6, 4, 4, 5 };
static int entitynum = sizeof(entities)/sizeof(entities[0]);

PR_IMPLEMENT(char *)
strdup_escape_entities( char *s, int *madecopyp )
{
/*
 * If the UTF8 string "s" contains any HTML special characters, make a
 * duplicate where the appropriate HTML "entities" have been substituted
 * for the special chars.  For example, "<mcs@ace.com>" will be translated
 * to "&lt;mcs@ace.com&gt;".
 * 
 * If "s" does not contain any special characters, it is returned and
 *	*madecopyp is set to 0.
 * Otherwise a malloc'd string is returned and *madecopyp is set to 1.
 */
    int		spcount, idx;
    char	*p, *q, *r, *d;

    spcount = 0;
    for ( p = s; *p != '\0'; LDAP_UTF8INC( p )) {
        if ( ((*p) & 0x80) == 0 && strchr( specials, *p ) != NULL ) {
            ++spcount;
        }
    }

    if ( spcount == 0 ) {
        *madecopyp = 0;
        return( s );
    }

    d = r = PR_Malloc( strlen( s ) + 1 + spcount * ADMINUTIL_MAX_ENTITY_LEN );
    for ( p = s; *p != '\0'; LDAP_UTF8INC( p )) {
        if ( ((*p) & 0x80) == 0 && ( q = strchr( specials, *p )) != NULL ) {
            idx = ( q - specials );
            memcpy( r, entities[ idx ], entitylen[ idx ] );
            r += entitylen[ idx ];
        } else {
            r += LDAP_UTF8COPY( r, p );
        }
    }
    *r = '\0';

    *madecopyp = 1;
    return( d );
}

/* this will convert a string with escaped entities ("&amp;")
   back to the original unescaped string ("&")
   This is necessary for converting URLs which would normally
   have entities in them (e.g. search?context=foo&amp;dn=bar)
   for use in javascript (e.g. window.href = 'search?context=foo&dn=bar')
   since javascript must use the unescaped version
   This converts the string in place since the entities "&amp;"
   take up much more room than the single character represented
   If you need to work on a copy then make a copy with strdup first.
*/
PR_IMPLEMENT(void)
unescape_entities(char *s)
{
    int		idx;
    char	*p, *r;

    if (!s || !*s) {
        return;
    }

    r = s;
    for ( p = s; *p != '\0'; LDAP_UTF8INC( p )) {
        if ( ((*p) & 0x80) == 0 && ( (*p) == '&') ) {
            for( idx = 0; idx < entitynum; ++idx ) {
                if (!strncmp(p, entities[ idx ], entitylen[ idx ])) {
                    break;
                }
            }
            if (idx < entitynum) {
                *r = specials[idx];
                ++r;
                p += entitylen[ idx ]-1; /* the 1 will be added in the for loop */
            } else {
                r += LDAP_UTF8COPY( r, p );
            }
        } else {
            r += LDAP_UTF8COPY( r, p );
        }
    }
    *r = '\0';

    return;
}

PR_IMPLEMENT(void)
form_unescape(char *str) 
{
    size_t x = 0, y = 0;
    size_t l = PL_strlen(str);

    while(x < l)  {
        str[y] = urlchar2char(str, &x, l);
        x++;
        y++;
    }
    str[y] = '\0';
}

/*
 * form_unescape_url_escape_html -- 1) unescape escaped chars in URL;
 *                                  2) escape unsecure chars for scripts
 * 1) "%##" is converted to one character which value is ##; so is '+' to ' '
 * 2) <, >, &, ", ' are escaped with "&XXX;" format
 */
PR_IMPLEMENT(char *)
form_unescape_url_escape_html(char *str) 
{
    register size_t x = 0, y = 0;
    size_t l = 0;
    char *rstr = NULL;

    if (NULL == str) {
        return NULL;
    }

    /* first, form_unescape to convert hex escapes to chars */
    form_unescape(str);

    /* next, allocate enough space for the escaped entities */
    for (x = 0, y = 0; str[x] != '\0'; x++) {
        if (('<' == str[x]) || ('>' == str[x]))
            y += 4;
        else if (('&' == str[x]) || ('\'' == str[x]))
            y += 5;
        else if ('"' == str[x])
            y += 6;
    }

    if (0 < y) {
        rstr = (char *)PR_Malloc(x + y + 1);
    } else {
        rstr = PL_strdup(str);
    }
    l = x; /* length of str */

    if (NULL == rstr) {
        if (admutil_i18nResource) {
            char buf[BUFSIZ];
            rpt_err(MEMORY_ERROR,
                    NULL,
                    (char*)res_getstring(admutil_i18nResource,
                                         DBT_formPost_PostStdinErr,
                                         admutil_acceptLang, buf, sizeof(buf),
                                         NULL), NULL);
        } else {
            rpt_err(MEMORY_ERROR,
                NULL, 
                "Could not allocate enough memory to read in the POST parameters.",
                NULL);
        }
        return NULL;
    }

    if (y == 0) { /* no entities to escape - just return the string copy */
        return rstr;
    }

    for (x = 0, y = 0; x < l; x++, y++) {
        char digit = str[x];
        /*  see if digit (the original or the unescaped char)
            needs to be html encoded */
        if ('<' == digit) {
            memcpy(&rstr[y], "&lt;", 4);
            y += 3;
        } else if ('>' == digit) {
            memcpy(&rstr[y], "&gt;", 4);
            y += 3;
        } else if ('&' == digit) {
            memcpy(&rstr[y], "&amp;", 5);
            y += 4;
        } else if ('"' == digit) {
            memcpy(&rstr[y], "&quot;", 6);
            y += 5;
        } else if ('\'' == digit) {
            memcpy(&rstr[y], "&#39;", 5);
            y += 4;
        } else { /* just write the char to the output string */
            rstr[y] = digit;
        }
    }
    rstr[y] = '\0';
    return rstr;
}

PR_IMPLEMENT(int)
post_begin(FILE *in) 
{
    char *vars = NULL, *tmp = NULL;
    int cl;
    char buf1[BUFSIZ];
    char buf2[BUFSIZ];
    int rc = 0;

    if(!(tmp = getenv("CONTENT_LENGTH"))) {
        if (admutil_i18nResource) {
            rpt_err(INCORRECT_USAGE,
                    (char*)res_getstring(admutil_i18nResource,
                                         DBT_formPost_Browser_err,
                                         admutil_acceptLang, buf1, sizeof(buf1), NULL),
                    (char*)res_getstring(admutil_i18nResource,
                                         DBT_formPost_Browser_errDetail,
                                         admutil_acceptLang, buf2, sizeof(buf2), NULL),
                    NULL);
        }
        else {
            rpt_err(INCORRECT_USAGE, 
                    "Browser Error", 
                    "Your browser sent no content length with a POST command. Please be sure to use a fully compliant browser.",
                    NULL);
        }
        rc = 1;
    }
        
    cl = atoi(tmp);

    if (!(vars = (char *)PR_Malloc(cl+1))) {
      if (admutil_i18nResource) {
          rpt_err(MEMORY_ERROR,
                  NULL,
                  (char*)res_getstring(admutil_i18nResource,
                                       DBT_formPost_PostStdinErr,
                                       admutil_acceptLang, buf1, sizeof(buf1), NULL),
                  NULL);
      }
      else {
        rpt_err(MEMORY_ERROR,
                NULL, 
                "Could not allocate enough memory to read in the POST parameters.",
                NULL);
      }
      rc = 1;
    }        

    if( !(fread(vars, 1, cl, in)) ) {
      if (admutil_i18nResource) {
          rpt_err(SYSTEM_ERROR,
                  NULL,
                  (char*)res_getstring(admutil_i18nResource,
                                       DBT_formPost_PostStdinErr,
                                       admutil_acceptLang, buf1, sizeof(buf1), NULL),
                  NULL);
      }
      else {
        rpt_err(SYSTEM_ERROR,
                NULL, 
                "The POST variables could not be read from stdin.",
                NULL);
      }
      rc = 1;
    }

    vars[cl] = '\0';

    input = string_to_vec(vars);
    PL_strfree(vars); /* string_to_vec dups it */
    return rc;
}

PR_IMPLEMENT(void)
get_begin(char *qs)
{
    input = string_to_vec(qs);
}

PR_IMPLEMENT(char **)
string_to_vec(char *in)
{
    char **ans = NULL;
    int vars = 0;
    register int x = 0;
    char *tmp;
    char buf[BUFSIZ];

    if (!(in = PL_strdup(in))) {
      if (admutil_i18nResource) {
          rpt_err(MEMORY_ERROR,
                  NULL,
                  (char*)res_getstring(admutil_i18nResource,
                                       DBT_formPost_PostStdinErr,
                                       admutil_acceptLang, buf, sizeof(buf), NULL),
                  NULL);
      }
      else {
        rpt_err(MEMORY_ERROR,
                NULL, 
                "Could not allocate enough memory to read in the POST parameters.",
                NULL);
      }
      return ans;
    }        

    while(in[x])
        if(in[x++]=='=')
            vars++;
    
    ans = new_strlist(vars+1);
  
    x=0;
    tmp = strtok(in, "&");
    if (!tmp || !strchr(tmp, '=')) { /* error, bail out */
        PR_Free(in);
        return(ans);
    }

    if (!(ans[x++] = form_unescape_url_escape_html(tmp))) {
        /* could not allocate enough memory */
        PR_Free(in);
        return ans;
    }

    while((tmp = strtok(NULL, "&")))  {
        if (!strchr(tmp, '=')) {
            PR_Free(in);
            return ans;
        }
        if (!(ans[x++] = form_unescape_url_escape_html(tmp))) {
            /* could not allocate enough memory */
            PR_Free(in);
            return ans;
        }
    }

    PR_Free(in);

    return(ans);
}

PR_IMPLEMENT(char *)
get_cgi_var(char *varname, char *elem_id, char *bongmsg)
{
    register int x = 0;
    int len = PL_strlen(varname);
    char *ans = NULL;
    char buf[BUFSIZ];
   
    while(input && input[x])  {
    /*  We want to get rid of the =, so len, len+1 */
        if((!strncmp(input[x], varname, len)) && (*(input[x]+len) == '='))  {
            if (!(ans = PL_strdup(input[x] + len + 1))) {
                if (admutil_i18nResource) {
                    rpt_err(MEMORY_ERROR,
                            NULL,
                            (char*)res_getstring(admutil_i18nResource,
                                                 DBT_formPost_PostStdinErr,
                                                 admutil_acceptLang, buf, sizeof(buf), NULL),
                            NULL);
                }
                else {
                    rpt_err(MEMORY_ERROR,
                            NULL, 
                            "Could not allocate enough memory to get the parameter.",
                            NULL);
                }
                return ans;
            }
        
            if(!strcmp(ans, ""))
                ans = NULL;
            break;
        }  else
            x++;
    }
    if(ans == NULL)  {
        if ((bongmsg) && PL_strlen(bongmsg))
            rpt_err(ELEM_MISSING, elem_id, bongmsg, NULL);
        else
            return NULL;
    }
    else
        return(ans);
    /* shut up gcc */
    return NULL;
}

PR_IMPLEMENT(long)
get_cgi_long(char *varname)
{
    char *ans = get_cgi_var(varname, NULL, NULL);
 
    return ans ? atol(ans) : -1L;
}

PR_IMPLEMENT(int)
get_cgi_int(char *varname)
{
    return (int)get_cgi_long(varname);
}

PR_IMPLEMENT(int)
get_cgi_bool(char *varname)
{
    char *ans = get_cgi_var(varname, NULL, NULL);
 
    return (ans && *ans && (*ans - '0')) ? 1 : 0;
}

PR_IMPLEMENT(char **)
get_cgi_multiple(char *varname, char *elem_id, char *bongmsg)
{
    register int n, x;
    int len = PL_strlen(varname);
    char **ans = NULL;
    char buf[BUFSIZ];

    for(n=0; input[n]; n++);
    ans = new_strlist(n + 1);

    for(x=n=0; input[x]; x++)  {
        if ((!strncmp(input[x], varname, len)) &&
        (*(input[x]+len) == '=') &&
        (*(input[x]+len+1))) {
            if (!(ans[n] = PL_strdup(input[x] + len + 1))) {
                if (admutil_i18nResource) {
                    rpt_err(MEMORY_ERROR,
                            NULL,
                            (char*)res_getstring(admutil_i18nResource,
                                                 DBT_formPost_PostStdinErr,
                                                 admutil_acceptLang, buf, sizeof(buf), NULL),
                            NULL);
                }
                else {
                    rpt_err(MEMORY_ERROR,
                            NULL, 
                            "Could not allocate enough memory to get the parameter.",
                            NULL);
                }
                return ans;
            }
        n++;
        }
    }
    ans[n] = NULL;

    if (!n)  {
        PR_Free(ans);
        if(bongmsg)
            rpt_err(ELEM_MISSING, elem_id, bongmsg, NULL);
        else
            return NULL;
    }
    else {
        return ans;
    }
    /* shut up gcc */
    return NULL;
}


PR_IMPLEMENT(void)
rm_trail_slash(char *target)
{
    int l;
    
    if(!target) return;

    l = PL_strlen(target) - 1;

    if((target[l] == '/') && (l != 0))
        target[l] = '\0';
}

/* Compresses spaces only. */
PR_IMPLEMENT(void)
compress_spaces(char *source) 
{
    register int x, y;

    if(!source) return;

    for(x=0, y=0; source[x]; x++)  {
        if(source[x] != ' ')
             source[y++] = source[x];
    }
    source[y] = '\0';
}

/* Compresses spaces and replaces commas with an or list */
PR_IMPLEMENT(char *)
compress_and_replace(char *source) 
{
    char *answer;
    int hascomma, x, y;

    if(!source) return source;

    hascomma = (strchr(source, ',')) ? 1 : 0;

    /* + 2 = 1 for trailing \0 and one put in by Aruna for NT */
    answer = (char *)PR_Malloc(PL_strlen(source) + 2);
    if (NULL == answer)
        return NULL;

    for(x=0, y=0; source[x]; x++)  {
        if(source[x] == ',')
             answer[y++] = '|';
        else if(source[x] != ' ')
             answer[y++] = source[x];
        answer[y] = '\0';
    }
    if(!hascomma)
        return(answer);
    else {
        char *newanswer = PR_smprintf("(%s)", answer);
        free_str(answer);
        return(newanswer);
    }
}

