/* 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 <HTUtils.h>
#include <HTAAUtil.h> /* Implemented here */
#include <HTAssoc.h> /* Assoc list */
#include <HTTCP.h>
#include <HTTP.h>
#ifdef USE_SSL
PRIVATE SSL * Handle = NULL; /* The SSL Handle */
#endif /* USE_SSL */
#include <LYStrings.h>
#include <LYLeaks.h>
/* 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)
{
char *upcased = NULL;
if (!name)
return HTAA_UNKNOWN;
StrAllocCopy(upcased, name);
LYUpperCase(upcased);
if (!strncmp(upcased, "NONE", 4)) {
FREE(upcased);
return HTAA_NONE;
} else if (!strncmp(upcased, "BASIC", 5)) {
FREE(upcased);
return HTAA_BASIC;
} else if (!strncmp(upcased, "PUBKEY", 6)) {
FREE(upcased);
return HTAA_PUBKEY;
} else if (!strncmp(upcased, "KERBEROSV4", 10)) {
FREE(upcased);
return HTAA_KERBEROS_V4;
} else if (!strncmp(upcased, "KERBEROSV5", 10)) {
FREE(upcased);
return HTAA_KERBEROS_V5;
} else {
FREE(upcased);
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";
case HTAA_BASIC:
return "Basic";
case HTAA_PUBKEY:
return "Pubkey";
case HTAA_KERBEROS_V4:
return "KerberosV4";
case HTAA_KERBEROS_V5:
return "KerberosV5";
case HTAA_UNKNOWN:
return "UNKNOWN";
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)
{
if (!name)
return METHOD_UNKNOWN;
if (0==strcasecomp(name, "GET"))
return METHOD_GET;
else if (0==strcasecomp(name, "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";
case METHOD_PUT:
return "PUT";
case METHOD_UNKNOWN:
return "UNKNOWN";
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))) {
CTRACE((tfp, " %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
** against, may contain a single wildcard
** character * which matches zero or more
** arbitrary characters.
** filename is the filename (or pathname) to be matched
** against 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
** against, may contain a single wildcard
** character * which matches zero or more
** arbitrary characters.
** filename is the filename (or pathname) to be matched
** against 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;
/* Find first mismatch */
for (; *p && *q && TOUPPER(*p) == TOUPPER(*q); p++, q++)
; /* 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 = '\0';
StrAllocCat(template, "*");
}
else
StrAllocCopy(template, "*");
CTRACE((tfp, "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) backwards
*/
#define KILLWS(s) {char *c=s-1; while (*c==' ' || *c=='\t') *(c--)='\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 n = 0;
if (!str)
return assoc_list;
while (*str) {
SKIPWS(str); /* Skip leading whitespace */
cur = str;
n++;
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++) = '\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++) = '\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++) = '\0';
/* else *cur already NULL */
}
}
else { /* No name, just a value */
if (*cur == ',')
*(cur++) = '\0'; /* Terminate value */
/* else last value on line (already terminated by NULL) */
HTSprintf0(&name, "%d", n); /* Item order number for name */
}
HTAssocList_add(assoc_list, name, str);
str = cur;
} /* while *str */
FREE(name);
return assoc_list;
}
/************** HEADER LINE READER -- DOES UNFOLDING *************************/
#define BUFFER_SIZE 1024
PRIVATE size_t buffer_length;
PRIVATE char *buffer = 0;
PRIVATE char *start_pointer;
PRIVATE char *end_pointer;
PRIVATE int in_soc = -1;
#ifdef LY_FIND_LEAKS
PRIVATE void FreeHTAAUtil NOARGS
{
FREE(buffer);
}
#endif /* LY_FIND_LEAKS */
/* 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)
{
if (!start_of_headers)
length = 0; /* initialize length (is this reached at all?) */
if (buffer == NULL) { /* first call? */
buffer_length = length;
if (buffer_length < BUFFER_SIZE) /* would fall below BUFFER_SIZE? */
buffer_length = BUFFER_SIZE;
buffer = (char*)malloc((size_t)(sizeof(char)*(buffer_length + 1)));
}
else if (length > (int)buffer_length) { /* need more space? */
buffer_length = length;
buffer = (char*)realloc((char*)buffer,
(size_t)(sizeof(char)*(buffer_length + 1)));
}
if (buffer == NULL) outofmem(__FILE__, "HTAA_setupReader");
#ifdef LY_FIND_LEAKS
atexit(FreeHTAAUtil);
#endif
start_pointer = buffer;
if (start_of_headers) {
strncpy(buffer, start_of_headers, length);
buffer[length] = '\0';
end_pointer = buffer + length;
}
else {
*start_pointer = '\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) {
CTRACE((tfp, "%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*/
#ifdef USE_SSL
if (Handle)
count = SSL_read(Handle, buffer, BUFFER_SIZE);
else
count = NETREAD(in_soc, buffer, BUFFER_SIZE);
#else
count = NETREAD(in_soc, buffer, BUFFER_SIZE);
#endif /* USE_SSL */
if (count <= 0) {
in_soc = -1;
return line;
}
start_pointer = buffer;
end_pointer = buffer + count;
*end_pointer = '\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 = '\0'; /* Overwrite LF */
if (*(cur-1) == '\r')
*(cur-1) = '\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 */
}