#!/usr/bin/perl
# Workgroup list: file:/$LIB/smb.cgi
# Server list: file:/$LIB/smb.cgi?workgroup
# Sahre list: file:/$LIB/smb.cgi?//server
# file:/$LIB/smb.cgi/server
# Directory: file:/$LIB/smb.cgi?//server/share
# file:/$LIB/smb.cgi?//server/share/dir...
# file:/$LIB/smb.cgi/server/share
# Get file: file:/$LIB/smb.cgi?//server/share/dir.../file
# file:/$LIB/smb.cgi/server/share/dir.../file
#
# ----- ~/.w3m/smb -----
# workgroup = <workgroup>
# [ username = <username> ]
# [ password = <password> ]
# [ password_file = <password_file> ]
# ----------------------
# --- <password_file> ---
# <password>
# -----------------------
# default:
# <username> = $USER
# <password> = $PASSWD (Don't use!)
# <password_file> = $PASSWD_FILE
$DEBUG = 1;
$MIME_TYPE = "~/.mime.types";
$AUTH_FILE = "~/.w3m/smb";
$MIME_TYPE =~ s@^~/@$ENV{"HOME"}/@;
$AUTH_FILE =~ s@^~/@$ENV{"HOME"}/@;
$WORKGROUP = "-";
$USER = $ENV{"USER"};
$PASSWD = $ENV{"PASSWD"};
$PASSWD_FILE = $ENV{"PASSWD_FILE"};
&load_auth_file($AUTH_FILE);
$NMBLOOKUP = "nmblookup";
$SMBCLIENT = "smbclient";
@NMBLOOKUP_OPT = ("-T");
@SMBCLIENT_OPT = ("-N");
$USE_OPT_A = defined($PASSWD) && (-f $AUTH_FILE) && &check_opt_a();
if ($USE_OPT_A) {
push(@SMBCLIENT_OPT, "-A", $AUTH_FILE);
} elsif (-f $PASSWD_FILE) {
$USE_PASSWD_FILE = 1;
} elsif (defined($PASSWD)) {
$USE_PASSWD_FD = 1;
$PASSWD_FD = 0;
}
if (defined($PASSWD)) {
$passwd = "*" x 8;
}
$DEBUG && print <<EOF;
DEBUG: NMBLOOKUP=$NMBLOOKUP @NMBLOOKUP_OPT
DEBUG: SMBCLIENT=$SMBCLIENT @SMBCLIENT_OPT
DEBUG: WORKGROUP=$WORKGROUP
DEBUG: USER=$USER
DEBUG: PASSWD=$passwd
DEBUG: PASSWD_FILE=$PASSWD_FILE
DEBUG: PASSWD_FD=$PASSWD_FD
EOF
$PAGER = "cat";
$FILE = "F000";
$CGI = "file://" . &file_encode($ENV{"SCRIPT_NAME"} || $0);
$QUERY = $ENV{"QUERY_STRING"};
$PATH_INFO = $ENV{"PATH_INFO"};
if ($PATH_INFO =~ m@^/@) {
$_ = $PATH_INFO;
if (! m@^//@) {
$_ = "/$_";
}
s@[\r\n\0\\"]@@g;
$DEBUG && print "DEBUG: PATH_INFO=\"$_\"\n";
$Q = "";
}
else {
$_ = &file_decode($QUERY);
$DEBUG && print "DEBUG: QUERY_STRING=\"$_\"\n";
$Q = "?";
}
if (s@^//([^/]+)@@) {
$server = $1;
# if (!$USE_OPT_A && !defined($PASSWD)) {
# &print_form("//$server$_");
# exit;
# }
if (s@^/([^/]+)@@) {
&file_list("//$server/$1", &cleanup($_));
} else {
&share_list($server);
}
} elsif (m@^[^/]@) {
&server_list($_);
} else {
&group_list();
}
sub file_list {
local($service, $file) = @_;
local(@files) = ();
local($dir, $qservice, $qfile);
local($_, $c);
$DEBUG && print "DEBUG: service=\"$service\" file=\"$file\"\n";
if ($file eq "/") {
goto get_list;
}
$_ = $file;
s@/@\\@g;
@cmd = ($SMBCLIENT, $service, @SMBCLIENT_OPT, "-c", "ls \"$_\"");
$F = &open_pipe(1, @cmd);
while (<$F>) {
$DEBUG && print "DEBUG: $_";
/^\s/ && last;
}
close($F);
if (s/\s+([A-Z]*) {1,8}\d+ (\w{3} ){2}[ \d]\d \d\d:\d\d:\d\d \d{4}\s*$//
&& $1 !~ /D/) {
&get_file($service, $file);
exit;
}
get_list:
$_ = "$file/*";
s@/+@\\@g;
@cmd = ($SMBCLIENT, $service, @SMBCLIENT_OPT, "-c", "ls \"$_\"");
$F = &open_pipe(1, @cmd);
while (<$F>) {
/^\s*$/ && last;
$DEBUG && print "DEBUG: $_";
/^cd\s+/ && last;
/^\S/ && next;
s/\r?\n//;
push(@files, $_);
}
close($F);
$qservice = &html_quote($service);
$service = &file_encode($service);
$qfile = &html_quote($file);
$file = &file_encode($file);
print "Content-Type: text/html\n\n";
print "<title>$qservice$qfile</title>\n";
print "<b>$qservice$qfile</b>\n";
print "<pre>\n";
for (sort @files) {
s/\s+([A-Z]*) {1,8}\d+ (\w{3} ){2}[ \d]\d \d\d:\d\d:\d\d \d{4}\s*$// || next;
$c = $&;
s/^ //;
$_ eq "." && next;
print "<a href=\"$CGI$Q$service"
. &cleanup("$file/" . &file_encode($_)) . "\">"
. &html_quote($_) . "</a>"
. &html_quote($c) . "\n";
}
print "</pre>\n";
}
sub get_file {
local($service, $file) = @_;
local($encoding, $type);
local($_, @cmd);
$_ = $file;
s@/@\\@g;
@cmd = ($SMBCLIENT, $service, @SMBCLIENT_OPT, "-E", "-c", "more \"$_\"");
$DEBUG && print "DEBUG: @cmd\n";
($encoding, $type) = &guess_type($file);
$file =~ s@^.*/@@;
$| = 1;
print "Content-Encoding: $encoding\n" if $encoding;
print "Content-Type: $type; name=\"$file\"\n\n";
$ENV{"PAGER"} = $PAGER if $PAGER;
&exec_cmd(1, @cmd);
}
sub share_list {
local($server) = @_;
local(@share);
local($qserver, $_, $d, @c);
@share = &get_list(1, $server, "Share");
$qserver = &html_quote($server);
$server = &file_encode($server);
print "Content-Type: text/html\n\n";
print "<title>Share list: $qserver</title>\n";
print "<table>\n";
print "<tr><td colspan=3><b>$qserver</b>";
for (sort @share) {
($_, $d, @c) = split(" ");
if ($d eq 'Disk') {
print "<tr><td>+ <a href=\"$CGI$Q//$server/"
. &file_encode($_) . "\">"
. &html_quote($_) . "</a>";
} else {
print "<tr><td>+ "
. &html_quote($_);
}
print "<td><td>"
. &html_quote($d) . "<td><td>"
. &html_quote("@c") . "\n";
}
print "</table>\n";
}
sub server_list {
local($group) = @_;
local($master, @server);
local($_, @c);
$master = &get_master($group);
@server = &get_list(0, $master, "Server");
$group = &html_quote($group);
print "Content-Type: text/html\n\n";
print "<title>Server list: $group</title>\n";
print "<table>\n";
print "<tr><td colspan=3><b>$group</b>\n";
for (sort @server) {
($_, @c) = split(" ");
print "<tr><td>+ <a href=\"$CGI$Q//"
. &file_encode($_) . "\">"
. &html_quote($_) . "</a><td><td>"
. &html_quote("@c") . "\n";
}
print "</table>\n";
}
sub group_list {
local($master, @group);
local($_, @c);
$master = &get_master($WORKGROUP || "-");
@group = &get_list(0, $master, "Workgroup");
print "Content-Type: text/html\n\n";
print "<title>Workgroup list</title>\n";
print "<table>\n";
for (sort @group) {
($_, @c) = split(" ");
print "<tr><td><a href=\"$CGI?"
. &file_encode($_) . "\">"
. &html_quote($_) . "</a><td><td>"
. &html_quote("@c") . "\n";
}
print "</table>\n";
}
sub check_opt_a {
local($_, $F, @cmd);
@cmd = ($SMBCLIENT, "-h");
$F = &open_pipe(0, @cmd);
while (<$F>) {
if (/^\s*-A\s/) {
$DEBUG && print "DEBUG: $_";
close($F);
return 1;
}
}
close($F);
return 0;
}
sub get_master {
local($group) = @_;
local($_, $F, @cmd);
@cmd = ($NMBLOOKUP, "-M", @NMBLOOKUP_OPT, $group);
$F = &open_pipe(0, @cmd);
$_ = <$F>;
$_ = <$F>;
close($F);
($_) = split(/[,\s]/);
s/\.*$//;
return $_;
}
sub get_list {
local($passwd, $server, $header) = @_;
local(@list) = ();
local($_, @cmd, $F);
@cmd = ($SMBCLIENT, @SMBCLIENT_OPT, "-L", $server);
$F = &open_pipe($passwd, @cmd);
while (<$F>) {
if (/^\s*$header/) {
$DEBUG && print "DEBUG: $_";
last;
}
}
while (<$F>) {
/^\s*$/ && last;
$DEBUG && print "DEBUG: $_";
/^\S/ && last;
/^\s*-/ && next;
push(@list, $_);
}
close($F);
return @list;
}
sub open_pipe {
local($passwd, @cmd) = @_;
local($F) = $FILE++;
$DEBUG && print "DEBUG: @cmd\n";
open($F, "-|") || &exec_cmd($passwd, @cmd);
return $F;
}
sub exec_cmd {
local($passwd, @cmd) = @_;
$ENV{"LC_ALL"} = "C";
$ENV{"USER"} = $USER;
if ($passwd && !$USE_OPT_A) {
if ($USE_PASSWD_FILE) {
$ENV{"PASSWD_FILE"} = $PASSWD_FILE;
} elsif ($USE_PASSWD_FD) {
$ENV{"PASSWD_FD"} = $PASSWD_FD;
if (open(W, "|-")) {
print W $PASSWD;
close(W);
exit;
}
}
}
open(STDERR, ">/dev/null");
exec @cmd;
exit 1;
}
sub print_form {
local($_) = @_;
local($q) = &html_quote($_);
$_ = &file_encode($_);
print <<EOF;
Content-Type: text/html
<h1>$q</h1>
<form action="$CGI$Q$_" method=POST>
<table>
<tr><td>Workgroup <td>User <td>Password
<tr><td><input type=text size=8 name=group value="$WORKGROUP">
<td><input type=text size=8 name=user value="$USER">
<td><input type=password size=8 name=passwd value="$PASSWD">
<td><input type=submit name=OK value=OK>
</table>
</form>
EOF
}
sub load_auth_file {
local($_) = @_;
if ($USER =~ s/%(.*)$//) {
$PASSWD = $1 unless $PASSWD;
}
open(F, $_) || return;
while (<F>) {
s/\s+$//;
if (s/^workgroup\s*=\s*//i) {
$WORKGROUP = $_;
} elsif (s/^user(name)?\s*=\s*//i) {
$USER = $_;
} elsif (s/^passw(or)?d\s*=\s*//i) {
$PASSWD = $_;
} elsif (s/^passw(or)?d_file\s*=\s*//i) {
$PASSWD_FILE = $_;
}
}
close(F);
}
sub load_mime_type {
local($_) = @_;
local(%mime) = ();
local($type, @suffix);
open(F, $_) || return ();
while(<F>) {
/^#/ && next;
chop;
(($type, @suffix) = split(" ")) >= 2 || next;
for (@suffix) {
$mime{$_} = $type;
}
}
close(F);
return %mime;
}
sub guess_type {
local($_) = @_;
local(%mime) = &load_mime_type($MIME_TYPE);
local($encoding) = undef;
if (s/\.gz$//i) {
$encoding = "gzip";
} elsif (s/\.Z$//i) {
$encoding = "compress";
} elsif (s/\.bz2?$//i) {
$encoding = "bzip2";
}
/\.(\w+)$/;
$_ = $1;
tr/A-Z/a-z/;
return ($encoding, $mime{$_} || "text/plain");
}
sub cleanup {
local($_) = @_;
$_ .= "/";
s@//+@/@g;
s@/\./@/@g;
while(m@/\.\./@) {
s@^/(\.\./)+@/@;
s@/[^/]+/\.\./@/@;
}
s@(.)/$@$1@;
return $_;
}
sub file_encode {
local($_) = @_;
s/[\000-\040\+:#?&%<>"\177-\377]/sprintf('%%%02X', unpack('C', $&))/eg;
return $_;
}
sub file_decode {
local($_) = @_;
s/\+/ /g;
s/%([\da-f][\da-f])/pack('C', hex($1))/egi;
s@[\r\n\0\\"]@@g;
return $_;
}
sub html_quote {
local($_) = @_;
local(%QUOTE) = (
'<', '<',
'>', '>',
'&', '&',
'"', '"',
);
s/[<>&"]/$QUOTE{$&}/g;
return $_;
}