1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
5 * Copyright (C) 2001-2009, Eduardo Silva P.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Library General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 * Some history about this module
24 * ------------------------------
25 * 2008 - Rewrite module, suport dynamic themes by Eduardo
26 * 2008 - Felipe Astroza (max) provided the mk_dirhtml_human_readable_size_func()
27 * 2007 - Add struct client_request support by Eduardo
28 * 2002 - Original version written by Daniel R. Ome
36 #include <sys/socket.h>
39 #include <sys/types.h>
45 #include "http_status.h"
57 #include "dirlisting.h"
59 /* Plugin data for register */
60 mk_plugin_data_t _shortname
= "dirlisting";
61 mk_plugin_data_t _name
= "Directory Listing";
62 mk_plugin_data_t _version
= "1.0";
63 mk_plugin_stage_t _stages
= MK_PLUGIN_STAGE_40
;;
65 struct plugin_api
*mk_api
;
67 char *_tags_global
[] = { "%_html_title_%",
72 char *_tags_entry
[] = { "%_target_title_%",
84 * *--> mk_dirhtml_conf()
86 * *--> mk_dirhtml_read_config()
87 * *--> mk_dirhtml_theme_load()
89 * *--> mk_dirhtml_load_file() (FILE_HEADER, FILE_ENTRY, FILE_FOOTER)
90 * *--> mk_dirhtml_template_create()
98 /* Function wrote by Max (Felipe Astroza), thanks! */
99 char *mk_dirhtml_human_readable_size(off_t size
)
101 unsigned long u
= 1024, i
, len
;
103 static const char *__units
[] = { "b", "K", "M", "G",
104 "T", "P", "E", "Z", "Y", NULL
107 for (i
= 0; __units
[i
] != NULL
; i
++) {
108 if ((size
/ u
) == 0) {
114 mk_api
->str_build(&buf
, &len
, "%u%s", size
, __units
[0]);
117 float fsize
= (float) ((double) size
/ (u
/ 1024));
118 mk_api
->str_build(&buf
, &len
, "%.1f%s", fsize
, __units
[i
]);
124 struct mk_f_list
*mk_dirhtml_create_element(char *file
,
127 unsigned long *list_len
)
132 struct mk_f_list
*entry
;
133 struct file_info
*entry_info
;
135 entry_info
= mk_api
->file_get_info(full_path
);
137 entry
= mk_api
->mem_alloc_z(sizeof(struct mk_f_list
));
140 entry
->info
= entry_info
;
143 st_time
= localtime((time_t *) & entry_info
->last_modification
);
145 entry
->ft_modif
= mk_api
->mem_alloc_z(50);
146 n
= strftime(entry
->ft_modif
, 50, "%d-%b-%G %H:%M", st_time
);
148 size
= entry
->info
->size
;
150 if (type
!= DT_DIR
) {
151 entry
->size
= mk_dirhtml_human_readable_size(size
);
154 entry
->size
= MK_DIRHTML_SIZE_DIR
;
157 *list_len
= *list_len
+ 1;
162 struct mk_f_list
*mk_dirhtml_create_list(DIR * dir
, char *path
,
163 unsigned long *list_len
)
168 struct mk_f_list
*list
= 0, *entry
= 0, *last
= 0;
170 /* Before to send the information, we need to build
171 * the list of entries, this really sucks because the user
172 * always will want to have the information sorted, why we don't
173 * add some spec to the HTTP protocol in order to send the information
174 * in a generic way and let the client choose how to show it
175 * as they does browsing a FTP server ???, we can save bandweight,
176 * let the cool firefox developers create different templates and
177 * we are going to have a more happy end users.
179 * that kind of ideas comes when you are in an airport just waiting :)
182 while ((ent
= readdir(dir
)) != NULL
) {
183 if (ent
->d_name
[0] == '.')
186 /* Look just for files and dirs */
187 if (ent
->d_type
!= DT_REG
&& ent
->d_type
!= DT_DIR
188 && ent
->d_type
!= DT_LNK
) {
193 puts("mk_dirhtml :: buffer error");
197 mk_api
->str_build(&full_path
, &len
, "%s%s", path
, ent
->d_name
);
199 entry
= mk_dirhtml_create_element(ent
->d_name
,
200 ent
->d_type
, full_path
, list_len
);
202 mk_api
->mem_free(full_path
);
220 /* Read dirhtml config and themes */
221 int mk_dirhtml_conf(char *confdir
)
227 mk_api
->str_build(&conf_file
, &len
, "%s", confdir
);
228 ret
= mk_dirhtml_read_config(conf_file
);
230 /* This function will load the default theme
231 * setted in dirhtml_conf struct
233 ret
= mk_dirhtml_theme_load();
234 mk_api
->pointer_set(&mk_dirhtml_default_mime
, MK_DIRHTML_DEFAULT_MIME
);
240 * Read the main configuration file for dirhtml: dirhtml.conf,
241 * it will alloc the dirhtml_conf struct
243 int mk_dirhtml_read_config(char *path
)
249 mk_api
->str_build(&default_file
, &len
, "%sdirhtml.conf", path
);
250 p
= conf
= mk_api
->config_create(default_file
);
252 /* alloc dirhtml config struct */
253 dirhtml_conf
= mk_api
->mem_alloc(sizeof(struct dirhtml_config
));
256 if (strcasecmp(p
->key
, "Theme") == 0) {
257 dirhtml_conf
->theme
= mk_api
->str_dup(p
->val
);
258 mk_api
->str_build(&dirhtml_conf
->theme_path
, &len
,
259 "%sthemes/%s/", path
, dirhtml_conf
->theme
);
264 mk_api
->mem_free(default_file
);
268 int mk_dirhtml_theme_load()
271 char *header
, *entry
, *footer
;
273 /* Load theme files */
274 header
= mk_dirhtml_load_file(MK_DIRHTML_FILE_HEADER
);
275 entry
= mk_dirhtml_load_file(MK_DIRHTML_FILE_ENTRY
);
276 footer
= mk_dirhtml_load_file(MK_DIRHTML_FILE_FOOTER
);
278 if (!header
|| !entry
|| !footer
) {
279 mk_api
->mem_free(header
);
280 mk_api
->mem_free(entry
);
281 mk_api
->mem_free(footer
);
286 mk_dirhtml_tpl_header
= mk_dirhtml_template_create(header
);
287 mk_dirhtml_tpl_entry
= mk_dirhtml_template_create(entry
);
288 mk_dirhtml_tpl_footer
= mk_dirhtml_template_create(footer
);
292 mk_dirhtml_theme_debug(&mk_dirhtml_tpl_header
);
293 mk_dirhtml_theme_debug(&mk_dirhtml_tpl_entry
);
294 mk_dirhtml_theme_debug(&mk_dirhtml_tpl_footer
);
297 mk_api
->mem_free(header
);
298 mk_api
->mem_free(entry
);
299 mk_api
->mem_free(footer
);
305 int mk_dirhtml_theme_debug(struct dirhtml_template
**st_tpl
)
308 struct dirhtml_template
*aux
;
312 printf("\n** DEBUG_THEME **");
316 printf("\n%i) len=%i, tag_id=%i", i
, aux
->len
, aux
->tag_id
);
317 if (aux
->tag_id
>= 0) {
318 printf(" (%s) ", aux
->tags
[aux
->tag_id
]);
328 /* Search which tag exists first in content :
331 int mk_dirhtml_theme_match_tag(char *content
, char *tpl
[])
335 for (i
= 0; tpl
[i
]; i
++) {
336 len
= strlen(tpl
[i
]);
337 match
= (int) mk_api
->str_search_n(content
, tpl
[i
], len
);
346 /* return the number of valid tags found in text string */
347 int mk_dirhtml_content_count_tags(char *content
, char *tpl
[])
349 int pos
= 0, count
= 0;
353 len
= strlen(content
);
356 (int) mk_api
->str_search_n(content
+ loop
, MK_DIRHTML_TAG_INIT
,
359 tpl_idx
= mk_dirhtml_theme_match_tag(content
+ loop
, tpl
);
374 struct dirhtml_template
*mk_dirhtml_template_create(char *content
)
377 int pos
, last
= 0; /* 0=search init, 1=search end */
378 int n_tags
= 0, tpl_idx
= 0;
386 /* Template to return */
387 struct dirhtml_template
*st_tpl
= 0;
389 cont_len
= strlen(content
);
394 /* Parsing content */
395 while (i
< cont_len
) {
396 pos
= (int) mk_api
->str_search_n(content
+ i
,
397 MK_DIRHTML_TAG_INIT
, -1);
403 /* Checking global tag, if it's not found, proceed with
406 _tpl
= (char **) _tags_global
;
407 tpl_idx
= mk_dirhtml_theme_match_tag(content
+ i
+ pos
, _tpl
);
409 /* if global template do not match, use the entry tags */
411 _tpl
= (char **) _tags_entry
;
412 tpl_idx
= mk_dirhtml_theme_match_tag(content
+ i
+ pos
, _tpl
);
415 /* if tag found is known, we add them to our list */
418 _buf
= mk_api
->str_copy_substr(content
, i
, i
+ pos
);
421 /* Dummy if/else to create or pass a created st_tpl */
423 st_tpl
= mk_dirhtml_template_list_add(NULL
,
424 _buf
, _len
, _tpl
, -1);
427 mk_dirhtml_template_list_add(&st_tpl
, _buf
, _len
, _tpl
, -1);
429 i
+= (pos
+ strlen(_tpl
[tpl_idx
]));
431 /* This means that a value need to be replaced */
432 mk_dirhtml_template_list_add(&st_tpl
, NULL
, -1, _tpl
, tpl_idx
);
440 if (last
< cont_len
) {
441 _buf
= mk_api
->str_copy_substr(content
, i
, cont_len
);
445 st_tpl
= mk_dirhtml_template_list_add(NULL
, _buf
, _len
, _tpl
, -1);
448 mk_dirhtml_template_list_add(&st_tpl
, _buf
, _len
, _tpl
, -1);
455 struct dirhtml_template
456 *mk_dirhtml_template_list_add(struct dirhtml_template
**header
,
457 char *buf
, int len
, char **tpl
, int tag_id
)
459 struct dirhtml_template
*node
, *aux
;
461 node
= mk_api
->mem_alloc_z(sizeof(struct dirhtml_template
));
469 node
->tag_id
= tag_id
;
475 printf("\n -> %s", node->tags[node->tag_id]);
479 return (struct dirhtml_template
*) node
;
483 while ((*aux
).next
!= NULL
) {
488 return (struct dirhtml_template
*) node
;
491 int mk_dirhtml_tag_get_id(char *tpl_tags
[], char *tag
)
494 for (i
= 0; tpl_tags
[i
]; i
++) {
495 if (strcmp(tpl_tags
[i
], tag
) == 0) {
503 int mk_dirhtml_template_len(struct dirhtml_template
*tpl
)
506 struct dirhtml_template
*aux
;
517 struct mk_iov
*mk_dirhtml_theme_compose(struct dirhtml_template
*template,
518 struct dirhtml_value
*values
)
521 * template = struct { char buf ; int len, int tag }
522 * values = struct {int tag, char *value, struct *next}
526 struct dirhtml_template
*tpl
= template;
527 struct dirhtml_value
*val
= values
;
531 tpl_len
= mk_dirhtml_template_len(template);
533 /* we duplicate the lenght in case we get separators */
534 iov
= (struct mk_iov
*) mk_api
->iov_create(tpl_len
* 2, 1);
538 /* check for dynamic value */
539 if (!tpl
->buf
&& tpl
->tag_id
>= 0) {
542 if (val
->tags
== tpl
->tags
&& val
->tag_id
== tpl
->tag_id
) {
543 mk_api
->iov_add_entry(iov
,
546 val
->sep
, MK_IOV_NOT_FREE_BUF
);
557 mk_api
->iov_add_entry(iov
, tpl
->buf
,
558 tpl
->len
, mk_iov_none
, MK_IOV_NOT_FREE_BUF
);
562 return (struct mk_iov
*) iov
;
565 struct dirhtml_value
*mk_dirhtml_tag_assign(struct dirhtml_value
**values
,
566 int tag_id
, mk_pointer sep
,
567 char *value
, char **tags
)
569 struct dirhtml_value
*check
, *aux
= 0;
571 aux
= mk_api
->mem_alloc(sizeof(struct dirhtml_value
));
576 aux
->tag_id
= tag_id
;
582 aux
->len
= strlen(value
);
591 return (struct dirhtml_value
*) aux
;
595 while ((*check
).next
) {
596 check
= (*check
).next
;
602 return (struct dirhtml_value
*) aux
;
605 void mk_dirhtml_tag_free_list(struct dirhtml_value
**list
)
607 struct dirhtml_value
*prev
, *target
;
611 while ((*target
).next
) {
613 target
= (*target
).next
;
615 mk_api
->mem_free(target
);
617 if (target
== *list
) {
626 char *mk_dirhtml_load_file(char *filename
)
628 char *tmp
= 0, *data
= 0;
631 mk_api
->str_build(&tmp
, &len
, "%s%s", dirhtml_conf
->theme_path
, filename
);
637 data
= mk_api
->file_to_buffer(tmp
);
638 mk_api
->mem_free(tmp
);
644 return (char *) data
;
647 int mk_dirhtml_entry_cmp(const void *a
, const void *b
)
649 struct mk_f_list
*const *f_a
= a
;
650 struct mk_f_list
*const *f_b
= b
;
652 return strcmp((*f_a
)->name
, (*f_b
)->name
);
655 int mk_dirhtml_send(int fd
, struct request
*sr
, struct mk_iov
*data
)
661 if (sr
->protocol
>= HTTP_PROTOCOL_11
) {
663 mk_api
->str_build(&buf
, &len
, "%x%s", data
->total_len
- 1, MK_CRLF
);
665 /* Add chunked information */
666 mk_api
->iov_set_entry(data
, buf
, len
, MK_IOV_FREE_BUF
, 0);
668 n
= (int) mk_api
->iov_send(fd
, data
, MK_IOV_SEND_TO_SOCKET
);
672 int mk_dirhtml_send_chunked_end(int fd
)
674 char *_end
= "0\r\n\r\n";
677 return write(fd
, _end
, len
);
680 void mk_dirhtml_free_list(struct mk_f_list
**toc
, unsigned long len
)
683 struct mk_f_list
*entry
;
685 for (i
= 0; i
< len
; i
++) {
688 if (entry
->type
!= DT_DIR
) {
689 mk_api
->mem_free(entry
->size
);
691 mk_api
->mem_free(entry
->ft_modif
);
692 mk_api
->mem_free(entry
->info
);
693 mk_api
->mem_free(entry
);
696 mk_api
->mem_free(toc
);
699 int mk_dirhtml_init(struct client_request
*cr
, struct request
*sr
)
706 unsigned long list_len
= 0;
708 struct mk_f_list
*file_list
, *entry
, **toc
;
709 struct mk_iov
*iov_header
, *iov_footer
, *iov_entry
;
710 struct dirhtml_value
*values_global
= 0;
711 struct dirhtml_value
*values_entry
= 0;
713 if (!(dir
= opendir(sr
->real_path
.data
))) {
717 file_list
= mk_dirhtml_create_list(dir
, sr
->real_path
.data
, &list_len
);
719 /* Building headers */
720 sr
->headers
->status
= M_HTTP_OK
;
721 sr
->headers
->cgi
= SH_CGI
;
722 sr
->headers
->breakline
= MK_HEADER_BREAKLINE
;
723 sr
->headers
->content_type
= mk_dirhtml_default_mime
;
725 if (sr
->protocol
>= HTTP_PROTOCOL_11
) {
726 sr
->headers
->transfer_encoding
= MK_HEADER_TE_TYPE_CHUNKED
;
729 /* Sending headers */
730 n
= (int) mk_api
->header_send(cr
->socket
, cr
, sr
, sr
->log
);
732 /* Creating response template */
733 /* Set %_html_title_% */
734 values_global
= mk_dirhtml_tag_assign(NULL
, 0, mk_iov_none
,
736 (char **) _tags_global
);
738 /* Set %_theme_path_% */
739 mk_dirhtml_tag_assign(&values_global
, 1, mk_iov_none
,
740 dirhtml_conf
->theme_path
, (char **) _tags_global
);
743 iov_header
= mk_dirhtml_theme_compose(mk_dirhtml_tpl_header
,
747 iov_footer
= mk_dirhtml_theme_compose(mk_dirhtml_tpl_footer
,
750 /* Creating table of contents and sorting */
751 toc
= mk_api
->mem_alloc(sizeof(struct mk_f_list
) * list_len
);
759 qsort(toc
, list_len
, sizeof(*toc
), mk_dirhtml_entry_cmp
);
761 n
= mk_dirhtml_send(cr
->socket
, sr
, iov_header
);
766 mk_dirhtml_tag_free_list(&values_global
);
767 mk_api
->iov_free(iov_header
);
768 mk_api
->iov_free(iov_footer
);
769 mk_dirhtml_free_list(toc
, list_len
);
774 for (i
= 0; i
< list_len
; i
++) {
775 /* %_target_title_% */
776 if (toc
[i
]->type
== DT_DIR
) {
784 values_entry
= mk_dirhtml_tag_assign(NULL
, 0, sep
,
786 (char **) _tags_entry
);
789 mk_dirhtml_tag_assign(&values_entry
, 1, sep
,
790 toc
[i
]->name
, (char **) _tags_entry
);
793 mk_dirhtml_tag_assign(&values_entry
, 2, sep
,
794 toc
[i
]->name
, (char **) _tags_entry
);
796 /* target modification time */
797 mk_dirhtml_tag_assign(&values_entry
, 3, mk_iov_none
,
798 toc
[i
]->ft_modif
, (char **) _tags_entry
);
801 mk_dirhtml_tag_assign(&values_entry
, 4, mk_iov_none
,
802 toc
[i
]->size
, (char **) _tags_entry
);
804 iov_entry
= mk_dirhtml_theme_compose(mk_dirhtml_tpl_entry
,
808 n
= mk_dirhtml_send(cr
->socket
, sr
, iov_entry
);
810 if ((i
% 20) == 0 && i
> 0) {
811 mk_api
->socket_cork_flag(cr
->socket
, TCP_CORK_OFF
);
812 mk_api
->socket_cork_flag(cr
->socket
, TCP_CORK_ON
);
819 /* free entry list */
820 mk_dirhtml_tag_free_list(&values_entry
);
821 mk_api
->iov_free(iov_entry
);
824 n
= mk_dirhtml_send(cr
->socket
, sr
, iov_footer
);
825 mk_api
->socket_cork_flag(cr
->socket
, TCP_CORK_OFF
);
827 if (sr
->protocol
>= HTTP_PROTOCOL_11
&& n
>= 0) {
828 mk_dirhtml_send_chunked_end(cr
->socket
);
833 mk_dirhtml_tag_free_list(&values_global
);
834 mk_api
->iov_free(iov_header
);
835 mk_api
->iov_free(iov_footer
);
836 mk_dirhtml_free_list(toc
, list_len
);
841 int _mk_plugin_init(void **api
, char *confdir
)
844 mk_dirhtml_conf(confdir
);
848 int _mk_plugin_stage_40(struct plugin
*plugin
, struct client_request
*cr
, struct request
*sr
)
850 /* Validate file/directory */
851 if (!sr
->file_info
) {
855 /* This plugin just handle directories */
856 if (sr
->file_info
->is_directory
== MK_FILE_FALSE
) {
861 if (sr
->host_conf
->getdir
== VAR_OFF
) {
862 mk_request_error(M_CLIENT_FORBIDDEN
, cr
, sr
, 1, sr
->log
);
866 mk_dirhtml_init(cr
, sr
);