indent -kr -i8 -l132 on everything in webcit-ng
[citadel.git] / webcit-ng / http.c
1 /*
2  * This module handles HTTP transactions.
3  *
4  * Copyright (c) 1996-2018 by the citadel.org team
5  *
6  * This program is open source software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License version 3.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  */
14
15 #include "webcit.h"
16
17 /*
18  * Write data to the HTTP client.  Encrypt if necessary.
19  */
20 int client_write(struct client_handle *ch, char *buf, int nbytes)
21 {
22         if (is_https) {
23                 return client_write_ssl(ch, buf, nbytes);
24         } else {
25                 return write(ch->sock, buf, nbytes);
26         }
27 }
28
29
30 /*
31  * Read data from the HTTP client.  Decrypt if necessary.
32  * Returns number of bytes read, or -1 to indicate an error.
33  */
34 int client_read(struct client_handle *ch, char *buf, int nbytes)
35 {
36         if (is_https) {
37                 return client_read_ssl(ch, buf, nbytes);
38         } else {
39                 int bytes_received = 0;
40                 int bytes_this_block = 0;
41                 while (bytes_received < nbytes) {
42                         bytes_this_block = read(ch->sock, &buf[bytes_received], nbytes - bytes_received);
43                         if (bytes_this_block < 1) {
44                                 return (-1);
45                         }
46                         bytes_received += bytes_this_block;
47                 }
48                 return (nbytes);
49         }
50 }
51
52
53 /*
54  * Read a newline-terminated line of text from the client.
55  * Implemented in terms of client_read() and is therefore transparent...
56  * Returns the string length or -1 for error.
57  */
58 int client_readline(struct client_handle *ch, char *buf, int maxbytes)
59 {
60         int len = 0;
61         int c = 0;
62
63         if (buf == NULL)
64                 return (-1);
65
66         while (len < maxbytes) {
67                 c = client_read(ch, &buf[len], 1);
68                 if (c <= 0) {
69                         syslog(LOG_DEBUG, "Socket error or zero-length read");
70                         return (-1);
71                 }
72                 if (buf[len] == '\n') {
73                         if ((len > 0) && (buf[len - 1] == '\r')) {
74                                 --len;
75                         }
76                         buf[len] = 0;
77                         return (len);
78                 }
79                 ++len;
80         }
81         return (len);
82 }
83
84
85 /*
86  * printf() type function to send data to the web client.
87  */
88 void client_printf(struct client_handle *ch, const char *format, ...)
89 {
90         va_list arg_ptr;
91         StrBuf *Buf = NewStrBuf();
92
93         va_start(arg_ptr, format);
94         StrBufVAppendPrintf(Buf, format, arg_ptr);
95         va_end(arg_ptr);
96
97         client_write(ch, (char *) ChrPtr(Buf), StrLength(Buf));
98         FreeStrBuf(&Buf);
99 }
100
101
102 /*
103  * Push one new header into the response of an HTTP request.
104  * When completed, the HTTP transaction now owns the memory allocated for key and val.
105  */
106 void add_response_header(struct http_transaction *h, char *key, char *val)
107 {
108         struct http_header *new_response_header = malloc(sizeof(struct http_header));
109         new_response_header->key = key;
110         new_response_header->val = val;
111         new_response_header->next = h->response_headers;
112         h->response_headers = new_response_header;
113 }
114
115
116 /*
117  * Entry point for this layer.  Socket is connected.  If running as an HTTPS
118  * server, SSL has already been negotiated.  Now perform one transaction.
119  */
120 void perform_one_http_transaction(struct client_handle *ch)
121 {
122         char buf[1024];
123         int len;
124         int lines_read = 0;
125         struct http_transaction h;
126         char *c, *d;
127         struct sockaddr_in sin;
128         struct http_header *clh;                // general purpose iterator variable
129
130         memset(&h, 0, sizeof h);
131
132         while (len = client_readline(ch, buf, sizeof buf), (len > 0)) {
133                 ++lines_read;
134
135                 if (lines_read == 1) {          // First line is method and URI.
136                         c = strchr(buf, ' ');   // IGnore the HTTP-version.
137                         if (c == NULL) {
138                                 h.method = strdup("NULL");
139                                 h.uri = strdup("/");
140                         } else {
141                                 *c = 0;
142                                 h.method = strdup(buf);
143                                 ++c;
144                                 d = c;
145                                 c = strchr(d, ' ');
146                                 if (c != NULL) {
147                                         *c = 0;
148                                 }
149                                 ++c;
150                                 h.uri = strdup(d);
151                         }
152                 } else {                        // Subsequent lines are headers.
153                         c = strchr(buf, ':');   // Header line folding is obsolete so we don't support it.
154                         if (c != NULL) {
155                                 struct http_header *new_request_header = malloc(sizeof(struct http_header));
156                                 *c = 0;
157                                 new_request_header->key = strdup(buf);
158                                 ++c;
159                                 new_request_header->val = strdup(c);
160                                 striplt(new_request_header->key);
161                                 striplt(new_request_header->val);
162                                 new_request_header->next = h.request_headers;
163                                 h.request_headers = new_request_header;
164 #ifdef DEBUG_HTTP
165                                 syslog(LOG_DEBUG, "{ %s: %s", new_request_header->key, new_request_header->val);
166 #endif
167                         }
168                 }
169
170         }
171
172         // build up the site prefix, such as https://foo.bar.com:4343
173
174         h.site_prefix = malloc(256);
175         strcpy(h.site_prefix, (is_https ? "https://" : "http://"));
176         char *hostheader = header_val(&h, "Host");
177         if (hostheader) {
178                 strcat(h.site_prefix, hostheader);
179         } else {
180                 strcat(h.site_prefix, "127.0.0.1");
181         }
182         socklen_t llen = sizeof(sin);
183         if (getsockname(ch->sock, (struct sockaddr *) &sin, &llen) >= 0) {
184                 sprintf(&h.site_prefix[strlen(h.site_prefix)], ":%d", ntohs(sin.sin_port));
185         }
186         // Now try to read in the request body (if one is present)
187
188         if (len < 0) {
189                 syslog(LOG_DEBUG, "Client disconnected");
190         } else {
191                 syslog(LOG_DEBUG, "< %s %s", h.method, h.uri);
192
193                 // If there is a request body, read it now.
194                 char *ccl = header_val(&h, "Content-Length");
195                 if (ccl) {
196                         h.request_body_length = atol(ccl);
197                 }
198                 if (h.request_body_length > 0) {
199                         syslog(LOG_DEBUG, "Reading request body (%ld bytes)", h.request_body_length);
200                         h.request_body = malloc(h.request_body_length);
201                         client_read(ch, h.request_body, h.request_body_length);
202
203                         //write(2, HKEY("\033[31m"));
204                         //write(2, h.request_body, h.request_body_length);
205                         //write(2, HKEY("\033[0m\n"));
206
207                 }
208                 // Now pass control up to the next layer to perform the request.
209                 perform_request(&h);
210
211                 // Output the results back to the client.
212                 //write(2, HKEY("\033[32m"));
213                 //write(2, h.response_body, h.response_body_length);
214                 //write(2, HKEY("\033[0m\n"));
215
216                 syslog(LOG_DEBUG, "> %03d %s", h.response_code, h.response_string);
217                 client_printf(ch, "HTTP/1.1 %03d %s\r\n", h.response_code, h.response_string);
218                 client_printf(ch, "Connection: close\r\n");
219                 client_printf(ch, "Content-Length: %ld\r\n", h.response_body_length);
220                 char *datestring = http_datestring(time(NULL));
221                 if (datestring) {
222                         client_printf(ch, "Date: %s\r\n", datestring);
223                         free(datestring);
224                 }
225
226                 client_printf(ch, "Content-encoding: identity\r\n");    // change if we gzip/deflate
227                 for (clh = h.response_headers; clh != NULL; clh = clh->next) {
228 #ifdef DEBUG_HTTP
229                         syslog(LOG_DEBUG, "} %s: %s", clh->key, clh->val);
230 #endif
231                         client_printf(ch, "%s: %s\r\n", clh->key, clh->val);
232                 }
233                 client_printf(ch, "\r\n");
234                 if ((h.response_body_length > 0) && (h.response_body != NULL)) {
235                         client_write(ch, h.response_body, h.response_body_length);
236                 }
237         }
238
239         // free the transaction memory
240         while (h.request_headers) {
241                 if (h.request_headers->key)
242                         free(h.request_headers->key);
243                 if (h.request_headers->val)
244                         free(h.request_headers->val);
245                 clh = h.request_headers->next;
246                 free(h.request_headers);
247                 h.request_headers = clh;
248         }
249         while (h.response_headers) {
250                 if (h.response_headers->key)
251                         free(h.response_headers->key);
252                 if (h.response_headers->val)
253                         free(h.response_headers->val);
254                 clh = h.response_headers->next;
255                 free(h.response_headers);
256                 h.response_headers = clh;
257         }
258         if (h.method)
259                 free(h.method);
260         if (h.uri)
261                 free(h.uri);
262         if (h.request_body)
263                 free(h.request_body);
264         if (h.response_string)
265                 free(h.response_string);
266         if (h.site_prefix)
267                 free(h.site_prefix);
268 }
269
270
271 /*
272  * Utility function to fetch a specific header from a completely read-in request.
273  * Returns the value of the requested header, or NULL if the specified header was not sent.
274  * The caller does NOT own the memory of the returned pointer, but can count on the pointer
275  * to still be valid through the end of the transaction.
276  */
277 char *header_val(struct http_transaction *h, char *requested_header)
278 {
279         struct http_header *clh;        // general purpose iterator variable
280         for (clh = h->request_headers; clh != NULL; clh = clh->next) {
281                 if (!strcasecmp(clh->key, requested_header)) {
282                         return (clh->val);
283                 }
284         }
285         return (NULL);
286 }