1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 _name
= "Directory Listing";
61 mk_plugin_data_t _version
= "1.0";
62 mk_plugin_stage_t _stages
= MK_PLUGIN_STAGE_40
;;
64 struct plugin_api
*mk_api
;
66 char *_tags_global
[] = {\
71 char *_tags_entry
[] =\
72 {"%_target_title_%", \
83 * *--> mk_dirhtml_conf()
85 * *--> mk_dirhtml_read_config()
86 * *--> mk_dirhtml_theme_load()
88 * *--> mk_dirhtml_load_file() (FILE_HEADER, FILE_ENTRY, FILE_FOOTER)
89 * *--> mk_dirhtml_template_create()
97 /* Function wrote by Max (Felipe Astroza), thanks! */
98 char *mk_dirhtml_human_readable_size(off_t size
)
100 unsigned long u
= 1024, i
, len
;
102 static const char *__units
[] = {"b", "K", "M", "G",
103 "T", "P", "E", "Z","Y", NULL
};
105 for(i
= 0; __units
[i
] != NULL
; i
++) {
112 mk_api
->str_build(&buf
, &len
, "%u%s", size
, __units
[0]);
115 float fsize
= (float)((double)size
/ (u
/1024));
116 mk_api
->str_build(&buf
, &len
, "%.1f%s", fsize
, __units
[i
]);
122 struct mk_f_list
*mk_dirhtml_create_element(char *file
,
123 unsigned char type
, char *full_path
,
124 unsigned long *list_len
)
129 struct mk_f_list
*entry
;
130 struct file_info
*entry_info
;
132 entry_info
= mk_api
->file_get_info(full_path
);
134 entry
= mk_api
->mem_alloc(sizeof(struct mk_f_list
));
137 entry
->info
= entry_info
;
140 st_time
= localtime((time_t *) &entry_info
->last_modification
);
142 entry
->ft_modif
= mk_api
->mem_alloc_z(50);
143 n
= strftime(entry
->ft_modif
, 50,
144 "%d-%b-%G %H:%M", st_time
);
146 size
= entry
->info
->size
;
149 entry
->size
= mk_dirhtml_human_readable_size(size
);
152 entry
->size
= MK_DIRHTML_SIZE_DIR
;
155 *list_len
= *list_len
+ 1;
160 struct mk_f_list
*mk_dirhtml_create_list(DIR *dir
, char *path
,
161 unsigned long *list_len
)
166 struct mk_f_list
*list
=0, *entry
=0, *last
=0;
168 /* Before to send the information, we need to build
169 * the list of entries, this really sucks because the user
170 * always will want to have the information sorted, why we don't
171 * add some spec to the HTTP protocol in order to send the information
172 * in a generic way and let the client choose how to show it
173 * as they does browsing a FTP server ???, we can save bandweight,
174 * let the cool firefox developers create different templates and
175 * we are going to have a more happy end users.
177 * that kind of ideas comes when you are in an airport just waiting :)
180 while((ent
= readdir(dir
)) != NULL
)
182 if(ent
->d_name
[0] == '.') continue;
184 /* Look just for files and dirs */
185 if(ent
->d_type
!=DT_REG
&& ent
->d_type
!=DT_DIR
186 && 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
,
204 mk_api
->mem_free(full_path
);
223 /* Read dirhtml config and themes */
224 int mk_dirhtml_conf()
230 mk_api
->str_build(&themes_path
,
233 mk_api
->config
->serverconf
);
234 ret
= mk_dirhtml_read_config(themes_path
);
236 /* This function will load the default theme
237 * setted in dirhtml_conf struct
239 ret
= mk_dirhtml_theme_load();
240 mk_api
->pointer_set(&mk_dirhtml_default_mime
, MK_DIRHTML_DEFAULT_MIME
);
246 * Read the main configuration file for dirhtml: dirhtml.conf,
247 * it will alloc the dirhtml_conf struct
249 int mk_dirhtml_read_config(char *path
)
255 char *variable
, *value
, *last
;
257 mk_api
->str_build(&default_file
, &len
, "%sdirhtml.conf", path
);
259 if(!(fileconf
= fopen(default_file
, "r")))
261 puts("Error: Cannot open dirhtml conf file");
262 mk_api
->mem_free(default_file
);
266 /* alloc dirhtml config struct */
267 dirhtml_conf
= mk_api
->mem_alloc(sizeof(struct dirhtml_config
));
269 while(fgets(buffer
, 255, fileconf
))
271 len
= strlen(buffer
);
272 if(buffer
[len
-1] == '\n') {
274 if(len
&& buffer
[len
-1] == '\r')
278 if(!buffer
[0] || buffer
[0] == '#')
281 variable
= strtok_r(buffer
, "\"\t ", &last
);
282 value
= strtok_r(NULL
, "\"\t ", &last
);
284 if (!variable
|| !value
) continue;
287 if(strcasecmp(variable
,"Theme")==0)
289 dirhtml_conf
->theme
= mk_api
->str_dup(value
);
290 mk_api
->str_build(&dirhtml_conf
->theme_path
, &len
,
291 "%s%s/", path
, dirhtml_conf
->theme
);
295 mk_api
->mem_free(default_file
);
299 int mk_dirhtml_theme_load()
302 char *header
, *entry
, *footer
;
304 /* Load theme files */
305 header
= mk_dirhtml_load_file(MK_DIRHTML_FILE_HEADER
);
306 entry
= mk_dirhtml_load_file(MK_DIRHTML_FILE_ENTRY
);
307 footer
= mk_dirhtml_load_file(MK_DIRHTML_FILE_FOOTER
);
309 if(!header
|| !entry
|| !footer
)
311 mk_api
->mem_free(header
);
312 mk_api
->mem_free(entry
);
313 mk_api
->mem_free(footer
);
318 mk_dirhtml_tpl_header
= mk_dirhtml_template_create(header
);
319 mk_dirhtml_tpl_entry
= mk_dirhtml_template_create(entry
);
320 mk_dirhtml_tpl_footer
= mk_dirhtml_template_create(footer
);
324 mk_dirhtml_theme_debug(&mk_dirhtml_tpl_header
);
325 mk_dirhtml_theme_debug(&mk_dirhtml_tpl_entry
);
326 mk_dirhtml_theme_debug(&mk_dirhtml_tpl_footer
);
329 mk_api
->mem_free(header
);
330 mk_api
->mem_free(entry
);
331 mk_api
->mem_free(footer
);
337 int mk_dirhtml_theme_debug(struct dirhtml_template
**st_tpl
)
340 struct dirhtml_template
*aux
;
344 printf("\n** DEBUG_THEME **");
349 printf("\n%i) len=%i, tag_id=%i", i
, aux
->len
, aux
->tag_id
);
350 if(aux
->tag_id
>= 0){
351 printf(" (%s) ",aux
->tags
[aux
->tag_id
]);
361 /* Search which tag exists first in content :
364 int mk_dirhtml_theme_match_tag(char *content
, char *tpl
[])
368 for(i
=0; tpl
[i
]; i
++){
369 len
= strlen(tpl
[i
]);
370 match
= (int) mk_api
->str_search_n(content
, tpl
[i
], len
);
379 /* return the number of valid tags found in text string */
380 int mk_dirhtml_content_count_tags(char *content
, char *tpl
[])
386 len
= strlen(content
);
389 pos
= (int) mk_api
->str_search_n(content
+loop
, MK_DIRHTML_TAG_INIT
, -1);
391 tpl_idx
= mk_dirhtml_theme_match_tag(content
+loop
, tpl
);
406 struct dirhtml_template
*mk_dirhtml_template_create(char *content
)
409 int pos
, last
=0; /* 0=search init, 1=search end */
410 int n_tags
=0, tpl_idx
=0;
418 /* Template to return */
419 struct dirhtml_template
*st_tpl
=0;
421 cont_len
= strlen(content
);
426 /* Parsing content */
429 pos
= (int) mk_api
->str_search_n(content
+i
,
430 MK_DIRHTML_TAG_INIT
, -1);
436 /* Checking global tag, if it's not found, proceed with
439 _tpl
= (char **) _tags_global
;
440 tpl_idx
= mk_dirhtml_theme_match_tag(content
+i
+pos
, _tpl
);
442 /* if global template do not match, use the entry tags */
444 _tpl
= (char **) _tags_entry
;
445 tpl_idx
= mk_dirhtml_theme_match_tag(content
+i
+pos
, _tpl
);
448 /* if tag found is known, we add them to our list */
451 _buf
= mk_api
->str_copy_substr(content
, i
, i
+pos
);
454 /* Dummy if/else to create or pass a created st_tpl */
456 st_tpl
= mk_dirhtml_template_list_add(NULL
,
463 mk_dirhtml_template_list_add(&st_tpl
,
469 i
+= (pos
+strlen(_tpl
[tpl_idx
]));
471 /* This means that a value need to be replaced */
472 mk_dirhtml_template_list_add(&st_tpl
, NULL
, -1,
482 _buf
= mk_api
->str_copy_substr(content
, i
, cont_len
);
486 st_tpl
= mk_dirhtml_template_list_add(NULL
, _buf
,
490 mk_dirhtml_template_list_add(&st_tpl
, _buf
,
498 struct dirhtml_template
499 *mk_dirhtml_template_list_add(struct dirhtml_template
**header
,
501 char **tpl
, int tag_id
)
503 struct dirhtml_template
*node
, *aux
;
505 node
= mk_api
->mem_alloc_z(sizeof(struct dirhtml_template
));
514 node
->tag_id
= tag_id
;
520 printf("\n -> %s", node->tags[node->tag_id]);
524 return (struct dirhtml_template
*) node
;
528 while((*aux
).next
!=NULL
){
533 return (struct dirhtml_template
*) node
;
536 int mk_dirhtml_tag_get_id(char *tpl_tags
[], char *tag
)
539 for(i
=0; tpl_tags
[i
]; i
++)
541 if(strcmp(tpl_tags
[i
], tag
)==0){
549 int mk_dirhtml_template_len(struct dirhtml_template
*tpl
)
552 struct dirhtml_template
*aux
;
563 struct mk_iov
*mk_dirhtml_theme_compose(struct dirhtml_template
*template,
564 struct dirhtml_value
*values
)
567 * template = struct { char buf ; int len, int tag }
568 * values = struct {int tag, char *value, struct *next}
572 struct dirhtml_template
*tpl
= template;
573 struct dirhtml_value
*val
= values
;
577 tpl_len
= mk_dirhtml_template_len(template);
579 /* we duplicate the lenght in case we get separators */
580 iov
= mk_api
->iov_create(tpl_len
*2, 1);
584 /* check for dynamic value */
585 if(!tpl
->buf
&& tpl
->tag_id
>=0){
588 if(val
->tags
== tpl
->tags
&&
589 val
->tag_id
== tpl
->tag_id
){
590 mk_api
->iov_add_entry(iov
,
594 MK_IOV_NOT_FREE_BUF
);
605 mk_api
->iov_add_entry(iov
, tpl
->buf
,
608 MK_IOV_NOT_FREE_BUF
);
612 return (struct mk_iov
*) iov
;
615 struct dirhtml_value
*mk_dirhtml_tag_assign(struct dirhtml_value
**values
,
616 int tag_id
, mk_pointer sep
,
617 char *value
, char **tags
)
619 struct dirhtml_value
*check
, *aux
=0;
621 aux
= mk_api
->mem_alloc(sizeof(struct dirhtml_value
));
626 aux
->tag_id
= tag_id
;
632 aux
->len
= strlen(value
);
641 return (struct dirhtml_value
*) aux
;
645 while((*check
).next
){
646 check
= (*check
).next
;
652 return (struct dirhtml_value
*) aux
;
655 void mk_dirhtml_tag_free_list(struct dirhtml_value
**list
)
657 struct dirhtml_value
*prev
, *target
;
661 while((*target
).next
){
663 target
= (*target
).next
;
665 mk_api
->mem_free(target
);
676 char *mk_dirhtml_load_file(char *filename
)
678 char *tmp
=0, *data
=0;
681 mk_api
->str_build(&tmp
, &len
, "%s%s",
682 dirhtml_conf
->theme_path
, filename
);
689 data
= mk_api
->file_to_buffer(tmp
);
690 mk_api
->mem_free(tmp
);
697 return (char *) data
;
700 int mk_dirhtml_entry_cmp(const void *a
, const void *b
)
702 struct mk_f_list
* const *f_a
= a
;
703 struct mk_f_list
* const *f_b
= b
;
705 return strcmp((*f_a
)->name
, (*f_b
)->name
);
708 int mk_dirhtml_send(int fd
, struct request
*sr
, struct mk_iov
*data
)
714 if(sr
->protocol
>= HTTP_PROTOCOL_11
){
716 mk_api
->str_build(&buf
, &len
, "%x%s",
717 data
->total_len
- 1, MK_CRLF
);
719 /* Add chunked information */
720 mk_api
->iov_set_entry(data
, buf
, len
, MK_IOV_FREE_BUF
, 0);
722 n
= (int) mk_api
->iov_send(fd
, data
, MK_IOV_SEND_TO_SOCKET
);
726 int mk_dirhtml_send_chunked_end(int fd
)
728 char *_end
= "0\r\n\r\n";
731 return write(fd
, _end
, len
);
734 void mk_dirhtml_free_list(struct mk_f_list
**toc
, unsigned long len
)
737 struct mk_f_list
*entry
;
743 if(entry
->type
!= DT_DIR
)
745 mk_api
->mem_free(entry
->size
);
747 mk_api
->mem_free(entry
->ft_modif
);
748 mk_api
->mem_free(entry
->info
);
749 mk_api
->mem_free(entry
);
752 mk_api
->mem_free(toc
);
755 int mk_dirhtml_init(struct client_request
*cr
, struct request
*sr
)
763 unsigned long list_len
=0;
765 struct mk_f_list
*file_list
, *entry
, **toc
;
766 struct mk_iov
*iov_header
, *iov_footer
, *iov_entry
;
767 struct dirhtml_value
*values_global
= 0;
768 struct dirhtml_value
*values_entry
= 0;
770 if(!(dir
= opendir(sr
->real_path
.data
)))
775 file_list
= mk_dirhtml_create_list(dir
, sr
->real_path
.data
, &list_len
);
777 /* Building headers */
778 sr
->headers
->status
= M_HTTP_OK
;
779 sr
->headers
->cgi
= SH_CGI
;
780 sr
->headers
->breakline
= MK_HEADER_BREAKLINE
;
781 sr
->headers
->content_type
= mk_dirhtml_default_mime
;
783 if(sr
->protocol
>= HTTP_PROTOCOL_11
)
785 sr
->headers
->transfer_encoding
= MK_HEADER_TE_TYPE_CHUNKED
;
788 /* Sending headers */
789 n
= (int) mk_api
->header_send(cr
->socket
, cr
, sr
, sr
->log
);
791 /* Creating response template */
792 /* Set %_html_title_% */
793 values_global
= mk_dirhtml_tag_assign(NULL
, 0, mk_iov_none
,
795 (char **) _tags_global
);
797 /* Set %_theme_path_% */
798 mk_dirhtml_tag_assign(&values_global
, 1, mk_iov_none
,
799 dirhtml_conf
->theme_path
,
800 (char **) _tags_global
);
803 iov_header
= mk_dirhtml_theme_compose(mk_dirhtml_tpl_header
,
807 iov_footer
= mk_dirhtml_theme_compose(mk_dirhtml_tpl_footer
,
810 mk_api
->socket_cork_flag(cr
->socket
, TCP_CORK_OFF
);
812 /* Creating table of contents and sorting */
813 toc
= mk_api
->mem_alloc(sizeof(struct mk_f_list
)*list_len
);
822 qsort(toc
, list_len
, sizeof(*toc
), mk_dirhtml_entry_cmp
);
824 n
= mk_dirhtml_send(cr
->socket
, sr
, iov_header
);
829 mk_dirhtml_tag_free_list(&values_global
);
830 mk_api
->iov_free(iov_header
);
831 mk_api
->iov_free(iov_footer
);
832 mk_dirhtml_free_list(toc
, list_len
);
838 for(i
=0; i
<list_len
; i
++)
840 /* %_target_title_% */
841 if(toc
[i
]->type
==DT_DIR
){
849 values_entry
= mk_dirhtml_tag_assign(NULL
, 0, sep
,
851 (char **) _tags_entry
);
854 mk_dirhtml_tag_assign(&values_entry
, 1, sep
,
856 (char **) _tags_entry
);
859 mk_dirhtml_tag_assign(&values_entry
, 2, sep
,
861 (char **) _tags_entry
);
863 /* target modification time */
864 mk_dirhtml_tag_assign(&values_entry
, 3, mk_iov_none
,
866 (char **) _tags_entry
);
869 mk_dirhtml_tag_assign(&values_entry
, 4, mk_iov_none
,
871 (char **) _tags_entry
);
873 iov_entry
= mk_dirhtml_theme_compose(mk_dirhtml_tpl_entry
,
877 n
= mk_dirhtml_send(cr
->socket
, sr
, iov_entry
);
879 /* free entry list */
880 mk_dirhtml_tag_free_list(&values_entry
);
881 mk_api
->iov_free(iov_entry
);
888 n
= mk_dirhtml_send(cr
->socket
, sr
, iov_footer
);
890 if(sr
->protocol
>= HTTP_PROTOCOL_11
&& n
>= 0){
891 mk_dirhtml_send_chunked_end(cr
->socket
);
896 mk_dirhtml_tag_free_list(&values_global
);
897 mk_api
->iov_free(iov_header
);
898 mk_api
->iov_free(iov_footer
);
899 mk_dirhtml_free_list(toc
, list_len
);
904 int _mk_plugin_init(void **api
)
911 int _mk_plugin_stage_40(struct client_request
*cr
, struct request
*sr
)
913 /* This plugin just handle directories */
914 if(sr
->file_info
->is_directory
== MK_FILE_FALSE
){
919 if(sr
->host_conf
->getdir
== VAR_OFF
){
920 mk_request_error(M_CLIENT_FORBIDDEN
, cr
, sr
, 1, sr
->log
);
924 mk_dirhtml_init(cr
, sr
);