File Coverage

blib/lib/Net/FTPServer/InMem/DirHandle.pm
Criterion Covered Total %
statement 129 142 90.8
branch 41 54 75.9
condition 12 20 60.0
subroutine 16 17 94.1
pod 10 10 100.0
total 208 243 85.6


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::InMem::DirHandle - Store files in local memory
26              
27             =head1 SYNOPSIS
28              
29             use Net::FTPServer::InMem::DirHandle;
30              
31             =head1 METHODS
32              
33             =cut
34              
35             package Net::FTPServer::InMem::DirHandle;
36              
37 75     75   1115 use strict;
  75         133  
  75         1935  
38              
39 75     75   335 use vars qw($VERSION);
  75         195  
  75         4367  
40             ( $VERSION ) = '$Revision: 1.1 $ ' =~ /\$Revision:\s+([^\s]+)/;
41              
42 75     75   411 use Carp qw(confess croak);
  75         147  
  75         3075  
43 75     75   403 use IO::Scalar;
  75         153  
  75         3905  
44              
45 75     75   436 use Net::FTPServer::DirHandle;
  75         980  
  75         1805  
46              
47 75     75   382 use vars qw(@ISA);
  75         144  
  75         3438  
48              
49             @ISA = qw(Net::FTPServer::DirHandle);
50              
51             # Global variables.
52 75     75   438 use vars qw(%dirs $next_dir_id %files $next_file_id);
  75         145  
  75         99692  
53              
54             # The initial directory structure.
55             $next_dir_id = 2;
56             $dirs{1} = { name => "", parent => 0 };
57             $next_file_id = 1;
58              
59             # Return a new directory handle.
60              
61             sub new
62             {
63 72     72 1 162 my $class = shift;
64 72         122 my $ftps = shift; # FTP server object.
65 72   100     438 my $pathname = shift || "/"; # (only used in internal calls)
66 72         139 my $dir_id = shift; # (only used in internal calls)
67              
68             # Create object.
69 72         698 my $self = Net::FTPServer::DirHandle->new ($ftps, $pathname);
70 72         177 bless $self, $class;
71              
72 72 100       184 if ($dir_id)
73             {
74 25         61 $self->{fs_dir_id} = $dir_id;
75             }
76             else
77             {
78 47         254 $self->{fs_dir_id} = 1;
79             }
80              
81 72         356 return $self;
82             }
83              
84             # Return a subdirectory handle or a file handle within this directory.
85              
86             sub get
87             {
88 179     179 1 335 my $self = shift;
89 179         347 my $filename = shift;
90              
91             # None of these cases should ever happen.
92 179 50 33     747 confess "no filename" unless defined($filename) && length($filename);
93 179 50       537 confess "slash filename" if $filename =~ /\//;
94 179 50       432 confess ".. filename" if $filename eq "..";
95 179 50       411 confess ". filename" if $filename eq ".";
96              
97             # Search for the file first, since files are more common than dirs.
98 179         740 foreach (keys %files)
99             {
100 635 100 100     2338 if ($files{$_}{dir_id} == $self->{fs_dir_id} &&
101             $files{$_}{name} eq $filename)
102             {
103             # Found a file.
104             return new Net::FTPServer::InMem::FileHandle ($self->{ftps},
105             $self->pathname . $filename,
106             $self->{fs_dir_id},
107             $_,
108 99         393 $files{$_}{content});
109             }
110             }
111              
112             # Search for a directory.
113 80         361 foreach (keys %dirs)
114             {
115 133 100 100     593 if ($dirs{$_}{parent} == $self->{fs_dir_id} &&
116             $dirs{$_}{name} eq $filename)
117             {
118             # Found a directory.
119             return new Net::FTPServer::InMem::DirHandle ($self->{ftps},
120 7         32 $self->pathname . $filename . "/",
121             $_);
122             }
123             }
124              
125             # Not found.
126 73         292 return undef;
127             }
128              
129             # Get parent of current directory.
130              
131             sub parent
132             {
133 3     3 1 5 my $self = shift;
134              
135 3 100       24 return $self if $self->is_root;
136              
137             # Get a new directory handle.
138 2         8 my $dirh = $self->SUPER::parent;
139              
140             # Find directory ID of the parent directory.
141 2         9 $dirh->{fs_dir_id} = $dirs{$self->{fs_dir_id}}{parent};
142              
143 2         6 return bless $dirh, ref $self;
144             }
145              
146             sub list
147             {
148 3     3 1 8 my $self = shift;
149 3         7 my $wildcard = shift;
150              
151             # Convert wildcard to regular expression.
152 3 100       9 if ($wildcard)
153             {
154 2 100       8 if ($wildcard ne "*")
155             {
156 1         6 $wildcard = $self->{ftps}->wildcard_to_regex ($wildcard);
157             }
158             else
159             {
160 1         3 $wildcard = undef;
161             }
162             }
163              
164             # Get subdirectories.
165 3         9 my @dirs;
166 3 100       12 if ($wildcard)
167             {
168 1         5 @dirs = grep { $dirs{$_}{parent} == $self->{fs_dir_id} &&
169 4 100       60 $dirs{$_}{name} =~ /$wildcard/ } keys %dirs;
170             }
171             else
172             {
173 2         12 @dirs = grep { $dirs{$_}{parent} == $self->{fs_dir_id} } keys %dirs;
  8         30  
174             }
175              
176 3         12 my @result = ();
177 3         15 my $username = substr $self->{ftps}{user}, 0, 8;
178              
179 3         11 foreach (@dirs)
180             {
181             my $dirh
182             = new Net::FTPServer::InMem::DirHandle ($self->{ftps},
183 6         17 $self->pathname . $dirs{$_}{name} . "/",
184             $_);
185              
186 6         21 push @result, [ $dirs{$_}{name}, $dirh ];
187             }
188              
189             # Get files.
190 3         8 my @files;
191 3 100       10 if ($wildcard)
192             {
193 1         6 @files = grep { $files{$_}{dir_id} == $self->{fs_dir_id} &&
194 6 50       41 $files{$_}{name} =~ /$wildcard/ } keys %files;
195             }
196             else
197             {
198 2         12 @files = grep { $files{$_}{dir_id} == $self->{fs_dir_id} } keys %files;
  12         33  
199             }
200              
201 3         9 foreach (@files)
202             {
203             my $fileh
204             = new Net::FTPServer::InMem::FileHandle ($self->{ftps},
205             $self->pathname . $files{$_}{name},
206             $self->{fs_dir_id},
207             $_,
208 18         49 $files{$_}{content});
209              
210 18         65 push @result, [ $files{$_}{name}, $fileh ];
211             }
212              
213 3         13 return \@result;
214             }
215              
216             sub list_status
217             {
218 8     8 1 21 my $self = shift;
219 8         18 my $wildcard = shift;
220              
221             # Convert wildcard to regular expression.
222 8 100       25 if ($wildcard)
223             {
224 2 100       8 if ($wildcard ne "*")
225             {
226 1         23 $wildcard = $self->{ftps}->wildcard_to_regex ($wildcard);
227             }
228             else
229             {
230 1         2 $wildcard = undef;
231             }
232             }
233              
234             # Get subdirectories.
235 8         18 my @dirs;
236 8 100       27 if ($wildcard)
237             {
238 1         4 @dirs = grep { $dirs{$_}{parent} == $self->{fs_dir_id} &&
239 4 100       43 $dirs{$_}{name} =~ /$wildcard/ } keys %dirs;
240             }
241             else
242             {
243 7         31 @dirs = grep { $dirs{$_}{parent} == $self->{fs_dir_id} } keys %dirs;
  32         112  
244             }
245              
246 8         23 my @result = ();
247 8         33 my $username = substr $self->{ftps}{user}, 0, 8;
248              
249 8         23 foreach (@dirs)
250             {
251             my $dirh
252             = new Net::FTPServer::InMem::DirHandle ($self->{ftps},
253 12         46 $self->pathname . $dirs{$_}{name} . "/",
254             $_);
255              
256 12         35 my @status = $dirh->status;
257 12         61 push @result, [ $dirs{$_}{name}, $dirh, \@status ];
258             }
259              
260             # Get files.
261 8         20 my @files;
262 8 100       26 if ($wildcard)
263             {
264 1         5 @files = grep { $files{$_}{dir_id} == $self->{fs_dir_id} &&
265 6 50       38 $files{$_}{name} =~ /$wildcard/ } keys %files;
266             }
267             else
268             {
269 7         31 @files = grep { $files{$_}{dir_id} == $self->{fs_dir_id} } keys %files;
  20         74  
270             }
271              
272 8         27 foreach (@files)
273             {
274             my $fileh
275             = new Net::FTPServer::InMem::FileHandle ($self->{ftps},
276             $self->pathname . $files{$_}{name},
277             $self->{fs_dir_id},
278             $_,
279 14         47 $files{$_}{content});
280              
281 14         45 my @status = $fileh->status;
282 14         65 push @result, [ $files{$_}{name}, $fileh, \@status ];
283             }
284              
285 8         42 return \@result;
286             }
287              
288             # Return the status of this directory.
289              
290             sub status
291             {
292 15     15 1 28 my $self = shift;
293 15         38 my $username = substr $self->{ftps}{user}, 0, 8;
294              
295 15         76 return ( 'd', 0755, 1, $username, "users", 1024, 0 );
296             }
297              
298             # Move a directory to elsewhere.
299              
300             sub move
301             {
302 0     0 1 0 my $self = shift;
303 0         0 my $dirh = shift;
304 0         0 my $filename = shift;
305              
306             # You can't move the root directory. That would be bad :-)
307 0 0       0 return -1 if $self->is_root;
308              
309 0         0 $dirs{$self->{fs_dir_id}}{parent} = $dirh->{fs_dir_id};
310 0         0 $dirs{$self->{fs_dir_id}}{name} = $filename;
311              
312 0         0 return 0;
313             }
314              
315             sub delete
316             {
317 1     1 1 2 my $self = shift;
318              
319 1         5 delete $dirs{$self->{fs_dir_id}};
320              
321 1         4 return 0;
322             }
323              
324             # Create a subdirectory.
325              
326             sub mkdir
327             {
328 11     11 1 29 my $self = shift;
329 11         23 my $dirname = shift;
330              
331 11         89 $dirs{$next_dir_id++} = { name => $dirname, parent => $self->{fs_dir_id} };
332              
333 11         51 return 0;
334             }
335              
336             # Open or create a file in this directory.
337              
338             sub open
339             {
340 56     56 1 112 my $self = shift;
341 56         112 my $filename = shift;
342 56         117 my $mode = shift;
343              
344 56 50       232 if ($mode eq "r") # Open an existing file for reading.
    100          
    50          
345             {
346 0         0 foreach (keys %files)
347             {
348 0 0 0     0 if ($files{$_}{dir_id} == $self->{fs_dir_id} &&
349             $files{$_}{name} eq $filename)
350             {
351 0         0 return new IO::Scalar ($files{$_}{content});
352             }
353             }
354              
355 0         0 return undef;
356             }
357             elsif ($mode eq "w") # Create/overwrite the file.
358             {
359             # If a file with the same name exists already, erase it.
360 55         199 foreach (keys %files)
361             {
362 204 100 66     708 if ($files{$_}{dir_id} == $self->{fs_dir_id} &&
363             $files{$_}{name} eq $filename)
364             {
365 1         4 delete $files{$_};
366 1         2 last;
367             }
368             }
369              
370 55         149 my $content = "";
371              
372             $files{$next_file_id++} = { dir_id => $self->{fs_dir_id},
373 55         396 name => $filename,
374             content => \$content };
375              
376 55         625 return new IO::Scalar (\$content);
377             }
378             elsif ($mode eq "a") # Append to the file.
379             {
380 1         6 foreach (keys %files)
381             {
382 1 50 33     18 if ($files{$_}{dir_id} == $self->{fs_dir_id} &&
383             $files{$_}{name} eq $filename)
384             {
385 1         11 return new IO::Scalar ($files{$_}{content});
386             }
387             }
388              
389 0           return undef;
390             }
391             else
392             {
393 0           croak "unknown file mode: $mode; use 'r', 'w' or 'a' instead";
394             }
395             }
396              
397             1 # So that the require or use succeeds.
398              
399             __END__