1
0
mirror of https://github.com/rkd77/elinks.git synced 2024-09-28 03:06:20 -04:00

SMB directory listing looks like normal directory listing.

This commit is contained in:
Witold Filipczyk 2010-09-06 18:38:36 +02:00 committed by Witold Filipczyk
parent a9898eab51
commit 00ebb8ef4b
3 changed files with 359 additions and 379 deletions

View File

@ -34,24 +34,10 @@
#include "protocol/smb/smb.h"
#include "protocol/uri.h"
#include "util/conv.h"
#include "util/file.h"
#include "util/memory.h"
#include "util/string.h"
/* These options are not used. */
#if 0
struct option_info smb_options[] = {
INIT_OPT_TREE("protocol", N_("SMB"),
"smb", 0,
N_("SAMBA specific options.")),
INIT_OPT_STRING("protocol.smb", N_("Credentials"),
"credentials", 0, "",
N_("Credentials file passed to smbclient via -A option.")),
NULL_OPTION_INFO,
};
#endif
struct module smb_protocol_module = struct_module(
/* name: */ N_("SMB"),
/* options: */ NULL,
@ -81,179 +67,167 @@ smb_error(struct connection_state error)
exit(1);
}
static int
compare(const void *a, const void *b)
/** First information such as permissions is gathered for each directory entry.
* All entries are then sorted. */
static struct directory_entry *
get_smb_directory_entries(int dir, struct string *prefix)
{
const struct smbc_dirent **da = (const struct smbc_dirent **)a;
const struct smbc_dirent **db = (const struct smbc_dirent **)b;
int res = (*da)->smbc_type - (*db)->smbc_type;
if (res) {
return res;
}
return strcmp((*da)->name, (*db)->name);
}
static void
smb_add_link(struct string *string, const struct smbc_dirent *entry,
const unsigned char *text, const unsigned char dircolor[])
{
struct string uri_string;
if (!init_string(&uri_string)) return;
encode_uri_string(&uri_string, entry->name, entry->namelen, 0);
add_to_string(string, "<a href=\"");
add_html_to_string(string, uri_string.source, uri_string.length);
done_string(&uri_string);
add_to_string(string, "\">");
if (*dircolor) {
add_to_string(string, "<font color=\"");
add_to_string(string, dircolor);
add_to_string(string, "\"><b>");
}
add_html_to_string(string, entry->name, entry->namelen);
if (*dircolor) {
add_to_string(string, "</b></font>");
}
add_to_string(string, "</a>");
if (text) add_to_string(string, text);
}
static void
display_entry(const struct smbc_dirent *entry, const unsigned char dircolor[])
{
static const unsigned char zero = '\0';
struct string string;
if (!init_string(&string)) return;
switch (entry->smbc_type) {
case SMBC_WORKGROUP:
smb_add_link(&string, entry, " WORKGROUP ", dircolor);
break;
case SMBC_SERVER:
smb_add_link(&string, entry, " SERVER ", dircolor);
if (entry->comment) {
add_html_to_string(&string, entry->comment, entry->commentlen);
}
break;
case SMBC_FILE_SHARE:
smb_add_link(&string, entry, " FILE SHARE ", dircolor);
if (entry->comment) {
add_html_to_string(&string, entry->comment, entry->commentlen);
}
break;
case SMBC_PRINTER_SHARE:
add_html_to_string(&string, entry->name, entry->namelen);
add_to_string(&string, " PRINTER ");
if (entry->comment) {
add_html_to_string(&string, entry->comment, entry->commentlen);
}
break;
case SMBC_COMMS_SHARE:
add_bytes_to_string(&string, entry->name, entry->namelen);
add_to_string(&string, " COMM");
break;
case SMBC_IPC_SHARE:
add_bytes_to_string(&string, entry->name, entry->namelen);
add_to_string(&string, " IPC");
break;
case SMBC_DIR:
smb_add_link(&string, entry, NULL, dircolor);
break;
case SMBC_LINK:
smb_add_link(&string, entry, " Link", &zero);
break;
case SMBC_FILE:
smb_add_link(&string, entry, NULL, &zero);
break;
default:
/* unknown type */
break;
}
fputs(string.source, data_out);
fputc('\n', data_out);
done_string(&string);
}
static void
sort_and_display_entries(int dir, const unsigned char dircolor[])
{
struct smbc_dirent *fentry, **table = NULL;
struct directory_entry *entries = NULL;
int size = 0;
int i;
struct smbc_dirent *entry;
while ((fentry = smbc_readdir(dir))) {
struct smbc_dirent **new_table, *new_entry;
unsigned int commentlen = fentry->commentlen;
unsigned int namelen = fentry->namelen;
while ((entry = smbc_readdir(dir))) {
struct stat st, *stp;
struct directory_entry *new_entries;
struct string attrib;
struct string name;
if (!strcmp(fentry->name, "."))
if (!strcmp(entry->name, "."))
continue;
/* In libsmbclient 3.0.10, @smbc_dirent.namelen and
* @smbc_dirent.commentlen include the null characters
* (tested with GDB). In libsmbclient 3.0.24, they
* don't. This is related to Samba bug 3030. Adjust
* the lengths to exclude the null characters, so that
* other code need not care.
*
* Make all changes to local copies rather than
* directly to *@fentry, so that there's no chance of
* ELinks messing up whatever mechanism libsmbclient
* will use to free @fentry. */
if (commentlen > 0 && fentry->comment[commentlen - 1] == '\0')
commentlen--;
if (namelen > 0 && fentry->name[namelen - 1] == '\0')
namelen--;
new_entries = mem_realloc(entries, (size + 2) * sizeof(*new_entries));
if (!new_entries) continue;
entries = new_entries;
/* libsmbclient seems to place the struct smbc_dirent,
* the name string, and the comment string all in one
* block of memory, which then is smbc_dirent.dirlen
* bytes long. This has however not been really
* documented, so ELinks should not assume copying
* fentry->dirlen bytes will copy the comment too.
* Yet, it would be wasteful to copy both dirlen bytes
* and then the comment string separately. What we do
* here is ignore fentry->dirlen and recompute the
* size based on namelen. */
new_entry = (struct smbc_dirent *)
memacpy((const unsigned char *) fentry,
offsetof(struct smbc_dirent, name)
+ namelen); /* memacpy appends '\0' */
if (!new_entry)
if (!init_string(&attrib)) {
continue;
new_entry->namelen = namelen;
new_entry->commentlen = commentlen;
if (fentry->comment)
new_entry->comment = memacpy(fentry->comment, commentlen);
if (!new_entry->comment)
new_entry->commentlen = 0;
}
new_table = mem_realloc(table, (size + 1) * sizeof(*table));
if (!new_table)
if (!init_string(&name)) {
done_string(&attrib);
continue;
table = new_table;
table[size] = new_entry;
}
add_string_to_string(&name, prefix);
add_to_string(&name, entry->name);
stp = (smbc_stat(name.source, &st)) ? NULL : &st;
stat_type(&attrib, stp);
stat_mode(&attrib, stp);
stat_links(&attrib, stp);
stat_user(&attrib, stp);
stat_group(&attrib, stp);
stat_size(&attrib, stp);
stat_date(&attrib, stp);
entries[size].name = stracpy(entry->name);
entries[size].attrib = attrib.source;
done_string(&name);
size++;
}
/* If size==0, then table==NULL. According to ISO/IEC 9899:1999
* 7.20.5p1, the NULL must not be given to qsort. */
if (size > 0)
qsort(table, size, sizeof(*table), compare);
smbc_closedir(dir);
for (i = 0; i < size; i++) {
display_entry(table[i], dircolor);
if (!size) {
/* We may have allocated space for entries but added none. */
mem_free_if(entries);
return NULL;
}
qsort(entries, size, sizeof(*entries), compare_dir_entries);
memset(&entries[size], 0, sizeof(*entries));
return entries;
}
static void
smb_directory(int dir, struct uri *uri)
add_smb_dir_entry(struct directory_entry *entry, struct string *page,
int pathlen, unsigned char *dircolor)
{
unsigned char *lnk = NULL;
struct string html_encoded_name;
struct string uri_encoded_name;
if (!init_string(&html_encoded_name)) return;
if (!init_string(&uri_encoded_name)) {
done_string(&html_encoded_name);
return;
}
encode_uri_string(&uri_encoded_name, entry->name + pathlen, -1, 1);
add_html_to_string(&html_encoded_name, entry->name + pathlen,
strlen(entry->name) - pathlen);
/* add_to_string(&fragment, &fragmentlen, " "); */
add_html_to_string(page, entry->attrib, strlen(entry->attrib));
add_to_string(page, "<a href=\"");
add_string_to_string(page, &uri_encoded_name);
if (entry->attrib[0] == 'd') {
add_char_to_string(page, '/');
#ifdef FS_UNIX_SOFTLINKS
} else if (entry->attrib[0] == 'l') {
struct stat st;
unsigned char buf[MAX_STR_LEN];
int readlen = readlink(entry->name, buf, MAX_STR_LEN);
if (readlen > 0 && readlen != MAX_STR_LEN) {
buf[readlen] = '\0';
lnk = straconcat(" -> ", buf, (unsigned char *) NULL);
}
if (!stat(entry->name, &st) && S_ISDIR(st.st_mode))
add_char_to_string(page, '/');
#endif
}
add_to_string(page, "\">");
if (entry->attrib[0] == 'd' && *dircolor) {
/* The <b> is for the case when use_document_colors is off. */
string_concat(page, "<font color=\"", dircolor, "\"><b>",
(unsigned char *) NULL);
}
add_string_to_string(page, &html_encoded_name);
done_string(&uri_encoded_name);
done_string(&html_encoded_name);
if (entry->attrib[0] == 'd' && *dircolor) {
add_to_string(page, "</b></font>");
}
add_to_string(page, "</a>");
if (lnk) {
add_html_to_string(page, lnk, strlen(lnk));
mem_free(lnk);
}
add_char_to_string(page, '\n');
}
/* First information such as permissions is gathered for each directory entry.
* Finally the sorted entries are added to the @data->fragment one by one. */
static void
add_smb_dir_entries(struct directory_entry *entries, unsigned char *dirpath,
struct string *page)
{
unsigned char dircolor[8];
int i;
/* Setup @dircolor so it's easy to check if we should color dirs. */
if (get_opt_bool("document.browse.links.color_dirs", NULL)) {
color_to_string(get_opt_color("document.colors.dirs", NULL),
(unsigned char *) &dircolor);
} else {
dircolor[0] = 0;
}
for (i = 0; entries[i].name; i++) {
add_smb_dir_entry(&entries[i], page, 0, dircolor);
mem_free(entries[i].attrib);
mem_free(entries[i].name);
}
/* We may have allocated space for entries but added none. */
mem_free_if(entries);
}
static void
smb_directory(int dir, struct string *prefix, struct uri *uri)
{
struct string buf;
unsigned char dircolor[8] = "";
struct directory_entry *entries;
if (!is_in_state(init_directory_listing(&buf, uri), S_OK)) {
smb_error(connection_state(S_OUT_OF_MEM));
@ -262,17 +236,12 @@ smb_directory(int dir, struct uri *uri)
fputs("text/html", header_out);
fclose(header_out);
entries = get_smb_directory_entries(dir, prefix);
add_smb_dir_entries(entries, NULL, &buf);
add_to_string(&buf, "</pre><hr/></body></html>\n");
fputs(buf.source, data_out);
fputc('\n', data_out);
if (get_opt_bool("document.browse.links.color_dirs", NULL)) {
color_to_string(get_opt_color("document.colors.dirs", NULL),
dircolor);
}
sort_and_display_entries(dir, dircolor);
fputs("</pre><hr/></body></html>\n", data_out);
smbc_closedir(dir);
done_string(&buf);
exit(0);
}
@ -329,9 +298,16 @@ do_smb(struct connection *conn)
smb_error(connection_state_for_errno(errno));
};
dir = smbc_opendir(url);
if (dir >= 0) {
smb_directory(dir, conn->uri);
struct string prefix;
init_string(&prefix);
add_to_string(&prefix, url);
add_char_to_string(&prefix, '/');
smb_directory(dir, &prefix, conn->uri);
done_string(&prefix);
} else {
const int errno_from_opendir = errno;
char buf[READ_SIZE];

View File

@ -283,206 +283,8 @@ safe_mkstemp(unsigned char *template)
return fd;
}
/** @name The stat_* functions set the various attributes for directory entries.
* @{ */
static inline void
stat_type(struct string *string, struct stat *stp)
{
unsigned char c = '?';
if (stp) {
if (S_ISDIR(stp->st_mode)) c = 'd';
else if (S_ISREG(stp->st_mode)) c = '-';
#ifdef S_ISBLK
else if (S_ISBLK(stp->st_mode)) c = 'b';
#endif
#ifdef S_ISCHR
else if (S_ISCHR(stp->st_mode)) c = 'c';
#endif
#ifdef S_ISFIFO
else if (S_ISFIFO(stp->st_mode)) c = 'p';
#endif
#ifdef S_ISLNK
else if (S_ISLNK(stp->st_mode)) c = 'l';
#endif
#ifdef S_ISSOCK
else if (S_ISSOCK(stp->st_mode)) c = 's';
#endif
#ifdef S_ISNWK
else if (S_ISNWK(stp->st_mode)) c = 'n';
#endif
}
add_char_to_string(string, c);
}
static inline void
stat_mode(struct string *string, struct stat *stp)
{
#ifdef FS_UNIX_RIGHTS
unsigned char rwx[10] = "---------";
if (stp) {
mode_t mode = stp->st_mode;
unsigned int shift;
/* Set permissions attributes for user, group and other */
for (shift = 0; shift <= 6; shift += 3) {
mode_t m = mode << shift;
if (m & S_IRUSR) rwx[shift + 0] = 'r';
if (m & S_IWUSR) rwx[shift + 1] = 'w';
if (m & S_IXUSR) rwx[shift + 2] = 'x';
}
#ifdef S_ISUID
if (mode & S_ISUID)
rwx[2] = (mode & S_IXUSR) ? 's' : 'S';
#endif
#ifdef S_ISGID
if (mode & S_ISGID)
rwx[5] = (mode & S_IXGRP) ? 's' : 'S';
#endif
#ifdef S_ISVTX
if (mode & S_ISVTX)
rwx[8] = (mode & S_IXOTH) ? 't' : 'T';
#endif
}
add_to_string(string, rwx);
#endif
add_char_to_string(string, ' ');
}
static inline void
stat_links(struct string *string, struct stat *stp)
{
#ifdef FS_UNIX_HARDLINKS
if (!stp) {
add_to_string(string, " ");
} else {
unsigned char lnk[64];
ulongcat(lnk, NULL, stp->st_nlink, 3, ' ');
add_to_string(string, lnk);
add_char_to_string(string, ' ');
}
#endif
}
static inline void
stat_user(struct string *string, struct stat *stp)
{
#ifdef FS_UNIX_USERS
static unsigned char last_user[64];
static int last_uid = -1;
if (!stp) {
add_to_string(string, " ");
return;
}
if (stp->st_uid != last_uid) {
struct passwd *pwd = getpwuid(stp->st_uid);
if (!pwd || !pwd->pw_name)
/* ulongcat() can't pad from right. */
sprintf(last_user, "%-8d", (int) stp->st_uid);
else
sprintf(last_user, "%-8.8s", pwd->pw_name);
last_uid = stp->st_uid;
}
add_to_string(string, last_user);
add_char_to_string(string, ' ');
#endif
}
static inline void
stat_group(struct string *string, struct stat *stp)
{
#ifdef FS_UNIX_USERS
static unsigned char last_group[64];
static int last_gid = -1;
if (!stp) {
add_to_string(string, " ");
return;
}
if (stp->st_gid != last_gid) {
struct group *grp = getgrgid(stp->st_gid);
if (!grp || !grp->gr_name)
/* ulongcat() can't pad from right. */
sprintf(last_group, "%-8d", (int) stp->st_gid);
else
sprintf(last_group, "%-8.8s", grp->gr_name);
last_gid = stp->st_gid;
}
add_to_string(string, last_group);
add_char_to_string(string, ' ');
#endif
}
static inline void
stat_size(struct string *string, struct stat *stp)
{
/* Check if st_size will cause overflow. */
/* FIXME: See bug 497 for info about support for big files. */
if (!stp || stp->st_size != (unsigned long) stp->st_size) {
add_to_string(string, " ");
} else {
unsigned char size[64];
int width = 9;
/* First try to fit the file size into 8 digits ... */
width = ulongcat(size, NULL, stp->st_size, width, ' ');
if (0 < width && width < sizeof(size)) {
/* ... if that is not enough expand the size buffer.
* This will only break the column alignment of the size
* attribute if really needed. */
ulongcat(size, NULL, stp->st_size, width, ' ');
}
add_to_string(string, size);
add_char_to_string(string, ' ');
}
}
static inline void
stat_date(struct string *string, struct stat *stp)
{
#ifdef HAVE_STRFTIME
if (stp) {
time_t current_time = time(NULL);
time_t when = stp->st_mtime;
unsigned char *fmt;
if (current_time > when + 6L * 30L * 24L * 60L * 60L
|| current_time < when - 60L * 60L)
fmt = "%b %e %Y";
else
fmt = "%b %e %H:%M";
add_date_to_string(string, fmt, &when);
add_char_to_string(string, ' ');
return;
}
#endif
add_to_string(string, " ");
}
/** @} */
/* comparison function for qsort() */
static int
int
compare_dir_entries(const void *v1, const void *v2)
{
const struct directory_entry *d1 = v1, *d2 = v2;

View File

@ -3,6 +3,9 @@
#define EL__UTIL_FILE_H
#include <stdio.h>
#include <sys/stat.h>
#include "util/conv.h"
#include "util/string.h"
/** Data read about an entry in a directory.
* The strings pointed to by this structure are in the system
@ -59,4 +62,203 @@ int safe_mkstemp(unsigned char *template);
* taken to be a filename, and simply ignored */
int mkalldirs(const unsigned char *path);
/* comparison function for qsort() */
int compare_dir_entries(const void *v1, const void *v2);
/** @name The stat_* functions set the various attributes for directory entries.
* @{ */
static inline void
stat_type(struct string *string, struct stat *stp)
{
unsigned char c = '?';
if (stp) {
if (S_ISDIR(stp->st_mode)) c = 'd';
else if (S_ISREG(stp->st_mode)) c = '-';
#ifdef S_ISBLK
else if (S_ISBLK(stp->st_mode)) c = 'b';
#endif
#ifdef S_ISCHR
else if (S_ISCHR(stp->st_mode)) c = 'c';
#endif
#ifdef S_ISFIFO
else if (S_ISFIFO(stp->st_mode)) c = 'p';
#endif
#ifdef S_ISLNK
else if (S_ISLNK(stp->st_mode)) c = 'l';
#endif
#ifdef S_ISSOCK
else if (S_ISSOCK(stp->st_mode)) c = 's';
#endif
#ifdef S_ISNWK
else if (S_ISNWK(stp->st_mode)) c = 'n';
#endif
}
add_char_to_string(string, c);
}
static inline void
stat_mode(struct string *string, struct stat *stp)
{
#ifdef FS_UNIX_RIGHTS
unsigned char rwx[10] = "---------";
if (stp) {
mode_t mode = stp->st_mode;
unsigned int shift;
/* Set permissions attributes for user, group and other */
for (shift = 0; shift <= 6; shift += 3) {
mode_t m = mode << shift;
if (m & S_IRUSR) rwx[shift + 0] = 'r';
if (m & S_IWUSR) rwx[shift + 1] = 'w';
if (m & S_IXUSR) rwx[shift + 2] = 'x';
}
#ifdef S_ISUID
if (mode & S_ISUID)
rwx[2] = (mode & S_IXUSR) ? 's' : 'S';
#endif
#ifdef S_ISGID
if (mode & S_ISGID)
rwx[5] = (mode & S_IXGRP) ? 's' : 'S';
#endif
#ifdef S_ISVTX
if (mode & S_ISVTX)
rwx[8] = (mode & S_IXOTH) ? 't' : 'T';
#endif
}
add_to_string(string, rwx);
#endif
add_char_to_string(string, ' ');
}
static inline void
stat_links(struct string *string, struct stat *stp)
{
#ifdef FS_UNIX_HARDLINKS
if (!stp) {
add_to_string(string, " ");
} else {
unsigned char lnk[64];
ulongcat(lnk, NULL, stp->st_nlink, 3, ' ');
add_to_string(string, lnk);
add_char_to_string(string, ' ');
}
#endif
}
static inline void
stat_user(struct string *string, struct stat *stp)
{
#ifdef FS_UNIX_USERS
static unsigned char last_user[64];
static int last_uid = -1;
if (!stp) {
add_to_string(string, " ");
return;
}
if (stp->st_uid != last_uid) {
struct passwd *pwd = getpwuid(stp->st_uid);
if (!pwd || !pwd->pw_name)
/* ulongcat() can't pad from right. */
sprintf(last_user, "%-8d", (int) stp->st_uid);
else
sprintf(last_user, "%-8.8s", pwd->pw_name);
last_uid = stp->st_uid;
}
add_to_string(string, last_user);
add_char_to_string(string, ' ');
#endif
}
static inline void
stat_group(struct string *string, struct stat *stp)
{
#ifdef FS_UNIX_USERS
static unsigned char last_group[64];
static int last_gid = -1;
if (!stp) {
add_to_string(string, " ");
return;
}
if (stp->st_gid != last_gid) {
struct group *grp = getgrgid(stp->st_gid);
if (!grp || !grp->gr_name)
/* ulongcat() can't pad from right. */
sprintf(last_group, "%-8d", (int) stp->st_gid);
else
sprintf(last_group, "%-8.8s", grp->gr_name);
last_gid = stp->st_gid;
}
add_to_string(string, last_group);
add_char_to_string(string, ' ');
#endif
}
static inline void
stat_size(struct string *string, struct stat *stp)
{
/* Check if st_size will cause overflow. */
/* FIXME: See bug 497 for info about support for big files. */
if (!stp || stp->st_size != (unsigned long) stp->st_size) {
add_to_string(string, " ");
} else {
unsigned char size[64];
int width = 9;
/* First try to fit the file size into 8 digits ... */
width = ulongcat(size, NULL, stp->st_size, width, ' ');
if (0 < width && width < sizeof(size)) {
/* ... if that is not enough expand the size buffer.
* This will only break the column alignment of the size
* attribute if really needed. */
ulongcat(size, NULL, stp->st_size, width, ' ');
}
add_to_string(string, size);
add_char_to_string(string, ' ');
}
}
static inline void
stat_date(struct string *string, struct stat *stp)
{
#ifdef HAVE_STRFTIME
if (stp) {
time_t current_time = time(NULL);
time_t when = stp->st_mtime;
unsigned char *fmt;
if (current_time > when + 6L * 30L * 24L * 60L * 60L
|| current_time < when - 60L * 60L)
fmt = "%b %e %Y";
else
fmt = "%b %e %H:%M";
add_date_to_string(string, fmt, &when);
add_char_to_string(string, ' ');
return;
}
#endif
add_to_string(string, " ");
}
#endif