Changeset View
Changeset View
Standalone View
Standalone View
plugins/htmlinterface/webserver.cpp
Show All 15 Lines | |||||
16 | * along with this program; if not, write to the * | 16 | * along with this program; if not, write to the * | ||
17 | * Free Software Foundation, Inc., * | 17 | * Free Software Foundation, Inc., * | ||
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * | 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * | ||
19 | ***************************************************************************/ | 19 | ***************************************************************************/ | ||
20 | 20 | | |||
21 | #include <iostream> | 21 | #include <iostream> | ||
22 | #include "webserver.h" | 22 | #include "webserver.h" | ||
23 | 23 | | |||
24 | using namespace std; | | |||
25 | | ||||
26 | #include <stdio.h> | 24 | #include <stdio.h> | ||
25 | #include <sys/stat.h> | ||||
26 | #include <fcntl.h> | ||||
27 | #include <string.h> | 27 | #include <string.h> | ||
28 | #include <QString> | 28 | #include <QString> | ||
29 | #include <QRegExp> | 29 | #include <QRegExp> | ||
30 | #include <QStringList> | 30 | #include <QStringList> | ||
31 | #include <QMimeDatabase> | ||||
32 | #include <QUuid> | ||||
33 | #define GET 0 | ||||
34 | #define POST 1 | ||||
35 | #define POSTBUFFERSIZE 65536 | ||||
31 | 36 | | |||
32 | using namespace std; | 37 | using namespace std; | ||
33 | 38 | | |||
34 | namespace kt { | 39 | namespace kt { | ||
35 | WebServer::WebServer(CoreInterface * core): core(core) | 40 | WebServer::WebServer(CoreInterface * core): core(core) | ||
36 | { | 41 | { | ||
37 | 42 | | |||
38 | } | 43 | } | ||
39 | 44 | | |||
40 | WebServer::~WebServer() | 45 | WebServer::~WebServer() | ||
41 | { | 46 | { | ||
47 | MHD_stop_daemon (daemon); | ||||
42 | } | 48 | } | ||
43 | 49 | | |||
50 | static QMimeDatabase mimeDatabase; | ||||
51 | | ||||
52 | static QString BASE_DIRECTORY = QString::fromLatin1("/usr/share/ktorrent/html/"); | ||||
53 | | ||||
44 | TorrentListGenerator * WebServer::listGenerator; | 54 | TorrentListGenerator * WebServer::listGenerator; | ||
45 | 55 | | |||
46 | void WebServer::process() | 56 | QString WebServer::sessionToken; | ||
57 | | ||||
58 | const char * WebServer::cookie; | ||||
59 | | ||||
60 | static int respond404(MHD_Connection * connection) | ||||
47 | { | 61 | { | ||
48 | listGenerator = new TorrentListGenerator(core); | 62 | int ret; | ||
49 | struct mg_connection *c; | 63 | struct MHD_Response *response; | ||
50 | 64 | | |||
51 | mg_mgr_init(&mgr, NULL); | 65 | const char * message = "<html><body>404: The requested resource was not found.</body></html>"; | ||
52 | c = mg_bind(&mgr, "8880", WebServer::httpeventhandler); | 66 | | ||
53 | mg_set_protocol_http_websocket(c); | 67 | response = MHD_create_response_from_buffer (strlen(message), (void *) message, MHD_RESPMEM_PERSISTENT); | ||
54 | while (true) { | 68 | ret = MHD_queue_response (connection, MHD_HTTP_NOT_FOUND, response); | ||
55 | mg_mgr_poll(&mgr, 1000); | 69 | MHD_add_response_header (response, MHD_HTTP_HEADER_CONTENT_ENCODING, "text/html"); | ||
56 | } | 70 | MHD_destroy_response (response); | ||
71 | return ret; | ||||
57 | } | 72 | } | ||
58 | 73 | | |||
59 | void WebServer::httpeventhandler(struct mg_connection * c, int ev, void * ev_data) { | 74 | static int respond500(MHD_Connection * connection) | ||
60 | if (ev == MG_EV_HTTP_REQUEST) | | |||
61 | { | 75 | { | ||
62 | struct mg_serve_http_opts opts; | 76 | int ret; | ||
77 | struct MHD_Response *response; | ||||
63 | 78 | | |||
64 | memset(&opts, 0, sizeof(opts)); // Reset all options to defaults | 79 | const char * message = "<html><body>500: An internal server error has occured.</body></html>"; | ||
65 | opts.document_root = "/usr/share/ktorrent/html/"; | | |||
66 | 80 | | |||
67 | struct http_message * message = (struct http_message *) ev_data; | 81 | response = MHD_create_response_from_buffer (strlen(message), (void *) message, MHD_RESPMEM_PERSISTENT); | ||
82 | ret = MHD_queue_response (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, response); | ||||
83 | MHD_add_response_header (response, MHD_HTTP_HEADER_CONTENT_ENCODING, "text/html"); | ||||
84 | MHD_destroy_response (response); | ||||
85 | return ret; | ||||
86 | } | ||||
68 | 87 | | |||
69 | char * method = new char[message->method.len + 1]; | 88 | struct Json | ||
70 | strncpy(method, message->method.p, message->method.len); | 89 | { | ||
71 | method[message->method.len] = '\0'; | 90 | QString json; | ||
91 | bool firstPass; | ||||
92 | }; | ||||
72 | 93 | | |||
73 | char * uri = new char[message->uri.len + 1]; | 94 | int WebServer::answer_to_connection(void * cls, struct MHD_Connection * connection, | ||
74 | strncpy(uri, message->uri.p, message->uri.len); | 95 | const char * url, const char * method, | ||
75 | uri[message->uri.len] = '\0'; | 96 | const char * version, const char * upload_data, | ||
97 | size_t * upload_data_size, void ** con_cls) | ||||
98 | { | ||||
99 | int ret; | ||||
100 | struct MHD_Response *response; | ||||
76 | 101 | | |||
77 | if (strcmp(uri, "/ktorrentdata") == 0 && strcmp(method, "GET") == 0) | 102 | if (strcmp("/ktorrentdata", url) == 0 && strcmp("GET", method) == 0) | ||
103 | { | ||||
104 | QByteArray json = listGenerator->get(); | ||||
105 | response = MHD_create_response_from_buffer (json.size(), (void *) json.data(), | ||||
106 | MHD_RESPMEM_PERSISTENT); | ||||
107 | ret = MHD_queue_response (connection, MHD_HTTP_OK, response); | ||||
108 | MHD_destroy_response (response); | ||||
109 | return ret; | ||||
110 | } | ||||
111 | if (strcmp("/ktorrentaction", url) == 0 && strcmp("POST", method) == 0) | ||||
112 | { | ||||
113 | Json * json; | ||||
114 | if (*con_cls == NULL) | ||||
78 | { | 115 | { | ||
79 | char * json = NULL; | 116 | json = new Json(); | ||
80 | int size; | 117 | *con_cls = (void *) json; | ||
81 | listGenerator->get(&json, &size); | 118 | json->firstPass = true; | ||
82 | mg_send_head(c, 200, size, "Content-Type: application/json\nAccess-Control-Allow-Origin: *"); | | |||
83 | mg_printf(c, "%s", json); | | |||
84 | return; | | |||
85 | } | 119 | } | ||
86 | if (strcmp(uri, "/ktorrentaction") == 0) | 120 | else | ||
87 | { | 121 | { | ||
88 | char * body = new char[message->body.len + 1]; | 122 | json = (Json *) *con_cls; | ||
89 | strncpy(body, message->body.p, message->body.len); | 123 | } | ||
90 | body[message->body.len] = '\0'; | | |||
91 | 124 | | |||
92 | if (strcmp(method, "POST") == 0) { | 125 | if (json->firstPass) { | ||
93 | listGenerator->post(body); | 126 | json->firstPass = false; | ||
127 | return MHD_YES; | ||||
128 | } | ||||
129 | if (*upload_data_size > 0 && !json->firstPass) | ||||
130 | { | ||||
131 | QString qJson = QString::fromLatin1(upload_data, *upload_data_size); | ||||
132 | json->json.append(qJson); | ||||
133 | if (checkForToken(connection)) | ||||
134 | { | ||||
135 | listGenerator->post(json->json); | ||||
136 | } | ||||
137 | *upload_data_size = 0; | ||||
138 | return MHD_YES; | ||||
94 | } | 139 | } | ||
95 | const char * validation = "{\"status\":\"Request received.\"}"; | 140 | const char * validation = "{\"status\":\"Request received.\"}"; | ||
96 | int len = strlen(validation); | 141 | response = MHD_create_response_from_buffer(strlen(validation), (void *) validation, | ||
97 | mg_send_head(c, 200, len, "Content-Type: application/json\nAccess-Control-Allow-Origin: *\nAccess-Control-Allow-Headers: X-ACCESS_TOKEN, Access-Control-Allow-Origin, Authorization, Origin, x-requested-with, Content-Type, Content-Range, Content-Disposition, Content-Description\nAccess-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS"); | 142 | MHD_RESPMEM_PERSISTENT); | ||
98 | mg_printf(c, "%s", validation); | 143 | MHD_add_response_header(response, "Content-Type", "application/json"); | ||
99 | return; | 144 | ret = MHD_queue_response(connection, MHD_HTTP_OK, response); | ||
145 | MHD_destroy_response(response); | ||||
146 | return ret; | ||||
147 | } | ||||
148 | struct stat sbuf; | ||||
149 | int fd; | ||||
150 | if (0 == strcmp(url, "/") || 0 == strcmp(url, "/index")) | ||||
151 | { | ||||
152 | url = "index.html"; | ||||
153 | } | ||||
154 | QString qUrl = QString::fromLatin1(url); | ||||
155 | const char * file = (BASE_DIRECTORY + qUrl).toLatin1().data(); | ||||
156 | if ((-1 == (fd = open(file, O_RDONLY))) || (0 != fstat(fd, &sbuf))) | ||||
157 | { | ||||
158 | return respond404(connection); | ||||
159 | } | ||||
160 | response = MHD_create_response_from_fd_at_offset64(sbuf.st_size, fd, 0); | ||||
161 | const char * mimeType = mimeDatabase.mimeTypeForFile(qUrl).name().toLatin1().data(); | ||||
162 | MHD_add_response_header(response, "Content-Type", mimeType); | ||||
163 | MHD_add_response_header(response, "Set-Cookie", makeCookie()); | ||||
164 | ret = MHD_queue_response(connection, MHD_HTTP_OK, response); | ||||
165 | MHD_destroy_response (response); | ||||
166 | return ret; | ||||
167 | } | ||||
168 | | ||||
169 | static void request_completed (void *cls, struct MHD_Connection *connection, | ||||
170 | void **con_cls, enum MHD_RequestTerminationCode toe) | ||||
171 | { | ||||
172 | Json * json = (Json *) *con_cls; | ||||
173 | | ||||
174 | delete json; | ||||
175 | } | ||||
176 | | ||||
177 | void WebServer::process() | ||||
178 | { | ||||
179 | listGenerator = new TorrentListGenerator(core); | ||||
180 | sessionToken = QUuid::createUuid().toString(); | ||||
181 | cookie = makeCookie(); | ||||
182 | | ||||
183 | daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, 8880, NULL, NULL, | ||||
184 | &answer_to_connection, NULL, | ||||
185 | MHD_OPTION_NOTIFY_COMPLETED, request_completed, | ||||
186 | NULL, MHD_OPTION_END); | ||||
187 | } | ||||
188 | | ||||
189 | const char * WebServer::makeCookie() | ||||
190 | { | ||||
191 | QString tempString = QString::fromLatin1("token=") + sessionToken; | ||||
192 | return tempString.toLatin1().data(); | ||||
100 | } | 193 | } | ||
101 | mg_serve_http(c, (struct http_message *) ev_data, opts); | 194 | | ||
195 | bool WebServer::checkForToken(MHD_Connection * connection) | ||||
196 | { | ||||
197 | const char * token = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "session-Token"); | ||||
198 | if (token == NULL) | ||||
199 | { | ||||
200 | return false; | ||||
201 | } | ||||
202 | if (strcmp(token, sessionToken.toLatin1().data()) == 0) | ||||
203 | { | ||||
204 | return true; | ||||
102 | } | 205 | } | ||
206 | return false; | ||||
103 | } | 207 | } | ||
104 | } | 208 | } |