<commit_before>/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */

/*  Monkey HTTP Daemon
 *  ------------------
 *  Copyright (C) 2001-2009, <NAME>.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Library General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

/*
 * Some history about this module
 * ------------------------------
 * 2008 - Rewrite module, suport dynamic themes by Eduardo
 * 2008 - <NAME> (max) provided the mk_dirhtml_human_readable_size_func()
 * 2007 - Add struct client_request support by Eduardo
 * 2002 - Original version written by <NAME>
 */

#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <time.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#include "monkey.h"
#include "http.h"
#include "http_status.h"
#include "str.h"
#include "memory.h"
#include "utils.h"
#include "config.h"
#include "method.h"
#include "socket.h"
#include "dir_html.h"
#include "header.h"
#include "file.h"
#include "iov.h"

/* DIR_HTML logic:
 * ---------------
 * [Monkey Start]
 * |
 * *--> mk_dirhtml_conf()
 *      |
 *      *--> mk_dirhtml_read_config()
 *      *--> mk_dirhtml_theme_load()
 *           |
 *           *--> mk_dirhtml_load_file() (FILE_HEADER, FILE_ENTRY, FILE_FOOTER)
 *           *--> mk_dirhtml_template_create()
 *                |
 *                *-->
 *
 *
 *
 */

/* Function wrote by Max (<NAME>), thanks! */
char *mk_dirhtml_human_readable_size(off_t size)
{
        unsigned long u = 1024, i, len;
        char *buf;
        static const char *__units[] = {"b", "K", "M", "G",
                                "T", "P", "E", "Z","Y", NULL};

        for(i = 0; __units[i] != NULL; i++) {
                if((size / u) == 0){
                        break;
                }
                u *= 1024;
        }
        if(!i){
                m_build_buffer(&buf, &len, "%u%s", size, __units[0]);
        }
        else {
                float fsize = (float)((double)size / (u/1024));
                m_build_buffer(&buf, &len, "%.1f%s", fsize, __units[i]);
        }

        return buf;
}

struct mk_f_list *mk_dirhtml_create_element(char *file,
                            unsigned char type, char *full_path,
                            unsigned long *list_len)
{
        off_t size;
        int n;
        struct tm *st_time;
        struct mk_f_list *entry;
        struct file_info *entry_info;

        entry_info = mk_file_get_info(full_path);

        entry = mk_mem_malloc(sizeof(struct mk_f_list));
        entry->name = file;
        entry->type = type;
        entry->info = entry_info;
        entry->next = NULL;

        st_time = localtime((time_t *) &entry_info->last_modification);

        entry->ft_modif = mk_mem_malloc_z(50);
        n = strftime(entry->ft_modif, 50,
                "%d-%b-%G %H:%M", st_time);

        size = entry->info->size;

        if(type != DT_DIR){
                entry->size = mk_dirhtml_human_readable_size(size);
        }
        else{
                entry->size = MK_DIRHTML_SIZE_DIR;
        }

        *list_len = *list_len + 1;

        return entry;
}

struct mk_f_list *mk_dirhtml_create_list(DIR *dir, char *path, 
                                         unsigned long *list_len)
{
	unsigned long len;
        char *full_path=0;
        struct dirent *ent;
        struct mk_f_list *list=0, *entry=0, *last=0;

	/* Before to send the information, we need to build
         * the list of entries, this really sucks because the user
         * always will want to have the information sorted, why we don't 
         * add some spec to the HTTP protocol in order to send the information
         * in a generic way and let the client choose how to show it
         * as they does browsing a FTP server ???, we can save bandweight,
         * let the cool firefox developers create different templates and
         * we are going to have a more happy end users.
         *
         * that kind of ideas comes when you are in an airport just waiting :)
         */

	while((ent = readdir(dir)) != NULL)
	{
                if(ent->d_name[0] == '.') continue;

                /* Look just for files and dirs */
                if(ent->d_type!=DT_REG && ent->d_type!=DT_DIR)
                {
                        continue;
                }
                
		if(!ent->d_name)
                {
			puts("mk_dirhtml :: buffer error");
		}


		m_build_buffer(&full_path, &len, "%s%s", path, ent->d_name);

		entry = mk_dirhtml_create_element(ent->d_name, 
                                                  ent->d_type,
                                                  full_path, 
                                                  list_len);

                mk_mem_free(full_path);

                if (!entry)
                {
                        continue;
		}

                if(!list){
                        list = entry;
                }
                else{
                        last->next = entry;
                }
                last = entry;
 	}

        return list;
}

/* Read dirhtml config and themes */
int mk_dirhtml_conf()
{
        int ret = 0;
        unsigned long len;
        char *themes_path;
        
        m_build_buffer(&themes_path, &len, "%s/dir_themes/", config->serverconf);
        ret = mk_dirhtml_read_config(themes_path);
        
        /* This function will load the default theme 
         * setted in dirhtml_conf struct 
         */
        ret = mk_dirhtml_theme_load();

        return ret;
}

/* 
 * Read the main configuration file for dirhtml: dirhtml.conf, 
 * it will alloc the dirhtml_conf struct
*/
int mk_dirhtml_read_config(char *path)
{
        unsigned long len;
        char *default_file;
        char buffer[255];
        FILE *fileconf;
        char *variable, *value, *last;

        m_build_buffer(&default_file, &len, "%sdirhtml.conf", path);

        if(!(fileconf = fopen(default_file, "r")))
        {
                puts("Error: Cannot open dirhtml conf file");
                mk_mem_free(default_file);
                return -1;
        }

        /* alloc dirhtml config struct */
        dirhtml_conf = mk_mem_malloc(sizeof(struct dirhtml_config));

        while(fgets(buffer, 255, fileconf))
        {
                 len = strlen(buffer);
                 if(buffer[len-1] == '\n') {
                   buffer[--len] = 0;
                   if(len && buffer[len-1] == '\r')
                     buffer[--len] = 0;
                 }
        
                 if(!buffer[0] || buffer[0] == '#')
                          continue;
            
                 variable   = strtok_r(buffer, "\"\t ", &last);
                 value     = strtok_r(NULL, "\"\t ", &last);

                 if (!variable || !value) continue;

                 /* Server Name */
                 if(strcasecmp(variable,"Theme")==0)
                 {
                         dirhtml_conf->theme = mk_string_dup(value);
                         m_build_buffer(&dirhtml_conf->theme_path, &len, 
                                        "%s%s/", path, dirhtml_conf->theme);
                 }
        }
        fclose(fileconf);
        mk_mem_free(default_file);
        return 0;
}

int mk_dirhtml_theme_load()
{
        /* Data */
	char *header, *entry, *footer;

        /* Load theme files */
        header = mk_dirhtml_load_file(MK_DIRHTML_FILE_HEADER);
        entry = mk_dirhtml_load_file(MK_DIRHTML_FILE_ENTRY);
        footer = mk_dirhtml_load_file(MK_DIRHTML_FILE_FOOTER);

        if(!header || !entry || !footer)
        {
                mk_mem_free(header);
                mk_mem_free(entry);
                mk_mem_free(footer);
                return -1;
        }

        /* Parse themes */
        mk_dirhtml_tpl_header = mk_dirhtml_template_create(header);
        mk_dirhtml_tpl_entry = mk_dirhtml_template_create(entry);
        mk_dirhtml_tpl_footer = mk_dirhtml_template_create(footer);

#ifdef DEBUG_THEME
        /* Debug data */
        mk_dirhtml_theme_debug(&mk_dirhtml_tpl_header);
        mk_dirhtml_theme_debug(&mk_dirhtml_tpl_entry);
        mk_dirhtml_theme_debug(&mk_dirhtml_tpl_footer);

#endif
        mk_mem_free(header);
        mk_mem_free(entry);
        mk_mem_free(footer);

        return 0;
}

#ifdef DEBUG_THEME
int mk_dirhtml_theme_debug(struct dirhtml_template **st_tpl)
{
        int i=0;
        struct dirhtml_template *aux;

        aux = *st_tpl;

        printf("\n** DEBUG_THEME **");
        fflush(stdout);

        while(aux)
        {
                printf("\n%i) len=%i, tag_id=%i", i, aux->len, aux->tag_id);
                if(aux->tag_id >= 0){
                        printf(" (%s) ",aux->tags[aux->tag_id]);
                }
                fflush(stdout);
                aux = aux->next;
                i++;
        }
        return 0;
}
#endif

/* Search which tag exists first in content :
 * ex: %_html_title_%
 */
int mk_dirhtml_theme_match_tag(char *content, char *tpl[])
{
        int i, len, match;
        
        for(i=0; tpl[i]; i++){
                len = strlen(tpl[i]);
                match = _mk_string_search(content, tpl[i], len);
                if(match>=0){
                        return i;
                }
        }

        return -1;
}

/* return the number of valid tags found in text string */
int mk_dirhtml_content_count_tags(char *content, char *tpl[])
{
        int pos=0, count=0;
        int len, tpl_idx;
        int loop=0;

        len = strlen(content);
        while(loop<len)
        {
                pos = _mk_string_search(content+loop, MK_DIRHTML_TAG_INIT, -1);
                if(pos>=0){
                        tpl_idx = mk_dirhtml_theme_match_tag(content+loop, tpl);
                        if(tpl_idx>=0){
                                count++;
                        }
                        loop+=pos;
                }
                else{
                        break;
                }
                loop++;

        }
        return count;
}

struct dirhtml_template *mk_dirhtml_template_create(char *content)
{
        int i=0, cont_len;
        int pos, last=0; /* 0=search init, 1=search end */
        int n_tags=0, tpl_idx=0;
        
        char *_buf;
        int _len;

        /* Global keys */
        char **_tpl=0;

        /* Template to return */
        struct dirhtml_template *st_tpl=0;

        cont_len = strlen(content);
        if(cont_len<=0){
                return NULL;
        }

        /* Parsing content */
        while(i<cont_len)
        {
                pos = _mk_string_search(content+i,
                                                MK_DIRHTML_TAG_INIT, -1);

                if(pos<0){
                        break;
                }

                /* Checking global tag, if it's not found, proceed with 
                 * 'entry tags'
                 */
                _tpl = (char **) _tags_global;
                tpl_idx = mk_dirhtml_theme_match_tag(content+i+pos, _tpl);

                /* if global template do not match, use the entry tags */
                if(tpl_idx < 0){
                        _tpl = (char **) _tags_entry;
                        tpl_idx = mk_dirhtml_theme_match_tag(content+i+pos, _tpl);
                }
                
                /* if tag found is known, we add them to our list */
                if(tpl_idx>=0){
                        
                        _buf = mk_string_copy_substr(content, i, i+pos);
                        _len = strlen(_buf);

                        /* Dummy if/else to create or pass a created st_tpl */
                        if(!st_tpl){
                                st_tpl = mk_dirhtml_template_list_add(NULL, 
                                                                      _buf, 
                                                                      _len,
                                                                      _tpl,
                                                                      -1);
                        }
                        else{
                                mk_dirhtml_template_list_add(&st_tpl, 
                                                             _buf, 
                                                             _len, 
                                                             _tpl, 
                                                             -1);
                        }
                        i += (pos+strlen(_tpl[tpl_idx]));

                        /* This means that a value need to be replaced */
                        mk_dirhtml_template_list_add(&st_tpl, NULL, -1, 
                                                     _tpl, tpl_idx);
                        n_tags++;
                }
                else{
                        i++;
                }     
        }

        if(last<cont_len){
                _buf = mk_string_copy_substr(content, i, cont_len);
                _len = strlen(_buf);

                if(n_tags<=0){
                        st_tpl = mk_dirhtml_template_list_add(NULL, _buf, 
                                                              _len, _tpl, -1);
                }
                else{
                        mk_dirhtml_template_list_add(&st_tpl, _buf, 
                                                     _len, _tpl, -1);
                }
        }

        return st_tpl;
}

struct dirhtml_template 
*mk_dirhtml_template_list_add(struct dirhtml_template **header,
                              char *buf, int len, 
                              char **tpl, int tag_id)
{
        struct dirhtml_template *node, *aux;

        node = mk_mem_malloc_z(sizeof(struct dirhtml_template));
        if(!node)
        {
                return NULL;
        }

        
        node->buf = buf;
        node->len = len;
        node->tag_id = tag_id;
        node->tags = tpl;
        node->next = NULL;

        /*
        if(tag_id >= 0){
                printf("\n  -> %s", node->tags[node->tag_id]);
        }
        */
        if(!header){
                return (struct dirhtml_template *) node;
        }

        aux = *header;
        while((*aux).next!=NULL){
                aux = (*aux).next;
        }

        (*aux).next = node;
        return (struct dirhtml_template *) node;
}

int mk_dirhtml_tag_get_id(char *tpl_tags[], char *tag)
{
        int i;
        for(i=0; tpl_tags[i]; i++)
        {
                if(strcmp(tpl_tags[i], tag)==0){
                        return i;
                }
        }

        return -1;
}

int mk_dirhtml_template_len(struct dirhtml_template *tpl)
{
        int len=0;
        struct dirhtml_template *aux;

        aux = tpl;
        while(aux){
                len++;
                aux = aux->next;
        }

        return len;
}

struct mk_iov *mk_dirhtml_theme_compose(struct dirhtml_template *template,
                             struct dirhtml_value *values)
{
        /*
         * template = struct { char buf ; int len, int tag }
         * values = struct {int tag, char *value, struct *next}
         */

        struct mk_iov *iov;
        struct dirhtml_template *tpl = template;
        struct dirhtml_value *val = values;

        int tpl_len;

        tpl_len = mk_dirhtml_template_len(template);

        /* we duplicate the lenght in case we get separators */
        iov = mk_iov_create(tpl_len*2, 1); 
        tpl = template;

        while(tpl){
                /* check for dynamic value */
                if(!tpl->buf && tpl->tag_id>=0){
                        val = values;
                        while(val){
                                if(val->tags == tpl->tags && 
                                   val->tag_id == tpl->tag_id){
                                        mk_iov_add_entry(iov, 
                                                 val->value,
                                                 val->len,
                                                 val->sep,
                                                 MK_IOV_NOT_FREE_BUF);
                                        break;
                                }
                                else{
                                        val = val->next;
                                }
                                
                        }
                } 
                /* static */
                else{
                        mk_iov_add_entry(iov, tpl->buf, 
                                         tpl->len, 
                                         mk_iov_none, 
                                         MK_IOV_NOT_FREE_BUF);
                }
                tpl = tpl->next;
        }
        return (struct mk_iov *) iov;
}

struct dirhtml_value *mk_dirhtml_tag_assign(struct dirhtml_value **values,
                                             int tag_id, mk_pointer sep, 
                                            char *value, char **tags)
{
        struct dirhtml_value *check, *aux=0;
        
        aux = mk_mem_malloc(sizeof(struct dirhtml_value));
        if(!aux){
                return NULL;
        }

        aux->tag_id = tag_id;
        aux->value = value;
        aux->sep = sep;
        aux->tags = tags;

        if(value){
                aux->len = strlen(value);
        }
        else{
                aux->len = -1;
        }

        aux->next = NULL;

        if(!values){
                return (struct dirhtml_value *) aux;
        }

        check = *values;
        while((*check).next){
                check = (*check).next;
        }

        (*check).next = aux;


        return (struct dirhtml_value *) aux;
}

char *mk_dirhtml_load_file(char *filename)
{
        char *tmp=0, *data=0;
        unsigned long len;

        m_build_buffer(&tmp, &len, "%s%s",
                       dirhtml_conf->theme_path, filename);

        if(!tmp)
        {
                return NULL;
        }

        data = mk_file_to_buffer(tmp);
        mk_mem_free(tmp);

        if(!data)
        {
                return NULL;
        }

        return (char *) data;
}

int mk_dirhtml_entry_cmp(const void *a, const void *b)
{
        struct mk_f_list * const *f_a = a;
        struct mk_f_list * const *f_b = b;

        return strcmp((*f_a)->name, (*f_b)->name);
}

int mk_dirhtml_send(int fd, struct mk_iov *data)
{
        int n;
        unsigned long len;
        char *buf=0;

        m_build_buffer(&buf, &len, "%x%s", data->total_len, MK_CRLF);

        /* Add chunked information */
        mk_iov_set_entry(data, buf, len, MK_IOV_FREE_BUF, 0);
        n = mk_iov_send(fd, data, MK_IOV_SEND_TO_SOCKET);

        return n;
}

int mk_dirhtml_send_chunked_end(int fd)
{
        char *_end = "0\r\n\r\n";
        int len = 5;

        return write(fd, _end, len);
}

void mk_dirhtml_free_list(struct mk_f_list **toc, unsigned long len)
{
        int i;
        struct mk_f_list *entry;

        for(i=0; i<len; i++)
        {
                entry = toc[i];

                if(entry->type != DT_DIR)
                {
                        mk_mem_free(entry->size);
                }
                mk_mem_free(entry->ft_modif);
                mk_mem_free(entry->info);
                mk_mem_free(entry);
        }
        
        mk_mem_free(toc);
}

int mk_dirhtml_init(struct client_request *cr, struct request *sr)
{
        DIR *dir;
        int i=0;

        mk_pointer sep;

	/* file info */
	unsigned long list_len=0;
        
        struct mk_f_list *file_list, *entry, **toc;
        struct mk_iov *iov_header, *iov_footer, *iov_entry;
        struct dirhtml_value *values_global = 0;
        struct dirhtml_value *values_entry = 0;

        if(!(dir = opendir(sr->real_path.data)))
        {
                return -1;
        }

        file_list = mk_dirhtml_create_list(dir, sr->real_path.data, &list_len);

        /* Building headers */
	sr->headers->status = M_HTTP_OK;
	sr->headers->cgi = SH_CGI;
        sr->headers->breakline = MK_HEADER_BREAKLINE;
        sr->headers->content_type = MK_DIRHTML_DEFAULT_MIME;

	if(sr->protocol==HTTP_PROTOCOL_11)
	{
		sr->headers->transfer_encoding = MK_HEADER_TE_TYPE_CHUNKED;
	}

	/* Sending headers */
	mk_header_send(cr->socket, cr, sr, sr->log);

        /* Creating response template */

        /* Set %_html_title_% */
        values_global = mk_dirhtml_tag_assign(NULL, 0, mk_iov_none,
                                              sr->uri_processed, 
                                              (char **) _tags_global);

        /* Set %_theme_path_% */
        mk_dirhtml_tag_assign(&values_global, 1, mk_iov_none, 
                              dirhtml_conf->theme_path, 
                              (char **) _tags_global);

        /* HTML Header */
        iov_header = mk_dirhtml_theme_compose(mk_dirhtml_tpl_header, 
                                              values_global);

        /* HTML Footer */
        iov_footer = mk_dirhtml_theme_compose(mk_dirhtml_tpl_footer, 
                                              values_global);

        mk_socket_set_cork_flag(cr->socket, TCP_CORK_OFF);
        mk_dirhtml_send(cr->socket, iov_header);

        /* Creating table of contents and sorting */
        toc = mk_mem_malloc(sizeof(struct mk_f_list)*list_len);
        entry = file_list;
        i = 0;
        while(entry)
        {
                toc[i] = entry;
                i++;
                entry = entry->next;
        }        
        qsort(toc, list_len, sizeof(*toc), mk_dirhtml_entry_cmp);

        /* sending TOC */
        for(i=0; i<list_len; i++)
	{
                /* %_target_title_% */
                if(toc[i]->type==DT_DIR){
                        sep = mk_iov_slash;
                }
                else{
                        sep = mk_iov_none;
                }

                /* target title */
                values_entry = mk_dirhtml_tag_assign(NULL, 0, sep,
                                      toc[i]->name, 
                                      (char **) _tags_entry);

                /* target url */
                mk_dirhtml_tag_assign(&values_entry, 1, sep, 
                                      toc[i]->name, 
                                      (char **) _tags_entry);

                /* target name */
                mk_dirhtml_tag_assign(&values_entry, 2, sep, 
                                      toc[i]->name, 
                                      (char **) _tags_entry);

                /* target modification time */
                mk_dirhtml_tag_assign(&values_entry, 3, mk_iov_none, 
                                      toc[i]->ft_modif, 
                                      (char **) _tags_entry);

                /* target size */
                mk_dirhtml_tag_assign(&values_entry, 4, mk_iov_none, 
                                      toc[i]->size, 
                                      (char **) _tags_entry);

                iov_entry = mk_dirhtml_theme_compose(mk_dirhtml_tpl_entry,
                                                     values_entry);

                mk_dirhtml_send(cr->socket, iov_entry);

                mk_mem_free(values_entry);
                mk_iov_free(iov_entry);
        }
        mk_dirhtml_send(cr->socket, iov_footer);
        mk_dirhtml_send_chunked_end(cr->socket);

        closedir(dir);

        mk_mem_free(values_global);
        mk_iov_free(iov_header);
        mk_iov_free(iov_footer);
        mk_dirhtml_free_list(toc, list_len);

        return 0;
}


<commit_msg>dir_html :: list symbolic links<commit_after>/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */

/*  Monkey HTTP Daemon
 *  ------------------
 *  Copyright (C) 2001-2009, <NAME>.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Library General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

/*
 * Some history about this module
 * ------------------------------
 * 2008 - Rewrite module, suport dynamic themes by Eduardo
 * 2008 - <NAME> (max) provided the mk_dirhtml_human_readable_size_func()
 * 2007 - Add struct client_request support by Eduardo
 * 2002 - Original version written by <NAME>
 */

#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <time.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#include "monkey.h"
#include "http.h"
#include "http_status.h"
#include "str.h"
#include "memory.h"
#include "utils.h"
#include "config.h"
#include "method.h"
#include "socket.h"
#include "dir_html.h"
#include "header.h"
#include "file.h"
#include "iov.h"

/* DIR_HTML logic:
 * ---------------
 * [Monkey Start]
 * |
 * *--> mk_dirhtml_conf()
 *      |
 *      *--> mk_dirhtml_read_config()
 *      *--> mk_dirhtml_theme_load()
 *           |
 *           *--> mk_dirhtml_load_file() (FILE_HEADER, FILE_ENTRY, FILE_FOOTER)
 *           *--> mk_dirhtml_template_create()
 *                |
 *                *-->
 *
 *
 *
 */

/* Function wrote by Max (<NAME>), thanks! */
char *mk_dirhtml_human_readable_size(off_t size)
{
        unsigned long u = 1024, i, len;
        char *buf;
        static const char *__units[] = {"b", "K", "M", "G",
                                "T", "P", "E", "Z","Y", NULL};

        for(i = 0; __units[i] != NULL; i++) {
                if((size / u) == 0){
                        break;
                }
                u *= 1024;
        }
        if(!i){
                m_build_buffer(&buf, &len, "%u%s", size, __units[0]);
        }
        else {
                float fsize = (float)((double)size / (u/1024));
                m_build_buffer(&buf, &len, "%.1f%s", fsize, __units[i]);
        }

        return buf;
}

struct mk_f_list *mk_dirhtml_create_element(char *file,
                            unsigned char type, char *full_path,
                            unsigned long *list_len)
{
        off_t size;
        int n;
        struct tm *st_time;
        struct mk_f_list *entry;
        struct file_info *entry_info;

        entry_info = mk_file_get_info(full_path);

        entry = mk_mem_malloc(sizeof(struct mk_f_list));
        entry->name = file;
        entry->type = type;
        entry->info = entry_info;
        entry->next = NULL;

        st_time = localtime((time_t *) &entry_info->last_modification);

        entry->ft_modif = mk_mem_malloc_z(50);
        n = strftime(entry->ft_modif, 50,
                "%d-%b-%G %H:%M", st_time);

        size = entry->info->size;

        if(type != DT_DIR){
                entry->size = mk_dirhtml_human_readable_size(size);
        }
        else{
                entry->size = MK_DIRHTML_SIZE_DIR;
        }

        *list_len = *list_len + 1;

        return entry;
}

struct mk_f_list *mk_dirhtml_create_list(DIR *dir, char *path, 
                                         unsigned long *list_len)
{
	unsigned long len;
        char *full_path=0;
        struct dirent *ent;
        struct mk_f_list *list=0, *entry=0, *last=0;

	/* Before to send the information, we need to build
         * the list of entries, this really sucks because the user
         * always will want to have the information sorted, why we don't 
         * add some spec to the HTTP protocol in order to send the information
         * in a generic way and let the client choose how to show it
         * as they does browsing a FTP server ???, we can save bandweight,
         * let the cool firefox developers create different templates and
         * we are going to have a more happy end users.
         *
         * that kind of ideas comes when you are in an airport just waiting :)
         */

	while((ent = readdir(dir)) != NULL)
	{
                if(ent->d_name[0] == '.') continue;

                /* Look just for files and dirs */
                if(ent->d_type!=DT_REG && ent->d_type!=DT_DIR 
                   && ent->d_type!=DT_LNK)
                {
                        continue;
                }
                
		if(!ent->d_name)
                {
			puts("mk_dirhtml :: buffer error");
		}


		m_build_buffer(&full_path, &len, "%s%s", path, ent->d_name);

		entry = mk_dirhtml_create_element(ent->d_name, 
                                                  ent->d_type,
                                                  full_path, 
                                                  list_len);

                mk_mem_free(full_path);

                if (!entry)
                {
                        continue;
		}

                if(!list){
                        list = entry;
                }
                else{
                        last->next = entry;
                }
                last = entry;
 	}

        return list;
}

/* Read dirhtml config and themes */
int mk_dirhtml_conf()
{
        int ret = 0;
        unsigned long len;
        char *themes_path;
        
        m_build_buffer(&themes_path, &len, "%s/dir_themes/", config->serverconf);
        ret = mk_dirhtml_read_config(themes_path);
        
        /* This function will load the default theme 
         * setted in dirhtml_conf struct 
         */
        ret = mk_dirhtml_theme_load();

        return ret;
}

/* 
 * Read the main configuration file for dirhtml: dirhtml.conf, 
 * it will alloc the dirhtml_conf struct
*/
int mk_dirhtml_read_config(char *path)
{
        unsigned long len;
        char *default_file;
        char buffer[255];
        FILE *fileconf;
        char *variable, *value, *last;

        m_build_buffer(&default_file, &len, "%sdirhtml.conf", path);

        if(!(fileconf = fopen(default_file, "r")))
        {
                puts("Error: Cannot open dirhtml conf file");
                mk_mem_free(default_file);
                return -1;
        }

        /* alloc dirhtml config struct */
        dirhtml_conf = mk_mem_malloc(sizeof(struct dirhtml_config));

        while(fgets(buffer, 255, fileconf))
        {
                 len = strlen(buffer);
                 if(buffer[len-1] == '\n') {
                   buffer[--len] = 0;
                   if(len && buffer[len-1] == '\r')
                     buffer[--len] = 0;
                 }
        
                 if(!buffer[0] || buffer[0] == '#')
                          continue;
            
                 variable   = strtok_r(buffer, "\"\t ", &last);
                 value     = strtok_r(NULL, "\"\t ", &last);

                 if (!variable || !value) continue;

                 /* Server Name */
                 if(strcasecmp(variable,"Theme")==0)
                 {
                         dirhtml_conf->theme = mk_string_dup(value);
                         m_build_buffer(&dirhtml_conf->theme_path, &len, 
                                        "%s%s/", path, dirhtml_conf->theme);
                 }
        }
        fclose(fileconf);
        mk_mem_free(default_file);
        return 0;
}

int mk_dirhtml_theme_load()
{
        /* Data */
	char *header, *entry, *footer;

        /* Load theme files */
        header = mk_dirhtml_load_file(MK_DIRHTML_FILE_HEADER);
        entry = mk_dirhtml_load_file(MK_DIRHTML_FILE_ENTRY);
        footer = mk_dirhtml_load_file(MK_DIRHTML_FILE_FOOTER);

        if(!header || !entry || !footer)
        {
                mk_mem_free(header);
                mk_mem_free(entry);
                mk_mem_free(footer);
                return -1;
        }

        /* Parse themes */
        mk_dirhtml_tpl_header = mk_dirhtml_template_create(header);
        mk_dirhtml_tpl_entry = mk_dirhtml_template_create(entry);
        mk_dirhtml_tpl_footer = mk_dirhtml_template_create(footer);

#ifdef DEBUG_THEME
        /* Debug data */
        mk_dirhtml_theme_debug(&mk_dirhtml_tpl_header);
        mk_dirhtml_theme_debug(&mk_dirhtml_tpl_entry);
        mk_dirhtml_theme_debug(&mk_dirhtml_tpl_footer);

#endif
        mk_mem_free(header);
        mk_mem_free(entry);
        mk_mem_free(footer);

        return 0;
}

#ifdef DEBUG_THEME
int mk_dirhtml_theme_debug(struct dirhtml_template **st_tpl)
{
        int i=0;
        struct dirhtml_template *aux;

        aux = *st_tpl;

        printf("\n** DEBUG_THEME **");
        fflush(stdout);

        while(aux)
        {
                printf("\n%i) len=%i, tag_id=%i", i, aux->len, aux->tag_id);
                if(aux->tag_id >= 0){
                        printf(" (%s) ",aux->tags[aux->tag_id]);
                }
                fflush(stdout);
                aux = aux->next;
                i++;
        }
        return 0;
}
#endif

/* Search which tag exists first in content :
 * ex: %_html_title_%
 */
int mk_dirhtml_theme_match_tag(char *content, char *tpl[])
{
        int i, len, match;
        
        for(i=0; tpl[i]; i++){
                len = strlen(tpl[i]);
                match = _mk_string_search(content, tpl[i], len);
                if(match>=0){
                        return i;
                }
        }

        return -1;
}

/* return the number of valid tags found in text string */
int mk_dirhtml_content_count_tags(char *content, char *tpl[])
{
        int pos=0, count=0;
        int len, tpl_idx;
        int loop=0;

        len = strlen(content);
        while(loop<len)
        {
                pos = _mk_string_search(content+loop, MK_DIRHTML_TAG_INIT, -1);
                if(pos>=0){
                        tpl_idx = mk_dirhtml_theme_match_tag(content+loop, tpl);
                        if(tpl_idx>=0){
                                count++;
                        }
                        loop+=pos;
                }
                else{
                        break;
                }
                loop++;

        }
        return count;
}

struct dirhtml_template *mk_dirhtml_template_create(char *content)
{
        int i=0, cont_len;
        int pos, last=0; /* 0=search init, 1=search end */
        int n_tags=0, tpl_idx=0;
        
        char *_buf;
        int _len;

        /* Global keys */
        char **_tpl=0;

        /* Template to return */
        struct dirhtml_template *st_tpl=0;

        cont_len = strlen(content);
        if(cont_len<=0){
                return NULL;
        }

        /* Parsing content */
        while(i<cont_len)
        {
                pos = _mk_string_search(content+i,
                                                MK_DIRHTML_TAG_INIT, -1);

                if(pos<0){
                        break;
                }

                /* Checking global tag, if it's not found, proceed with 
                 * 'entry tags'
                 */
                _tpl = (char **) _tags_global;
                tpl_idx = mk_dirhtml_theme_match_tag(content+i+pos, _tpl);

                /* if global template do not match, use the entry tags */
                if(tpl_idx < 0){
                        _tpl = (char **) _tags_entry;
                        tpl_idx = mk_dirhtml_theme_match_tag(content+i+pos, _tpl);
                }
                
                /* if tag found is known, we add them to our list */
                if(tpl_idx>=0){
                        
                        _buf = mk_string_copy_substr(content, i, i+pos);
                        _len = strlen(_buf);

                        /* Dummy if/else to create or pass a created st_tpl */
                        if(!st_tpl){
                                st_tpl = mk_dirhtml_template_list_add(NULL, 
                                                                      _buf, 
                                                                      _len,
                                                                      _tpl,
                                                                      -1);
                        }
                        else{
                                mk_dirhtml_template_list_add(&st_tpl, 
                                                             _buf, 
                                                             _len, 
                                                             _tpl, 
                                                             -1);
                        }
                        i += (pos+strlen(_tpl[tpl_idx]));

                        /* This means that a value need to be replaced */
                        mk_dirhtml_template_list_add(&st_tpl, NULL, -1, 
                                                     _tpl, tpl_idx);
                        n_tags++;
                }
                else{
                        i++;
                }     
        }

        if(last<cont_len){
                _buf = mk_string_copy_substr(content, i, cont_len);
                _len = strlen(_buf);

                if(n_tags<=0){
                        st_tpl = mk_dirhtml_template_list_add(NULL, _buf, 
                                                              _len, _tpl, -1);
                }
                else{
                        mk_dirhtml_template_list_add(&st_tpl, _buf, 
                                                     _len, _tpl, -1);
                }
        }

        return st_tpl;
}

struct dirhtml_template 
*mk_dirhtml_template_list_add(struct dirhtml_template **header,
                              char *buf, int len, 
                              char **tpl, int tag_id)
{
        struct dirhtml_template *node, *aux;

        node = mk_mem_malloc_z(sizeof(struct dirhtml_template));
        if(!node)
        {
                return NULL;
        }

        
        node->buf = buf;
        node->len = len;
        node->tag_id = tag_id;
        node->tags = tpl;
        node->next = NULL;

        /*
        if(tag_id >= 0){
                printf("\n  -> %s", node->tags[node->tag_id]);
        }
        */
        if(!header){
                return (struct dirhtml_template *) node;
        }

        aux = *header;
        while((*aux).next!=NULL){
                aux = (*aux).next;
        }

        (*aux).next = node;
        return (struct dirhtml_template *) node;
}

int mk_dirhtml_tag_get_id(char *tpl_tags[], char *tag)
{
        int i;
        for(i=0; tpl_tags[i]; i++)
        {
                if(strcmp(tpl_tags[i], tag)==0){
                        return i;
                }
        }

        return -1;
}

int mk_dirhtml_template_len(struct dirhtml_template *tpl)
{
        int len=0;
        struct dirhtml_template *aux;

        aux = tpl;
        while(aux){
                len++;
                aux = aux->next;
        }

        return len;
}

struct mk_iov *mk_dirhtml_theme_compose(struct dirhtml_template *template,
                             struct dirhtml_value *values)
{
        /*
         * template = struct { char buf ; int len, int tag }
         * values = struct {int tag, char *value, struct *next}
         */

        struct mk_iov *iov;
        struct dirhtml_template *tpl = template;
        struct dirhtml_value *val = values;

        int tpl_len;

        tpl_len = mk_dirhtml_template_len(template);

        /* we duplicate the lenght in case we get separators */
        iov = mk_iov_create(tpl_len*2, 1); 
        tpl = template;

        while(tpl){
                /* check for dynamic value */
                if(!tpl->buf && tpl->tag_id>=0){
                        val = values;
                        while(val){
                                if(val->tags == tpl->tags && 
                                   val->tag_id == tpl->tag_id){
                                        mk_iov_add_entry(iov, 
                                                 val->value,
                                                 val->len,
                                                 val->sep,
                                                 MK_IOV_NOT_FREE_BUF);
                                        break;
                                }
                                else{
                                        val = val->next;
                                }
                                
                        }
                } 
                /* static */
                else{
                        mk_iov_add_entry(iov, tpl->buf, 
                                         tpl->len, 
                                         mk_iov_none, 
                                         MK_IOV_NOT_FREE_BUF);
                }
                tpl = tpl->next;
        }
        return (struct mk_iov *) iov;
}

struct dirhtml_value *mk_dirhtml_tag_assign(struct dirhtml_value **values,
                                             int tag_id, mk_pointer sep, 
                                            char *value, char **tags)
{
        struct dirhtml_value *check, *aux=0;
        
        aux = mk_mem_malloc(sizeof(struct dirhtml_value));
        if(!aux){
                return NULL;
        }

        aux->tag_id = tag_id;
        aux->value = value;
        aux->sep = sep;
        aux->tags = tags;

        if(value){
                aux->len = strlen(value);
        }
        else{
                aux->len = -1;
        }

        aux->next = NULL;

        if(!values){
                return (struct dirhtml_value *) aux;
        }

        check = *values;
        while((*check).next){
                check = (*check).next;
        }

        (*check).next = aux;


        return (struct dirhtml_value *) aux;
}

char *mk_dirhtml_load_file(char *filename)
{
        char *tmp=0, *data=0;
        unsigned long len;

        m_build_buffer(&tmp, &len, "%s%s",
                       dirhtml_conf->theme_path, filename);

        if(!tmp)
        {
                return NULL;
        }

        data = mk_file_to_buffer(tmp);
        mk_mem_free(tmp);

        if(!data)
        {
                return NULL;
        }

        return (char *) data;
}

int mk_dirhtml_entry_cmp(const void *a, const void *b)
{
        struct mk_f_list * const *f_a = a;
        struct mk_f_list * const *f_b = b;

        return strcmp((*f_a)->name, (*f_b)->name);
}

int mk_dirhtml_send(int fd, struct mk_iov *data)
{
        int n;
        unsigned long len;
        char *buf=0;

        m_build_buffer(&buf, &len, "%x%s", data->total_len, MK_CRLF);

        /* Add chunked information */
        mk_iov_set_entry(data, buf, len, MK_IOV_FREE_BUF, 0);
        n = mk_iov_send(fd, data, MK_IOV_SEND_TO_SOCKET);

        return n;
}

int mk_dirhtml_send_chunked_end(int fd)
{
        char *_end = "0\r\n\r\n";
        int len = 5;

        return write(fd, _end, len);
}

void mk_dirhtml_free_list(struct mk_f_list **toc, unsigned long len)
{
        int i;
        struct mk_f_list *entry;

        for(i=0; i<len; i++)
        {
                entry = toc[i];

                if(entry->type != DT_DIR)
                {
                        mk_mem_free(entry->size);
                }
                mk_mem_free(entry->ft_modif);
                mk_mem_free(entry->info);
                mk_mem_free(entry);
        }
        
        mk_mem_free(toc);
}

int mk_dirhtml_init(struct client_request *cr, struct request *sr)
{
        DIR *dir;
        int i=0;

        mk_pointer sep;

	/* file info */
	unsigned long list_len=0;
        
        struct mk_f_list *file_list, *entry, **toc;
        struct mk_iov *iov_header, *iov_footer, *iov_entry;
        struct dirhtml_value *values_global = 0;
        struct dirhtml_value *values_entry = 0;

        if(!(dir = opendir(sr->real_path.data)))
        {
                return -1;
        }

        file_list = mk_dirhtml_create_list(dir, sr->real_path.data, &list_len);

        /* Building headers */
	sr->headers->status = M_HTTP_OK;
	sr->headers->cgi = SH_CGI;
        sr->headers->breakline = MK_HEADER_BREAKLINE;
        sr->headers->content_type = MK_DIRHTML_DEFAULT_MIME;

	if(sr->protocol==HTTP_PROTOCOL_11)
	{
		sr->headers->transfer_encoding = MK_HEADER_TE_TYPE_CHUNKED;
	}

	/* Sending headers */
	mk_header_send(cr->socket, cr, sr, sr->log);

        /* Creating response template */

        /* Set %_html_title_% */
        values_global = mk_dirhtml_tag_assign(NULL, 0, mk_iov_none,
                                              sr->uri_processed, 
                                              (char **) _tags_global);

        /* Set %_theme_path_% */
        mk_dirhtml_tag_assign(&values_global, 1, mk_iov_none, 
                              dirhtml_conf->theme_path, 
                              (char **) _tags_global);

        /* HTML Header */
        iov_header = mk_dirhtml_theme_compose(mk_dirhtml_tpl_header, 
                                              values_global);

        /* HTML Footer */
        iov_footer = mk_dirhtml_theme_compose(mk_dirhtml_tpl_footer, 
                                              values_global);

        mk_socket_set_cork_flag(cr->socket, TCP_CORK_OFF);
        mk_dirhtml_send(cr->socket, iov_header);

        /* Creating table of contents and sorting */
        toc = mk_mem_malloc(sizeof(struct mk_f_list)*list_len);
        entry = file_list;
        i = 0;
        while(entry)
        {
                toc[i] = entry;
                i++;
                entry = entry->next;
        }        
        qsort(toc, list_len, sizeof(*toc), mk_dirhtml_entry_cmp);

        /* sending TOC */
        for(i=0; i<list_len; i++)
	{
                /* %_target_title_% */
                if(toc[i]->type==DT_DIR){
                        sep = mk_iov_slash;
                }
                else{
                        sep = mk_iov_none;
                }

                /* target title */
                values_entry = mk_dirhtml_tag_assign(NULL, 0, sep,
                                      toc[i]->name, 
                                      (char **) _tags_entry);

                /* target url */
                mk_dirhtml_tag_assign(&values_entry, 1, sep, 
                                      toc[i]->name, 
                                      (char **) _tags_entry);

                /* target name */
                mk_dirhtml_tag_assign(&values_entry, 2, sep, 
                                      toc[i]->name, 
                                      (char **) _tags_entry);

                /* target modification time */
                mk_dirhtml_tag_assign(&values_entry, 3, mk_iov_none, 
                                      toc[i]->ft_modif, 
                                      (char **) _tags_entry);

                /* target size */
                mk_dirhtml_tag_assign(&values_entry, 4, mk_iov_none, 
                                      toc[i]->size, 
                                      (char **) _tags_entry);

                iov_entry = mk_dirhtml_theme_compose(mk_dirhtml_tpl_entry,
                                                     values_entry);

                mk_dirhtml_send(cr->socket, iov_entry);

                mk_mem_free(values_entry);
                mk_iov_free(iov_entry);
        }
        mk_dirhtml_send(cr->socket, iov_footer);
        mk_dirhtml_send_chunked_end(cr->socket);

        closedir(dir);

        mk_mem_free(values_global);
        mk_iov_free(iov_header);
        mk_iov_free(iov_footer);
        mk_dirhtml_free_list(toc, list_len);

        return 0;
}



{'fraction_non_alphanumeric': 0.10551509274351621, 'fraction_numerical': 0.006189778612251635, 'mean_word_length': 1.2224871606749816, 'pattern_counts': {'":': 0, '<': 49, '<?xml version=': 0, '>': 248, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 162, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2076287', 'n_tokens_mistral': 14382, 'n_tokens_neox': 13566, 'n_words': 2986}