2 * This module sits directly above the HTTP layer. By the time we get here,
3 * an HTTP request has been fully received and parsed. Control is passed up
4 * to this layer to actually perform the request. We then fill in the response
5 * and pass control back down to the HTTP layer to output the response back to
8 * Copyright (c) 1996-2018 by the citadel.org team
10 * This program is open source software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 3.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
25 void do_404(struct http_transaction *h)
27 h->response_code = 404;
28 h->response_string = strdup("NOT FOUND");
29 add_response_header(h, strdup("Content-type"), strdup("text/plain"));
30 h->response_body = strdup("404 NOT FOUND\r\n");
31 h->response_body_length = strlen(h->response_body);
36 * Precondition failed (such as if-match)
38 void do_412(struct http_transaction *h)
40 h->response_code = 412;
41 h->response_string = strdup("PRECONDITION FAILED");
46 * We throw an HTTP error "502 bad gateway" when we need to connect to Citadel, but can't.
48 void do_502(struct http_transaction *h)
50 h->response_code = 502;
51 h->response_string = strdup("bad gateway");
52 add_response_header(h, strdup("Content-type"), strdup("text/plain"));
55 ("This program was unable to connect or stay connected to the Citadel server. Please report this problem to your system administrator."));
56 h->response_body_length = strlen(h->response_body);
61 * Tell the client to authenticate using HTTP-AUTH (RFC 2617)
63 void request_http_authenticate(struct http_transaction *h)
65 h->response_code = 401;
66 h->response_string = strdup("Unauthorized");
67 add_response_header(h, strdup("WWW-Authenticate"), strdup("Basic realm=\"Citadel Server\""));
72 * Easy and fun utility function to throw a redirect.
74 void http_redirect(struct http_transaction *h, char *to_where)
76 syslog(LOG_DEBUG, "Redirecting to: %s", to_where);
77 h->response_code = 302;
78 h->response_string = strdup("Moved Temporarily");
79 add_response_header(h, strdup("Location"), strdup(to_where));
80 add_response_header(h, strdup("Content-type"), strdup("text/plain"));
81 h->response_body = strdup(to_where);
82 h->response_body_length = strlen(h->response_body);
87 * perform_request() is the entry point for *every* HTTP transaction.
89 void perform_request(struct http_transaction *h)
91 struct ctdlsession *c;
93 // Determine which code path to take based on the beginning of the URI.
94 // This is implemented as a series of strncasecmp() calls rather than a
95 // lookup table in order to make the code more readable.
97 if (IsEmptyStr(h->uri)) { // Sanity check
101 // Right about here is where we should try to handle anything that doesn't start
102 // with the /ctdl/ prefix.
105 if ((!strcmp(h->uri, "/")) && (!strcasecmp(h->method, "GET"))) {
106 http_redirect(h, "/ctdl/s/index.html");
109 // Legacy URI patterns (/readnew?gotoroom=xxx&start_reading_at=yyy) ...
110 // Direct room name (/my%20blog) ...
112 // Everything below this line is strictly REST URI patterns.
114 if (strncasecmp(h->uri, HKEY("/ctdl/"))) { // Reject non-REST
119 if (!strncasecmp(h->uri, HKEY("/ctdl/s/"))) { // Static content
124 if (h->uri[7] != '/') {
128 // Anything below this line:
129 // 1. Is in the format of /ctdl/?/*
130 // 2. Requires a connection to a Citadel server.
132 c = connect_to_citadel(h);
137 // WebDAV methods like OPTIONS and PROPFIND *require* a logged-in session,
138 // even if the Citadel server allows anonymous access.
139 if (IsEmptyStr(c->auth)) {
140 if ((!strcasecmp(h->method, "OPTIONS"))
141 || (!strcasecmp(h->method, "PROPFIND"))
142 || (!strcasecmp(h->method, "REPORT"))
143 || (!strcasecmp(h->method, "DELETE"))
145 request_http_authenticate(h);
146 disconnect_from_citadel(c);
150 // Break down the URI by path and send the request to the appropriate part of the program.
153 case 'a': // /ctdl/a/ == RESTful path to admin functions
156 case 'c': // /ctdl/c/ == misc Citadel server commands
159 case 'r': // /ctdl/r/ == RESTful path to rooms
162 case 'u': // /ctdl/u/ == RESTful path to users
169 // Are we in an authenticated session? If so, set a cookie so we stay logged in.
170 if (!IsEmptyStr(c->auth)) {
171 char koekje[AUTH_MAX];
172 char *exp = http_datestring(time(NULL) + (86400 * 30));
173 snprintf(koekje, AUTH_MAX, "wcauth=%s; path=/ctdl/; Expires=%s", c->auth, exp);
175 add_response_header(h, strdup("Set-Cookie"), strdup(koekje));
177 // During development we are foiling the browser cache completely. In production we'll be more selective.
178 add_response_header(h, strdup("Cache-Control"), strdup("no-store, must-revalidate"));
179 add_response_header(h, strdup("Pragma"), strdup("no-cache"));
180 add_response_header(h, strdup("Expires"), strdup("0"));
182 // Unbind from our Citadel server connection.
183 disconnect_from_citadel(c);