/** * \file * * \author Georg Hopp * * \copyright * Copyright © 2013 Georg Hopp * * 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 3 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, see . */ #define _GNU_SOURCE #include #include #include #include #include "class.h" #include "application/application.h" #include "application/adapter/http.h" #include "hash.h" #include "http/worker.h" #include "http/header.h" #include "http/response.h" #include "auth/credential.h" #include "user.h" #include "utils/memory.h" #define NO_SESSION_SID NULL #define SESS_HEADER "{\"id\":\"%s\",\"timeout\":%d,\"timeleft\":%ld}" static inline char * getSessionId(Hash cookies) { HashValue sidstr = hashGet(cookies, CSTRA("sid")); if (NULL != sidstr) { return (char*)sidstr->value; } return NO_SESSION_SID; } static Session getSession(Queue sess_queue, const char * sid) { Session sess = NULL; time_t now = time(NULL); /** * session start or update * * @TODO * I need to walk through the whole hash at this point to find * expired sessions and remove them....this is not good and * probably I need another(faster) way to identify expired * sessions.... * * @TODO * Build a way to cleanup the hash by a filter...currently * there is nothing I could use for this. * Well this is practically impossible in reasonable time * because every time I remove one element the tree has to * be rebalanced.... * * I have to store all nodes in a different structure that * gives me the possibility to find fast expired objects. * These can then be removed from both structures.... * * Anyway this is the pure horror...because I have to compute * the condition for every stored session....and I really have to * do this...else the tree would grow and grow all the time... * * I think the best I can do at this point is, at least for the moment, * to store the sessions in a list and not in a stack. * Each request will than have to walk through that list, * remove expired sessions and pick out its own.... * this is O(n), but currently I gave no better idea at all. */ while (NULL != sess_queue->next) { Session session = (Session)sess_queue->next->msg; if (now >= session->livetime) { Queue to_delete = sess_queue->next; sess_queue->next = sess_queue->next->next; delete(session); delete(to_delete); continue; } if (NULL != sid && 0 == memcmp(session->id, sid, 36)) { session->livetime = time(NULL) + SESSION_LIVETIME; sess = session; } sess_queue = sess_queue->next; } if (NULL == sess) { sess = new(Session); queuePut(sess_queue, sess); } return sess; } static void loginAdapter(Application application, HttpWorker worker, Session session) { HashValue username; HashValue password; Credential credential; username = hashGet( worker->current_request->post, CSTRA("username")); password = hashGet( worker->current_request->post, CSTRA("password")); if (NULL == username || NULL == password) { worker->current_response = new(HttpResponse, "HTTP/1.1", 403, "Forbidden"); return; } credential = new(Credential, CRED_PASSWORD, (char *)(username->value), username->nvalue, (char *)(password->value), password->nvalue); if (! applicationLogin(application, credential, session)) { worker->current_response = new(HttpResponse, "HTTP/1.1", 403, "Forbidden"); } else { worker->current_response = (HttpMessage)httpResponseUser(session->user); } delete(credential); } static void signupAdapter(Application application, HttpWorker worker, Session session) { HashValue email; HashValue password; HashValue pwrepeat; HashValue firstname; HashValue surname; Credential credential; User user; email = hashGet( worker->current_request->post, CSTRA("email")); password = hashGet( worker->current_request->post, CSTRA("password")); pwrepeat = hashGet( worker->current_request->post, CSTRA("pwrepeat")); firstname = hashGet( worker->current_request->post, CSTRA("firstname")); surname = hashGet( worker->current_request->post, CSTRA("surname")); if ( NULL == email || NULL == password || NULL == pwrepeat || NULL == firstname || NULL == surname) { // maybe this is not a 500...have to check repsonse codes. worker->current_response = (HttpMessage)httpResponse500(); return; } if (password->nvalue != pwrepeat->nvalue || 0 != memcmp(password->value, pwrepeat->value, password->nvalue)) { // maybe this is not a 500...have to check repsonse codes. worker->current_response = (HttpMessage)httpResponse500(); return; } credential = new(Credential, CRED_PASSWORD, (char *)(email->value), email->nvalue, (char *)(password->value), password->nvalue); user = new(User, (char *)(email->value), email->nvalue, (char *)(firstname->value), firstname->nvalue, (char *)(surname->value), surname->nvalue); if (! applicationSignup(application, credential, user, session)) { worker->current_response = (HttpMessage)httpResponse500(); } else { loginAdapter(application, worker, session); } delete(credential); delete(user); } void applicationAdapterHttpUpdate(void * _this, void * subject) { ApplicationAdapterHttp this = _this; HttpWorker worker = (HttpWorker)subject; char * sid; Session session; char buf[200]; size_t nbuf; sid = getSessionId(worker->current_request->cookies); session = getSession(this->application->active_sessions, sid); nbuf = sprintf(buf, SESS_HEADER, session->id, SESSION_LIVETIME, session->livetime - time(NULL)); queuePut( worker->additional_headers, new(HttpHeader, CSTRA("X-TaskramblerSession"), buf, nbuf)); nbuf = sprintf(buf, "sid=%s;Path=/", session->id); queuePut( worker->additional_headers, new(HttpHeader, CSTRA("Set-Cookie"), buf, nbuf)); if (0 == strcmp("POST", worker->current_request->method)) { if (0 == strcmp("/login/", worker->current_request->path)) { loginAdapter(this->application, worker, session); return; } if (0 == strcmp("/signup/", worker->current_request->path)) { signupAdapter(this->application, worker, session); return; } } if (0 == strcmp("GET", worker->current_request->method)) { if (0 == strcmp("/user/get/", worker->current_request->path)) { worker->current_response = (HttpMessage)httpResponseUser(session->user); return; } // if (0 == strcmp("/sess/", worker->current_request->path)) { // if (NO_SESSION_SID == sid // || NULL == applicationSessionGet(this->application, sid)) { // sid = applicationSessionStart(this->application, NULL, 0); // } // // worker->current_response = // (HttpMessage)httpResponseSession( // applicationSessionGet(this->application, sid)); // return; // } if (0 == strcmp("/randval/", worker->current_request->path)) { if (NULL != session->user) { worker->current_response = (HttpMessage)httpResponseRandval( this->application->val->timestamp, this->application->val->value); } else { worker->current_response = (HttpMessage)httpResponse403(); } } } } // vim: set ts=4 sw=4: