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

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)