char * sc_https2http (char * i) { if (i && strlen(i) >= 4 && i[4] == 's') memmove(i+4, i+5, strlen(i)-3); return i; } char * sc_queryhtml (const struct sc_query * q, const char * add_form, size_t l, const char * r) { /* remember to free returned string in the caller */ /* caller takes care of freeing */ 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 && (!l || i < l); 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 "

%s " \ "%s

%s

" #define SC_HIF "" #define SC_HRA i, safeurl ? strstr(add_form, "name=h") ? sc_https2http(safeurl) : safeurl : SC_I18N_NO_HREFLINK, i, safetitle ? safetitle : SC_I18N_NO_TITLE, \ safebreadcrumbs ? safebreadcrumbs : safeurl ? safeurl : SC_I18N_NO_HREFLINK, safebody ? safebody : SC_I18N_NO_DESCRIPTION char * safetitle = htmlspecialchars(q->results[i]->title); /* htmlspecialchars returns NULL if input is null */ char * safebody = htmlspecialchars(q->results[i]->desc); char * safeurl = htmlspecialchars(q->results[i]->url); char * safebreadcrumbs = htmlspecialchars(q->results[i]->breadcrumbs); size_t ws; if (q->opt & SC_OPT_IMAGE) { ws = snprintf(NULL, 0, SC_HIF, SC_HRA); SC_HRC(resultshtml, ws); resultshtml_written += sprintf(resultshtml+resultshtml_written, SC_HIF, SC_HRA); } else { ws = snprintf(NULL, 0, SC_HRF, SC_HRA); SC_HRC(resultshtml, ws); resultshtml_written += sprintf(resultshtml+resultshtml_written, SC_HRF, SC_HRA); } free(safebreadcrumbs); free(safetitle); free(safebody); free(safeurl); } char formatted_time[128]; struct tm tm; localtime_r(&q->lookup_time, &tm); strftime(formatted_time, 128, SC_I18N_DATETIME_FORMAT, &tm); char * safesuggested = NULL; if (q->suggested && strlen(q->suggested) < 4096) { safesuggested = alloca(strlen(q->suggested)*3+256); strcpy(safesuggested, "?q="); urlencode(safesuggested+3, q->suggested); } if (!q->suggested && r && strlen(r) < 4096) { safesuggested = alloca(strlen(r)*3+256); strcpy(safesuggested, "?q="); urlencode(safesuggested+3, r); } char * htmlsuggested = htmlspecialchars(q->suggested); if (!htmlsuggested) htmlsuggested = htmlspecialchars(r); if (safesuggested) { if (strstr(add_form, "name=h")) strcat(safesuggested, "&h=h"); if (strstr(add_form, "name=l")) sprintf(safesuggested+strlen(safesuggested), "&l=%d", atoi(strstr(add_form, "name=l")+8)); if (strstr(add_form, "name=h")) strcat(safesuggested, "&h=h"); if (strstr(add_form, "name=e") || r) strcat(safesuggested, "&e=e"); } char * suggested = NULL; if (htmlsuggested && safesuggested) suggested = malloc(1+strlen(SC_I18N_DID_YOU_REALLY_MEAN)+strlen(SC_I18N_DID_YOU_MEAN)+strlen(safesuggested)+strlen(htmlsuggested)); if (suggested) sprintf(suggested, "%s %s", q->suggested ? SC_I18N_DID_YOU_MEAN : SC_I18N_DID_YOU_REALLY_MEAN, safesuggested, htmlsuggested); char * queryinfo = malloc(256+strlen(suggested ? suggested : "")); snprintf(queryinfo, 256, "%s%s" SC_I18N_NUMBER_OF_RESULTS ": %zu | " SC_I18N_QUERY_TIME ": %s" , suggested ? suggested : "", suggested ? " | " : "", q->results_length, formatted_time); char * safequery = htmlspecialchars(q->string); char * response = malloc(strlen((char *) sc_hp)+2*strlen(safequery)+strlen(queryinfo)+strlen(resultshtml)+strlen(add_form)); sprintf(response, (char *) sc_hp, safequery, safequery, add_form, queryinfo, resultshtml); free(queryinfo); free(suggested); free(htmlsuggested); free(safequery); free(resultshtml); return response; } #ifdef SC_LOGMEM 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 "
[%s] %s " \ "%s()@%s:%zu: %s
" #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; } #endif enum MHD_Result 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; SC_OPT_TYPE opt = SC_OPT_INIT; if (MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "i")) opt |= SC_OPT_IMAGE; if (!host) host = ""; const struct sc_query * q = NULL; char add_form[128]; const char * l = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "l"); const char * h = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "h"); const char * f = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "f"); const char * e = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "e"); const char * r = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "r"); snprintf(add_form, 128, "%s%s%d%s", h ? "" : "", l ? "" : " -->"); size_t response_len = 0; 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)*3); sprintf(response, sc_osdd, host, host, host); content_type = "application/opensearchdescription+xml"; break; case 'c': /* css.css - stylesheets */ mhdrmm = MHD_RESPMEM_PERSISTENT; response = sc_css; content_type = "text/css"; break; case 'f': /* favicon.ico */ mhdrmm = MHD_RESPMEM_PERSISTENT; response = (char *) sc_ico; response_len = sizeof sc_ico; content_type = "image/x-icon"; break; case 'l': /* logs.html */ { #ifdef SC_LOGMEM char * logshtml = sc_logshtml(c); response = malloc(strlen((char *) sc_hp)+strlen(SC_I18N_LOGS)+strlen(logshtml ? logshtml : SC_I18N_LOGS_ERROR)+strlen(add_form)); sprintf(response, (char *) sc_hp, "", "", add_form, SC_I18N_LOGS, logshtml ? logshtml : SC_I18N_LOGS_ERROR); free(logshtml); #else response = malloc(strlen((char *) sc_hp)+strlen(SC_I18N_LOGS_NOT_ENABLED)+strlen(SC_I18N_HP_ERROR_HEADING)+strlen(SC_I18N_LOGS)+strlen(add_form)); sprintf(response, (char *) sc_hp, SC_I18N_HP_ERROR_HEADING, "", add_form, SC_I18N_LOGS, SC_I18N_LOGS_NOT_ENABLED); #endif } break; } if (!response) { response = malloc(strlen((char *) sc_hp)+strlen(SC_I18N_HP_HEADING)+strlen(SC_I18N_HP_BODY)+strlen(add_form)); sprintf(response, (char *) sc_hp, "", "", add_form, SC_I18N_HP_HEADING, SC_I18N_HP_BODY); } } else { int already_retried = 0; const struct sc_query query_to_find = { .string = (char *) query, .opt = opt }; retry: SC_CRLE(c, c->queries_lock); #ifdef SC_OLD_STORAGE for (size_t i = 0; i < c->queries_length; i++) if (!sc_query_compar(c->queries[i], &query_to_find)) q = c->queries[i]; #else /* tfind(3) also requires a pointer to the variable that holds rootp! */ const struct sc_query ** i_am_retarded = tfind(&query_to_find, &c->qrp, SC_COMPAR_CAST sc_query_compar); q = i_am_retarded ? *i_am_retarded : NULL; #endif if (q) { if (f && q->results_length > 0) { mhdrmm = MHD_RESPMEM_PERSISTENT;/* no need to gen HTML */ content_type = "text/plain"; /* we have a feeling of luck! */ response = SC_I18N_HORSESHOE_RESPONSE; status_code = 307; #define LOCHORSE (q->results[0]->url ? q->results[0]->url : SC_I18N_NO_HREFLINK) char * out = alloca(strlen(LOCHORSE)+1); strcpy(out, LOCHORSE); if (h) sc_https2http(LOCHORSE); location = out; } else response = sc_queryhtml(q, add_form, atoi(l ? l : "0"), r); /* MHD_create_response_from_buffer will free response (; */ SC_CUE(c, c->queries_lock); } else { SC_CUE(c, c->queries_lock); char * redirect = NULL; enum sc_return r = sc_query_google(query, c, NULL, e ? NULL : &redirect, opt); if (redirect && strlen(query) < 4096 && strlen(redirect) < 4096) { status_code = 307; location = alloca(256+strlen(query)*3+strlen(redirect)*3); sprintf(location, "?l=%d%s%s%s%s&q=", atoi(l ? l : ""), (opt & SC_OPT_IMAGE) ? "&i=i" : "", h ? "&h=h" : "", f ? "&f=f" : "", e ? "&e=e" : ""); urlencode(location+strlen(location), redirect); free(redirect); redirect = NULL; strcat(location, "&r="); urlencode(location+strlen(location), query); content_type = "text/plain"; char * safeurl = htmlspecialchars(location); free(response); response = malloc(strlen(safeurl)*3+512); sprintf(response, "%s", safeurl, safeurl, safeurl); free(safeurl); goto sendresp; } if (already_retried++ || r == SC_CAPTCHA) { status_code = 570+ABS(r); if (r == SC_CAPTCHA && strlen(query) < 4096) { if (getenv("SC_FALLBACK")) { status_code = 307; location = alloca(strlen(getenv("SC_FALLBACK")) + 256 + strlen(query)*3); sprintf(location, "%sl=%d%s%s%s%s&q=", getenv("SC_FALLBACK"), atoi(l ? l : ""), (opt & SC_OPT_IMAGE) ? "&i=i" : "", h ? "&h=h" : "", f ? "&f=f" : "", e ? "&e=e" : ""); urlencode(location+strlen(location), query); } char * safequery = htmlspecialchars(query); response = malloc(strlen((char*) sc_hp) + strlen(safequery) * 2 + strlen(SC_I18N_HP_CAPTCHA_HEADING) + strlen(SC_I18N_HP_CAPTCHA_BODY) + strlen(add_form)); sprintf(response, (char *) sc_hp, safequery, safequery, add_form, SC_I18N_HP_CAPTCHA_HEADING, SC_I18N_HP_CAPTCHA_BODY); free(safequery); } else { 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) + strlen(add_form)); sprintf(response, (char *) sc_hp, safequery, safequery, add_form, SC_I18N_HP_ERROR_HEADING, SC_I18N_HP_ERROR_BODY); free(safequery); } } else goto retry; } } sendresp: httpd_response = MHD_create_response_from_buffer (response_len ? response_len : 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; }