earlybrowserreborn - Rev 1

Subversion Repositories:
Rev:

/* MODULE                                                       HTAAUtil.c
**              COMMON PARTS OF ACCESS AUTHORIZATION MODULE
**                      FOR BOTH SERVER AND BROWSER
**
** IMPORTANT:
**      Routines in this module use dynamic allocation, but free
**      automatically all the memory reserved by them.
**
**      Therefore the caller never has to (and never should)
**      free() any object returned by these functions.
**
**      Therefore also all the strings returned by this package
**      are only valid until the next call to the same function
**      is made. This approach is selected, because of the nature
**      of access authorization: no string returned by the package
**      needs to be valid longer than until the next call.
**
**      This also makes it easy to plug the AA package in:
**      you don't have to ponder whether to free() something
**      here or is it done somewhere else (because it is always
**      done somewhere else).
**
**      The strings that the package needs to store are copied
**      so the original strings given as parameters to AA
**      functions may be freed or modified with no side effects.
**
**      The AA package does not free() anything else than what
**      it has itself allocated.
**
**      AA (Access Authorization) package means modules which
**      names start with HTAA.
**
** AUTHORS:
**      AL      Ari Luotonen    luotonen@dxcern.cern.ch
**      MD      Mark Donszelmann    duns@vxdeop.cern.ch
**
** HISTORY:
**       8 Nov 93  MD   (VMS only) Added case insensitive comparison in HTAA_templateCaseMatch
**
**
** BUGS:
**
**
*/


#include <string.h>
#include "HTUtils.h"
#include "tcp.h"        /* NETREAD() etc.       */
#include "HTAAUtil.h"   /* Implemented here     */
#include "HTAssoc.h"    /* Assoc list           */


/* PUBLIC                                               HTAAScheme_enum()
**              TRANSLATE SCHEME NAME INTO
**              A SCHEME ENUMERATION
**
** ON ENTRY:
**      name            is a string representing the scheme name.
**
** ON EXIT:
**      returns         the enumerated constant for that scheme.
*/

PUBLIC HTAAScheme HTAAScheme_enum ARGS1(CONST char*, name)
{
    static char *upcased = NULL;
    char *cur;

    if (!name) return HTAA_UNKNOWN;

    StrAllocCopy(upcased, name);
    cur = upcased;
    while (*cur) {
        *cur = TOUPPER(*cur);
        cur++;
    }
   
    if (!strncmp(upcased, "NONE", 4))
        return HTAA_NONE;
    else if (!strncmp(upcased, "BASIC", 5))
        return HTAA_BASIC;
    else if (!strncmp(upcased, "PUBKEY", 6))
        return HTAA_PUBKEY;
    else if (!strncmp(upcased, "KERBEROSV4", 10))
        return HTAA_KERBEROS_V4;
    else if (!strncmp(upcased, "KERBEROSV5", 10))
        return HTAA_KERBEROS_V5;
    else
        return HTAA_UNKNOWN;
}


/* PUBLIC                                               HTAAScheme_name()
**                      GET THE NAME OF A GIVEN SCHEME
** ON ENTRY:
**      scheme          is one of the scheme enum values:
**                      HTAA_NONE, HTAA_BASIC, HTAA_PUBKEY, ...
**
** ON EXIT:
**      returns         the name of the scheme, i.e.
**                      "None", "Basic", "Pubkey", ...
*/

PUBLIC char *HTAAScheme_name ARGS1(HTAAScheme, scheme)
{
    switch (scheme) {
      case HTAA_NONE:           return "None";          break;
      case HTAA_BASIC:          return "Basic";         break;
      case HTAA_PUBKEY:         return "Pubkey";        break;
      case HTAA_KERBEROS_V4:    return "KerberosV4";    break;
      case HTAA_KERBEROS_V5:    return "KerberosV5";    break;
      case HTAA_UNKNOWN:        return "UNKNOWN";       break;
      default:                  return "THIS-IS-A-BUG";
    }
}


/* PUBLIC                                                   HTAAMethod_enum()
**              TRANSLATE METHOD NAME INTO AN ENUMERATED VALUE
** ON ENTRY:
**      name            is the method name to translate.
**
** ON EXIT:
**      returns         HTAAMethod enumerated value corresponding
**                      to the given name.
*/

PUBLIC HTAAMethod HTAAMethod_enum ARGS1(CONST char *, name)
{
    char tmp[MAX_METHODNAME_LEN+1];
    CONST char *src = name;
    char *dest = tmp;

    if (!name) return METHOD_UNKNOWN;

    while (*src) {
        *dest = TOUPPER(*src);
        dest++;
        src++;
    }
    *dest = 0;

    if (0==strcmp(tmp, "GET"))
        return METHOD_GET;
    else if (0==strcmp(tmp, "PUT"))
        return METHOD_PUT;
    else
        return METHOD_UNKNOWN;
}


/* PUBLIC                                               HTAAMethod_name()
**                      GET THE NAME OF A GIVEN METHOD
** ON ENTRY:
**      method          is one of the method enum values:
**                      METHOD_GET, METHOD_PUT, ...
**
** ON EXIT:
**      returns         the name of the scheme, i.e.
**                      "GET", "PUT", ...
*/

PUBLIC char *HTAAMethod_name ARGS1(HTAAMethod, method)
{
    switch (method) {
      case METHOD_GET:          return "GET";           break;
      case METHOD_PUT:          return "PUT";           break;
      case METHOD_UNKNOWN:      return "UNKNOWN";       break;
      default:                  return "THIS-IS-A-BUG";
    }
}


/* PUBLIC                                               HTAAMethod_inList()
**              IS A METHOD IN A LIST OF METHOD NAMES
** ON ENTRY:
**      method          is the method to look for.
**      list            is a list of method names.
**
** ON EXIT:
**      returns         YES, if method was found.
**                      NO, if not found.
*/

PUBLIC BOOL HTAAMethod_inList ARGS2(HTAAMethod, method,
                                    HTList *,   list)
{
    HTList *cur = list;
    char *item;

    while (NULL != (item = (char*)HTList_nextObject(cur))) {
        if (TRACE) fprintf(stderr, " %s", item);
        if (method == HTAAMethod_enum(item))
            return YES;
    }

    return NO;  /* Not found */
}



/* PUBLIC                                               HTAA_templateMatch()
**              STRING COMPARISON FUNCTION FOR FILE NAMES
**                 WITH ONE WILDCARD * IN THE TEMPLATE
** NOTE:
**      This is essentially the same code as in HTRules.c, but it
**      cannot be used because it is embedded in between other code.
**      (In fact, HTRules.c should use this routine, but then this
**       routine would have to be more sophisticated... why is life
**       sometimes so hard...)
**
** ON ENTRY:
**      template        is a template string to match the file name
**                      agaist, may contain a single wildcard
**                      character * which matches zero or more
**                      arbitrary characters.
**      filename        is the filename (or pathname) to be matched
**                      agaist the template.
**
** ON EXIT:
**      returns         YES, if filename matches the template.
**                      NO, otherwise.
*/

PUBLIC BOOL HTAA_templateMatch ARGS2(CONST char *, template,
                                     CONST char *, filename)
{
    CONST char *p = template;
    CONST char *q = filename;
    int m;

    for( ; *p  &&  *q  &&  *p == *q; p++, q++)  /* Find first mismatch */
        ; /* do nothing else */

    if (!*p && !*q)     return YES;     /* Equally long equal strings */
    else if ('*' == *p) {               /* Wildcard */
        p++;                            /* Skip wildcard character */
        m = strlen(q) - strlen(p);      /* Amount to match to wildcard */
        if (m < 0) return NO;           /* No match, filename too short */
        else {                  /* Skip the matched characters and compare */
            if (strcmp(p, q+m)) return NO;      /* Tail mismatch */
            else                return YES;     /* Tail match */
        }
    }   /* if wildcard */
    else                return NO;      /* Length or character mismatch */
}    


/* PUBLIC                                               HTAA_templateCaseMatch()
**              STRING COMPARISON FUNCTION FOR FILE NAMES
**                 WITH ONE WILDCARD * IN THE TEMPLATE (Case Insensitive)
** NOTE:
**      This is essentially the same code as in HTAA_templateMatch, but
**      it compares case insensitive (for VMS). Reason for this routine
**      is that HTAA_templateMatch gets called from several places, also
**      there where a case sensitive match is needed, so one cannot just
**      change the HTAA_templateMatch routine for VMS.
**
** ON ENTRY:
**      template        is a template string to match the file name
**                      agaist, may contain a single wildcard
**                      character * which matches zero or more
**                      arbitrary characters.
**      filename        is the filename (or pathname) to be matched
**                      agaist the template.
**
** ON EXIT:
**      returns         YES, if filename matches the template.
**                      NO, otherwise.
*/

PUBLIC BOOL HTAA_templateCaseMatch ARGS2(CONST char *, template,
                                         CONST char *, filename)
{
    CONST char *p = template;
    CONST char *q = filename;
    int m;

    for( ; *p  &&  *q  &&  toupper(*p) == toupper(*q); p++, q++) /* Find first mismatch */
        ; /* do nothing else */

    if (!*p && !*q)     return YES;     /* Equally long equal strings */
    else if ('*' == *p) {               /* Wildcard */
        p++;                            /* Skip wildcard character */
        m = strlen(q) - strlen(p);      /* Amount to match to wildcard */
        if (m < 0) return NO;           /* No match, filename too short */
        else {                  /* Skip the matched characters and compare */
            if (strcasecomp(p, q+m))    return NO;      /* Tail mismatch */
            else                return YES;     /* Tail match */
        }
    }   /* if wildcard */
    else                return NO;      /* Length or character mismatch */
}    


/* PUBLIC                                       HTAA_makeProtectionTemplate()
**              CREATE A PROTECTION TEMPLATE FOR THE FILES
**              IN THE SAME DIRECTORY AS THE GIVEN FILE
**              (Used by server if there is no fancier way for
**              it to tell the client, and by browser if server
**              didn't send WWW-ProtectionTemplate: field)
** ON ENTRY:
**      docname is the document pathname (from URL).
**
** ON EXIT:
**      returns a template matching docname, and other files
**              files in that directory.
**
**              E.g.  /foo/bar/x.html  =>  /foo/bar/ *
**                                                  ^
**                              Space only to prevent it from
**                              being a comment marker here,
**                              there really isn't any space.
*/

PUBLIC char *HTAA_makeProtectionTemplate ARGS1(CONST char *, docname)
{
    char *template = NULL;
    char *slash = NULL;

    if (docname) {
        StrAllocCopy(template, docname);
        slash = strrchr(template, '/');
        if (slash) slash++;
        else slash = template;
        *slash = (char)0;
        StrAllocCat(template, "*");
    }
    else StrAllocCopy(template, "*");

    if (TRACE) fprintf(stderr,
                       "make_template: made template `%s' for file `%s'\n",
                       template, docname);

    return template;
}




/*
** Skip leading whitespace from *s forward
*/

#define SKIPWS(s) while (*s==' ' || *s=='\t') s++;

/*
** Kill trailing whitespace starting from *(s-1) backwords
*/

#define KILLWS(s) {char *c=s-1; while (*c==' ' || *c=='\t') *(c--)=(char)0;}


/* PUBLIC                                               HTAA_parseArgList()
**              PARSE AN ARGUMENT LIST GIVEN IN A HEADER FIELD
** ON ENTRY:
**      str     is a comma-separated list:
**
**                      item, item, item
**              where
**                      item ::= value
**                             | name=value
**                             | name="value"
**
**              Leading and trailing whitespace is ignored
**              everywhere except inside quotes, so the following
**              examples are equal:
**
**                      name=value,foo=bar
**                       name="value",foo="bar"
**                        name = value ,  foo = bar
**                         name = "value" ,  foo = "bar"
**
** ON EXIT:
**      returns a list of name-value pairs (actually HTAssocList*).
**              For items with no name, just value, the name is
**              the number of order number of that item. E.g.
**              "1" for the first, etc.
*/

PUBLIC HTAssocList *HTAA_parseArgList ARGS1(char *, str)
{
    HTAssocList *assoc_list = HTAssocList_new();
    char *cur = NULL;
    char *name = NULL;
    int index = 0;

    if (!str) return assoc_list;

    while (*str) {
        SKIPWS(str);                            /* Skip leading whitespace */
        cur = str;
        index++;

        while (*cur  &&  *cur != '='  &&  *cur != ',')
            cur++;      /* Find end of name (or lonely value without a name) */
        KILLWS(cur);    /* Kill trailing whitespace */

        if (*cur == '=') {                      /* Name followed by a value */
            *(cur++) = (char)0;                 /* Terminate name */
            StrAllocCopy(name, str);
            SKIPWS(cur);                        /* Skip WS leading the value */
            str = cur;
            if (*str == '"') {                  /* Quoted value */
                str++;
                cur = str;
                while (*cur  &&  *cur != '"') cur++;
                if (*cur == '"')
                    *(cur++) = (char)0; /* Terminate value */
                /* else it is lacking terminating quote */
                SKIPWS(cur);                    /* Skip WS leading comma */
                if (*cur == ',') cur++;         /* Skip separating colon */
            }
            else {                              /* Unquoted value */
                while (*cur  &&  *cur != ',') cur++;
                KILLWS(cur);                    /* Kill trailing whitespace */
                if (*cur == ',')
                    *(cur++) = (char)0;
                /* else *cur already NULL */
            }
        }
        else {  /* No name, just a value */
            if (*cur == ',')
                *(cur++) = (char)0;             /* Terminate value */
            /* else last value on line (already terminated by NULL) */
            StrAllocCopy(name, "nnn");  /* Room for item order number */
            sprintf(name, "%d", index); /* Item order number for name */
        }
        HTAssocList_add(assoc_list, name, str);
        str = cur;
    } /* while *str */

    return assoc_list;
}



/************** HEADER LINE READER -- DOES UNFOLDING *************************/

#define BUFFER_SIZE     1024

PRIVATE char buffer[BUFFER_SIZE + 1];
PRIVATE char *start_pointer = buffer;
PRIVATE char *end_pointer = buffer;
PRIVATE int in_soc = -1;

/* PUBLIC                                               HTAA_setupReader()
**              SET UP HEADER LINE READER, i.e. give
**              the already-read-but-not-yet-processed
**              buffer of text to be read before more
**              is read from the socket.
** ON ENTRY:
**      start_of_headers is a pointer to a buffer containing
**                      the beginning of the header lines
**                      (rest will be read from a socket).
**      length          is the number of valid characters in
**                      'start_of_headers' buffer.
**      soc             is the socket to use when start_of_headers
**                      buffer is used up.
** ON EXIT:
**      returns         nothing.
**                      Subsequent calls to HTAA_getUnfoldedLine()
**                      will use this buffer first and then
**                      proceed to read from socket.
*/

PUBLIC void HTAA_setupReader ARGS3(char *,      start_of_headers,
                                   int,         length,
                                   int,         soc)
{
    start_pointer = buffer;
    if (start_of_headers) {
        strncpy(buffer, start_of_headers, length);
        buffer[length] = (char)0;
        end_pointer = buffer + length;
    }
    else {
        *start_pointer = (char)0;
        end_pointer = start_pointer;
    }
    in_soc = soc;
}


/* PUBLIC                                               HTAA_getUnfoldedLine()
**              READ AN UNFOLDED HEADER LINE FROM SOCKET
** ON ENTRY:
**      HTAA_setupReader must absolutely be called before
**      this function to set up internal buffer.
**
** ON EXIT:
**      returns a newly-allocated character string representing
**              the read line.  The line is unfolded, i.e.
**              lines that begin with whitespace are appended
**              to current line.  E.g.
**
**                      Field-Name: Blaa-Blaa
**                       This-Is-A-Continuation-Line
**                       Here-Is_Another
**
**              is seen by the caller as:
**
**      Field-Name: Blaa-Blaa This-Is-A-Continuation-Line Here-Is_Another
**
*/

PUBLIC char *HTAA_getUnfoldedLine NOARGS
{
    char *line = NULL;
    char *cur;
    int count;
    BOOL peek_for_folding = NO;

    if (in_soc < 0) {
        fprintf(stderr, "%s %s\n",
                "HTAA_getUnfoldedLine: buffer not initialized",
                "with function HTAA_setupReader()");
        return NULL;
    }

    for(;;) {

        /* Reading from socket */

        if (start_pointer >= end_pointer) {/*Read the next block and continue*/
            count = NETREAD(in_soc, buffer, BUFFER_SIZE);
            if (count <= 0) {
                in_soc = -1;
                return line;
            }
            start_pointer = buffer;
            end_pointer = buffer + count;
            *end_pointer = (char)0;
#ifdef NOT_ASCII
            cur = start_pointer;
            while (cur < end_pointer) {
                *cur = TOASCII(*cur);
                cur++;
            }
#endif /*NOT_ASCII*/
        }
        cur = start_pointer;


        /* Unfolding */
       
        if (peek_for_folding) {
            if (*cur != ' '  &&  *cur != '\t')
                return line;    /* Ok, no continuation line */
            else                /* So this is a continuation line, continue */
                peek_for_folding = NO;
        }


        /* Finding end-of-line */

        while (cur < end_pointer && *cur != '\n') /* Find the end-of-line */
            cur++;                                /* (or end-of-buffer).  */

       
        /* Terminating line */

        if (cur < end_pointer) {        /* So *cur==LF, terminate line */
            *cur = (char)0;             /* Overwrite LF */
            if (*(cur-1) == '\r')
                *(cur-1) = (char)0;     /* Overwrite CR */
            peek_for_folding = YES;     /* Check for a continuation line */
        }


        /* Copying the result */

        if (line)
            StrAllocCat(line, start_pointer);   /* Append */
        else
            StrAllocCopy(line, start_pointer);  /* A new line */

        start_pointer = cur+1;  /* Skip the read line */

    } /* forever */
}