summaryrefslogblamecommitdiffstats
path: root/src/httpd.c
blob: c200fc60121c8fb5c5ffe652db60fbdbaab264a1 (plain) (tree)
1
2
3
4
5
6
7
8
9
10




                                               
                                                                                                                                                                                          



                                                        
                                                                         




                                                                                     
                                                                                          
                                                              
                                                                                                
                                                  
                                                                                                                                                               

                                                                                                                                 

                                                                        
                                                                                      









                                                                                                        
                                      



                                



                                                                    































                                                                                                                                                              
                                                       

                                                                                                                                    


                            



                          
                










                                                                                                                             

                                                                                                     






















                                                                                                      

                                     



























                                                                                                                                           
                                      

                                                                                

                          
                                         


                                                                                             
                                                                                             

                                                                                             



                                                                                                                                        
                                














                                                                                          

                                                                                          

                                                                                               




                                                                        


                                                                        
                                                                     

                                                                      

                                                         
                
                                                                                 

                                                                                                                                                                                 
                                                               



                                                                                                                                                                                                  



                                              

                                                                                                                                      


                                        



                                                       

                                            
                     
                                                              
                                                                            
                                                  



                                                                                                                        
                        


                                                                                                
                                                                      
                                                  





                                                                                
                              
                                                                                                                                                       


                                                   


















                                                                                                                                                                                






                                                                                               
                                                                                                                                                                                                                       
                                                                                            





















                                                                                                        


                                          
                 
                                                                                                                                     






                                                                              
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 "<div class=result id=result%zu><h4><a href=\"%s\" accesskey=%zu>%s</a> " \
	"<span class=breadcrumb>%s</span></h4><p>%s</p></div>"
#define SC_HIF "<a class=result id=result%zu href=\"%s\" accesskey=%zu><img data-title=\"%s\"" \
	"data-breadcrumb=\"%s\" src=\"%s\" /></a>"
#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 <a href='%s'>%s</a>", 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 "<div class=result id=log%zu>[<span class=%s>%s</span>] %s " \
		"<a href=\"" SC_I18N_GIT_URL "/src/branch/master/%s#L%zu\">%s()@%s:%zu</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;
}
#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 ? "<input type=hidden name=h value=h />" : "",
		l ? "<input type=hidden name=l value=" : "<!-- Odgovor na dokončno vprašanje o Življenju, Vesolju in sploh Vsem je ",
		l ? atoi(l) : 42,
		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, "<meta http-equiv=refresh content='0;URL=%s'><a id=a href='%s'>%s</a><script>a.click();</script>", 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;
}