From eaa4d76ce5d85ad9057bdf1583a5eb74c7443c08 Mon Sep 17 00:00:00 2001 From: Heikki Hannikainen Date: Thu, 11 Aug 2016 21:52:06 +0300 Subject: [PATCH] Provide translated strings over http --- src/Makefile.in | 2 + src/http.c | 189 ++++++++++++++++++++++++++++++++++++++++------- src/web/aprsc.js | 2 +- 3 files changed, 166 insertions(+), 27 deletions(-) diff --git a/src/Makefile.in b/src/Makefile.in index abd29b4..538e0dd 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -105,6 +105,8 @@ DATAINSTALL = install --preserve-timestamps -m $(DATAMODE) WEBFILES = \ src/web/aprsc.css src/web/aprsc.js src/web/aprsc-graph.js \ + src/web/strings-en.json \ + src/web/strings-fi.json \ src/web/index.html \ src/web/favicon.ico \ src/web/aprsc-logo4.png \ diff --git a/src/http.c b/src/http.c index 355d718..af078a9 100644 --- a/src/http.c +++ b/src/http.c @@ -19,10 +19,12 @@ #include #include #include +#include #include #include #include +#include #if 0 #ifdef HAVE_EVENT2_EVENT_H @@ -109,6 +111,9 @@ static struct http_static_t http_static_files[] = { { NULL, NULL } }; +int http_language_count = 0; +struct http_static_t **http_language_files = NULL; + /* * Content types for the required file extensions */ @@ -120,6 +125,7 @@ static struct http_static_t http_content_types[] = { { ".js", "application/x-javascript; charset=UTF-8" }, { ".jpg", "image/jpeg" }, { ".jpeg", "image/jpeg" }, + { ".json", "application/json" }, { ".png", "image/png" }, { ".gif", "image/gif" }, { NULL, NULL } @@ -525,7 +531,7 @@ static void http_counterdata(struct evhttp_request *r, const char *uri) const char *query; query = evhttp_uri_get_query(evhttp_request_get_evhttp_uri(r)); - hlog(LOG_DEBUG, "counterdata query: %s", query); + hlog(LOG_DEBUG, "http counterdata query: %s", query); json = cdata_json_string(query); if (!json) { @@ -546,43 +552,25 @@ static void http_counterdata(struct evhttp_request *r, const char *uri) * HTTP static file server */ -#define HTTP_FNAME_LEN 1024 - -static void http_route_static(struct evhttp_request *r, const char *uri) +static void http_static_file(struct evhttp_request *r, const char *fname) { - struct http_static_t *cmdp; struct stat st; - char fname[HTTP_FNAME_LEN]; - char last_modified[128]; - char *contenttype; int fd; int file_size; - char *buf; struct evkeyvalq *req_headers; const char *ims; - - for (cmdp = http_static_files; cmdp->name != NULL; cmdp++) - if (strcmp(cmdp->name, uri) == 0) - break; - - if (cmdp->name == NULL) { - hlog(LOG_DEBUG, "HTTP: 404"); - evhttp_send_error(r, HTTP_NOTFOUND, "Not found"); - return; - } - - snprintf(fname, HTTP_FNAME_LEN, "%s/%s", webdir, cmdp->filename); - - //hlog(LOG_DEBUG, "static file request %s", uri); + char last_modified[128]; + char *contenttype; + char *buf; fd = open(fname, 0, O_RDONLY); if (fd < 0) { if (errno == ENOENT) { /* don't complain about missing motd.html - it's optional. */ int level = LOG_ERR; - if (strcmp(cmdp->filename, "motd.html") == 0) + if (strcmp(fname, "motd.html") == 0) level = LOG_DEBUG; - hlog(level, "http static file '%s' not found", fname); + hlog(level, "http static file '%s' not found: 404", fname); evhttp_send_error(r, HTTP_NOTFOUND, "Not found"); return; } @@ -602,7 +590,7 @@ static void http_route_static(struct evhttp_request *r, const char *uri) http_date(last_modified, sizeof(last_modified), st.st_mtime); - contenttype = http_content_type(cmdp->filename); + contenttype = http_content_type(fname); //hlog(LOG_DEBUG, "found content-type %s", contenttype); struct evkeyvalq *headers = evhttp_request_get_output_headers(r); @@ -651,6 +639,69 @@ static void http_route_static(struct evhttp_request *r, const char *uri) hfree(buf); } + +#define HTTP_FNAME_LEN 1024 + +static void http_route_static(struct evhttp_request *r, const char *uri) +{ + struct http_static_t *cmdp; + char fname[HTTP_FNAME_LEN]; + + for (cmdp = http_static_files; cmdp->name != NULL; cmdp++) + if (strcmp(cmdp->name, uri) == 0) + break; + + if (cmdp->name == NULL) { + hlog(LOG_DEBUG, "http static file request: 404: %s", uri); + evhttp_send_error(r, HTTP_NOTFOUND, "Not found"); + return; + } + + snprintf(fname, HTTP_FNAME_LEN, "%s/%s", webdir, cmdp->filename); + + //hlog(LOG_DEBUG, "http static file request: %s", uri); + + return http_static_file(r, fname); +} + +/* + * Return translated strings in JSON + */ + +static void http_strings(struct evhttp_request *r, const char *uri) +{ + const char *query; + + query = evhttp_uri_get_query(evhttp_request_get_evhttp_uri(r)); + //hlog(LOG_DEBUG, "strings query: %s", query); + + const char *lang = NULL; + struct evkeyvalq args; + + if (evhttp_parse_query_str(query, &args) == 0) { + lang = evhttp_find_header(&args, "lang"); + //hlog(LOG_DEBUG, "lang: %s, checking against %d languages", lang, http_language_count); + + int i; + for (i = 0; i < http_language_count; i++) { + if (strcasecmp(lang, http_language_files[i]->name) == 0) { + hlog(LOG_DEBUG, "http strings query: %s: %s", lang, http_language_files[i]->filename); + evhttp_clear_headers(&args); + + return http_static_file(r, http_language_files[i]->filename); + } + } + } + + hlog(LOG_DEBUG, "http strings query: 404: %s", uri); + evhttp_send_error(r, HTTP_NOTFOUND, "Not found"); + + evhttp_clear_headers(&args); + + return; +} + + /* * HTTP request router */ @@ -679,6 +730,11 @@ static void http_router(struct evhttp_request *r, void *which_server) return; } + if (strncmp(uri, "/strings?", 9) == 0) { + http_strings(r, uri); + return; + } + http_route_static(r, uri); return; } @@ -762,6 +818,85 @@ static void http_server_free(void) } } +/* + * Scan for language string files + */ + +void lang_scan(void) +{ + hlog(LOG_DEBUG, "Scanning languages"); + + struct http_static_t **new_language_files = NULL; + int languages_loaded = 0; + +#define LANG_FNAME_PREFIX "strings-" +#define LANG_FNAME_SUFFIX ".json" + const char glob_s[] = LANG_FNAME_PREFIX "*" LANG_FNAME_SUFFIX; + int glob_l = strlen(webdir) + 1 + strlen(glob_s) + 1; + char *fullglob = hmalloc(glob_l); + snprintf(fullglob, glob_l, "%s/%s", webdir, glob_s); + + glob_t globbuf; + + int ret = glob(fullglob, GLOB_NOSORT|GLOB_ERR, NULL, &globbuf); + if (ret == 0) { + int i; + + hlog(LOG_DEBUG, "%d language files found", globbuf.gl_pathc); + + new_language_files = hmalloc(sizeof(*new_language_files) * globbuf.gl_pathc); + memset(new_language_files, 0, sizeof(*new_language_files) * globbuf.gl_pathc); + + for (i = 0; i < globbuf.gl_pathc; i++) { + hlog(LOG_DEBUG, "Language file: %s", globbuf.gl_pathv[i]); + + char *lang = NULL; + char *bp, *ep = NULL; + bp = strstr(globbuf.gl_pathv[i], LANG_FNAME_PREFIX); + if (bp) { + bp += strlen(LANG_FNAME_PREFIX); + ep = strstr(bp, LANG_FNAME_SUFFIX); + } + + if (ep) { + int langlen = ep - bp; + lang = hmalloc(langlen + 1); + strncpy(lang, bp, langlen + 1); + lang[langlen] = 0; + + new_language_files[languages_loaded] = hmalloc(sizeof(struct http_static_t)); + new_language_files[languages_loaded]->name = lang; + new_language_files[languages_loaded]->filename = hstrdup(globbuf.gl_pathv[i]); + + hlog(LOG_INFO, "Language %d installed: %s: %s", languages_loaded, lang, globbuf.gl_pathv[i]); + + languages_loaded++; + } + + } + } else { + switch (ret) { + case GLOB_NOSPACE: + hlog(LOG_ERR, "Language file search failed: Out of memory"); + break; + case GLOB_ABORTED: + hlog(LOG_ERR, "Language file search failed: Read error / %s", strerror(errno)); + break; + case GLOB_NOMATCH: + hlog(LOG_INFO, "Language file search failed: No files found"); + break; + default: + break; + } + } + + globfree(&globbuf); + hfree(fullglob); + + http_language_count = languages_loaded; + http_language_files = new_language_files; +} + /* * HTTP server thread */ @@ -792,6 +927,8 @@ void http_thread(void *asdf) /* start the http thread, which will start server threads */ hlog(LOG_INFO, "HTTP thread starting..."); + lang_scan(); + /* we allocate a worker structure to be used within the http thread * for parsing incoming packets and passing them on to the dupecheck * thread. diff --git a/src/web/aprsc.js b/src/web/aprsc.js index dcdf5e6..bb4dab4 100644 --- a/src/web/aprsc.js +++ b/src/web/aprsc.js @@ -339,7 +339,7 @@ var app = angular.module('aprsc', [ 'pascalprecht.translate', 'graph', 'ngDialog }); - $translateProvider.useUrlLoader("/strings.json"); + $translateProvider.useUrlLoader("/strings"); $translateProvider.useSanitizeValueStrategy('escape'); $translateProvider.preferredLanguage('fi'); }).