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) { /* 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 "
"
#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);
}
#define SC_HRS SC_I18N_NUMBER_OF_RESULTS ": %zu | " 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)+strlen(add_form));
sprintf(response, (char *) sc_hp, safequery, safequery, add_form, queryinfo, resultshtml);
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 ""
#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");
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")); /* MHD_create_response_from_buffer will free response (; */
SC_CUE(c, c->queries_lock);
} else {
SC_CUE(c, c->queries_lock);
enum sc_return r = sc_query_google(query, c, NULL, opt);
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&q=", getenv("SC_FALLBACK"),
atoi(l ? l : ""));
urlencode(location+strlen(location), query);
if (opt & SC_OPT_IMAGE)
strcat(location, "&i=i");
if (h)
strcat(location, "&h=h");
if (f)
strcat(location, "&f=f");
}
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;
}
}
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;
}