File Coverage

blib/lib/WebEditor/SystemExplorer.pm
Criterion Covered Total %
statement 30 266 11.2
branch 0 116 0.0
condition 0 32 0.0
subroutine 10 27 37.0
pod 0 15 0.0
total 40 456 8.7


line stmt bran cond sub pod time code
1             # -*- perl -*-
2              
3             #
4             # $Id: SystemExplorer.pm,v 1.6 2004/10/08 15:20:50 eserte Exp $
5             # Author: Slaven Rezic
6             #
7             # Copyright (C) 2003,2004 Slaven Rezic. All rights reserved.
8             # This package is free software; you can redistribute it and/or
9             # modify it under the same terms as Perl itself.
10             #
11             # Mail: slaven@rezic.de
12             # WWW: http://www.rezic.de/eserte/
13             #
14              
15             package WebEditor::SystemExplorer;
16              
17 1     1   4618 use strict;
  1         2  
  1         39  
18 1     1   6 no strict 'refs';
  1         2  
  1         28  
19 1     1   5 use vars qw($VERSION);
  1         1  
  1         78  
20             $VERSION = sprintf("%d.%02d", q$Revision: 1.6 $ =~ /(\d+)\.(\d+)/);
21              
22 1     1   6 use File::Spec;
  1         2  
  1         28  
23 1     1   6 use File::Basename qw(dirname basename);
  1         2  
  1         84  
24 1     1   6 use File::Path qw(mkpath);
  1         2  
  1         79  
25 1     1   5 use File::Copy qw(cp);
  1         2  
  1         44  
26              
27 1     1   5 use Template;
  1         2  
  1         36  
28              
29 1     1   4 use constant UPLOADDIR => "/tmp/templateeditorupload";
  1         2  
  1         50  
30              
31 1     1   9 use constant DEBUG => 0;
  1         2  
  1         3670  
32              
33             sub OK {
34 0 0   0 0   if (defined &Apache::Constants::OK) {
35 0           Apache::Constants::OK();
36             } else {
37 0           1;
38             }
39             }
40              
41             sub http_header {
42 0     0 0   my($self, $type) = @_;
43 0           my $r = $self->{R};
44 0 0         if ($r) {
    0          
45 0           $r->send_http_header($type);
46             } elsif (!$self->{Controller}{HeaderPrinted}) {
47 0           my $cgi = $self->{CGI};
48 0 0         $type = "text/html" if !defined $type;
49 0           print $cgi->header($type);
50 0           $self->{Controller}{HeaderPrinted}++;
51             }
52             }
53              
54             sub new {
55 0     0 0   my($class, $oc) = @_;
56 0           my $self = { Controller => $oc,
57             R => $oc->R,
58             C => { WEsiteinfo => $oc->C },
59             };
60 0           my $cgi;
61 0 0 0       if ($ENV{MOD_PERL} && eval q{ require Apache::Request; 1 }) {
62 0           $cgi = Apache::Request->new($self->{R});
63             } else {
64 0           require CGI;
65 0           $cgi = CGI->new;
66             }
67 0           $self->{CGI} = $cgi;
68              
69 0           bless $self, $class;
70             }
71              
72             sub dispatch {
73 0     0 0   my $self = shift;
74              
75 0           my($cgi, $c) = @{$self}{qw(CGI C)};
  0            
76 0           my $wesiteinfo = $c->{WEsiteinfo};
77              
78 0           my $t = Template->new
79             (#DEBUG => $debug, # XXX make templates debug-clean
80             INCLUDE_PATH => [$wesiteinfo->paths->site_we_templatebase,
81             $wesiteinfo->paths->we_templatebase],
82             PLUGIN_BASE => ["WE_" . $wesiteinfo->project->name . "::Plugin",
83             "WE_Frontend::Plugin"],
84             );
85              
86 0           $self->{TT} = $t;
87              
88 0   0       my $action = $cgi->param("action") || "";
89 0   0       my $path = $cgi->param("path") || "";
90 0 0         if ($action eq 'newfileform') {
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
91 0           return $self->new_file_form($path);
92             } elsif ($action eq 'newfile') {
93 0           return $self->new_file($path);
94             } elsif ($action eq 'delfile') {
95 0           return $self->do_del_file($path);
96             } elsif ($action eq 'newdirform') {
97 0           return $self->new_dir_form($path);
98             } elsif ($action eq 'newdir') {
99 0           return $self->new_dir($path);
100             } elsif ($action eq 'download') {
101 0           return $self->do_download($path);
102             } elsif ($action eq 'upload') {
103 0           return $self->upload_intermediate_page($path);
104             } elsif ($action eq 'doupload') {
105 0           return $self->do_upload($path);
106             } elsif ($action eq 'version') {
107 0           return $self->version_page($path);
108             } else {
109             # just show it
110 0           return $self->directory_or_file($path);
111             }
112             }
113              
114             sub directory_or_file {
115 0     0 0   my($self, $path, %args) = @_;
116 0           my($r, $cgi, $t, $c) = @{$self}{qw(R CGI TT C)};
  0            
117 0           my $wesiteinfo = $c->{WEsiteinfo};
118 0 0 0       $path = "/" if !defined $path || $path eq '';
119             ## ja?
120             # my $msg;
121             # if (!$self->check_permissions($path, \$msg)) {
122             # die $msg;
123             # }
124 0           my $rootdir = $wesiteinfo->paths->rootdir;
125 0           my $abspath = File::Spec->catfile($rootdir, $path);
126 0 0         if (-d $abspath) {
    0          
127 0           $self->directory_listing($path, %args);
128             } elsif (-f $abspath) {
129 0           $self->file_page($path, %args);
130             } else {
131 0           die "Invalid file type: $abspath";
132             }
133             }
134              
135             sub directory_listing {
136 0     0 0   my($self, $path, %args) = @_;
137 0           my($r, $cgi, $t, $c) = @{$self}{qw(R CGI TT C)}; my $wesiteinfo = $c->{WEsiteinfo};
  0            
  0            
138 0           my $message = $args{-message};
139 0           my $htmlmessage = $args{-htmlmessage};
140              
141 0           my $rootdir = $wesiteinfo->paths->rootdir;
142 0           my $abspath = File::Spec->catfile($rootdir, $path);
143 0           my @file;
144 0 0         if (opendir(D, $abspath)) {
145 0           my $f;
146 0           while(defined($f = readdir(D))) {
147 0 0         next if $f =~ /(^(RCS$|CVS$|\.)|~$ )/x;
148 0           my $desc = "";
149 0 0         if (open(DESC, File::Spec->catfile($abspath, ".desc.$f"))) {
150 0           local $/ = undef;
151 0           $desc = ;
152 0           close DESC;
153             }
154 0           my $absfile = File::Spec->catfile($abspath,$f);
155 0           my @s = stat $absfile;
156 0 0         push @file, {path => File::Spec->catfile($path,$f),
157             directory => -d $absfile,
158             symlink => -l $absfile,
159             symlink_target => (-l $absfile ? readlink($absfile) : undef),
160             name => $f,
161             stat => \@s,
162             modtime => $s[9],
163             size => $s[7],
164             desc => $desc,
165             (DEBUG ? (fs_path => File::Spec->catfile($abspath,$f)) : ()),
166             };
167             }
168 0           closedir D;
169             }
170 0           @file = sort { $a->{name} cmp $b->{name} } @file;
  0            
171              
172 0 0         if ($path ne '/') {
173 0           unshift @file, {path => dirname($path),
174             directory => 1,
175             name => "Parent directory",
176             desc => "Zur nächsthöheren Ebene (" . dirname($path) . ") wechseln",
177             s => [],
178             size => "",
179             modtime => "",
180             (DEBUG ? (fs_path => dirname($abspath)) : ()),
181             };
182             }
183              
184 0           $self->http_header("text/html");
185              
186 0 0         $t->process("tmpleditor_dirlisting.tpl.html",
187             { message => $message,
188             htmlmessage => $htmlmessage,
189             paths => $wesiteinfo->paths,
190             files => \@file,
191             r => $r,
192             currentdir => $path,
193             currentdirfs => $abspath,
194             dirwritable => -w $abspath,
195             debug => DEBUG,
196             }) or die $t->error;
197              
198 0           OK;
199             }
200              
201             sub file_page {
202 0     0 0   my($self, $path, %args) = @_;
203 0           my($r, $cgi, $t, $c) = @{$self}{qw(R CGI TT C)}; my $wesiteinfo = $c->{WEsiteinfo};
  0            
  0            
204 0           my $message = $args{-message};
205 0           my $htmlmessage = $args{-htmlmessage};
206              
207 0           my $rootdir = $wesiteinfo->paths->rootdir;
208 0           my $abspath = File::Spec->catfile($rootdir, $path);
209              
210 0           my $imgurl;
211             my $text;
212 0           my $text_fragment;
213 0 0 0       if ($path =~ /\.(gif|jpe?g|png|tiff?)$/) {
    0          
214 0           $imgurl = File::Spec->catfile($wesiteinfo->paths->rooturl, $path);
215             } elsif (-T $abspath && open(my $f, $abspath)) {
216 0           local $/ = undef;
217 0           $text = <$f>;
218 0 0         if (length($text) > 1024) {
219 0           $text_fragment = substr($text, 0, 512) . "\n\n ... (Teile der Datei weggelassen, bitte Download für Ansicht der kompletten Datei benutzen) ...\n\n" . substr($text, -512);
220             }
221             }
222              
223             # my @versions;
224             # my $vcs_url = "vcs://localhost/VCS::" . VCS_IMPL . $abspath;
225             # eval {
226             # my $vcs = VCS::File->new($vcs_url);
227             # @versions = $vcs->versions;
228             # };
229             # if ($@) {
230             # warn $@;
231             # }
232              
233 0           $self->http_header("text/html");
234              
235 0 0         $t->process("tmpleditor_filepage.tpl.html",
    0          
    0          
236             { message => $message,
237             htmlmessage => $htmlmessage,
238             paths => $wesiteinfo->paths,
239             r => $r,
240             currentfile => $path,
241             currentbasename => basename($path),
242             parentdir => dirname($path),
243             imgurl => (defined $imgurl ? $imgurl : undef),
244             text => $text,
245             textfragment => $text_fragment,
246             # versions => \@versions,
247             filewritable => -w $abspath,
248             selfuri => $r ? $r->uri : $cgi->url(-relative => 1),
249             }) or die $t->error;
250              
251 0           OK;
252             }
253              
254             # sub version_page {
255             # my($self, $path, %args) = @_;
256             # my($r, $cgi, $t, $c) = @{$self}{qw(R CGI TT C)}; my $wesiteinfo = $c->{WEsiteinfo};
257             # my $message = $args{-message};
258             # my $htmlmessage = $args{-htmlmessage};
259              
260             # my $rootdir = $wesiteinfo->paths->rootdir;
261             # my $abspath = File::Spec->catfile($rootdir, $path);
262              
263             # my $version_nr = $cgi->param("version");
264              
265             # my $vcs_url = "vcs://localhost/VCS::" . VCS_IMPL . $abspath . "/$version_nr";
266             # my $vcs = VCS::File->new($vcs_url);
267              
268             # my $imgurl;# XXX not implemented
269             # my $text;
270             # my $text_fragment;
271             # if (-T $abspath && open(my $f, $abspath)) {
272             # local $/ = undef;
273             # $text = <$f>;
274             # if (length($text) > 2048) {
275             # $text_fragment = substr($text, 0, 1024) . "\n\n ... (Teile der Datei weggelassen, bitte Download für komplette Datei benutzen) ...\n\n" . substr($text, -1024);
276             # }
277             # }
278              
279             # $t->process("tmpleditor_versionpage.tpl.html",
280             # { message => $message,
281             # htmlmessage => $htmlmessage,
282             # paths => $wesiteinfo->paths,
283             # r => $r,
284             # currentfile => $path,
285             # version => $version_nr,
286             # imgurl => $imgurl,
287             # text => $text,
288             # textfragment => $text_fragment,
289             # versions => \@versions,
290             # }) or die $t->error;
291              
292             # return Apache::Constants::OK();
293             # }
294              
295             #sub upload_intermediate_page {
296             sub do_upload {
297 0     0 0   my($self, $path, %args) = @_;
298 0           my($r, $cgi, $t, $c) = @{$self}{qw(R CGI TT C)}; my $wesiteinfo = $c->{WEsiteinfo};
  0            
  0            
299 0           my $message = $args{-message};
300 0           my $htmlmessage = $args{-htmlmessage};
301 0           my $rootdir = $wesiteinfo->paths->rootdir;
302 0           my $abspath = File::Spec->catfile($rootdir, $path);
303              
304 0 0         if ($cgi->param("uploadfile") eq '') {
305 0           return $self->file_page($path, -message => "Bitte Uploaddatei angeben.");
306             }
307              
308 0           mkpath([UPLOADDIR],0,0775);
309              
310             # tie my %sess, 'Apache::Session::DB_File', undef,
311             # { FileName => UPLOADDIR . "/sessions.db", # XXX make configurable
312             # LockDirectory => '/tmp',
313             # }
314             # or die "Can't tie Apache::Session: $!";
315             # my $sessionid = $sess{_session_id};
316             # my $dest_file = File::Spec->catfile(UPLOADDIR, "upload-$sessionid");
317 0           my $dest_file = UPLOADDIR . "/$$";
318              
319 0           my $upload = $cgi->upload("uploadfile");
320 0 0         if (!$upload) {
321 0           return $self->file_page($path, -message => "Kein Upload?");
322             }
323              
324 0 0         open(my $out_fh, "> $dest_file") or die "Can't write to $dest_file: $!";
325 0 0 0       my $in_fh = ref $upload && $upload->can("fh") ? $upload->fh : $upload;
326 0           while(<$in_fh>) {
327 0           print $out_fh $_;
328             }
329 0           close $in_fh;
330 0 0         close $out_fh or do {
331 0           return $self->file_page($path, -message => "Schreiben der Datei <$dest_file> fehlgeschlagen: $!");
332             };
333              
334 0 0         if (!cp($dest_file, $abspath)) {
335 0           unlink $dest_file;
336 0           return $self->file_page($path, -message => "Kopieren von <$dest_file> nach <$abspath> fehlgeschlagen: $!");
337             }
338 0           unlink $dest_file;
339              
340 0           my $msg = "";
341 0           eval {
342 0           upload_hook($path, $abspath, \$msg);
343             };
344 0 0 0       if ($@ && $@ !~ /Undefined subroutine &WebEditor::SystemExplorer::upload_hook called/) {
345 0           return $self->file_page($path, -message => "Der Upload-Hook für $path hat die folgende Fehlermeldung erzeugt: $@");
346             }
347              
348 0           $self->file_page($path, -message => "Der Upload war erfolgreich. $msg");
349             }
350              
351             # sub do_upload {
352             # my($self, $path, %args) = @_;
353             # my($r, $cgi, $t, $c) = @{$self}{qw(R CGI TT C)}; my $wesiteinfo = $c->{WEsiteinfo};
354             # my $message = $args{-message};
355             # my $htmlmessage = $args{-htmlmessage};
356             # my $rootdir = $wesiteinfo->paths->rootdir;
357             # my $abspath = File::Spec->catfile($rootdir, $path);
358              
359             # tie my %sess, 'Apache::Session::DB_File', $cgi->param("sessionid"),
360             # { FileName => UPLOADDIR . "/sessions.db", # XXX make configurable
361             # LockDirectory => '/tmp',
362             # }
363             # or die "Can't tie Apache::Session with id " . $cgi->param("sessionid") . ": $!";
364              
365             # my $uploadfile = $sess{file};
366             # if (!-e $uploadfile) {
367             # return $self->file_page($path, -message => "Upload fehlgeschlagen. Datei auf dem Server <$uploadfile> existiert nicht mehr.");
368             # }
369              
370             # my $logentry = $cgi->param("logentry")||"";
371              
372             # my $vcs_url = "vcs://localhost/VCS::" . VCS_IMPL . $abspath;
373             # my $vcs;
374             # eval {
375             # $vcs = VCS::File->new($vcs_url);
376             # };
377             # if (!$vcs) {
378             # my $rcsdir = File::Spec->catfile(dirname($abspath), "RCS");
379             # if (!-d $rcsdir) {
380             # mkdir $rcsdir;
381             # }
382             # if (!-d $rcsdir) {
383             # return $self->file_page($path, -message => "Das RCS-Verzeichnis <$rcsdir> konnte nicht angelegt werden.");
384             # }
385             # eval {
386             # $vcs = VCS::File->new($vcs_url);
387             # };
388             # if (!$vcs) {
389             # # erst einmal alte Version erzeugen
390             # system("ci", "-l", $abspath);
391             # }
392             # }
393              
394             # if (!cp($uploadfile, $abspath)) {
395             # return $self->file_page($path, -message => "<$uploadfile> konnte nicht nach <$abspath> kopiert werden.");
396             # }
397              
398             # system("ci", "-m$logentry", "-l", $abspath);
399              
400             # $vcs = VCS::File->new($vcs_url);
401              
402             # my $version_nr = ($vcs->versions)[-1]->version;
403              
404             # tied(%sess)->delete;
405              
406             # $self->file_page($path, -message => "Version $version_nr erfolgreich angelegt.");
407              
408             # }
409              
410             sub do_download {
411 0     0 0   my($self, $path) = @_;
412 0           my($r, $cgi, $t, $c) = @{$self}{qw(R CGI TT C)}; my $wesiteinfo = $c->{WEsiteinfo};
  0            
  0            
413 0           my $rootdir = $wesiteinfo->paths->rootdir;
414 0           my $abspath = File::Spec->catfile($rootdir, $path);
415              
416 0           my $contenttype = "application/octet-stream";
417 0           $self->http_header($contenttype);
418 0 0         open(my $fh, $abspath) or die "Can't open $abspath: $!";
419 0           while(<$fh>) {
420 0           print $_;
421             }
422 0           OK;
423             }
424              
425             sub do_del_file {
426 0     0 0   my($self, $path) = @_;
427 0           my($r, $cgi, $t, $c) = @{$self}{qw(R CGI TT C)};
  0            
428 0           my $wesiteinfo = $c->{WEsiteinfo};
429 0           my $rootdir = $wesiteinfo->paths->rootdir;
430 0           my $abspath = File::Spec->catfile($rootdir, $path);
431 0           unlink $abspath;
432 0 0         if (-e $abspath) {
433 0           return $self->file_page($path, -message => "Die Datei <$path> konnte nicht gelöscht werden. Grund: $!");
434             }
435 0           $self->directory_listing(dirname($path), -message => "Die Datei <$path> wurde erfolgreich gelöscht.");
436             }
437              
438             sub new_file_form {
439 0     0 0   my($self, $path, %args) = @_;
440 0           my($r, $cgi, $t, $c) = @{$self}{qw(R CGI TT C)}; my $wesiteinfo = $c->{WEsiteinfo};
  0            
  0            
441 0           my $message = $args{-message};
442 0           my $htmlmessage = $args{-htmlmessage};
443              
444 0           $self->http_header("text/html");
445              
446 0 0         $t->process("tmpleditor_newfile.tpl.html",
447             { message => $message,
448             htmlmessage => $htmlmessage,
449             paths => $wesiteinfo->paths,
450             r => $r,
451             currentdir => $path,
452             }) or die $t->error;
453              
454 0           OK;
455             }
456              
457             sub new_file {
458 0     0 0   my($self, $path, %args) = @_;
459              
460 0           my($r, $cgi, $t, $c) = @{$self}{qw(R CGI TT C)}; my $wesiteinfo = $c->{WEsiteinfo};
  0            
  0            
461 0           my $message = $args{-message};
462 0           my $htmlmessage = $args{-htmlmessage};
463              
464 0 0         if ($cgi->param("uploadfile") eq '') {
465 0           return $self->new_file_form($path, -message => "Bitte Uploaddatei angeben.");
466             }
467              
468 0   0       my $basename = $cgi->param("filename") || "";
469 0 0 0       if ($basename ne "" && _invalid_file_name($basename)) {
470 0           return $self->new_file_form($path, -message => "Dateiname ist ungültig");
471             }
472              
473 0           my $upload = $cgi->upload("uploadfile");
474 0 0         if (!$upload) {
475 0           return $self->new_file_form($path, -message => "Kein Upload?");
476             }
477              
478 0 0         if ($basename eq '') {
479 0           $basename = basename($upload->filename);
480 0           $basename =~ s|.*\\||; # strip DOS file names
481 0 0         if (_invalid_file_name($basename)) {
482 0           return $self->new_file_form($path, -message => "Automatisch ermittelter Dateiname <$basename> ist ungültig, bitte einen anderen Dateinamen angeben.");
483             }
484             }
485              
486 0           my $rootdir = $wesiteinfo->paths->rootdir;
487 0           my $abspath = File::Spec->catfile($rootdir, $path);
488              
489 0           my $out_name = File::Spec->catfile($abspath, $basename);
490              
491 0 0         open(my $out_fh, "> $out_name") or die "Can't write to $out_name: $!";
492 0 0 0       my $in_fh = ref $upload && $upload->can("fh") ? $upload->fh : $upload;
493 0           while(<$in_fh>) {
494 0           print $out_fh $_;
495             }
496 0           close $in_fh;
497 0           close $out_fh;
498              
499 0           my $msg = "";
500 0           eval {
501 0           newfile_hook($path, $abspath, \$msg);
502             };
503 0 0 0       if ($@ && $@ !~ /Undefined subroutine &WebEditor::SystemExplorer::newfile_hook called/) {
504 0           return $self->file_page($path, -message => "Der Hook für $path hat die folgende Fehlermeldung erzeugt: $@");
505             }
506              
507 0           return $self->directory_listing($path, -message => "Die Datei <$basename> wurde erfolgreich gespeichert. $msg");
508             }
509              
510             sub new_dir_form {
511 0     0 0   my($self, $path, %args) = @_;
512 0           my($r, $cgi, $t, $c) = @{$self}{qw(R CGI TT C)}; my $wesiteinfo = $c->{WEsiteinfo};
  0            
  0            
513 0           my $message = $args{-message};
514 0           my $htmlmessage = $args{-htmlmessage};
515              
516 0           $self->http_header("text/html");
517              
518 0 0         $t->process("tmpleditor_newdir.tpl.html",
519             { message => $message,
520             htmlmessage => $htmlmessage,
521             paths => $wesiteinfo->paths,
522             r => $r,
523             currentdir => $path,
524             }) or die $t->error;
525              
526 0           OK;
527             }
528              
529             sub new_dir {
530 0     0 0   my($self, $path, %args) = @_;
531              
532 0           my($r, $cgi, $t, $c) = @{$self}{qw(R CGI TT C)}; my $wesiteinfo = $c->{WEsiteinfo};
  0            
  0            
533 0           my $message = $args{-message};
534 0           my $htmlmessage = $args{-htmlmessage};
535              
536 0   0       my $basename = $cgi->param("dirname") || "";
537 0 0         if ($basename eq "") {
538 0           return $self->new_dir_form($path, -message => "Es wurde kein Verzeichnisname angegeben.");
539             }
540 0 0         if (_invalid_file_name($basename)) {
541 0           return $self->new_dir_form($path, -message => "Verzeichnisname ist ungültig");
542             }
543              
544 0           my $rootdir = $wesiteinfo->paths->rootdir;
545 0           my $abspath = File::Spec->catfile($rootdir, $path);
546              
547 0           my $out_name = File::Spec->catfile($abspath, $basename);
548              
549 0           mkdir $out_name, 0777;
550 0 0         if (!-d $out_name) {
551 0           return $self->new_dir_form($path, -message => "Das Verzeichnis $out_name konnte nicht erzeugt werden: $!");
552             }
553              
554 0           return $self->directory_listing($path, -message => "Das Verzeichnis <$basename> wurde erfolgreich erzeugt.");
555             }
556              
557             sub check_permissions {
558 0     0 0   my($self, $relpath, $msgref) = @_;
559 0 0         if (_invalid_path_name($relpath)) {
560 0 0         $$msgref = "Invalid file name (contains ..): $relpath"
561             if ref $msgref;
562 0           return 0;
563             }
564 0 0         if ($relpath !~ m#^(|images|styles|we/(styles|script|images|we_templates|oszportal_(templates|prototypes|we_prototypes)))/#) {
565 0 0         $$msgref = "Invalid file name: $relpath"
566             if ref $msgref;
567 0           return 0;
568             }
569 0           return 1;
570             }
571              
572             sub _invalid_path_name {
573 0     0     my $file = shift;
574 0 0         return 1 if $file =~ /(^|\/)\.\.($|\/)/;
575             }
576              
577             sub _invalid_file_name {
578 0     0     my $file = shift;
579 0 0         return 1 if $file =~ m|/|;
580 0 0         return 1 if $file =~ m|^\.|; # no hidden files, please
581 0           return _invalid_path_name($file);
582             }
583              
584             1;
585              
586             __END__