diff options
-rw-r--r-- | CMakeLists.txt | 3 | ||||
-rw-r--r-- | include/local-def.h | 17 | ||||
-rw-r--r-- | include/proto-def.h | 4 | ||||
-rw-r--r-- | plugins/radio/radio-api.c | 2 | ||||
-rw-r--r-- | plugins/samples/HelloWorld.c | 5 | ||||
-rw-r--r-- | plugins/samples/SamplePost.c | 17 | ||||
-rw-r--r-- | src/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/afb-apis.c | 83 | ||||
-rw-r--r-- | src/afb-apis.h | 2 | ||||
-rw-r--r-- | src/afb-hreq.c | 62 | ||||
-rw-r--r-- | src/afb-hreq.h | 18 | ||||
-rw-r--r-- | src/afb-req-itf.h | 23 | ||||
-rw-r--r-- | src/afb-rest-api.c | 6 | ||||
-rw-r--r-- | src/afb-websock.c | 4 | ||||
-rw-r--r-- | src/helper-api.c | 43 | ||||
-rw-r--r-- | src/http-svc.c | 85 | ||||
-rw-r--r-- | src/main.c | 2 | ||||
-rw-r--r-- | src/session.c | 257 | ||||
-rw-r--r-- | src/session.h | 22 |
19 files changed, 499 insertions, 157 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 58f82406..d1780784 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,10 +18,11 @@ INCLUDE(GNUInstallDirs) add_compile_options(-Wall -Wextra -Wconversion) add_compile_options(-Wno-unused-parameter) # frankly not using a parameter does it care? add_compile_options(-Werror=maybe-uninitialized) -#add_compile_options(-Werror=implicit-function-declaration) +add_compile_options(-Werror=implicit-function-declaration) add_compile_options(-ffunction-sections -fdata-sections) add_compile_options(-Wl,--gc-sections) add_compile_options(-fPIC) +add_compile_options(-g) set(CMAKE_C_FLAGS_PROFILING "-g -O2 -pg -Wp,-U_FORTIFY_SOURCE") set(CMAKE_C_FLAGS_DEBUG "-g -O2 -ggdb -Wp,-U_FORTIFY_SOURCE") diff --git a/include/local-def.h b/include/local-def.h index 91b61abc..3f618717 100644 --- a/include/local-def.h +++ b/include/local-def.h @@ -202,15 +202,6 @@ typedef struct { size_t len; } AFB_redirect_msg; -// User Client Session Context -typedef struct { - char uuid[37]; // long term authentication of remote client - char token[37]; // short term authentication of remote client - time_t timeStamp; // last time token was refresh - int restfull; // client does not use cookie - void **contexts; // application specific context [one per plugin]] -} AFB_clientCtx; - // main config structure struct AFB_config { @@ -233,15 +224,17 @@ struct AFB_config typedef struct { const char *uuid; const char *url; - char *prefix; // plugin convivial name - char *method; + const char *prefix; // plugin convivial name + const char *method; +/* AFB_PostRequest *post; +*/ json_object *jresp; void *context; // Hold Client Context when using session int restfull; // request is resfull [uuid token provided] int errcode; // http error code struct AFB_config *config; // plugin may need access to config - struct MHD_Connection *connection; + struct afb_req *areq; } AFB_request; struct afb_hsrv_handler; diff --git a/include/proto-def.h b/include/proto-def.h index 0db8abb1..c364035a 100644 --- a/include/proto-def.h +++ b/include/proto-def.h @@ -22,10 +22,10 @@ // helper-api extern const char* getQueryValue (const AFB_request * request, const char *name); extern int getQueryAll(AFB_request * request, char *query, size_t len); -extern AFB_PostHandle* getPostHandle (AFB_request *request); +/* extern json_object* getPostFile (AFB_request *request, AFB_PostItem *item, char* destination) ; -extern AFB_PostCtx* getPostContext (AFB_request *request); extern char* getPostPath (AFB_request *request); +*/ extern json_object *jsonNewMessage (AFB_error level, char* format, ...); diff --git a/plugins/radio/radio-api.c b/plugins/radio/radio-api.c index bdb1768f..c6e4b905 100644 --- a/plugins/radio/radio-api.c +++ b/plugins/radio/radio-api.c @@ -329,6 +329,6 @@ PUBLIC AFB_plugin* pluginRegister () { plugin->freeCtxCB = (AFB_freeCtxCB)freeRadio; - radio = initRadioPlugin(); + the_radio = initRadioPlugin(); return plugin; }; diff --git a/plugins/samples/HelloWorld.c b/plugins/samples/HelloWorld.c index 067365dd..6c2d0cf9 100644 --- a/plugins/samples/HelloWorld.c +++ b/plugins/samples/HelloWorld.c @@ -29,11 +29,8 @@ STATIC json_object* pingSample (AFB_request *request) { len = getQueryAll (request, query, sizeof(query)); if (len == 0) strcpy (query,"NoSearchQueryList"); - // check if we have some post data - if (request->post != NULL) request->post->data="NoData"; - // return response to caller - response = jsonNewMessage(AFB_SUCCESS, "Ping Binder Daemon %d query={%s} PostData: \'%s\' ", pingcount++, query, request->post); + response = jsonNewMessage(AFB_SUCCESS, "Ping Binder Daemon %d query={%s}", pingcount++, query); if (verbose) fprintf(stderr, "%d: \n", pingcount); return (response); diff --git a/plugins/samples/SamplePost.c b/plugins/samples/SamplePost.c index d29fb05a..24ee5385 100644 --- a/plugins/samples/SamplePost.c +++ b/plugins/samples/SamplePost.c @@ -23,38 +23,31 @@ static json_object* getPingTest(AFB_request *request) { static int pingcount = 0; json_object *response; - char query [256]; - char session[256]; + char query [8000]; int len; // request all query key/value len = getQueryAll (request, query, sizeof(query)); if (len == 0) strncpy (query, "NoSearchQueryList", sizeof(query)); - // check if we have some post data - if (request->post == NULL) request->post->data="NoData"; - // return response to caller - response = jsonNewMessage(AFB_SUCCESS, "Ping Binder Daemon count=%d uuid=%s query={%s} session={0x%x} PostData: [%s] " - , pingcount++, request->uuid, query, session, request->post->data); + response = jsonNewMessage(AFB_SUCCESS, "Ping Binder Daemon count=%d uuid=%s query={%s}" + , pingcount++, request->uuid, query); return (response); } // With content-type=json data are directly avaliable in request->post->data STATIC json_object* GetJsonByPost (AFB_request *request) { json_object* jresp; - char query [256]; + char query [8000]; int len; - // check if we have some post data - if (request->post == NULL) request->post->data="NoData"; - // Get all query string [Note real app should probably use value=getQueryValue(request,"key")] len = getQueryAll (request, query, sizeof(query)); if (len == 0) strncpy (query, "NoSearchQueryList", sizeof(query)); // for debug/test return response to caller - jresp = jsonNewMessage(AFB_SUCCESS, "GetJsonByPost query={%s} PostData: [%s]", query, request->post->data); + jresp = jsonNewMessage(AFB_SUCCESS, "GetJsonByPost query={%s}", query); return (jresp); } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 03878dc2..9ddb5113 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,7 +3,6 @@ ADD_LIBRARY(src OBJECT main.c session.c http-svc.c - afb-rest-api.c afb-apis.c afb-method.c afb-hreq.c diff --git a/src/afb-apis.c b/src/afb-apis.c index 3dcad651..ca427437 100644 --- a/src/afb-apis.c +++ b/src/afb-apis.c @@ -35,6 +35,7 @@ #include "../include/local-def.h" +#include "afb-req-itf.h" #include "afb-apis.h" struct api_desc { @@ -278,3 +279,85 @@ int afb_apis_add_pathset(const char *pathset) }; } +/* +// Check of apiurl is declare in this plugin and call it +extern __thread sigjmp_buf *error_handler; +static int callPluginApi(AFB_request * request) +{ + sigjmp_buf jmpbuf, *older; + + // save context before calling the API + status = setjmp(jmpbuf); + if (status != 0) { + return 0; + } + + // Trigger a timer to protect from unacceptable long time execution + if (request->config->apiTimeout > 0) + alarm((unsigned)request->config->apiTimeout); + + older = error_handler; + error_handler = &jmpbuf; + doCallPluginApi(request, apiidx, verbidx, context); + error_handler = older; + + // cancel timeout and plugin signal handle before next call + alarm(0); + return 1; +} +*/ + +static void handle(struct afb_req req, const struct api_desc *api, const struct AFB_restapi *verb) +{ + AFB_request request; + + request.uuid = request.url = "fake"; + request.prefix = api->prefix; + request.method = verb->name; + request.context = NULL; + request.restfull = 0; + request.errcode = 0; + request.config = NULL; + request.areq = &req; + + switch(verb->session) { + case AFB_SESSION_CREATE: + case AFB_SESSION_RENEW: + /*if (check) new*/ + break; + case AFB_SESSION_CLOSE: + case AFB_SESSION_CHECK: + /*check*/ + break; + case AFB_SESSION_NONE: + default: + break; + } + verb->callback(&request, NULL); + + if (verb->session == AFB_SESSION_CLOSE) + /*del*/; +} + +int afb_apis_handle(struct afb_req req, const char *api, size_t lenapi, const char *verb, size_t lenverb) +{ + int i, j; + const struct api_desc *a; + const struct AFB_restapi *v; + + a = apis_array; + for (i = 0 ; i < apis_count ; i++, a++) { + if (a->prefixlen == lenapi && !strcasecmp(a->prefix, api)) { + v = a->plugin->apis; + for (j = 0 ; v->name ; j++, v++) { + if (!strncasecmp(v->name, verb, lenverb) && !v->name[lenverb]) { + handle(req, a, v); + return 1; + } + } + break; + } + } + return 0; +} + diff --git a/src/afb-apis.h b/src/afb-apis.h index a37df5d3..dc4401cf 100644 --- a/src/afb-apis.h +++ b/src/afb-apis.h @@ -34,4 +34,6 @@ extern int afb_apis_add_path(const char *path); extern int afb_apis_add_pathset(const char *pathset); +struct afb_req; +extern int afb_apis_handle(struct afb_req req, const char *api, size_t lenapi, const char *verb, size_t lenverb); diff --git a/src/afb-hreq.c b/src/afb-hreq.c index 60ff6b01..16114675 100644 --- a/src/afb-hreq.c +++ b/src/afb-hreq.c @@ -36,6 +36,12 @@ struct hreq_data { char *value; }; +static const struct afb_req_itf afb_hreq_itf = { + .argument = (void*)afb_hreq_get_argument, + .is_argument_file = (void*)afb_hreq_is_argument_a_file, + .iterate_arguments = (void*)afb_hreq_iterate_arguments +}; + static struct hreq_data *get_data(struct afb_hreq *hreq, const char *key, int create) { struct hreq_data *data = hreq->data; @@ -279,12 +285,6 @@ const char *afb_hreq_get_header(struct afb_hreq *hreq, const char *name) return MHD_lookup_connection_value(hreq->connection, MHD_HEADER_KIND, name); } -const struct afb_req_itf afb_hreq_itf = { - .get_cookie = (void*)afb_hreq_get_cookie, - .get_argument = (void*)afb_hreq_get_argument -}; - - void afb_hreq_post_end(struct afb_hreq *hreq) { struct hreq_data *data = hreq->data; @@ -340,3 +340,53 @@ int afb_hreq_post_add_file(struct afb_hreq *hreq, const char *key, const char *f } +int afb_hreq_is_argument_a_file(struct afb_hreq *hreq, const char *key) +{ + struct hreq_data *hdat = get_data(hreq, key, 0); + return hdat != NULL && hdat->file != 0; +} + + +struct afb_req afb_hreq_to_req(struct afb_hreq *hreq) +{ + return (struct afb_req){ .itf = &afb_hreq_itf, .data = hreq }; +} + +struct iterator_data +{ + struct afb_hreq *hreq; + int (*iterator)(void *closure, const char *key, const char *value, int isfile); + void *closure; +}; + +static int itargs(struct iterator_data *id, enum MHD_ValueKind kind, const char *key, const char *value) +{ + if (get_data(id->hreq, key, 0)) + return 1; + return id->iterator(id->closure, key, value, 0); +} + +void afb_hreq_iterate_arguments(struct afb_hreq *hreq, int (*iterator)(void *closure, const char *key, const char *value, int isfile), void *closure) +{ + struct iterator_data id = { .hreq = hreq, .iterator = iterator, .closure = closure }; + struct hreq_data *data = hreq->data; + while (data) { + if (!iterator(closure, data->key, data->value, !!data->file)) + return; + data = data->next; + } + MHD_get_connection_values (hreq->connection, MHD_GET_ARGUMENT_KIND, (void*)itargs, &id); +} + +void afb_hreq_drop_data(struct afb_hreq *hreq) +{ + struct hreq_data *data = hreq->data; + while (data) { + hreq->data = data->next; + free(data->key); + free(data->value); + free(data); + data = hreq->data; + } +} + diff --git a/src/afb-hreq.h b/src/afb-hreq.h index 7e774276..01409a58 100644 --- a/src/afb-hreq.h +++ b/src/afb-hreq.h @@ -26,7 +26,8 @@ struct afb_hreq { const char *tail; size_t lentail; struct MHD_PostProcessor *postform; - void *data; + struct AFB_clientCtx *context; + struct hreq_data *data; }; extern int afb_hreq_unprefix(struct afb_hreq *request, const char *prefix, size_t length); @@ -43,16 +44,21 @@ extern int afb_hreq_redirect_to(struct afb_hreq *request, const char *url); extern const char *afb_hreq_get_cookie(struct afb_hreq *hreq, const char *name); +extern const char *afb_hreq_get_header(struct afb_hreq *hreq, const char *name); + extern const char *afb_hreq_get_argument(struct afb_hreq *hreq, const char *name); -extern const char *afb_hreq_get_header(struct afb_hreq *hreq, const char *name); +extern int afb_hreq_is_argument_a_file(struct afb_hreq *hreq, const char *name); -extern int afb_hreq_post_add_file(struct afb_hreq *hreq, const char *key, const char *file, const char *data, size_t size); +extern int afb_hreq_post_add_file(struct afb_hreq *hreq, const char *name, const char *file, const char *data, size_t size); -extern int afb_hreq_post_add(struct afb_hreq *hreq, const char *key, const char *data, size_t size); +extern int afb_hreq_post_add(struct afb_hreq *hreq, const char *name, const char *data, size_t size); extern void afb_hreq_post_end(struct afb_hreq *hreq); -struct afb_req_itf; -extern const struct afb_req_itf afb_hreq_itf; +extern struct afb_req afb_hreq_to_req(struct afb_hreq *hreq); + +extern void afb_hreq_drop_data(struct afb_hreq *hreq); + +extern void afb_hreq_iterate_arguments(struct afb_hreq *hreq, int (*iterator)(void *closure, const char *key, const char *value, int isfile), void *closure); diff --git a/src/afb-req-itf.h b/src/afb-req-itf.h index 6fa854c2..d747d0b8 100644 --- a/src/afb-req-itf.h +++ b/src/afb-req-itf.h @@ -17,26 +17,29 @@ struct afb_req_itf { - const char *(*get_cookie)(void *data, const char *name); - const char *(*get_argument)(void *data, const char *name); -#if 0 - int (*set_cookie)(void *data, const char *name, const char *value); -#endif + const char *(*argument)(void *data, const char *name); + int (*is_argument_file)(void *data, const char *name); + int (*iterate_arguments)(void *data, int (*iterator)(void *closure, const char *key, const char *value, int isfile), void *closure); }; struct afb_req { - struct afb_req_itf *itf; + const struct afb_req_itf *itf; void *data; }; -inline const char *afb_get_cookie(struct afb_req req, const char *name) +static inline const char *afb_req_argument(struct afb_req req, const char *name) { - return req.itf->get_cookie(req.data, name); + return req.itf->argument(req.data, name); } -inline const char *afb_get_argument(struct afb_req req, const char *name) +static inline int afb_req_argument_file(struct afb_req req, const char *name) { - return req.itf->get_argument(req.data, name); + return req.itf->is_argument_file(req.data, name); +} + +static inline int afb_req_iterate_arguments(struct afb_req req, int (*iterator)(void *closure, const char *key, const char *value, int isfile), void *closure) +{ + return req.itf->iterate_arguments(req.data, iterator, closure); } diff --git a/src/afb-rest-api.c b/src/afb-rest-api.c index 49e5d8b2..80de7d65 100644 --- a/src/afb-rest-api.c +++ b/src/afb-rest-api.c @@ -315,8 +315,8 @@ STATIC int doPostIterate(void *cls, enum MHD_ValueKind kind, const char *key, co STATIC void freeRequest(AFB_request * request) { - free(request->prefix); - free(request->method); + free((void*)request->prefix); + free((void*)request->method); free(request); } @@ -346,7 +346,7 @@ STATIC AFB_request *createRequest(struct MHD_Connection *connection, AFB_session goto Done; } // build request structure - request->connection = connection; +// request->connection = connection; request->config = session->config; request->url = url; request->prefix = strdup(baseurl); diff --git a/src/afb-websock.c b/src/afb-websock.c index b6044326..23d489ab 100644 --- a/src/afb-websock.c +++ b/src/afb-websock.c @@ -172,7 +172,7 @@ int afb_websock_check(struct afb_hreq *hreq, int *later) /* is a supported version ? */ vernum = atoi(version); if (vernum != 13) { - response = MHD_create_response_from_data(0,NULL,0,0); + response = MHD_create_response_from_buffer(0, NULL, MHD_RESPMEM_PERSISTENT); MHD_add_response_header (response, sec_websocket_version_s, "13"); MHD_queue_response (hreq->connection, MHD_HTTP_BAD_REQUEST, response); MHD_destroy_response (response); @@ -185,7 +185,7 @@ int afb_websock_check(struct afb_hreq *hreq, int *later) /* send the accept connection */ make_accept_value(key, acceptval); - response = MHD_create_response_from_data(0,NULL,0,0); + response = MHD_create_response_from_buffer(0, NULL, MHD_RESPMEM_PERSISTENT); MHD_add_response_header (response, sec_websocket_accept_s, acceptval); MHD_add_response_header (response, MHD_HTTP_HEADER_CONNECTION, MHD_HTTP_HEADER_UPGRADE); MHD_add_response_header (response, MHD_HTTP_HEADER_UPGRADE, websocket_s); diff --git a/src/helper-api.c b/src/helper-api.c index 303fd57d..58015d12 100644 --- a/src/helper-api.c +++ b/src/helper-api.c @@ -23,11 +23,12 @@ #include <sys/types.h> #include <stdarg.h> +#include "afb-req-itf.h" // handle to hold queryAll values typedef struct { char *msg; - int idx; + size_t idx; size_t len; } queryHandleT; @@ -49,17 +50,14 @@ static const char *ERROR_LABEL[] = {"false", "true", "fatal", "fail", "warning", // Helper to retrieve argument from connection const char* getQueryValue(const AFB_request * request, const char *name) { - const char *value; - - value = MHD_lookup_connection_value(request->connection, MHD_GET_ARGUMENT_KIND, name); - return (value); + return afb_req_argument(*request->areq, name); } -static int getQueryCB (void*handle, enum MHD_ValueKind kind, const char *key, const char *value) { - queryHandleT *query = (queryHandleT*)handle; - - query->idx += snprintf (&query->msg[query->idx],query->len," %s: \'%s\',", key, value); - return MHD_YES; /* continue to iterate */ +static int getQueryCB (queryHandleT *query, const char *key, const char *value, int isfile) { + if (query->idx >= query->len) + return 0; + query->idx += snprintf (&query->msg[query->idx], query->len-query->idx, " %s: %s\'%s\',", key, isfile?"FILE=":"", value); + return 1; /* continue to iterate */ } // Helper to retrieve argument from connection @@ -70,27 +68,12 @@ int getQueryAll(AFB_request * request, char *buffer, size_t len) { query.len = len; query.idx = 0; - MHD_get_connection_values (request->connection, MHD_GET_ARGUMENT_KIND, getQueryCB, &query); - return (len); -} - -// Helper to retrieve POST handle -AFB_PostHandle* getPostHandle (AFB_request *request) { - if (request->post == NULL) return (NULL); - return ((AFB_PostHandle*) request->post->data); -} - -// Helper to retrieve POST file context -AFB_PostCtx* getPostContext (AFB_request *request) { - AFB_PostHandle* postHandle; - if (request->post == NULL) return (NULL); - - postHandle = (AFB_PostHandle*) request->post->data; - if (postHandle == NULL) return NULL; - - return ((AFB_PostCtx*) postHandle->ctx); + afb_req_iterate_arguments(*request->areq, getQueryCB, &query); + buffer[len-1] = 0; + return query.idx >= len ? len - 1 : query.idx; } +#if 0 char* getPostPath (AFB_request *request) { AFB_PostHandle *postHandle = getPostHandle(request); AFB_PostCtx *postFileCtx; @@ -212,7 +195,7 @@ ExitOnError: return NULL; } - +#endif static void jsoninit() { diff --git a/src/http-svc.c b/src/http-svc.c index e8ccfe83..9fa6e84c 100644 --- a/src/http-svc.c +++ b/src/http-svc.c @@ -25,6 +25,9 @@ #include "afb-method.h" #include "afb-hreq.h" #include "afb-websock.h" +#include "afb-apis.h" +#include "session.h" +#include "afb-req-itf.h" #define JSON_CONTENT "application/json" #define FORM_CONTENT MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA @@ -129,21 +132,59 @@ int afb_hsrv_add_handler( return 1; } -static int relay_to_doRestApi(struct afb_hreq *hreq, void *data) +static const char uuid_header[] = "x-afb-uuid"; +static const char uuid_arg[] = "uuid"; +static const char uuid_cookie[] = "uuid"; + +static struct AFB_clientCtx *afb_hreq_context(struct afb_hreq *hreq) +{ + const char *uuid; + + if (hreq->context == NULL) { + uuid = afb_hreq_get_header(hreq, uuid_header); + if (uuid == NULL) + uuid = afb_hreq_get_argument(hreq, uuid_arg); + if (uuid == NULL) + uuid = afb_hreq_get_cookie(hreq, uuid_cookie); + hreq->context = _ctxClientGet(uuid); + } + return hreq->context; +} + +static int afb_hreq_websocket_switch(struct afb_hreq *hreq, void *data) { int later; - if (hreq->lentail == 0 && afb_websock_check(hreq, &later)) { - if (!later) { - struct afb_websock *ws = afb_websock_create(hreq->connection); + + afb_hreq_context(hreq); + if (hreq->lentail != 0 || !afb_websock_check(hreq, &later)) + return 0; + + if (!later) { + struct afb_websock *ws = afb_websock_create(hreq->connection); + if (ws == NULL) { + /* TODO */ + } else { + /* TODO */ } - return 1; } + return 1; +} -return 0; -/* - return doRestApi(hreq->connection, hreq->session, &hreq->tail[1], get_method_name(hreq->method), - post->upload_data, post->upload_data_size, (void **)hreq->recorder); -*/ +static int afb_hreq_rest_api(struct afb_hreq *hreq, void *data) +{ + const char *api, *verb; + size_t lenapi, lenverb; + + api = hreq->tail; + lenapi = strspn(api, "/"); + verb = &hreq->tail[lenapi]; + verb = &verb[strcspn(verb, "/")]; + lenverb = strspn(verb, "/"); + + if (!(*api && *verb && lenapi && lenverb)) + return 0; + + return afb_apis_handle(afb_hreq_to_req(hreq), api, lenapi, verb, lenverb); } static int handle_alias(struct afb_hreq *hreq, void *data) @@ -232,6 +273,7 @@ static int access_handler( size_t *upload_data_size, void **recordreq) { + int rc; struct afb_hreq *hreq; enum afb_method method; AFB_session *session; @@ -250,10 +292,8 @@ static int access_handler( /* get the method */ method = get_method(methodstr); method &= afb_method_get | afb_method_post; - if (method == afb_method_none) { - afb_hsrv_reply_error(connection, MHD_HTTP_BAD_REQUEST); - return MHD_YES; - } + if (method == afb_method_none) + goto bad_request; /* init the request */ hreq->session = cls; @@ -295,6 +335,12 @@ static int access_handler( /* flush the data */ afb_hreq_post_end(hreq); + if (hreq->postform != NULL) { + rc = MHD_destroy_post_processor(hreq->postform); + hreq->postform = NULL; + if (rc == MHD_NO) + goto bad_request; + } /* search an handler for the request */ iter = session->handlers; @@ -312,6 +358,10 @@ static int access_handler( afb_hreq_reply_error(hreq, MHD_HTTP_NOT_FOUND); return MHD_YES; +bad_request: + afb_hsrv_reply_error(connection, MHD_HTTP_BAD_REQUEST); + return MHD_YES; + internal_error: afb_hsrv_reply_error(connection, MHD_HTTP_INTERNAL_SERVER_ERROR); return MHD_YES; @@ -330,6 +380,8 @@ static void end_handler(void *cls, struct MHD_Connection *connection, void **rec if (hreq != NULL) { if (hreq->postform != NULL) MHD_destroy_post_processor(hreq->postform); + afb_hreq_drop_data(hreq); + free(hreq); } } @@ -372,7 +424,10 @@ static int my_default_init(AFB_session * session) { int idx; - if (!afb_hsrv_add_handler(session, session->config->rootapi, relay_to_doRestApi, NULL, 1)) + if (!afb_hsrv_add_handler(session, session->config->rootapi, afb_hreq_websocket_switch, NULL, 20)) + return 0; + + if (!afb_hsrv_add_handler(session, session->config->rootapi, afb_hreq_rest_api, NULL, 10)) return 0; for (idx = 0; session->config->aliasdir[idx].url != NULL; idx++) @@ -549,7 +549,7 @@ int main(int argc, char *argv[]) { if (session->config->ldpaths) afb_apis_add_pathset(session->config->ldpaths); - ctxStoreInit(CTX_NBCLIENTS, session->config->cntxTimeout, afb_apis_count()); + ctxStoreInit(CTX_NBCLIENTS, session->config->cntxTimeout, afb_apis_count(), session->config->token); install_error_handlers(); diff --git a/src/session.c b/src/session.c index 3ebd9826..eded1416 100644 --- a/src/session.c +++ b/src/session.c @@ -29,10 +29,13 @@ #include <sys/types.h> #include <pthread.h> #include <search.h> +#include <assert.h> #include "afb-apis.h" #include "session.h" +#define NOW (time(NULL)) + // Session UUID are store in a simple array [for 10 sessions this should be enough] static struct { pthread_mutex_t mutex; // declare a mutex to protect hash table @@ -41,6 +44,7 @@ static struct { int max; int timeout; int apicount; + const char *initok; } sessions; static const char key_uuid[] = "uuid"; @@ -64,14 +68,18 @@ static void ctxUuidFreeCB (AFB_clientCtx *client) } // Create a new store in RAM, not that is too small it will be automatically extended -void ctxStoreInit (int nbSession, int timeout, int apicount) +void ctxStoreInit (int nbSession, int timeout, int apicount, const char *initok) { - - // let's create as store as hashtable does not have any - sessions.store = calloc (1 + (unsigned)nbSession, sizeof(AFB_clientCtx)); - sessions.max = nbSession; - sessions.timeout = timeout; - sessions.apicount = apicount; + // let's create as store as hashtable does not have any + sessions.store = calloc (1 + (unsigned)nbSession, sizeof(AFB_clientCtx)); + sessions.max = nbSession; + sessions.timeout = timeout; + sessions.apicount = apicount; + if (strlen(initok) >= 37) { + fprintf(stderr, "Error: initial token '%s' too long (max length 36)", initok); + exit(1); + } + sessions.initok = initok; } static AFB_clientCtx *ctxStoreSearch (const char* uuid) @@ -79,19 +87,19 @@ static AFB_clientCtx *ctxStoreSearch (const char* uuid) int idx; AFB_clientCtx *client; - if (uuid == NULL) - return NULL; + assert (uuid != NULL); pthread_mutex_lock(&sessions.mutex); for (idx=0; idx < sessions.max; idx++) { - if (sessions.store[idx] && (0 == strcmp (uuid, sessions.store[idx]->uuid))) break; + client = sessions.store[idx]; + if (client && (0 == strcmp (uuid, client->uuid))) + goto found; } + client = NULL; - if (idx == sessions.max) client=NULL; - else client= sessions.store[idx]; +found: pthread_mutex_unlock(&sessions.mutex); - return client; } @@ -100,24 +108,21 @@ static AFB_error ctxStoreDel (AFB_clientCtx *client) int idx; int status; - if (client == NULL) - return AFB_FAIL; + assert (client != NULL); pthread_mutex_lock(&sessions.mutex); for (idx=0; idx < sessions.max; idx++) { - if (sessions.store[idx] && (0 == strcmp (client->uuid, sessions.store[idx]->uuid))) break; + if (sessions.store[idx] == client) { + sessions.store[idx]=NULL; + sessions.count--; + ctxUuidFreeCB (client); + status = AFB_SUCCESS; + goto deleted; + } } - - if (idx == sessions.max) - status = AFB_FAIL; - else { - sessions.count--; - ctxUuidFreeCB (sessions.store[idx]); - sessions.store[idx]=NULL; - status = AFB_SUCCESS; - } - + status = AFB_FAIL; +deleted: pthread_mutex_unlock(&sessions.mutex); return status; } @@ -134,39 +139,37 @@ static AFB_error ctxStoreAdd (AFB_clientCtx *client) pthread_mutex_lock(&sessions.mutex); for (idx=0; idx < sessions.max; idx++) { - if (NULL == sessions.store[idx]) break; - } - - if (idx == sessions.max) status=AFB_FAIL; - else { - status=AFB_SUCCESS; - sessions.count ++; - sessions.store[idx]= client; + if (NULL == sessions.store[idx]) { + sessions.store[idx]= client; + sessions.count++; + status = AFB_SUCCESS; + goto added; + } } + status = AFB_FAIL; +added: pthread_mutex_unlock(&sessions.mutex); return status; } // Check if context timeout or not -static int ctxStoreTooOld (AFB_clientCtx *ctx, int timeout) +static int ctxStoreTooOld (AFB_clientCtx *ctx, time_t now) { - int res; - time_t now = time(NULL); - res = (ctx->timeStamp + timeout) <= now; - return res; + return ctx->timeStamp <= now; } // Loop on every entry and remove old context sessions.hash -void ctxStoreGarbage (const int timeout) +void ctxStoreGarbage () { AFB_clientCtx *ctx; long idx; + time_t now = NOW; // Loop on Sessions Table and remove anything that is older than timeout for (idx=0; idx < sessions.max; idx++) { ctx = sessions.store[idx]; - if ((ctx != NULL) && (ctxStoreTooOld(ctx, timeout))) { + if ((ctx != NULL) && (ctxStoreTooOld(ctx, now))) { ctxStoreDel (ctx); } } @@ -182,7 +185,7 @@ AFB_clientCtx *ctxClientGet (AFB_request *request) if (request->config->token == NULL) return NULL; // Check if client as a context or not inside the URL - uuid = MHD_lookup_connection_value(request->connection, MHD_GET_ARGUMENT_KIND, key_uuid); + uuid = NULL; //MHD_lookup_connection_value(request->connection, MHD_GET_ARGUMENT_KIND, key_uuid); // if UUID in query we're restfull with no cookies otherwise check for cookie if (uuid != NULL) @@ -191,7 +194,7 @@ AFB_clientCtx *ctxClientGet (AFB_request *request) char cookie[64]; request->restfull = FALSE; snprintf(cookie, sizeof cookie, "%s-%d", COOKIE_NAME, request->config->httpdPort); - uuid = MHD_lookup_connection_value (request->connection, MHD_COOKIE_KIND, cookie); + uuid = NULL; //MHD_lookup_connection_value (request->connection, MHD_COOKIE_KIND, cookie); }; // Warning when no cookie defined MHD_lookup_connection_value may return something !!! @@ -200,7 +203,7 @@ AFB_clientCtx *ctxClientGet (AFB_request *request) clientCtx = ctxStoreSearch (uuid); if (clientCtx) { - if (ctxStoreTooOld (clientCtx, sessions.timeout)) { + if (ctxStoreTooOld (clientCtx, NOW)) { // this session is too old let's delete it ctxStoreDel (clientCtx); clientCtx = NULL; @@ -220,7 +223,7 @@ AFB_clientCtx *ctxClientGet (AFB_request *request) uuid_unparse_lower(newuuid, clientCtx->uuid); // if table is full at 50% let's clean it up - if(sessions.count > (sessions.max / 2)) ctxStoreGarbage(sessions.timeout); + if(sessions.count > (sessions.max / 2)) ctxStoreGarbage(); // finally add uuid into hashtable if (AFB_SUCCESS != ctxStoreAdd (clientCtx)) { @@ -239,14 +242,14 @@ AFB_error ctxTokenCheck (AFB_clientCtx *clientCtx, AFB_request *request) return AFB_EMPTY; // this time have to extract token from query list - token = MHD_lookup_connection_value(request->connection, MHD_GET_ARGUMENT_KIND, key_token); + token = NULL; //MHD_lookup_connection_value(request->connection, MHD_GET_ARGUMENT_KIND, key_token); // if not token is providing we refuse the exchange if ((token == NULL) || (clientCtx->token == NULL)) return AFB_FALSE; // compare current token with previous one - if ((0 == strcmp (token, clientCtx->token)) && (!ctxStoreTooOld (clientCtx, sessions.timeout))) { + if ((0 == strcmp (token, clientCtx->token)) && (!ctxStoreTooOld (clientCtx, NOW))) { return AFB_SUCCESS; } @@ -285,7 +288,7 @@ AFB_error ctxTokenCreate (AFB_clientCtx *clientCtx, AFB_request *request) if (request->config->token[0] != '\0') { // check for initial token secret and return if not presented - token = MHD_lookup_connection_value(request->connection, MHD_GET_ARGUMENT_KIND, key_token); + token = NULL; //MHD_lookup_connection_value(request->connection, MHD_GET_ARGUMENT_KIND, key_token); if (token == NULL) return AFB_UNAUTH; @@ -299,7 +302,7 @@ AFB_error ctxTokenCreate (AFB_clientCtx *clientCtx, AFB_request *request) uuid_unparse_lower(newuuid, clientCtx->token); // keep track of time for session timeout and further clean up - clientCtx->timeStamp=time(NULL); + clientCtx->timeStamp = time(NULL) + sessions.timeout; // Token is also store in context but it might be convenient for plugin to access it directly return AFB_SUCCESS; @@ -323,8 +326,162 @@ AFB_error ctxTokenRefresh (AFB_clientCtx *clientCtx, AFB_request *request) uuid_unparse_lower(newuuid, clientCtx->token); // keep track of time for session timeout and further clean up - clientCtx->timeStamp=time(NULL); + clientCtx->timeStamp = time(NULL) + sessions.timeout; return AFB_SUCCESS; } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +// This function will return exiting client context or newly created client context +AFB_clientCtx *_ctxClientGet (const char *uuid) +{ + uuid_t newuuid; + AFB_clientCtx *clientCtx; + + /* search for an existing one not too old */ + clientCtx = uuid != NULL ? ctxStoreSearch (uuid) : NULL; + if (clientCtx) { + if (!ctxStoreTooOld (clientCtx, NOW)) + return clientCtx; + ctxStoreDel (clientCtx); + } + + /* mimic old behaviour */ + if (sessions.initok == NULL) + return NULL; + + /* cleanup before creating */ + if(2 * sessions.count >= sessions.max) + ctxStoreGarbage(); + + /* returns a new one */ + clientCtx = calloc(1, sizeof(AFB_clientCtx)); // init NULL clientContext + if (clientCtx != NULL) { + clientCtx->contexts = calloc ((unsigned)sessions.apicount, sizeof (void*)); + if (clientCtx->contexts != NULL) { + /* generate the uuid */ + uuid_generate(newuuid); + uuid_unparse_lower(newuuid, clientCtx->uuid); + clientCtx->timeStamp = time(NULL) + sessions.timeout; + strcpy(clientCtx->token, sessions.initok); + if (AFB_SUCCESS == ctxStoreAdd (clientCtx)) + return clientCtx; + free(clientCtx->contexts); + } + free(clientCtx); + } + return NULL; +} + +// Free Client Session Context +AFB_error _ctxClientDel (AFB_clientCtx *clientCtx) +{ + assert(clientCtx != NULL); + return ctxStoreDel (clientCtx); +} + +// Sample Generic Ping Debug API +AFB_error _ctxTokenCheck (AFB_clientCtx *clientCtx, const char *token) +{ + assert(clientCtx != NULL); + assert(token != NULL); + + // compare current token with previous one + if (ctxStoreTooOld (clientCtx, NOW)) + return AFB_FAIL; + if (!clientCtx->token[0] || 0 == strcmp (token, clientCtx->token)) { + clientCtx->timeStamp = time(NULL) + sessions.timeout; + return AFB_SUCCESS; + } + + // Token is not valid let move level of assurance to zero and free attached client handle + return AFB_FAIL; +} + +// generate a new token and update client context +AFB_error _ctxTokenNew (AFB_clientCtx *clientCtx) +{ + uuid_t newuuid; + + assert(clientCtx != NULL); + + // Old token was valid let's regenerate a new one + uuid_generate(newuuid); // create a new UUID + uuid_unparse_lower(newuuid, clientCtx->token); + + // keep track of time for session timeout and further clean up + clientCtx->timeStamp = time(NULL) + sessions.timeout; + + return AFB_SUCCESS; +} + diff --git a/src/session.h b/src/session.h index 6c0b1d89..0862b37b 100644 --- a/src/session.h +++ b/src/session.h @@ -14,11 +14,31 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +// User Client Session Context +struct AFB_clientCtx +{ + time_t timeStamp; // last time token was refresh + int restfull; // client does not use cookie + void **contexts; // application specific context [one per plugin]] + char uuid[37]; // long term authentication of remote client + char token[37]; // short term authentication of remote client +}; +typedef struct AFB_clientCtx AFB_clientCtx; + +/* extern AFB_error ctxTokenRefresh (AFB_clientCtx *clientCtx, AFB_request *request); extern AFB_error ctxTokenCreate (AFB_clientCtx *clientCtx, AFB_request *request); extern AFB_error ctxTokenCheck (AFB_clientCtx *clientCtx, AFB_request *request); extern AFB_error ctxTokenReset (AFB_clientCtx *clientCtx, AFB_request *request); extern AFB_clientCtx *ctxClientGet (AFB_request *request); -extern void ctxStoreInit (int nbSession, int timeout, int apicount); +*/ +extern void ctxStoreGarbage (); + +extern void ctxStoreInit (int nbSession, int timeout, int apicount, const char *initok); + +extern AFB_clientCtx *_ctxClientGet (const char *uuid); +extern AFB_error _ctxClientDel (AFB_clientCtx *clientCtx); +extern AFB_error _ctxTokenCheck (AFB_clientCtx *clientCtx, const char *token); +extern AFB_error _ctxTokenNew (AFB_clientCtx *clientCtx); |