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: dir.c,v 1.13 2003/02/05 23:02:51 rich Exp $
29 #ifdef HAVE_SYS_TYPES_H
30 #include <sys/types.h>
33 #ifdef HAVE_SYS_STAT_H
41 #ifdef HAVE_SYS_SYSLIMITS_H
42 #include <sys/syslimits.h>
50 #include <pthr_pseudothread.h>
51 #include <pthr_http.h>
52 #include <pthr_iolib.h>
54 #include "process_rq.h"
55 #include "mime_types.h"
62 static void choose_icon (process_rq p,
63 const char *filename, const struct stat *statbuf,
64 const char **icon, const char **icon_alt,
65 int *icon_width, int *icon_height);
66 static void standard_icon (process_rq p, const char *name,
67 const char **icon, const char **icon_alt,
68 int *icon_width, int *icon_height);
69 static void unknown_icon (process_rq p,
70 const char **icon, const char **icon_alt,
71 int *icon_width, int *icon_height);
72 static int parse_icon_str (process_rq p, const char *icon_str,
73 const char **icon, const char **icon_alt,
74 int *icon_width, int *icon_height);
75 static const char *get_printable_size (process_rq p,
76 const struct stat *statbuf);
77 static const char *get_link_field (process_rq p, const char *filename);
80 my_strcmp (const char **p1, const char **p2)
82 return strcmp (*p1, *p2);
86 dir_serve (process_rq p)
88 http_response http_response;
91 struct stat index_statbuf;
96 /* Is there an index file in this directory? If so, internally redirect
97 * the request to that file.
99 index_file = psprintf (p->pool, "%s/index.html", p->file_path);
100 if (stat (index_file, &index_statbuf) == 0 &&
101 S_ISREG (index_statbuf.st_mode))
103 /* Update the request structure appropriately. */
104 p->file_path = index_file;
105 p->remainder = psprintf (p->pool, "%s/index.html", p->remainder);
106 p->statbuf = index_statbuf;
108 /* Serve the file. */
109 return file_serve (p);
112 /* Are we allowed to generate a directory listing? */
113 if (!cfg_get_bool (p->host, p->alias, "list", 0))
114 return bad_request_error (p, "directory listing not allowed");
116 /* Yes: read the files into a local vector. */
117 dir = opendir (p->file_path);
119 return bad_request_error (p, "error opening directory");
121 files = new_vector (p->pool, const char *);
122 while ((d = readdir (dir)) != 0)
124 if (d->d_name[0] != '.') /* Ignore hidden files. */
126 char *name = pstrdup (p->pool, d->d_name);
127 vector_push_back (files, name);
132 /* Sort them into alphabetical order. */
133 psort (files, my_strcmp);
135 /* Not changed, so it's a real cache hit. */
136 http_response = new_http_response (p->pool, p->http_request, p->io,
138 http_response_send_headers (http_response,
140 "Content-Type", "text/html",
141 /* End of headers. */
143 close = http_response_end_headers (http_response);
145 if (http_request_is_HEAD (p->http_request)) return close;
148 "<html><head><title>Directory listing: %s</title></head>" CRLF
149 "<body bgcolor=\"#ffffff\">" CRLF
150 "<h1>Directory listing: %s</h1>" CRLF
151 "<a href=\"..\">Go up to parent directory</a>" CRLF
152 "<table border=\"0\">" CRLF,
153 p->canonical_path, p->canonical_path);
155 for (i = 0; i < vector_size (files); ++i)
157 const char *name, *pathname, *icon, *icon_alt;
158 const char *size = "", *link_field = "";
159 int icon_width, icon_height;
160 struct stat file_statbuf;
162 vector_get (files, i, name);
164 /* Generate the full pathname. */
165 pathname = psprintf (p->pool, "%s/%s", p->file_path, name);
167 /* Stat the file to get type and size information. */
168 if (lstat (pathname, &file_statbuf) == 0)
170 /* Choose an icon type. */
171 choose_icon (p, name, &file_statbuf, &icon, &icon_alt,
172 &icon_width, &icon_height);
175 if (S_ISREG (file_statbuf.st_mode))
176 size = get_printable_size (p, &file_statbuf);
178 /* If it's a link, get the link field. */
179 if (S_ISLNK (file_statbuf.st_mode))
180 link_field = get_link_field (p, pathname);
182 /* Print the pathname. */
184 "<tr><td><img src=\"%s\" alt=\"%s\" width=\"%d\" height=\"%d\"></td><td><a href=\"%s%s\">%s</a> %s</td><td>%s</td></tr>" CRLF,
185 icon, icon_alt, icon_width, icon_height,
187 S_ISDIR (file_statbuf.st_mode) ? "/" : "",
197 "</body></html>" CRLF,
198 http_get_servername ());
204 choose_icon (process_rq p,
205 const char *filename, const struct stat *statbuf,
206 const char **icon, const char **icon_alt,
207 int *icon_width, int *icon_height)
209 if (S_ISREG (statbuf->st_mode))
211 const char *mime_type = 0;
212 const char *icon_str;
215 /* Get the file extension and map it to a MIME type. */
216 if ((v = prematch (p->pool, filename, re_ext, 0)) != 0)
220 vector_get (v, 1, ext);
221 mime_type = mime_types_get_type (ext);
225 standard_icon (p, "no type",
226 icon, icon_alt, icon_width, icon_height);
230 /* If there a icon specified for this MIME type? */
231 icon_str = cfg_get_string (p->host, p->alias,
232 psprintf (p->pool, "icon for %s", mime_type),
236 /* Try looking for an icon for class / * instead. */
237 v = pstrcsplit (p->pool, mime_type, '/');
238 if (vector_size (v) >= 1)
240 const char *mime_class;
242 vector_get (v, 0, mime_class);
243 icon_str = cfg_get_string (p->host, p->alias,
244 psprintf (p->pool, "icon for %s/*",
251 unknown_icon (p, icon, icon_alt, icon_width, icon_height);
255 /* Split up the string. */
256 if (!parse_icon_str (p, icon_str, icon, icon_alt, icon_width, icon_height))
259 "cannot parse icon description: %s (mime_type = %s)\n",
260 icon_str, mime_type);
261 unknown_icon (p, icon, icon_alt, icon_width, icon_height);
265 else if (S_ISDIR (statbuf->st_mode))
267 standard_icon (p, "directory", icon, icon_alt, icon_width, icon_height);
269 else if (S_ISLNK (statbuf->st_mode))
271 standard_icon (p, "link", icon, icon_alt, icon_width, icon_height);
275 standard_icon (p, "special", icon, icon_alt, icon_width, icon_height);
280 standard_icon (process_rq p, const char *name,
281 const char **icon, const char **icon_alt,
282 int *icon_width, int *icon_height)
284 const char *icon_str;
286 icon_str = cfg_get_string (p->host, p->alias,
287 psprintf (p->pool, "%s icon", name), 0);
290 unknown_icon (p, icon, icon_alt, icon_width, icon_height);
294 if (!parse_icon_str (p, icon_str, icon, icon_alt, icon_width, icon_height))
296 fprintf (stderr, "cannot parse icon description: %s\n", icon_str);
297 unknown_icon (p, icon, icon_alt, icon_width, icon_height);
303 unknown_icon (process_rq p,
304 const char **icon, const char **icon_alt,
305 int *icon_width, int *icon_height)
307 const char *icon_str;
309 icon_str = cfg_get_string (p->host, p->alias, "unknown icon", 0);
313 "``unknown icon'' must be present in configuration file\n");
317 if (!parse_icon_str (p, icon_str, icon, icon_alt, icon_width, icon_height))
319 fprintf (stderr, "cannot parse icon description: %s\n", icon_str);
325 parse_icon_str (process_rq p, const char *icon_str,
326 const char **icon, const char **icon_alt,
327 int *icon_width, int *icon_height)
332 /* Split up the string. */
333 if ((v = prematch (p->pool, icon_str, re_icon, 0)) == 0)
336 if (vector_size (v) != 5) return 0;
338 vector_get (v, 1, *icon);
339 vector_get (v, 2, s);
340 sscanf (s, "%d", icon_width);
341 vector_get (v, 3, s);
342 sscanf (s, "%d", icon_height);
343 vector_get (v, 4, *icon_alt);
349 get_printable_size (process_rq p,
350 const struct stat *statbuf)
352 unsigned long size = statbuf->st_size;
355 return psprintf (p->pool, "%lu bytes", size);
356 else if (size < 1024 * 1024)
357 return psprintf (p->pool, "%.1f KB", size / 1024.0);
359 return psprintf (p->pool, "%.1f MB", size / (1024 * 1024.0));
363 get_link_field (process_rq p, const char *filename)
365 const char prefix[] = "-> ";
366 const int prefix_sz = sizeof prefix - 1;
371 /* Solaris defines NAME_MAX on a per-filesystem basis.
372 * See: http://lists.spine.cx/archives/everybuddy/2002-May/001419.html
374 long NAME_MAX = pathconf (filename, _PC_NAME_MAX);
377 buffer = pmalloc (p->pool, NAME_MAX + 1 + prefix_sz);
379 memcpy (buffer, prefix, prefix_sz);
381 n = readlink (filename, buffer + prefix_sz, NAME_MAX + 1);
382 if (n == -1) return "";
384 buffer[n + prefix_sz] = '\0';