From 764e9f839f2b93a7f95b156a09c2c5f2067b95d3 Mon Sep 17 00:00:00 2001 From: Wolfgang Draxinger Date: Fri, 13 Sep 2013 21:17:08 +0200 Subject: custom base64 decoder --- coroutine.h | 181 ------------------------------------- picohttp.c | 260 ++++++++++++++++++++++++++++++++++++++++++++++-------- picohttp.h | 41 ++++++++- picohttp_base64.c | 78 ++++++++++++++++ picohttp_base64.h | 21 +++++ picohttp_debug.h | 27 ++++++ 6 files changed, 387 insertions(+), 221 deletions(-) delete mode 100644 coroutine.h create mode 100644 picohttp_base64.c create mode 100644 picohttp_base64.h create mode 100644 picohttp_debug.h 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 - -/* - * `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 -#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 #include #include +#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 #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 +#include + +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 +#endif + +#ifndef debug_printf +#ifdef HOST_DEBUG +#include +#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 +#define debug_putc(x) do{fputc(x, stderr);}while(0) +#else +#define debug_putc(x) do{}while(0) +#endif +#endif + +#endif/*PICOHTTP_DEBUG_H*/ -- cgit v1.2.3