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

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)