Move plugins directory to root path
[MonkeyD.git] / plugins / dirlisting / dirlisting.c
blobfc6dbf0a71c2d6012146f6cc74322f085a424f6a
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
3 /* Monkey HTTP Daemon
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., 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
31 #include <dirent.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <sys/stat.h>
36 #include <sys/socket.h>
37 #include <time.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <unistd.h>
43 #include "monkey.h"
44 #include "http.h"
45 #include "http_status.h"
46 #include "str.h"
47 #include "memory.h"
48 #include "utils.h"
49 #include "config.h"
50 #include "method.h"
51 #include "socket.h"
52 #include "header.h"
53 #include "file.h"
54 #include "iov.h"
56 #include "plugin.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[] = {\
67 "%_html_title_%", \
68 "%_theme_path_%", \
69 NULL};
71 char *_tags_entry[] =\
72 {"%_target_title_%", \
73 "%_target_url_%", \
74 "%_target_name_%", \
75 "%_target_time_%", \
76 "%_target_size_%", \
77 NULL};
79 /* DIR_HTML logic:
80 * ---------------
81 * [Monkey Start]
82 * |
83 * *--> mk_dirhtml_conf()
84 * |
85 * *--> mk_dirhtml_read_config()
86 * *--> mk_dirhtml_theme_load()
87 * |
88 * *--> mk_dirhtml_load_file() (FILE_HEADER, FILE_ENTRY, FILE_FOOTER)
89 * *--> mk_dirhtml_template_create()
90 * |
91 * *-->
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;
101 char *buf;
102 static const char *__units[] = {"b", "K", "M", "G",
103 "T", "P", "E", "Z","Y", NULL};
105 for(i = 0; __units[i] != NULL; i++) {
106 if((size / u) == 0){
107 break;
109 u *= 1024;
111 if(!i){
112 mk_api->str_build(&buf, &len, "%u%s", size, __units[0]);
114 else {
115 float fsize = (float)((double)size / (u/1024));
116 mk_api->str_build(&buf, &len, "%.1f%s", fsize, __units[i]);
119 return buf;
122 struct mk_f_list *mk_dirhtml_create_element(char *file,
123 unsigned char type, char *full_path,
124 unsigned long *list_len)
126 off_t size;
127 int n;
128 struct tm *st_time;
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));
135 entry->name = file;
136 entry->type = type;
137 entry->info = entry_info;
138 entry->next = NULL;
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;
148 if(type != DT_DIR){
149 entry->size = mk_dirhtml_human_readable_size(size);
151 else{
152 entry->size = MK_DIRHTML_SIZE_DIR;
155 *list_len = *list_len + 1;
157 return entry;
160 struct mk_f_list *mk_dirhtml_create_list(DIR *dir, char *path,
161 unsigned long *list_len)
163 unsigned long len;
164 char *full_path=0;
165 struct dirent *ent;
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)
188 continue;
191 if(!ent->d_name)
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,
201 full_path,
202 list_len);
204 mk_api->mem_free(full_path);
206 if (!entry)
208 continue;
211 if(!list){
212 list = entry;
214 else{
215 last->next = entry;
217 last = entry;
220 return list;
223 /* Read dirhtml config and themes */
224 int mk_dirhtml_conf()
226 int ret = 0;
227 unsigned long len;
228 char *themes_path;
230 mk_api->str_build(&themes_path,
231 &len,
232 "%s/dir_themes/",
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);
242 return ret;
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)
251 unsigned long len;
252 char *default_file;
253 char buffer[255];
254 FILE *fileconf;
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);
263 return -1;
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') {
273 buffer[--len] = 0;
274 if(len && buffer[len-1] == '\r')
275 buffer[--len] = 0;
278 if(!buffer[0] || buffer[0] == '#')
279 continue;
281 variable = strtok_r(buffer, "\"\t ", &last);
282 value = strtok_r(NULL, "\"\t ", &last);
284 if (!variable || !value) continue;
286 /* Server Name */
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);
294 fclose(fileconf);
295 mk_api->mem_free(default_file);
296 return 0;
299 int mk_dirhtml_theme_load()
301 /* Data */
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);
314 return -1;
317 /* Parse themes */
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);
322 #ifdef DEBUG_THEME
323 /* Debug data */
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);
328 #endif
329 mk_api->mem_free(header);
330 mk_api->mem_free(entry);
331 mk_api->mem_free(footer);
333 return 0;
336 #ifdef DEBUG_THEME
337 int mk_dirhtml_theme_debug(struct dirhtml_template **st_tpl)
339 int i=0;
340 struct dirhtml_template *aux;
342 aux = *st_tpl;
344 printf("\n** DEBUG_THEME **");
345 fflush(stdout);
347 while(aux)
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]);
353 fflush(stdout);
354 aux = aux->next;
355 i++;
357 return 0;
359 #endif
361 /* Search which tag exists first in content :
362 * ex: %_html_title_%
364 int mk_dirhtml_theme_match_tag(char *content, char *tpl[])
366 int i, len, match;
368 for(i=0; tpl[i]; i++){
369 len = strlen(tpl[i]);
370 match = (int) mk_api->str_search_n(content, tpl[i], len);
371 if(match>=0){
372 return i;
376 return -1;
379 /* return the number of valid tags found in text string */
380 int mk_dirhtml_content_count_tags(char *content, char *tpl[])
382 int pos=0, count=0;
383 int len, tpl_idx;
384 int loop=0;
386 len = strlen(content);
387 while(loop<len)
389 pos = (int) mk_api->str_search_n(content+loop, MK_DIRHTML_TAG_INIT, -1);
390 if(pos>=0){
391 tpl_idx = mk_dirhtml_theme_match_tag(content+loop, tpl);
392 if(tpl_idx>=0){
393 count++;
395 loop+=pos;
397 else{
398 break;
400 loop++;
403 return count;
406 struct dirhtml_template *mk_dirhtml_template_create(char *content)
408 int i=0, cont_len;
409 int pos, last=0; /* 0=search init, 1=search end */
410 int n_tags=0, tpl_idx=0;
412 char *_buf;
413 int _len;
415 /* Global keys */
416 char **_tpl=0;
418 /* Template to return */
419 struct dirhtml_template *st_tpl=0;
421 cont_len = strlen(content);
422 if(cont_len<=0){
423 return NULL;
426 /* Parsing content */
427 while(i<cont_len)
429 pos = (int) mk_api->str_search_n(content+i,
430 MK_DIRHTML_TAG_INIT, -1);
432 if(pos<0){
433 break;
436 /* Checking global tag, if it's not found, proceed with
437 * 'entry tags'
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 */
443 if(tpl_idx < 0){
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 */
449 if(tpl_idx>=0){
451 _buf = mk_api->str_copy_substr(content, i, i+pos);
452 _len = strlen(_buf);
454 /* Dummy if/else to create or pass a created st_tpl */
455 if(!st_tpl){
456 st_tpl = mk_dirhtml_template_list_add(NULL,
457 _buf,
458 _len,
459 _tpl,
460 -1);
462 else{
463 mk_dirhtml_template_list_add(&st_tpl,
464 _buf,
465 _len,
466 _tpl,
467 -1);
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,
473 _tpl, tpl_idx);
474 n_tags++;
476 else{
477 i++;
481 if(last<cont_len){
482 _buf = mk_api->str_copy_substr(content, i, cont_len);
483 _len = strlen(_buf);
485 if(n_tags<=0){
486 st_tpl = mk_dirhtml_template_list_add(NULL, _buf,
487 _len, _tpl, -1);
489 else{
490 mk_dirhtml_template_list_add(&st_tpl, _buf,
491 _len, _tpl, -1);
495 return st_tpl;
498 struct dirhtml_template
499 *mk_dirhtml_template_list_add(struct dirhtml_template **header,
500 char *buf, int len,
501 char **tpl, int tag_id)
503 struct dirhtml_template *node, *aux;
505 node = mk_api->mem_alloc_z(sizeof(struct dirhtml_template));
506 if(!node)
508 return NULL;
512 node->buf = buf;
513 node->len = len;
514 node->tag_id = tag_id;
515 node->tags = tpl;
516 node->next = NULL;
519 if(tag_id >= 0){
520 printf("\n -> %s", node->tags[node->tag_id]);
523 if(!header){
524 return (struct dirhtml_template *) node;
527 aux = *header;
528 while((*aux).next!=NULL){
529 aux = (*aux).next;
532 (*aux).next = node;
533 return (struct dirhtml_template *) node;
536 int mk_dirhtml_tag_get_id(char *tpl_tags[], char *tag)
538 int i;
539 for(i=0; tpl_tags[i]; i++)
541 if(strcmp(tpl_tags[i], tag)==0){
542 return i;
546 return -1;
549 int mk_dirhtml_template_len(struct dirhtml_template *tpl)
551 int len=0;
552 struct dirhtml_template *aux;
554 aux = tpl;
555 while(aux){
556 len++;
557 aux = aux->next;
560 return len;
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}
571 struct mk_iov *iov;
572 struct dirhtml_template *tpl = template;
573 struct dirhtml_value *val = values;
575 int tpl_len;
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);
581 tpl = template;
583 while(tpl){
584 /* check for dynamic value */
585 if(!tpl->buf && tpl->tag_id>=0){
586 val = values;
587 while(val){
588 if(val->tags == tpl->tags &&
589 val->tag_id == tpl->tag_id){
590 mk_api->iov_add_entry(iov,
591 val->value,
592 val->len,
593 val->sep,
594 MK_IOV_NOT_FREE_BUF);
595 break;
597 else{
598 val = val->next;
603 /* static */
604 else{
605 mk_api->iov_add_entry(iov, tpl->buf,
606 tpl->len,
607 mk_iov_none,
608 MK_IOV_NOT_FREE_BUF);
610 tpl = tpl->next;
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));
622 if(!aux){
623 return NULL;
626 aux->tag_id = tag_id;
627 aux->value = value;
628 aux->sep = sep;
629 aux->tags = tags;
631 if(value){
632 aux->len = strlen(value);
634 else{
635 aux->len = -1;
638 aux->next = NULL;
640 if(!values){
641 return (struct dirhtml_value *) aux;
644 check = *values;
645 while((*check).next){
646 check = (*check).next;
649 (*check).next = aux;
652 return (struct dirhtml_value *) aux;
655 void mk_dirhtml_tag_free_list(struct dirhtml_value **list)
657 struct dirhtml_value *prev, *target;
659 target = *list;
660 while(target){
661 while((*target).next){
662 prev = target;
663 target = (*target).next;
665 mk_api->mem_free(target);
667 if(target == *list){
668 break;
670 (*prev).next = NULL;
671 target = *list;
673 *list = NULL;
676 char *mk_dirhtml_load_file(char *filename)
678 char *tmp=0, *data=0;
679 unsigned long len;
681 mk_api->str_build(&tmp, &len, "%s%s",
682 dirhtml_conf->theme_path, filename);
684 if(!tmp)
686 return NULL;
689 data = mk_api->file_to_buffer(tmp);
690 mk_api->mem_free(tmp);
692 if(!data)
694 return NULL;
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)
710 int n;
711 unsigned long len;
712 char *buf=0;
714 if(sr->protocol >= HTTP_PROTOCOL_11){
715 /* Chunk header */
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);
723 return n;
726 int mk_dirhtml_send_chunked_end(int fd)
728 char *_end = "0\r\n\r\n";
729 int len = 5;
731 return write(fd, _end, len);
734 void mk_dirhtml_free_list(struct mk_f_list **toc, unsigned long len)
736 int i;
737 struct mk_f_list *entry;
739 for(i=0; i<len; i++)
741 entry = toc[i];
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)
757 DIR *dir;
758 int i=0, n;
760 mk_pointer sep;
762 /* file info */
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)))
772 return -1;
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,
794 sr->uri_processed,
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);
802 /* HTML Header */
803 iov_header = mk_dirhtml_theme_compose(mk_dirhtml_tpl_header,
804 values_global);
806 /* HTML Footer */
807 iov_footer = mk_dirhtml_theme_compose(mk_dirhtml_tpl_footer,
808 values_global);
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);
814 entry = file_list;
815 i = 0;
816 while(entry)
818 toc[i] = entry;
819 i++;
820 entry = entry->next;
822 qsort(toc, list_len, sizeof(*toc), mk_dirhtml_entry_cmp);
824 n = mk_dirhtml_send(cr->socket, sr, iov_header);
826 if(n < 0){
827 closedir(dir);
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);
833 return 0;
837 /* sending TOC */
838 for(i=0; i<list_len; i++)
840 /* %_target_title_% */
841 if(toc[i]->type==DT_DIR){
842 sep = mk_iov_slash;
844 else{
845 sep = mk_iov_none;
848 /* target title */
849 values_entry = mk_dirhtml_tag_assign(NULL, 0, sep,
850 toc[i]->name,
851 (char **) _tags_entry);
853 /* target url */
854 mk_dirhtml_tag_assign(&values_entry, 1, sep,
855 toc[i]->name,
856 (char **) _tags_entry);
858 /* target name */
859 mk_dirhtml_tag_assign(&values_entry, 2, sep,
860 toc[i]->name,
861 (char **) _tags_entry);
863 /* target modification time */
864 mk_dirhtml_tag_assign(&values_entry, 3, mk_iov_none,
865 toc[i]->ft_modif,
866 (char **) _tags_entry);
868 /* target size */
869 mk_dirhtml_tag_assign(&values_entry, 4, mk_iov_none,
870 toc[i]->size,
871 (char **) _tags_entry);
873 iov_entry = mk_dirhtml_theme_compose(mk_dirhtml_tpl_entry,
874 values_entry);
876 /* send 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);
883 if(n<0){
884 break;
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);
894 closedir(dir);
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);
901 return 0;
904 int _mk_plugin_init(void **api)
906 mk_api = *api;
907 mk_dirhtml_conf();
908 return 0;
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){
915 return -1;
918 /* check setup */
919 if(sr->host_conf->getdir == VAR_OFF){
920 mk_request_error(M_CLIENT_FORBIDDEN, cr, sr, 1, sr->log);
921 return -1;
924 mk_dirhtml_init(cr, sr);
925 return 0;