#$Id: OpenIndex.pm,v 1.04b 2001/10/17 13:33:42 perler@xorgate.com Exp $ package Apache::OpenIndex; use strict; $Apache::OpenIndex::VERSION = '1.04b'; use Apache::Constants qw(:common OPT_INDEXES DECLINE_CMD REDIRECT DIR_MAGIC_TYPE); use DynaLoader (); use Fcntl qw/:flock/; use Apache::Util qw(ht_time size_string escape_html); use Apache::ModuleConfig; use Apache::Icon; use Apache::Language; use Apache::Request; use Apache::Log; #Configuration constants use constant FANCY_INDEXING => 1; use constant ICONS_ARE_LINKS => 2; use constant SCAN_HTML_TITLES => 4; use constant SUPPRESS_LAST_MOD => 8; use constant SUPPRESS_SIZE => 16; use constant SUPPRESS_DESC => 32; use constant SUPPRESS_PREAMBLE => 64; use constant SUPPRESS_COLSORT => 128; use constant THUMBNAILS => 256; use constant SHOW_PERMS => 512; use constant HIDE_EXT => 1024; use constant FOLDERS_FIRST => 2048; use constant NO_OPTIONS => 4096; use constant SKIP_INDEX => -1; use constant ERROR => -2; use constant URI_FILE => 1; use constant URI_DIR => 2; use constant URI_ROOT => 4; use constant URI_MARK => 8; use vars qw(%GenericDirectives); %GenericDirectives = ( fancyindexing => FANCY_INDEXING, iconsarelinks => ICONS_ARE_LINKS, scanhtmltitles => SCAN_HTML_TITLES, suppresslastmodified => SUPPRESS_LAST_MOD, suppresssize => SUPPRESS_SIZE, suppressdescription => SUPPRESS_DESC, suppresshtmlpreamble => SUPPRESS_PREAMBLE, suppresscolumnsorting => SUPPRESS_COLSORT, thumbnails => THUMBNAILS, showpermissions => SHOW_PERMS, hideext => HIDE_EXT, foldersfirst => FOLDERS_FIRST, ); #Default values use constant DEFAULT_ICON_WIDTH => 20; use constant DEFAULT_ICON_HEIGHT=> 22; use constant DEFAULT_NAME_WIDTH => 23; use constant DEFAULT_ORDER => 'ND'; use constant DEFAULT_FAKE_DIR => '.XOI'; use constant DEFAULT_MARK_DIR => '.MARK'; use constant DEFAULT_TEXT_LEN => 49; use constant DEFAULT_MENU => ['Upload','Unzip','Delete','MkDir','MkFile','Copy','Move','Edit','Rename','Help',]; use constant DEFAULT_ADMN_MENU => ['SetGID','Revoke','Debug',]; use constant DEFAULT_POST_MAX => 4194304; use constant DEFAULT_EDIT_MAX => 131072; use constant DEFAULT_HELP_URL => 'http://www.xorgate.com/help/OpenIndex'; use constant DEFAULT_DIR_MOD => 0770; use constant DEFAULT_FILE_MOD => 0460; use constant REVOKE_DIR => '/revoke'; use constant REVOKE_FILE => '/revoked'; use vars qw(%sortname); %sortname = ( 'N'=>'Name', 'M'=>'LastModified', 'S'=>'Size', 'D'=>'Description', ); #Statistics variables use vars qw($nDir $nRedir $nIndex $nThumb); $nDir=0; $nRedir=0; $nIndex=0; $nThumb=0; # global arguments use vars qw($debug $dodump $errmsg $chgid $users $iconfig %commands); %commands = ( Menu => { back=>\&procform, }, Upload => { # name of the menu button selected cmd=>\&Upload, # routine to call when selected req=>'browse', # have to have browse form field src=>'browse', }, Unzip => { cmd=>\&Unzip, min=>1, # at least 1 item has to be selected }, Delete => { cmd=>\&Delete, min=>1, # at least 1 item has to be selected }, MkDir => { cmd=>\&MkDir, req=>'dst', # has to have a destination }, MkFile => { cmd=>\&Edit, req=>'dst', src=>'dst', back=>\&EditSave, # routine called back after MkFile submit }, Copy => { cmd=>\&Copy, req=>'dst', # has to have a destination min=>1, }, Move => { cmd=>\&Move, req=>'dst', min=>1, }, Edit => { cmd=>\&Edit, min=>1, max=>1, # can only operate on one item back=>\&EditSave, # routine called back after Edit submit }, Rename => { cmd=>\&Rename, req=>'dst', min=>1, max=>1, # can only operate on one item }, Help => { cmd=>\&Help, }, SetGID => { cmd=>\&SetGID, min=>1, req=>'group', dst=>'group', admin=>1, }, Revoke => { cmd=>\&Revoke, back=>\&Revokem, admin=>1, }, Debug => { cmd=>\&Debug, admin=>1, }, SelectAll => { cmd=>\&SelectAll, }, ); if ($ENV{MOD_PERL}){ no strict; @ISA=qw(DynaLoader); __PACKAGE__->bootstrap($Apache::OpenIndex::VERSION); if (Apache->module('Apache::Status')) { Apache::Status->menu_item('OpenIndex'=>'Apache::OpenIndex status',\&status); } } sub oindex { my ($r,$args,$filename,$mode,$cfg) = @_; my $uri = $r->uri; my $fakedir=$cfg->{fakedir}; my $markdir=$cfg->{markdir}; my $lang = new Apache::Language($r) if $cfg->{language}; my $isroot; my $retval=1; $r->filename($filename); return 0 unless opendir HDH, $filename; my $msg=$lang->{IndexOf} || 'Index of'; chomp($msg); my $ref=$args->{dir}; if($mode) { if($mode & URI_MARK) { if($cfg->{markroot}) { $isroot=$filename=~m:^$cfg->{markroot}$:; } else { $isroot=$filename=~m:$fakedir/$markdir/$:; } } elsif($mode & URI_ROOT) { $isroot=$uri=~m:^$args->{root}$fakedir/$:; $ref=~s:/$fakedir/:/:; } } else { $isroot=$uri=~m:^$args->{root}$:; } print STDERR "oindex() open $filename\n" if $debug; thumb_conf($r) if $cfg->{options} & THUMBNAILS; tagout('h2',$cfg,'', qq~$msg $ref~); if($mode) { tagout('form',$cfg,qq~method="post" action="$uri" enctype="multipart/form-data"~); cmd_form($r,$args,$cfg); } $nDir++; if($cfg->{options} & FANCY_INDEXING) { $retval=fancy_page($r,$args,\*HDH,$mode,$isroot); } else { $retval=plain_page($r,$args,\*HDH,$mode,$isroot); } print "\n" if($mode); closedir HDH; $retval; } sub procform { my ($r,$args,$cfg,$docroot) = @_; my $fakedir = $cfg->{fakedir}; my $lang = new Apache::Language($r) if $cfg->{language}; my $mode=$args->{mode}; my $msg; my $dir; my $formsrc; my $formdst; my $userdir; my $count; my $retval=0; my $items=$args->{items}; # Items array selected my $icnt=@$items; # The number selected my $cmd = getcmd($cfg->{menu},$args); $cmd||=getcmd($cfg->{admnmenu},$args); my $cmdname=$lang->{$cmd} || $cmd; chomp $cmdname; my $req=$commands{$cmd}{req}; if($mode & URI_MARK) { if($args->{dst}=~m:^/:o) { $formdst=$args->{dst}; } else { $formdst="$args->{dir}$args->{dst}"; } $dir=$args->{dir}; } elsif($mode & URI_ROOT) { if($args->{dst}=~m:^/:o) { $formdst=$args->{dst}; } else { ($formdst="$args->{dir}$args->{dst}")=~s:/$fakedir/:/:; } ($dir=$args->{dir})=~s:/$fakedir/:/:; } else { $msg=$lang->{mode} || 'UNKNOWN: mode'; errmsg($msg); return 0; } my $dst=$commands{$cmd}{dst}; if($dst) { if($dst eq 'src') { $formdst=$formsrc; } else { $formdst=$args->{$dst}; } } print STDERR "procform($cmd)\n" if $debug; # check if cmd unless($cmd) { $msg=$lang->{command} || 'UNKNOWN: command'; errmsg($msg); $r->log->error(__PACKAGE__." internal error: NULL command"); return ERROR; } # check min select $count=$commands{$cmd}{min}; if($count && $icnt<$count) { $msg=$lang->{min} || 'Select more items!'; errmsg("$cmdname: $msg"); $r->log->warn(__PACKAGE__." $cmd ERROR: $args->{user}: $msg"); return ERROR; } # check max select $count=$commands{$cmd}{max}; if($count && $icnt>$count) { $msg=$lang->{max} || 'Too many items selected!'; errmsg("$cmdname: $msg"); $r->log->warn(__PACKAGE__." $cmd ERROR: $args->{user}: $msg"); return ERROR; } # check req if($req) { unless($args->{$req}) { $msg=$lang->{$req} || "$req"; chomp($msg); $msg.=' '; $msg.=$lang->{required} || "required!"; errmsg("$cmdname: $msg"); $r->log->warn(__PACKAGE__." $cmd ERROR: $args->{user}: $msg"); return ERROR; } unless($args->{isadmin}) { # do not allow hidden files names to be used. my $ignore_regex; if($cfg->{ignore}) { if($args->{$req}=~m:[/\\]:o) { $ignore_regex = '.*[/\\\\]'.join('$|.*[/\\\\]',@{$cfg->{ignore}}).'$'; } else { $ignore_regex = '^'.join('$|^',@{$cfg->{ignore}}).'$'; } } if($args->{$req}=~m:$ignore_regex:) { $msg=$lang->{forbid} || 'Forbidden: '; $msg.=$lang->{$req} || $args->{$req}; errmsg($msg); $r->log->error(__PACKAGE__." FORBIDDEN: $req=$args->{$req}"); return ERROR; } } } $docroot='' if $mode & URI_MARK && $cfg->{markroot}; $userdir=$cfg->{userdir}; $docroot='' if $formdst=~m:/~:o && $userdir=~m:^/:o; $formdst=xuserdir($formdst,$userdir) if $userdir; $dir =~tr{ :.a-zA-Z0-9~!@#$^&+i_\\\-/}{}cd; #strip unusual characters $formdst=~tr{ :.a-zA-Z0-9~!@#$^&+i_\\\-/}{}cd; unless(dirbound($formdst,$args->{root})) { # Don't allow $formdst below root $msg=$lang->{ProcDstRoot} || 'Destination goes below the root directory'; errmsg($msg); return ERROR; } my $oldmask=umask $cfg->{umask} if $args->{gid} && $cfg->{umask}; # process any before command if($commands{$cmd}{before}) { unless($commands{$cmd}{before}->($r,$args,$cfg,$docroot,$items,$formdst)) { $r->log->error(__PACKAGE__." $cmd before"); return ERROR; } } do { my $src=$commands{$cmd}{src}; if($src) { if($src eq 'dst') { $formsrc=$formdst; } else { $formsrc=$args->{$src}; } } else { $formsrc="$dir$items->[--$icnt]"; } $formsrc=xuserdir($formsrc,$userdir) if $userdir; $formsrc=~tr{ :.a-zA-Z0-9~!@#$^&+i_\\\-/}{}cd; unless(dirbound($formsrc,$args->{root})) { # Don't allow $formsrc below root $msg=$lang->{SourcePath} || 'Bad source path'; errmsg($msg); umask($oldmask) if $args->{gid} && $cfg->{umask}; $retval=ERROR; } else { $retval=$commands{$cmd}{cmd}->($r,$args,$cfg,$docroot,$formsrc,$formdst); unless($retval) { $r->log->warn(__PACKAGE__." $cmd ERROR: $args->{user}: $docroot: src=$formsrc dst=$formdst"); $retval=ERROR; } else { $retval=0 unless $retval<0 || $retval>99; } } } until $icnt<1 || $retval; # process any after command if($commands{$cmd}{after}) { $retval=$commands{$cmd}{after}->($r,$args,$cfg,$docroot,$formdst); unless($retval) { $r->log->error(__PACKAGE__." $cmd after"); $retval=ERROR; } } umask($oldmask) if $args->{gid} && $cfg->{umask}; $retval; } # Returns the translated UserDir $path according to the $pattern sub xuserdir { my($path,$pattern)=@_; print STDERR "xuserdir() path=$path pattern=$pattern" if $debug; return $path unless $pattern; my($head,$user,$tail)=$path=~m:^(.*)/~(.+)/(.*):o; my $userdir=$pattern; if($pattern=~m:/\*/:o) { $userdir=~s:/\*:/$user: if $user; $path=~s:/~$user:$userdir:; } elsif($pattern=~m:^/:o) { $path=$pattern; $path.='/' unless $path=~m:/$:o; $path.="$user/$tail"; } else { $path="$head/$user/$pattern"; $path.="/$tail" if $tail; } print STDERR "->$path\n" if $debug; $path; } sub frames { my($r,$args,$cfg) = @_; my $uri = $r->uri; my $footer=gotfooter($r,$cfg); my $lang = new Apache::Language($r) if $cfg->{language}; my $ac = $uri=~m:\?:o ? '&':'?'; my $msg=qq~src="$uri${ac}frame=main"~; print STDERR "frames() uri=$uri src=$msg footer=$footer\n" if $debug; if($cfg->{frameset}) { tagout('frameset',$cfg); } else { print qq~
\n~; 1; } sub header { my ($r,$args,$cfg,$notitle)=@_; print STDERR "header() $cfg->{headuri}\n" if $debug; if($cfg->{headuri}) { my $subr = $r->lookup_uri($cfg->{headuri}); $subr->run; } place_doc($r,$cfg,'header') if $cfg->{header}; unless($notitle || $cfg->{notitle}) { tagout('h1',$cfg,'','OpenIndex'); if($args->{gid}) { my $lang = new Apache::Language($r) if $cfg->{language}; my $msg=$lang->{user} || 'User'; print " $msg=$args->{user}" if $args->{user}; my $cnt=@{$args->{gid}}-1; $msg=$lang->{access} || 'Access'; print " $msg=$args->{gidname}[$cnt]"; for($cnt--;$cnt>=0;$cnt--) { print ",$args->{gidname}[$cnt]"; } } print "\n"; } 1; } sub httphead { my ($r,$title)=@_; my $cfg = Apache::ModuleConfig->get($r); my $lang = new Apache::Language($r) if $cfg->{language}; $r->no_cache(1) if $cfg->{nocache}; $r->send_http_header('text/html'); return 0 if $r->header_only; print STDERR "httpdhead()\n" if $debug; # print qq~\n~; print qq~{frames}?'xhtml1-frameset.dtd':'xhtml1-transitional.dtd',qq~">\n~; print ''; tagout('head',$cfg); print $lang->{$cfg->{htmltext}{head}} || $cfg->{htmltext}{head} if $cfg->{htmltext}{head}; tagout('title',$cfg); print "$title\n"; if($cfg->{htmltext}{style}) { tagout('style',$cfg); print "$cfg->{htmltext}{style}\n\n"; } print "\n"; tagout('body',$cfg) unless $cfg->{frames}; 1; } sub footer { my ($r,$cfg)=@_; print STDERR "footer() $cfg->{footuri}\n" if $debug; if($cfg->{readme}) { etagout('hr',$cfg) unless $cfg->{frames}; place_doc($r,$cfg,'readme'); } if($cfg->{footuri}) { my $subr = $r->lookup_uri($cfg->{footuri}); $subr->run; } 1; } sub gotfooter { my ($r,$cfg)=@_; return 1 if $cfg->{footuri} || $cfg->{readme}; 0; } sub cmd_form { my ($r,$args,$cfg)=@_; my $uri=$r->uri; my $dst; my $setgid; my $docroot=$r->document_root; my $fakedir=$cfg->{fakedir}; my $textlen=$cfg->{textlen} || DEFAULT_TEXT_LEN; my $menu=$cfg->{menu}||DEFAULT_MENU; if($args->{error}) { tagout('h3',$cfg); if($cfg->{font}) { tagout('font',$cfg,'',"ERROR: $errmsg"); } else { print qq~ ERROR: $errmsg~; } $args->{error}=0; } if(!$args->{src}) { if(!$args->{file} && $args->{child}) { $args->{src}=$args->{child}; } else { $args->{src}=$args->{file}; } } $dst=$args->{dst}; $setgid=$args->{gid}; my $didit; my $msg=''; my $lang = new Apache::Language($r) if $cfg->{language}; tagout('div',$cfg); foreach (@$menu) { if($_ eq 'Upload') { $msg=$lang->{$_} || $_; chomp $msg; chomp $msg; textout($r,$cfg,'browse'); print "$cfg->{browse}" if $cfg->{browse}; etagout('input',$cfg,qq~type="file" name="browse" size=$textlen maxlength=255~); textout($r,$cfg,'upload'); etagout('input',$cfg,qq~type="submit" name="$_" value="$msg"~); etagout('br',$cfg); } } foreach (@$menu) { unless($_ eq 'Upload') { $msg=$lang->{$_} || $_; chomp $msg; chomp $msg; textout($r,$cfg,$_); etagout('input',$cfg,qq~type="submit" name="$_" value="$msg"~); } } unless($cfg->{options} & FANCY_INDEXING) { # enter the source item if not FANCY $msg=$lang->{src} || 'Select Item'; chomp $msg; textout($r,$cfg,'src'); etagout('br',$cfg); etagout('input',$cfg, qq~TYPE="text" name="src" size=$textlen maxlength=255 value="$args->{src}"~,$msg); } $msg=$lang->{dst} || 'Destination'; chomp $msg; textout($r,$cfg,'dst'); etagout('br',$cfg); etagout('input',$cfg,qq~type="text" name="dst" size=$textlen maxlength=255 value="$dst"~,$msg); tagout('p',$cfg); if($args->{isadmin}) { my $halflen=($textlen+($textlen%2))/2; $menu=$cfg->{admnmenu}||DEFAULT_ADMN_MENU; foreach (@$menu) { $msg=$lang->{$_} || $_; chomp $msg; chomp $msg; next if $_ eq 'Revoke' && !$cfg->{revoke}; next if $_ eq 'Debug' && !$cfg->{debug}; textout($r,$cfg,$_); etagout('input',$cfg,qq~type="text" name="group" size=$halflen maxlength=255~) if $_ eq 'SetGID'; etagout('input',$cfg,qq~type="submit" name="$_" value="$msg"~); } } print qq~\n~; print qq~\n~ if $args->{all}; print qq~\n~ if $args->{frame}; print "\n"; 1; } sub textout { my ($r,$cfg,$cmd)=@_; return unless $cfg->{htmltext}{$cmd}; my $lang = new Apache::Language($r) if $cfg->{language}; print $lang->{$cfg->{htmltext}{$cmd}} || $cfg->{htmltext}{$cmd}; } sub plain_page { my ($r,$args,$dirhandle,$mode,$isroot)=@_; my $cfg = Apache::ModuleConfig->get($r); my $hide=!($args->{isadmin} && $dodump); my $ignore_regex; $ignore_regex = join('$|^',@{$cfg->{ignore}}) if $cfg->{ignore}; print "\%list\n"; print Dumper \%$list; print ""; } 1; } # Start of internal menu command routines sub SelectAll { my ($r,$args,$cfg) = @_; my $uri = $r->uri; my $c='?'; unless($args->{all}) { $uri.='?all=1'; $c='&'; } if($args->{frame}) { $uri.="${c}frame=$args->{frame}"; $c='&'; } $uri.="${c}dst=$args->{dst}" if $args->{dst}; print STDERR "SelectAll() uri=$uri\n" if $debug; $r->header_out(Location=>$uri); REDIRECT; } sub Help { my ($r,$args,$cfg) = @_; my $uri=$cfg->{help}||DEFAULT_HELP_URL; $uri.="?version=$Apache::OpenIndex::VERSION&postmax=$cfg->{postmax}"; $uri.="&mark=1" if $cfg->{mark}; $uri.="&perms=1" if $args->{gid}; $uri.="&admin=1" if $args->{isadmin}; $uri.="&frame=$args->{frame}" if $args->{frame}; $r->header_out(Location=>$uri); $r->log->notice(__PACKAGE__." $args->{user}: Help: $uri"); REDIRECT; } sub Debug { my ($r,$args,$cfg) = @_; my $lang = new Apache::Language($r) if $cfg->{language}; my $msg=''; my $cmdname=$lang->{Debug} || 'Debug'; $dodump = !$dodump if $debug; print STDERR "Debug=$dodump\n" if $debug; $r->log->notice(__PACKAGE__." $args->{user}: Debug: $dodump"); 1; } sub SetGID { # Set the item (file or dir) GID my ($r,$args,$cfg,$root,$src,$igid) = @_; my $lang = new Apache::Language($r) if $cfg->{language}; my $msg=''; my $cmdname=$lang->{SetGID} || 'SetGID'; my $name; $src="$root$src"; chomp $cmdname; if($igid=~m:[^0-9]:o) { # if not a number look-up the group $name=$igid; unless(($igid=getgrnam $name)) { $msg=$lang->{GIDbad} || 'GID name not found'; errmsg(qq~${cmdname}: "$name" $msg~); return 0; } } else { unless(($name=getgrgid $igid)) { $msg=$lang->{GIDbad} || 'GID name not found'; errmsg(qq~${cmdname}: "$igid" $msg~); return 0; } } unless($igid && chown(-1,$igid,$src)) { $msg=$lang->{GIDset} || 'GID not set'; errmsg(qq~${cmdname}: "$name" $msg~); return 0; } $r->log->notice(__PACKAGE__." $args->{user}: SetGID: $igid $src"); 1; } sub Revoke { my ($r,$args,$cfg) = @_; my $lang = new Apache::Language($r) if $cfg->{language}; my $msg=''; my $cmdname=$lang->{Revoke} || 'Revoke'; my $uri = $r->uri; my $textlen=$cfg->{textlen} || DEFAULT_TEXT_LEN; my $halflen=($textlen+($textlen%2))/2; chomp $cmdname; $r->no_cache(1); # Always make sure that the data is not cached return SKIP_INDEX unless httphead($r,"OpenIndex $cmdname"); header($r,$args,$cfg) unless $args->{frame}; tagout('h3',$cfg,'',"OpenIndex $cmdname"); my $gotdata; my $type; my $name; foreach (keys %$users) { if($users->{$_} eq '-') { my($ruser,$rgid)=m:^(.*?)#(.*?)#:; unless($gotdata) { $msg=$lang->{Revoked} || 'The following have been revoked:'; tagout('p',$cfg,'',"$msg"); tagout('table',$cfg,qq~summary="$msg" cols="2"~); tagout('tr',$cfg); tagout('th',$cfg,'',' Type '); tagout('th',$cfg,'',' Name '); $gotdata=1; } if($ruser) { $type='user'; $name=$ruser; } if($rgid) { $type='gid'; $name=getgrgid $rgid || $rgid; } tagout('tr',$cfg); tagout('td',$cfg,''," $type "); tagout('td',$cfg,''," $name "); } } print "\n" if $gotdata; unless($gotdata) { $msg=$lang->{NoUsers} || 'No user or group revoke information available'; tagout('p',$cfg,'',"$msg"); } tagout('form',$cfg,qq~method="post" action="$uri" enctype="multipart/form-data"~); etagout('input',$cfg,qq~type="text" name="id" size=$halflen maxlength=255~); $msg=$lang->{EnableUID} || 'Enable User'; chomp $msg; etagout('input',$cfg,qq~type="submit" name="enauid" value="$msg"~); $msg=$lang->{DisableUID} || 'Disable User'; chomp $msg; etagout('input',$cfg,qq~type="submit" name="disuid" value="$msg"~); $msg=$lang->{EnableGID} || 'Enable GID'; chomp $msg; etagout('input',$cfg,qq~type="submit" name="enagid" value="$msg"~); $msg=$lang->{DisableGID} || 'Disable GID'; chomp $msg; etagout('input',$cfg,qq~type="submit" name="disgid" value="$msg"~); tagout('p',$cfg); $msg=$lang->{Return} || 'Return'; chomp $msg; etagout('input',$cfg,qq~type="submit" name="return" value="$msg"~); etagout('input',$cfg,qq~type="hidden" name="proc" value="Revoke"~); hidenargs($args); print ''; etagout('hr',$cfg); $r->log->notice(__PACKAGE__." $args->{user}: Revoke:"); SKIP_INDEX; } sub Edit { my ($r,$args,$cfg,$root,$src) = @_; my $relsrc=$src; $src="$root$src"; my $lang = new Apache::Language($r) if $cfg->{language}; my $msg; my %info; my $inifile; my $opened; my $uri = $r->uri; my $fgid=(stat $src)[5]; my $cmdname=$lang->{Edit} || 'Edit'; chomp $cmdname; if(-e _) { unless(isagid($args->{gid},$fgid) || $args->{isadmin}) { $msg=$lang->{SourceAccess} || 'Source access denied'; errmsg("${cmdname}: $msg"); return 0; } unless(-f _) { $msg=$lang->{NotText} || 'Item is not a text file'; errmsg("${cmdname}: $msg"); return 0; } unless(-T _) { $msg=$lang->{NotText} || 'Item is not a text file'; errmsg("${cmdname}: $msg"); return 0; } my $editmax=$cfg->{editmax} | DEFAULT_EDIT_MAX; unless(-s _ <= $editmax) { $msg=$lang->{FileTooBig} || 'File size is larger than'; errmsg("${cmdname}: $msg $editmax"); return 0; } unless(open ITEM, "<$src") { $msg=$lang->{FileOpen} || 'File open'; errmsg("${cmdname}: $msg"); return 0; } $opened=1; } else { return 0 unless parentok($src,$args,$cfg,$cmdname,$lang); } ($inifile=$src)=~s:^(.*/)(.+):$1\.$2\.ini:; if(open INIFILE,"<$inifile") { $info{open}=1; while(
\%info\n"; print Dumper \%info; print ''; etagout('hr',$cfg); } $r->log->notice(__PACKAGE__." $args->{user}: Edit: $src"); SKIP_INDEX; } sub MkDir { my ($r,$args,$cfg,$root,$src,$dst) = @_; my $lang = new Apache::Language($r) if $cfg->{language}; my $msg; my $cmdname=$lang->{MkDir} || 'MkDir'; chomp $cmdname; unless($dst) { $msg=$lang->{DestPath} || 'Bad destination path'; errmsg("${cmdname}: $msg"); return 0; } $dst="$root$dst"; if(-e $dst) { $msg=$lang->{DestExists} || 'Destination exists'; errmsg("${cmdname}: $msg"); return 0; } if($args->{gid}) { my $fgid=parentok($dst,$args,$cfg,$cmdname,$lang); return 0 unless $fgid; unless(mkdir $dst,0755) { errmsg("${cmdname}: $!"); return 0; } chown(-1,$fgid,$dst); } else { unless(mkdir $dst,0755) { errmsg("${cmdname}: $!"); return 0; } } $r->log->notice(__PACKAGE__." $args->{user}: MkDir: $dst"); 1; } sub Unzip { my ($r,$args,$cfg,$root,$src,$dst) = @_; $dst=~s:/$::; # strip any trailing '/' use Archive::Zip qw( :ERROR_CODES :CONSTANTS ); use Archive::Zip::Tree; my $lang = new Apache::Language($r) if $cfg->{language}; my $msg; my $cmdname=$lang->{Unzip} || 'Unzip'; chomp $cmdname; unless($dst) { $msg=$lang->{DestPath} || 'Bad destination path'; errmsg("${cmdname}: $msg"); return 0; } $src="$root$src"; $dst="$root$dst"; my $fgid=(stat $src)[5]; unless(isagid($args->{gid},$fgid) || $args->{isadmin}) { $msg=$lang->{SourceAccess} || 'Source access denied'; errmsg("${cmdname}: $msg"); return 0; } $fgid=(stat $dst)[5]; if(! -d _) { $msg=$lang->{DestDir} || 'Destination is not a directory'; errmsg("${cmdname}: $msg"); return 0; } unless(isagid($args->{gid},$fgid) || $args->{isadmin}) { $msg=$lang->{DestAccess} || 'Destination access denied'; errmsg("${cmdname}: $msg"); return 0; } my $zip=Archive::Zip->new($src); unless ($zip) { $msg=$lang->{FileRead} || 'file read'; errmsg("${cmdname}: $msg"); return 0; } my $files=0; my $name; $dst.='/'; for my $member ($zip->members()) { ($name=$dst).=$member->fileName(); if($member->isDirectory()) { mkdir $name,0775; chown(-1,$fgid,$name); next; } unless($member->extractToFileNamed($name)==AZ_OK) { errmsg("$cmdname: $name"); return 0; } chown(-1,$fgid,$name); ++$files; } $r->log->notice(__PACKAGE__." $args->{user}: Unzip: $src files=$files"); 1; } sub Move { my ($r,$args,$cfg,$root,$src,$dst) = @_; my $target=$src; $src="$root$src"; $dst="$root$dst"; use File::Copy qw(move); my $lang = new Apache::Language($r) if $cfg->{language}; my $msg; my $cmdname=$lang->{Move} || 'Move'; chomp $cmdname; unless($target) { $msg=$lang->{DestPath} || 'Bad destination path'; errmsg("${cmdname}: $msg"); return 0; } my $srcgid=(stat $src)[5]; my $src_is_dir=1 if -d _; unless(isagid($args->{gid},$srcgid) || $args->{isadmin}) { $msg=$lang->{SourceAccess} || 'Source access denied'; errmsg("${cmdname}: $msg"); return 0; } my $dstgid=(stat $dst)[5]; unless(isagid($args->{gid},$dstgid) || $args->{isadmin}) { $msg=$lang->{DestAccess} || 'Destination access denied'; errmsg("${cmdname}: $msg"); return 0; } $target=~s:^.*/(.*):$1:; $dst="$dst/$target" if $src_is_dir; unless(File::Copy::move($src, $dst)) { errmsg("${cmdname}: $!"); return 0; } chown(-1,$dstgid,$dst) unless $args->{isadmin}; # admin can move others $r->log->notice(__PACKAGE__." $args->{user}: Move: $src->$dst"); 1; } sub Rename { my ($r,$args,$cfg,$root,$src,$dst) = @_; my $target=$dst; $src="$root$src"; $dst="$root$dst"; use File::Copy qw(move); my $lang = new Apache::Language($r) if $cfg->{language}; my $msg; my $fgid=(stat $src)[5]; my $cmdname=$lang->{Rename} || 'Rename'; chomp $cmdname; unless(isagid($args->{gid},$fgid) || $args->{isadmin}) { $msg=$lang->{SourceAccess} || 'source access denied'; errmsg("${cmdname}: $msg"); return 0; } unless($target) { $msg=$lang->{DestPath} || 'Bad destination path'; errmsg("${cmdname}: $msg"); return 0; } if(-e $dst) { $msg=$lang->{DestExists} || 'Destination'; errmsg("${cmdname}: $msg"); return 0; } unless(File::Copy::move($src, $dst)) { errmsg("${cmdname}: $!"); return 0; } $r->log->notice(__PACKAGE__." $args->{user}: Rename: $src->$dst"); 1; } ################################################################### # The following override is requried because File::NCopy uses glob # which can not deal with spaces in the file names. ################################################################### package File::NCopy; use subs qw(glob); sub glob {@_}; package Apache::OpenIndex; ################################################################### sub Copy { my ($r,$args,$cfg,$root,$src,$dst) = @_; my $target=$src; $src="$root$src"; $dst="$root$dst"; use File::NCopy qw(copy); my $lang = new Apache::Language($r) if $cfg->{language}; my $msg; my $cmdname=$lang->{Copy} || 'Copy'; chomp $cmdname; unless($target) { $msg=$lang->{DestPath} || 'Bad destination path'; errmsg("${cmdname}: $msg"); return 0; } my $file; my $isdir; my $fgid=(stat $dst)[5]; $chgid=0; if(-e _) { $isdir=1 if -d _; unless(isagid($args->{gid},$fgid) || $args->{isadmin}) { $msg=$lang->{DestAccess} || 'Destination access denied'; errmsg("${cmdname}: $msg"); return 0; } $chgid=$fgid if $args->{gid}; # global used by chgid() to set GID } else { $msg=$lang->{SourcePath} || 'Bad source path'; errmsg("${cmdname}: $msg"); return 0; } unless(isagid($args->{gid},$fgid) || $args->{isadmin}) { $msg=$lang->{SourceAccess} || 'Source access denied'; errmsg("${cmdname}: $msg"); return 0; } if(-d _) { unless($isdir) { $msg=$lang->{DirConflict} || 'Source directory but a destination file'; errmsg("${cmdname}: $msg"); return 0; } if($dst=~m:^$src:) { $msg=$lang->{CopyRecusive} || 'Recursive copy detected'; errmsg("${cmdname}: $msg"); return 0; } $file=File::NCopy->new(recursive=>1,force_write=>1,set_permission=>\&chgid); } else { $file=File::NCopy->new(force_write=>1,set_permission=>\&chgid); } unless($file->copy($src, $dst)) { $msg=$lang->{DestCheck} || 'Check destination path'; errmsg("${cmdname}: $msg: $!"); return 0; } $r->log->notice(__PACKAGE__." $args->{user}: Copy: $src->$dst"); 1; } sub Delete { my ($r,$args,$cfg,$root,$src) = @_; $src="$root$src"; use File::Path qw(rmtree); my $lang = new Apache::Language($r) if $cfg->{language}; my $msg; my $fgid=(stat $src)[5]; my $cmdname=$lang->{Delete} || 'Delete'; chomp $cmdname; unless(-e _) { $msg=$lang->{SourcePath} || 'Bad source path'; errmsg("${cmdname}: $msg"); return 0; } unless(isagid($args->{gid},$fgid) || $args->{isadmin}) { $msg=$lang->{SourceAccess} || 'Source access denied'; errmsg("${cmdname}: $msg"); return 0; } if(-d _) { unless(File::Path::rmtree($src)) { errmsg("${cmdname}: $!"); return 0; } } else { unless(unlink($src)) { errmsg("${cmdname}: $!"); return 0; } } $r->log->notice(__PACKAGE__." $args->{user}: Delete: $src"); 1; } sub Upload { my ($r,$args,$cfg,$root,$src,$dst) = @_; my $upload=$r->upload; my $sfh=$upload->fh; my $bytes=0; my $size=0; my $lang = new Apache::Language($r) if $cfg->{language}; my $msg; my $cmdname=$lang->{Upload} || 'Upload'; chomp $cmdname; $src=~s:.*[\\/]::o; # strip off the UNIX or DOS filename $dst="$root$dst$src"; unless($sfh) { $msg=$lang->{internal} || 'internal'; errmsg("${cmdname}: $msg"); return 0; } my $fgid=parentok($dst,$args,$cfg,$cmdname,$lang); return 0 unless $fgid; unless(open DFH, ">$dst") { $msg=$lang->{DestOpen} || 'Destination open'; errmsg("${cmdname}: $msg"); return 0; } my $buf; while(($size=read($sfh, $buf, 4096))) { unless(print DFH $buf) { close DFH; $msg=$lang->{write} || 'write'; errmsg("${cmdname}: $msg"); return 0; } $bytes+=$size; } $args->{bytes}+=$bytes; close DFH; chown(-1,$fgid,$dst); $r->log->notice(__PACKAGE__." $args->{user}: Upload: $bytes: $src->$dst"); 1; } sub View { my ($r,$args,$cfg,$root,$src,$dst) = @_; $src.="?frame=$args->{frame}" if($args->{frame}); $r->log->notice(__PACKAGE__." View: $args->{user}: $src"); $r->header_out(Location=>$src); return REDIRECT; } # End of internal menu command routines # Start of internal proc call back routines sub EditSave { my ($r,$args,$cfg,$docroot)=@_; my $file="$docroot$args->{edit}"; if($args->{save}) { my $lang = new Apache::Language($r) if $cfg->{language}; my $msg; my $exists=1 if -e $file; my $cmdname=$lang->{EditSave} || 'EditSave'; chomp $cmdname; unless(open FILE, ">$file") { $msg=$lang->{FileOpen} || 'File Open'; errmsg("${cmdname}: $msg"); return ERROR; } else { print FILE $args->{text}; close FILE; unless($exists) { my ($parent)=$file=~m:(^.*)/.+:o; my $fgid=(stat $parent)[5]; chown(-1,$fgid,$file); } $r->log->notice(__PACKAGE__." $args->{user}: EditSave: $file"); } } editini($r,$args,$file,"$docroot$args->{info}"); } sub editini { my ($r,$args,$file,$inifile)=@_; if($args->{save} || $args->{user} eq $args->{saver}) { if($args->{save}) { unless(open INIFILE, ">$inifile") { errmsg("Edit: Lock File write open"); $args->{error}=1; } else { unless(flock INIFILE, LOCK_EX|LOCK_NB) { errmsg("Edit: Couldn't lock file. Try again"); $args->{error}=1; } else { my $fgid=(stat $file)[5]; $fgid=getgrgid $fgid || $fgid; print INIFILE "editedby=$args->{user}\ngid=$fgid\ntime=",scalar localtime,"\nstatus=in\n"; } } } else { unless(open INIFILE, ">>$inifile") { errmsg("Edit: Lock File append open"); $args->{error}=1; } else { unless(flock INIFILE, LOCK_EX|LOCK_NB) { errmsg("Edit: Couldn't lock file. Try again"); $args->{error}=1; } else { print INIFILE "status=in\n"; } } } flock INIFILE,LOCK_UN; close INIFILE; delete $args->{text}; } 1; } sub Revokem { my ($r,$args,$cfg,$docroot) = @_; return 0 if $args->{return}; my $lang = new Apache::Language($r) if $cfg->{language}; my $msg=''; my $cmdname=$lang->{Revoke} || 'Revoke'; my $revgid=$args->{id} if $args->{enagid} || $args->{disgid}; my $revuser=$args->{id} if $args->{enauid} || $args->{disuid}; my $file="$docroot$args->{root}$cfg->{fakedir}".REVOKE_DIR; $file.=REVOKE_FILE; if($revgid=~m:[A-Za-z]:o) { $revgid=getgrnam $revgid; } if($revuser eq $args->{user} || $revgid==$cfg->{admin}) { $r->warn(__PACKAGE__ . " revoke self not allowed"); errmsg("admin IDs can not be revoked"); return 0; } else { my $result=1; $result=revoker($r,$args,$cfg,'user','+',$args->{id}) if $args->{enauid}; $result=revoker($r,$args,$cfg,'user','-',$args->{id}) if $args->{disuid}; $result=revoker($r,$args,$cfg,'gid', '+',$args->{id}) if $args->{enagid}; $result=revoker($r,$args,$cfg,'gid', '-',$args->{id}) if $args->{disgid}; unless($result) { $r->warn(__PACKAGE__ . " Revoke: $args->{user}: $args->{id}"); return 0; } else { $r->log->notice(__PACKAGE__." Revoke: $args->{user}: $args->{id}"); } } 1; } sub revoker { my ($r,$args,$cfg,$type,$action,$name)=@_; my $lang = new Apache::Language($r) if $cfg->{language}; my $msg; my $cmdname=$lang->{Revoke} || 'Revoke'; chomp $cmdname; print STDERR "revoker() type=$type action=$action name=$name\n" if $debug; unless($name) { $msg=$lang->{RevokeName} || 'No ID number or name provided'; errmsg("${cmdname}: $msg"); return 0; } $name= lc $name; my $docroot=$r->document_root; my $path="$docroot$args->{root}$cfg->{fakedir}".REVOKE_DIR; unless(-e $path) { chmod 0750,$path; # Attempt to create revoke dir unless(mkdir $path,0750) { # If it does not exist $msg=$lang->{create} || 'Can\'t create path'; $msg.=" $args->{root}$cfg->{fakedir}".REVOKE_DIR; $msg.=" $!"; errmsg($msg); chmod 0550,$path; return 0; } chmod 0550,$path; } my $file=$path.REVOKE_FILE; if(-e "$file.new") { # gross file locking, should never happen $r->warn(__PACKAGE__ . " revoke file locked: ${file}.new exists"); $msg=$lang->{FileLocked} || 'File locked'; errmsg("${cmdname}: $msg"); return 0; } my $server=$r->get_server_name; my $key; my $val; if($name=~m:[^0-9]:o) { # if not a number get the GID for the name $key=getgrnam $name || $name; } $key="#${key}#${server}#$args->{root}" if $type eq 'gid'; $key="${key}##${server}#$args->{root}" if $type eq 'user'; if($action eq '-') { # '-' implies disable user/group return 1 if $users->{$key} eq '-'; # return if already disabled $users->{$key}='-'; if(open REVOKE, ">>$file") { # append name to the revoke file print REVOKE "$type=$name\n"; close REVOKE; } else { $r->warn(__PACKAGE__ . " revoke file append open"); $msg=$lang->{FileOpen} || 'File open'; errmsg("${cmdname}: $msg"); return 0; } } elsif($action eq '+') { # '+' implies enable user/group return 1 if $users->{$key} eq '+'; # return if already enabled $users->{$key}='+'; if(open REVOKE, "<$file") { # remove name from revoke file if(open NEWREVOKE, ">$file.new") { while(
' unless $ishtml;
print STDERR "$ofile\n" if $debug;
outfile($ofile,$cfg->{options} & SUPPRESS_PREAMBLE);
unless($ishtml) {
print '';
etagout('hr',$cfg);
}
next;
}
} else {
$subr = $r->lookup_uri("${uri}${doc}");
if(stat $subr->finfo) {
$ofile=$subr->filename();
$ishtml=1 if $subr->content_type() eq 'text/html';
print '' unless $ishtml;
print STDERR "$ofile\n" if $debug;
outfile($ofile,$cfg->{options} & SUPPRESS_PREAMBLE);
unless($ishtml) {
print '';
etagout('hr',$cfg);
}
next;
}
}
print STDERR "\n\$cfg\n"; print Dumper $cfg; print "
\%args\n"; print Dumper \%args; print "
Global variables\n";
if($cfg->{revoke}) {
print "\$users\n";
print Dumper $users;
}
print "\$commands\n";
print Dumper %commands;
print "\$iconfig\n";
print Dumper $iconfig;
print "Environment variables\n"; print Dumper $r->subprocess_env(); print "