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

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)