do not extern 'interp' from command.c
[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.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
122 int retcode;
123
124 const char *script = alloc_printf("set dummy_val $httppostdata(%s); set dummy_val",
125 name);
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 char *name = (char*)Jim_GetString(argv[1], NULL);
163
164
165 const char *script = alloc_printf("set dummy_val $httppostdata(%s); set dummy_val",
166 name);
167 int retcode = Jim_Eval_Named(interp, script, __FILE__, __LINE__);
168 free((void *) script);
169 if (retcode != JIM_OK)
170 {
171 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
172 } else
173 {
174 Jim_SetResult(interp, Jim_GetResult(interp));
175 }
176
177 return JIM_OK;
178 }
179
180 struct httpd_request
181 {
182 int post;
183 Jim_Interp *interp;
184 struct MHD_PostProcessor *postprocessor;
185
186 //Jim_Obj *dict;
187
188 int complete; /* did we receive the entire post ? */
189
190 };
191
192 static void request_completed(void *cls, struct MHD_Connection *connection,
193 void **con_cls, enum MHD_RequestTerminationCode toe)
194 {
195 struct httpd_request *r = (struct httpd_request*) *con_cls;
196
197 if (NULL == r)
198 return;
199
200 if (r->postprocessor)
201 {
202 openocd_sleep_postlude();
203 MHD_destroy_post_processor(r->postprocessor);
204 openocd_sleep_prelude();
205 }
206
207 free(r);
208 *con_cls = NULL;
209 }
210
211 /* append to said key in dictionary */
212 static void append_key(Jim_Interp *interp,
213 struct httpd_request *r, const char *key,
214 const char *data, size_t off, size_t size)
215 {
216 Jim_Obj *keyObj = Jim_NewStringObj(interp, key, -1);
217 Jim_IncrRefCount(keyObj);
218 Jim_Obj *value = NULL;
219
220 Jim_Obj *dict = Jim_GetVariableStr(interp, "httppostdata", 0);
221
222 if (dict != NULL)
223 {
224 if (Jim_DictKey(interp, dict, keyObj, &value, 0) != JIM_OK)
225 {
226 value = NULL;
227 }
228 else
229 {
230 Jim_IncrRefCount(value);
231 }
232 }
233
234 if (value == NULL)
235 {
236 value = Jim_NewStringObj(interp, "", -1);
237 Jim_IncrRefCount(value);
238
239 }
240
241 /* create a new object we append to and insert into this location */
242 Jim_Obj *newObj = Jim_NewStringObj(interp, "", -1);
243 Jim_IncrRefCount(newObj);
244 Jim_AppendObj(interp, newObj, value);
245 Jim_AppendString(interp, newObj, data, size);
246 /* uhh... use name here of dictionary */
247 dict = Jim_NewStringObj(interp, "httppostdata", -1);
248 Jim_IncrRefCount(dict);
249 Jim_SetDictKeysVector(interp, dict, &keyObj, 1, newObj);
250 Jim_DecrRefCount(interp, dict);
251 Jim_DecrRefCount(interp, value);
252 Jim_DecrRefCount(interp, newObj);
253 Jim_DecrRefCount(interp, keyObj);
254 }
255
256 /* append data to each key */
257 static int iterate_post(void *con_cls, enum MHD_ValueKind kind,
258 const char *key, const char *filename, const char *content_type,
259 const char *transfer_encoding, const char *data, uint64_t off,
260 size_t size)
261 {
262 struct httpd_request *r = (struct httpd_request*) con_cls;
263
264 append_key(r->interp, r, key, data, off, size);
265
266 return MHD_YES;
267 }
268
269 static int record_arg(void *cls, enum MHD_ValueKind kind, const char *key,
270 const char *value)
271 {
272 struct httpd_request *r = (struct httpd_request*) cls;
273 append_key(r->interp, r, key, value, 0, strlen(value));
274 return MHD_YES;
275 }
276
277
278 static int handle_request(Jim_Interp *interp,
279 struct MHD_Connection * connection, const char * url)
280 {
281 struct MHD_Response * response;
282
283 int ret;
284 const char *suffix;
285 suffix = strrchr(url, '.');
286 if ((suffix != NULL) && (strcmp(suffix, ".tcl") == 0))
287 {
288 printf("Run tcl %s\n", url);
289
290 int retcode;
291
292 const char *script = alloc_printf(
293 "global httpdata; source {%s}; set httpdata", url);
294 retcode = Jim_Eval_Named(interp, script, __FILE__, __LINE__);
295 free((void *) script);
296
297 if (retcode != JIM_OK)
298 {
299 printf("Tcl failed\n");
300 const char *t = httpd_exec_cgi_tcl_error(interp);
301 if (t == NULL)
302 return MHD_NO;
303
304 response = MHD_create_response_from_data(strlen(t), (void *) t,
305 MHD_YES, MHD_NO);
306 ret = MHD_queue_response(connection,
307 MHD_HTTP_INTERNAL_SERVER_ERROR, response);
308 MHD_destroy_response(response);
309 return ret;
310 }
311 else
312 {
313 LOG_DEBUG("Tcl OK");
314 /* FIX!!! how to handle mime types??? */
315 const char *result;
316 int reslen;
317 result = Jim_GetString(Jim_GetResult(interp), &reslen);
318
319 response = MHD_create_response_from_data(reslen, (void *) result,
320 MHD_NO, MHD_YES);
321 ret = MHD_queue_response(connection,
322 MHD_HTTP_INTERNAL_SERVER_ERROR, response);
323 MHD_destroy_response(response);
324 return ret;
325 }
326 }
327 else
328 {
329 void *data;
330 size_t len;
331
332 int retval = loadFile(url, &data, &len);
333 if (retval != ERROR_OK)
334 {
335 printf("Did not find %s\n", url);
336
337 response = MHD_create_response_from_data(strlen(PAGE_NOT_FOUND),
338 (void *) PAGE_NOT_FOUND, MHD_NO, MHD_NO);
339 ret = MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response);
340 MHD_destroy_response(response);
341 return ret;
342 }
343
344 LOG_DEBUG("Serving %s length=%zu", url, len);
345 /* serve file directly */
346 response = MHD_create_response_from_data(len, data, MHD_YES, MHD_NO);
347 /* Should we expose mimetype via tcl here or just let the browser
348 guess?
349 MHD_add_response_header(response, "Content-Type", "image/png");
350 */
351
352 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
353 MHD_destroy_response(response);
354
355 //free(data);
356 return ret;
357 }
358 }
359
360 static int ahc_echo_inner(void * cls, struct MHD_Connection * connection,
361 const char * url, const char * method, const char * version,
362 const char * upload_data, size_t * upload_data_size, void ** ptr)
363 {
364 Jim_Interp *interp = (Jim_Interp *)cls;
365 int post = 0;
366
367 if (0 == strcmp(method, "POST"))
368 {
369 post = 1;
370 }
371 else if (0 == strcmp(method, "GET"))
372 {
373 }
374 else
375 {
376 return MHD_NO; /* unexpected method */
377 }
378
379 struct httpd_request *r;
380 if (*ptr == NULL)
381 {
382 /* The first time only the headers are valid,
383 do not respond in the first round... */
384
385 *ptr = malloc(sizeof(struct httpd_request));
386 if (*ptr == NULL)
387 return MHD_NO;
388 memset(*ptr, 0, sizeof(struct httpd_request));
389
390 r = (struct httpd_request *) *ptr;
391 r->interp = interp;
392 r->post = post;
393 Jim_SetVariableStr(interp, "httppostdata", Jim_NewDictObj(interp, NULL, 0));
394
395 /* fill in url query strings in dictionary */
396 MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND,
397 record_arg, r);
398
399 if (r->post)
400 {
401 r->postprocessor = MHD_create_post_processor(connection, 2048
402 * 1024, &iterate_post, r);
403 }
404
405 return MHD_YES;
406 }
407
408 r = (struct httpd_request *) *ptr;
409
410 if (r->post)
411 {
412 /* consume post data */
413 if (*upload_data_size)
414 {
415 MHD_post_process(r->postprocessor, upload_data, *upload_data_size);
416 *upload_data_size = 0;
417 return MHD_YES;
418 }
419 else
420 {
421 }
422 } else
423 {
424 }
425
426 /* hand over to request who will be using it. */
427 // r->dict = NULL;
428
429
430 /* FIX!!!! we need more advanced handling of url's to avoid them
431 * being subverted to evil purposes
432 */
433
434 const char *httpd_dir = PKGDATADIR "/httpd";
435
436 if (*url=='/')
437 {
438 url++; /* skip '/' */
439 }
440 if (!*url)
441 url="index.tcl";
442
443 const char *file_name = alloc_printf("%s/%s", httpd_dir, url);
444 int result = handle_request(interp, connection, file_name);
445 free((void *)file_name);
446 return result;
447 }
448
449
450 static int ahc_echo(void * cls, struct MHD_Connection * connection,
451 const char * url, const char * method, const char * version,
452 const char * upload_data, size_t * upload_data_size, void ** ptr)
453 {
454 int result;
455
456 openocd_sleep_postlude();
457
458 result = ahc_echo_inner(cls, connection, url, method, version, upload_data, upload_data_size, ptr);
459
460 openocd_sleep_prelude();
461
462 return result;
463 }
464
465 static struct MHD_Daemon * d;
466
467 static const struct command_registration httpd_command_handlers[] = {
468 {
469 .name = "formfetch",
470 .jim_handler = &httpd_Jim_Command_formfetch,
471 .mode = COMMAND_EXEC,
472 .usage = "<parameter_name>",
473 .help = "Reads a posted form value.",
474 },
475 {
476 .name = "writeform",
477 .jim_handler = &httpd_Jim_Command_writeform,
478 .mode = COMMAND_EXEC,
479 .usage = "<parameter_name> <file>",
480 .help = "Writes a form value to a file.",
481 },
482 COMMAND_REGISTRATION_DONE
483 };
484
485 int httpd_start(struct command_context *cmd_ctx)
486 {
487 pthread_mutexattr_t attr;
488 pthread_mutexattr_init(&attr);
489 pthread_mutex_init(&mutex, &attr);
490
491 int port = 8888;
492 LOG_USER("Launching httpd server on port %d", port);
493 d = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, port, NULL, NULL,
494 &ahc_echo, cmd_ctx->interp,
495 MHD_OPTION_NOTIFY_COMPLETED, request_completed, NULL, /* Closure... what's that??? */
496 MHD_OPTION_END);
497 if (d == NULL)
498 return ERROR_FAIL;
499
500 return register_commands(cmd_ctx, NULL, httpd_command_handlers);
501 }
502
503 void httpd_stop(void)
504 {
505 MHD_stop_daemon(d);
506 pthread_mutex_destroy(&mutex);
507 }
508

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)