diff --git a/src/db/driver_sqlite.cpp b/src/db/driver_sqlite.cpp index 5f75429..b3956fa 100644 --- a/src/db/driver_sqlite.cpp +++ b/src/db/driver_sqlite.cpp @@ -239,16 +239,12 @@ namespace UDPT _to_hex_str(info_hash, xHash); - // if non-dynamic, called only when adding to DB. - if (!this->isDynamic()) - { - sqlite3_stmt *stmt; - sqlite3_prepare(this->db, "INSERT INTO torrents (info_hash,created) VALUES (?,?)", -1, &stmt, NULL); - sqlite3_bind_blob(stmt, 1, info_hash, 20, NULL); - sqlite3_bind_int(stmt, 1, time(NULL)); - sqlite3_step(stmt); - sqlite3_finalize(stmt); - } + sqlite3_stmt *stmt; + sqlite3_prepare(this->db, "INSERT INTO torrents (info_hash,created) VALUES (?,?)", -1, &stmt, NULL); + sqlite3_bind_blob(stmt, 1, info_hash, 20, NULL); + sqlite3_bind_int(stmt, 1, time(NULL)); + sqlite3_step(stmt); + sqlite3_finalize(stmt); string sql = "CREATE TABLE IF NOT EXISTS 't"; sql += xHash; @@ -338,14 +334,11 @@ namespace UDPT bool SQLite3Driver::removeTorrent(uint8_t info_hash[20]) { // if non-dynamic, remove from table - if (!this->isDynamic()) - { - sqlite3_stmt *stmt; - sqlite3_prepare(this->db, "DELETE FROM torrents WHERE info_hash=?", -1, &stmt, NULL); - sqlite3_bind_blob(stmt, 1, info_hash, 20, NULL); - sqlite3_step(stmt); - sqlite3_finalize(stmt); - } + sqlite3_stmt *stmt; + sqlite3_prepare(this->db, "DELETE FROM torrents WHERE info_hash=?", -1, &stmt, NULL); + sqlite3_bind_blob(stmt, 1, info_hash, 20, NULL); + sqlite3_step(stmt); + sqlite3_finalize(stmt); // remove from stats sqlite3_stmt *rmS; diff --git a/src/http/httpserver.cpp b/src/http/httpserver.cpp index 8c67baf..c32a2b9 100644 --- a/src/http/httpserver.cpp +++ b/src/http/httpserver.cpp @@ -1,203 +1,503 @@ -/* - * Copyright © 2012,2013 Naim A. - * - * This file is part of UDPT. - * - * UDPT 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. - * - * UDPT 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 UDPT. If not, see . - */ - -#include "httpserver.hpp" -#include "../tools.h" - -namespace UDPT -{ - namespace API - { - HTTPServer::HTTPServer (uint16_t port, int threads) - { - int r; - - this->thread_count = threads; - this->threads = new HANDLE [threads]; - - SOCKADDR_IN endpoint; - endpoint.sin_family = AF_INET; - endpoint.sin_port = m_hton16(port); - endpoint.sin_addr.s_addr = 0L; - - this->sock = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (this->sock == INVALID_SOCKET) - throw APIException("Invalid Socket"); - - r = bind(this->sock, (SOCKADDR*)&endpoint, sizeof(SOCKADDR_IN)); - if (r == SOCKET_ERROR) - throw APIException("Failed to bind port."); - - this->isRunning = true; - - this->rootNode.name = ""; - this->rootNode.children.clear(); - this->rootNode.callback = NULL; - - for (int i = 0;i < threads;i++) - { -#ifdef WIN32 - this->threads[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&HTTPServer::doServe, (LPVOID)this, 0, NULL); -#else - pthread_create (&this->threads[0], NULL, HTTPServer::doServe, (void*)this); -#endif - } - } - -#ifdef WIN32 - DWORD HTTPServer::doServe (LPVOID arg) -#else - void* HTTPServer::doServe (void* arg) -#endif - { - HTTPServer *srv = (HTTPServer*)arg; - int r; - SOCKADDR addr; - int addrSz = sizeof (addr); - SOCKET conn; - - while (srv->isRunning) - { - r = listen (srv->sock, SOMAXCONN); - if (r == SOCKET_ERROR) - throw APIException("Failed to listen"); - - addrSz = sizeof (addr); - - conn = accept(srv->sock, &addr, &addrSz); - if (conn == INVALID_SOCKET) - { - continue; - } - cout << "A" << endl; - - Request req = Request (conn, &addr); - Response resp = Response (conn); - - HTTPServer::handleConnection(srv, &req, &resp); - closesocket(conn); - } - -#ifdef WIN32 - return 0; -#else - return NULL; -#endif - } - - void HTTPServer::handleConnection (HTTPServer *srv, Request *req, Response *resp) - { - // follow path... - serveNode *cNode = &srv->rootNode; - list::iterator it; - for (it = req->path.begin();(it != req->path.end() && cNode != NULL);it++) - { - if ((*it).length() == 0) - continue; // same node. - - map::iterator np; - np = cNode->children.find((*it)); - if (np == srv->rootNode.children.end()) - { - cNode = NULL; - break; - } - else - cNode = &np->second; - } - - if (cNode->callback != NULL) - cNode->callback (req, resp); - else - { - // TODO: add HTTP error handler (404 NOT FOUND...) - cout << "Page Not Found" << endl; - } - } - - list HTTPServer::split (const string str, const string del) - { - list lst; - - unsigned s, e; - s = e = 0; - - while (true) - { - e = str.find(del, s); - - if (e == string::npos) - e = str.length(); - - if (e == str.length()) - break; - s = e + del.length(); - } - - return lst; - } - - void HTTPServer::addApplication (const string path, srvCallback *callback) - { - list p = split (path, "/"); - list::iterator it; - - serveNode *node = &this->rootNode; - - for (it = p.begin();it != p.end();it++) - { - if ((*it).length() == 0) - continue; // same node... - - node = &node->children[*it]; - node->name = *it; - } - node->callback = callback; - } - - HTTPServer::~HTTPServer() - { - this->isRunning = false; - closesocket(this->sock); - for (int i = 0;i < this->thread_count;i++) - { -#ifdef WIN32 - TerminateThread(this->threads[i], 0); -#else - pthread_detach (this->threads[i]); - pthread_cancel (this->threads[i]); -#endif - } - delete[] this->threads; - } - - - HTTPServer::Request::Request(SOCKET sock, const SOCKADDR *sa) - { - this->sock = sock; - this->sock_addr = sa; - } - - HTTPServer::Response::Response(SOCKET sock) - { - - } - - }; -}; +/* + * Copyright © 2013 Naim A. + * + * This file is part of UDPT. + * + * UDPT 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. + * + * UDPT 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 UDPT. If not, see . + */ + +#include +#include +#include +#include +#include +#include "httpserver.hpp" + +using namespace std; + +namespace UDPT +{ + namespace Server + { + /* HTTPServer */ + HTTPServer::HTTPServer (uint16_t port, int threads) + { + int r; + SOCKADDR_IN sa; + + this->thread_count = threads; + this->threads = new HANDLE[threads]; + this->isRunning = false; + + this->rootNode.callback = NULL; + + this->srv = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (this->srv == INVALID_SOCKET) + { + throw ServerException (1, "Failed to create Socket"); + } + + sa.sin_addr.s_addr = 0L; + sa.sin_family = AF_INET; + sa.sin_port = htons (port); + + r = bind (this->srv, (SOCKADDR*)&sa, sizeof(sa)); + if (r == SOCKET_ERROR) + { + throw ServerException (2, "Failed to bind socket"); + } + + this->isRunning = true; + for (int i = 0;i < threads;i++) + { +#ifdef WIN32 + this->threads[i] = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE)_thread_start, this, 0, NULL); +#else + pthread_create (&this->threads[i], NULL, &HTTPServer::_thread_start, this); +#endif + } + } + +#ifdef WIN32 + DWORD HTTPServer::_thread_start (LPVOID arg) +#else + void* HTTPServer::_thread_start (void *arg) +#endif + { + HTTPServer *s = (HTTPServer*)arg; +doSrv: + try { + HTTPServer::handleConnections (s); + } catch (ServerException &se) + { + cerr << "SRV ERR #" << se.getErrorCode() << ": " << se.getErrorMsg () << endl; + goto doSrv; + } + return 0; + } + + void HTTPServer::handleConnections (HTTPServer *server) + { + int r; +#ifdef WIN32 + int addrSz; +#else + socklen_t addrSz; +#endif + SOCKADDR_IN addr; + SOCKET cli; + + while (server->isRunning) + { + r = listen (server->srv, 50); + if (r == SOCKET_ERROR) + { +#ifdef WIN32 + Sleep (500); +#else + sleep (1); +#endif + continue; + } + addrSz = sizeof addr; + cli = accept (server->srv, (SOCKADDR*)&addr, &addrSz); + if (cli == INVALID_SOCKET) + continue; + + Response resp (cli); // doesn't throw exceptions. + + try { + Request req (cli, &addr); // may throw exceptions. + reqCallback *cb = getRequestHandler (&server->rootNode, req.getPath()); + if (cb == NULL) + { + // error 404 + resp.setStatus (404, "Not Found"); + resp.addHeader ("Content-Type", "text/html; charset=US-ASCII"); + stringstream stream; + stream << ""; + stream << "Not Found"; + stream << "

Not Found

The server couldn't find the request resource.


© 2013 Naim A. | The UDPT Project
"; + stream << ""; + string str = stream.str(); + resp.write (str.c_str(), str.length()); + } + else + { + try { + cb (server, &req, &resp); + } catch (...) + { + resp.setStatus(500, "Internal Server Error"); + resp.addHeader ("Content-Type", "text/html; charset=US-ASCII"); + stringstream stream; + stream << ""; + stream << "Internal Server Error"; + stream << "

Internal Server Error

An Error Occurred while trying to process your request.


© 2013 Naim A. | The UDPT Project
"; + stream << ""; + string str = stream.str(); + resp.write (str.c_str(), str.length()); + } + } + resp.finalize(); + } catch (ServerException &e) + { + // Error 400 Bad Request! + } + + closesocket (cli); + } + } + + void HTTPServer::addApp (list *path, reqCallback *cb) + { + list::iterator it = path->begin(); + appNode *node = &this->rootNode; + while (it != path->end()) + { + map::iterator se; + se = node->nodes.find (*it); + if (se == node->nodes.end()) + { + node->nodes[*it].callback = NULL; + } + node = &node->nodes[*it]; + it++; + } + node->callback = cb; + } + + HTTPServer::reqCallback* HTTPServer::getRequestHandler (appNode *node, list *path) + { + appNode *cn = node; + list::iterator it = path->begin(), + end = path->end(); + map::iterator n; + while (true) + { + if (it == end) + { + return cn->callback; + } + + n = cn->nodes.find (*it); + if (n == cn->nodes.end()) + return NULL; // node not found! + cn = &n->second; + + it++; + } + return NULL; + } + + void HTTPServer::setData(string k, void *d) + { + this->customData[k] = d; + } + + void* HTTPServer::getData(string k) + { + map::iterator it = this->customData.find(k); + if (it == this->customData.end()) + return NULL; + return it->second; + } + + HTTPServer::~HTTPServer () + { + if (this->srv != INVALID_SOCKET) + closesocket (this->srv); + + if (this->isRunning) + { + for (int i = 0;i < this->thread_count;i++) + { +#ifdef WIN32 + TerminateThread (this->threads[i], 0x00); +#else + pthread_detach (this->threads[i]); + pthread_cancel (this->threads[i]); +#endif + } + } + + delete[] this->threads; + } + + /* HTTPServer::Request */ + HTTPServer::Request::Request (SOCKET cli, const SOCKADDR_IN *addr) + { + this->conn = cli; + this->addr = addr; + + this->parseRequest (); + } + + inline static char* nextReqLine (int &cPos, char *buff, int len) + { + for (int i = cPos;i < len - 1;i++) + { + if (buff[i] == '\r' && buff[i + 1] == '\n') + { + buff[i] = '\0'; + + int r = cPos; + cPos = i + 2; + return (buff + r); + } + } + + return (buff + len); // end + } + + inline void parseURL (string request, list *path, map *params) + { + string::size_type p; + string query, url; + p = request.find ('?'); + if (p == string::npos) + { + p = request.length(); + } + else + { + query = request.substr (p + 1); + } + url = request.substr (0, p); + + path->clear (); + string::size_type s, e; + s = 0; + while (true) + { + e = url.find ('/', s); + if (e == string::npos) + e = url.length(); + + string x = url.substr (s, e - s); + if (!(x.length() == 0 || x == ".")) + { + if (x == "..") + { + if (path->empty()) + throw ServerException (1, "Hack attempt"); + else + path->pop_back (); + } + path->push_back (x); + } + + if (e == url.length()) + break; + s = e + 1; + } + + string::size_type vS, vE, kS, kE; + vS = vE = kS = kE = 0; + while (kS < query.length()) + { + kE = query.find ('=', kS); + if (kE == string::npos) break; + vS = kE + 1; + vE = query.find ('&', vS); + if (vE == string::npos) vE = query.length(); + + params->insert (pair( query.substr (kS, kE - kS), query.substr (vS, vE - vS) )); + + kS = vE + 1; + } + } + + inline void setCookies (string &data, map *cookies) + { + string::size_type kS, kE, vS, vE; + kS = 0; + while (kS < data.length ()) + { + kE = data.find ('=', kS); + if (kE == string::npos) + break; + vS = kE + 1; + vE = data.find ("; ", vS); + if (vE == string::npos) + vE = data.length(); + + (*cookies) [data.substr (kS, kE-kS)] = data.substr (vS, vE-vS); + + kS = vE + 2; + } + } + + void HTTPServer::Request::parseRequest () + { + char buffer [REQUEST_BUFFER_SIZE]; + int r; + r = recv (this->conn, buffer, REQUEST_BUFFER_SIZE, 0); + if (r == REQUEST_BUFFER_SIZE) + throw ServerException (1, "Request Size too big."); + if (r <= 0) + throw ServerException (2, "Socket Error"); + + char *cLine; + int n = 0; + int pos = 0; + string::size_type p; + while ( (cLine = nextReqLine (pos, buffer, r)) < (buffer + r)) + { + string line = string (cLine); + if (line.length() == 0) break; // CRLF CRLF = end of headers. + n++; + + if (n == 1) + { + string::size_type uS, uE; + p = line.find (' '); + if (p == string::npos) + throw ServerException (5, "Malformed request method"); + uS = p + 1; + this->requestMethod.str = line.substr (0, p); + + if (this->requestMethod.str == "GET") + this->requestMethod.rm = RM_GET; + else if (this->requestMethod.str == "POST") + this->requestMethod.rm = RM_POST; + else + this->requestMethod.rm = RM_UNKNOWN; + + uE = uS; + while (p < line.length()) + { + if (p == string::npos) + break; + p = line.find (' ', p + 1); + if (p == string::npos) + break; + uE = p; + } + if (uE + 1 >= line.length()) + throw ServerException (6, "Malformed request"); + string httpVersion = line.substr (uE + 1); + + + parseURL (line.substr (uS, uE - uS), &this->path, &this->params); + } + else + { + p = line.find (": "); + if (p == string::npos) + throw ServerException (4, "Malformed headers"); + string key = line.substr (0, p); + string value = line.substr (p + 2); + if (key != "Cookie") + this->headers.insert(pair( key, value)); + else + setCookies (value, &this->cookies); + } + } + if (n == 0) + throw ServerException (3, "No Request header."); + } + + list* HTTPServer::Request::getPath () + { + return &this->path; + } + + string HTTPServer::Request::getParam (const string key) + { + map::iterator it = this->params.find (key); + if (it == this->params.end()) + return ""; + else + return it->second; + } + + multimap::iterator HTTPServer::Request::getHeader (const string name) + { + multimap::iterator it = this->headers.find (name); + return it; + } + + HTTPServer::Request::RequestMethod HTTPServer::Request::getRequestMethod () + { + return this->requestMethod.rm; + } + + string HTTPServer::Request::getRequestMethodStr () + { + return this->requestMethod.str; + } + + string HTTPServer::Request::getCookie (const string name) + { + map::iterator it = this->cookies.find (name); + if (it == this->cookies.end()) + return ""; + else + return it->second; + } + + const SOCKADDR_IN* HTTPServer::Request::getAddress () + { + return this->addr; + } + + /* HTTPServer::Response */ + HTTPServer::Response::Response (SOCKET cli) + { + this->conn = cli; + + setStatus (200, "OK"); + } + + void HTTPServer::Response::setStatus (int c, const string m) + { + this->status_code = c; + this->status_msg = m; + } + + void HTTPServer::Response::addHeader (string key, string value) + { + this->headers.insert (pair(key, value)); + } + + void HTTPServer::Response::write (const char *data, int len) + { + if (len < 0) + len = strlen (data); + msg.write(data, len); + } + + void HTTPServer::Response::finalize () + { + stringstream x; + x << "HTTP/1.1 " << this->status_code << " " << this->status_msg << "\r\n"; + multimap::iterator it, end; + end = this->headers.end(); + for (it = this->headers.begin(); it != end;it++) + { + x << it->first << ": " << it->second << "\r\n"; + } + x << "Connection: Close\r\n"; + x << "Content-Length: " << this->msg.tellp() << "\r\n"; + x << "Server: udpt\r\n"; + x << "\r\n"; + x << this->msg.str(); + + // write to socket + send (this->conn, x.str().c_str(), x.str().length(), 0); + } + + }; +}; diff --git a/src/http/httpserver.hpp b/src/http/httpserver.hpp index eb42412..4a32e0a 100644 --- a/src/http/httpserver.hpp +++ b/src/http/httpserver.hpp @@ -1,110 +1,166 @@ -/* - * Copyright © 2012,2013 Naim A. - * - * This file is part of UDPT. - * - * UDPT 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. - * - * UDPT 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 UDPT. If not, see . - */ - -#ifndef HTTPSERVER_HPP_ -#define HTTPSERVER_HPP_ - -#include -#include -#include -#include -#include -#include "../multiplatform.h" - -using namespace std; - -namespace UDPT -{ - namespace API - { - class APIException - { - public: - inline - APIException (const string msg) - { - this->msg = msg; - } - - private: - string msg; - }; - - class HTTPServer - { - public: - class Request - { - public: - Request (SOCKET sock, const SOCKADDR *sock_addr); - private: - friend class HTTPServer; - - SOCKET sock; - multimap headers; - list path; // /some/path - map query; // a=b&c=d - const SOCKADDR *sock_addr; // IP address+family - - void loadAndParse (); - }; - class Response - { - public: - Response (SOCKET sock); - - private: - friend class HTTPServer; - }; - - typedef int (srvCallback) (Request *, Response *); - - HTTPServer (uint16_t port, int threads); - - void addApplication (const string path, srvCallback *callback); - - virtual ~HTTPServer (); - - - static list split (const string str, const string del); - private: - typedef struct _serve_node { - string name; // part of path name - map children; - srvCallback *callback; - } serveNode; - - bool isRunning; - serveNode rootNode; - SOCKET sock; - int thread_count; - HANDLE *threads; - -#ifdef WIN32 - static DWORD doServe (LPVOID arg); -#else - static void* doServe (void* arg); -#endif - - static void handleConnection (HTTPServer *, Request *, Response *); - }; - }; -}; - -#endif /* HTTPSERVER_HPP_ */ +/* + * Copyright © 2013 Naim A. + * + * This file is part of UDPT. + * + * UDPT 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. + * + * UDPT 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 UDPT. If not, see . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include "../multiplatform.h" +using namespace std; + +#define REQUEST_BUFFER_SIZE 2048 + +namespace UDPT +{ + namespace Server + { + class ServerException + { + public: + inline ServerException (int ec) + { + this->ec = ec; + this->em = NULL; + } + + inline ServerException (int ec, const char *em) + { + this->ec = ec; + this->em = em; + } + + inline const char *getErrorMsg () const + { + return this->em; + } + + inline int getErrorCode () const + { + return this->ec; + } + private: + int ec; + const char *em; + }; + + class HTTPServer + { + public: + class Request + { + public: + enum RequestMethod + { + RM_UNKNOWN = 0, + RM_GET = 1, + RM_POST = 2 + }; + + Request (SOCKET, const SOCKADDR_IN *); + list* getPath (); + + string getParam (const string key); + multimap::iterator getHeader (const string name); + RequestMethod getRequestMethod (); + string getRequestMethodStr (); + string getCookie (const string name); + const SOCKADDR_IN* getAddress (); + + private: + const SOCKADDR_IN *addr; + SOCKET conn; + struct { + int major; + int minor; + } httpVer; + struct { + string str; + RequestMethod rm; + } requestMethod; + list path; + map params; + map cookies; + multimap headers; + + void parseRequest (); + }; + + class Response + { + public: + Response (SOCKET conn); + + void setStatus (int, const string); + void addHeader (string key, string value); + + int writeRaw (const char *data, int len); + void write (const char *data, int len = -1); + + private: + friend class HTTPServer; + + SOCKET conn; + int status_code; + string status_msg; + multimap headers; + stringstream msg; + + void finalize (); + }; + + typedef void (reqCallback)(HTTPServer*,Request*,Response*); + + HTTPServer (uint16_t port, int threads); + + void addApp (list *path, reqCallback *); + + void setData (string, void *); + void* getData (string); + + virtual ~HTTPServer (); + + private: + typedef struct appNode + { + reqCallback *callback; + map nodes; + } appNode; + + SOCKET srv; + int thread_count; + HANDLE *threads; + bool isRunning; + appNode rootNode; + map customData; + + static void handleConnections (HTTPServer *); + +#ifdef WIN32 + static DWORD _thread_start (LPVOID); +#else + static void* _thread_start (void*); +#endif + + static reqCallback* getRequestHandler (appNode *, list *); + }; + }; +}; diff --git a/src/http/webapp.cpp b/src/http/webapp.cpp new file mode 100644 index 0000000..4f2f870 --- /dev/null +++ b/src/http/webapp.cpp @@ -0,0 +1,266 @@ +/* + * Copyright © 2013 Naim A. + * + * This file is part of UDPT. + * + * UDPT 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. + * + * UDPT 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 UDPT. If not, see . + */ + +#include "webapp.hpp" +#include "../tools.h" +#include +#include +using namespace std; + +namespace UDPT +{ + namespace Server + { + + static uint32_t _getNextIPv4 (string::size_type &i, string &line) + { + string::size_type len = line.length(); + char c; + while (i < len) + { + c = line.at(i); + if (c >= '0' && c <= '9') + break; + i++; + } + + uint32_t ip = 0; + for (int n = 0;n < 4;n++) + { + int cn = 0; + while (i < len) + { + c = line.at (i++); + if (c == '.' || ((c == ' ' || c == ',' || c == ';') && n == 3)) + break; + else if (!(c >= '0' && c <= '9')) + return 0; + cn *= 10; + cn += (c - '0'); + } + ip *= 256; + ip += cn; + } + return ip; + } + + static bool _hex2bin (uint8_t *data, const string str) + { + int len = str.length(); + + if (len % 2 != 0) + return false; + + char a, b; + uint8_t c; + for (int i = 0;i < len;i+=2) + { + a = str.at (i); + b = str.at (i + 1); + c = 0; + + if (a >= 'a' && a <= 'f') + a = (a - 'a') + 10; + else if (a >= '0' && a <= '9') + a = (a - '0'); + else + return false; + + if (b >= 'a' && b <= 'f') + b = (b - 'a') + 10; + else if (b >= '0' && b <= '9') + b = (b - '0'); + else + return false; + + c = (a * 16) + b; + + data [i / 2] = c; + } + + return true; + } + + WebApp::WebApp(HTTPServer *srv, DatabaseDriver *db, Settings *settings) + { + this->instance = srv; + this->db = db; + this->sc_api = settings->getClass("api"); + + Settings::SettingClass *apiKeys = settings->getClass("api.keys"); + if (apiKeys != NULL) + { + map* aK = apiKeys->getMap(); + map::iterator it, end; + end = aK->end(); + for (it = aK->begin();it != end;it++) + { + string key = it->first; + list ips; + + string::size_type strp = 0; + uint32_t ip; + while ((ip = _getNextIPv4(strp, it->second)) != 0) + { + ips.push_back( m_hton32(ip) ); + } + + this->ip_whitelist.insert(pair >(key, ips)); + } + + } + + srv->setData("webapp", this); + } + + WebApp::~WebApp() + { + } + + void WebApp::deploy() + { + list path; + this->instance->addApp(&path, &WebApp::handleRoot); + + path.push_back("api"); + this->instance->addApp(&path, &WebApp::handleAPI); // "/api" + + path.pop_back(); + path.push_back("announce"); + this->instance->addApp(&path, &WebApp::handleAnnounce); + } + + void WebApp::handleRoot (HTTPServer *srv, HTTPServer::Request *req, HTTPServer::Response *resp) + { + // It would be very appreciated to keep this in the code. + resp->write("" + "Powered by UDPT" + "" + "

The UDPT Project

" + "
This tracker is running on UDPT Software.
" + "UDPT is a open-source project, freely available for anyone to use. If you would like to obtain a copy of the software, you can get it here: http://code.googe.com/p/udpt." + "

If you would like to help the project grow, please donate for our hard work, effort & time: " + "\"Donate" + "
" + "

© 2013 Naim A. | Powered by UDPT
" + "" + ""); + } + + bool WebApp::isAllowedIP (WebApp *app, string key, uint32_t ip) + { + std::map >::iterator it, end; + end = app->ip_whitelist.end (); + it = app->ip_whitelist.find (key); + if (it == app->ip_whitelist.end()) + return false; // no such key + + list *lst = &it->second; + list::iterator ipit; + for (ipit = lst->begin();ipit != lst->end();ipit++) + { + if (*ipit == ip) + return true; + } + + return false; + } + + void WebApp::doRemoveTorrent (HTTPServer::Request *req, HTTPServer::Response *resp) + { + string strHash = req->getParam("hash"); + if (strHash.length() != 40) + { + resp->write("{\"error\":\"Hash length must be 40 characters.\"}"); + return; + } + uint8_t hash [20]; + if (!_hex2bin(hash, strHash)) + { + resp->write("{\"error\":\"invalid info_hash.\"}"); + return; + } + + + if (this->db->removeTorrent(hash)) + resp->write("{\"success\":true}"); + else + resp->write("{\"error\":\"failed to remove torrent from DB\"}"); + } + + void WebApp::doAddTorrent (HTTPServer::Request *req, HTTPServer::Response *resp) + { + string strHash = req->getParam("hash"); + if (strHash.length() != 40) + { + resp->write("{\"error\":\"Hash length must be 40 characters.\"}"); + return; + } + uint8_t hash [20]; + if (!_hex2bin(hash, strHash)) + { + resp->write("{\"error\":\"invalid info_hash.\"}"); + return; + } + + if (this->db->addTorrent(hash)) + resp->write("{\"success\":true}"); + else + resp->write("{\"error\":\"failed to add torrent to DB\"}"); + } + + void WebApp::handleAnnounce (HTTPServer *srv, HTTPServer::Request *req, HTTPServer::Response *resp) + { + resp->write("d14:failure reason42:this is a UDP tracker, not a HTTP tracker.e"); + } + + void WebApp::handleAPI(HTTPServer *srv, HTTPServer::Request *req, HTTPServer::Response *resp) + { + if (req->getAddress()->sin_family != AF_INET) + { + throw ServerException (0, "IPv4 supported Only."); + } + + string key = req->getParam("auth"); + if (key.length() <= 0) + throw ServerException (0, "Bad Authentication Key"); + + WebApp *app = (WebApp*)srv->getData("webapp"); + if (app == NULL) + throw ServerException(0, "WebApp object wasn't found"); + + if (!isAllowedIP(app, key, req->getAddress()->sin_addr.s_addr)) + { + resp->setStatus(403, "Forbidden"); + resp->write("IP not whitelisted. Access Denied."); + return; + } + + string action = req->getParam("action"); + if (action == "add") + app->doAddTorrent(req, resp); + else if (action == "remove") + app->doRemoveTorrent(req, resp); + else + { + resp->write("{\"error\":\"unknown action\"}"); + } + } + }; +}; diff --git a/src/http/webapp.hpp b/src/http/webapp.hpp new file mode 100644 index 0000000..6a8011f --- /dev/null +++ b/src/http/webapp.hpp @@ -0,0 +1,60 @@ +/* + * Copyright © 2013 Naim A. + * + * This file is part of UDPT. + * + * UDPT 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. + * + * UDPT 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 UDPT. If not, see . + */ + +#pragma once + +#include "httpserver.hpp" +#include "../db/database.hpp" +#include "../settings.hpp" +#include +#include +#include +using namespace std; + +using namespace UDPT; +using namespace UDPT::Data; + +namespace UDPT +{ + namespace Server + { + class WebApp + { + public: + WebApp (HTTPServer *, DatabaseDriver *, Settings *); + ~WebApp (); + void deploy (); + + + private: + HTTPServer *instance; + UDPT::Data::DatabaseDriver *db; + Settings::SettingClass *sc_api; + std::map > ip_whitelist; + + static void handleRoot (HTTPServer*,HTTPServer::Request*, HTTPServer::Response*); + static void handleAnnounce (HTTPServer*,HTTPServer::Request*, HTTPServer::Response*); + static void handleAPI (HTTPServer*,HTTPServer::Request*, HTTPServer::Response*); + static bool isAllowedIP (WebApp *, string, uint32_t); + + void doAddTorrent (HTTPServer::Request*, HTTPServer::Response*); + void doRemoveTorrent (HTTPServer::Request*, HTTPServer::Response*); + }; + }; +}; diff --git a/src/main.cpp b/src/main.cpp index 4baba4e..32e83f0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -22,15 +22,52 @@ #include "multiplatform.h" #include "udpTracker.hpp" #include "settings.hpp" +#include "http/httpserver.hpp" +#include "http/webapp.hpp" +#include // atoi using namespace std; using namespace UDPT; +using namespace UDPT::Server; static void _print_usage () { cout << "Usage: udpt []" << endl; } +static void _doAPIStart (Settings *settings, WebApp **wa, HTTPServer **srv, DatabaseDriver *drvr) +{ + if (settings == NULL) + return; + Settings::SettingClass *sc = settings->getClass("apiserver"); + if (sc == NULL) + return; // no settings set! + + if (sc->get("enable") != "1") + { + cerr << "API Server not enabled." << endl; + return; + } + + string s_port = sc->get("port"); + string s_threads = sc->get("threads"); + + uint16_t port = (s_port == "" ? 6969 : atoi (s_port.c_str())); + uint16_t threads = (s_threads == "" ? 1 : atoi (s_threads.c_str())); + + if (threads <= 0) + threads = 1; + + try { + *srv = new HTTPServer (port, threads); + *wa = new WebApp (*srv, drvr, settings); + (*wa)->deploy(); + } catch (ServerException &e) + { + cerr << "ServerException #" << e.getErrorCode() << ": " << e.getErrorMsg() << endl; + } +} + int main(int argc, char *argv[]) { Settings *settings = NULL; @@ -60,43 +97,53 @@ int main(int argc, char *argv[]) { const char strDATABASE[] = "database"; const char strTRACKER[] = "tracker"; + const char strAPISRV [] = "apiserver"; // set default settings: settings->set (strDATABASE, "driver", "sqlite3"); settings->set (strDATABASE, "file", "tracker.db"); - settings->set (strTRACKER, "port", "6969"); + settings->set (strTRACKER, "port", "6969"); // UDP PORT settings->set (strTRACKER, "threads", "5"); settings->set (strTRACKER, "allow_remotes", "yes"); settings->set (strTRACKER, "allow_iana_ips", "yes"); settings->set (strTRACKER, "announce_interval", "1800"); settings->set (strTRACKER, "cleanup_interval", "120"); + settings->set (strAPISRV, "enable", "1"); + settings->set (strAPISRV, "threads", "1"); + settings->set (strAPISRV, "port", "6969"); // TCP PORT + settings->save(); - cout << "Failed to read from '" << config_file.c_str() << "'. Using default settings." << endl; + cerr << "Failed to read from '" << config_file.c_str() << "'. Using default settings." << endl; } usi = new UDPTracker (settings); + HTTPServer *apiSrv = NULL; + WebApp *wa = NULL; + r = usi->start(); if (r != UDPTracker::START_OK) { - cout << "Error While trying to start server." << endl; + cerr << "Error While trying to start server." << endl; switch (r) { case UDPTracker::START_ESOCKET_FAILED: - cout << "Failed to create socket." << endl; + cerr << "Failed to create socket." << endl; break; case UDPTracker::START_EBIND_FAILED: - cout << "Failed to bind socket." << endl; + cerr << "Failed to bind socket." << endl; break; default: - cout << "Unknown Error" << endl; + cerr << "Unknown Error" << endl; break; } goto cleanup; } + _doAPIStart(settings, &wa, &apiSrv, usi->conn); + cout << "Press Any key to exit." << endl; cin.get(); @@ -106,6 +153,8 @@ cleanup: delete usi; delete settings; + delete apiSrv; + delete wa; #ifdef WIN32 WSACleanup(); diff --git a/src/multiplatform.h b/src/multiplatform.h index 97b5d4c..8b9c7a4 100644 --- a/src/multiplatform.h +++ b/src/multiplatform.h @@ -29,7 +29,7 @@ #ifdef WIN32 #include #include -#define VERSION "1.0.0 (Windows)" +#define VERSION "1.0.0-beta (Windows)" #elif defined (linux) #include #include @@ -52,9 +52,6 @@ typedef void* LPVOID; typedef void (LPTHREAD_START_ROUTINE)(LPVOID); typedef pthread_t HANDLE; -#ifndef min -#define min(a,b) (a > b ? b : a) -#endif -#define VERSION "1.0.0-alpha (Linux)" +#define VERSION "1.0.0-beta (Linux)" #endif diff --git a/src/settings.cpp b/src/settings.cpp index 2a7ea99..5ef0d05 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -249,6 +249,8 @@ void _settings_clean_string (char **str) SettingClass *c; c = this->getClass(classN); + if (c == NULL) + return ""; return c->get(name); } @@ -277,9 +279,16 @@ void _settings_clean_string (char **str) string Settings::SettingClass::get (const string name) { + if (this->entries.find(name) == this->entries.end()) + return ""; return this->entries[name]; } + map* Settings::SettingClass::getMap() + { + return &this->entries; + } + bool Settings::SettingClass::set (const string name, const string value) { pair::iterator, bool> r; diff --git a/src/settings.hpp b/src/settings.hpp index 8e9ec51..bda24d5 100644 --- a/src/settings.hpp +++ b/src/settings.hpp @@ -36,6 +36,7 @@ namespace UDPT SettingClass (const string className); bool set (const string key, const string value); string get (const string key); + map* getMap (); private: friend class Settings; string className; diff --git a/src/udpTracker.cpp b/src/udpTracker.cpp index 555fb51..d9e0a34 100644 --- a/src/udpTracker.cpp +++ b/src/udpTracker.cpp @@ -455,7 +455,7 @@ static int _isIANA_IP (uint32_t ip) } } - cout << ":: " << (void*)m_hton32(remote->sin_addr.s_addr) << ": " << m_hton16(remote->sin_port) << " ACTION=" << action << endl; +// cout << ":: " << (void*)m_hton32(remote->sin_addr.s_addr) << ": " << m_hton16(remote->sin_port) << " ACTION=" << action << endl; if (action == 0 && r >= 16) return UDPTracker::handleConnection (usi, remote, data); @@ -465,7 +465,7 @@ static int _isIANA_IP (uint32_t ip) return UDPTracker::handleScrape (usi, remote, data, r); else { - cout << "E: action=" << action << ", r=" << r << endl; +// cout << "E: action=" << action << ", r=" << r << endl; UDPTracker::sendError (usi, remote, cR->transaction_id, "Tracker couldn't understand Client's request."); return -1; } diff --git a/src/udpTracker.hpp b/src/udpTracker.hpp index f0bd18e..282020f 100644 --- a/src/udpTracker.hpp +++ b/src/udpTracker.hpp @@ -131,6 +131,7 @@ namespace UDPT */ virtual ~UDPTracker (); + Data::DatabaseDriver *conn; private: SOCKET sock; uint16_t port; @@ -142,7 +143,6 @@ namespace UDPT uint8_t settings; Settings *o_settings; - Data::DatabaseDriver *conn; #ifdef WIN32 static DWORD _thread_start (LPVOID arg);