// Copyright 2006-2008 Deutsches Forschungszentrum fuer Kuenstliche Intelligenz // or its licensors, as applicable. // // You may not use this file except under the terms of the accompanying license. // // Licensed under the Apache License, Version 2.0 (the "License"); you // may not use this file except in compliance with the License. You may // obtain a copy of the License at http: www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Project: // File: settings-server.h // Purpose: remotely editable hashtable // Responsible: mezhirov // Reviewer: // Primary Repository: // Web Sites: www.iupr.org, www.dfki.de #if HAVE_MICROHTTPD #include #endif #include "colib.h" #include "ocr-utils.h" #include "settings-server.h" using namespace colib; using namespace ocropus; namespace { /// strictly speaking, that should be a union but I didn't bother. struct NumberOrString { bool is_number; strbuf string; double number; NumberOrString() : is_number(false) {} }; #if HAVE_MICROHTTPD static int on_accept(void *cls, MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, unsigned int *upload_data_size, void **ptr); struct pstrbuf_and_phash { strbuf *s; strhash *hash; }; static int append_key_value_pairs(void *cls, MHD_ValueKind kind, const char *key, const char *value) { pstrbuf_and_phash *pp = (pstrbuf_and_phash *) cls; strbuf &s = *pp->s; strhash &hash = *pp->hash; s += key; s += "="; NumberOrString &n = hash(key); if(n.is_number) { char buf[50]; snprintf(buf, sizeof(buf), "%g", n.number); s += buf; } else { if(n.string) s += n.string; } s += "\n"; return MHD_YES; } static int set_key_value_pairs(void *cls, MHD_ValueKind kind, const char *key, const char *value) { if(!strcmp(value, "")) return MHD_YES; // we don't set to empty strings strhash &hash = * (strhash *) cls; NumberOrString &n = hash(key); if(n.is_number) n.number = strtod(value, NULL); else n.string = value; return MHD_YES; } #endif struct SettingsServer : IComponent { strhash hash; #if HAVE_MICROHTTPD MHD_Daemon *mhd; #endif virtual const char *description() { return "settings server"; } /// Set a string property or throw an exception if not implemented. virtual void set(const char *key, const char *value) { NumberOrString &n = hash(key); n.is_number = false; n.string = value; } /// Set a number property or throw an exception if not implemented. virtual void set(const char *key, double value) { NumberOrString &n = hash(key); n.is_number = true; n.number = value; } /// Get a string property or throw an exception if not implemented. virtual const char *gets(const char *key) { NumberOrString &n = hash(key); if(n.is_number) throw_fmt("server settings: %s should be a string", key); return n.string; } /// Get a number property or throw an exception if not implemented. virtual double getd(const char *key) { NumberOrString &n = hash(key); if(!n.is_number) throw_fmt("server settings: %s should be a number", key); return n.number; } #if HAVE_MICROHTTPD int on_accept(MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, unsigned int *upload_data_size, void **ptr) { MHD_Response *response; if (strcmp(method, "GET")) return MHD_NO; /* unexpected method */ static int dummy; if (&dummy != *ptr) { /* The first time only the headers are valid, do not respond in the first round... */ // HUH??? *ptr = &dummy; return MHD_YES; } *ptr = NULL; /* clear context pointer */ strbuf s; s = ""; MHD_get_connection_values(connection, MHD_POSTDATA_KIND, &set_key_value_pairs, &hash); MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, set_key_value_pairs, &hash); pstrbuf_and_phash pp; pp.hash = &hash; pp.s = &s; MHD_get_connection_values(connection, MHD_POSTDATA_KIND, &append_key_value_pairs, &pp); MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, &append_key_value_pairs, &pp); response = MHD_create_response_from_data(s.length(), (void*) (const char *) s, /* must free: */ MHD_NO, /* must copy: */ MHD_YES); int result = MHD_queue_response(connection, MHD_HTTP_OK, response); MHD_destroy_response(response); return result; } SettingsServer(int port) { mhd = MHD_start_daemon(/* flags: */ MHD_USE_THREAD_PER_CONNECTION, /* port: */ port, /* accept policy callback: */ NULL, /* accept policy 1st arg: */ NULL, /* accept handler callback: */ &::on_accept, /* accept handler 1st arg: */ this, /* list of options: */ MHD_OPTION_END); } virtual ~SettingsServer() { MHD_stop_daemon(mhd); } #else SettingsServer(int port) { } virtual ~SettingsServer() { } #endif }; #if HAVE_MICROHTTPD static int on_accept(void *cls, MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, unsigned int *upload_data_size, void **ptr) { return ((SettingsServer *) cls)->on_accept(connection, url, method, version, upload_data, upload_data_size, ptr); } #endif } namespace ocropus { IComponent *make_SettingsServer(int port) { return new SettingsServer(port); } }