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

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)