#!/usr/bin/perl # # $Id: archive.pl,v 1.2 2024/05/21 02:57:17 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # # This file is part of the LearningOnline Network with CAPA (LON-CAPA). # # LON-CAPA is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # LON-CAPA is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with LON-CAPA; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # /home/httpd/cgi-bin/archive.pl # # http://www.lon-capa.org/ # # The LearningOnline Network with CAPA # # A CGI script which creates a compressed archive file of the current # directory in Authoring Space, with optional (a) recursion into # sub-directories, and (b) filtering by filetype. # Supported formats are: tar.gz, tar.bz2, tar.xz and zip. #### use strict; use lib '/home/httpd/lib/perl'; use File::Find; use Apache::lonnet; use Apache::loncommon; use Apache::lonlocal; use LONCAPA::loncgi; use Cwd; use HTML::Entities; $|++; my $lock; our %excluded = ( bak => 1, save => 1, log => 1, ); our $maxdepth = 0; our %included = (); our $alltypes = ''; our $recurse = ''; our $includeother = ''; our $prefix = ''; our $totalfiles = 0; our $totalsize = 0; our $totalsubdirs = 0; our %subdirs = (); our $fh; if (!&LONCAPA::loncgi::check_cookie_and_load_env()) { &Apache::lonlocal::get_language_handle(); print(&LONCAPA::loncgi::missing_cookie_msg()); } else { &Apache::lonlocal::get_language_handle(); my %lt = &Apache::lonlocal::texthash ( indi => 'Invalid directory name', outo => 'Output of command:', comp => 'Archive creation complete.', erro => 'An error occurred.', cctf => 'Cannot create tar file', dtf => 'Download tar file', ); # Get the identifier and set a lock my %perlvar=%{&LONCAPA::Configuration::read_conf('loncapa.conf')}; my $londocroot = $Apache::lonnet::perlvar{'lonDocRoot'}; &Apache::lonlocal::get_language_handle(); &Apache::loncommon::content_type(undef,'text/html'); my $identifier = $ENV{'QUERY_STRING'}; my ($hashref,$dir,$dirurl,$jsdirurl,$auname,$audom,$allowed,$error, $format,$compress,$fname,$extension,$adload,$url,$mime); my @posstypes = qw(problem library sty sequence page task rights meta xml html xhtml htm xhtm css js tex txt gif jpg jpeg png svg other); if (($identifier =~ /^\d+_\d+_\d+$/) && (exists($env{'cgi.'.$identifier.'.archive'}))) { $hashref = &Apache::lonnet::thaw_unescape($env{'cgi.'.$identifier.'.archive'}); if (ref($hashref) eq 'HASH') { $dir = $hashref->{'dir'}; $dir =~ s{\.+}{.}g; if (-d $dir) { $dirurl = $dir; ($auname,$audom) = &Apache::lonnet::constructaccess($dir); if (($auname ne '') && ($audom ne '')) { $dirurl =~ s/^\Q$londocroot\E//; $prefix = $londocroot.$dirurl; $maxdepth = $prefix =~ tr{/}{}; $jsdirurl = &js_escape($dirurl); if (($auname eq $env{'user.name'}) && ($audom eq $env{'user.domain'}) && ($env{'environment.canarchive'})) { $allowed = 1; if ($hashref->{'recurse'}) { $recurse = 1; } else { $recurse = 0; } if ($hashref->{'types'} eq 'all') { $alltypes = 1; } else { $alltypes = 0; my %possincluded; map { $possincluded{$_} = 1; } split(/,/,$hashref->{'types'}); $includeother = 0; foreach my $type (@posstypes) { if ($type eq 'other') { if ($possincluded{$type}) { $includeother = 1; } else { $includeother = 0; } } else { if ($possincluded{$type}) { $included{$type} = 1; } else { $excluded{$type} = 1; } } } } if ((exists($hashref->{'format'}) && $hashref->{'format'} =~ /^zip$/i)) { $format = lc($hashref->{'format'}); } else { $format = 'tar'; } unless ($format eq 'zip') { if ((exists($hashref->{'compress'})) && ($hashref->{'compress'} =~ /^(xz|bzip2)$/i)) { $compress = lc($hashref->{'compress'}); } else { $compress = 'gzip'; } } if ($hashref->{'adload'}) { $adload = $hashref->{'adload'}; } if ($hashref->{'fname'}) { $fname = $hashref->{'fname'}; } if ($hashref->{'extension'}) { $extension = $hashref->{'extension'}; } } } } else { $error = 'indi'; } } else { $error = 'nohash'; } # delete cgi.$identifier.archive from %env if error if ($error) { &Apache::lonnet::delenv('cgi.'.$identifier.'.archive'); } } else { $error = 'noid'; } $env{'request.noversionuri'} = '/cgi-bin/archive.pl'; my ($brcrum,$title); if ($error) { $brcrum = [{'href' => '', 'text' => 'Missing information'}]; } elsif (!$allowed) { $brcrum = [{'href' => '', 'text' => 'Access denied'}]; } else { # Breadcrumbs $title = 'Creating archive file'; $brcrum = [{'href' => $dirurl, 'text' => 'Authoring Space'}, {'href' => "javascript:gocstr('/adm/cfile?action=archive','$jsdirurl');", 'text' => 'File Operation'}, {'href' => '', 'text' => $title}]; } # Set up files to write two and url my ($js,%location_of,$suffix,$namesdest,$filesdest,$filesurl); if ($allowed) { my @tocheck; if ($format ne '') { push(@tocheck,$format); } if ($compress ne '') { push(@tocheck,$compress); } foreach my $program (@tocheck) { foreach my $dir ('/bin/','/usr/bin/','/usr/local/bin/','/sbin/', '/usr/sbin/') { if (-x $dir.$program) { $location_of{$program} = $dir.$program; last; } } } if (($format ne '') && (exists($location_of{$format}))) { if ($format eq 'zip') { $suffix = '.zip'; $mime = 'application/x-zip-compressed'; } else { $suffix = '.tar'; if (($compress ne '') && (exists($location_of{$compress}))) { if ($compress eq 'bzip2') { $suffix .= '.bz2'; $mime = 'application/x-bzip2'; } elsif ($compress eq 'gzip') { $suffix .= '.gz'; $mime = 'application/x-gzip'; } elsif ($compress eq 'xz') { $suffix .= '.xz'; $mime = 'application/x-xz'; } } } $namesdest = $perlvar{'lonPrtDir'}.'/'.$env{'user.name'}.'_'.$env{'user.domain'}.'_archive_'.$identifier.'.txt'; $filesdest = $perlvar{'lonPrtDir'}.'/'.$env{'user.name'}.'_'.$env{'user.domain'}.'_archive_'.$identifier.$suffix; $filesurl = '/prtspool/'.$env{'user.name'}.'_'.$env{'user.domain'}.'_archive_'.$identifier.$suffix; if ($suffix eq $extension) { $fname =~ s{\Q$suffix\E$}{}; } if ($fname eq '') { $fname = $env{'user.name'}.'_'.$env{'user.domain'}.'_archive_'.$identifier.$suffix; } else { $fname .= $suffix; } my $downloadurl = &Apache::lonnet::absolute_url().$filesurl; my $delarchive = $identifier.$suffix; $js = &js($filesurl,$mime,$fname,$delarchive); } } print &Apache::loncommon::start_page($title, '', {'bread_crumbs' => $brcrum,})."\n". '
'.&Apache::loncommon::end_page(); # Code to delete archive file after successful download %included = (); $alltypes = ''; $recurse = ''; $includeother = ''; $prefix = ''; $totalfiles = 0; $totalsize = 0; $totalsubdirs = 0; %excluded = ( bak => 1, save => 1, log => 1, ); } sub filter_files { my @PossibleFiles = @_; my @ChosenFiles; foreach my $file (@PossibleFiles) { if (-d $File::Find::dir."/".$file) { if (!$recurse) { my $depth = $File::Find::dir =~ tr[/][]; next unless ($depth < $maxdepth-1); } push(@ChosenFiles,$file); } else { next if ($file =~ /^\./); my ($extension) = ($file =~ /\.([^.]+)$/); if ((!$excluded{$extension}) && ($alltypes || $includeother || $included{$extension})) { push(@ChosenFiles,$file); } } } return @ChosenFiles; } sub store_names { my $filename = $File::Find::name; if (-d $filename) { unless ("$filename/" eq $prefix) { if ($recurse) { $subdirs{$filename} = 1; $totalsubdirs ++; } } next; } $totalfiles ++; $totalsize += -s $filename; $filename =~ s{^$prefix}{}; print $fh "$filename\n"; } sub archive_link { my ($adload,$filesurl,$suffix) = @_; if ($adload) { return ''."\n". ' '."\n". ''."\n"; } else { return ''."\n"; } } sub js { my ($url,$mime,$fname,$delarchive) = @_; &js_escape(\$url); &js_escape(\$mime); &js_escape(\$fname); my %js_lt = &Apache::lonlocal::texthash ( afdo => 'Archive file download complete.', diun => 'Download is unavailable.', tfbr => 'The archive file has been removed.', ynrd => 'You do not have rights to download the archive file.', ); &js_escape(\%js_lt); return <<"END"; END }