telnet_server: review unused symbols
[fw/openocd] / src / server / httpd.c
index b9df9085e7acbab7f1fd1f50e67c0138366c86d6..af8c3c8bb5f7097b403b498591f485c26599b78f 100644 (file)
-/***************************************************************************\r
- *   Copyright (C) 2007,2008 Øyvind Harboe                                 *\r
- *   oyvind.harboe@zylin.com                                               *\r
- *                                                                         *\r
- *   Copyright (C) 2008 Free Software Foundation\r
- *                                                                         *\r
- *   This program is free software; you can redistribute it and/or modify  *\r
- *   it under the terms of the GNU General Public License as published by  *\r
- *   the Free Software Foundation; either version 2 of the License, or     *\r
- *   (at your option) any later version.                                   *\r
- *                                                                         *\r
- *   This program is distributed in the hope that it will be useful,       *\r
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *\r
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *\r
- *   GNU General Public License for more details.                          *\r
- *                                                                         *\r
- *   You should have received a copy of the GNU General Public License     *\r
- *   along with this program; if not, write to the                         *\r
- *   Free Software Foundation, Inc.,                                       *\r
- *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *\r
- ***************************************************************************/\r
-\r
-/* some bits were copied from ahttpd which is under eCos license and\r
- * copyright to FSF\r
- */\r
-#ifdef HAVE_CONFIG_H\r
-#include "config.h"\r
-#endif\r
-\r
-#include "replacements.h"\r
-\r
-#include "server.h"\r
-\r
-#include "log.h"\r
-#include "telnet_server.h"\r
-#include "target.h"\r
-\r
-#include <command.h>\r
-#include <string.h>\r
-#include <stdlib.h>\r
-#include <errno.h>\r
-#include <unistd.h>\r
-#include <sys/types.h>\r
-#include <fcntl.h>\r
-#include <signal.h>\r
-\r
-#include <sys/types.h>\r
-#include <sys/select.h>\r
-#include <sys/socket.h>\r
-#include <microhttpd.h>\r
-#include <stdlib.h>\r
-#include <string.h>\r
-#include <stdio.h>\r
-\r
-#define PAGE_NOT_FOUND "<html><head><title>File not found</title></head><body>File not found</body></html>"\r
-\r
-static const char *appendf(const *prev, const char *format, ...)\r
-{\r
-       va_list ap;\r
-       va_start(ap, format);\r
-       char *string = alloc_vprintf(format, ap);\r
-       va_end(ap);\r
-       char *string2 = NULL;\r
-\r
-       if (string != NULL)\r
-       {\r
-               string2 = alloc_printf("%s%s", (prev == NULL) ? "" : prev, string);\r
-       }\r
-\r
-       if (prev != NULL)\r
-       {\r
-               free(prev);\r
-       }\r
-\r
-       if (string == NULL)\r
-               free(string);\r
-\r
-       return string2;\r
-}\r
-\r
-static const char *httpd_exec_cgi_tcl_error(Jim_Interp *interp)\r
-{\r
-       int len, i;\r
-\r
-       const char *t = NULL;\r
-       t = appendf(t, "<html><body>\n");\r
-\r
-       t = appendf(t, "Runtime error, file \"%s\", line %d:<br>",\r
-                       interp->errorFileName, interp->errorLine);\r
-       t = appendf(t, "    %s<br>", Jim_GetString(interp->result, NULL));\r
-       Jim_ListLength(interp, interp->stackTrace, &len);\r
-       for (i = 0; i < len; i += 3)\r
-       {\r
-               Jim_Obj *objPtr;\r
-               const char *proc, *file, *line;\r
-\r
-               Jim_ListIndex(interp, interp->stackTrace, i, &objPtr, JIM_NONE);\r
-               proc = Jim_GetString(objPtr, NULL);\r
-               Jim_ListIndex(interp, interp->stackTrace, i + 1, &objPtr, JIM_NONE);\r
-               file = Jim_GetString(objPtr, NULL);\r
-               Jim_ListIndex(interp, interp->stackTrace, i + 2, &objPtr, JIM_NONE);\r
-               line = Jim_GetString(objPtr, NULL);\r
-               t = appendf(t, "In procedure '%s' called at file \"%s\", line %s<br>",\r
-                               proc, file, line);\r
-       }\r
-       t = appendf(t, "</html></body>\n");\r
-\r
-       return t;\r
-}\r
-\r
-static int httpd_Jim_Command_writeform(Jim_Interp *interp, int argc,\r
-               Jim_Obj * const *argv)\r
-{\r
-       if (argc != 3)\r
-       {\r
-               Jim_WrongNumArgs(interp, 1, argv, "method ?args ...?");\r
-               return JIM_ERR;\r
-       }\r
-       char *name = (char*) Jim_GetString(argv[1], NULL);\r
-       char *file = (char*) Jim_GetString(argv[2], NULL);\r
-\r
-       // Find length\r
-       char *data;\r
-       int actual;\r
-\r
-       int retcode;\r
-\r
-       const char *script = alloc_printf("set dummy_val $httppostdata(%s); set dummy_val",\r
-                       name);\r
-       retcode = Jim_Eval_Named(interp, script, "httpd.c", __LINE__ );\r
-       free((void *) script);\r
-       if (retcode != JIM_OK)\r
-               return retcode;\r
-\r
-       data = Jim_GetString(Jim_GetResult(interp), &actual);\r
-\r
-       FILE *f;\r
-       f = fopen(file, "wb");\r
-       if (f != NULL)\r
-       {\r
-               int ok;\r
-               ok = fwrite(data, 1, actual, f) == actual;\r
-               fclose(f);\r
-\r
-               if (!ok)\r
-               {\r
-                       Jim_SetResultString(interp, "Could not write to file", -1);\r
-                       return JIM_ERR;\r
-               }\r
-       }\r
-       else\r
-       {\r
-               Jim_SetResultString(interp, "Could not create file", -1);\r
-               return JIM_ERR;\r
-       }\r
-       return JIM_OK;\r
-}\r
-\r
-\r
-int\r
-httpd_Jim_Command_formfetch(Jim_Interp *interp,\r
-                                   int argc,\r
-                                   Jim_Obj *const *argv)\r
-{\r
-    if (argc!=2)\r
-    {\r
-        Jim_WrongNumArgs(interp, 1, argv, "method ?args ...?");\r
-        return JIM_ERR;\r
-    }\r
-    char *name = (char*)Jim_GetString(argv[1], NULL);\r
-\r
-\r
-    const char *script = alloc_printf("set dummy_val $httppostdata(%s); set dummy_val",\r
-                       name);\r
-       int retcode = Jim_Eval_Named(interp, script, "httpd.c", __LINE__ );\r
-       free((void *) script);\r
-       if (retcode != JIM_OK)\r
-       {\r
-           Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));\r
-       } else\r
-       {\r
-           Jim_SetResult(interp, Jim_GetResult(interp));\r
-       }\r
-\r
-    return JIM_OK;\r
-}\r
-\r
-struct httpd_request\r
-{\r
-       int post;\r
-       struct MHD_PostProcessor *postprocessor;\r
-\r
-       //Jim_Obj *dict;\r
-\r
-       int complete; /* did we receive the entire post ? */\r
-\r
-};\r
-\r
-static void request_completed(void *cls, struct MHD_Connection *connection,\r
-               void **con_cls, enum MHD_RequestTerminationCode toe)\r
-{\r
-       struct httpd_request *r = (struct httpd_request*) *con_cls;\r
-\r
-       if (NULL == r)\r
-               return;\r
-\r
-       if (r->postprocessor)\r
-       {\r
-               MHD_destroy_post_processor(r->postprocessor);\r
-       }\r
-\r
-       free(r);\r
-       *con_cls = NULL;\r
-}\r
-\r
-/* append to said key in dictonary */\r
-static void append_key(struct httpd_request *r, const char *key,\r
-               const char *data, size_t off, size_t size)\r
-{\r
-       Jim_Obj *keyObj = Jim_NewStringObj(interp, key, -1);\r
-       Jim_Obj *value = NULL;\r
-\r
-       Jim_Obj *dict = Jim_GetVariableStr(interp, "httppostdata", 0);\r
-\r
-       if (dict!=NULL)\r
-       {\r
-               if (Jim_DictKey(interp, dict, keyObj, &value, 0) != JIM_OK)\r
-               {\r
-                        value = NULL;\r
-               }\r
-       }\r
-       if (value == NULL)\r
-               value = Jim_NewStringObj(interp, "", -1);\r
-\r
-       /* create a new object we append to and insert into this location */\r
-       Jim_Obj *newObj = Jim_NewStringObj(interp, "", -1);\r
-       Jim_AppendObj(interp, newObj, value);\r
-       Jim_AppendString(interp, newObj, data, size);\r
-       /* uhh... use name here of dictionary */\r
-       Jim_SetDictKeysVector(interp, Jim_NewStringObj(interp, "httppostdata", -1), &keyObj, 1, newObj);\r
-}\r
-\r
-/* append data to each key */\r
-static int iterate_post(void *con_cls, enum MHD_ValueKind kind,\r
-               const char *key, const char *filename, const char *content_type,\r
-               const char *transfer_encoding, const char *data, size_t off,\r
-               size_t size)\r
-{\r
-       struct httpd_request *r = (struct httpd_request*) con_cls;\r
-\r
-       append_key(r, key, data, off, size);\r
-\r
-       return MHD_YES;\r
-}\r
-\r
-static int record_arg(void *cls, enum MHD_ValueKind kind, const char *key,\r
-               const char *value)\r
-{\r
-       struct httpd_request *r = (struct httpd_request*) cls;\r
-       append_key(r, key, value, 0, strlen(value));\r
-       return MHD_YES;\r
-}\r
-\r
-static int ahc_echo(void * cls, struct MHD_Connection * connection,\r
-               const char * url, const char * method, const char * version,\r
-               const char * upload_data, unsigned int * upload_data_size, void ** ptr)\r
-{\r
-       struct MHD_Response * response;\r
-       int ret;\r
-\r
-       int post = 0;\r
-\r
-       if (0 == strcmp(method, "POST"))\r
-       {\r
-               post = 1;\r
-       }\r
-       else if (0 == strcmp(method, "GET"))\r
-       {\r
-       }\r
-       else\r
-       {\r
-               return MHD_NO; /* unexpected method */\r
-       }\r
-\r
-       struct httpd_request *r;\r
-       if (*ptr == NULL)\r
-       {\r
-               /* The first time only the headers are valid,\r
-                do not respond in the first round... */\r
-\r
-               *ptr = malloc(sizeof(struct httpd_request));\r
-               if (*ptr == NULL)\r
-                       return MHD_NO;\r
-               memset(*ptr, 0, sizeof(struct httpd_request));\r
-\r
-               r = (struct httpd_request *) *ptr;\r
-\r
-               r->post = post;\r
-               Jim_SetVariableStr(interp, "httppostdata", Jim_NewDictObj(interp, NULL, 0));\r
-\r
-               /* fill in url query strings in dictonary */\r
-               MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND,\r
-                               record_arg, r);\r
-\r
-               if (r->post)\r
-               {\r
-                       r->postprocessor = MHD_create_post_processor(connection, 2048\r
-                                       * 1024, iterate_post, r);\r
-               }\r
-\r
-               return MHD_YES;\r
-       }\r
-\r
-       r = (struct httpd_request *) *ptr;\r
-\r
-       if (r->post)\r
-       {\r
-               /* consume post data */\r
-               if (*upload_data_size)\r
-               {\r
-                       MHD_post_process(r->postprocessor, upload_data, *upload_data_size);\r
-                       *upload_data_size = 0;\r
-                       return MHD_YES;\r
-               }\r
-               else\r
-               {\r
-               }\r
-       } else\r
-       {\r
-       }\r
-\r
-       /* hand over to request who will be using it. */\r
-       //      r->dict = NULL;\r
-\r
-\r
-       /* FIX!!!! we need more advanced handling of url's to avoid them\r
-        * being subverted to evil purposes\r
-        */\r
-\r
-       url++; /* skip '/' */\r
-\r
-       const char *suffix;\r
-       suffix = strrchr(url, '.');\r
-       if ((suffix != NULL) && (strcmp(suffix, ".tcl") == 0))\r
-       {\r
-               printf("Run tcl %s\n", url);\r
-\r
-               int retcode;\r
-\r
-               const char *script = alloc_printf(\r
-                               "global httpdata; source {%s}; set httpdata", url);\r
-               retcode = Jim_Eval_Named(interp, script, "httpd.c", __LINE__ );\r
-               free((void *) script);\r
-\r
-               if (retcode == JIM_ERR)\r
-               {\r
-                       printf("Tcl failed\n");\r
-                       const char *t = httpd_exec_cgi_tcl_error(interp);\r
-                       if (t == NULL)\r
-                               return MHD_NO;\r
-\r
-                       response = MHD_create_response_from_data(strlen(t), (void *) t,\r
-                                       MHD_YES, MHD_NO);\r
-                       ret = MHD_queue_response(connection,\r
-                                       MHD_HTTP_INTERNAL_SERVER_ERROR, response);\r
-                       MHD_destroy_response(response);\r
-                       return ret;\r
-               }\r
-               else\r
-               {\r
-                       printf("Tcl OK\n");\r
-                       /* FIX!!! how to handle mime types??? */\r
-                       const char *result;\r
-                       int reslen;\r
-                       result = Jim_GetString(Jim_GetResult(interp), &reslen);\r
-\r
-                       response = MHD_create_response_from_data(reslen, (void *) result,\r
-                                       MHD_NO, MHD_YES);\r
-                       ret = MHD_queue_response(connection,\r
-                                       MHD_HTTP_INTERNAL_SERVER_ERROR, response);\r
-                       MHD_destroy_response(response);\r
-                       return ret;\r
-               }\r
-       }\r
-       else\r
-       {\r
-               void *data;\r
-               int len;\r
-\r
-               int retval = loadFile(url, &data, &len);\r
-               if (retval != ERROR_OK)\r
-               {\r
-                       printf("Did not find %s\n", url);\r
-\r
-                       response = MHD_create_response_from_data(strlen(PAGE_NOT_FOUND),\r
-                                       (void *) PAGE_NOT_FOUND, MHD_NO, MHD_NO);\r
-                       ret = MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response);\r
-                       MHD_destroy_response(response);\r
-                       return ret;\r
-               }\r
-\r
-               printf("Serving %s length=%d\n", url, len);\r
-               /* serve file directly */\r
-               response = MHD_create_response_from_data(len, data, MHD_YES, MHD_NO);\r
-               MHD_add_response_header(response, "Content-Type", "image/png");\r
-\r
-               ret = MHD_queue_response(connection, MHD_HTTP_OK, response);\r
-               MHD_destroy_response(response);\r
-\r
-               //free(data);\r
-               return ret;\r
-       }\r
-}\r
-\r
-static struct MHD_Daemon * d;\r
-\r
-int httpd_start(void)\r
-{\r
-\r
-       int port = 8888;\r
-       LOG_USER("Launching httpd server on port %d", port);\r
-       d = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, port, NULL, NULL,\r
-                       &ahc_echo, NULL, /* could be data for handler, but we only have a single handler, use global variables instead */\r
-                       MHD_OPTION_NOTIFY_COMPLETED, request_completed, NULL, /* Closure... what's that??? */\r
-                       MHD_OPTION_END);\r
-       if (d == NULL)\r
-               return ERROR_FAIL;\r
-\r
-       Jim_CreateCommand(interp,\r
-                                                 "formfetch",\r
-                                                 httpd_Jim_Command_formfetch,\r
-                                                 NULL,\r
-                                                 NULL);\r
-\r
-    Jim_CreateCommand(interp,\r
-                      "writeform",\r
-                      httpd_Jim_Command_writeform,\r
-                      NULL,\r
-                      NULL);\r
-\r
-\r
-       return ERROR_OK;\r
-}\r
-\r
-void httpd_stop(void)\r
-{\r
-       MHD_stop_daemon(d);\r
-}\r
-\r
-void openocd_sleep_prelude(void)\r
-{\r
-}\r
-\r
-void openocd_sleep_postlude(void)\r
-{\r
-}\r
-\r
+/***************************************************************************
+ *   Copyright (C) 2007,2008,2009 Øyvind Harboe                            *
+ *   oyvind.harboe@zylin.com                                               *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+/* some bits were copied from ahttpd which is under eCos license and
+ * copyright to FSF
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "telnet_server.h"
+#include <target/target.h>
+
+#include <microhttpd.h>
+#include <pthread.h>
+#include <signal.h>
+
+#define PAGE_NOT_FOUND "<html><head><title > File not found</title></head><body > File not found</body></html>"
+
+static pthread_mutex_t mutex;
+
+void openocd_sleep_prelude(void)
+{
+       pthread_mutex_unlock(&mutex);
+}
+
+void openocd_sleep_postlude(void)
+{
+       pthread_mutex_lock(&mutex);
+}
+
+
+
+int loadFile(const char *name, void **data, size_t *len);
+
+static const char *appendf(const char *prev, const char *format, ...)
+{
+       va_list ap;
+       va_start(ap, format);
+       char *string = alloc_vprintf(format, ap);
+       va_end(ap);
+       char *string2 = NULL;
+
+       if (string != NULL)
+       {
+               string2 = alloc_printf("%s%s", (prev == NULL) ? "" : prev, string);
+       }
+
+       if (prev != NULL)
+       {
+               free((void *)prev);
+       }
+
+       if (string == NULL)
+               free(string);
+
+       return string2;
+}
+
+static const char *httpd_exec_cgi_tcl_error(Jim_Interp *interp)
+{
+       int len, i;
+
+       const char *t = NULL;
+       t = appendf(t, "<html><body>\n");
+
+       t = appendf(t, "Runtime error, file \"%s\", line %d:<br>",
+                       interp->errorFileName, interp->errorLine);
+       t = appendf(t, "    %s < br>", Jim_GetString(interp->result, NULL));
+       Jim_ListLength(interp, interp->stackTrace, &len);
+       for (i = 0; i < len; i += 3)
+       {
+               Jim_Obj *objPtr;
+               const char *proc, *file, *line;
+
+               Jim_ListIndex(interp, interp->stackTrace, i, &objPtr, JIM_NONE);
+               proc = Jim_GetString(objPtr, NULL);
+               Jim_ListIndex(interp, interp->stackTrace, i + 1, &objPtr, JIM_NONE);
+               file = Jim_GetString(objPtr, NULL);
+               Jim_ListIndex(interp, interp->stackTrace, i + 2, &objPtr, JIM_NONE);
+               line = Jim_GetString(objPtr, NULL);
+               t = appendf(t, "In procedure '%s' called at file \"%s\", line %s < br>",
+                               proc, file, line);
+       }
+       t = appendf(t, "</html></body>\n");
+
+       return t;
+}
+
+static int httpd_Jim_Command_writeform(Jim_Interp *interp, int argc,
+               Jim_Obj * const *argv)
+{
+       if (argc != 3)
+       {
+               Jim_WrongNumArgs(interp, 1, argv, "method ?CMD_ARGV ...?");
+               return JIM_ERR;
+       }
+       char *name = (char*) Jim_GetString(argv[1], NULL);
+       char *file = (char*) Jim_GetString(argv[2], NULL);
+
+       // Find length
+       const char *data;
+       int actual;
+       int retcode;
+       const char *script = alloc_printf(
+                       "set dummy_val $httppostdata(%s); set dummy_val",
+                       name);
+
+       retcode = Jim_Eval_Named(interp, script, __FILE__, __LINE__);
+       free((void *) script);
+       if (retcode != JIM_OK)
+               return retcode;
+
+       data = Jim_GetString(Jim_GetResult(interp), &actual);
+
+       FILE *f = fopen(file, "wb");
+       if (NULL == f)
+       {
+               Jim_SetResultString(interp, "Could not create file", -1);
+               return JIM_ERR;
+       }
+
+       int result = fwrite(data, 1, actual, f);
+       fclose(f);
+
+       if (result != actual)
+       {
+               Jim_SetResultString(interp, "Could not write to file", -1);
+               return JIM_ERR;
+       }
+       return JIM_OK;
+}
+
+
+int
+httpd_Jim_Command_formfetch(Jim_Interp *interp,
+                                   int argc,
+                                   Jim_Obj *const *argv)
+{
+       if (argc != 2)
+       {
+               Jim_WrongNumArgs(interp, 1, argv, "method ?CMD_ARGV ...?");
+               return JIM_ERR;
+       }
+
+       char *name = (char*)Jim_GetString(argv[1], NULL);
+       const char *script = alloc_printf(
+               "set dummy_val $httppostdata(%s); set dummy_val",
+               name);
+       int retcode = Jim_Eval_Named(interp, script, __FILE__, __LINE__);
+
+       free((void *) script);
+       if (retcode != JIM_OK)
+               Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
+       else
+               Jim_SetResult(interp, Jim_GetResult(interp));
+
+       return JIM_OK;
+}
+
+struct httpd_request
+{
+       int post;
+       Jim_Interp *interp;
+       struct MHD_PostProcessor *postprocessor;
+
+       //Jim_Obj *dict;
+
+       int complete; /* did we receive the entire post ? */
+
+};
+
+static void request_completed(void *cls, struct MHD_Connection *connection,
+               void **con_cls, enum MHD_RequestTerminationCode toe)
+{
+       struct httpd_request *r = (struct httpd_request*) *con_cls;
+
+       if (NULL == r)
+               return;
+
+       if (r->postprocessor)
+       {
+               openocd_sleep_postlude();
+               MHD_destroy_post_processor(r->postprocessor);
+               openocd_sleep_prelude();
+       }
+
+       free(r);
+       *con_cls = NULL;
+}
+
+/* append to said key in dictionary */
+static void append_key(Jim_Interp *interp,
+               struct httpd_request *r, const char *key,
+               const char *data, size_t off, size_t size)
+{
+       Jim_Obj *keyObj = Jim_NewStringObj(interp, key, -1);
+       Jim_IncrRefCount(keyObj);
+       Jim_Obj *value = NULL;
+
+       Jim_Obj *dict = Jim_GetVariableStr(interp, "httppostdata", 0);
+
+       if (dict != NULL)
+       {
+               if (Jim_DictKey(interp, dict, keyObj, &value, 0) != JIM_OK)
+               {
+                        value = NULL;
+               }
+               else
+               {
+                       Jim_IncrRefCount(value);
+               }
+       }
+
+       if (value == NULL)
+       {
+               value = Jim_NewStringObj(interp, "", -1);
+               Jim_IncrRefCount(value);
+
+       }
+
+       /* create a new object we append to and insert into this location */
+       Jim_Obj *newObj = Jim_NewStringObj(interp, "", -1);
+       Jim_IncrRefCount(newObj);
+       Jim_AppendObj(interp, newObj, value);
+       Jim_AppendString(interp, newObj, data, size);
+       /* uhh... use name here of dictionary */
+       dict = Jim_NewStringObj(interp, "httppostdata", -1);
+       Jim_IncrRefCount(dict);
+       Jim_SetDictKeysVector(interp, dict, &keyObj, 1, newObj);
+       Jim_DecrRefCount(interp, dict);
+       Jim_DecrRefCount(interp, value);
+       Jim_DecrRefCount(interp, newObj);
+       Jim_DecrRefCount(interp, keyObj);
+}
+
+/* append data to each key */
+static int iterate_post(void *con_cls, enum MHD_ValueKind kind,
+               const char *key, const char *filename, const char *content_type,
+               const char *transfer_encoding, const char *data, uint64_t off,
+               size_t size)
+{
+       struct httpd_request *r = (struct httpd_request*) con_cls;
+
+       append_key(r->interp, r, key, data, off, size);
+
+       return MHD_YES;
+}
+
+static int record_arg(void *cls, enum MHD_ValueKind kind, const char *key,
+               const char *value)
+{
+       struct httpd_request *r = (struct httpd_request*) cls;
+       append_key(r->interp, r, key, value, 0, strlen(value));
+       return MHD_YES;
+}
+
+
+static int handle_request(Jim_Interp *interp,
+               struct MHD_Connection * connection, const char * url)
+{
+       struct MHD_Response * response;
+
+       int ret;
+       const char *suffix;
+       suffix = strrchr(url, '.');
+       if ((suffix != NULL) && (strcmp(suffix, ".tcl") == 0))
+       {
+               printf("Run tcl %s\n", url);
+
+               int retcode;
+
+               const char *script = alloc_printf(
+                               "global httpdata; source {%s}; set httpdata", url);
+               retcode = Jim_Eval_Named(interp, script, __FILE__, __LINE__);
+               free((void *) script);
+
+               if (retcode != JIM_OK)
+               {
+                       printf("Tcl failed\n");
+                       const char *t = httpd_exec_cgi_tcl_error(interp);
+                       if (t == NULL)
+                               return MHD_NO;
+
+                       response = MHD_create_response_from_data(strlen(t), (void *) t,
+                                       MHD_YES, MHD_NO);
+                       ret = MHD_queue_response(connection,
+                                       MHD_HTTP_INTERNAL_SERVER_ERROR, response);
+                       MHD_destroy_response(response);
+                       return ret;
+               }
+               else
+               {
+                       LOG_DEBUG("Tcl OK");
+                       /* FIX!!! how to handle mime types??? */
+                       const char *result;
+                       int reslen;
+                       result = Jim_GetString(Jim_GetResult(interp), &reslen);
+
+                       response = MHD_create_response_from_data(reslen, (void *) result,
+                                       MHD_NO, MHD_YES);
+                       ret = MHD_queue_response(connection,
+                                       MHD_HTTP_INTERNAL_SERVER_ERROR, response);
+                       MHD_destroy_response(response);
+                       return ret;
+               }
+       }
+       else
+       {
+               void *data;
+               size_t len;
+
+               int retval = loadFile(url, &data, &len);
+               if (retval != ERROR_OK)
+               {
+                       printf("Did not find %s\n", url);
+
+                       response = MHD_create_response_from_data(strlen(PAGE_NOT_FOUND),
+                                       (void *) PAGE_NOT_FOUND, MHD_NO, MHD_NO);
+                       ret = MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response);
+                       MHD_destroy_response(response);
+                       return ret;
+               }
+
+               LOG_DEBUG("Serving %s length=%zu", url, len);
+               /* serve file directly */
+               response = MHD_create_response_from_data(len, data, MHD_YES, MHD_NO);
+               /* Should we expose mimetype via tcl here or just let the browser
+                  guess?
+               MHD_add_response_header(response, "Content-Type", "image/png");
+               */
+
+               ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
+               MHD_destroy_response(response);
+
+               //free(data);
+               return ret;
+       }
+}
+
+static int ahc_echo_inner(void * cls, struct MHD_Connection * connection,
+               const char * url, const char * method, const char * version,
+               const char * upload_data, size_t * upload_data_size, void ** ptr)
+{
+       Jim_Interp *interp = (Jim_Interp *)cls;
+       int post = 0;
+
+       if (0 == strcmp(method, "POST"))
+       {
+               post = 1;
+       }
+       else if (0 == strcmp(method, "GET"))
+       {
+       }
+       else
+       {
+               return MHD_NO; /* unexpected method */
+       }
+
+       struct httpd_request *r;
+       if (*ptr == NULL)
+       {
+               /* The first time only the headers are valid,
+                do not respond in the first round... */
+
+               *ptr = malloc(sizeof(struct httpd_request));
+               if (*ptr == NULL)
+                       return MHD_NO;
+               memset(*ptr, 0, sizeof(struct httpd_request));
+
+               r = (struct httpd_request *) *ptr;
+               r->interp = interp;
+               r->post = post;
+               Jim_SetVariableStr(interp, "httppostdata", Jim_NewDictObj(interp, NULL, 0));
+
+               /* fill in url query strings in dictionary */
+               MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND,
+                               record_arg, r);
+
+               if (r->post)
+               {
+                       r->postprocessor = MHD_create_post_processor(connection, 2048
+                                       * 1024, &iterate_post, r);
+               }
+
+               return MHD_YES;
+       }
+
+       r = (struct httpd_request *) *ptr;
+
+       if (r->post)
+       {
+               /* consume post data */
+               if (*upload_data_size)
+               {
+                       MHD_post_process(r->postprocessor, upload_data, *upload_data_size);
+                       *upload_data_size = 0;
+                       return MHD_YES;
+               }
+               else
+               {
+               }
+       } else
+       {
+       }
+
+       /* hand over to request who will be using it. */
+       //      r->dict = NULL;
+
+
+       /* FIX!!!! we need more advanced handling of url's to avoid them
+        * being subverted to evil purposes
+        */
+
+       const char *httpd_dir = PKGDATADIR "/httpd";
+
+       if (*url=='/')
+       {
+               url++; /* skip '/' */
+       }
+       if (!*url)
+               url="index.tcl";
+
+       const char *file_name = alloc_printf("%s/%s", httpd_dir, url);
+       int result = handle_request(interp, connection, file_name);
+       free((void *)file_name);
+       return result;
+}
+
+
+static int ahc_echo(void * cls, struct MHD_Connection * connection,
+               const char * url, const char * method, const char * version,
+               const char * upload_data, size_t * upload_data_size, void ** ptr)
+{
+       int result;
+
+       openocd_sleep_postlude();
+
+       result = ahc_echo_inner(cls, connection, url, method, version, upload_data, upload_data_size, ptr);
+
+       openocd_sleep_prelude();
+
+       return result;
+}
+
+static struct MHD_Daemon * d;
+
+static const struct command_registration httpd_command_handlers[] = {
+       {
+               .name = "formfetch",
+               .jim_handler = httpd_Jim_Command_formfetch,
+               .mode = COMMAND_EXEC,
+               .usage = "parameter_name",
+               .help = "Reads a posted form value.",
+       },
+       {
+               .name = "writeform",
+               .jim_handler = httpd_Jim_Command_writeform,
+               .mode = COMMAND_EXEC,
+               .usage = "parameter_name filename",
+               .help = "Writes a form value to a file.",
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+int httpd_start(struct command_context *cmd_ctx)
+{
+       pthread_mutexattr_t attr;
+       pthread_mutexattr_init(&attr);
+       pthread_mutex_init(&mutex, &attr);
+
+       int port = 8888;
+       LOG_USER("Launching httpd server on port %d", port);
+       d = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, port, NULL, NULL,
+                       &ahc_echo, cmd_ctx->interp,
+                       MHD_OPTION_NOTIFY_COMPLETED, request_completed, NULL, /* Closure... what's that??? */
+                       MHD_OPTION_END);
+       if (d == NULL)
+               return ERROR_FAIL;
+
+       return register_commands(cmd_ctx, NULL, httpd_command_handlers);
+}
+
+void httpd_stop(void)
+{
+       MHD_stop_daemon(d);
+       pthread_mutex_destroy(&mutex);
+}
+