/* Copyright (C) 2008 NFG Net Facilities Group BV, support@nfg.nl This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * * implementation for http commands */ #include "dbmail.h" #include "dm_request.h" #include "dm_http.h" #define THIS_MODULE "Request" /* * */ #define T Request_T struct T { struct evhttp_request *req; void *data; u64_t user_id; const char *uri; const char *controller; const char *id; const char *method; const char *arg; struct evkeyvalq *GET; struct evkeyvalq *POST; clientbase_t *ci; void (*cb)(T); char **parts; }; //--------------------------------------------------------------------------------------// static void basic_unauth(T R, const char *realm) { char *fmt = "\n" "\n" " \n" " Error\n" " \n" " \n" "

401 Unauthorised.

\n" "\n"; struct evbuffer *buf = evbuffer_new(); char *r = g_strdup_printf("Basic realm=\"%s\"", realm); evhttp_add_header(R->req->output_headers, "WWW-Authenticate", r); evbuffer_add_printf(buf,"%s", fmt); Request_send(R, 401, "UNAUTHORIZED", buf); evbuffer_free(buf); g_free(r); } static gboolean Request_user_auth(T R, char *token) { gboolean r = FALSE; gchar **array; array = g_strsplit(token, ":", 2); if (array[0] && array[1]) { u64_t user_id = 0; gchar *username, *password; username = array[0]; password = array[1]; if (auth_validate(NULL, username, password, &user_id) > 0) { R->user_id = user_id; r = TRUE; } } g_strfreev(array); return r; } static gboolean Request_basic_auth(T R) { const char *auth; field_t realm; memset(realm,0,sizeof(field_t)); config_get_value("realm", "HTTP", realm); if (! strlen(realm)) strcpy(realm,"DBMail HTTP Access"); // authenticate if (! (auth = evhttp_find_header(R->req->input_headers, "Authorization"))) { TRACE(TRACE_DEBUG,"No authorization header"); basic_unauth(R, realm); return FALSE; } if (strncmp(auth,"Basic ", 6) == 0) { field_t userpw; gsize len; guchar *s; gchar *safe; memset(userpw,0,sizeof(field_t)); config_get_value("admin", "HTTP", userpw); auth+=6; TRACE(TRACE_DEBUG, "auth [%s]", auth); s = g_base64_decode(auth, &len); safe = g_strndup((char *)s, len); g_free(s); TRACE(TRACE_DEBUG,"Authorization [%ld][%s] <-> [%s]", len, safe, userpw); if ((strlen(userpw) != strlen(safe)) || (strncmp(safe,(char *)userpw,strlen(userpw))!=0)) { if (! Request_user_auth(R, safe)) { TRACE(TRACE_DEBUG,"Authorization failed"); basic_unauth(R, realm); g_free(safe); return FALSE; } } g_free(safe); } else { return FALSE; } return TRUE; } static void Request_parse_getvars(T R) { struct evkeyval *val; R->GET = g_new0(struct evkeyvalq,1); evhttp_parse_query(R->uri, R->GET); TAILQ_FOREACH(val, R->GET, next) TRACE(TRACE_DEBUG,"GET: [%s]->[%s]", val->key, val->value); } static void Request_parse_postvars(T R) { struct evkeyval *val; char *post = NULL, *rawpost = NULL; rawpost = g_strndup((char *)EVBUFFER_DATA(R->req->input_buffer), EVBUFFER_LENGTH(R->req->input_buffer)); if (rawpost) { post = evhttp_decode_uri(rawpost); g_free(rawpost); } R->POST = g_new0(struct evkeyvalq,1); TAILQ_INIT(R->POST); if (post) { int i = 0; char **p = g_strsplit(post,"&",0); while (p[i]) { struct evkeyval *header = g_new0(struct evkeyval,1); char **kv = g_strsplit(p[i],"=",2); if (! (kv[0] && kv[1])) break; header->key = kv[0]; header->value = kv[1]; TAILQ_INSERT_TAIL(R->POST, header, next); i++; } g_strfreev(p); g_free(post); } TAILQ_FOREACH(val, R->POST, next) TRACE(TRACE_DEBUG,"POST: [%s]->[%s]", val->key, val->value); } #define EXISTS(x) ((x) && strlen(x)) T Request_new(struct evhttp_request *req, void *data) { T R; struct evkeyval *val; R = g_malloc0(sizeof(*R)); R->req = req; R->data = data; R->uri = evhttp_decode_uri(evhttp_request_uri(R->req)); R->parts = g_strsplit_set(R->uri,"/?",0); Request_parse_getvars(R); Request_parse_postvars(R); TRACE(TRACE_DEBUG,"R->uri: [%s]", R->uri); TAILQ_FOREACH(val, R->req->input_headers, next) TRACE(TRACE_DEBUG,"input_header: [%s: %s]", val->key, val->value); // // uri-parts: /controller/id/method/arg // if (EXISTS(R->parts[1])) { R->controller = R->parts[1]; TRACE(TRACE_DEBUG,"R->controller: [%s]", R->controller); if (EXISTS(R->parts[2])) { R->id = R->parts[2]; TRACE(TRACE_DEBUG,"R->id: [%s]", R->id); if (EXISTS(R->parts[3])) { R->method = R->parts[3]; TRACE(TRACE_DEBUG,"R->method: [%s]", R->method); if (EXISTS(R->parts[4])) { R->arg = R->parts[4]; TRACE(TRACE_DEBUG,"R->arg: [%s]", R->arg); } } } } return R; } u64_t Request_getUser(T R) { return R->user_id; } const char * Request_getController(T R) { return R->controller; } const char * Request_getId(T R) { return R->id; } const char * Request_getMethod(T R) { return R->method; } const char * Request_getArg(T R) { return R->arg; } struct evkeyvalq * Request_getPOST(T R) { return R->POST; } struct evkeyvalq * Request_getGET(T R) { return R->GET; } void Request_send(T R, int code, const char *message, struct evbuffer *buf) { evhttp_send_reply(R->req, code, message, buf); } void Request_error(T R, int code, const char *message) { char *fmt = "\n" "%d %s\n" "\n" "

%d %s

\n" "\n"; struct evbuffer *buf = evbuffer_new(); /* close the connection on error */ Request_header(R, "connection", "close"); evbuffer_add_printf(buf, fmt, code, message, code, message); Request_send(R, code, message, buf); evbuffer_free(buf); } void Request_header(T R, const char *name, const char *value) { evhttp_add_header(R->req->output_headers, name, value); } void Request_setContentType(T R, const char *type) { evhttp_remove_header(R->req->output_headers, "Content-type"); Request_header(R, "Content-type", type); } void Request_free(T *R) { T r = *R; g_strfreev(r->parts); evhttp_clear_headers(r->GET); evhttp_clear_headers(r->POST); r = NULL; } /* routing setup */ void Request_handle(T R) { if (R->controller) { if (MATCH(R->controller,"users")) R->cb = Http_getUsers; else if (MATCH(R->controller,"mailboxes")) R->cb = Http_getMailboxes; else if (MATCH(R->controller,"messages")) R->cb = Http_getMessages; } if (R->cb) { if (! Request_basic_auth(R)) return; Request_setContentType(R,"text/html; charset=utf-8"); R->cb(R); } else { const char *host = evhttp_find_header(R->req->input_headers, "Host"); char *url = g_strdup_printf("http://%s%s", host?host:"","/users/"); Request_header(R, "Location", url); g_free(url); Request_error(R, HTTP_MOVEPERM, "Not found"); } } // Public void Request_cb(struct evhttp_request *req, void * data) { Request_T R = Request_new(req, data); Request_handle(R); Request_free(&R); }