aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWolfgang Draxinger <Wolfgang.Draxinger@physik.uni-muenchen.de>2013-09-13 21:17:08 +0200
committerWolfgang Draxinger <Wolfgang.Draxinger@physik.uni-muenchen.de>2013-09-13 21:17:08 +0200
commit764e9f839f2b93a7f95b156a09c2c5f2067b95d3 (patch)
treea8a7c17a34bda999cf6be3100c8acdfe33142db9
parent71a023a46a7ab8636ff3e5aa6ae42b181a6ac69d (diff)
downloadlitheweb-764e9f839f2b93a7f95b156a09c2c5f2067b95d3.tar.gz
litheweb-764e9f839f2b93a7f95b156a09c2c5f2067b95d3.tar.bz2
custom base64 decoder
-rw-r--r--coroutine.h181
-rw-r--r--picohttp.c260
-rw-r--r--picohttp.h41
-rw-r--r--picohttp_base64.c78
-rw-r--r--picohttp_base64.h21
-rw-r--r--picohttp_debug.h27
6 files changed, 387 insertions, 221 deletions
diff --git a/coroutine.h b/coroutine.h
deleted file mode 100644
index d54a718..0000000
--- a/coroutine.h
+++ /dev/null
@@ -1,181 +0,0 @@
-/* coroutine.h
- *
- * Coroutine mechanics, implemented on top of standard ANSI C. See
- * http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html for
- * a full discussion of the theory behind this.
- *
- * To use these macros to define a coroutine, you need to write a
- * function that looks something like this.
- *
- * [Simple version using static variables (scr macros)]
- * int ascending (void) {
- * static int i;
- *
- * scrBegin;
- * for (i=0; i<10; i++) {
- * scrReturn(i);
- * }
- * scrFinish(-1);
- * }
- *
- * [Re-entrant version using an explicit context structure (ccr macros)]
- * int ascending (ccrContParam) {
- * ccrBeginContext;
- * int i;
- * ccrEndContext(foo);
- *
- * ccrBegin(foo);
- * for (foo->i=0; foo->i<10; foo->i++) {
- * ccrReturn(foo->i);
- * }
- * ccrFinish(-1);
- * }
- *
- * In the static version, you need only surround the function body
- * with `scrBegin' and `scrFinish', and then you can do `scrReturn'
- * within the function and on the next call control will resume
- * just after the scrReturn statement. Any local variables you need
- * to be persistent across an `scrReturn' must be declared static.
- *
- * In the re-entrant version, you need to declare your persistent
- * variables between `ccrBeginContext' and `ccrEndContext'. These
- * will be members of a structure whose name you specify in the
- * parameter to `ccrEndContext'.
- *
- * The re-entrant macros will malloc() the state structure on first
- * call, and free() it when `ccrFinish' is reached. If you want to
- * abort in the middle, you can use `ccrStop' to free the state
- * structure immediately (equivalent to an explicit return() in a
- * caller-type routine).
- *
- * A coroutine returning void type may call `ccrReturnV',
- * `ccrFinishV' and `ccrStopV', or `scrReturnV', to avoid having to
- * specify an empty parameter to the ordinary return macros.
- *
- * Ground rules:
- * - never put `ccrReturn' or `scrReturn' within an explicit `switch'.
- * - never put two `ccrReturn' or `scrReturn' statements on the same
- * source line.
- *
- * The caller of a static coroutine calls it just as if it were an
- * ordinary function:
- *
- * void main(void) {
- * int i;
- * do {
- * i = ascending();
- * printf("got number %d\n", i);
- * } while (i != -1);
- * }
- *
- * The caller of a re-entrant coroutine must provide a context
- * variable:
- *
- * void main(void) {
- * ccrContext z = 0;
- * do {
- * printf("got number %d\n", ascending (&z));
- * } while (z);
- * }
- *
- * Note that the context variable is set back to zero when the
- * coroutine terminates (by crStop, or by control reaching
- * crFinish). This can make the re-entrant coroutines more useful
- * than the static ones, because you can tell when they have
- * finished.
- *
- * If you need to dispose of a crContext when it is non-zero (that
- * is, if you want to stop calling a coroutine without suffering a
- * memory leak), the caller should call `ccrAbort(ctx)' where `ctx'
- * is the context variable.
- *
- * This mechanism could have been better implemented using GNU C
- * and its ability to store pointers to labels, but sadly this is
- * not part of the ANSI C standard and so the mechanism is done by
- * case statements instead. That's why you can't put a crReturn()
- * inside a switch() statement.
- */
-
-/*
- * coroutine.h is copyright 1995,2000 Simon Tatham.
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation
- * files (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use,
- * copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following
- * conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR
- * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
- * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- *
- * $Id$
- */
-
-#ifndef COROUTINE_H
-#define COROUTINE_H
-
-#include <stdlib.h>
-
-/*
- * `scr' macros for static coroutines.
- */
-
-#define scrBegin static int scrLine = 0; switch(scrLine) { case 0:;
-#define scrFinish(z) } return (z)
-#define scrFinishV } return
-
-#define scrReturn(z) \
- do {\
- scrLine=__LINE__;\
- return (z); case __LINE__:;\
- } while (0)
-#define scrReturnV \
- do {\
- scrLine=__LINE__;\
- return; case __LINE__:;\
- } while (0)
-
-/*
- * `ccr' macros for re-entrant coroutines.
- */
-
-#define ccrContParam void **ccrParam
-
-#define ccrBeginContext struct ccrContextTag { int ccrLine
-#define ccrEndContext(x) } *x = (struct ccrContextTag *)*ccrParam
-
-#define ccrBegin(x) if(!x) {x= *ccrParam=malloc(sizeof(*x)); x->ccrLine=0;}\
- if (x) switch(x->ccrLine) { case 0:;
-#define ccrFinish(z) } free(*ccrParam); *ccrParam=0; return (z)
-#define ccrFinishV } free(*ccrParam); *ccrParam=0; return
-
-#define ccrReturn(z) \
- do {\
- ((struct ccrContextTag *)*ccrParam)->ccrLine=__LINE__;\
- return (z); case __LINE__:;\
- } while (0)
-#define ccrReturnV \
- do {\
- ((struct ccrContextTag *)*ccrParam)->ccrLine=__LINE__;\
- return; case __LINE__:;\
- } while (0)
-
-#define ccrStop(z) do{ free(*ccrParam); *ccrParam=0; return (z); }while(0)
-#define ccrStopV do{ free(*ccrParam); *ccrParam=0; return; }while(0)
-
-#define ccrContext void *
-#define ccrAbort(ctx) do { free (ctx); ctx = 0; } while (0)
-
-#endif /* COROUTINE_H */
diff --git a/picohttp.c b/picohttp.c
index 92b0996..1c166f7 100644
--- a/picohttp.c
+++ b/picohttp.c
@@ -1,24 +1,12 @@
#include "picohttp.h"
-
-#define picohttpIoWrite(ioops,size,buf) (ioops->write(size, buf, ioops->data))
-#define picohttpIoRead(ioops,size,buf) (ioops->read(size, buf, ioops->data))
-#define picohttpIoGetch(ioops) (ioops->getch(ioops->data))
-#define picohttpIoPutch(ioops,c) (ioops->putch(c, ioops->data))
-#define picohttpIoFlush(ioops) (ioops->flush(ioops->data))
-
-#ifdef HOST_DEBUG
-#include <stdio.h>
-#define debug_printf(...) do{fprintf(stderr, __VA_ARGS__);}while(0)
-#define debug_putc(x) do{fputc(x, stderr);}while(0)
-#else
-#define debug_printf(...) do{}while(0)
-#define debug_putc(x) do{}while(0)
-#endif
+#include "picohttp_debug.h"
#include <alloca.h>
#include <string.h>
#include <stdlib.h>
+#include "picohttp_base64.h"
+
static char const PICOHTTP_STR_CRLF[] = "\r\n";
static char const PICOHTTP_STR_CLSP[] = ": ";
static char const PICOHTTP_STR_HTTP_[] = "HTTP/";
@@ -56,9 +44,17 @@ static char const PICOHTTP_STR_NAME__[] = " name=\"";
static char const PICOHTTP_STR_CHUNKED[] = "chunked";
+static char const PICOHTTP_STR_WWW_AUTHENTICATE[] = "WWW-Authenticate";
+static char const PICOHTTP_STR_AUTHORIZATION[] = "Authorization";
+static char const PICOHTTP_STR_BASIC_[] = "Basic ";
+static char const PICOHTTP_STR_DIGEST_[] = "Digest ";
+static char const PICOHTTP_STR_REALM__[] = "realm=\"";
+
/* compilation unit local function forward declarations */
static int picohttpProcessHeaders (
struct picohttpRequest * const req,
+ size_t const hvbuflen,
+ char * const headervalue,
picohttpHeaderFieldCallback headerfieldcallback,
void * const data,
int ch );
@@ -85,6 +81,7 @@ static size_t picohttp_fmt_uint(char *dest, unsigned int i)
return len;
}
+#if 0
static size_t picohttp_fmt_int(char *dest,int i) {
if( i < 0 ) {
if( dest )
@@ -93,6 +90,8 @@ static size_t picohttp_fmt_int(char *dest,int i) {
}
return picohttp_fmt_uint(dest, i);
}
+#endif
+
#else
#include <djb/byte/fmt.h>
#define picohttp_fmt_uint fmt_ulong
@@ -106,6 +105,10 @@ static char const *picohttpStatusString(int code)
return "OK";
case 400:
return "Bad Request";
+ case 401:
+ return "Unauthorized";
+ case 403:
+ return "Forbidden";
case 404:
return "Not Found";
case 414:
@@ -130,6 +133,39 @@ void picohttpStatusResponse(
picohttpResponseWrite(req, strlen(c), c);
}
+void picohttpAuthRequired(
+ struct picohttpRequest *req,
+ char const * const realm )
+{
+ /* FIXME: Fits only for Basic Auth!
+ * Adjust this for Digest Auth header */
+ size_t const www_authenticate_maxlen = 1 + /* terminating 0 */
+ sizeof(PICOHTTP_STR_BASIC_)-1 +
+ sizeof(PICOHTTP_STR_REALM__)-1 +
+ strlen(realm) +
+ 1; /* closing '"' */
+#ifdef PICOWEB_CONFIG_USE_C99VARARRAY
+ char www_authenticate[www_authenticate_maxlen+1];
+#else
+ char *www_authenticate = alloca(www_authenticate_maxlen+1);
+#endif
+ memset(www_authenticate, 0, www_authenticate_maxlen);
+
+ char *c = www_authenticate;
+ memcpy(c, PICOHTTP_STR_BASIC_, sizeof(PICOHTTP_STR_BASIC_)-1);
+ c += sizeof(PICOHTTP_STR_BASIC_)-1;
+ memcpy(c, PICOHTTP_STR_REALM__, sizeof(PICOHTTP_STR_REALM__)-1);
+ c += sizeof(PICOHTTP_STR_REALM__)-1;
+ for(size_t i=0; realm[i]; i++) {
+ *c++ = realm[i];
+ }
+ *c='"';
+
+ req->response.www_authenticate = www_authenticate;
+
+ picohttpStatusResponse(req, PICOHTTP_STATUS_401_UNAUTHORIZED);
+}
+
static uint8_t picohttpIsCRLF(int ch)
{
switch(ch) {
@@ -318,6 +354,7 @@ int picohttpGetch(struct picohttpRequest * const req)
}
if( 0 > (ch = picohttpProcessHeaders(
req,
+ 0, NULL,
NULL, NULL,
ch))
) {
@@ -393,6 +430,7 @@ int picohttpRead(struct picohttpRequest * const req, size_t len, char * const bu
}
if( 0 > (ch = picohttpProcessHeaders(
req,
+ 0, NULL,
NULL, NULL,
ch))
) {
@@ -557,7 +595,7 @@ static int picohttpProcessURL (
return -PICOHTTP_STATUS_400_BAD_REQUEST;
}
- if( (urliter - req->url) >= url_max_length ) {
+ if( (size_t)(urliter - req->url) >= url_max_length ) {
return -PICOHTTP_STATUS_414_REQUEST_URI_TOO_LONG;
}
*urliter = ch;
@@ -617,7 +655,7 @@ static int picohttpProcessQuery (
return -PICOHTTP_STATUS_400_BAD_REQUEST;
}
- if( variter - var >= var_max_length ) {
+ if( (size_t)(variter - var) >= var_max_length ) {
/* variable name in request longer than longest
* variable name accepted by route --> skip to next variable */
do {
@@ -634,7 +672,7 @@ static int picohttpProcessQuery (
ch = picohttpIoGetch(req->ioops);
}
if( '=' == ch ) {
- debug_printf("set variable '%s'\n", var);
+ debug_printf("set variable '%s'\r\n", var);
} else {
}
}
@@ -747,13 +785,93 @@ static void picohttpProcessHeaderContentType(
}
}
+static void picohttpProcessHeaderAuthorization(
+ struct picohttpRequest * const req,
+ char const *authorization )
+{
+ if(!strncmp(authorization,
+ PICOHTTP_STR_BASIC_,
+ sizeof(PICOHTTP_STR_BASIC_)-1)) {
+ authorization += sizeof(PICOHTTP_STR_BASIC_)-1;
+ /* HTTP RFC-2617 Basic Auth */
+
+ if( !req->query.auth
+ || !req->query.auth->username
+ || !req->query.auth->pwresponse ) {
+ return;
+ }
+ size_t user_password_max_len =
+ req->query.auth->username_maxlen +
+ req->query.auth->pwresponse_maxlen;
+
+#ifdef PICOWEB_CONFIG_USE_C99VARARRAY
+
+ char user_password[user_password_max_len+1];
+#else
+ char *user_password = alloca(user_password_max_len+1);
+#endif
+ char const *a = authorization;
+ size_t i = 0;
+ while(*a && i < user_password_max_len) {
+ phb64enc_t e = {0,0,0,0};
+ for(size_t j=0; *a && j < 4; j++) {
+ e[j] = *a++;
+ }
+ phb64raw_t r;
+ size_t l = phb64decode(e, r);
+ for(size_t j=0; j < l && i < user_password_max_len; j++, i++) {
+ user_password[i] = r[j];
+ }
+ }
+ user_password[i] = 0;
+
+ debug_printf(
+ "[picohttp] user_password='%s'\r\n",
+ user_password);
+
+ char *c;
+ for(c = user_password; *c && ':' != *c; c++);
+ if( !*c
+ || (c - user_password >= user_password_max_len)
+ || (c - user_password > req->query.auth->username_maxlen)
+ || (strlen(c+1) > req->query.auth->pwresponse_maxlen) ) {
+ /* no colon found, or colon is last character in string
+ * or username part doesn't fit into auth.username field
+ */
+ return;
+ }
+ memset(req->query.auth->username, 0,
+ req->query.auth->username_maxlen);
+ memset(req->query.auth->pwresponse, 0,
+ req->query.auth->pwresponse_maxlen);
+
+ memcpy( req->query.auth->username,
+ user_password,
+ c - user_password );
+ if(*(++c)) {
+ strncpy(req->query.auth->pwresponse,
+ c,
+ req->query.auth->pwresponse_maxlen);
+ }
+ debug_printf(
+ "[picohttp] Basic Auth: username='%s', password='%s'\r\n",
+ req->query.auth->username,
+ req->query.auth->pwresponse);
+ }
+
+ if(!strncmp(authorization,
+ PICOHTTP_STR_BASIC_,
+ sizeof(PICOHTTP_STR_BASIC_)-1)) {
+ }
+}
+
static void picohttpProcessHeaderField(
void * const data,
char const *headername,
char const *headervalue)
{
struct picohttpRequest * const req = data;
- debug_printf("%s: %s\n", headername, headervalue);
+ debug_printf("[picohttp] %s: %s\r\n", headername, headervalue);
if(!strncmp(headername,
PICOHTTP_STR_CONTENT,
sizeof(PICOHTTP_STR_CONTENT)-1)) {
@@ -791,18 +909,31 @@ static void picohttpProcessHeaderField(
}
return;
}
+
+ if(!strncmp(headername,
+ PICOHTTP_STR_AUTHORIZATION,
+ sizeof(PICOHTTP_STR_AUTHORIZATION)-1)) {
+ picohttpProcessHeaderAuthorization(req, headervalue);
+ return;
+ }
}
static int picohttpProcessHeaders (
struct picohttpRequest * const req,
+ size_t const headervalue_maxlen,
+ char * const headervalue,
picohttpHeaderFieldCallback headerfieldcallback,
- void * const data,
+ void * const cb_data,
int ch )
{
#define PICOHTTP_HEADERNAME_MAX_LEN 32
char headername[PICOHTTP_HEADERNAME_MAX_LEN+1] = {0,};
+
+#if 0
#define PICOHTTP_HEADERVALUE_MAX_LEN 224
char headervalue[PICOHTTP_HEADERVALUE_MAX_LEN+1] = {0,};
+#endif
+
char *hn = headername;
char *hv = headervalue;
@@ -820,8 +951,8 @@ static int picohttpProcessHeaders (
/* read until EOL */
for(;
0 < ch && !picohttpIsCRLF(ch);
- hv = (hv - headervalue) <
- PICOHTTP_HEADERVALUE_MAX_LEN ?
+ hv = (size_t)(hv - headervalue) <
+ (headervalue_maxlen-1) ?
hv+1 : 0 ) {
/* add to header field content */
@@ -833,14 +964,14 @@ static int picohttpProcessHeaders (
} else {
if( *headername && *headervalue && headerfieldcallback )
headerfieldcallback(
- data,
+ cb_data,
headername,
headervalue );
/* new header field */
memset(headername, 0, PICOHTTP_HEADERNAME_MAX_LEN+1);
hn = headername;
- memset(headervalue, 0, PICOHTTP_HEADERVALUE_MAX_LEN+1);
+ memset(headervalue, 0, headervalue_maxlen);
hv = headervalue;
/* read until ':' or EOL */
for(;
@@ -872,23 +1003,48 @@ static int picohttpProcessHeaders (
}
if( *headername && *headervalue && headerfieldcallback)
headerfieldcallback(
- data,
+ cb_data,
headername,
headervalue );
return ch;
}
-void picohttpProcessRequest (
- struct picohttpIoOps const * const ioops,
- struct picohttpURLRoute const * const routes )
+/* This wraps picohttpProcessHeaders with a *large* header value buffer
+ * so that we can process initial headers of a HTTP request, with loads
+ * of content. Most importantly Digest authentication, which can push quite
+ * some data.
+ *
+ * By calling this function from picohttpProcessRequest the allocation
+ * stays within the stack frame of this function. After the function
+ * returns, the stack gets available for the actual request handler.
+ */
+static int picohttp_wrap_request_prochdrs (
+ struct picohttpRequest * const req,
+ int ch )
{
+#if PICOHTTP_NO_DIGEST_AUTH
+ size_t const headervalue_maxlen = 256;
+#else
+ size_t const headervalue_maxlen = 768;
+#endif
+ char headervalue[headervalue_maxlen];
- int ch;
- struct picohttpRequest request = {0,};
+ memset(headervalue, 0, headervalue_maxlen);
+
+ return picohttpProcessHeaders(
+ req,
+ headervalue_maxlen,
+ headervalue,
+ picohttpProcessHeaderField,
+ req,
+ ch );
+}
+size_t picohttpRoutesMaxUrlLength(
+ struct picohttpURLRoute const * const routes )
+{
size_t url_max_length = 0;
-#if 1
for(size_t i = 0; routes[i].urlhead; i++) {
size_t url_length =
strlen(routes[i].urlhead) +
@@ -897,9 +1053,21 @@ void picohttpProcessRequest (
if(url_length > url_max_length)
url_max_length = url_length;
}
-#else
- url_max_length = 512;
-#endif
+ return url_max_length;
+}
+
+void picohttpProcessRequest (
+ struct picohttpIoOps const * const ioops,
+ struct picohttpURLRoute const * const routes,
+ struct picohttpAuthData * const authdata,
+ void *userdata)
+{
+
+ int ch;
+ struct picohttpRequest request;
+ memset(&request, 0, sizeof(request));
+
+ size_t const url_max_length = picohttpRoutesMaxUrlLength(routes);
#ifdef PICOWEB_CONFIG_USE_C99VARARRAY
char url[url_max_length+1];
#else
@@ -916,6 +1084,8 @@ void picohttpProcessRequest (
request.sent.header = 0;
request.sent.octets = 0;
request.received_octets = 0;
+ request.userdata = userdata;
+ request.query.auth = authdata;
request.method = picohttpProcessRequestMethod(ioops);
if( !request.method ) {
@@ -956,11 +1126,7 @@ void picohttpProcessRequest (
goto http_error;
}
- if( 0 > (ch = picohttpProcessHeaders(
- &request,
- picohttpProcessHeaderField,
- &request,
- ch)) )
+ if( 0 > (ch = picohttp_wrap_request_prochdrs(&request, ch)) )
goto http_error;
if( '\r' == ch ) {
@@ -1076,6 +1242,17 @@ int picohttpResponseSendHeaders (
return e;
}
+ /* WWW-Authenticate header */
+ if( req->response.www_authenticate ){
+ if( 0 > (e = picohttpIO_WRITE_STATIC_STR(PICOHTTP_STR_WWW_AUTHENTICATE)) ||
+ 0 > (e = picohttpIO_WRITE_STATIC_STR(PICOHTTP_STR_CLSP)) ||
+ 0 > (e = picohttpIoWrite(
+ req->ioops, strlen(req->response.www_authenticate),
+ req->response.www_authenticate)) ||
+ 0 > (e = picohttpIO_WRITE_STATIC_STR(PICOHTTP_STR_CRLF)) )
+ return e;
+ }
+
if( 0 > (e = picohttpIO_WRITE_STATIC_STR(PICOHTTP_STR_CRLF)) )
return e;
@@ -1313,6 +1490,8 @@ int picohttpMultipartNext(
return -1;
}
+ char headervalbuf[224];
+
for(;;) {
int ch = picohttpMultipartGetch(mp);
if( 0 > ch ) {
@@ -1326,8 +1505,11 @@ int picohttpMultipartNext(
if( 0 > (ch = picohttpGetch(mp->req)) )
return ch;
+ memset(headervalbuf, 0, sizeof(headervalbuf));
if( 0 > (ch = picohttpProcessHeaders(
mp->req,
+ sizeof(headervalbuf),
+ headervalbuf,
picohttpMultipartHeaderField,
mp,
ch)) )
diff --git a/picohttp.h b/picohttp.h
index a15cffb..176b2e0 100644
--- a/picohttp.h
+++ b/picohttp.h
@@ -39,6 +39,8 @@
#define PICOHTTP_STATUS_200_OK 200
#define PICOHTTP_STATUS_400_BAD_REQUEST 400
+#define PICOHTTP_STATUS_401_UNAUTHORIZED 401
+#define PICOHTTP_STATUS_403_FORBIDDEN 402
#define PICOHTTP_STATUS_404_NOT_FOUND 404
#define PICOHTTP_STATUS_405_METHOD_NOT_ALLOWED 405
#define PICOHTTP_STATUS_414_REQUEST_URI_TOO_LONG 414
@@ -55,6 +57,12 @@ struct picohttpIoOps {
void *data;
};
+#define picohttpIoWrite(ioops,size,buf) (ioops->write(size, buf, ioops->data))
+#define picohttpIoRead(ioops,size,buf) (ioops->read(size, buf, ioops->data))
+#define picohttpIoGetch(ioops) (ioops->getch(ioops->data))
+#define picohttpIoPutch(ioops,c) (ioops->putch(c, ioops->data))
+#define picohttpIoFlush(ioops) (ioops->flush(ioops->data))
+
enum picohttpVarType {
PICOHTTP_TYPE_UNDEFINED = 0,
PICOHTTP_TYPE_INTEGER = 1,
@@ -103,6 +111,24 @@ struct picohttpDateTime {
unsigned int s:5; /* seconds / 2 */
};
+struct picohttpAuthData {
+ size_t const username_maxlen;
+ char * const username;
+
+ size_t const realm_maxlen;
+ char* const realm;
+
+ /* Basic auth password or Digest auth response */
+ size_t const pwresponse_maxlen;
+ char * const pwresponse;
+
+ size_t const uri_maxlen;
+ char * const uri;
+
+ int qop;
+
+};
+
struct picohttpRequest {
struct picohttpIoOps const * ioops;
struct picohttpURLRoute const * route;
@@ -122,10 +148,12 @@ struct picohttpRequest {
uint8_t transferencoding;
char multipartboundary[PICOHTTP_MULTIPARTBOUNDARY_MAX_LEN+1];
size_t chunklength;
+ struct picohttpAuthData *auth;
} query;
struct {
char const *contenttype;
char const *disposition;
+ char const *www_authenticate;
struct picohttpDateTime lastmodified;
int max_age;
size_t contentlength;
@@ -137,6 +165,7 @@ struct picohttpRequest {
size_t octets;
uint8_t header;
} sent;
+ void *userdata;
};
struct picohttpMultipart {
@@ -157,13 +186,23 @@ typedef void (*picohttpHeaderFieldCallback)(
char const *headername,
char const *headervalue);
+size_t picohttpRoutesMaxUrlLength(
+ struct picohttpURLRoute const * const routes );
+
void picohttpProcessRequest(
struct picohttpIoOps const * const ioops,
- struct picohttpURLRoute const * const routes );
+ struct picohttpURLRoute const * const routes,
+ struct picohttpAuthData * const authdata,
+ void *userdata );
void picohttpStatusResponse(
struct picohttpRequest *req, int status );
+void picohttpAuthRequired(
+ struct picohttpRequest *req,
+ char const * const realm );
+
+
int picohttpResponseSendHeader (
struct picohttpRequest * const req );
diff --git a/picohttp_base64.c b/picohttp_base64.c
new file mode 100644
index 0000000..1374bff
--- /dev/null
+++ b/picohttp_base64.c
@@ -0,0 +1,78 @@
+#include "picohttp_base64.h"
+
+void phb64encode(
+ phb64raw_t const raw,
+ size_t count,
+ phb64enc_t enc)
+{
+ switch(count) {
+ default: break;
+ case 2:
+ enc[3] = 0xff;
+ case 1:
+ enc[2] = 0xff;
+ }
+ switch(count) {
+ default:
+ return;
+ case 4:
+ enc[3] |= ((raw[3] & 0x3f));
+ case 3:
+ case 2:
+ enc[2] |= ((raw[2] & 0xc0) >> 6) | ((raw[1] & 0x0f) << 2);
+ case 1:
+ enc[1] = ((raw[1] & 0xf0) >> 4) | ((raw[0] & 0x03) << 4);
+ enc[0] = ((raw[0] & 0xfc) >> 2);
+ }
+
+ for(int i = 0; i < 4; i++) {
+ if( 26 > enc[i] ) {
+ enc[i] = enc[i] + 'A';
+ } else
+ if( 52 > enc[i] ) {
+ enc[i] = (enc[i]-26) + 'a';
+ } else
+ if( 62 > enc[i] ) {
+ enc[i] = (enc[i]-52) + '0';
+ } else
+ switch( enc[i] ) {
+ case 62: enc[i] = '+'; break;
+ case 63: enc[i] = '/'; break;
+ default: enc[i] = '=';
+ }
+ }
+}
+
+size_t phb64decode(
+ phb64enc_t const enc,
+ phb64raw_t raw)
+{
+ size_t count = 3;
+ phb64enc_t v;
+ for(int i = 0; i < 4; i++) {
+ if( 'A' <= enc[i] && 'Z' >= enc[i] ) {
+ v[i] = enc[i] - 'A';
+ } else
+ if( 'a' <= enc[i] && 'z' >= enc[i] ) {
+ v[i] = enc[i] - 'a' + 26;
+ } else
+ if( '0' <= enc[i] && '9' >= enc[i] ) {
+ v[i] = enc[i] - '0' + 52;
+ } else
+ switch(enc[i]) {
+ case '+': v[i] = 62; break;
+ case '/': v[i] = 63; break;
+ case 0: /* slightly deviating from the RFC, but reasonable */
+ case '=': v[i] = 0; count--; break;
+ default:
+ return 0;
+ }
+ }
+
+ raw[0] = ((v[0] & 0x3f) << 2) | ((v[1] & 0x30) >> 4);
+ raw[1] = ((v[1] & 0x0f) << 4) | ((v[2] & 0x3c) >> 2);
+ raw[2] = ((v[2] & 0x03) << 6) | ( v[3] & 0x3f );
+
+ return count;
+}
+
diff --git a/picohttp_base64.h b/picohttp_base64.h
new file mode 100644
index 0000000..736b670
--- /dev/null
+++ b/picohttp_base64.h
@@ -0,0 +1,21 @@
+#pragma once
+#ifndef PICOHTTP_BASE64_H
+#define PICOHTTP_BASE64_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+typedef uint8_t phb64raw_t[3];
+typedef char phb64enc_t[4];
+typedef uint32_t phb64state_t;
+
+void phb64encode(
+ phb64raw_t const raw,
+ size_t count,
+ phb64enc_t enc);
+
+size_t phb64decode(
+ phb64enc_t const enc,
+ phb64raw_t raw);
+
+#endif/*PICOHTTP_BASE64_H*/
diff --git a/picohttp_debug.h b/picohttp_debug.h
new file mode 100644
index 0000000..9454f49
--- /dev/null
+++ b/picohttp_debug.h
@@ -0,0 +1,27 @@
+#pragma once
+#ifndef PICOHTTP_DEBUG_H
+#define PICOHTTP_DEBUG_H
+
+#if 1
+#include <util/debug_utils.h>
+#endif
+
+#ifndef debug_printf
+#ifdef HOST_DEBUG
+#include <stdio.h>
+#define debug_printf(...) do{fprintf(stderr, __VA_ARGS__);}while(0)
+#else
+#define debug_printf(...) do{}while(0)
+#endif
+#endif
+
+#ifndef debug_putc
+#ifdef HOST_DEBUG
+#include <stdio.h>
+#define debug_putc(x) do{fputc(x, stderr);}while(0)
+#else
+#define debug_putc(x) do{}while(0)
+#endif
+#endif
+
+#endif/*PICOHTTP_DEBUG_H*/