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. It runs great on the
11 // Linux operating system (and probably elsewhere). You can use,
12 // copy, and run it under the terms of the GNU General Public
15 // This program is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
26 void do_404(struct http_transaction *h)
28 h->response_code = 404;
29 h->response_string = strdup("NOT FOUND");
30 add_response_header(h, strdup("Content-type"), strdup("text/plain"));
31 h->response_body = strdup("404 NOT FOUND\r\n");
32 h->response_body_length = strlen(h->response_body);
37 * Precondition failed (such as if-match)
39 void do_412(struct http_transaction *h)
41 h->response_code = 412;
42 h->response_string = strdup("PRECONDITION FAILED");
47 * We throw an HTTP error "502 bad gateway" when we need to connect to Citadel, but can't.
49 void do_502(struct http_transaction *h)
51 h->response_code = 502;
52 h->response_string = strdup("bad gateway");
53 add_response_header(h, strdup("Content-type"), strdup("text/plain"));
56 ("This program was unable to connect or stay connected to the Citadel server. Please report this problem to your system administrator."));
57 h->response_body_length = strlen(h->response_body);
62 * Tell the client to authenticate using HTTP-AUTH (RFC 2617)
64 void request_http_authenticate(struct http_transaction *h)
66 h->response_code = 401;
67 h->response_string = strdup("Unauthorized");
68 add_response_header(h, strdup("WWW-Authenticate"), strdup("Basic realm=\"Citadel Server\""));
73 * Easy and fun utility function to throw a redirect.
75 void http_redirect(struct http_transaction *h, char *to_where)
77 syslog(LOG_DEBUG, "Redirecting to: %s", to_where);
78 h->response_code = 302;
79 h->response_string = strdup("Moved Temporarily");
80 add_response_header(h, strdup("Location"), strdup(to_where));
81 add_response_header(h, strdup("Content-type"), strdup("text/plain"));
82 h->response_body = strdup(to_where);
83 h->response_body_length = strlen(h->response_body);
88 * perform_request() is the entry point for *every* HTTP transaction.
90 void perform_request(struct http_transaction *h)
92 struct ctdlsession *c;
94 // Determine which code path to take based on the beginning of the URI.
95 // This is implemented as a series of strncasecmp() calls rather than a
96 // lookup table in order to make the code more readable.
98 if (IsEmptyStr(h->uri)) { // Sanity check
102 // Right about here is where we should try to handle anything that doesn't start
103 // with the /ctdl/ prefix.
106 if ((!strcmp(h->uri, "/")) && (!strcasecmp(h->method, "GET"))) {
107 http_redirect(h, "/ctdl/s/index.html");
110 // Legacy URI patterns (/readnew?gotoroom=xxx&start_reading_at=yyy) ...
111 // Direct room name (/my%20blog) ...
113 // Everything below this line is strictly REST URI patterns.
115 if (strncasecmp(h->uri, HKEY("/ctdl/"))) { // Reject non-REST
120 if (!strncasecmp(h->uri, HKEY("/ctdl/s/"))) { // Static content
125 if (h->uri[7] != '/') {
129 // Anything below this line:
130 // 1. Is in the format of /ctdl/?/*
131 // 2. Requires a connection to a Citadel server.
133 c = connect_to_citadel(h);
138 // WebDAV methods like OPTIONS and PROPFIND *require* a logged-in session,
139 // even if the Citadel server allows anonymous access.
140 if (IsEmptyStr(c->auth)) {
141 if ((!strcasecmp(h->method, "OPTIONS"))
142 || (!strcasecmp(h->method, "PROPFIND"))
143 || (!strcasecmp(h->method, "REPORT"))
144 || (!strcasecmp(h->method, "DELETE"))
146 request_http_authenticate(h);
147 disconnect_from_citadel(c);
151 // Break down the URI by path and send the request to the appropriate part of the program.
154 case 'a': // /ctdl/a/ == RESTful path to admin functions
157 case 'c': // /ctdl/c/ == misc Citadel server commands
160 case 'r': // /ctdl/r/ == RESTful path to rooms
163 case 'u': // /ctdl/u/ == RESTful path to users
170 // Are we in an authenticated session? If so, set a cookie so we stay logged in.
171 if (!IsEmptyStr(c->auth)) {
172 char koekje[AUTH_MAX];
173 char *exp = http_datestring(time(NULL) + (86400 * 30));
174 snprintf(koekje, AUTH_MAX, "wcauth=%s; path=/ctdl/; Expires=%s", c->auth, exp);
176 add_response_header(h, strdup("Set-Cookie"), strdup(koekje));
178 // During development we are foiling the browser cache completely. In production we'll be more selective.
179 add_response_header(h, strdup("Cache-Control"), strdup("no-store, must-revalidate"));
180 add_response_header(h, strdup("Pragma"), strdup("no-cache"));
181 add_response_header(h, strdup("Expires"), strdup("0"));
183 // Unbind from our Citadel server connection.
184 disconnect_from_citadel(c);