File Coverage

blib/lib/Net/FTPServer/DBeg1/DirHandle.pm
Criterion Covered Total %
statement 24 190 12.6
branch 0 54 0.0
condition 0 68 0.0
subroutine 8 18 44.4
pod 10 10 100.0
total 42 340 12.3


line stmt bran cond sub pod time code
1             # -*- perl -*-
2              
3             # Net::FTPServer A Perl FTP Server
4             # Copyright (C) 2000 Bibliotech Ltd., Unit 2-3, 50 Carnwath Road,
5             # London, SW6 3EG, United Kingdom.
6             #
7             # This program is free software; you can redistribute it and/or modify
8             # it under the terms of the GNU General Public License as published by
9             # the Free Software Foundation; either version 2 of the License, or
10             # (at your option) any later version.
11             #
12             # This program is distributed in the hope that it will be useful,
13             # but WITHOUT ANY WARRANTY; without even the implied warranty of
14             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15             # GNU General Public License for more details.
16             #
17             # You should have received a copy of the GNU General Public License
18             # along with this program; if not, write to the Free Software
19             # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20              
21             =pod
22              
23             =head1 NAME
24              
25             Net::FTPServer::DBeg1::DirHandle - The example DB FTP server personality
26              
27             =head1 SYNOPSIS
28              
29             use Net::FTPServer::DBeg1::DirHandle;
30              
31             =head1 METHODS
32              
33             =cut
34              
35             package Net::FTPServer::DBeg1::DirHandle;
36              
37 1     1   6 use strict;
  1         2  
  1         26  
38              
39 1     1   5 use vars qw($VERSION);
  1         2  
  1         59  
40             ( $VERSION ) = '$Revision: 1.1 $ ' =~ /\$Revision:\s+([^\s]+)/;
41              
42 1     1   5 use DBI;
  1         3  
  1         29  
43 1     1   5 use Carp qw(confess croak);
  1         2  
  1         44  
44              
45 1     1   5 use Net::FTPServer::DirHandle;
  1         2  
  1         28  
46 1     1   5 use Net::FTPServer::DBeg1::IOBlob;
  1         2  
  1         16  
47              
48 1     1   4 use vars qw(@ISA);
  1         2  
  1         53  
49              
50             @ISA = qw(Net::FTPServer::DirHandle);
51              
52             # Cached statement handles.
53 1     1   5 use vars qw($sth1 $sth2 $sth3 $sth4 $sth5 $sth6 $sth7 $sth8 $sth9 $sth10 $sth11 $sth12 $sth13 $sth14 $sth15 $sth16 $sth17 $sth18 $sth19 $sth20);
  1         1  
  1         1609  
54              
55             # Return a new directory handle.
56              
57             sub new
58             {
59 0     0 1   my $class = shift;
60 0           my $ftps = shift; # FTP server object.
61 0   0       my $pathname = shift || "/"; # (only used in internal calls)
62 0           my $dir_id = shift; # (only used in internal calls)
63              
64             # Create object.
65 0           my $self = Net::FTPServer::DirHandle->new ($ftps, $pathname);
66 0           bless $self, $class;
67              
68 0 0         if ($dir_id)
69             {
70 0           $self->{fs_dir_id} = $dir_id;
71             }
72             else
73             {
74             # Find the root directory ID.
75 0           my $sql = "select id from directories where parent_id is null";
76 0   0       $sth6 ||= $self->{ftps}{fs_dbh}->prepare ($sql);
77 0           $sth6->execute;
78              
79 0 0         my $row = $sth6->fetch
80             or die "no root directory in database (has the database been populated?): $!";
81              
82 0           $self->{fs_dir_id} = $row->[0];
83             }
84              
85 0           return $self;
86             }
87              
88             # Return a subdirectory handle or a file handle within this directory.
89              
90             sub get
91             {
92 0     0 1   my $self = shift;
93 0           my $filename = shift;
94              
95             # None of these cases should ever happen.
96 0 0 0       confess "no filename" unless defined($filename) && length($filename);
97 0 0         confess "slash filename" if $filename =~ /\//;
98 0 0         confess ".. filename" if $filename eq "..";
99 0 0         confess ". filename" if $filename eq ".";
100              
101             # Search for the file first, since files are more common than dirs.
102 0           my $sql = "select id, content from files where dir_id = ? and name = ?";
103 0   0       $sth1 ||= $self->{ftps}{fs_dbh}->prepare ($sql);
104 0           $sth1->execute (int ($self->{fs_dir_id}), $filename);
105              
106 0           my $row = $sth1->fetch;
107              
108 0 0         if ($row)
109             {
110             # Found a file.
111             return new Net::FTPServer::DBeg1::FileHandle ($self->{ftps},
112             $self->pathname . $filename,
113             $self->{fs_dir_id},
114 0           $row->[0], $row->[1],
115             $row->[2]);
116             }
117              
118             # Search for a directory.
119 0           $sql = "select id from directories where parent_id = ? and name = ?";
120 0   0       $sth2 ||= $self->{ftps}{fs_dbh}->prepare ($sql);
121 0           $sth2->execute (int ($self->{fs_dir_id}), $filename);
122              
123 0           $row = $sth2->fetch;
124              
125 0 0         if ($row)
126             {
127             # Found a directory.
128             return new Net::FTPServer::DBeg1::DirHandle ($self->{ftps},
129 0           $self->pathname . $filename . "/",
130             $row->[0]);
131             }
132              
133             # Not found.
134 0           return undef;
135             }
136              
137             # Get parent of current directory.
138              
139             sub parent
140             {
141 0     0 1   my $self = shift;
142              
143 0 0         return $self if $self->is_root;
144              
145             # Get a new directory handle.
146 0           my $dirh = $self->SUPER::parent;
147              
148             # Find directory ID of the parent directory.
149 0           my $sql = "select parent_id from directories where id = ?";
150 0   0       $sth3 ||= $self->{ftps}{fs_dbh}->prepare ($sql);
151 0           $sth3->execute (int ($self->{fs_dir_id}));
152              
153             my $row = $sth3->fetch
154 0 0         or die "directory ID ", $self->{fs_dir_id}, " missing";
155              
156 0           $dirh->{fs_dir_id} = $row->[0];
157              
158 0           return bless $dirh, ref $self;
159             }
160              
161             sub list
162             {
163 0     0 1   my $self = shift;
164 0           my $wildcard = shift;
165              
166             # Convert wildcard into a SQL LIKE pattern.
167 0 0         if ($wildcard)
168             {
169 0 0         if ($wildcard ne "*")
170             {
171 0           $wildcard = $self->{ftps}->wildcard_to_sql_like ($wildcard);
172             }
173             else
174             {
175             # If wildcard is "*" then it defaults to undefined (for speed).
176 0           $wildcard = undef;
177             }
178             }
179              
180             # Get subdirectories.
181 0           my ($sql, $sth);
182 0 0         if ($wildcard)
183             {
184 0           $sql = "select id, name from directories
185             where parent_id = ? and name like ?";
186 0   0       $sth15 ||= $self->{ftps}{fs_dbh}->prepare ($sql);
187 0           $sth15->execute (int ($self->{fs_dir_id}), $wildcard);
188 0           $sth = $sth15;
189             }
190             else
191             {
192 0           $sql = "select id, name from directories where parent_id = ?";
193 0   0       $sth4 ||= $self->{ftps}{fs_dbh}->prepare ($sql);
194 0           $sth4->execute (int ($self->{fs_dir_id}));
195 0           $sth = $sth4;
196             }
197              
198 0           my @result = ();
199 0           my $username = substr $self->{ftps}{user}, 0, 8;
200              
201 0           while (my $row = $sth->fetch)
202             {
203             my $dirh
204             = new Net::FTPServer::DBeg1::DirHandle ($self->{ftps},
205 0           $self->pathname . $row->[1] . "/",
206             $row->[0]);
207              
208 0           push @result, [ $row->[1], $dirh ];
209             }
210              
211             # Get files.
212 0 0         if ($wildcard)
213             {
214 0           $sql = "select id, name, content from files
215             where dir_id = ? and name like ?";
216 0   0       $sth16 ||= $self->{ftps}{fs_dbh}->prepare ($sql);
217 0           $sth16->execute (int ($self->{fs_dir_id}), $wildcard);
218 0           $sth = $sth16;
219             }
220             else
221             {
222 0           $sql = "select id, name, content from files where dir_id = ?";
223 0   0       $sth5 ||= $self->{ftps}{fs_dbh}->prepare ($sql);
224 0           $sth5->execute (int ($self->{fs_dir_id}));
225 0           $sth = $sth5;
226             }
227              
228 0           while (my $row = $sth->fetch)
229             {
230             my $fileh
231             = new Net::FTPServer::DBeg1::FileHandle ($self->{ftps},
232             $self->pathname . $row->[1],
233             $self->{fs_dir_id},
234 0           $row->[0],
235             $row->[2]);
236              
237 0           push @result, [ $row->[1], $fileh ];
238             }
239              
240 0           return \@result;
241             }
242              
243             sub list_status
244             {
245 0     0 1   my $self = shift;
246 0           my $wildcard = shift;
247              
248             # Convert wildcard into a SQL LIKE pattern.
249 0 0         if ($wildcard)
250             {
251 0 0         if ($wildcard ne "*")
252             {
253 0           $wildcard = $self->{ftps}->wildcard_to_sql_like ($wildcard);
254             }
255             else
256             {
257             # If wildcard is "*" then it defaults to undefined (for speed).
258 0           $wildcard = undef;
259             }
260             }
261              
262             # Get subdirectories.
263 0           my ($sql, $sth);
264 0 0         if ($wildcard)
265             {
266 0           $sql = "select id, name from directories
267             where parent_id = ? and name like ?";
268 0   0       $sth18 ||= $self->{ftps}{fs_dbh}->prepare ($sql);
269 0           $sth18->execute (int ($self->{fs_dir_id}), $wildcard);
270 0           $sth = $sth18;
271             }
272             else
273             {
274 0           $sql = "select id, name from directories where parent_id = ?";
275 0   0       $sth17 ||= $self->{ftps}{fs_dbh}->prepare ($sql);
276 0           $sth17->execute (int ($self->{fs_dir_id}));
277 0           $sth = $sth17;
278             }
279              
280 0           my @result = ();
281 0           my $username = substr $self->{ftps}{user}, 0, 8;
282              
283 0           while (my $row = $sth->fetch)
284             {
285             my $dirh
286             = new Net::FTPServer::DBeg1::DirHandle ($self->{ftps},
287 0           $self->pathname . $row->[1] . "/",
288             $row->[0]);
289              
290 0           my @status = $dirh->status;
291 0           push @result, [ $row->[1], $dirh, \@status ];
292             }
293              
294             # Get files.
295 0 0         if ($wildcard)
296             {
297 0           $sql = "select id, name, content from files
298             where dir_id = ? and name like ?";
299 0   0       $sth20 ||= $self->{ftps}{fs_dbh}->prepare ($sql);
300 0           $sth20->execute (int ($self->{fs_dir_id}), $wildcard);
301 0           $sth = $sth20;
302             }
303             else
304             {
305 0           $sql = "select id, name, content from files where dir_id = ?";
306 0   0       $sth19 ||= $self->{ftps}{fs_dbh}->prepare ($sql);
307 0           $sth19->execute (int ($self->{fs_dir_id}));
308 0           $sth = $sth19;
309             }
310              
311 0           while (my $row = $sth->fetch)
312             {
313             my $fileh
314             = new Net::FTPServer::DBeg1::FileHandle ($self->{ftps},
315             $self->pathname . $row->[1],
316             $self->{fs_dir_id},
317 0           $row->[0],
318             $row->[2]);
319              
320 0           my @status = $fileh->status;
321 0           push @result, [ $row->[1], $fileh, \@status ];
322             }
323              
324 0           return \@result;
325             }
326              
327             # Return the status of this directory.
328              
329             sub status
330             {
331 0     0 1   my $self = shift;
332 0           my $username = substr $self->{ftps}{user}, 0, 8;
333              
334 0           return ( 'd', 0755, 1, $username, "users", 1024, 0 );
335             }
336              
337             # Move a directory to elsewhere.
338              
339             sub move
340             {
341 0     0 1   my $self = shift;
342 0           my $dirh = shift;
343 0           my $filename = shift;
344              
345             # You can't move the root directory. That would be bad :-)
346 0 0         return -1 if $self->is_root;
347              
348 0           my $sql = "update directories set parent_id = ?, name = ? where id = ?";
349 0   0       $sth12 ||= $self->{ftps}{fs_dbh}->prepare ($sql);
350             $sth12->execute (int ($dirh->{fs_dir_id}), $filename,
351 0           int ($self->{fs_dir_id}));
352              
353 0           return 0;
354             }
355              
356             # We should only be able to delete a directory if the directory
357             # is empty. Postgres >= 6.5 can check this using referential
358             # constraints. However, I'm using Postgres 6.4, so instead I have
359             # to check the constraints by hand before allowing the delete.
360              
361             sub delete
362             {
363 0     0 1   my $self = shift;
364              
365             # Check referential constraints.
366 0           my $sql = "select count(id) from files where dir_id = ?";
367 0   0       $sth7 ||= $self->{ftps}{fs_dbh}->prepare ($sql);
368 0           $sth7->execute (int ($self->{fs_dir_id}));
369              
370 0 0         my $row = $sth7->fetch or die "no rows returned from count";
371              
372 0           my $nr_files = $row->[0];
373              
374 0           $sql = "select count(id) from directories where parent_id = ?";
375 0   0       $sth8 ||= $self->{ftps}{fs_dbh}->prepare ($sql);
376 0           $sth8->execute (int ($self->{fs_dir_id}));
377              
378 0 0         $row = $sth8->fetch or die "no rows returned from count";
379              
380 0           my $nr_dirs = $row->[0];
381              
382 0 0 0       return -1 if $nr_files > 0 || $nr_dirs > 0;
383              
384             # Delete the directory.
385 0           $sql = "delete from directories where id = ?";
386 0   0       $sth9 ||= $self->{ftps}{fs_dbh}->prepare ($sql);
387 0           $sth9->execute (int ($self->{fs_dir_id}));
388              
389 0           return 0;
390             }
391              
392             # Create a subdirectory.
393              
394             sub mkdir
395             {
396 0     0 1   my $self = shift;
397 0           my $dirname = shift;
398              
399 0           my $sql = "insert into directories (parent_id, name)
400             values (?, ?)";
401 0   0       $sth10 ||= $self->{ftps}{fs_dbh}->prepare ($sql);
402 0           $sth10->execute (int ($self->{fs_dir_id}), $dirname);
403              
404 0           return 0;
405             }
406              
407             # Open or create a file in this directory.
408              
409             sub open
410             {
411 0     0 1   my $self = shift;
412 0           my $filename = shift;
413 0           my $mode = shift;
414              
415 0 0         if ($mode eq "r") # Open an existing file for reading.
    0          
    0          
416             {
417 0           my $sql = "select content from files where dir_id = ? and name = ?";
418 0   0       $sth11 ||= $self->{ftps}{fs_dbh}->prepare ($sql);
419 0           $sth11->execute (int ($self->{fs_dir_id}), $filename);
420              
421 0 0         my $row = $sth11->fetch or return undef;
422              
423 0           return new Net::FTPServer::DBeg1::IOBlob ('r', $self->{ftps}{fs_dbh}, $row->[0]);
424             }
425             elsif ($mode eq "w") # Create/overwrite the file.
426             {
427 0           my $dbh = $self->{ftps}{fs_dbh};
428             my $blob_id = $dbh->func ($dbh->{pg_INV_WRITE}|$dbh->{pg_INV_READ},
429 0           'lo_creat');
430              
431             # Insert it into the database.
432 0           my $sql = "insert into files (name, dir_id, content) values (?, ?, ?)";
433 0   0       $sth14 ||= $dbh->prepare ($sql);
434 0           $sth14->execute ($filename, int ($self->{fs_dir_id}), $blob_id);
435              
436 0           return new Net::FTPServer::DBeg1::IOBlob ('w', $self->{ftps}{fs_dbh}, $blob_id);
437             }
438             elsif ($mode eq "a") # Append to the file.
439             {
440 0           my $sql = "select content from files where dir_id = ? and name = ?";
441 0   0       $sth13 ||= $self->{ftps}{fs_dbh}->prepare ($sql);
442 0           $sth13->execute (int ($self->{fs_dir_id}), $filename);
443              
444 0 0         my $row = $sth13->fetch or return undef;
445              
446 0           return new Net::FTPServer::DBeg1::IOBlob ('w', $self->{ftps}{fs_dbh}, $row->[0]);
447             }
448             else
449             {
450 0           croak "unknown file mode: $mode; use 'r', 'w' or 'a' instead";
451             }
452             }
453              
454             1 # So that the require or use succeeds.
455              
456             __END__