#!/usr/bin/perl -I. # TODO: Cache domain/workgroup per-drive share # TODO: Clickable box header # TODO: Input validation of form inputs # TODO: escape # TODO: Permission controls # TODO: Share-independent clipboard # TODO: GUI changes (?) # TODO: Set maximum file upload size via Apache:: # TODO: Display drive letter on browse page # TODO: 'Select all' button # TODO: Pull-down menu with bookmarks # TODO: Bookmark sorting # TODO: Display all file shares if there are no recently used ones # TODO: Display cut items with faded icon and/or text color # DONE: Rename (files and folders) # DONE: Make rename js popup prompt compatible with older browsers # DONE: Fix timestamps # DONE: Display file sizes in pretty KB # DONE: DNS resolution # DONE: Supernat # DONE: Set supernat addresses Pos::defaults # DONE: Error pages # DONE: WINS resolution # DONE: Delete confirmation dialog # DONE: Cut & paste # DONE: Directory deletion # DONE: 'Create directory' button # DONE: Handle deep-linked file shares (in preview pane, as well) # DONE: Preview pane with most commonly used list # DONE: Username & password prompt if auth credentials aren't cached # DONE: Share auth credentials with vpn client # DONE: Copy & paste # DONE: New smbclient-compatible Smb.pm use strict; use Pos::Webtop::FileShare::Smb; use Pos::Webtop::Web::ContentModule; use Digest::MD5 (); use Pos::Webtop::OcxLauncher (); package Pos::Webtop::Web::ContentModule::FileShares; our @ISA = qw( Pos::Webtop::Web::ContentModule ); sub getAclRules { my $self = shift; my $tb = $self->getToolBox(); my $db = $tb->getWebtopDbReader(); my $client = $tb->getClient(); my $userId = $client->getUserId(); if ( not defined $self->{'aclRules'} ) { my @aclRules = (); my @rules = $db->query( sql=> " select upper(protocol_name), location, upper(target_name) from w_user_acls a, w_acl_targets t, w_acl_protocols p where a.protocol_id = p.protocol_id and t.target_id = a.target_id and user_id = $userId order by rule_number "); foreach ( @rules ) { my ($protocol,$location,$target) = @{ $_ }; push @aclRules, { protocol=> $protocol, location=> $location, target=> $target }; } $self->{'aclRules'} = \@aclRules; } return $self->{'aclRules'}; } sub aclCanAccess { my $self = shift; my ($server,$servers,$path,$protocol) = @{{@_}}{qw/server servers path protocol/}; $path =~ s/\\/\//g; # convert to forward slashes # path comes in with a leading slash on it.. remove it $path =~ s/^\///g; # remove dupe slashes $path =~ s/[\/]+/\//g; # protocol is FILE for 'file shares (smb or other shares)' # protocol is FTP for ftp # servers allows you to pass an array ref of servers (like ip vs hostname) if ( not defined $servers ) { $servers = [$server]; } my @checkLocations; foreach ( @{ $servers } ) { my $checkLocation = $_ . "/" . $path; $checkLocation =~ s/[\\\/]$//g; $self->{'debug'} and warn "aclCanAccess: checkLocation: $checkLocation\n"; push @checkLocations, $checkLocation; # now we also check with a / at end incase it's a dir match in the policy $checkLocation .= "/"; $self->{'debug'} and warn "aclCanAccess: checkLocation: $checkLocation\n"; push @checkLocations, $checkLocation; } # now check locations is filled with 'all possible representations of where we are going' $protocol = "FILE" unless defined $protocol; my $tb = $self->getToolBox(); my $db = $tb->getWebtopDbReader(); my $aclRules = $self->getAclRules(); # we iterate the rules, considering all that are our protocol. my $defaultGood = 1; my $good = undef; foreach my $ar ( @{ $aclRules } ) { $ar->{'protocol'} eq $protocol or next; if ( not length( $ar->{'location'} ) ) { # this is a 'default' policy for this protocol... $defaultGood = ( $ar->{'target'} eq 'ALLOW' ? 1 : 0 ); next; } # now we have to convert the 'location' with * wildcards into a safe match regex # and compare it against. We have to keep track if the last char was the * char # since split will not put an 'empty' at the end of @pieces if it was... we need # that. my @pieces = split(/\*/,$ar->{'location'}); my @pieces2; if ( $ar->{'location'} =~ /\*$/ ) { push @pieces, ""; } foreach my $piece ( @pieces ) { $piece =~ s/^\\\\//g; # this will replace \ or / with [\/]... which matches any /\ in regex # note that I have to close and reopen the \Q \E combos here $piece =~ s/[\\\/]/\E\[\\\/\]\Q/g; push @pieces2, $piece; } # now @pieces2 is filled with safe chunks of regex that are / independent for path matches... @pieces = (); foreach my $piece ( @pieces2 ) { $piece = "" unless defined $piece; push @pieces, "\Q" . $piece . "\E"; } # now @pieces is filled with things that are safe to use in regex # place them in one big regex separated by .* my $regex = "^" . join(".*",@pieces) . "\$"; # now we compare the regex to all the @checkLocations entries... foreach ( @checkLocations ) { $self->{'debug'} and warn "aclCanAccess: regex: $regex\n"; if ( $_ =~ /$regex/i ) { # we match... $good = ( $ar->{'target'} eq 'ALLOW' ? 1 : 0 ); last; } } if ( defined $good ) { last; } } if ( not defined $good ) { $good = $defaultGood } return $good; } sub randomMd5 { my $data = rand() . rand() . rand() . rand(); my $md5 = Digest::MD5->new(); $md5->add( $data ); return $md5->hexdigest(); } # be sure to commit on the webtopDbWriter after calling to this sub storeShareInfo { my $self = shift; my ($server, $share, $user, $password) = @{{@_}}{qw/server share user password/}; $self->{'displayGroup'} = randomMd5() unless defined $self->{'displayGroup'}; $self->{'storedShareId'} = 0 unless defined $self->{'storedShareId'}; $self->{'storedShareId'}++; my $tb = $self->getToolBox(); my $db = $tb->getWebtopDbWriter(); $server = "" unless defined $server; $share = "" unless defined $share; $user = "" unless defined $user; $password = "" unless defined $password; $db->do( sql=> " insert into client_session_share_info ( client_session_id, display_group, share_id, server, sharename, username, password ) values ( " . $tb->getClient()->getSessionId() . ", '" . $self->{'displayGroup'} . "', " . $self->{'storedShareId'} . ", '" . $db->escape( dirty=> $server ) . "', '" . $db->escape( dirty=> $share ) . "', '" . $db->escape( dirty=> $user ) . "', '" . $db->escape( dirty=> $password ) . "' ) "); } sub getStoredShareInfo { my $self = shift; my ($shareId,$displayGroup) = @{{@_}}{qw/shareId displayGroup/}; return {} unless defined $shareId; return {} unless defined $displayGroup; my $tb = $self->getToolBox(); # even though this is a reader, i need the writer to be sure that we get the data from our last write my $db = $tb->getWebtopDbWriter(); my @info = $db->query( sql=> " select server, sharename, username, password from client_session_share_info where client_session_id = " . $tb->getClient()->getSessionId() . " and display_group = '" . $db->escape( dirty=> $displayGroup ) . "' and share_id = '" . $db->escape( dirty=> $shareId ) . "' "); return {} unless @info; my ($server,$share,$user,$password) = @{ $info[0] }; return { server=> $server, share=> $share, user=> $user, password=> $password }; } # be sure to call commit on the webtopDbWriter after using this # this isn't even used -- all items are just left in db until session expires # this shouldn't be an issue.. items are only stored on the front page or a # manual login. sub cleanupDisplayGroup { my $self = shift; my ($displayGroup) = @{{@_}}{qw/displayGroup/}; my $tb = $self->getToolBox(); my $db = $tb->getWebtopDbWriter(); $db->do( sql=> " delete from client_session_share_info where client_session_id = " . $tb->getClient()->getSessionId() . " and display_group = '" . $db->escape( dirty=> $displayGroup ) . "' "); } sub new { my $this = shift; my $class = ref($this) || $this; my ($self) = @{{@_}}{qw/self/}; if ( not defined $self ) { $self = {}; bless($self,$class); } # setup base class my %args = @_; $args{'self'} = $self; Pos::Webtop::Web::ContentModule->new( %args ); $self->{'smbShare'} = undef; $self->{'debug'} = 0; return $self; } sub altLinkCheck { my $self = shift; my $toolBox = $self->getToolBox(); my $client = $toolBox->getClient(); my $ret = ""; my $userId = $client->getUserId(); my $db = $toolBox->getWebtopDbReader(); my @altLink = $db->query( sql=> "select link_text, link_href from w_user_alt_fileshares where user_id = $userId" ); if ( @altLink ) { my ($text,$href) = @{ $altLink[0] }; $ret .= "Access Your File Shares:
" . HTML::Entities::encode($text) . "

\n"; } return $ret; } sub canConnectNewFileShare { my $self = shift; my $tb = $self->getToolBox(); my $client = $tb->getClient(); if ( not defined( $self->{'canConnectNewFileShare'} ) ) { my $moduleAccess = $client->getAllowedContentModules(); $self->{'canConnectNewFileShare'} = $moduleAccess->{'FileShares'}->{'sm_newFileshare'}; } return $self->{'canConnectNewFileShare'}; } # (pure virtual defined in Pos::Webtop::Web::ContentModule) sub main { my $self = shift; my $toolBox = $self->getToolBox(); my $client = $toolBox->getClient(); my $cgi = $toolBox->getCgi(); my $ret = ""; $client->checkSession(); $ret .= $self->altLinkCheck(); if ( length($ret) ) { my $page = $toolBox->getPage(); print $page->getHeaderHtml( title=> "File Share Browser" ); #$ret .= Pos::Webtop::OcxLauncher::getLauncherHtml( tb=> $toolBox ); print $ret; print $page->getFooterHtml(); return 1; } my $act = $cgi->param('act'); if ( defined $act ) { if ( $act eq "browseShareDir" || $act eq "Refresh" ) { $ret .= $self->getShareBrowseHtml(); } elsif ( $act eq "downloadFile" ) { my $temp = $self->downloadFile(); defined($temp) and $ret .= $self->downloadFile(); } elsif ( $act eq "uploadFile" || $act eq "Upload File" ) { $ret .= $self->uploadFile(); } elsif ( $act eq "Delete" ) { $ret .= $self->deleteItems(); } elsif ( $act eq "Rename" ) { $ret .= $self->renameItems(); } elsif ( $act eq "Cut" ) { $ret .= $self->addItemsToClipboard( cut => 1 ); } elsif ( $act eq "Copy" ) { $ret .= $self->addItemsToClipboard( cut => 0 ); } elsif ( $act eq "Paste" ) { $ret .= $self->pasteClipboardItems(); } elsif ( $act eq "Create Folder" ) { $ret .= $self->createFolder(); } elsif ( $act eq "RemoveDriveShare" ) { $ret .= $self->removeDriveShare(); } else { warn ref($self) . "->main(): user submitted an unknown act ('$act'); returning login page"; $ret .= $self->getShareLoginHtml(); } } else { $ret .= $self->getShareLoginHtml(); } print $ret; return 1; } # (pure virtual defined in Pos::Webtop::Web::ContentModule) sub getPreviewHtml { my $self = shift; my $ret = ""; my $toolBox = $self->getToolBox(); my $client = $toolBox->getClient(); my $cgi = $toolBox->getCgi(); $ret .= $self->altLinkCheck(); if ( length($ret) ) { return $self->getPreviewHeader() . $ret . $self->getPreviewFooter(); } eval { $toolBox->debugLog( msg => "######## ------------- blah") }; # OBSOLETE #my $userId = $client->getUserId(); $ret = $self->getPreviewHeader(); $ret .= " "; # Get all of the users file shares out of the db my (@appFileShares) = $self->getAppFileShares(); my (@webtopFileShares) = $self->getWebtopFileShares(); # TODO: combine these lists, then sort them based on last accessed date, then trim all but the top 5 my @unsortedFileShares = (@appFileShares,@webtopFileShares); my @accessedFileShares; if (@accessedFileShares) { $client->logger( severity=>'NORMAL', logMessage=>"WEBTOP Fileshares: ->getPreviewHtml() \@accessedFileShares has shit in it" ); } my @unaccessedFileShares = (); my %nodupes = (); foreach my $fileShare (@unsortedFileShares) { if ( defined $fileShare->{'fshLastAccess'} && length($fileShare->{'fshLastAccess'})) { if ( not defined $nodupes{ $fileShare->{'fsvHostname'} . ":" . $fileShare->{'fshName'} } ) { push(@accessedFileShares,$fileShare); $nodupes{ $fileShare->{'fsvHostname'} . ":" . $fileShare->{'fshName'} } = 1; } } else { push(@unaccessedFileShares,$fileShare); } } # if now we add more to the end to make it at least 5 even if they weren't accessed ever while ( scalar(@accessedFileShares) < 5 && scalar(@unaccessedFileShares) ) { my $fileShare = shift @unaccessedFileShares; if ( not defined $nodupes{ $fileShare->{'fsvHostname'} . ":" . $fileShare->{'fshName'} } ) { $fileShare->{'fshLastAccess'} = 0; push @accessedFileShares, $fileShare; } } my @fileShares = sort { $b->{'fshLastAccess'} cmp $a->{'fshLastAccess'} } @accessedFileShares; # Now that the sorting is done, just take the top 5 @fileShares = @fileShares[0..4]; my $tb = $self->getToolBox(); my $dbw = $tb->getWebtopDbWriter(); my $skinOemId = Pos::Webtop::Web::OEMLoader::getSkinOemId( oemId=> $tb->getOemId ); my $hadSomeDriveLetter = 0; foreach ( @fileShares ) { if ( $_->{'fshDriveLetter'} ) { $hadSomeDriveLetter = 1; last; } } foreach my $fileShare (@fileShares) { #warn " Last access for ($fileShare->{'fshDriveLetter'}) $fileShare->{'fsvHostname'}: $fileShare->{'fshLastAccess'}"; if ( not ( defined $fileShare->{'fsvHostname'} && defined $fileShare->{'fshName'} ) ) { next; } my $sharePath = "\\\\" . $fileShare->{'fsvHostname'} . "\\" . $fileShare->{'fshName'} . "\\"; $sharePath .= $fileShare->{'fshInitialFolder'} unless not defined $fileShare->{'fshInitialFolder'}; $self->storeShareInfo( server=> $fileShare->{'fsvHostname'}, share=> $fileShare->{'fshName'}, user=> $fileShare->{'fsaUsername'}, password=> $fileShare->{'fsaPassword'} ); if ( $hadSomeDriveLetter eq 1 ) { $ret .= " " . ( defined $fileShare->{'fshDriveLetter'} ? ("") : "" ) . " "; } elsif ( $hadSomeDriveLetter eq 0 ) { $ret .= " "; } $ret .= " "; } $dbw->commit(); $ret .= "
Recently used file shares:
   (" . $fileShare->{'fshDriveLetter'} . ":)
   " . ( $client->getFormGetCode( text => "$sharePath", values => [ { name => "act", value => "browseShareDir" }, { name => ( $client->getPersistPrefix() . "module" ), value => "FileShares" }, { name => "currentDir", value => HTML::Entities::encode(( "\\" . ( defined $fileShare->{'fshInitialFolder'} ? $fileShare->{'fshInitialFolder'} : "" ) )) }, { name => "displayGroup", value=> $self->{'displayGroup'} }, { name => "shareId", value=> $self->{'storedShareId'} }, { name => "initialFolder", value => HTML::Entities::encode(( "" . ( defined $fileShare->{'fshInitialFolder'} ? $fileShare->{'fshInitialFolder'} : "" ) )) }, { name => "isRecent", value=> 1 }, { name => "admintool", vaue=> $fileShare->{'fshAdmintool'} } ])) . "

" . $client->getFormGetCode( values => [ { name => 'module', value => 'FileShares' } ], text => "More..." ) . "
"; # text => "
File Share Browser" $ret .= " "; $ret .= $self->getPreviewFooter(); return $ret; } sub getPreviewTitle { return shift->{'previewTitle'}; } sub getTitle { return shift->{'title'}; } sub getPreviewWidth { return shift->{'previewWidth'}; } # Returns Share Login Page HTML sub getShareLoginHtml { my $self = shift; my $toolBox = $self->getToolBox(); my $client = $toolBox->getClient(); my $cgi = $toolBox->getCgi(); my $isMobile = $client->isMobileWebtop(); my ($topText) = @{{@_}}{qw/topText/}; # Setup the form entry fields my @loginFields = ( { name => "serverName", desc => "Server Address", size => ( $isMobile ? "10" : "25" ) }, { name => "shareName", desc => "Share Name", size => ( $isMobile ? "10" : "15" ) }, { name => "shareUserName", desc => "Share Username", size => ( $isMobile ? "10" : "15" ) } ); # Page header my $page = $toolBox->getPage(); # Currently, this is the default page for this content module so we won't add anything #$page->addNavBarElement( description => "Login" ); my $ret = $page->getHeaderHtml( title => "File Shares: Login" ); #$ret .= Pos::Webtop::OcxLauncher::getLauncherHtml( tb=> $toolBox ); my $listBox = $toolBox->createBox( title => "File Shares", width => '500' ); my $customBox = $toolBox->createBox( title => "Connect to a New File Share" , width => '500'); # Form header $ret .= "
"; $ret .= $client->getFormInputs(); # 'Choose a file share' box $ret .= "
" . ( $listBox->getHeaderHtml() ) . " " . ($isMobile ? "" : " ") . " "; # TODO: Sort listing by drive letter, then by hostname, then by sharename # List all of the file shares from the app and webtop tables my (@appFileShares) = $self->getAppFileShares(); my (@webtopFileShares) = $self->getWebtopFileShares(); my $dbw = $toolBox->getWebtopDbWriter(); foreach my $fileShare (@appFileShares,@webtopFileShares) { my $sharePath = "\\\\" . $fileShare->{'fsvHostname'} . "\\" . $fileShare->{'fshName'} . "\\" . ( defined $fileShare->{'fshInitialFolder'} ? $fileShare->{'fshInitialFolder'} : "" ); my (undef,$mainServer,$mainShare,$mainFolder) = split(/\\{1,2}/,$sharePath,4); $self->storeShareInfo( server=> $fileShare->{'fsvHostname'}, share=> $fileShare->{'fshName'}, user=> $fileShare->{'fsaUsername'}, password=> $fileShare->{'fsaPassword'} ); $ret .= " " . ($isMobile ? "" : " ") . " "; } $dbw->commit(); $ret .= "
Click on a file share below to connect
File Share Username Action
" . ( defined $fileShare->{'fshDriveLetter'} ? ("(" . $fileShare->{'fshDriveLetter'} . ":) ") : "" ) . ( $client->getFormGetCode( text => "" . (defined($sharePath) ? "$sharePath" : "") . "", values => [ { name => "act", value => "browseShareDir" }, { name => "currentDir", value => HTML::Entities::encode(( "\\" . ( defined $fileShare->{'fshInitialFolder'} ? $fileShare->{'fshInitialFolder'} : "" ) )) }, { name => "displayGroup", value=> (defined($self->{'displayGroup'}) ? $self->{'displayGroup'} : "") }, { name => "shareId", value=> (defined($self->{'storedShareId'}) ? $self->{'storedShareId'} : "") }, { name => "isRecent", value=> 1 }, { name => "initialFolder", value=> HTML::Entities::encode(( "" . ( defined $mainFolder ? $mainFolder : "" ) )) }, { name => "admintool", value=> $fileShare->{'fshAdmintool'} }, ])) . " " . (defined($fileShare->{'fsaUsername'}) ? $fileShare->{'fsaUsername'} : "") . " " . (defined $fileShare->{'fshDriveLetter'} ? "" : " [" . $client->getFormGetCode( text => 'remove', values => [ { name => 'module', value => 'FileShares' }, { name => 'fileShareHostName', value => $mainServer }, { name => 'act', value => 'RemoveDriveShare' }, { name => 'fileShareName', value => $mainShare }, { name => 'fileShareInitialFolder', value => $mainFolder } ]) ."]") . "
\n" . $listBox->getFooterHtml() . "
\n"; if ($self->canConnectNewFileShare()) { # 'New file share' box $ret .= " " . ( $customBox->getHeaderHtml() ) . " "; # Form and table footer $ret .= "
Please enter the information below to access a file share
Remote Address
Share Username
Share Password
" . $customBox->getFooterHtml() . "
\n"; } $ret .= $page->getFooterHtml(); return $ret; } # Unable to get directory listing (unknown error) - asks users to re-enter server, share, username, password sub getShareConnectErrorHtml { my $self = shift; my $toolBox = $self->getToolBox(); my $cgi = $toolBox->getCgi(); my $dbr = $toolBox->getWebtopDbReader(); my $dbw = $toolBox->getWebtopDbWriter(); my $client = $toolBox->getClient(); my $userId = $client->getUserId(); my $isMobile = $client->isMobileWebtop(); my ($initialTry) = @{{@_}}{qw/initialTry/}; $initialTry = 0 unless defined $initialTry; my ($admintool) = @{{@_}}{qw/admintool/}; $admintool = 1 unless defined $admintool; # Format currentDir my $currentDir = $cgi->param('currentDir'); $currentDir = '\\' unless $currentDir; my $shareId = $client->getPersistedValue( name=> 'shareId' ); $shareId = $cgi->param('shareId') if defined $cgi->param('shareId'); my $displayGroup = $client->getPersistedValue( name=> 'displayGroup' ); $displayGroup = $cgi->param('displayGroup') if defined $cgi->param('displayGroup'); my $shareInfo = $self->getStoredShareInfo( shareId => $shareId, displayGroup=> $displayGroup ); my $shareUserName = $cgi->param('shareUserName'); !defined($shareUserName) || !length($shareUserName) and $shareUserName = $shareInfo->{'user'}; my $sharePassword = $shareInfo->{'password'}; #my $shareName = $shareInfo->{'share'}; my $shareName = $cgi->param('shareName'); #my $serverName = $shareInfo->{'server'}; my $serverName = $cgi->param('serverName'); defined($serverName) and $serverName =~ s/^\\\\//g; $client->setPersistedValue( name=> "shareId", value=> $shareId ); $client->setPersistedValue( name=> "displayGroup", value=> $displayGroup ); my $initialFolder = $cgi->param('initialFolder'); my $errorMessageText = undef; my $inputTextForServer = undef; my $inputTextForShare = undef; # Page header my $page = $toolBox->getPage(); # Currently, this is the default page for this content module so we won't add anything #$page->addNavBarElement( description => "Error: " ); my $ret = $page->getHeaderHtml( title => "File Shares: Login" ); #$ret .= Pos::Webtop::OcxLauncher::getLauncherHtml( tb=> $toolBox ); my $customBox = $toolBox->createBox( title => "File Share Login", width => '500' ); my $remoteAddress = $cgi->param('remoteAddress'); if($admintool eq '1') { !defined($remoteAddress) || !length($remoteAddress) and $remoteAddress = "\\\\" . $shareInfo->{'server'} . (defined($shareInfo->{'share'}) ? "\\" . $shareInfo->{'share'} . "" : "") . (defined($currentDir) ? "$currentDir" : ""); while ( $remoteAddress =~ /\\$/ ) { chop $remoteAddress } $errorMessageText = "The drive share or username and password specified by your administrator are invalid. You may enter your username and password to gain access to the file share. If this problem persists, contact your administrator. (Note: Use your network username and password from work)"; $inputTextForServer = "" . HTML::Entities::encode($remoteAddress) . ""; } else { !defined($remoteAddress) || !length($remoteAddress) and $remoteAddress = "\\\\" . $shareInfo->{'server'} . (defined($shareInfo->{'share'}) ? "\\" . $shareInfo->{'share'} . "" : "") . (defined($currentDir) ? "$currentDir" : ""); while ( $remoteAddress =~ /\\$/ ) { chop $remoteAddress } #!defined($remoteAddress) and $remoteAddress = $serverName . (defined($shareName) && length($shareName)? "\\$serverName" : "") . (defined($currentDir) && length($currentDir) ? "\\$currentDir" : ""); $errorMessageText = "Invalid drive share, username, or password. Please verify information and reenter your password. (Note: Use your network username and password from work)"; $inputTextForServer = ""; } # Form header $ret .= "
"; $ret .= $client->getFormInputs(); $ret .= "
" . ( $customBox->getHeaderHtml() ) . " "; # Form and table footer $ret .= "
$errorMessageText
Server Name $inputTextForServer
Share Username
Share Password
" . $client->getFormGetCode( values => [ { name => 'module', value => 'FileShares' } ], text => "Click here to go back" ) . "
" . $customBox->getFooterHtml() . "
\n"; $ret .= $page->getFooterHtml(); return $ret; } # Normal browsing page - displays the contents of a folder in a file share sub getShareBrowseHtml { my $self = shift; my $toolBox = $self->getToolBox(); my $cgi = $toolBox->getCgi(); my $dbr = $toolBox->getWebtopDbReader(); my $dbw = $toolBox->getWebtopDbWriter(); my $client = $toolBox->getClient(); my $userId = $client->getUserId(); my $isMobile = $client->isMobileWebtop(); # So far, I am unable to detect the file share block size with stat(), so it's static my $shareBlockSize = 512; # TODO: Persist the username and password in the database # TODO: Detect the share type (NetWare, DFS, etc.) my $shareId = $client->getPersistedValue( name=> 'shareId' ); $shareId = $cgi->param('shareId') if defined $cgi->param('shareId'); my $displayGroup = $client->getPersistedValue( name=> 'displayGroup' ); $displayGroup = $cgi->param('displayGroup') if defined $cgi->param('displayGroup'); my $shareInfo = $self->getStoredShareInfo( shareId => $shareId, displayGroup=> $displayGroup ); my $shareUserName = $shareInfo->{'user'}; my $sharePassword = $shareInfo->{'password'}; my $shareName = $shareInfo->{'share'}; my $serverName = $shareInfo->{'server'}; $serverName =~ s/^\\\\//g; $client->setPersistedValue( name=> "shareId", value=> $shareId ); $client->setPersistedValue( name=> "displayGroup", value=> $displayGroup ); my $admintool = $cgi->param('admintool'); my $manualFileShare = $cgi->param('manualFileShare'); my $manualFileShareAddress = $cgi->param('remoteAddress'); my $fromCache = 0; my $currentDir = $cgi->param('currentDir'); my $initialFolder = $cgi->param('initialFolder'); my $usedUserValues = 0; # if this is set, we will NOT look for cached goodness later if ( (defined $cgi->param('shareUserName') and length $cgi->param('shareUserName')) || $manualFileShare ) { (undef,$serverName,$shareName,$currentDir) = split(/\\{1,2}/,$manualFileShareAddress,4); $initialFolder = $currentDir; $currentDir = "\\" . $currentDir; $usedUserValues = 1; $shareUserName = $cgi->param('shareUserName'); $sharePassword = $cgi->param('sharePassword'); $serverName =~ s/^\\\\//g; $self->storeShareInfo( user=> $shareUserName, password=> $sharePassword, server=> $serverName, share=> $shareName ); $dbw->commit(); $client->setPersistedValue( name=> 'displayGroup', value=> $self->{'displayGroup'} ); $client->setPersistedValue( name=> 'shareId', value=> $self->{'storedShareId'} ); $self->{'initShareId'} = $self->{'storedShareId'}; $self->{'initDisplayGroup'} = $self->{'displayGroup'}; } my ($canCacheCredentials) = $dbr->query( sql=> " select can_Cache_credentials from user_share_credential_caching where user_id = $userId " ); $canCacheCredentials = 1 unless defined $canCacheCredentials; # here's a special hack that raz put in... the idea is this: with the new policy to allow or disallow # credential caching, people without caching turned on can click on recently used links and they will not # have usernames or passwords assigned to those shares. This means that if they previously used a share # with login creds and then go back via the recent list, they are trying to auth without any creds initially... # this is normally fine but if the remote server allows anonymous logins they are going to get in without the right # permissions. To work around this, I have made the system return the connection error / login page screen before trying # anything if the user has no password and username.... and only if it doesn't see the submit from the password prompt. # This allows the user to enter an initial credential set for this use case. # Format currentDir $currentDir = '\\' unless $currentDir; # Remove duplicate backslashes while ( $currentDir =~ s/\\\\/\\/g ) { } # OBSOLETE #my $currentDirEsc = $currentDir; #$currentDirEsc =~ s/\\/\\\\/g; #cut off trailing slashes chop($currentDir) if ($currentDir =~ /^(.+)\\$/); chop($initialFolder) if ($initialFolder =~ /^(.+)\\$/); # Set the upDir and upDirEsc strings my $upDir = undef; if ($currentDir eq "\\" . $initialFolder) { $upDir = undef; } elsif ( $currentDir =~ /^(.*)\\[^\\].*$/ ) { $upDir = $1; } elsif ( $currentDir ne "\\" && $currentDir ne "" ) { $upDir = "\\"; } my $upDirEsc = $upDir; if ( defined $upDir ) { $upDirEsc =~ s/\\/\\\\/g; } # Page header my $page = $toolBox->getPage(); $page->addNavBarElement( description => "\\\\$serverName\\$shareName", values => [ { name => "act", value => "browseShareDir" }, { name => "currentDir", value => ($upDir) } ] ); my $path = $dbr->escapeLike(dirty => "\\\\$serverName\\$shareName"); # this block now runs to 'try auth cache for share' no matter what (this MUST happen before the page header so the # block of code can replace a persisted value and have it correctly in the page header if ( ! $usedUserValues ) { $client->logger( severity=>'NORMAL', logMessage=>"WEBTOP Fileshares: ->getShareBrowserHtml() Looking for auth credentials in netresource auth cache" ); my (@authCacheRows) = $dbr->query( sql => " SELECT net_user, net_pass FROM netresource_auth_cache WHERE network_path like '$path%' AND user_id = '$userId' " . $dbr->getLimitOneRowString(prefix => 'AND' ) . " "); foreach my $authCacheRow (@authCacheRows) { my $username = $authCacheRow->[0]; my $password = $authCacheRow->[1]; if ( ( defined $username && length $username ) || ( defined $password && length $password ) ) { $password = "" unless defined $password; $username = "" unless defined $username; $client->logger( severity=>'NORMAL', logMessage=>"WEBTOP Fileshares: ->getShareBrowserHtml() Got cached username '$username' and password HIDDEN; resetting persisted values" ); #$client->setPersistedValue( name => 'shareUserName', value => $username ); #$client->setPersistedValue( name => 'sharePassword', value => $password ); $shareUserName = $username; $sharePassword = $password; # the init share function will use the new share number here to setup the share... # they will be retrieved via the getPersistedValue function $self->storeShareInfo( user=> $shareUserName, password=> $sharePassword, share=> $shareName, server=> $serverName ); $client->setPersistedValue( name=> 'shareId', value=> $self->{'storedShareId'} ); $client->setPersistedValue( name=> 'displayGroup', value=> $self->{'displayGroup'} ); $dbw->commit(); # make sure stored share info is saved # we have to override the stored share id and display group for the init function to work right $self->{'initShareId'} = $self->{'storedShareId'}; $self->{'initDisplayGroup'} = $self->{'displayGroup'}; # Indicate that credentials were obtained through the cache. $fromCache = 1; } } } # If the credentials weren't obtained through the cache and # they have an empty password, prompt them to enter it. if (!$fromCache and not ( defined($shareUserName) and length($shareUserName) and defined($sharePassword) and length($sharePassword) ) and defined $cgi->param('isRecent') ) { $client->logger( severity=>'NORMAL', logMessage=>"WEBTOP Fileshares: ->getShareBrowserHtml() detected user with no password caching ability -- giving a chance to provide credentials." ); return $self->getShareConnectErrorHtml( initialTry=> 1, admintool=> $admintool ); } my $ret .= $page->getHeaderHtml( title => "File Share Browser: Connected"); #$ret .= Pos::Webtop::OcxLauncher::getLauncherHtml( tb=> $toolBox ); # TODO: Clickable folders in link on # my $titleHtml = "File Share Browser: \\" . # ( $client->getFormGetCode( # text => "$sharePath", # values => [ # { name => "act", value => "browseShareDir" }, # { name => "currentDir", value => ($upDir) } # ]) # ) . ""; # my $browseBox = $toolBox->createBox( # title => "File Share Browser: \\\\$serverName\\$shareName$currentDir", # width => "100%" # ); my $currentDirCopy = $currentDir; my $initialFolderCopy = $initialFolder; $initialFolderCopy =~ s/\\/\\\\/g; $currentDirCopy =~ s/^\\$initialFolderCopy//g; $initialFolderCopy =~ s/\\\\/\\/g; my @initialFolders = split /\\/,$initialFolderCopy; my @currentDirs = split /\\/,$currentDirCopy; my $cleanShareName = $shareName; $cleanShareName =~ s/\\//g; my $shareLink = $client->getFormGetCode( text => ("$cleanShareName", tagAttributes => [ { name => "class", value => "BOXLINK"}, ], values => [ { name => "act", value => "browseShareDir" }, { name => "initialFolder", value => $initialFolder }, { name => "currentDir", value => "\\" } ])); my $dow = ""; my $dizzle = ""; my $i = 1; foreach my $initialFolders ( @initialFolders ) { if (!length($initialFolders)) { next; } $dizzle .= "\\$initialFolders"; $dizzle =~ s/\\\\/\\/g; if ($i == scalar(@initialFolders)) { $dow .= "\\"; $dow .= $client->getFormGetCode( text => ("$initialFolders", tagAttributes => [ { name => "class", value => "BOXLINK"}, ], values => [ { name => "act", value => "browseShareDir" }, { name => "initialFolder", value => $initialFolder }, { name => "currentDir", value => "$dizzle" } ])); } else { $dow .= "\\$initialFolders"; } $i++; } my $dow2 = ""; my $dizzle2 = ""; foreach my $currentDirs ( @currentDirs ) { if (!length($currentDirs)) { next; } $dizzle2 .= "\\$currentDirs"; $dizzle2 =~ s/\\\\/\\/g; $dow2 .= "\\"; $dow2 .= $client->getFormGetCode( text => ("$currentDirs", tagAttributes => [ { name => "class", value => "BOXLINK"}, ], values => [ { name => "act", value => "browseShareDir" }, { name => "initialFolder", value => $initialFolder }, { name => "currentDir", value => "\\$initialFolder$dizzle2" } ])); } my $browseBox = $toolBox->createBox( title=>"File Share Browser: \\\\$serverName\\" . (length($dow) ? "$cleanShareName$dow" : "$shareLink" ) . "" . (length($dow2) ? "$dow2" : "" ) . "", width => "100%" ); # JS, Form and table header $ret .= " "; $ret .= "
" . ( $client->getFormInputs ) . $browseBox->getHeaderHtml() . " " . ($isMobile ? "" : " " ) . " "; # If no username was entered, try to get username/password from netresource auth cache #if ( ( not defined $shareUserName ) || ( $shareUserName eq "" ) ) #if ( ( not defined $shareUserName ) || ( $shareUserName eq "" ) || ( not defined $shareUserName ) || ( $shareUserName eq "" ) ) # Initialize smbShare object if ( !$self->initSmbShare() ) { $client->logger( severity=>'ERROR', logMessage=>"WEBTOP Fileshares: ->getShareBrowserHtml() couldn't initialize Smb object; returning error text" ); if($admintool eq '1') { return $toolBox->getErrorPage( errorTitle => "File Share Browser", errorMsg => "Unable to connect to file share", currentDir => $currentDir, initialFolder => $initialFolder ); } else { return $self->getShareConnectErrorHtml( admintool => 0 ); } } my $share = $self->{'smbShare'}; if ( not $self->aclCanAccess( servers=> $self->{'remoteServerValues'}, path=> $shareName . $currentDir, protocol=> "FILE" ) ) { return $toolBox->getErrorPage( errorTitle => "File Share Browser", errorMsg => "Access to the requested resource is denied by your policies", currentDir => $currentDir, initialFolder => $initialFolder ); } # Get directory listing ( { itemType => 'file', itemName= 'dog.txt' }, ... ) my $dirListing = $share->getDirListing( dirPath => $currentDir, getExtendedInfo => 1 ); if ( not defined $dirListing) { $client->logger( severity=>'ERROR', logMessage=>"WEBTOP Fileshares: ->getShareBrowserHtml() couldn't get directory listing for '$currentDir'; returning error text" ); return $self->getShareConnectErrorHtml( admintool => $admintool ); # return $toolBox->getErrorPage( errorTitle => "File Share Browser", errorMsg => "Unable to get directory listing (you may not have access to this folder)" ); } # If this was a custom drive map, add it to the webtop fileshare database # TODO: Update the 'recently used file shares' table, as well # (Note: We wouldn't want to do this if we hadn't gotten a directory listing) my $fileShareId; if ( defined $fileShareId) { $client->logger( severity=>'NORMAL', logMessage=>"WEBTOP Fileshares: ->getShareBrowserHtml() \$fileShareId has shit in it" ); } # This is to prevent adding duplicate drive shares my ($testUserId) = $dbr->query( sql => " select user_id from client_drive_shares where user_id = '$userId' and remote_addr = '" . $dbr->escape( dirty => "\\\\$serverName\\$shareName" . (defined($currentDir) && $currentDir ne "\\" ? "$currentDir" : "" ) . "" ) . "' "); my $customerId = $client->getCustomerId(); if ( defined $cgi->param('share_login_submit') && $cgi->param('share_login_submit') eq 'Connect' && not defined($testUserId)) { # Check for existing fileserver entry in db for this customer my ($fileServerId) = $dbr->query( sql => " SELECT fileserver_id FROM w_smb_fileservers WHERE customer_id = '$customerId' AND fileserver_hostname = '" . $dbr->escape( dirty => $serverName ) . "' "); # If there isn't one with the same hostname already, create one if ( not defined $fileServerId ) { # Get the next fileserver_id sequence ($fileServerId) = $dbr->getNextSequenceValue(sequence => "w_smb_fileservers_pk"); # Add the new fileserver $dbw->do( sql => " INSERT INTO w_smb_fileservers (fileserver_id, customer_id, fileserver_hostname, display_name) VALUES ('$fileServerId', '$customerId', '" . $dbr->escape( dirty => $serverName ) . "', '". $dbr->escape( dirty => $serverName ) . "') "); } # Check for existing fileshare entry in db for this fileserver ($fileShareId) = $dbr->query( sql => " SELECT fileshare_id FROM w_smb_fileshares WHERE fileserver_id = '$fileServerId' AND fileshare_name = '" . $dbr->escape( dirty => "$shareName" . (defined($currentDir) && $currentDir ne "\\" ? "$currentDir" : "" ) . "" ) . "' "); # If there isn't a fileshare with the same sharename already, create one if ( not (defined $fileShareId && $fileShareId) ) { # Get the next available fileshare_id (or 0 if none) ($fileShareId) = $dbw->getNextSequenceValue( sequence => "w_smb_fileshares_pk"); # Add the new fileshare $dbw->do( sql => " INSERT INTO w_smb_fileshares (fileshare_id, fileserver_id, fileshare_name) VALUES ('$fileShareId', '$fileServerId', '" . $dbr->escape( dirty => "$shareName" . (defined($currentDir) && $currentDir ne "\\" ? "$currentDir" : "" ) . "" ) . "') "); # Link the newly-created fileshare to the user $dbw->do( sql => " INSERT INTO w_smb_fileshare_users (fileshare_id, user_id) VALUES ('$fileShareId', '$userId') "); } # In all cases, we need to make sure the user is linked to this fileserver # See if there's already an entry in the db linking the fileshare to this user my ($areUserShareLinked) = $dbw->query( sql => " SELECT count(fileshare_id) FROM w_smb_fileshare_users WHERE user_id = '$userId' AND fileshare_id = '$fileShareId' "); # If the user isn't linked to the fileshare... if ( not ( defined $areUserShareLinked && $areUserShareLinked ) ) { # Link the existing fileshare to the user $dbw->do( sql => " INSERT INTO w_smb_fileshare_users (fileshare_id, user_id) VALUES ('$fileShareId', '$userId') "); } # Check for an existing entry in the webtop fileshare auth cache my ($authCached) = $dbw->query( sql => " SELECT count(fileshare_id) FROM w_smb_fileshare_auth_cache WHERE user_id = '$userId' AND fileshare_id = '$fileShareId' "); # If there are no auth credentials cached... if ( !$authCached and $canCacheCredentials and defined($shareUserName) and length($shareUserName) ) { my $sessionId = $client->getSessionId(); # Cache the ones being used (they seem to have worked) # Note: insert a web_session_id to cache them for this session only $dbw->do( sql => " INSERT INTO w_smb_fileshare_auth_cache (client_session_id, user_id, fileshare_id, user_name, password) VALUES ('$sessionId', '$userId', '$fileShareId', '" . $dbr->escape( dirty => $shareUserName ) . "', '" . $dbr->escape( dirty => $sharePassword ) . "') "); } } # Get a fileShareId, if there is one ($fileShareId) = $dbw->query( sql => " SELECT fsh.fileshare_id FROM w_smb_fileshares fsh, w_smb_fileservers fsv, w_smb_fileshare_users fsu WHERE fsh.fileshare_name = '" . $dbr->escape( dirty => $shareName ) . "' AND fsh.fileserver_id = fsv.fileserver_id AND fsv.fileserver_hostname = '" . $dbr->escape( dirty => $serverName ) . "' AND fsv.customer_id = '$customerId' AND fsu.user_id = '$userId' AND fsu.fileshare_id = fsh.fileshare_id "); # Update the last access date so we can sort by that in the preview pane # Only if this is a fileshare created from the webtop should we attempt to update the webtop tables if ( defined $fileShareId ) { $client->logger( severity=>'NORMAL', logMessage=>'WEBTOP Fileshares: ->getShareBrowserHtml() Updating last access date for webtop fileshare'); $dbw->do( sql => " UPDATE w_smb_fileshare_users SET last_access_date = " . $dbw->getCurrentDateString() . " WHERE fileshare_id = '$fileShareId' AND user_id = '$userId' "); } # else # { $client->logger( severity=>'NORMAL', logMessage=>'WEBTOP Fileshares: ->getShareBrowserHtml() Updating last access date for vpn fileshare' ); # TODO: Only update the drive share they're actually using from client_drive_shares $dbw->do( sql => " UPDATE client_drive_shares SET last_access_date = " . $dbw->getCurrentDateString() . " WHERE user_id = '$userId' AND restrict_set_id = " . $client->getRestrictionSetId . " AND remote_addr LIKE '$path%' "); # } # Store (potentially) updated auth credentials in netresource_auth_cache # TODO: Don't break multiple folders with different auth credentials (DFS, etc.) # First, see if there are any auth credentials cached my ($isAuthCached) = $dbw->query( sql => " SELECT count(user_id) FROM netresource_auth_cache WHERE network_path like '$path%' AND user_id = '$userId' "); $client->logger( severity=>'NORMAL', logMessage=>"WEBTOP Fileshares: ->getShareBrowserHtml() ISAUTHCACHED: $isAuthCached SERVERNAME: " . $serverName . " SHARE: " . $shareName . ""); # If there's already an entry, just update it if ( $isAuthCached ) { $dbw->do( sql => " UPDATE netresource_auth_cache SET net_user = '" . $dbr->escape( dirty => $shareUserName ) . "', net_pass = '" . $dbr->escape( dirty => $sharePassword ) . "' WHERE network_path like '$path%' AND user_id = '$userId' "); } # Otherwise, create a new one elsif ( $canCacheCredentials ) { $dbw->do( sql => " INSERT INTO netresource_auth_cache (user_id, network_path, net_user, net_pass) VALUES ('$userId','\\\\" . ( Pos::defaults::isPostgresDb() ? "\\\\" : "") . "" . $dbr->escape( dirty => $serverName ) . "\\" . (Pos::defaults::isPostgresDb() ? "\\" : "") . "" . $dbr->escape( dirty => $shareName ) . "', '" . $dbr->escape( dirty => $shareUserName ) . "', '" . $dbr->escape( dirty => $sharePassword ) . "') "); } # Sort the directory listing (first by name, then by type) my @sortedDirListing = sort { ($a->{'itemType'} . uc($a->{'itemName'})) cmp ($b->{'itemType'} . uc($b->{'itemName'})) } @{$dirListing}; # List all items in the directory # First, a link to upDir if ( defined $upDir ) { $ret .= " "; } # Used for converting the string-like time that is returned by Smb.pm my %months = ( "Jan" => 0, "Feb" => 1, "Mar" => 2, "Apr" => 3, "May" => 4, "Jun" => 5, "Jul" => 6, "Aug" => 7, "Sep" => 8, "Oct" => 9, "Nov" => 10, "Dec" => 11 ); # Then, list each subdirectory foreach my $dirItem (@sortedDirListing) { if ( not defined $dirItem->{'itemType'} ) { next; } if ( $dirItem->{'itemType'} eq "directory" ) { # Create printable time my ($sec,$min,$hour,$mday,$mon,$year) = split(/\s/,$dirItem->{'itemMtime'}); my $modTime = ( $mon + 1 ) . "/$mday/" . ( $year ) . " " . ( $hour > 12 ? (24 - $hour) : $hour ) . ":$min " . ( $hour > 12 ? "PM" : "AM" ); $ret .= " " . ($isMobile ? "" : " " ) . " "; } # Finally, list each file elsif ( $dirItem->{'itemType'} eq "file" ) { # Create printable time my ($sec,$min,$hour,$mday,$mon,$year) = split(/\s/,$dirItem->{'itemMtime'}); my $modTime = ( $mon + 1 ) . "/$mday/" . ( $year ) . " " . ( $hour > 12 ? (24 - $hour) : $hour ) . ":$min " . ( $hour > 12 ? "PM" : "AM" ); # Create printable filesize (in kb) my $rfileSizeKb = reverse (sprintf("%d", ( $dirItem->{'itemBytes'} / 1024 ))); $rfileSizeKb =~ s/(\d\d\d)(?=\d)(?!\d*\.)/$1,/g; my $fileSizeKb = reverse $rfileSizeKb; # DEPRECATED: Display MB # " . ( $fileSizeKb < 1024 ? "$fileSizeKb KB" : "$fileSizeMb MB" ) . " $ret .= " " . ($isMobile ? "" : " " ) . " "; } } $ret .= " "; # Form and table footer $dbr->commit(); $dbw->commit(); $ret .= "\n
" . ( $isMobile ? "" : "
                     Filename:   
" ) . "
Name To download a file, right-click it and select \"Save Target As...\"  
Size Date Modified
" . ( $client->getFormGetCode( text => " Parent Directory", tagAttributes => [ { name => "class", value => "BOXLINK" } ], values => [ { name => "act", value => "browseShareDir" }, { name => "initialFolder", value => $initialFolder }, { name => "currentDir", value => ($upDir) } ])) . "    
" . ($isMobile ? "\ \ " : " {'itemName'} ) ) . "\">" ) . " " . ( $client->getFormGetCode( text => (" " . $dirItem->{'itemName'}) . "", tagAttributes => [ { name => "class", value => "BOXLINK" } ], values => [ { name => "act", value => "browseShareDir" }, { name => "initialFolder", value => HTML::Entities::encode(( "" . ( defined $initialFolder ? $initialFolder : "" ) )) }, { name => "currentDir", value => ("$currentDir\\" . $dirItem->{'itemName'}) } ] ) ) . "   $modTime
" . ($isMobile ? "\ \ " : " {'itemName'} ) ) . "\">" ) . " " . ( $client->getFormGetCode( text => (" " . $dirItem->{'itemName'}) . "", tagAttributes => [ { name => "class", value => "BOXLINK"}, { name => "target", value => "_blank"} ], values => [ { name => "act", value => "downloadFile" }, { name => "currentDir", value => ($currentDir) }, { name => "downloadFilename", value => ($dirItem->{'itemName'}) } ])) . " $fileSizeKb KB $modTime
" . ($isMobile ? "" : "
                    
" ) . "
\n" . ( $browseBox->getFooterHtml() ); $ret .= $page->getFooterHtml(); return $ret; } # Receieve the file, then display the browse page # This is a multi-part form post containing the file upload sub uploadFile { my $self = shift; my $toolBox = $self->getToolBox(); my $client = $toolBox->getClient(); my $cgi = $toolBox->getCgi(); my $currentDir = $cgi->param('currentDir'); my $initialFolder = $cgi->param('initialFolder'); my $uploadFilename = $cgi->param('uploadFile'); my $uploadFileHandle = $cgi->upload('uploadFile'); my $overwriteFile = $cgi->param('overwriteFile'); # Make sure we have all of the form parameters if ( not ( defined $currentDir && $currentDir ) ) { $client->logger( severity=>'ERROR', logMessage=>"WEBTOP Fileshares: ->uploadFile() Can't get current directory to upload the file" ); return $toolBox->getErrorPage( errorTitle => "File Share Browser", errorMsg => "Your web browser was unable to upload the file", currentDir => $currentDir, initialFolder => $initialFolder ); } if ( not ( defined $uploadFileHandle && $uploadFileHandle ) ) { $client->logger( severity=>'ERROR', logMessage=>"WEBTOP Fileshares: ->uploadFile() uploadFileHandle ($uploadFileHandle) was empty for file '$uploadFilename'" ); return $toolBox->getErrorPage( errorTitle => "File Share Browser", errorMsg => "Your web browser was unable to upload the file", currentDir => $currentDir, initialFolder => $initialFolder ); } if ( not ( defined $uploadFilename && $uploadFilename ) ) { $client->logger( severity=>'ERROR', logMessage=>"WEBTOP Fileshares: ->uploadFile() didn't get filename for upload to '$currentDir'" ); return $toolBox->getErrorPage( errorTitle => "File Share Browser", errorMsg => "An invalid filename was specified for upload", currentDir => $currentDir, initialFolder => $initialFolder ); } if ( $uploadFilename =~ s/.*[\\\/]+(.*)$/$1/ ) { $client->logger( severity=>'NORMAL', logMessage=>"WEBTOP Fileshares: ->uploadFile() removed slashes from filename; result: '$uploadFilename'" ); } # Initialize smbShare object if ( !$self->initSmbShare() ) { $client->logger( severity=>'ERROR', logMessage=>"WEBTOP Fileshares: ->uploadFile() couldn't initialize Smb object to upload file; returning error msg" ); return $toolBox->getErrorPage( errorTitle => "File Share Browser", errorMsg => "Unable to connect to the file share", currentDir => $currentDir, initialFolder => $initialFolder ); } my $share = $self->{'smbShare'}; my $shareName = $share->getRoot(); if ( not ( $self->aclCanAccess( servers=> $self->{'remoteServerValues'}, path=> $shareName . $currentDir . "\\" . $uploadFilename, protocol=> "FILE" ) and $self->aclCanAccess( servers=> $self->{'remoteServerValues'}, path=> $shareName . $currentDir, protocol=> "FILE" ) ) ) { return $toolBox->getErrorPage( errorTitle => "File Share Browser", errorMsg => "Access to the requested resource is denied by your policies", currentDir => $currentDir, initialFolder => $initialFolder ); } # Upload the file; once that's done, display the browse page $client->logger( severity=>'NORMAL', logMessage=>"WEBTOP Fileshares: ->uploadFile() uploadFileHandle: $uploadFileHandle" ); my $dbh = $toolBox->getWebtopDbWriter()->{'dbh'}; my $oldAutoCommit = $dbh->{'AutoCommit'}; $dbh->{'AutoCommit'} = 1; $dbh->commit(); my $result = $share->putFile( dirPath => $currentDir, filename => $uploadFilename, uploadFileHandle => $uploadFileHandle, overwriteFile => $overwriteFile ); $dbh->{'AutoCommit'} = $oldAutoCommit; $dbh->commit(); if ( $result ) { $client->logger( severity=>'ERROR', logMessage=>"WEBTOP Fileshares: ->uploadFile() file upload was successful; returning skillz" ); my $ret .= $self->getShareBrowseHtml(); return $ret; } else { $client->logger( severity=>'ERROR', logMessage=>"WEBTOP Fileshares: ->uploadFile() file upload failed; returning error msg" ); return $toolBox->getErrorPage( errorTitle => "File Share Browser", errorMsg => "File upload failed. You may not have access to upload files to this folder or there may already be a file named \"$uploadFilename\" in this folder.", currentDir => $currentDir, initialFolder => $initialFolder ); } } # Create new folder sub createFolder { my $self = shift; my $toolBox = $self->getToolBox(); my $client = $toolBox->getClient(); my $cgi = $toolBox->getCgi(); my $currentDir = $cgi->param('currentDir'); my $initialFolder = $cgi->param('initialFolder'); # Remove trailing \ from currentDir chop($currentDir) if ($currentDir =~ /\\$/); # They need to enter something into the javascript pop-up asking for a new filename/foldername my $newItemName = $cgi->param('newItemName'); if ( not ( defined $newItemName && $newItemName ) ) { $client->logger( severity=>'ERROR', logMessage=>"WEBTOP Fileshares: ->createFolder() Didn't get new name to create folder; returning error msg" ); return $toolBox->getErrorPage( errorTitle => "File Share Browser", errorMsg => "The name of the new folder is invalid. Please try again.", currentDir => $currentDir, initialFolder => $initialFolder ); } # Initialize smbShare object if ( !$self->initSmbShare() ) { $client->logger( severity=>'ERROR', logMessage=>"WEBTOP Fileshares: ->createFolder() couldn't initialize Smb object to create folder; returning error msg" ); return $toolBox->getErrorPage( errorTitle => "File Share Browser", errorMsg => "Unable to connect to the file share", currentDir => $currentDir, initialFolder => $initialFolder ); } my $share = $self->{'smbShare'}; my $shareName = $share->getRoot(); if ( not ( $self->aclCanAccess( servers=> $self->{'remoteServerValues'}, path=> $shareName . $currentDir . "\\" . $newItemName, protocol=> "FILE" ) and $self->aclCanAccess( servers=> $self->{'remoteServerValues'}, path=> $shareName . $currentDir, protocol=> "FILE" ) ) ) { return $toolBox->getErrorPage( errorTitle => "File Share Browser", errorMsg => "Access to the requested resource is denied by your policies", currentDir => $currentDir, initialFolder => $initialFolder ); } # Rename the file or folder # TODO: Visual indicator of rename success or failure if ( $share->createFolder( dirPath => $currentDir, folderName => $newItemName ) ) { $client->logger( severity=>'NORMAL', logMessage=>"WEBTOP Fileshares: ->createFolder() folder creation succeeded: $currentDir\\$newItemName" ); } else { $client->logger( severity=>'ERROR', logMessage=>"WEBTOP Fileshares: folder creation failed: $currentDir\\$newItemName" ); } my $ret .= $self->getShareBrowseHtml(); return $ret; } # Rename selected file or folder using Pos::Webtop::FileShare::Smb::moveItem sub renameItems { my $self = shift; my $toolBox = $self->getToolBox(); my $client = $toolBox->getClient(); my $cgi = $toolBox->getCgi(); my $currentDir = $cgi->param('currentDir'); my $initialFolder = $cgi->param('initialFolder'); # Make sure that the user selected stuff, in case javascript didn't catch it my ($selectedItem,$newItemName); if (defined $selectedItem || defined $newItemName) { $client->logger( severity=>'NORMAL', logMessage=>"WEBTOP Fileshares: ->renameItems()\$selectedItem or \$newItemName have shit in them" ); } if ( defined $cgi->param('selectedItem') ) { $selectedItem = $cgi->param('selectedItem'); } if ( not ( defined $selectedItem && $selectedItem ) ) { $client->logger( severity=>'ERROR', logMessage=>"WEBTOP Fileshares: ->renameItems(): No item was selected; returning error msg" ); return $toolBox->getErrorPage( errorTitle => "File Share Browser", errorMsg => "No item was selected for renaming", currentDir => $currentDir, initialFolder => $initialFolder ); } # They need to enter something into the javascript pop-up asking for a new filename/foldername if ( defined $cgi->param('newItemName') ) { $newItemName = $cgi->param('newItemName'); } if ( not ( defined $newItemName && $newItemName ) ) { $client->logger( severity=>'ERROR', logMessage=>"WEBTOP Fileshares: ->renameItems(): Didn't get new name for file/folder; returning error msg" ); return $toolBox->getErrorPage( errorTitle => "File Share Browser", errorMsg => "The new name for the selected item was not specified", currentDir => $currentDir, initialFolder => $initialFolder ); } # Initialize smbShare object if ( !$self->initSmbShare() ) { $client->logger( severity=>'ERROR', logMessage=>"WEBTOP Fileshares: ->renameItems(): couldn't initialize Smb object; returning error msg" ); return $toolBox->getErrorPage( errorTitle => "File Share Browser", errorMsg => "Unable to connect to the file share", currentDir => $currentDir, initialFolder => $initialFolder ); } my $share = $self->{'smbShare'}; my $shareName = $share->getRoot(); if ( not ( $self->aclCanAccess( servers=> $self->{'remoteServerValues'}, path=> $shareName . $currentDir . "\\" . $selectedItem, protocol=> "FILE" ) and $self->aclCanAccess( servers=> $self->{'remoteServerValues'}, path=> $shareName . $currentDir, protocol=> "FILE" ) and $self->aclCanAccess( servers=> $self->{'remoteServerValues'}, path=> $shareName . $currentDir . "\\" . $newItemName, protocol=> "FILE" ) ) ) { return $toolBox->getErrorPage( errorTitle => "File Share Browser", errorMsg => "Access to the requested resource is denied by your policies", currentDir => $currentDir, initialFolder => $initialFolder ); } # Rename the file or folder # TODO: Visual indicator of rename success or failure if ( $share->moveItem( dirPath => $currentDir, itemName => $selectedItem, newDirPath => $currentDir, newItemName => $newItemName ) ) { $client->logger( severity=>'NORMAL', logMessage=>"WEBTOP Fileshares: ->renameItems(): item rename succeeded: $currentDir\\$selectedItem -> $newItemName" ); } else { $client->logger( severity=>'ERROR', logMessage=>"WEBTOP Fileshares: ->renameItems(): item rename failed: $currentDir\\$selectedItem -> $newItemName" ); } my $ret .= $self->getShareBrowseHtml(); return $ret; } # Delete selected files and/or folders sub deleteItems { my $self = shift; my $toolBox = $self->getToolBox(); my $client = $toolBox->getClient(); my $cgi = $toolBox->getCgi(); my $currentDir = $cgi->param('currentDir'); my $initialFolder = $cgi->param('initialFolder'); # Make sure that the user selected stuff, in case javascript didn't catch it my (@selectedFiles,@selectedFolders); @selectedFiles = $cgi->param('selectedFiles'); @selectedFolders = $cgi->param('selectedFolders'); if ( !$#selectedFiles && !$#selectedFolders ) { $client->logger( severity=>'ERROR', logMessage=>"WEBTOP Fileshares: ->deleteItems(): No items were selected; returning error msg" ); return $toolBox->getErrorPage( errorTitle => "File Share Browser", errorMsg => "No items were selected for deletion", currentDir => $currentDir, initialFolder => $initialFolder ); } # Initialize smbShare object if ( !$self->initSmbShare() ) { $client->logger( severity=>'ERROR', logMessage=>"WEBTOP Filshares: ->deleteItems(): couldn't initialize Smb object; returning error msg" ); return $toolBox->getErrorPage( errorTitle => "File Share Browser", errorMsg => "Unable to connect to the file share", currentDir => $currentDir, initialFolder => $initialFolder ); } my $share = $self->{'smbShare'}; my $shareName = $share->getRoot(); if ( not $self->aclCanAccess( servers=> $self->{'remoteServerValues'}, path=> $shareName . $currentDir, protocol=> "FILE" ) ) { return $toolBox->getErrorPage( errorTitle => "File Share Browser", errorMsg => "Access to the requested resource is denied by your policies", currentDir => $currentDir, initialFolder => $initialFolder ); } foreach my $file ( @selectedFiles ) { next unless defined $file and length $file; if ( not $self->aclCanAccess( servers=> $self->{'remoteServerValues'}, path=> $shareName . $currentDir . "\\" . $file, protocol=> "FILE" ) ) { return $toolBox->getErrorPage( errorTitle => "File Share Browser", errorMsg => "Access to the requested resource ($file) is denied by your policies", currentDir => $currentDir, initialFolder => $initialFolder ); } } foreach my $folder ( @selectedFolders ) { next unless defined $folder and length $folder; if ( not $self->aclCanAccess( servers=> $self->{'remoteServerValues'}, path=> $shareName . $currentDir . "\\" . $folder, protocol=> "FILE" ) ) { return $toolBox->getErrorPage( errorTitle => "File Share Browser", errorMsg => "Access to the requested resource ($folder) is denied by your policies", currentDir => $currentDir, initialFolder => $initialFolder ); } } # TODO: Visual indicator of delete success or failure foreach my $file ( @selectedFiles ) { next if !$file; if ( $share->deleteFile( dirPath => $currentDir, filename => $file ) ) { $client->logger( severity=>'NORMAL', logMessage=>"WEBTOP Fileshares: ->deleteItems(): file delete succeeded: $currentDir\\$file" ); } else { $client->logger( severity=>'ERROR', logMessage=>"WEBTOP Fileshares: ->deleteItems(): file delete failed: $currentDir\\$file" ); } } # TODO: Folder deletion foreach my $folder ( @selectedFolders ) { next if !$folder; # Remove trailing \ chop($currentDir) if ($currentDir =~ /\\$/); if ( $share->deleteFolder( dirPath => "$currentDir", folderName => "$folder" ) ) { $client->logger( severity=>'NORMAL', logMessage=>"WEBTOP Fileshares: ->deleteItems(): folder delete succeeded: $currentDir\\$folder" ); } else { $client->logger( severity=>'ERROR', logMessage=>"WEBTOP Fileshares: ->deleteItems(): folder delete failed: $currentDir\\$folder" ); return $toolBox->getErrorPage( errorTitle => "File Share Browser", errorMsg => "Unable to delete \"$folder\": The folder is not empty", currentDir => $currentDir, initialFolder => $initialFolder ); } } my $ret .= $self->getShareBrowseHtml(); return $ret; } # Copy/move items on the clipboard to the current directory sub pasteClipboardItems { my $self = shift; my $toolBox = $self->getToolBox(); my $cgi = $toolBox->getCgi(); my $dbw = $toolBox->getWebtopDbWriter(); my $client = $toolBox->getClient(); my $sessionId = $client->getSessionId(); my $currentDir = $cgi->param('currentDir'); my $initialFolder = $cgi->param('initialFolder'); my $shareId = $client->getPersistedValue( name=> 'shareId' ); $shareId = $cgi->param('shareId') if defined $cgi->param('shareId'); my $displayGroup = $client->getPersistedValue( name=> 'displayGroup' ); $displayGroup = $cgi->param('displayGroup') if defined $cgi->param('displayGroup'); my $shareInfo = $self->getStoredShareInfo( shareId => $shareId, displayGroup=> $displayGroup ); my $shareName = $shareInfo->{'share'}; my $serverName = $shareInfo->{'server'}; $serverName =~ s/^\\\\//g; $client->setPersistedValue( name=> "shareId", value=> $shareId ); $client->setPersistedValue( name=> "displayGroup", value=> $displayGroup ); if ( not ( defined $shareName && defined $serverName ) ) { $client->logger( severity=>'ERROR', logMessage=>"WEBTOP Fileshares: ->pasteClipboardItems: missing one of the following form fields: shareName, serverName; displaying error page" ); return $toolBox->getErrorPage( errorTitle => "File Share Browser", errorMsg => "Unable to paste items from clipboard", currentDir => $currentDir, initialFolder => $initialFolder ); } # TODO: Support pasting from other fileshares (requires changes to initSmbShare and Smb->moveItem) # Get items from clipboard for the current fileshare my @clipboardItems = $dbw->query( sql => " SELECT directory, filename, fileshare_cut FROM w_web_smb_fileshare_clipboard WHERE fileserver_hostname = '" . $dbw->escape(dirty => $serverName) . "' AND fileshare_name = '" . $dbw->escape(dirty => $shareName) . "' AND client_session_id = '$sessionId' "); # Initialize smbShare object if ( !$self->initSmbShare() ) { $client->logger( severity=>'ERROR', logMessage=>"WEBTOP Fileshares: ->pasteClipboardItems(): couldn't initialize Smb object; returning error msg" ); return $toolBox->getErrorPage( errorTitle => "File Share Browser", errorMsg => "Unable to connect to the file share", currentDir => $currentDir, initialFolder => $initialFolder ); } my $share = $self->{'smbShare'}; if ( not $self->aclCanAccess( servers=> $self->{'remoteServerValues'}, path=> $shareName . $currentDir, protocol=> "FILE" ) ) { return $toolBox->getErrorPage( errorTitle => "File Share Browser", errorMsg => "Access to the requested resource is denied by your policies", currentDir => $currentDir, initialFolder => $initialFolder ); } foreach my $item (@clipboardItems) { my $origDir = $item->[0]; my $itemName = $item->[1]; my $cut = $item->[2]; if ( not ( $self->aclCanAccess( servers=> $self->{'remoteServerValues'}, path=> $shareName . $origDir . "\\" . $itemName, protocol=> "FILE" ) and $self->aclCanAccess( servers=> $self->{'remoteServerValues'}, path=> $shareName . $currentDir . "\\" . $itemName, protocol=> "FILE" ) ) ) { return $toolBox->getErrorPage( errorTitle => "File Share Browser", errorMsg => "Access to the requested resource ($itemName) is denied by your policies", currentDir => $currentDir, initialFolder => $initialFolder ); } } if ( !@clipboardItems ) { return $toolBox->getErrorPage( errorTitle => "File Share Browser", errorMsg => "You must cut/copy at least one item before you can paste", currentDir => $currentDir, initialFolder => $initialFolder ); } # Go through each file and folder on the clipboard and move it to the current dir foreach my $item (@clipboardItems) { my $origDir = $item->[0]; my $itemName = $item->[1]; my $cut = $item->[2]; my $isDir; # Remove trailing \ chop($origDir) if ($origDir =~ /\\$/); # If itemName is empty, there was no filename in the database. Ergo, this is a directory # So, the directory becomes the itemName if ( not (defined $itemName && $itemName) ) { $origDir =~ /(.*)\\(.*)/; $origDir = $1; $itemName = $2; $isDir = 1; } # Cut/paste or copy/paste the items from the clipboard if ($cut) { # Move the items $client->logger( severity=>'NORMAL', logMessage=>"WEBTOP Fileshares: ->pasteClipboardItems(): Attempting to cut/paste (move) item: '$origDir\\$itemName' -> '$currentDir\\$itemName'" ); if ( $share->moveItem( dirPath => $origDir, itemName => $itemName, newDirPath => $currentDir, newItemName => $itemName ) ) { $client->logger( severity=>'NORMAL', logMessage=>"WEBTOP Fileshares: ->pasteClipboardItems(): Cut/paste succeeded" ); } else { $client->logger( severity=>'ERROR', logMessage=>"WEBTOP Fileshares: ->pasteClipboardItems(): Cut/paste failed" ); return $toolBox->getErrorPage( errorTitle => "File Share Browser", errorMsg => "Could not paste item ($itemName). Make sure there are no items with the same name in this directory.", currentDir => $currentDir, initialFolder => $initialFolder ); } # Clear clipboard since the files were cut $dbw->do( sql => " DELETE FROM w_web_smb_fileshare_clipboard WHERE fileserver_hostname = '" . $dbw->escape(dirty => $serverName) . "' AND fileshare_name = '" . $dbw->escape(dirty => $shareName) . "' AND client_session_id = '$sessionId' "); } else { # Copy the items; leave them on the clipboard $client->logger( severity=>'NORMAL', logMessage=>"WEBTOP Fileshares: ->pasteClipboardItems(): Attempting to copy/paste item: '$origDir\\$itemName' -> '$currentDir\\$itemName'" ); if ( $share->copyItem( dirPath => $origDir, itemName => $itemName, newDirPath => $currentDir, newItemName => $itemName, isDir => $isDir ) ) { $client->logger( severity=>'NORMAL', logMessage=>"WEBTOP Fileshares: ->pasteClipboardItems(): Copy/paste succeeded" ); } else { $client->logger( severity=>'ERROR', logMessage=>"WEBTOP Fileshares: ->pasteClipboardItems(): Copy/paste failed" ); return $toolBox->getErrorPage( errorTitle => "File Share Browser", errorMsg => "Could not paste item ($itemName). Make sure there are no items with the same name in this directory.", currentDir => $currentDir, initialFolder => $initialFolder ); } } } my $ret .= $self->getShareBrowseHtml(); return $ret; } # Insert selected selected files and/or folders into the clipboard database sub addItemsToClipboard { my $self = shift; my ($cut) = @{{@_}}{qw/cut/}; $cut = 0 unless $cut == 1; my $toolBox = $self->getToolBox(); my $cgi = $toolBox->getCgi(); my $dbw = $toolBox->getWebtopDbWriter(); my $client = $toolBox->getClient(); my $sessionId = $client->getSessionId(); my $act = $cgi->param('act'); my $currentDir = $cgi->param('currentDir'); my $initialFolder = $cgi->param('initialFolder'); # Remove trailing \ from currentDir chop($currentDir) if ($currentDir =~ /.*\\$/); $currentDir = '\\' if !$currentDir; my $shareId = $client->getPersistedValue( name=> 'shareId' ); $shareId = $cgi->param('shareId') if defined $cgi->param('shareId'); my $displayGroup = $client->getPersistedValue( name=> 'displayGroup' ); $displayGroup = $cgi->param('displayGroup') if defined $cgi->param('displayGroup'); my $shareInfo = $self->getStoredShareInfo( shareId => $shareId, displayGroup=> $displayGroup ); my $shareName = $shareInfo->{'share'}; my $serverName = $shareInfo->{'server'}; $serverName =~ s/^\\\\//g; $client->setPersistedValue( name=> "shareId", value=> $shareId ); $client->setPersistedValue( name=> "displayGroup", value=> $displayGroup ); if ( not ( defined $shareName && defined $serverName ) ) { $client->logger( severity=>'ERROR', logMessage=>"WEBTOP Fileshares: ->addItemsToClipboard: missing one of the following form fields: shareName, serverName; displaying error page" ); return $toolBox->getErrorPage( errorTitle => "File Share Browser", errorMsg => "Unable to add items to clipboard", currentDir => $currentDir, initialFolder => $initialFolder ); } # Make sure that the user selected stuff, in case javascript didn't catch it my (@selectedFiles,@selectedFolders); if (@selectedFiles || @selectedFolders) { $client->logger( severity=>'NORMAL', logMessage=>"WEBTOP Fileshares: ->addItemsToClipboard() \@selectedFiles or \@selectedFolders have shit in them" ); } @selectedFiles = $cgi->param('selectedFiles'); @selectedFolders = $cgi->param('selectedFolders'); if ( !$#selectedFiles && !$#selectedFolders ) { $client->logger( severity=>'ERROR', logMessage=>"WEBTOP Fileshares: ->addItemsToClipboard(): No items were selected; returning error msg" ); return $toolBox->getErrorPage( errorTitle => "File Share Browser", errorMsg => "No items were selected to add to the clipboard", currentDir => $currentDir, initialFolder => $initialFolder ); } # Clear the clipboard $dbw->do( sql => " DELETE FROM w_web_smb_fileshare_clipboard WHERE fileserver_hostname = '" . $dbw->escape(dirty => $serverName) . "' AND fileshare_name = '" . $dbw->escape(dirty => $shareName) . "' AND client_session_id = '$sessionId' "); # Insert each item into the database foreach my $file ( @selectedFiles ) { if ($file eq "") { next; } $dbw->do( sql => " INSERT INTO w_web_smb_fileshare_clipboard (client_session_id, clipboard_id, fileshare_cut, fileserver_hostname, fileshare_name, directory, filename) VALUES ('$sessionId', " . $dbw->getNextValueString( sequence => 'w_smb_fileshare_clipboard_pk') . ", '$cut', '" . $dbw->escape(dirty => $serverName) . "', '" . $dbw->escape(dirty => $shareName) . "', '" . $dbw->escape(dirty => $currentDir) . "', '" . $dbw->escape(dirty => $file) . "') "); } foreach my $folder ( @selectedFolders ) { if ($folder eq "") { next; } $dbw->do( sql => " INSERT INTO w_web_smb_fileshare_clipboard (client_session_id, clipboard_id, fileshare_cut, fileserver_hostname, fileshare_name, directory) VALUES ('$sessionId', " . $dbw->getNextValueString( sequence => 'w_smb_fileshare_clipboard_pk') . ", '$cut', '" . $dbw->escape(dirty => $serverName) . "', '" . $dbw->escape(dirty => $shareName) . "', '" . $dbw->escape(dirty => "$currentDir\\$folder") . "') "); } my $ret .= $self->getShareBrowseHtml(); return $ret; } # Stream the file to the user sub downloadFile { my $self = shift; my $toolBox = $self->getToolBox(); my $client = $toolBox->getClient(); my $cgi = $toolBox->getCgi(); my $currentDir = $cgi->param('currentDir'); my $initialFolder = $cgi->param('initialFolder'); my $downloadFilename = $cgi->param('downloadFilename'); if ( not defined $downloadFilename || !$downloadFilename ) { $client->logger( severity=>'ERROR', logMessage=>"WEBTOP Fileshares: ->downloadFile() didn't get downloadFilename form variable; returning error msg" ); return $toolBox->getErrorPage( errorTitle => "File Share Browser", errorMsg => "Unable to download file (an invalid filename was requested for download)", currentDir => $currentDir, initialFolder => $initialFolder ); } if ( not defined $currentDir || $currentDir eq "" ) { $client->logger( severity=>'ERROR', logMessage=>"WEBTOP Fileshares: ->downloadFile() didn't get currentDir form variable; returning error msg" ); return $toolBox->getErrorPage( errorTitle => "File Share Browser", errorMsg => "Unable to download file (can't determine current directory)", currentDir => $currentDir, initialFolder => $initialFolder ); } $client->logger( severity=>'NORMAL', logMessage=>"WEBTOP Fileshares: ->downloadFile() currentDir: '$currentDir'" ); # Initialize smbShare object if ( !$self->initSmbShare() ) { $client->logger( severity=>'ERROR', logMessage=>"WEBTOP Fileshares: ->downloadFile() couldn't initialize Smb object; returning error msg" ); return $toolBox->getErrorPage( errorTitle => "File Share Browser", errorMsg => "Unable to connect to file share", currentDir => $currentDir, initialFolder => $initialFolder ); } my $share = $self->{'smbShare'}; my $shareName = $share->getRoot(); if ( not $self->aclCanAccess( servers=> $self->{'remoteServerValues'}, path=> $shareName . $currentDir . "\\" . $downloadFilename, protocol=> "FILE" ) ) { $client->logger( severity=>'ERROR', logMessage=>"WEBTOP Fileshares: ->downloadFile() Access to the resource is denied by policies" ); return $toolBox->getErrorPage( errorTitle => "File Share Browser", errorMsg => "Access to the requested resource is denied by your policies", currentDir => $currentDir, initialFolder => $initialFolder ); } $client->logger( severity=>'NORMAL', logMessage=>"WEBTOP Fileshares: ->downloadFile: attempting to send file '$currentDir\\$downloadFilename' to user " ); my $dbh = $toolBox->getWebtopDbWriter()->{'dbh'}; my $oldAutoCommit = $dbh->{'AutoCommit'}; $dbh->{'AutoCommit'} = 1; $dbh->commit(); # getFile prints directly to STDOUT $share->getFile( dirPath => $currentDir, filename => $downloadFilename ); $dbh->{'AutoCommit'} = $oldAutoCommit; $dbh->commit(); return undef; } # Retrieve all file shares configured for the user via the policy manager (ipsec vpn) sub getAppFileShares { my $self = shift; my $toolBox = $self->getToolBox(); my $cgi = $toolBox->getCgi(); my $dbr = $toolBox->getWebtopDbReader(); my $client = $toolBox->getClient(); my $userId = $client->getUserId(); # Get the file shares my @fileShareRows = $dbr->query( sql => " SELECT net_user, net_pass, drive_letter, remote_addr, to_char(last_access_date, 'yyyy/mm/dd hh24:mi:ss'), 1 FROM client_drive_shares WHERE user_id = '$userId' AND restrict_set_id = " . $client->getRestrictionSetId . " "); # Store file share info in an array of hashes # TODO: Get fshLastAccessDate my @fileShares; if (@fileShares) { $client->logger( severity=>'NORMAL', logMessage=>"WEBTOP Fileshares: ->getAppFileShares() \@fileShares has shit" ); } foreach my $fileShareRow (@fileShareRows) { my $fsaUsername = $fileShareRow->[0]; my $fsaPassword = $fileShareRow->[1]; my $fshDriveLetter = $fileShareRow->[2]; my (undef,$fsvHostname,$fshName,$fshInitialFolder) = split(/\\{1,2}/,$fileShareRow->[3],4); my $fshLastAccess = $fileShareRow->[4]; my $fshAdmintool = $fileShareRow->[5]; $client->logger( severity=>'NORMAL', logMessage=>"WEBTOP FileShares: ->getAppFileShares() server: $fsvHostname, share: $fshName" ); push @fileShares, { fsvHostname => $fsvHostname, fshName => $fshName, fshDriveLetter => $fshDriveLetter, fsaUsername => $fsaUsername, fsaPassword => $fsaPassword, fshInitialFolder => $fshInitialFolder, fshLastAccess => $fshLastAccess, fshAdmintool => $fshAdmintool }; } return @fileShares; } # Retrieve all file shares for the current user from the webtop database sub getWebtopFileShares { my $self = shift; my $toolBox = $self->getToolBox(); my $cgi = $toolBox->getCgi(); my $dbr = $toolBox->getWebtopDbReader(); my $client = $toolBox->getClient(); my $userId = $client->getUserId(); my $sessionId = $client->getSessionId(); #my $customerId = $client->getCustomerId(); # Store file share info in an array of hashes my @fileShares; if (@fileShares) { $client->logger( severity=>'NORMAL', logMessage=>"WEBTOP FileShares: ->getWebtopFileshares() \@fileShares has shit in it" ); } # TODO: Don't retrieve auth credentials if they were cached for another session (web_session_id would be populated) # TODO: Don't break in stupid ways if there are no auth credentials my @fileShareRows = $dbr->query( sql => " SELECT fsv.display_name, fsv.fileserver_hostname, fsv.fileserver_desc, fsh.fileshare_name, fsh.fileshare_desc, fsu.display_drive_letter, to_char(fsu.last_access_date, 'yyyy/mm/dd hh24:mi:ss'), fsh.fileshare_id, 0 FROM w_smb_fileservers fsv, w_smb_fileshares fsh, w_smb_fileshare_users fsu WHERE fsh.fileshare_id = fsu.fileshare_id AND fsh.fileserver_id = fsv.fileserver_id AND fsu.user_id = '$userId' "); my %fileShare; if (%fileShare) { $client->logger( severity=>'NORAML', logMessage=>"WEBTOP Fileshares: ->getWebtopFileShares() \%fileShare has shit in it" ); } foreach my $fileShareRow (@fileShareRows) { # raz changed this to two queries so that we can return share info if there is no # auth cache (we added a no auth cache policy) my $fsvDisplayName = $fileShareRow->[0]; my $fsvHostname = $fileShareRow->[1]; my $fsvDesc = $fileShareRow->[2]; my $fshName = $fileShareRow->[3]; my $fshDesc = $fileShareRow->[4]; my $fshDriveLetter = $fileShareRow->[5]; my $fshLastAccess = $fileShareRow->[6]; my $fshFileShareId = $fileShareRow->[7]; my $fshAdmintool = $fileShareRow->[8]; my ($fsaUsername,$fsaPassword) = (undef,undef); my @row = $dbr->query( sql=> " select user_name, password from W_SMB_FILESHARE_AUTH_CACHE where user_id = $userId and fileshare_id = $fshFileShareId "); @row and ($fsaUsername,$fsaPassword) = @{ $row[0] }; $client->logger( severity=>'NORMAL', logMessage=>"WEBTOP FileShares: ->getWebtopFileshares() server: $fsvHostname, share: $fshName, user: " . (defined($fsaUsername) ? $fsaUsername : "NONE") . "" ); push @fileShares, { fsaDisplayName => $fsvDisplayName, fsvHostname => $fsvHostname, fsvDesc => $fsvDesc, fshName => $fshName, fshDesc => $fshDesc, fshDriveLetter => $fshDriveLetter, fsaUsername => $fsaUsername, fsaPassword => $fsaPassword, fshLastAccess => $fshLastAccess, fshAdmintool => $fshAdmintool }; } return @fileShares; } sub removeDriveShare { my $self = shift; my $tb = $self->getToolBox(); my $cgi = $tb->getCgi(); my $db = $tb->getWebtopDbReader(); my $client = $tb->getClient(); my $userId = $client->getUserId(); my $fileShareHostName = $cgi->param('fileShareHostName'); my $fileShareName = $cgi->param('fileShareName'); my $fileShareInitialFolder = $cgi->param('fileShareInitialFolder'); my $okay = $cgi->param('okay'); my $fileServerId = undef; my $fileShareId = undef; if(defined($okay) and $okay eq '1') { ($fileServerId) = $db->query( sql => " select fileserver_id from w_smb_fileservers where fileserver_hostname = '" . $fileShareHostName . "' and customer_id = ( select customer_id from groups where group_id = ( select group_id from users where user_id = '" . $userId . "' ))"); if( defined($fileServerId)) { my $fullShareDir = $fileShareInitialFolder; $fullShareDir =~ s/\\$//g; $fullShareDir = $fileShareName . "" . (defined($fullShareDir) && length($fullShareDir) ? "\\$fullShareDir" : "") . ""; ($fileShareId) = $db->query( sql => " select fileshare_id from w_smb_fileshares where fileserver_id = '" . $fileServerId ."' and fileshare_name = '" . $db->escape( dirty=> $fullShareDir ) . "' "); } if( defined($fileShareId)) { $db->do(sql => " delete from w_smb_fileshare_users where user_id = '" . $userId . "' AND fileshare_id = '" . $fileShareId . "' "); $self->getShareLoginHtml(topText => "Successfully removed File share \"\\\\$fileShareHostName\\$fileShareName\\$fileShareInitialFolder\".

"); } else { $self->getShareLoginHtml(topText => " File share \"\\\\$fileShareHostName\\$fileShareName\\$fileShareInitialFolder\" NOT removed. Please call Technical Support for assistance

"); } } else { my $p = $tb->getPage(); $p->addNavBarElement(description => "Remove Drive Share", values => [ { name => "act", value => "RemoveDriveShare" }, { name => "fileShareHostName", value => $fileShareHostName }, { name => "fileShareName", value => $fileShareName }, { name => "fileShareInitialFolder", value => $fileShareInitialFolder } ]); print $p->getHeaderHtml(title => "Webtop: File share: Remove") . "
"; #print Pos::Webtop::OcxLauncher::getLauncherHtml( tb=> $tb ); my $box = $tb->createBox(title => "Remove a File share.", width => "400"); print $box->getHeaderHtml; print "
This will remove \"\\\\$fileShareHostName\\$fileShareName\\$fileShareInitialFolder\" from your list of File Share browsers.

" . $client->getFormInputs( values => [ { name => 'act', value => 'RemoveDriveShare' }, { name => 'okay', value => '1' }, { name => 'fileShareHostName', value => $fileShareHostName }, { name => 'fileShareName', value => $fileShareName }, { name => 'fileShareInitialFolder', value => $fileShareInitialFolder } ]) . "
" . $client->getFormInputs() . "
"; print $box->getFooterHtml; print "
" . $p->getFooterHtml; } } # Initialize the Pos::Webtop::FileShare::Smb object sub initSmbShare { my $self = shift; my $toolBox = $self->getToolBox(); my $cgi = $toolBox->getCgi(); my $dbr = $toolBox->getWebtopDbReader(); my $dbw = $toolBox->getWebtopDbWriter(); my $client = $toolBox->getClient(); my $userId = $client->getUserId(); # Reset the class attribute $self->{'smbShare'} = undef; my $shareId = $client->getPersistedValue( name=> 'shareId' ); $shareId = $cgi->param('shareId') if defined $cgi->param('shareId'); $shareId = $self->{'initShareId'} if defined $self->{'initShareId'}; my $displayGroup = $client->getPersistedValue( name=> 'displayGroup' ); $displayGroup = $cgi->param('displayGroup') if defined $cgi->param('displayGroup'); $displayGroup = $self->{'initDisplayGroup'} if defined $self->{'initDisplayGroup'}; my $shareInfo = $self->getStoredShareInfo( shareId => $shareId, displayGroup=> $displayGroup ); my $shareUserName = $shareInfo->{'user'}; my $sharePassword = $shareInfo->{'password'}; my $shareName = $shareInfo->{'share'}; my $serverName = $shareInfo->{'server'}; $serverName =~ s/^\\\\//g; $client->setPersistedValue( name=> "shareId", value=> $shareId ); $client->setPersistedValue( name=> "displayGroup", value=> $displayGroup ); # this block was not needed since the only place that will ever take a username password # combo from the user will always go to the share browse screen... it will then # seed us with a initShareId etc just like auth cache would... otherwise we will use the # persisted or form post values for the shareid and display group. # # incase we came from a login... we have the special case of needing to read from the form # # we check initShareId incase to avoid reading form values if we are being overridden # # by an auth cache seed. # if ( defined $cgi->param('shareUserName') and not defined $self->{'initShareId'} ) # { # $shareUserName = $cgi->param('shareUserName'); # $sharePassword = $cgi->param('sharePassword'); # $shareName = $cgi->param('shareName'); # $serverName = $cgi->param('serverName'); # # # note that we don't save the stored info since that is handled by the caller # } if ( not ( defined $shareName && defined $serverName ) ) { $client->logger( severity=>'ERROR', logMessage=>"WEBTOP Fileshares: ->initSmbShare(): missing one of the following form fields: shareName, serverName; returning 0" ); return 0; } push @{ $self->{'remoteServerValues'} }, $serverName; my $supernattedServerIp; my $coIp; my $ipInt; { $coIp = $toolBox->resolveHost( host=> $serverName ); defined $coIp or last; push @{ $self->{'remoteServerValues'} }, $coIp; $ipInt = 0; my $i = 0; my @ot = split(/\./,'127.0.0.1'); $ipInt += $ot[0] << 24; $ipInt += $ot[1] << 16; $ipInt += $ot[2] << 8; $ipInt += $ot[3]; push @{ $self->{'remoteServerValues'} }, $ipInt; } # we decide on the 'thing we will use' (could be ip or name) my $nameToUse = (defined $coIp ? $coIp : $serverName ); # no supernat anymore, taken care of by smbclient binary # # Right now dev doesn't have supernat, so don't try to do supernat if we're on dev. # if ($ENV{'SERVER_NAME'} eq 'dev.positivenetworks.net') # { # $supernattedServerIp = $serverName; # } # else # { # $supernattedServerIp = $toolBox->getSupernattedIp( host => $nameToUse ); # } # # if ( not defined $supernattedServerIp ) # { # warn ref($self) . "->initSmbShare(): unable to resolve or get supernatted IP for serverName '$serverName'"; # return 0; # } # $supernattedServerIp = $serverName; my ($domain) = $dbr->query( sql => " SELECT corp_network_domain FROM users WHERE user_id = '$userId' "); my $webSessionId = $client->getSessionId(); if(defined $domain and $domain) { $client->logger( severity=>'NORMAL', logMessage=>"WEBTOP Fileshares: initSmbShare: shareUserName => $shareUserName, sharePassword => HIDDEN, serverName => $serverName, shareName => $shareName, domain => $domain, webSessionId => $webSessionId"); # Create Smb object $self->{'smbShare'} = Pos::Webtop::FileShare::Smb->new( shareUserName => $shareUserName, sharePassword => $sharePassword, serverName => $serverName, shareName => $shareName, domain => $domain, webSessionId => $webSessionId ); } else { $client->logger( severity=>'NORMAL', logMessage=>"WEBTOP Fileshares: initSmbShare shareUserName => $shareUserName, sharePassword => HIDDEN, serverName => $serverName, shareName => $shareName, webSessionId => $webSessionId"); # Create Smb object $self->{'smbShare'} = Pos::Webtop::FileShare::Smb->new( shareUserName => $shareUserName, sharePassword => $sharePassword, serverName => $serverName, shareName => $shareName, webSessionId => $webSessionId ); } # Return 1 if creation succeeded, else return 0 if ( $self->{'smbShare'} ) { return 1; } else { $client->logger( severity=>'ERROR', logMessage=>"WEBTOP Fileshares: ->initSmbShare: unable to create Pos::Webtop::FileShare::Smb object; returning 0" ); return 0; } } 1;