1 /* Pseudothread server example.
2 * - by Richard W.M. Jones <rich@annexia.org>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library 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 GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the Free
16 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 * $Id: pthr_eg2_server.c,v 1.7 2003/02/05 22:13:32 rich Exp $
33 #ifdef HAVE_SYS_STAT_H
45 #ifdef HAVE_SYS_SYSLIMITS_H
46 #include <sys/syslimits.h>
56 #include "src/pthr_pseudothread.h"
57 #include "src/pthr_iolib.h"
58 #include "src/pthr_http.h"
59 #include "src/pthr_cgi.h"
60 #include "pthr_eg2_server.h"
62 struct eg2_server_processor
64 /* Pseudothread handle. */
70 /* Pool for memory allocations. */
74 http_request http_request;
80 static int file_not_found_error (eg2_server_processor p);
81 static int moved_permanently (eg2_server_processor p, const char *);
82 static int serve_directory (eg2_server_processor p, const char *, const struct stat *);
83 static int serve_file (eg2_server_processor p, const char *, const struct stat *);
84 static void run (void *vp);
87 new_eg2_server_processor (int sock)
90 eg2_server_processor p;
93 p = pmalloc (pool, sizeof *p);
97 p->pth = new_pseudothread (pool, run, p, "eg2_server_processor");
107 eg2_server_processor p = (eg2_server_processor) vp;
112 p->io = io_fdopen (p->sock);
114 /* Sit in a loop reading HTTP requests. */
117 /* ----- HTTP request ----- */
118 p->http_request = new_http_request (p->pool, p->io);
119 if (p->http_request == 0) /* Normal end of file. */
122 /* Get the path and locate the file. */
123 path = http_request_path (p->http_request);
124 if (stat (path, &statbuf) == -1)
126 close = file_not_found_error (p);
130 /* File or directory? */
131 if (S_ISDIR (statbuf.st_mode))
133 close = serve_directory (p, path, &statbuf);
136 else if (S_ISREG (statbuf.st_mode))
138 close = serve_file (p, path, &statbuf);
143 close = file_not_found_error (p);
154 file_not_found_error (eg2_server_processor p)
156 http_response http_response;
159 http_response = new_http_response (p->pool, p->http_request, p->io,
160 404, "File or directory not found");
161 http_response_send_headers (http_response,
163 "Content-Type", "text/html",
165 /* End of headers. */
167 close = http_response_end_headers (http_response);
169 if (http_request_is_HEAD (p->http_request)) return close;
172 "<html><head><title>File or directory not found</title></head>" CRLF
173 "<body bgcolor=\"#ffffff\">" CRLF
174 "<h1>404 File or directory not found</h1>" CRLF
175 "The file you requested was not found on this server." CRLF
176 "</body></html>" CRLF);
182 moved_permanently (eg2_server_processor p, const char *location)
184 http_response http_response;
187 http_response = new_http_response (p->pool, p->http_request, p->io,
188 301, "Moved permanently");
189 http_response_send_headers (http_response,
190 /* Content length. */
191 "Content-Length", "0",
193 "Location", location,
194 /* End of headers. */
196 close = http_response_end_headers (http_response);
198 if (http_request_is_HEAD (p->http_request)) return close;
204 serve_directory (eg2_server_processor p, const char *path,
205 const struct stat *statbuf)
207 http_response http_response;
213 /* Solaris defines NAME_MAX on a per-filesystem basis.
214 * See: http://lists.spine.cx/archives/everybuddy/2002-May/001419.html
216 long NAME_MAX = pathconf (path, _PC_NAME_MAX);
219 /* If the path doesn't end with a "/", then we need to send
220 * a redirect back to the client so it refetches the page
223 if (path[strlen (path)-1] != '/')
225 char *location = psprintf (p->pool, "%s/", path);
226 return moved_permanently (p, location);
229 dir = opendir (path);
231 return file_not_found_error (p);
233 http_response = new_http_response (p->pool, p->http_request, p->io,
235 http_response_send_headers (http_response,
237 "Content-Type", "text/html",
239 /* End of headers. */
241 close = http_response_end_headers (http_response);
243 if (http_request_is_HEAD (p->http_request)) return close;
246 "<html><head><title>Directory: %s</title></head>" CRLF
247 "<body bgcolor=\"#ffffff\">" CRLF
248 "<h1>Directory: %s</h1>" CRLF
250 "<tr><td></td><td></td>"
251 "<td><a href=\"..\">Parent directory</a></td></tr>" CRLF,
254 while ((d = readdir (dir)) != 0)
256 if (d->d_name[0] != '.') /* Ignore hidden files. */
258 const char *filename;
259 struct stat fstatbuf;
261 /* Generate the full pathname to this file. */
262 filename = psprintf (p->pool, "%s/%s", path, d->d_name);
264 /* Stat the file to find out what it is. */
265 if (lstat (filename, &fstatbuf) == 0)
270 if (S_ISDIR (fstatbuf.st_mode))
272 else if (S_ISREG (fstatbuf.st_mode))
274 else if (S_ISLNK (fstatbuf.st_mode))
279 size = fstatbuf.st_size;
281 /* Print the details. */
283 "<tr><td>[ %s ]</td><td align=right>%d</td>"
284 "<td><a href=\"%s%s\">%s</a>",
287 S_ISDIR (fstatbuf.st_mode) ? "/" : "",
290 if (S_ISLNK (fstatbuf.st_mode))
292 char link[NAME_MAX+1];
295 r = readlink (filename, link, NAME_MAX);
296 if (r >= 0) link[r] = '\0';
297 else strcpy (link, "unknown");
299 io_fprintf (p->io, " -> %s", link);
302 io_fputs ("</td></tr>" CRLF, p->io);
308 "</table></body></html>" CRLF);
314 serve_file (eg2_server_processor p, const char *path,
315 const struct stat *statbuf)
317 http_response http_response;
319 char *buffer = alloca (n);
321 char *content_length = pitoa (p->pool, statbuf->st_size);
323 fd = open (path, O_RDONLY);
325 return file_not_found_error (p);
327 http_response = new_http_response (p->pool, p->http_request, p->io,
329 http_response_send_headers (http_response,
331 "Content-Type", "text/plain",
332 "Content-Length", content_length,
333 /* End of headers. */
335 cl = http_response_end_headers (http_response);
337 if (http_request_is_HEAD (p->http_request)) return cl;
339 while ((r = read (fd, buffer, n)) > 0)
341 io_fwrite (buffer, r, 1, p->io);