diff options
Diffstat (limited to '')
-rw-r--r-- | src/httpd.c | 169 |
1 files changed, 169 insertions, 0 deletions
diff --git a/src/httpd.c b/src/httpd.c new file mode 100644 index 0000000..bf5c3d1 --- /dev/null +++ b/src/httpd.c @@ -0,0 +1,169 @@ +char * sc_queryhtml (struct sc_query * q) { /* remember to free returned string in the caller */ /* caller takes care of locking */ + size_t resultshtml_written = 0; + size_t resultshtml_sizeof = SC_ALLOC_CHUNK; + char * resultshtml = malloc(resultshtml_sizeof); + resultshtml[0] = '\0'; + for (size_t i = 0; i < q->results_length; i++) { +#define SC_HRC(string, wanted) \ + if (string##_written+wanted >= string##_sizeof) { \ + string##_sizeof = (string##_written+wanted+1)*SC_REALLOC_K; \ + string = realloc(string, string##_sizeof); \ + } +#define SC_HRF "<div class=result><h4><a href=\"%s\">%s</a></h4><p>%s</p></div>" + char * safetitle = htmlspecialchars(q->results[i]->title); + char * safebody = htmlspecialchars(q->results[i]->desc); + char * safeurl = htmlspecialchars(q->results[i]->url); + size_t ws = snprintf(NULL, 0, SC_HRF, safeurl, safetitle, safebody); + SC_HRC(resultshtml, ws); + resultshtml_written += sprintf(resultshtml+resultshtml_written, SC_HRF, safeurl, safetitle, safebody); + free(safetitle); + free(safebody); + free(safeurl); + } +#define SC_HRS SC_I18N_NUMBER_OF_RESULTS ": %ld | " SC_I18N_QUERY_TIME ": %s" + char formatted_time[128]; + struct tm tm; + localtime_r(&q->lookup_time, &tm); + strftime(formatted_time, 128, SC_I18N_DATETIME_FORMAT, &tm); + char queryinfo[256]; + snprintf(queryinfo, 256, SC_HRS, q->results_length, formatted_time); + char * safequery = htmlspecialchars(q->string); + char * response = malloc(strlen((char *) sc_hp)+2*strlen(safequery)+strlen(queryinfo)+strlen(resultshtml)); + sprintf(response, (char *) sc_hp, safequery, safequery, queryinfo, resultshtml); + free(safequery); + free(resultshtml); + return response; +} +char * sc_logshtml (struct sc_cache * c) { /* remember to free on caller, remember not to report errors here whilst locked */ + char * html = malloc(SC_ALLOC_CHUNK); + html[0] = '\0'; + size_t html_written = 0; + size_t html_sizeof = 0; + pthread_rwlock_rdlock(c->logentries_lock); + if (!c->logentries) { + free(html); + return NULL; + } + for (size_t i = 0; i < c->logentries_length; i++) { +#define SC_HLF "<div class=result id=log%lu>[<span class=%s>%s</span>] %s " \ + "<a href=\"" SC_I18N_GIT_URL "/src/branch/master/%s#L%lu\">%s()@%s:%lu</a>: %s</div>" +#define SC_HLA i, \ + sc_log_str(c->logentries[i]->type), \ + sc_log_str(c->logentries[i]->type), \ + formatted_time, \ + c->logentries[i]->file, \ + c->logentries[i]->line, \ + c->logentries[i]->function, /* compile-time burned in values are safe from xss :) */ \ + c->logentries[i]->file, \ + c->logentries[i]->line, \ + safemessage /* ... whereas this might contain < */ + struct tm tm; + char formatted_time[128]; + localtime_r(&c->logentries[i]->time, &tm); + strftime(formatted_time, 128, SC_I18N_DATETIME_FORMAT, &tm); + char * safemessage = htmlspecialchars(c->logentries[i]->message); + size_t ws = snprintf(NULL, 0, SC_HLF, SC_HLA); + SC_HRC(html, ws); + html_written += sprintf(html+html_written, SC_HLF, SC_HLA); + free(safemessage); + } + pthread_rwlock_unlock(c->logentries_lock); + return html; +} +int sc_httpd (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) { + struct sc_cache * c = (struct sc_cache *) cls; + static int dummy; + struct MHD_Response * httpd_response; + int ret; + if (0 != strcmp(method, "GET")) + return MHD_NO; /* unexpected method */ + if (&dummy != *ptr) { + /* the first time only the headers are valid, do not respond in the first round ... */ + *ptr = &dummy; + return MHD_YES; + } + if (0 != *upload_data_size) + return MHD_NO; /* upload data in a GET?! */ + *ptr = NULL; /* clear context pointer */ + char * response = NULL; + enum MHD_ResponseMemoryMode mhdrmm = MHD_RESPMEM_MUST_FREE; + const char * query = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "q"); + const char * host = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Host"); + char * location = "//git.sijanec.eu/sijanec/sear.c"; + char * content_type = "text/html"; + int status_code = MHD_HTTP_OK; + if (!host) + host = ""; + struct sc_query * q = NULL; + if (!query) { + if (url[0] == '/') + switch (url[1]) { + case 's': /* security.txt */ + case '.': /* .well-known/security.txt */ + mhdrmm = MHD_RESPMEM_PERSISTENT; + response = sc_securitytxt; + content_type = "text/plain"; + break; + case 'r': /* robots.txt */ + mhdrmm = MHD_RESPMEM_PERSISTENT; + response = sc_robotstxt; + content_type = "text/plain"; + break; + case 'o': /* osdd.xml - opensearch description document */ + response = malloc(strlen(sc_osdd)+strlen(host)); + sprintf(response, sc_osdd, host); + content_type = "application/opensearchdescription+xml"; + break; + case 'l': /* logs.html */ + { + char * logshtml = sc_logshtml(c); + response = malloc(strlen((char *) sc_hp)+strlen(SC_I18N_LOGS)+strlen(logshtml ? logshtml : SC_I18N_LOGS_ERROR)); + sprintf(response, (char *) sc_hp, "", "", SC_I18N_LOGS, logshtml ? logshtml : SC_I18N_LOGS_ERROR); + free(logshtml); + } + break; + } + if (!response) { + response = malloc(strlen((char *) sc_hp)+strlen(SC_I18N_HP_HEADING)+strlen(SC_I18N_HP_BODY)); + sprintf(response, (char *) sc_hp, "", "", SC_I18N_HP_HEADING, SC_I18N_HP_BODY); + } + } else { + int already_retried = 0; +retry: + SC_CRLE(c, c->queries_lock); + for (size_t i = 0; i < c->queries_length; i++) + if (!strcmp(c->queries[i]->string, query)) + q = c->queries[i]; + if (q) { + response = sc_queryhtml(q); /* MHD_create_response_from_buffer will free response (; */ + if (MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "f") && q->results_length > 0) { + status_code = 307; + location = q->results[0]->url; + } + SC_CUE(c, c->queries_lock); + } else { + SC_CUE(c, c->queries_lock); + sc_query_google(query, c, NULL); + if (already_retried++) { + char * safequery = htmlspecialchars(query); + response = malloc(strlen((char*) sc_hp)+strlen(safequery)*2+strlen(SC_I18N_HP_ERROR_HEADING)+strlen(SC_I18N_HP_ERROR_BODY)); + sprintf(response, (char *) sc_hp, safequery, safequery, SC_I18N_HP_ERROR_HEADING, SC_I18N_HP_ERROR_BODY); + free(safequery); + } else goto retry; + } + } + httpd_response = MHD_create_response_from_buffer (strlen(response), (void *) response, mhdrmm); + MHD_add_response_header(httpd_response, "Content-Type", content_type); + if (status_code >= 300 && status_code <= 399) + MHD_add_response_header(httpd_response, "Location", location); + ret = MHD_queue_response(connection, status_code, httpd_response); + MHD_destroy_response(httpd_response); + return ret; +} |