Fix warnings in httpd.c; builds with libmicrohttpd-0.4.1.
[openocd.git] / src / server / httpd.c
1 /***************************************************************************
2 * Copyright (C) 2007,2008 Øyvind Harboe *
3 * oyvind.harboe@zylin.com *
4 * *
5 * Copyright (C) 2008 Free Software Foundation
6 * *
7 * This program is free software; you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation; either version 2 of the License, or *
10 * (at your option) any later version. *
11 * *
12 * This program is distributed in the hope that it will be useful, *
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15 * GNU General Public License for more details. *
16 * *
17 * You should have received a copy of the GNU General Public License *
18 * along with this program; if not, write to the *
19 * Free Software Foundation, Inc., *
20 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
21 ***************************************************************************/
22
23 /* some bits were copied from ahttpd which is under eCos license and
24 * copyright to FSF
25 */
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #include "replacements.h"
31
32 #include "server.h"
33
34 #include "log.h"
35 #include "telnet_server.h"
36 #include "target.h"
37
38 #include <command.h>
39 #include <string.h>
40 #include <stdlib.h>
41 #include <errno.h>
42 #include <unistd.h>
43 #include <sys/types.h>
44 #include <fcntl.h>
45 #include <pthread.h>
46 #include <signal.h>
47
48 #include <sys/types.h>
49 #include <sys/select.h>
50 #include <sys/socket.h>
51 #include <microhttpd.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <stdio.h>
55
56 #define PAGE_NOT_FOUND "<html><head><title>File not found</title></head><body>File not found</body></html>"
57
58 int loadFile(const char *name, void **data, size_t *len);
59
60 static const char *appendf(const char *prev, const char *format, ...)
61 {
62 va_list ap;
63 va_start(ap, format);
64 char *string = alloc_vprintf(format, ap);
65 va_end(ap);
66 char *string2 = NULL;
67
68 if (string != NULL)
69 {
70 string2 = alloc_printf("%s%s", (prev == NULL) ? "" : prev, string);
71 }
72
73 if (prev != NULL)
74 {
75 free((void *)prev);
76 }
77
78 if (string == NULL)
79 free(string);
80
81 return string2;
82 }
83
84 static const char *httpd_exec_cgi_tcl_error(Jim_Interp *interp)
85 {
86 int len, i;
87
88 const char *t = NULL;
89 t = appendf(t, "<html><body>\n");
90
91 t = appendf(t, "Runtime error, file \"%s\", line %d:<br>",
92 interp->errorFileName, interp->errorLine);
93 t = appendf(t, " %s<br>", Jim_GetString(interp->result, NULL));
94 Jim_ListLength(interp, interp->stackTrace, &len);
95 for (i = 0; i < len; i += 3)
96 {
97 Jim_Obj *objPtr;
98 const char *proc, *file, *line;
99
100 Jim_ListIndex(interp, interp->stackTrace, i, &objPtr, JIM_NONE);
101 proc = Jim_GetString(objPtr, NULL);
102 Jim_ListIndex(interp, interp->stackTrace, i + 1, &objPtr, JIM_NONE);
103 file = Jim_GetString(objPtr, NULL);
104 Jim_ListIndex(interp, interp->stackTrace, i + 2, &objPtr, JIM_NONE);
105 line = Jim_GetString(objPtr, NULL);
106 t = appendf(t, "In procedure '%s' called at file \"%s\", line %s<br>",
107 proc, file, line);
108 }
109 t = appendf(t, "</html></body>\n");
110
111 return t;
112 }
113
114 static int httpd_Jim_Command_writeform(Jim_Interp *interp, int argc,
115 Jim_Obj * const *argv)
116 {
117 if (argc != 3)
118 {
119 Jim_WrongNumArgs(interp, 1, argv, "method ?args ...?");
120 return JIM_ERR;
121 }
122 char *name = (char*) Jim_GetString(argv[1], NULL);
123 char *file = (char*) Jim_GetString(argv[2], NULL);
124
125 // Find length
126 const char *data;
127 int actual;
128
129 int retcode;
130
131 const char *script = alloc_printf("set dummy_val $httppostdata(%s); set dummy_val",
132 name);
133 retcode = Jim_Eval_Named(interp, script, "httpd.c", __LINE__ );
134 free((void *) script);
135 if (retcode != JIM_OK)
136 return retcode;
137
138 data = Jim_GetString(Jim_GetResult(interp), &actual);
139
140 FILE *f = fopen(file, "wb");
141 if (NULL == f)
142 {
143 Jim_SetResultString(interp, "Could not create file", -1);
144 return JIM_ERR;
145 }
146
147 int result = fwrite(data, 1, actual, f);
148 fclose(f);
149
150 if (result != actual)
151 {
152 Jim_SetResultString(interp, "Could not write to file", -1);
153 return JIM_ERR;
154 }
155 return JIM_OK;
156 }
157
158
159 int
160 httpd_Jim_Command_formfetch(Jim_Interp *interp,
161 int argc,
162 Jim_Obj *const *argv)
163 {
164 if (argc!=2)
165 {
166 Jim_WrongNumArgs(interp, 1, argv, "method ?args ...?");
167 return JIM_ERR;
168 }
169 char *name = (char*)Jim_GetString(argv[1], NULL);
170
171
172 const char *script = alloc_printf("set dummy_val $httppostdata(%s); set dummy_val",
173 name);
174 int retcode = Jim_Eval_Named(interp, script, "httpd.c", __LINE__ );
175 free((void *) script);
176 if (retcode != JIM_OK)
177 {
178 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
179 } else
180 {
181 Jim_SetResult(interp, Jim_GetResult(interp));
182 }
183
184 return JIM_OK;
185 }
186
187 struct httpd_request
188 {
189 int post;
190 struct MHD_PostProcessor *postprocessor;
191
192 //Jim_Obj *dict;
193
194 int complete; /* did we receive the entire post ? */
195
196 };
197
198 static void request_completed(void *cls, struct MHD_Connection *connection,
199 void **con_cls, enum MHD_RequestTerminationCode toe)
200 {
201 struct httpd_request *r = (struct httpd_request*) *con_cls;
202
203 if (NULL == r)
204 return;
205
206 if (r->postprocessor)
207 {
208 MHD_destroy_post_processor(r->postprocessor);
209 }
210
211 free(r);
212 *con_cls = NULL;
213 }
214
215 /* append to said key in dictonary */
216 static void append_key(struct httpd_request *r, const char *key,
217 const char *data, size_t off, size_t size)
218 {
219 Jim_Obj *keyObj = Jim_NewStringObj(interp, key, -1);
220 Jim_Obj *value = NULL;
221
222 Jim_Obj *dict = Jim_GetVariableStr(interp, "httppostdata", 0);
223
224 if (dict!=NULL)
225 {
226 if (Jim_DictKey(interp, dict, keyObj, &value, 0) != JIM_OK)
227 {
228 value = NULL;
229 }
230 }
231 if (value == NULL)
232 value = Jim_NewStringObj(interp, "", -1);
233
234 /* create a new object we append to and insert into this location */
235 Jim_Obj *newObj = Jim_NewStringObj(interp, "", -1);
236 Jim_AppendObj(interp, newObj, value);
237 Jim_AppendString(interp, newObj, data, size);
238 /* uhh... use name here of dictionary */
239 Jim_SetDictKeysVector(interp, Jim_NewStringObj(interp, "httppostdata", -1), &keyObj, 1, newObj);
240 }
241
242 /* append data to each key */
243 static int iterate_post(void *con_cls, enum MHD_ValueKind kind,
244 const char *key, const char *filename, const char *content_type,
245 const char *transfer_encoding, const char *data, uint64_t off,
246 size_t size)
247 {
248 struct httpd_request *r = (struct httpd_request*) con_cls;
249
250 append_key(r, key, data, off, size);
251
252 return MHD_YES;
253 }
254
255 static int record_arg(void *cls, enum MHD_ValueKind kind, const char *key,
256 const char *value)
257 {
258 struct httpd_request *r = (struct httpd_request*) cls;
259 append_key(r, key, value, 0, strlen(value));
260 return MHD_YES;
261 }
262
263
264 int handle_request(struct MHD_Connection * connection, const char * url)
265 {
266 struct MHD_Response * response;
267
268 int ret;
269 const char *suffix;
270 suffix = strrchr(url, '.');
271 if ((suffix != NULL) && (strcmp(suffix, ".tcl") == 0))
272 {
273 printf("Run tcl %s\n", url);
274
275 int retcode;
276
277 const char *script = alloc_printf(
278 "global httpdata; source {%s}; set httpdata", url);
279 retcode = Jim_Eval_Named(interp, script, "httpd.c", __LINE__ );
280 free((void *) script);
281
282 if (retcode == JIM_ERR)
283 {
284 printf("Tcl failed\n");
285 const char *t = httpd_exec_cgi_tcl_error(interp);
286 if (t == NULL)
287 return MHD_NO;
288
289 response = MHD_create_response_from_data(strlen(t), (void *) t,
290 MHD_YES, MHD_NO);
291 ret = MHD_queue_response(connection,
292 MHD_HTTP_INTERNAL_SERVER_ERROR, response);
293 MHD_destroy_response(response);
294 return ret;
295 }
296 else
297 {
298 LOG_DEBUG("Tcl OK");
299 /* FIX!!! how to handle mime types??? */
300 const char *result;
301 int reslen;
302 result = Jim_GetString(Jim_GetResult(interp), &reslen);
303
304 response = MHD_create_response_from_data(reslen, (void *) result,
305 MHD_NO, MHD_YES);
306 ret = MHD_queue_response(connection,
307 MHD_HTTP_INTERNAL_SERVER_ERROR, response);
308 MHD_destroy_response(response);
309 return ret;
310 }
311 }
312 else
313 {
314 void *data;
315 size_t len;
316
317 int retval = loadFile(url, &data, &len);
318 if (retval != ERROR_OK)
319 {
320 printf("Did not find %s\n", url);
321
322 response = MHD_create_response_from_data(strlen(PAGE_NOT_FOUND),
323 (void *) PAGE_NOT_FOUND, MHD_NO, MHD_NO);
324 ret = MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response);
325 MHD_destroy_response(response);
326 return ret;
327 }
328
329 LOG_DEBUG("Serving %s length=%u", url, len);
330 /* serve file directly */
331 response = MHD_create_response_from_data(len, data, MHD_YES, MHD_NO);
332 MHD_add_response_header(response, "Content-Type", "image/png");
333
334 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
335 MHD_destroy_response(response);
336
337 //free(data);
338 return ret;
339 }
340 }
341
342 static int ahc_echo(void * cls, struct MHD_Connection * connection,
343 const char * url, const char * method, const char * version,
344 const char * upload_data, unsigned int * upload_data_size, void ** ptr)
345 {
346 int post = 0;
347
348 if (0 == strcmp(method, "POST"))
349 {
350 post = 1;
351 }
352 else if (0 == strcmp(method, "GET"))
353 {
354 }
355 else
356 {
357 return MHD_NO; /* unexpected method */
358 }
359
360 struct httpd_request *r;
361 if (*ptr == NULL)
362 {
363 /* The first time only the headers are valid,
364 do not respond in the first round... */
365
366 *ptr = malloc(sizeof(struct httpd_request));
367 if (*ptr == NULL)
368 return MHD_NO;
369 memset(*ptr, 0, sizeof(struct httpd_request));
370
371 r = (struct httpd_request *) *ptr;
372
373 r->post = post;
374 Jim_SetVariableStr(interp, "httppostdata", Jim_NewDictObj(interp, NULL, 0));
375
376 /* fill in url query strings in dictonary */
377 MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND,
378 record_arg, r);
379
380 if (r->post)
381 {
382 r->postprocessor = MHD_create_post_processor(connection, 2048
383 * 1024, &iterate_post, r);
384 }
385
386 return MHD_YES;
387 }
388
389 r = (struct httpd_request *) *ptr;
390
391 if (r->post)
392 {
393 /* consume post data */
394 if (*upload_data_size)
395 {
396 MHD_post_process(r->postprocessor, upload_data, *upload_data_size);
397 *upload_data_size = 0;
398 return MHD_YES;
399 }
400 else
401 {
402 }
403 } else
404 {
405 }
406
407 /* hand over to request who will be using it. */
408 // r->dict = NULL;
409
410
411 /* FIX!!!! we need more advanced handling of url's to avoid them
412 * being subverted to evil purposes
413 */
414
415 const char *httpd_dir=PKGLIBDIR "/httpd";
416
417 if (*url=='/')
418 {
419 url++; /* skip '/' */
420 }
421 if (!*url)
422 url="index.tcl";
423
424 const char *file_name=alloc_printf("%s/%s", httpd_dir, url);
425 int result = handle_request(connection, file_name);
426 free((void *)file_name);
427 return result;
428 }
429
430 static struct MHD_Daemon * d;
431 static pthread_mutex_t mutex;
432
433
434 int httpd_start(void)
435 {
436 pthread_mutexattr_t attr;
437 pthread_mutexattr_init( &attr );
438 pthread_mutex_init( &mutex, &attr );
439
440 int port = 8888;
441 LOG_USER("Launching httpd server on port %d", port);
442 d = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, port, NULL, NULL,
443 &ahc_echo, NULL, /* could be data for handler, but we only have a single handler, use global variables instead */
444 MHD_OPTION_NOTIFY_COMPLETED, request_completed, NULL, /* Closure... what's that??? */
445 MHD_OPTION_END);
446 if (d == NULL)
447 return ERROR_FAIL;
448
449 Jim_CreateCommand(interp,
450 "formfetch",
451 httpd_Jim_Command_formfetch,
452 NULL,
453 NULL);
454
455 Jim_CreateCommand(interp,
456 "writeform",
457 httpd_Jim_Command_writeform,
458 NULL,
459 NULL);
460
461
462 return ERROR_OK;
463 }
464
465 void httpd_stop(void)
466 {
467 MHD_stop_daemon(d);
468 pthread_mutex_destroy( &mutex );
469 }
470
471 void openocd_sleep_prelude(void)
472 {
473 pthread_mutex_unlock( &mutex );
474 }
475
476 void openocd_sleep_postlude(void)
477 {
478 pthread_mutex_lock( &mutex );
479 }
480

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)