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

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)