Changeset 84

Show
Ignore:
Timestamp:
03/03/2005 05:33:44 PM (4 years ago)
Author:
jan
Message:

added dir-listing.hide-dotfiles and customizable CSS for dirlisting written by smi

Location:
trunk
Files:
2 added
6 modified

Legend:

Unmodified
Added
Removed
  • trunk/doc/Makefile.am

    r65 r84  
    6565        state.dot fastcgi-state.dot fastcgi-state.ps.gz \ 
    6666        spawn-php.sh \ 
     67        newstyle.css \ 
     68        oldstyle.css \ 
    6769        $(DOCS) 
    6870  
  • trunk/doc/configuration.txt

    r41 r84  
    182182  enables virtual directory listings if a directory is requested no 
    183183  index-file was found  
     184 
     185dir-listing.hide-dotfiles 
     186  if enabled, does not list hidden files in directory listings generated 
     187  by the dir-listing option. 
     188 
     189  default: enabled 
     190 
     191dir-listing.external-css 
     192  path to an external css stylesheet for the directory listing 
    184193 
    185194server.follow-symlink 
  • trunk/src/base.h

    r54 r84  
    223223        buffer *error_handler; 
    224224        buffer *server_tag; 
     225        buffer *dirlist_css; 
    225226         
    226227        unsigned short dir_listing; 
     228        unsigned short hide_dotfiles; 
    227229        unsigned short max_keep_alive_requests; 
    228230        unsigned short max_keep_alive_idle; 
  • trunk/src/config.c

    r49 r84  
    7373                { "ssl.ca-file",                 NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER },      /* 38 */ 
    7474                 
     75                { "dir-listing.hide-dotfiles",   NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 39 */ 
     76                { "dir-listing.external-css",    NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },  /* 40 */ 
    7577                 
    7678                { "server.host",                 "use server.bind instead", T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET }, 
     
    116118                s->document_root = buffer_init(); 
    117119                s->dir_listing   = 0; 
     120                s->hide_dotfiles = 1; 
    118121                s->indexfiles    = array_init(); 
    119122                s->mimetypes     = array_init(); 
     
    123126                s->error_handler = buffer_init(); 
    124127                s->server_tag    = buffer_init(); 
     128                s->dirlist_css   = buffer_init(); 
    125129                s->max_keep_alive_requests = 128; 
    126130                s->max_keep_alive_idle = 30; 
     
    168172                cv[35].destination = &(s->allow_http11); 
    169173                cv[38].destination = s->ssl_ca_file; 
     174                cv[39].destination = &(s->hide_dotfiles); 
     175                cv[40].destination = s->dirlist_css; 
    170176                 
    171177                srv->config_storage[i] = s; 
     
    189195        PATCH(document_root); 
    190196        PATCH(dir_listing); 
     197        PATCH(dirlist_css); 
     198        PATCH(hide_dotfiles); 
    191199        PATCH(indexfiles); 
    192200        PATCH(max_keep_alive_requests); 
     
    234242                        } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.dir-listing"))) { 
    235243                                PATCH(dir_listing); 
     244                        } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("dir-listing.hide-dotfiles"))) { 
     245                                PATCH(hide_dotfiles); 
     246                        } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("dir-listing.external-css"))) { 
     247                                PATCH(dirlist_css); 
    236248                        } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.error-handler-404"))) { 
    237249                                PATCH(error_handler); 
  • trunk/src/response.c

    r79 r84  
    459459 
    460460typedef struct { 
    461         char **ptr; 
    462         size_t used; 
    463         size_t size; 
    464 } string_buffer; 
     461        size_t  namelen; 
     462        time_t  mtime; 
     463        off_t   size; 
     464} dirls_entry_t; 
     465 
     466typedef struct { 
     467        dirls_entry_t **ent; 
     468        int used; 
     469        int size; 
     470} dirls_list_t; 
     471 
     472#define DIRLIST_ENT_NAME(ent)   (char*) ent + sizeof(dirls_entry_t) 
     473#define DIRLIST_BLOB_SIZE               16 
     474 
     475/* simple combsort algorithm */ 
     476static void http_dirls_sort(dirls_entry_t **ent, int num) { 
     477        int gap = num; 
     478        int i, j; 
     479        int swapped; 
     480        dirls_entry_t *tmp; 
     481 
     482        do { 
     483                gap = (gap * 10) / 13; 
     484                if (gap == 9 || gap == 10) 
     485                        gap = 11; 
     486                if (gap < 1) 
     487                        gap = 1; 
     488                swapped = 0; 
     489 
     490                for (i = 0; i < num - gap; i++) { 
     491                        j = i + gap; 
     492                        if (strcmp(DIRLIST_ENT_NAME(ent[i]), DIRLIST_ENT_NAME(ent[j])) > 0) { 
     493                                tmp = ent[i]; 
     494                                ent[i] = ent[j]; 
     495                                ent[j] = tmp; 
     496                                swapped = 1; 
     497                        } 
     498                } 
     499 
     500        } while (gap > 1 || swapped); 
     501} 
     502 
     503/* buffer must be able to hold "999.9K" 
     504 * conversion is simple but not perfect 
     505 */ 
     506static int http_list_directory_sizefmt(char *buf, off_t size) { 
     507        const char unit[] = "KMGTPE";   /* Kilo, Mega, Tera, Peta, Exa */ 
     508        const char *u = unit - 1;               /* u will always increment at least once */ 
     509        int remain; 
     510        char *out = buf; 
     511 
     512        if (size < 100) 
     513                size += 99; 
     514        if (size < 100) 
     515                size = 0; 
     516 
     517        while (1) { 
     518                remain = (int) size & 1023; 
     519                size >>= 10; 
     520                u++; 
     521                if ((size & (~0 ^ 1023)) == 0) 
     522                        break; 
     523        } 
     524 
     525        remain /= 100; 
     526        if (remain > 9) 
     527                remain = 9; 
     528        if (size > 999) { 
     529                size   = 0; 
     530                remain = 9; 
     531                u++; 
     532        } 
     533 
     534        out   += ltostr(out, size); 
     535        out[0] = '.'; 
     536        out[1] = remain + '0'; 
     537        out[2] = *u; 
     538        out[3] = '\0'; 
     539 
     540        return (out + 3 - buf); 
     541} 
     542 
     543static void http_list_directory_header(buffer *out, connection *con) { 
     544        BUFFER_APPEND_STRING_CONST(out, 
     545                "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n" 
     546                "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\">\n" 
     547                "<head>\n" 
     548                "<title>Index of " 
     549        ); 
     550        buffer_append_string_html_encoded(out, con->uri.path->ptr); 
     551        BUFFER_APPEND_STRING_CONST(out, "</title>\n"); 
     552 
     553        if (con->conf.dirlist_css->used > 1) { 
     554                BUFFER_APPEND_STRING_CONST(out, "<link rel=\"stylesheet\" type=\"text/css\" href=\""); 
     555                buffer_append_string_buffer(out, con->conf.dirlist_css); 
     556                BUFFER_APPEND_STRING_CONST(out, "\" />\n"); 
     557        } else { 
     558                BUFFER_APPEND_STRING_CONST(out, 
     559                        "<style type=\"text/css\">\n" 
     560                        "a, a:active {text-decoration: none; color: blue;}\n" 
     561                        "a:visited {color: #48468F;}\n" 
     562                        "a:hover, a:focus {text-decoration: underline; color: red;}\n" 
     563                        "body {background-color: #F5F5F5;}\n" 
     564                        "h2 {margin-bottom: 12px;}\n" 
     565                        "table {margin-left: 12px;}\n" 
     566                        "th, td {" 
     567                        " font-family: \"Courier New\", Courier, monospace;" 
     568                        " font-size: 10pt;" 
     569                        " text-align: left;" 
     570                        "}\n" 
     571                        "th {" 
     572                        " font-weight: bold;" 
     573                        " padding-right: 14px;" 
     574                        " padding-bottom: 3px;" 
     575                        "}\n" 
     576                ); 
     577                BUFFER_APPEND_STRING_CONST(out, 
     578                        "td {padding-right: 14px;}\n" 
     579                        "td.s, th.s {text-align: right;}\n" 
     580                        "div.list {" 
     581                        " background-color: white;" 
     582                        " border-top: 1px solid #646464;" 
     583                        " border-bottom: 1px solid #646464;" 
     584                        " padding-top: 10px;" 
     585                        " padding-bottom: 14px;" 
     586                        "}\n" 
     587                        "div.foot {" 
     588                        " font-family: \"Courier New\", Courier, monospace;" 
     589                        " font-size: 10pt;" 
     590                        " color: #787878;" 
     591                        " padding-top: 4px;" 
     592                        "}\n" 
     593                        "</style>\n" 
     594                ); 
     595        } 
     596 
     597        BUFFER_APPEND_STRING_CONST(out, "</head>\n<body>\n<h2>Index of "); 
     598        buffer_append_string_html_encoded(out, con->uri.path->ptr); 
     599        BUFFER_APPEND_STRING_CONST(out, 
     600                "</h2>\n" 
     601                "<div class=\"list\">\n" 
     602                "<table cellpadding=\"0\" cellspacing=\"0\">\n" 
     603                "<thead>" 
     604                "<tr>" 
     605                        "<th class=\"n\">Name</th>" 
     606                        "<th class=\"m\">Last Modified</th>" 
     607                        "<th class=\"s\">Size</th>" 
     608                        "<th class=\"t\">Type</th>" 
     609                "</tr>" 
     610                "</thead>\n" 
     611                "<tbody>\n" 
     612                "<tr>" 
     613                        "<td class=\"n\"><a href=\"../\">Parent Directory</a>/</td>" 
     614                        "<td class=\"m\">&nbsp;</td>" 
     615                        "<td class=\"s\">- &nbsp;</td>" 
     616                        "<td class=\"t\">Directory</td>" 
     617                "</tr>\n" 
     618        ); 
     619} 
     620 
     621static void http_list_directory_footer(buffer *out, connection *con) { 
     622        BUFFER_APPEND_STRING_CONST(out, 
     623                "</tbody>\n" 
     624                "</table>\n" 
     625                "</div>\n" 
     626                "<div class=\"foot\">" 
     627        ); 
     628 
     629        if (buffer_is_empty(con->conf.server_tag)) { 
     630                BUFFER_APPEND_STRING_CONST(out, PACKAGE_NAME "/" PACKAGE_VERSION); 
     631        } else { 
     632                buffer_append_string_buffer(out, con->conf.server_tag); 
     633        } 
     634 
     635        BUFFER_APPEND_STRING_CONST(out, 
     636                "</div>\n" 
     637                "</body>\n" 
     638                "</html>\n" 
     639        ); 
     640} 
    465641 
    466642static int http_list_directory(server *srv, connection *con, buffer *dir) { 
    467         DIR *d; 
     643        DIR *dp; 
     644        buffer *out; 
    468645        struct dirent *dent; 
    469         buffer *b, *date_buf; 
    470         string_buffer *sb; 
    471         size_t i; 
    472  
    473         if (NULL == (d = opendir(dir->ptr))) { 
     646        struct stat st; 
     647        char *path, *path_file; 
     648        int i; 
     649        int hide_dotfiles = con->conf.hide_dotfiles; 
     650        dirls_list_t dirs, files, *list; 
     651        dirls_entry_t *tmp; 
     652        char sizebuf[sizeof("999.9K")]; 
     653        char datebuf[sizeof("2005-Jan-01 22:23:24")]; 
     654        size_t k; 
     655        const char *content_type; 
     656#ifdef HAVE_XATTR 
     657        char attrval[128]; 
     658        int attrlen; 
     659#endif 
     660#ifdef HAVE_LOCALTIME_R 
     661        struct tm tm; 
     662#endif 
     663 
     664        i = dir->used - 1; 
     665        if (i <= 0) return -1; 
     666         
     667        path = malloc(i + NAME_MAX + 1); 
     668        assert(path); 
     669        strcpy(path, dir->ptr); 
     670        path_file = path + i; 
     671 
     672        if (NULL == (dp = opendir(path))) { 
    474673                log_error_write(srv, __FILE__, __LINE__, "sbs",  
    475674                        "opendir failed:", dir, strerror(errno)); 
     675 
     676                free(path); 
    476677                return -1; 
    477678        } 
    478          
    479         b = chunkqueue_get_append_buffer(con->write_queue); 
    480         buffer_copy_string(b,  
    481                            "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n" 
    482                            "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n" 
    483                            "         \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n" 
    484                            "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n" 
    485                            " <head>\n" 
    486                            "  <title>Directory Listing for "); 
    487         buffer_append_string_html_encoded(b, con->uri.path->ptr); 
    488          
    489         BUFFER_APPEND_STRING_CONST(b, 
    490                             "</title>\n" 
    491                             "  <style type=\"text/css\">\n"); 
    492          
    493         BUFFER_APPEND_STRING_CONST(b, 
    494                             "th.dirlisting { background-color: black; color: white; }\n" 
    495                             "table.dirlisting { border: black solid thin; }\n" 
    496                             "td.dirlisting { background-color: #f0f0f0; }\n" 
    497                             "td.dirlistingnumber { background-color: #f0f0f0; text-align: right }\n" 
    498                             ); 
    499          
    500         BUFFER_APPEND_STRING_CONST(b, 
    501                             "  </style>\n" 
    502                             " </head>\n" 
    503                             " <body>\n" 
    504                             "  <h2>Directory Listing for "); 
    505         buffer_append_string_html_encoded(b, con->uri.path->ptr); 
    506         BUFFER_APPEND_STRING_CONST(b,"</h2>\n  <table class='dirlisting'>\n"); 
    507         BUFFER_APPEND_STRING_CONST(b,"   <tr class='dirlisting'><th class='dirlisting'>date</th><th class='dirlisting'>size</th><th class='dirlisting'>type</th><th class='dirlisting'>name</th></tr>\n"); 
    508          
    509          
    510         /* allocate memory */ 
    511         sb = calloc(1, sizeof(*sb)); 
    512         assert(sb); 
    513          
    514         sb->ptr = NULL; 
    515         sb->used = 0; 
    516         sb->size = 0; 
    517          
    518         while(NULL != (dent = readdir(d))) { 
    519                 size_t j; 
    520                 if (sb->size == 0) { 
    521                         sb->size = 4; 
    522                         sb->ptr = malloc(sizeof(*sb->ptr) * sb->size); 
    523                         assert(sb->ptr); 
    524                 } else if (sb->used == sb->size) { 
    525                         sb->size += 4; 
    526                         sb->ptr = realloc(sb->ptr, sizeof(*sb->ptr) * sb->size); 
    527                         assert(sb->ptr); 
    528                 } 
    529                  
    530                 for (i = 0; i < sb->used; i++) { 
    531                         if (strcmp(dent->d_name, sb->ptr[i]) < 0) { 
    532                                 break; 
    533                         } 
    534                 } 
    535                  
    536                 /* <left><right> */ 
    537                 /* <left>[i]<right> */ 
    538                  
    539                 for (j = sb->used - 1; sb->used && j >= i && (j+1) > 0; j--) { 
    540                         sb->ptr[j + 1] = sb->ptr[j]; 
    541                 } 
    542                 sb->ptr[i] = strdup(dent->d_name); 
    543                  
    544                 sb->used++; 
    545                  
    546         } 
    547          
    548         closedir(d); 
    549          
    550         date_buf = buffer_init(); 
    551         buffer_prepare_copy(date_buf, 22); 
    552         for (i = 0; i < sb->used; i++) { 
    553                 struct stat st; 
    554                 struct tm tm; 
    555                 size_t s_len = strlen(sb->ptr[i]); 
    556                  
    557                  
    558                 buffer_copy_string(srv->tmp_buf, dir->ptr); 
    559                 buffer_append_string(srv->tmp_buf, sb->ptr[i]); 
    560                  
    561                 if (0 != stat(srv->tmp_buf->ptr, &st)) { 
    562                         free(sb->ptr[i]); 
     679 
     680        dirs.ent   = (dirls_entry_t**) malloc(sizeof(dirls_entry_t*) * DIRLIST_BLOB_SIZE); 
     681        assert(dirs.ent); 
     682        dirs.size  = DIRLIST_BLOB_SIZE; 
     683        dirs.used  = 0; 
     684        files.ent  = (dirls_entry_t**) malloc(sizeof(dirls_entry_t*) * DIRLIST_BLOB_SIZE); 
     685        assert(files.ent); 
     686        files.size = DIRLIST_BLOB_SIZE; 
     687        files.used = 0; 
     688 
     689        while ((dent = readdir(dp)) != NULL) { 
     690                if (dent->d_name[0] == '.') { 
     691                        if (hide_dotfiles) 
     692                                continue; 
     693                        if (dent->d_name[1] == '\0') 
     694                                continue; 
     695                        if (dent->d_name[1] == '.' && dent->d_name[2] == '\0') 
     696                                continue; 
     697                } 
     698 
     699                /* NOTE: the manual says, d_name is never more than NAME_MAX 
     700                 *       so this should actually not be a buffer-overflow-risk 
     701                 */ 
     702                i = strlen(dent->d_name); 
     703                if (i > NAME_MAX) 
    563704                        continue; 
    564                 } 
    565                  
    566                 BUFFER_APPEND_STRING_CONST(b, 
    567                                     "   <tr><td class='dirlisting'>"); 
    568                  
     705                memcpy(path_file, dent->d_name, i + 1); 
     706                if (stat(path, &st) != 0) 
     707                        continue; 
     708 
     709                list = &files; 
     710                if (S_ISDIR(st.st_mode)) 
     711                        list = &dirs; 
     712 
     713                if (list->used == list->size) { 
     714                        list->size += DIRLIST_BLOB_SIZE; 
     715                        list->ent   = (dirls_entry_t**) realloc(list->ent, sizeof(dirls_entry_t*) * list->size); 
     716                        assert(list->ent); 
     717                } 
     718 
     719                tmp = (dirls_entry_t*) malloc(sizeof(dirls_entry_t) + 1 + i); 
     720                tmp->mtime = st.st_mtime; 
     721                tmp->size  = st.st_size; 
     722                tmp->namelen = i; 
     723                memcpy(DIRLIST_ENT_NAME(tmp), dent->d_name, i + 1); 
     724 
     725                list->ent[list->used++] = tmp; 
     726        } 
     727        closedir(dp); 
     728 
     729        if (dirs.used) http_dirls_sort(dirs.ent, dirs.used); 
     730 
     731        if (files.used) http_dirls_sort(files.ent, files.used); 
     732 
     733        out = chunkqueue_get_append_buffer(con->write_queue); 
     734        BUFFER_COPY_STRING_CONST(out, "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n"); 
     735        http_list_directory_header(out, con); 
     736 
     737        /* directories */ 
     738        for (i = 0; i < dirs.used; i++) { 
     739                tmp = dirs.ent[i]; 
     740 
    569741#ifdef HAVE_LOCALTIME_R 
    570                 /* localtime_r is faster */ 
    571                 localtime_r(&(st.st_mtime), &tm); 
    572                  
    573                 date_buf->used = strftime(date_buf->ptr, date_buf->size - 1, "%Y-%m-%d %H:%M:%S", &tm); 
     742                localtime_r(&(tmp->mtime), &tm); 
     743                strftime(datebuf, sizeof(datebuf), "%Y-%b-%d %H:%M:%S", &tm); 
    574744#else 
    575                 date_buf->used = strftime(date_buf->ptr, date_buf->size - 1, "%Y-%m-%d %H:%M:%S", localtime(&(st.st_mtime))); 
     745                strftime(datebuf, sizeof(datebuf), "%Y-%b-%d %H:%M:%S", localtime(&(tmp->mtime))); 
    576746#endif 
    577                 date_buf->used++; 
    578                  
    579                 buffer_append_string_buffer(b, date_buf); 
    580                 BUFFER_APPEND_STRING_CONST(b, 
    581                                     "</td><td class='dirlistingnumber'>"); 
    582                  
    583                 buffer_append_off_t(b, st.st_size); 
    584                  
    585                 /* mime type */ 
    586                 BUFFER_APPEND_STRING_CONST(b, 
    587                                     "</td><td class='dirlisting'>"); 
    588                  
    589                 if (S_ISDIR(st.st_mode)) { 
    590                         buffer_append_string_rfill(b, "directory", 28); 
    591                 } else { 
    592                         size_t k; 
    593                         unsigned short have_content_type = 0; 
     747 
     748                BUFFER_APPEND_STRING_CONST(out, "<tr><td class=\"n\"><a href=\""); 
     749                buffer_append_string_url_encoded(out, DIRLIST_ENT_NAME(tmp)); 
     750                BUFFER_APPEND_STRING_CONST(out, "/\">"); 
     751                buffer_append_string_html_encoded(out, DIRLIST_ENT_NAME(tmp)); 
     752                BUFFER_APPEND_STRING_CONST(out, "</a>/</td><td class=\"m\">"); 
     753                buffer_append_string_len(out, datebuf, sizeof(datebuf) - 1); 
     754                BUFFER_APPEND_STRING_CONST(out, "</td><td class=\"s\">- &nbsp;</td><td class=\"t\">Directory</td></tr>\n"); 
     755 
     756                free(tmp); 
     757        } 
     758 
     759        /* files */ 
     760        for (i = 0; i < files.used; i++) { 
     761                tmp = files.ent[i]; 
    594762 
    595763#ifdef HAVE_XATTR 
    596                         char attrval[128]; 
    597                         int attrlen = sizeof(attrval) - 1; 
    598                          
    599                         if(con->conf.use_xattr && 0 == attr_get(srv->tmp_buf->ptr, "Content-Type", attrval, &attrlen, 0)) { 
    600                                 attrval[attrlen] = 0; 
    601                                 buffer_append_string_rfill(b, attrval, 28); 
    602                                 have_content_type = 1; 
    603                         } 
     764                content_type = NULL; 
     765                if (con->conf.use_xattr) { 
     766                        memcpy(path_file, DIRLIST_ENT_NAME(tmp), tmp->namelen + 1); 
     767                        attrlen = sizeof(attrval) - 1; 
     768                        if (attr_get(path, "Content-Type", attrval, &attrlen, 0) == 0) { 
     769                                attrval[attrlen] = '\0'; 
     770                                content_type = attrval; 
     771                        } 
     772                } 
     773                if (content_type == NULL) { 
     774#else 
     775                if (1) { 
    604776#endif 
    605                          
    606                         if(!have_content_type) { 
    607                                 for (k = 0; k < con->conf.mimetypes->used; k++) { 
    608                                         data_string *ds = (data_string *)con->conf.mimetypes->data[k]; 
    609                                         size_t ct_len; 
    610                                          
    611                                         if (ds->key->used == 0) continue; 
    612                                          
    613                                         ct_len = ds->key->used - 1; 
    614                                          
    615                                         if (s_len < ct_len) continue; 
    616                                          
    617                                         if (0 == strncmp(sb->ptr[i] + s_len - ct_len, ds->key->ptr, ct_len)) { 
    618                                                 buffer_append_string_rfill(b, ds->value->ptr, 28); 
    619                                                 break; 
    620                                         } 
     777                        content_type = "application/octet-stream"; 
     778                        for (k = 0; k < con->conf.mimetypes->used; k++) { 
     779                                data_string *ds = (data_string *)con->conf.mimetypes->data[k]; 
     780                                size_t ct_len; 
     781 
     782                                if (ds->key->used == 0) 
     783                                        continue; 
     784 
     785                                ct_len = ds->key->used - 1; 
     786                                if (tmp->namelen < ct_len) 
     787                                        continue; 
     788 
     789                                if (0 == strncmp(DIRLIST_ENT_NAME(tmp) + tmp->namelen - ct_len, ds->key->ptr, ct_len)) { 
     790                                        content_type = ds->value->ptr; 
     791                                        break; 
    621792                                } 
    622                                  
    623                                 if (k == con->conf.mimetypes->used) { 
    624                                         buffer_append_string_rfill(b, "application/octet-stream", 28); 
    625                                 } 
    626                         } 
    627                 } 
    628                  
    629                 /* URL */ 
    630                 BUFFER_APPEND_STRING_CONST(b,"</td><td class='dirlisting'><a href=\""); 
    631                 /* URL encode */ 
    632                 buffer_append_string_url_encoded(b, sb->ptr[i]); 
    633                 if (S_ISDIR(st.st_mode)) { 
    634                         BUFFER_APPEND_STRING_CONST(b,"/"); 
    635                 } 
    636                 BUFFER_APPEND_STRING_CONST(b,"\">"); 
    637                 /* HTML encode */ 
    638                 buffer_append_string_html_encoded(b, sb->ptr[i]); 
    639                 if (S_ISDIR(st.st_mode)) { 
    640                         BUFFER_APPEND_STRING_CONST(b,"/"); 
    641                 } 
    642                 BUFFER_APPEND_STRING_CONST(b,"</a></td></tr>\n"); 
    643                          
    644                  
    645                 free(sb->ptr[i]); 
    646         } 
    647          
    648         buffer_free(date_buf); 
    649         free(sb->ptr); 
    650         free(sb); 
    651          
     793                        } 
     794                } 
     795 
     796#ifdef HAVE_LOCALTIME_R 
     797                localtime_r(&(tmp->mtime), &tm); 
     798                strftime(datebuf, sizeof(datebuf), "%Y-%b-%d %H:%M:%S", &tm); 
     7