2 * TUX - Integrated Application Protocols Layer and Object Cache
4 * Copyright (C) 2000, 2001, Ingo Molnar <mingo@redhat.com>
6 * proto_http.c: HTTP application protocol support
8 * Right now we detect simple GET headers, anything more
9 * subtle gets redirected to secondary server port.
15 /****************************************************************
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2, or (at your option)
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
30 ****************************************************************/
33 * Parse the HTTP message and put results into the request structure.
34 * CISAPI extensions do not see the actual message buffer.
36 * Any perceived irregularity is honored with a redirect to the
37 * secondary server - which in most cases should be Apache. So
38 * if TUX gets confused by some strange request we fall back
39 * to Apache to be RFC-correct.
41 * The parser is 'optimistic', ie. it's optimized for the case where
42 * the whole message is available and correct. The parser is also
43 * supposed to be 'robust', ie. it can be called multiple times with
44 * an incomplete message, as new packets arrive.
47 static inline int TOHEX (char c)
50 case '0' ... '9': c -= '0'; break;
51 case 'a' ... 'f': c -= 'a'-10; break;
52 case 'A' ... 'F': c -= 'A'-10; break;
60 * This function determines whether the client supports
61 * gzip-type content-encoding.
63 static int may_gzip (const char *str, int len)
65 const char *tmp, *curr;
71 for (i = 0; i <= len-6; i++) {
72 Dprintk("gzip-checking: {%s}\n", tmp);
73 if (memcmp(tmp, " gzip", 5)) {
79 if (*curr == ',' || *curr == '\r')
81 if (memcmp(curr, ";q=", 3))
85 * Every qvalue except explicitly zero is accepted.
86 * Zero values are "q=0.0", "q=0.00", "q=0.000".
87 * Parsing is optimized.
93 if (*curr == ' ' || *curr == '\r')
97 if (*curr == ' ' || *curr == '\r')
114 * This function strips off 'strip_host_tail' number of hostname
115 * components from the tail of the hostname.
117 * Eg. with a value of '1', the "somesite.hosting.com" hostname gets
118 * transformed into the "somesite" string.
120 static void strip_hostname(tux_req_t *req)
122 int strip = strip_host_tail;
123 int left = req->host_len;
130 if (req->host[left] != '.')
132 if (++component == strip)
138 req->host_len = left;
141 static void http_lookup_vhost (tux_req_t *req, int cachemiss);
142 static void http_process_message (tux_req_t *req, int cachemiss);
144 int parse_http_message (tux_req_t *req, const int total_len)
146 int hexhex = 0, hex_val_0 = 0, hex_val_1 = 0;
147 const char *curr, *uri, *message;
148 unsigned int objectname_len, left;
149 unsigned int have_r = 0;
153 message = req->headers;
154 Dprintk("parsing request:\n---\n%s\n---\n", message);
158 * Request-Line = Method SP Request-URI SP HTTP-Version CRLF
166 #define GOTO_INCOMPLETE do { Dprintk("incomplete at %s:%d.\n", __FILE__, __LINE__); goto incomplete_message; } while (0)
167 #define GOTO_REDIR do { TDprintk("redirect secondary at %s:%d.\n", __FILE__, __LINE__); goto error; } while (0)
169 #define PRINT_MESSAGE_LEFT \
170 Dprintk("message left (%d) at %s:%d:\n--->{%s}<---\n", left, __FILE__, __LINE__, curr)
174 if (PARSE_METHOD(req,curr,GET,left))
179 if (PARSE_METHOD(req,curr,HEAD,left))
184 if (PARSE_METHOD(req,curr,POST,left))
186 if (PARSE_METHOD(req,curr,PUT,left))
194 req->method_str = message;
195 req->method_len = curr-message-1;
197 Dprintk("got method %d\n", req->method);
202 * Ok, we got one of the methods we can handle, parse
207 // Do not allow leading "../" and intermediate "/../"
209 char *tmp = req->objectname;
212 req->uri_str = uri = curr;
215 c = get_c(curr,left);
223 if (c == ' ' || ((c == '?') && (tux_ignore_query != 1)) || c == '\r' || c == '\n')
228 Dprintk("hexhex: %d.\n", hexhex);
230 * First handle HEX HEX encoding
236 goto continue_parsing;
240 hex_val_0 = TOHEX(c);
244 goto continue_parsing;
246 hex_val_1 = TOHEX(c);
249 c = (hex_val_0 << 4) | hex_val_1;
284 if (!dotdot && (c == '/'))
289 if (curr - uri >= MAX_OBJECTNAME_LEN)
295 // handle trailing "/.."
299 objectname_len = tmp - req->objectname;
300 req->objectname_len = objectname_len;
302 Dprintk("got filename %s (%d)\n", req->objectname, req->objectname_len);
307 * Parse optional query string. Copy until end-of-string or space.
313 req->query_str = query = curr;
316 c = get_c(curr,left);
322 if (unlikely(tux_ignore_query == 2))
323 req->query_str = NULL;
325 query_len = curr-query-1;
326 req->query_len = query_len;
330 Dprintk("got query string %s (%d)\n", req->query_str, req->query_len);
331 req->uri_len = curr-uri-1;
334 Dprintk("got URI %s (%d)\n", req->uri_str, req->uri_len);
338 * Parse the HTTP version field:
340 req->version_str = curr;
341 if (!PARSE_TOKEN(curr,"HTTP/1.",left))
344 switch (get_c(curr,left)) {
346 req->version = HTTP_1_0;
349 req->version = HTTP_1_1;
355 * We default to keepalive in the HTTP/1.1 case and default
356 * to non-keepalive in the HTTP/1.0 case. If max_keepalives
357 * is 0 then we do no keepalives.
359 clear_keepalive(req);
360 if (tux_max_keepalives && (req->version == HTTP_1_1))
362 req->version_len = curr - req->version_str;
364 if (get_c(curr,left) != '\r')
366 if (get_c(curr,left) != '\n')
369 Dprintk("got version %d [%d]\n", req->version, req->version_len);
373 * Now parse (optional) request header fields:
378 c = get_c(curr,left);
394 #define PARSE_STR_FIELD(char,field,str,len) \
395 if (PARSE_TOKEN(curr,field,left)) { \
397 SKIP_LINE(curr,left); \
398 req->len = curr - req->str - 2; \
399 Dprintk(char field "field: %s.\n", req->str); \
403 #define ALLOW_UNKNOWN_FIELDS 1
404 #ifdef ALLOW_UNKNOWN_FIELDS
405 # define UNKNOWN_FIELD { SKIP_LINE(curr,left); break; }
407 # define UNKNOWN_FIELD GOTO_REDIR
412 PARSE_STR_FIELD("A","ccept: ",
413 accept_str,accept_len);
414 if (PARSE_TOKEN(curr,"ccept-Encoding: ",left)) {
415 const char *str = curr-1;
417 req->accept_encoding_str = curr;
418 SKIP_LINE(curr,left);
419 req->accept_encoding_len = curr - req->accept_encoding_str - 2;
420 Dprintk("Accept-Encoding field: {%s}.\n", str);
422 if (tux_compression && may_gzip(str,curr-str)) {
423 Dprintk("client accepts gzip!.\n");
424 req->may_send_gzip = 1;
428 PARSE_STR_FIELD("A","ccept-Charset: ",
429 accept_charset_str,accept_charset_len);
430 PARSE_STR_FIELD("A","ccept-Language: ",
431 accept_language_str,accept_language_len);
435 if (PARSE_TOKEN(curr,"onnection: ",left)) {
437 switch (get_c(curr,left)) {
439 if (!PARSE_TOKEN(curr,"eep-Alive",left))
441 if (tux_max_keepalives)
447 if (!PARSE_TOKEN(curr,"lose",left))
449 clear_keepalive(req);
453 if (!PARSE_TOKEN(curr,"eep-alive",left))
455 if (tux_max_keepalives)
459 if (PARSE_TOKEN(curr,"E",left))
461 if (PARSE_TOKEN(curr,"railers",left))
463 if (PARSE_TOKEN(curr,"ransfer-Encoding",left))
467 if (PARSE_TOKEN(curr,"roxy-Authenticate",left))
469 if (PARSE_TOKEN(curr,"roxy-Authorization",left))
473 if (!PARSE_TOKEN(curr,"pgrade",left))
488 // allow other tokens.
489 SKIP_LINE(curr,left);
493 PARSE_STR_FIELD("C","ookie: ",
494 cookies_str,cookies_len);
495 PARSE_STR_FIELD("C","ontent-Type: ",
496 content_type_str,content_type_len);
498 if (PARSE_TOKEN(curr,"ontent-Length: ",left) ||
499 PARSE_TOKEN(curr,"ontent-length: ",left)) {
501 req->contentlen_str = curr;
502 SKIP_LINE(curr,left);
503 req->contentlen_len = curr - req->contentlen_str - 2;
504 if (req->contentlen_len) {
505 tmp = req->contentlen_str;
506 req->content_len = simple_strtoul(tmp, NULL, 10);
508 Dprintk("Content-Length field: %s [%d].\n", req->contentlen_str, req->contentlen_len);
509 Dprintk("Content-Length value: %d.\n", req->content_len);
512 PARSE_STR_FIELD("C","ache-Control: ",
513 cache_control_str,cache_control_len);
517 if (PARSE_TOKEN(curr,"ost: ",left)) {
518 const char *tmp = curr;
519 char *tmp2 = req->host;
522 * canonize the hostname:
524 * 1) strip off preceding 'www.' variants,
525 * 2) transform it to lowercase.
526 * 3) strip trailing dots
527 * 4) potentially strip off tail
530 #define is_w(n) ((curr[n] == 'w') || (curr[n] == 'W'))
532 if ((left > 4) && is_w(0) && is_w(1) &&
533 is_w(2) && curr[3] == '.') {
539 COPY_LINE_TOLOWER(curr, tmp2, left, req->host+MAX_HOST_LEN-2);
540 req->host_len = curr - tmp - 2;
541 while (req->host[req->host_len] == '.') {
546 req->host[req->host_len] = 0;
549 Dprintk("Host field: %s [%d].\n", req->host, req->host_len);
555 PARSE_STR_FIELD("I","f-None-Match: ",
556 if_none_match_str,if_none_match_len);
557 PARSE_STR_FIELD("I","f-Modified-Since: ",
558 if_modified_since_str,if_modified_since_len);
559 PARSE_STR_FIELD("I","f-Range: ",
560 if_range_str,if_range_len);
564 PARSE_STR_FIELD("N","egotiate: ",
565 negotiate_str,negotiate_len);
569 PARSE_STR_FIELD("P","ragma: ",
570 pragma_str,pragma_len);
575 PARSE_STR_FIELD("R","eferer: ",
576 referer_str,referer_len);
577 if (!PARSE_TOKEN(curr,"ange: bytes=",left))
580 const char *tmp = curr;
581 char *tmp2 = (char *)curr;
582 unsigned int offset_start = 0, offset_end = 0;
585 offset_start = simple_strtoul(tmp2, &tmp2, 10);
589 offset_end = simple_strtoul(tmp2, &tmp2, 10) +1;
594 req->offset_start = offset_start;
595 req->offset_end = offset_end;
597 SKIP_LINE(curr,left);
598 Dprintk("Range field: %s [%d] (%d-%d).\n", tmp, curr-tmp, offset_start, offset_end);
603 PARSE_STR_FIELD("U","ser-Agent: ",
604 user_agent_str,user_agent_len);
616 if ((req->method == METHOD_POST) && req->content_len) {
618 if (curr + req->content_len > message + total_len)
620 req->post_data_str = curr;
621 req->post_data_len = req->content_len;
622 curr += req->content_len;
623 left -= req->content_len;
624 Dprintk("POST-ed data: {%s}\n", req->post_data_str);
627 switch (req->method) {
637 #define TUX_SCHEME "http://"
638 #define TUX_SCHEME_LEN (sizeof(TUX_SCHEME)-1)
640 if (!memcmp(req->objectname, TUX_SCHEME, TUX_SCHEME_LEN)) {
642 /* http://user:password@host:port/object */
644 const char *head, *tail, *end, *host, *port;
645 int host_len, objectname_len;
647 head = req->objectname + TUX_SCHEME_LEN;
648 end = req->objectname + req->objectname_len;
650 tail = memchr(head, '/', end - head);
653 host = memchr(head, '@', tail - head);
660 port = memchr(host, ':', tail - host);
662 host_len = port - host;
664 host_len = tail - host;
665 if (host_len >= MAX_HOST_LEN)
667 memcpy(req->host, host, host_len);
668 req->host_len = host_len;
669 req->host[host_len] = 0;
675 req->uri_len = end - tail;
681 objectname_len = end - tail;
682 memcpy(req->objectname, tail, objectname_len);
683 req->objectname_len = objectname_len;
684 req->objectname[objectname_len] = 0;
686 if (req->uri_str[0] != '/')
689 if ((req->version == HTTP_1_1) && !req->host_len)
691 if (req->objectname[0] == '/')
694 * Lets make sure nobody plays games with the host
695 * header in a virtual hosting environment:
697 if (req->virtual && req->host_len) {
698 if (memchr(req->host, '/', req->host_len))
700 if (req->host[0] == '.') {
701 if (req->host_len == 1)
703 if ((req->host_len == 2) && (req->host[0] == '.'))
708 * From this point on the request is for the main TUX engine:
710 Dprintk("ok, request accepted.\n");
712 if (req->keep_alive) {
713 req->nr_keepalives++;
714 if (req->nr_keepalives == -1)
715 req->nr_keepalives--;
716 INC_STAT(nr_keepalive_reqs);
718 INC_STAT(nr_nonkeepalive_reqs);
719 INC_STAT(keepalive_hist[req->nr_keepalives]);
722 req->parsed_len = curr-message;
725 req->virtual = tux_virtual_server;
727 add_tux_atom(req, http_lookup_vhost);
729 req->docroot_dentry = dget(req->proto->main_docroot.dentry);
730 req->docroot_mnt = mntget(req->proto->main_docroot.mnt);
731 add_tux_atom(req, http_process_message);
734 return req->parsed_len;
737 Dprintk("incomplete message!\n");
744 req->parsed_len = total_len;
749 TDprintk("redirecting message to secondary server.\n");
755 static int lookup_url (tux_req_t *req, const unsigned int flag)
758 * -1 : no previous checks made
759 * 0 : previous check failed, do not check farther,
760 * 1 : previous check successed, check farther
762 int not_modified = -1;
764 struct dentry *dentry = NULL;
765 struct vfsmount *mnt = NULL;
767 const char *filename;
770 * Do not do any etag or last_modified header checking
773 if (!tux_generate_etags && !tux_generate_last_mod)
780 filename = req->objectname;
781 Dprintk("will look up {%s} (%d)\n", filename, req->objectname_len);
782 Dprintk("current->fsuid: %d, current->fsgid: %d, ngroups: %d\n",
783 current->fsuid, current->fsgid, current->group_info->ngroups);
785 dentry = tux_lookup(req, filename, flag, &mnt);
787 #define INDEX "/index.html"
789 if (!dentry || IS_ERR(dentry)) {
790 if (PTR_ERR(dentry) == -EWOULDBLOCKIO)
793 if (tux_http_dir_indexing && (req->lookup_dir == 1)) {
794 // undo the index.html appending:
795 req->objectname_len -= sizeof(INDEX)-1;
796 req->objectname[req->objectname_len] = 0;
800 if (!req->lookup_404) {
801 int len = strlen(tux_404_page);
802 memcpy(req->objectname, tux_404_page, len);
803 req->objectname[len] = 0;
804 req->objectname_len = len;
809 TDprintk("abort - lookup error.\n");
813 Dprintk("SUCCESS, looked up {%s} == dentry %p (inode %p, count %d.)\n", filename, dentry, dentry->d_inode, atomic_read(&dentry->d_count));
814 inode = dentry->d_inode;
817 * At this point we have a real, non-negative dentry.
819 perm = tux_permission(inode);
821 if ((perm < 0) || (!S_ISDIR(dentry->d_inode->i_mode)
822 && !S_ISREG(dentry->d_inode->i_mode))) {
823 Dprintk("FAILED trusted dentry %p permission %d.\n", dentry, perm);
827 if ((req->lookup_dir != 2) && S_ISDIR(dentry->d_inode->i_mode)) {
828 if (req->lookup_dir || (req->objectname_len +
829 sizeof(INDEX) >= MAX_OBJECTNAME_LEN)) {
833 if (req->objectname_len && (req->objectname[req->objectname_len-1] != '/')) {
839 memcpy(req->objectname + req->objectname_len,
840 INDEX, sizeof(INDEX));
841 req->objectname_len += sizeof(INDEX)-1;
849 if (tux_max_object_size && (inode->i_size > tux_max_object_size)) {
850 TDprintk("too big object, %Ld bytes.\n", inode->i_size);
854 req->total_file_len = inode->i_size;
855 req->mtime = inode->i_mtime.tv_sec;
858 loff_t num = req->total_file_len;
860 unsigned long modulo;
861 char * etag_p = req->etag;
865 modulo = do_div(num, 10);
866 digits[nr_digits++] = '0' + modulo;
869 req->lendigits = nr_digits;
870 req->etaglen = nr_digits;
873 *etag_p++ = digits[--nr_digits];
880 digits[nr_digits++] = 'a' + num % 16;
883 req->etaglen += nr_digits+1;
885 *etag_p++ = digits[--nr_digits];
889 if ((req->if_none_match_len >= req->etaglen) && (abs(not_modified) == 1)) {
891 char * etag_p = req->etag;
892 const char * match_p = req->if_none_match_str;
893 int pos = req->etaglen - 1;
894 int matchpos = req->etaglen - 1;
897 while (etag_p[matchpos--] == match_p[pos--])
901 pos = req->if_none_match_len;
903 if (match_p[pos+1] == ',')
904 pos += req->etaglen + 2;
906 pos += req->etaglen-matchpos;
907 matchpos = req->etaglen - 1;
909 } while (pos < req->if_none_match_len);
913 TDprintk("Etag matched.\n");
918 if ((req->if_modified_since_len >= 24) && (abs(not_modified) == 1)) {
919 if (parse_time(req->if_modified_since_str, req->if_modified_since_len) >= req->mtime ) {
921 Dprintk("Last-Modified matched.\n");
926 if (not_modified == 1) {
931 Dprintk("looked up cached dentry %p, (count %d.)\n", dentry, dentry ? atomic_read(&dentry->d_count) : -1 );
933 url_hist_hit(req->total_file_len);
935 install_req_dentry(req, dentry, mnt);
953 #ifdef CONFIG_TUX_DEBUG
955 TDprintk("req %p has lookup errors!\n", req);
964 int handle_gzip_req (tux_req_t *req, unsigned int flags)
966 char *curr = req->objectname + req->objectname_len;
967 struct dentry *dentry;
968 struct vfsmount *mnt = NULL;
969 struct inode *inode, *orig_inode;
970 loff_t size, orig_size;
976 req->objectname_len += 3;
978 dentry = tux_lookup(req, req->objectname, flags, &mnt);
980 req->objectname_len -= 3;
981 req->objectname[req->objectname_len] = 0;
985 if (IS_ERR(dentry)) {
986 if (PTR_ERR(dentry) == -EWOULDBLOCKIO) {
987 release_req_dentry(req);
993 inode = dentry->d_inode;
994 size = inode->i_size;
995 orig_inode = req->dentry->d_inode;
996 orig_size = orig_inode->i_size;
998 if (!tux_permission(inode)
999 && (size < orig_size)
1000 && (inode->i_mtime.tv_sec >= orig_inode->i_mtime.tv_sec)) {
1002 release_req_dentry(req);
1003 install_req_dentry(req, dentry, mnt);
1004 req->total_file_len = req->output_len = size;
1005 Dprintk("content WILL be gzipped!\n");
1006 req->content_gzipped = 1;
1015 static spinlock_t mimetypes_lock = SPIN_LOCK_UNLOCKED;
1017 static LIST_HEAD(mimetypes_head);
1019 static mimetype_t default_mimetype = { type: "text/plain", type_len: 10, expire_str: "", expire_str_len: 0 };
1021 #define MAX_MIMETYPE_LEN 128
1022 #define MAX_CACHE_CONTROL_AGE_LEN 30
1024 void add_mimetype (char *new_ext, char *new_type, char *new_expire)
1026 int type_len = strlen(new_type);
1027 int ext_len = strlen(new_ext);
1028 int expire_len = strlen(new_expire);
1030 char *ext, *type, *expire;
1032 if (type_len > MAX_MIMETYPE_LEN)
1033 type_len = MAX_MIMETYPE_LEN;
1034 if (ext_len > MAX_URI_LEN)
1035 ext_len = MAX_URI_LEN;
1036 if (expire_len > MAX_CACHE_CONTROL_AGE_LEN)
1037 expire_len = MAX_CACHE_CONTROL_AGE_LEN;
1039 mime = tux_kmalloc(sizeof(*mime));
1040 memset(mime, 0, sizeof(*mime));
1041 ext = tux_kmalloc(ext_len + 1);
1042 type = tux_kmalloc(type_len + 1);
1043 expire = tux_kmalloc(expire_len + 1);
1045 strncpy(ext, new_ext, ext_len);
1046 strncpy(type, new_type, type_len);
1047 strncpy(expire, new_expire, expire_len);
1049 // in case one of the above parameters was too long :
1051 ext[ext_len] = '\0';
1052 type[type_len] = '\0';
1053 expire[expire_len] = '\0';
1056 mime->ext_len = ext_len;
1059 mime->type_len = type_len;
1061 mime->expire_str = expire;
1062 mime->expire_str_len = expire_len;
1064 mime->special = NORMAL_MIME_TYPE;
1065 if (!strcmp(type, "TUX/redirect"))
1066 mime->special = MIME_TYPE_REDIRECT;
1067 if (!strcmp(type, "TUX/CGI"))
1068 mime->special = MIME_TYPE_CGI;
1069 if (!strcmp(type, "TUX/module"))
1070 mime->special = MIME_TYPE_MODULE;
1072 spin_lock(&mimetypes_lock);
1073 list_add(&mime->list, &mimetypes_head);
1074 spin_unlock(&mimetypes_lock);
1077 static inline int ext_matches (char *file, int len, char *ext, int extlen)
1080 char *tmp = file + len-1;
1081 char *tmp2 = ext + extlen-1;
1086 for (i = 0; i < extlen; i++) {
1096 * Overhead is not a problem, we cache the MIME type
1099 static mimetype_t * lookup_mimetype (tux_req_t *req)
1101 char *objectname = req->objectname;
1102 int len = req->objectname_len;
1103 mimetype_t *mime = NULL;
1104 struct list_head *head, *tmp, *tmp1, *tmp2, *tmp3;
1106 if (!memchr(objectname, '.', len))
1109 spin_lock(&mimetypes_lock);
1110 head = &mimetypes_head;
1113 while (tmp != head) {
1114 mime = list_entry(tmp, mimetype_t, list);
1115 if (ext_matches(objectname, len, mime->ext, mime->ext_len)) {
1117 * Percolate often-used mimetypes up:
1119 if (tmp->prev != &mimetypes_head) {
1122 tmp3 = tmp->prev->prev;
1125 list_add(tmp, tmp3);
1126 list_add(tmp2, tmp);
1133 spin_unlock(&mimetypes_lock);
1137 mime = &default_mimetype;
1141 void free_mimetypes (void)
1143 struct list_head *head, *tmp, *next;
1146 spin_lock(&mimetypes_lock);
1147 head = &mimetypes_head;
1150 while (tmp != head) {
1152 mime = list_entry(tmp, mimetype_t, list);
1163 spin_unlock(&mimetypes_lock);
1167 * Various constant HTTP responses:
1170 static const char forbidden[] =
1171 "HTTP/1.1 403 Forbidden\r\n"
1172 "Connection: Keep-Alive\r\n" \
1173 "Content-Length: 24\r\n\r\n"
1174 "<HTML> Forbidden </HTML>";
1176 static const char not_found[] =
1177 "HTTP/1.1 404 Not Found\r\n"
1178 "Connection: Keep-Alive\r\n" \
1179 "Content-Length: 29\r\n\r\n"
1180 "<HTML> Page Not Found </HTML>";
1182 #define NOTMODIFIED_1 \
1183 "HTTP/1.1 304 Not Modified\r\n" \
1184 "Connection: Keep-Alive\r\n" \
1187 #define NOTMODIFIED_1_LEN (sizeof(NOTMODIFIED_1) - 1)
1189 #define NOTMODIFIED_2 \
1192 #define NOTMODIFIED_2_LEN (sizeof(NOTMODIFIED_2) - 1)
1194 #define NOTMODIFIED_3 \
1197 #define NOTMODIFIED_3_LEN (sizeof(NOTMODIFIED_3) - 1)
1199 #define REDIRECT_1 \
1200 "HTTP/1.1 301 Moved Permanently\r\n" \
1203 #define REDIRECT_1_LEN (sizeof(REDIRECT_1) - 1)
1205 #define REDIRECT_2 \
1206 "/\r\nContent-Length: 36\r\n" \
1207 "Connection: Keep-Alive\r\n" \
1208 "Content-Type: text/html\r\n\r\n" \
1209 "<HTML> 301 Moved Permanently </HTML>"
1211 #define REDIRECT_2_LEN (sizeof(REDIRECT_2) - 1)
1213 void send_async_err_forbidden (tux_req_t *req)
1215 send_async_message(req, forbidden, 403, 1);
1218 void send_async_err_not_found (tux_req_t *req)
1220 send_async_message(req, not_found, 404, 1);
1223 static void send_ret_notmodified (tux_req_t *req)
1228 size = NOTMODIFIED_1_LEN + DATE_LEN - 1 + NOTMODIFIED_2_LEN + req->etaglen + NOTMODIFIED_3_LEN;
1229 buf = get_abuf(req, size);
1230 memcpy(buf, NOTMODIFIED_1, NOTMODIFIED_1_LEN);
1231 buf += NOTMODIFIED_1_LEN;
1232 memcpy(buf, tux_date, DATE_LEN-1);
1234 memcpy(buf, NOTMODIFIED_2, NOTMODIFIED_2_LEN);
1235 buf += NOTMODIFIED_2_LEN;
1236 memcpy(buf, &req->etag, req->etaglen);
1237 buf += req->etaglen;
1238 memcpy(buf, NOTMODIFIED_3, NOTMODIFIED_3_LEN);
1239 buf += NOTMODIFIED_3_LEN;
1242 send_abuf(req, size, MSG_DONTWAIT);
1243 add_req_to_workqueue(req);
1246 static void send_ret_redirect (tux_req_t *req, int cachemiss)
1250 unsigned int uts_len = 0;
1252 size = REDIRECT_1_LEN;
1254 size += req->host_len;
1256 down_read(&uts_sem);
1257 uts_len = strlen(system_utsname.nodename);
1260 if (req->objectname[0] != '/')
1262 size += req->objectname_len;
1263 size += REDIRECT_2_LEN;
1265 if (size > PAGE_SIZE) {
1266 req->error = TUX_ERROR_CONN_CLOSE;
1267 zap_request(req, cachemiss);
1271 buf = get_abuf(req, size);
1273 memcpy(buf, REDIRECT_1, REDIRECT_1_LEN);
1274 buf += REDIRECT_1_LEN;
1276 Dprintk("req %p, host: %s, host_len: %d.\n", req, req->host, req->host_len);
1277 if (req->host_len) {
1278 memcpy(buf, req->host, req->host_len);
1279 buf += req->host_len;
1281 memcpy(buf, system_utsname.nodename, uts_len);
1285 if (req->objectname[0] != '/') {
1290 memcpy(buf, req->objectname, req->objectname_len);
1291 buf += req->objectname_len;
1293 memcpy(buf, REDIRECT_2, REDIRECT_2_LEN);
1294 buf += REDIRECT_2_LEN;
1297 send_abuf(req, size, MSG_DONTWAIT);
1298 add_req_to_workqueue(req);
1301 static void http_got_request (tux_req_t *req)
1305 add_tux_atom(req, parse_request);
1306 add_req_to_workqueue(req);
1310 tux_attribute_t * lookup_tux_attribute (tux_req_t *req)
1312 tux_attribute_t *attr;
1313 struct inode *inode;
1316 attr = tux_kmalloc(sizeof(*attr));
1317 memset(attr, 0, sizeof(*attr));
1319 mime = lookup_mimetype(req);
1321 inode = req->dentry->d_inode;
1322 if (!inode->i_uid && !inode->i_gid) {
1323 if (mime->special == MIME_TYPE_MODULE) {
1324 attr->tcapi = lookup_tuxmodule(req->objectname);
1327 mime = &default_mimetype;
1331 if (mime->special && (mime->special != MIME_TYPE_REDIRECT))
1332 mime = &default_mimetype;
1339 static void handle_range(tux_req_t *req)
1341 if (req->if_range_len) {
1344 range_time = parse_time(req->if_range_str, req->if_range_len);
1347 * If the file is newer then we send the whole file.
1349 if (range_time < req->mtime )
1352 /* if no offset_end was specified then default to 'end of file': */
1353 if (!req->offset_end)
1354 req->offset_end = req->total_file_len;
1358 * - is the range between 0...file_len-1 ?
1359 * - is offset_end after offset_start?
1361 * (note that offset_end is higher by 1)
1363 if ((req->offset_end > req->total_file_len) ||
1364 (req->offset_start >= req->total_file_len) ||
1365 (req->offset_end <= req->offset_start))
1368 * If the range is 0...file_len-1 then send the whole file:
1370 if (!req->offset_start && (req->offset_end == req->total_file_len))
1373 /* ok, the range is valid, use it: */
1375 req->output_len = req->offset_end - req->offset_start;
1376 req->in_file->f_pos = req->offset_start;
1380 req->offset_start = 0;
1381 req->offset_end = 0;
1384 static void http_pre_header (tux_req_t *req, int push);
1385 static void http_post_header (tux_req_t *req, int cachemiss);
1386 static void http_send_body (tux_req_t *req, int cachemiss);
1388 #define DIRLIST_HEAD_1 "\
1389 <!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\
1390 <HTML><HEAD><TITLE>Index of %s</TITLE></HEAD><BODY>\
1391 <H1>Index of %s </H1><PRE><HR>\n%s"
1393 #define DIRLIST_HEAD_2 "\
1394 <IMG SRC=\"/icons/back.gif\"ALT=\"[DIR]\"> <A HREF=\"../\">Parent Directory</A>\n"
1396 #define DIRLIST_HEAD_SIZE (sizeof(DIRLIST_HEAD_1) + sizeof(DIRLIST_HEAD_2))
1398 static void http_dirlist_head (tux_req_t *req, int cachemiss)
1400 char *buf1, *buf2, *path;
1403 buf1 = (char *)__get_free_page(GFP_KERNEL);
1404 buf2 = (char *)__get_free_page(GFP_KERNEL);
1407 path = tux_print_path(req, req->dentry, req->mnt, buf1, PAGE_SIZE);
1408 if (path[0] == '/' && path[1] == '/' && !path[3])
1410 if (2*strlen(path) + DIRLIST_HEAD_SIZE >= PAGE_SIZE)
1412 len = sprintf(buf2, DIRLIST_HEAD_1, path, path, req->dentry == req->docroot_dentry ? "" : DIRLIST_HEAD_2);
1413 __send_async_message(req, buf2, 200, len, 0);
1417 free_page((unsigned long)buf1);
1419 free_page((unsigned long)buf2);
1422 #define DIRLIST_TAIL "\
1423 </PRE><HR><ADDRESS><IMG SRC=\"/icons/tuxlogo.gif\"ALIGN=\"MIDDLE\"ALT=\"[TUX]\">Powered by Linux/TUX 3.0</ADDRESS>\n</BODY></HTML>"
1425 static void http_dirlist_tail (tux_req_t *req, int cachemiss)
1427 __send_async_message(req, DIRLIST_TAIL, 200, sizeof(DIRLIST_TAIL)-1, 1);
1430 static void http_dirlist (tux_req_t *req, int cachemiss)
1432 int head = (req->method == METHOD_HEAD);
1434 req->lookup_dir = 3;
1435 clear_keepalive(req);
1437 add_tux_atom(req, http_dirlist_tail);
1438 add_tux_atom(req, list_directory);
1439 add_tux_atom(req, http_dirlist_head);
1441 http_pre_header(req, head);
1442 add_req_to_workqueue(req);
1445 static char *host_path_hash(tux_req_t *req, char *tmp)
1447 if (req->host_len < 2)
1450 switch (mass_hosting_hash) {
1456 // www.ABCDEFG.com => A/ABCDEFG.com
1458 tmp[0] = req->host[0];
1460 memcpy(tmp + 2, req->host, req->host_len);
1461 tmp[req->host_len + 2] = 0;
1465 // www.ABCDEFG.com => A/AB/ABCDEFG.com
1467 tmp[0] = req->host[0];
1469 tmp[2] = req->host[0];
1470 tmp[3] = req->host[1];
1472 memcpy(tmp + 5, req->host, req->host_len);
1473 tmp[req->host_len + 5] = 0;
1477 // www.ABCDEFG.com => A/AB/ABC/ABCDEFG.com
1479 tmp[0] = req->host[0];
1481 tmp[2] = req->host[0];
1482 tmp[3] = req->host[1];
1484 tmp[5] = req->host[0];
1485 tmp[6] = req->host[1];
1486 tmp[7] = req->host[2];
1488 memcpy(tmp + 9, req->host, req->host_len);
1489 tmp[req->host_len + 9] = 0;
1495 static struct dentry * vhost_lookup (tux_req_t *req, struct nameidata* base, struct vfsmount **mnt)
1497 struct dentry *dentry = NULL;
1499 char ip [3+1+3+1+3+1+3 + 2];
1501 if (req->virtual >= TUX_VHOST_IP) {
1502 sprintf(ip, "%d.%d.%d.%d",
1503 NIPQUAD(inet_sk(req->sock->sk)->rcv_saddr));
1504 dentry = __tux_lookup (req, ip, base, mnt);
1505 if (!dentry || IS_ERR(dentry)) {
1506 if (PTR_ERR(dentry) == -EWOULDBLOCKIO)
1508 base->dentry = dget(req->proto->main_docroot.dentry);
1509 base->mnt = mntget(req->proto->main_docroot.mnt);
1510 goto lookup_default;
1512 if (req->virtual == TUX_VHOST_IP)
1515 // fall through in mixed mode:
1518 if (!req->host_len) {
1521 dentry = __tux_lookup (req, tux_default_vhost, base, mnt);
1523 char tmp [MAX_HOST_LEN*2];
1526 host_path = host_path_hash(req, tmp);
1527 Dprintk("host path hash returned: {%s}\n", host_path);
1532 dentry = __tux_lookup (req, host_path, base, mnt);
1534 if (!dentry || IS_ERR(dentry)) {
1535 if (PTR_ERR(dentry) == -EWOULDBLOCKIO)
1537 base->dentry = dget(req->proto->main_docroot.dentry);
1538 base->mnt = mntget(req->proto->main_docroot.mnt);
1539 if (req->virtual >= TUX_VHOST_IP) {
1541 dentry = __tux_lookup (req, ip, base, mnt);
1542 if (!dentry || IS_ERR(dentry)) {
1543 if (PTR_ERR(dentry) == -EWOULDBLOCKIO)
1545 base->dentry = dget(req->proto->main_docroot.dentry);
1546 base->mnt = mntget(req->proto->main_docroot.mnt);
1549 goto lookup_default;
1556 static void http_lookup_vhost (tux_req_t *req, int cachemiss)
1558 struct dentry *dentry;
1559 struct nameidata base = { };
1560 struct vfsmount *mnt = NULL;
1561 unsigned int flag = cachemiss ? 0 : LOOKUP_ATOMIC;
1563 Dprintk("http_lookup_vhost(%p, %d, virtual: %d, host: %s (%d).)\n", req, flag, req->virtual, req->host, req->host_len);
1565 base.flags = LOOKUP_FOLLOW|flag;
1566 base.last_type = LAST_ROOT;
1567 base.dentry = dget(req->proto->main_docroot.dentry);
1568 base.mnt = mntget(req->proto->main_docroot.mnt);
1570 dentry = vhost_lookup(req, &base, &mnt);
1572 Dprintk("looked up dentry %p.\n", dentry);
1574 if (dentry && !IS_ERR(dentry) && !dentry->d_inode)
1577 if (!dentry || IS_ERR(dentry)) {
1578 if (PTR_ERR(dentry) == -EWOULDBLOCKIO) {
1579 add_tux_atom(req, http_lookup_vhost);
1580 queue_cachemiss(req);
1586 req->docroot_dentry = dentry;
1587 req->docroot_mnt = mnt;
1589 add_tux_atom(req, http_process_message);
1590 add_req_to_workqueue(req);
1594 if (!IS_ERR(dentry))
1604 add_req_to_workqueue(req);
1607 static void http_process_message (tux_req_t *req, int cachemiss)
1609 tux_attribute_t *attr;
1611 unsigned int lookup_flag = cachemiss ? 0 : LOOKUP_ATOMIC;
1613 Dprintk("handling req %p, cachemiss: %d.\n", req, cachemiss);
1616 * URL redirection support - redirect all valid requests
1617 * to the first userspace module.
1619 if (tux_all_userspace) {
1620 tcapi_template_t *tcapi = get_first_usermodule();
1623 req->usermodule_idx = tcapi->userspace_id;
1627 missed = lookup_url(req, lookup_flag);
1629 if (req->query_str) {
1630 req->error = TUX_ERROR_REDIRECT;
1633 send_ret_redirect(req, cachemiss);
1642 Dprintk("uncached request.\n");
1643 INC_STAT(static_lookup_cachemisses);
1646 add_tux_atom(req, http_process_message);
1647 queue_cachemiss(req);
1651 * HTML directory indexing.
1653 if (S_ISDIR(req->dentry->d_inode->i_mode))
1654 return http_dirlist(req, cachemiss);
1655 if (!S_ISREG(req->dentry->d_inode->i_mode))
1659 attr = req->dentry->d_extra_attributes;
1661 attr = lookup_tux_attribute(req);
1664 req->dentry->d_extra_attributes = attr;
1667 Dprintk("using MIME type %s:%s, %d.\n", attr->mime->type, attr->mime->ext, attr->mime->special);
1670 req->usermodule_idx = attr->tcapi->userspace_id;
1671 if (req->module_dentry)
1673 req->module_dentry = dget(req->dentry);
1674 release_req_dentry(req);
1678 switch (attr->mime->special) {
1679 case MIME_TYPE_MODULE:
1683 case MIME_TYPE_REDIRECT:
1684 req->error = TUX_ERROR_REDIRECT;
1688 #ifdef CONFIG_TUX_EXTCGI
1689 Dprintk("CGI request %p.\n", req);
1695 if (req->query_str) {
1696 req->error = TUX_ERROR_REDIRECT;
1701 switch (req->method) {
1706 req->error = TUX_ERROR_REDIRECT;
1712 req->output_len = req->total_file_len;
1714 * Do range calculations.
1716 if (req->offset_end || req->offset_start)
1719 if (req->may_send_gzip && !req->offset_start && !req->offset_end) {
1720 if (handle_gzip_req(req, lookup_flag))
1722 if ((tux_compression >= 2) && !req->content_gzipped)
1723 req->content_gzipped = 2;
1725 if (req->parsed_len)
1731 add_tux_atom(req, http_send_body);
1732 add_tux_atom(req, http_post_header);
1734 http_pre_header(req, req->method == METHOD_HEAD);
1736 add_req_to_workqueue(req);
1741 zap_request(req, cachemiss);
1745 add_req_to_workqueue(req);
1748 static void http_post_header (tux_req_t *req, int cachemiss)
1750 #ifdef CONFIG_TUX_DEBUG
1751 req->bytes_expected = req->output_len;
1753 req->bytes_sent = 0; // data comes now.
1755 add_req_to_workqueue(req);
1758 static void http_send_body (tux_req_t *req, int cachemiss)
1762 Dprintk("SEND req %p <%p> (sock %p, sk %p) (keepalive: %d, status: %d)\n", req, __builtin_return_address(0), req->sock, req->sock->sk, req->keep_alive, req->status);
1764 SET_TIMESTAMP(req->output_timestamp);
1767 #ifdef CONFIG_TUX_DEBUG
1768 req->bytes_expected = 0;
1770 req->in_file->f_pos = 0;
1772 * We are in the middle of a file transfer,
1773 * zap it immediately:
1775 TDprintk("req->error = TUX_ERROR_CONN_CLOSE.\n");
1776 req->error = TUX_ERROR_CONN_CLOSE;
1777 zap_request(req, cachemiss);
1785 if (req->method != METHOD_HEAD) {
1786 ret = generic_send_file(req, req->sock, cachemiss);
1787 Dprintk("body send-file returned: %d.\n", ret);
1789 #ifdef CONFIG_TUX_DEBUG
1790 req->bytes_expected = 0;
1796 add_tux_atom(req, http_send_body);
1797 output_timeout(req);
1800 add_tux_atom(req, http_send_body);
1801 if (add_output_space_event(req, req->sock)) {
1807 INC_STAT(static_sendfile_cachemisses);
1808 add_tux_atom(req, http_send_body);
1809 queue_cachemiss(req);
1814 req->in_file->f_pos = 0;
1815 add_req_to_workqueue(req);
1820 #define DEFAULT_DATE "Wed, 01 Jan 1970 00:00:01 GMT"
1822 char tux_date [DATE_LEN] = DEFAULT_DATE;
1828 #define HEADER_PART1A \
1829 "HTTP/1.1 200 OK\r\n" \
1832 #define HEADER_PART1B \
1835 #define HEADER_PART1AP \
1836 "HTTP/1.1 206 Partial Content\r\n" \
1839 #define HEADER_PART1BP \
1840 "HTTP/1.1 206 Partial Content"
1842 #define HEADER_PART1C \
1843 "HTTP/1.1 404 Page Not Found\r\n" \
1846 #define HEADER_PART1D \
1847 "HTTP/1.1 200 OK\r\n" \
1848 "Content-Type: text/html\r\n" \
1849 "Connection: close\r\n"
1851 #define HEADER_PART2_keepalive "\r\nConnection: Keep-Alive\r\nDate: "
1853 #define HEADER_PART2_close "\r\nConnection: close\r\nDate: "
1855 #define HEADER_PART2_none "\r\nDate: "
1859 #define HEADER_PART3A "\r\nContent-Encoding: gzip"
1860 #define HEADER_PART3BX "\r\nContent-Length: "
1863 * Please acknowledge our hard work by not changing this define, or
1864 * at least please acknowledge us by leaving "TUX/2.0 (Linux)" in
1865 * the ID string. Thanks! :-)
1867 #define HEADER_PART3BY "\r\nServer: TUX/2.0 (Linux)\r\nContent-Length: "
1868 #define HEADER_PART3C "\r\nETag: \""
1869 #define HEADER_PART3ACC "\r\nAccept-Ranges: bytes"
1870 #define HEADER_PART3L "\r\nLast-Modified: "
1871 #define HEADER_PART3P "\r\nContent-Range: bytes "
1872 #define HEADER_PART3CA "\r\nCache-Control: max-age="
1873 #define HEADER_PART4 "\r\n\r\n"
1875 #define MAX_OUT_HEADER_LEN (sizeof(HEADER_PART1AP) + MAX_MIMETYPE_LEN + \
1876 sizeof(HEADER_PART2_keepalive) + DATE_LEN + \
1877 sizeof(HEADER_PART3A) + sizeof(HEADER_PART3BY) + \
1878 12 + sizeof(HEADER_PART3C) + 21 + sizeof(HEADER_PART3L) + \
1879 sizeof(HEADER_PART3P) + 32 + \
1880 DATE_LEN + sizeof(HEADER_PART4) + sizeof(tux_extra_html_header) \
1881 + sizeof(HEADER_PART3CA) + MAX_CACHE_CONTROL_AGE_LEN)
1883 static void http_pre_header (tux_req_t *req, int head)
1885 int partial = req->offset_start | req->offset_end;
1886 unsigned long flags;
1888 mimetype_t *mime = NULL;
1892 if (MAX_OUT_HEADER_LEN > PAGE_SIZE)
1894 if ((req->attr && req->attr->tcapi) || req->usermode)
1897 #define COPY_STATIC_PART(nr,curr) \
1899 memcpy(curr, HEADER_PART##nr, sizeof(HEADER_PART##nr)-1); \
1900 curr += sizeof(HEADER_PART##nr)-1; \
1903 buf = curr = get_abuf(req, MAX_OUT_HEADER_LEN);
1905 if (req->lookup_dir) {
1906 COPY_STATIC_PART(1D, curr);
1909 mime = req->attr->mime;
1913 if (req->status == 404) {
1914 COPY_STATIC_PART(1C, curr);
1915 memcpy(curr, mime->type, mime->type_len);
1916 curr += mime->type_len;
1918 if (tux_noid && (mime == &default_mimetype)) {
1920 COPY_STATIC_PART(1BP, curr);
1922 COPY_STATIC_PART(1B, curr);
1925 COPY_STATIC_PART(1AP, curr);
1927 COPY_STATIC_PART(1A, curr);
1928 memcpy(curr, mime->type, mime->type_len);
1929 curr += mime->type_len;
1933 if (tux_generate_cache_control && mime->expire_str_len) {
1934 COPY_STATIC_PART(3CA, curr);
1935 memcpy(curr, mime->expire_str, mime->expire_str_len);
1936 curr += mime->expire_str_len;
1939 if (req->keep_alive /* && (req->version == HTTP_1_0) */)
1940 COPY_STATIC_PART(2_keepalive, curr);
1941 else if (!req->keep_alive && (req->version == HTTP_1_1))
1942 COPY_STATIC_PART(2_close, curr);
1944 // HTTP/1.0 default means close
1945 COPY_STATIC_PART(2_none, curr);
1948 memcpy(curr, tux_date, DATE_LEN-1);
1951 if (req->content_gzipped)
1952 COPY_STATIC_PART(3A, curr);
1957 if (!req->lookup_dir) {
1959 COPY_STATIC_PART(3BX, curr);
1961 COPY_STATIC_PART(3BY, curr);
1964 curr += sprintf(curr, "%Ld", req->output_len);
1966 if (req->content_gzipped)
1967 curr += sprintf(curr, "%Ld",
1968 req->total_file_len);
1970 memcpy(curr, &req->etag, req->lendigits);
1971 curr += req->lendigits;
1974 if (tux_generate_etags && (req->status != 404)) {
1975 COPY_STATIC_PART(3C, curr);
1976 memcpy(curr, &req->etag, req->etaglen);
1977 curr += req->etaglen;
1981 if (tux_generate_last_mod || tux_generate_etags)
1982 COPY_STATIC_PART(3ACC, curr);
1984 if (tux_generate_last_mod && (req->status != 404)) {
1985 COPY_STATIC_PART(3L, curr);
1986 last_mod_time(curr, req->mtime);
1990 COPY_STATIC_PART(3P, curr);
1991 curr += sprintf(curr, "%Ld-%Ld/%Ld", req->offset_start,
1992 req->offset_end-1, req->total_file_len);
1994 COPY_STATIC_PART(4, curr);
1996 * Possibly add an extra HTML header:
1998 if (tux_extra_html_header_size && mime && !strcmp(mime->type, "text/html")) {
1999 unsigned int len = tux_extra_html_header_size;
2001 memcpy(curr, tux_extra_html_header, len);
2007 #ifdef CONFIG_TUX_DEBUG
2009 Dprintk("{%s} [%d/%d]\n", buf, size, strlen(buf));
2012 flags = MSG_DONTWAIT;
2015 send_abuf(req, size, flags);
2018 void http_illegal_request (tux_req_t *req, int cachemiss)
2020 if (req->status == 304)
2021 send_ret_notmodified(req);
2023 if (req->status == 403)
2024 send_async_err_forbidden(req);
2026 send_async_err_not_found(req);
2030 static int http_check_req_err (tux_req_t *req, int cachemiss)
2032 if ((req->sock->sk->sk_state <= TCP_SYN_RECV) &&
2033 !tcp_sk(req->sock->sk)->urg_data)
2035 Dprintk("http_check_req_err(%p,%d): 1 (state: %d, urg: %d)\n",
2036 req, cachemiss, req->sock->sk->sk_state,
2037 tcp_sk(req->sock->sk)->urg_data);
2038 #ifdef CONFIG_TUX_DEBUG
2039 req->bytes_expected = 0;
2041 req->in_file->f_pos = 0;
2042 req->error = TUX_ERROR_CONN_CLOSE;
2043 zap_request(req, cachemiss);
2048 #define COPY_STR(str) \
2049 do { memcpy(tmp, str, sizeof(str)-1); \
2050 tmp += sizeof(str)-1; } while (0)
2052 static char * http_print_dir_line (tux_req_t *req, char *tmp, char *d_name, int d_len, int d_type, struct dentry *dentry, struct inode *inode)
2059 COPY_STR("<IMG SRC=\"/icons/dir.gif\" ALT=\"[DIR]\">");
2063 (d_name[d_len-3] == '.') &&
2064 (d_name[d_len-2] == 'g') &&
2065 (d_name[d_len-1] == 'z'))
2066 COPY_STR("<IMG SRC=\"/icons/compressed.gif\" ALT=\"[ ]\">");
2069 (d_name[d_len-4] == '.') &&
2070 (d_name[d_len-3] == 't') &&
2071 (d_name[d_len-2] == 'g') &&
2072 (d_name[d_len-1] == 'z'))
2073 COPY_STR("<IMG SRC=\"/icons/compressed.gif\" ALT=\"[ ]\">");
2076 (d_name[d_len-4] == '.') &&
2077 (d_name[d_len-3] == 't') &&
2078 (d_name[d_len-2] == 'x') &&
2079 (d_name[d_len-1] == 't'))
2080 COPY_STR("<IMG SRC=\"/icons/text.gif\" ALT=\"[ ]\">");
2083 (d_name[d_len-4] == '.') &&
2084 (d_name[d_len-3] == 'b') &&
2085 (d_name[d_len-2] == 'z') &&
2086 (d_name[d_len-1] == '2'))
2087 COPY_STR("<IMG SRC=\"/icons/compressed.gif\" ALT=\"[ ]\">");
2090 (d_name[d_len-4] == '.') &&
2091 (d_name[d_len-3] == 'z') &&
2092 (d_name[d_len-2] == 'i') &&
2093 (d_name[d_len-1] == 'p'))
2094 COPY_STR("<IMG SRC=\"/icons/compressed.gif\" ALT=\"[ ]\">");
2096 COPY_STR("<IMG SRC=\"/icons/file.gif\" ALT=\"[ ]\">");
2099 COPY_STR("<IMG SRC=\"/icons/link.gif\" ALT=\"[LNK]\">");
2102 if (tux_hide_unreadable)
2104 COPY_STR("<IMG SRC=\"/icons/unknown.gif\" ALT=\"[ ]\">");
2108 #define LIST_1 " <A HREF=\""
2109 #define LIST_2 "\">"
2110 #define LIST_2_DIR "/\">"
2111 #define LIST_3 "</A> "
2114 memcpy(tmp, d_name, d_len);
2116 if (d_type == DT_DIR)
2117 COPY_STR(LIST_2_DIR);
2125 memcpy(tmp, d_name, len);
2131 if (d_type == DT_DIR)
2144 memset(tmp, ' ', FILL-d_len);
2148 tmp += time_unix2ls(inode->i_mtime.tv_sec, tmp);
2151 if (d_type != DT_REG) {
2155 size = inode->i_size >> 10;
2157 tmp += sprintf(tmp, "%8Lik ", size);
2162 tmp += sprintf(tmp, "%8LiM ", size);
2167 tmp += sprintf(tmp, "%8LiG ", size);
2172 tmp += sprintf(tmp, "%8LiT ", size);
2176 tmp += sprintf(tmp, "%8LiT ", size);
2187 tux_proto_t tux_proto_http = {
2190 got_request: http_got_request,
2191 parse_message: parse_http_message,
2192 illegal_request: http_illegal_request,
2193 check_req_err: http_check_req_err,
2194 print_dir_line: http_print_dir_line,