File Coverage

blib/lib/Net/FTPServer.pm
Criterion Covered Total %
statement 1192 2249 53.0
branch 396 1060 37.3
condition 94 225 41.7
subroutine 122 199 61.3
pod 28 33 84.8
total 1832 3766 48.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              
22             =pod
23              
24             =head1 NAME
25              
26             Net::FTPServer - A secure, extensible and configurable Perl FTP server
27              
28             =head1 SYNOPSIS
29              
30             ftpd.sh [--help] [-d] [-v] [-p port] [-s] [-S] [-V] [-C conf_file]
31             [-P pidfile] [-o option=value]
32              
33             =head1 DESCRIPTION
34              
35             C is a secure, extensible and configurable FTP
36             server written in Perl.
37              
38             Current features include:
39              
40             * Authenticated FTP access.
41             * Anonymous FTP access.
42             * Complete implementation of current RFCs.
43             * ASCII or binary type file transfers.
44             * Active or passive mode file transfers.
45             * Run standalone or from inetd(8).
46             * Security features: chroot, resource limits, tainting,
47             protection against buffer overflows.
48             * IP-based and/or IP-less virtual hosts.
49             * Complete access control system.
50             * Anonymous read-only FTP personality.
51             * Virtual filesystem allows files to be served
52             from a database.
53             * Directory aliases and CDPATH support.
54             * Extensible command set.
55             * Generate archives on the fly.
56              
57             =head1 INSTALLING AND RUNNING THE SERVER
58              
59             A standard C file is supplied with the distribution.
60             Full documentation for all the possible options which you
61             may use in this file is contained in this manual page. See
62             the section CONFIGURATION below.
63              
64             After doing C, the standard C file should
65             have been installed in C. You will probably need to
66             edit this file to suit your local configuration.
67              
68             Also after doing C, several start-up scripts will have
69             been installed in C. (On Debian in C or
70             C). Each start-up script starts the server in a
71             different configuration: either as a full FTP server, or as an
72             anonymous-only read-only FTP server, etc.
73              
74             The commonly used scripts are:
75              
76             * /usr/sbin/ftpd.pl
77             * /usr/sbin/ro-ftpd.pl
78              
79             The first script is for the full FTP server.
80              
81             These scripts assume that the C interpreter can be found on the
82             current C<$PATH>. In the rare situation when this is not the case, you
83             may need to edit these scripts.
84              
85             =head2 STANDALONE SERVER
86              
87             If you have a high load site, you will want to run C
88             as a standalone server. To start C as a standalone
89             server, do:
90              
91             /usr/sbin/ftpd.pl -S
92              
93             You may want to add this to your local start-up files so that
94             the server starts automatically when you boot the machine.
95              
96             To stop the server, do:
97              
98             killall ftpd.pl
99              
100             (Note: C points out that the above is a Linux-ism. Solaris
101             administrators may get a nasty shock if they type C as C!
102             Just kill the parent C process by hand instead).
103              
104             =head2 RUNNING FROM INETD
105              
106             Add the following line to C:
107              
108             ftp stream tcp nowait root /usr/sbin/tcpd ftpd.pl
109              
110             (This assumes that you have the C package installed to
111             provide basic access control through C and
112             C. This access control is in addition to any access
113             control which you may configure through C.)
114              
115             After editing this file you will need to inform C:
116              
117             killall -HUP inetd
118              
119             =head2 RUNNING FROM XINETD
120              
121             C is a modern alternative to C which is supposedly
122             simpler to configure. In practice, however, it has proven to be quite
123             difficult to configure services under C (mainly because
124             C gives no diagnostic information when things go wrong). The
125             following configuration has worked for me:
126              
127             Create the file C containing:
128              
129             # default: on
130             # description: Net::FTPServer, a secure, \
131             # extensible, configurable FTP server.
132             #
133             service ftp
134             {
135             socket_type = stream
136             wait = no
137             user = root
138             server = /usr/sbin/ftpd.pl
139             log_on_success += DURATION USERID
140             log_on_failure += USERID
141             disable = no
142             }
143              
144             Check any other possible FTP server configurations to ensure they
145             are all disabled (ie. C in all other files).
146              
147             Restart C using:
148              
149             /etc/init.d/xinetd restart
150              
151             =head1 COMMAND LINE FLAGS
152              
153             --help Display help and exit
154             -d, -v Enable debugging
155             -p PORT Listen on port PORT instead of the default port
156             -s Run in daemon mode (default: run from inetd)
157             -S Run in background and in daemon mode
158             -V Show version information and exit
159             -C CONF Use CONF as configuration file (default:
160             /etc/ftpd.conf)
161             -P PIDFILE Save pid into PIDFILE (daemon mode only)
162             -o option=value Override config file option with value
163             --test Test mode (used only in automatic testing scripts)
164              
165             =head1 CONFIGURING AND EXTENDING THE SERVER
166              
167             C can be configured and extended in a number
168             of different ways.
169              
170             Firstly, almost all common server configuration can be carried
171             out by editing the configuration file C.
172              
173             Secondly, commands can be loaded into the server at run-time
174             to provide custom extensions to the common FTP command set.
175             These custom commands are written in Perl.
176              
177             Thirdly, one of several different supplied I can be
178             chosen. Personalities can be used to make deep changes to the FTP
179             server: for example, there is a supplied personality which allows the
180             FTP server to serve files from a relational database. By subclassing
181             C, C and
182             C you may also write your own
183             personalities.
184              
185             The next sections talk about each of these possibilities in turn.
186              
187             =head2 CONFIGURATION
188              
189             A standard C file is supplied with C
190             in the distribution. The possible configuration options are listed in
191             full below.
192              
193             Simple configuration options can also be given on the command line
194             using the C<-o> option. Command line configuration options override
195             those from the configuration file.
196              
197             =over 4
198              
199             =item EInclude filenameE
200              
201             Use the EInclude filenameE directive to include
202             the contents of C directly at the current point
203             within the configuration file.
204              
205             You cannot use EIncludeE within a EHostE
206             section, or at least you I but it wonE<39>t work the
207             way you expect.
208              
209             =item EIncludeWildcard wildcardE
210              
211             Include all files matching C at this point in
212             the file. The files are included in alphabetical order.
213              
214             You cannot use EIncludeWildcardE within a EHostE
215             section, or at least you I but it wonE<39>t work the
216             way you expect.
217              
218             =item debug
219              
220             Run with debugging. Equivalent to the command line C<-d> option.
221              
222             Default: 0
223              
224             Example: C
225              
226             =item port
227              
228             The TCP port number on which the FTP server listens when
229             running in daemon mode (see C option below).
230              
231             Default: The standard ftp/tcp service port from C
232              
233             Example: C
234              
235             =item daemon mode
236              
237             Run as a daemon. If set, the FTP server will open a listening
238             socket on its default port number, accept new connections and
239             fork off a new process to handle each connection. If not set
240             (the default), the FTP server will handle a single connection
241             on stdin/stdout, which is suitable for use from inetd.
242              
243             The equivalent command line options are C<-s> and C<-S>.
244              
245             Default: 0
246              
247             Example: C
248              
249             =item run in background
250              
251             Run in the background. If set, the FTP server will fork into
252             the background before running.
253              
254             The equivalent command line option is C<-S>.
255              
256             Default: 0
257              
258             Example: C
259              
260             =item error log
261              
262             If set, then all warning and error messages are appended to
263             this file. If not set, warning and error messages get sent to
264             STDERR and to syslog.
265              
266             Having an error log is I.
267              
268             Default: (not set, warnings and errors go to syslog)
269              
270             Example: C
271              
272             =item rotate log files
273              
274             If set, and if the log file names contain a '%' directive, then the
275             server will check if a new log file is needed whenever the system
276             accepts a new connection. This implements a log rotation feature for
277             long-running servers.
278              
279             If not set, then any '%' directive will be evaluated only when the log
280             files gets created.
281              
282             Default: (not set, log file name evaluated only once)
283              
284             Example: C
285              
286             =item maintainer email
287              
288             MaintainerE<39>s email address.
289              
290             Default: root@I
291              
292             Example: C
293              
294             =item class
295              
296             Assign users into classes. One or more C directives can be
297             added to the configuration file to aggregate individual users into
298             larger groups of users called classes.
299              
300             By default all anonymous users are in class C and every
301             other user is in class C.
302              
303             The configuration file can contain zero or more C
304             directives. The format of the class directive is either:
305              
306             class: CLASSNAME USERNAME[,USERNAME[,...]]
307              
308             or:
309              
310             class: CLASSNAME { perl code ... }
311              
312             Examples of the first form are:
313              
314             class: staff rich
315             class: students ann,mary,pete
316              
317             User C will be placed into class C, and users C,
318             C and C will be placed into class C.
319              
320             Examples of the second form are:
321              
322             class: family { /jones$/ }
323             class: friends { $_ ne "jeff" }
324              
325             Any username ending in C (eg. C, C) will be
326             in class C. Any other user except C will be placed in
327             class C. Note that the Perl code must be surrounded by
328             C<{...}> and must return a boolean true or false value. The username
329             is available as C<$_>. The Perl code is arbitrary: it might, for
330             example, use an external file or database lookup in order to work out
331             if a user belongs to a class.
332              
333             C directives are evaluated in the order in which they appear in
334             the configuration file until one matches the username.
335              
336             Default: Anonymous users are assigned to class C and
337             everyone else is assigned to class C.
338              
339             =item timeout
340              
341             Timeout on control connection. If a command has not been
342             received after this many seconds, the server drops the
343             connection. You may set this to zero to disable timeouts
344             completely (although this is not recommended).
345              
346             Default: 900 (seconds)
347              
348             Example: C
349              
350             =item limit memory
351              
352             =item limit nr processes
353              
354             =item limit nr files
355              
356             Resource limits. These limits are applied to each child
357             process and are important in avoiding denial of service (DoS)
358             attacks against the FTP server.
359              
360             Resource Default Unit
361             limit memory 16384 KBytes Amount of memory per child
362             limit nr processes 10 (none) Number of processes
363             limit nr files 20 (none) Number of open files
364              
365             To instruct the server I to limit a particular resource, set the
366             limit to C<-1>.
367              
368             Example:
369              
370             limit memory: 32768
371             limit nr processes: 20
372             limit nr files: 40
373              
374             limit nr processes: -1
375              
376             =item max clients
377              
378             Limit on the number of clients who can simultaneously connect.
379             If this limit is ever reached, new clients will immediately be
380             closed. It will not even ask the client to login. This
381             feature works in daemon mode only.
382              
383             Default: 255
384              
385             Example: C
386              
387             =item max clients message
388              
389             Message to display when ``max clients'' has been reached.
390              
391             You may use the following % escape sequences within the
392             message for internal variables:
393              
394             %x ``max clients'' setting that has been reached
395             %E maintainer email address (from ``maintainer email''
396             setting above)
397             %G time in GMT
398             %R remote hostname or IP address if ``resolve addresses''
399             is not set
400             %L local hostname
401             %T local time
402             %% just an ordinary ``%''
403              
404             Default: Maximum connections reached
405              
406             Example: C
407              
408             =item resolve addresses
409              
410             Resolve addresses. If set, attempt to do a reverse lookup on
411             client addresses for logging purposes. If you set this then
412             some clients may experience long delays when they try to
413             connect. Not recommended on high load servers.
414              
415             Default: 0
416              
417             Example: C
418              
419             =item require resolved addresses
420              
421             Require resolved addresses. If set, client addresses must validly resolve
422             otherwise clients will not be able to connect. If you set this
423             then some clients will not be able to connect, even though it is
424             probably the fault of their ISP.
425              
426             Default: 0
427              
428             Example: C
429              
430             =item change process name
431              
432             Change process name. If set (the default) then the FTP server will
433             change its process name to reflect the IP address or hostname of
434             the client. If not set then the FTP server will not try to change
435             its process name.
436              
437             Default: 1
438              
439             Example: C
440              
441             =item greeting type
442              
443             Greeting type. The greeting is printed before the user has logged in.
444             Possible greeting types are:
445              
446             full Full greeting, including hostname and version number.
447             brief Hostname only.
448             terse Nothing
449             text Display greeting from ``greeting text'' option.
450              
451             The SITE VERSION command can also reveal the version number. You
452             may need to turn this off by setting C
453             below.
454              
455             Default: full
456              
457             Example: C
458              
459             =item greeting text
460              
461             Greeting text. If the C is set to C then this
462             contains the text to display.
463              
464             Default: none
465              
466             Example: Cll be your server today.>
467              
468             =item welcome type
469              
470             Welcome type. The welcome is printed after a user has logged in.
471             Possible welcome types are:
472              
473             normal Normal welcome message: ``Welcome <>.''
474             text Take the welcome message from ``welcome text'' option.
475             file Take the welcome message from ``welcome file'' file.
476              
477             Default: normal
478              
479             Example: C
480              
481             =item welcome text
482              
483             If C is set to C, then this contains the text
484             to be printed after a user has logged in.
485              
486             You may use the following % escape sequences within the welcome
487             text to substitute for internal variables:
488              
489             %E maintainer's email address (from ``maintainer email''
490             setting above)
491             %G time in GMT
492             %R remote hostname or IP address if ``resolve addresses''
493             is not set
494             %L local hostname
495             %m user's home directory (see ``home directory'' below)
496             %T local time
497             %U username given when logging in
498             %u currently a synonym for %U, but in future will be
499             determined from RFC931 authentication, like wu-ftpd
500             %% just an ordinary ``%''
501              
502             Default: none
503              
504             Example: C
505              
506             =item welcome file
507              
508             If C is set to C, then this contains the file
509             to be printed after a user has logged in.
510              
511             You may use any of the % escape sequences defined in C
512             above.
513              
514             Default: none
515              
516             Example: C
517              
518             =item home directory
519              
520             Home directory. This is the home directory where we put the
521             user once they have logged in. This only applies to non-anonymous
522             logins. Anonymous logins are always placed in "/", which is at the
523             root of their chrooted environment.
524              
525             You may use an absolute path here, or else one of the following
526             special forms:
527              
528             %m Use home directory from password file or from NSS.
529             %U Username.
530             %% A single % character.
531              
532             For example, to force a user to start in C<~/anon-ftp> when they
533             log in, set this to C<%m/anon-ftp>.
534              
535             Note that setting the home directory does not perform a chroot.
536             Use the C setting below to jail users into a
537             particular directory.
538              
539             Home directories are I to the current root directory.
540              
541             In the anonymous read-only (ro-ftpd) personality, set home
542             directory to C or else you will get a warning whenever a user
543             logs in.
544              
545             Default: %m
546              
547             Examples:
548              
549             home directory: %m/anon-ftp
550             home directory: /
551              
552             =item root directory
553              
554             Root directory. Immediately after logging in, perform a chroot
555             into the named directory. This only applies to non-anonymous
556             logins, and furthermore it only applies if you have a non-database
557             VFS installed. Database VFSes typically cannot perform chroot
558             (or, to be more accurate, they have a different concept of
559             chroot - typically assigning each user their own completely
560             separate namespace).
561              
562             You may use %m and %U as above.
563              
564             For example, to jail a user under C<~/anon-ftp> after login, do:
565              
566             home directory: /
567             root directory: %m/anon-ftp
568              
569             Notice that the home directory is I to the current
570             root directory.
571              
572             Default: (none)
573              
574             Example: C
575              
576             =item time zone
577              
578             Time zone to be used for MDTM and LIST stat information.
579              
580             Default: GMT
581              
582             Examples:
583              
584             time zone: Etc/GMT+3
585             time zone: Europe/London
586             time zone: US/Mountain
587              
588             =item local address
589              
590             Local addresses. If you wish the FTP server (in daemon mode) to
591             only bind to a particular local interface, then give its address
592             here.
593              
594             Default: none
595              
596             Example: C
597              
598             =item allow anonymous
599              
600             Allow anonymous access. If set, then allow anonymous access through
601             the C and C accounts.
602              
603             Default: 0
604              
605             Example: C
606              
607             =item anonymous password check
608              
609             =item anonymous password enforce
610              
611             Validate email addresses. Normally when logging in anonymously,
612             you are asked to enter your email address as a password. These options
613             can be used to check and enforce email addresses in this field (to
614             some extent, at least -- you obviously canE<39>t force someone to
615             enter a true email address).
616              
617             The C option may be set to C,
618             C, C or C. If set to C then
619             the user must enter a valid RFC 822 email address as password. If
620             set to C then a valid RFC 822 email address must be
621             entered, and various common browser email addresses like
622             C and CUser@> are refused. If set to C
623             then we just check that the address contains an @ char. If set to
624             C, then we do no checking. The default is C.
625              
626             If the C option is set and the
627             password fails the check above, then the user will not be allowed
628             to log in. The default is 0 (unset).
629              
630             These options only have effect when C is set.
631              
632             Example:
633              
634             anonymous password check: rfc822
635             anonymous password enforce: 1
636              
637             =item allow proxy ftp
638              
639             Allow proxy FTP. If this is set, then the FTP server can be told to
640             actively connect to addresses and ports on any machine in the world.
641             This is not such a great idea, but required if you follow the RFC
642             very closely. If not set (the default), the FTP server will only
643             connect back to the client machine.
644              
645             Default: 0
646              
647             Example: C
648              
649             =item allow connect low port
650              
651             Allow the FTP server to connect back to ports E 1024. This is rarely
652             useful and could pose a serious security hole in some circumstances.
653              
654             Default: 0
655              
656             Example: C
657              
658             =item passive port range
659              
660             What range of local ports will the FTP server listen on in passive
661             mode? Choose a range here like C<1024-5999,49152-65535>. The special
662             value C<0> means that the FTP server will use a kernel-assigned
663             ephemeral port.
664              
665             Default: 49152-65535
666              
667             Example: C
668              
669             =item ftp data port
670              
671             Which source port to use for active (non-passive) mode when connecting
672             to the client for PORT mode transfers. The special value C<0> means
673             that the FTP server will use a kernel-assigned ephemeral port. To
674             strictly follow RFC, this should be set to C. This may
675             be required for certain brain-damaged firewall configurations. However,
676             for security reasons, the default setting is intentionally set to C<0>
677             to utilize a kernel-assigned ephemeral port. Use this directive at
678             your own risk!
679              
680             SECURITY PRECAUTIONS:
681              
682             1) Unfortunately, to use a port E 1024 requires super-user
683             privileges. Thus, low ports will not work unless the FTP server is
684             invoked as super-user. This also implies that all processes handling
685             the client connections must also I super-user throughout
686             the entire session. It is highly discouraged to use a low port.
687              
688             http://cr.yp.to/ftp/security.html
689             (See "Connection laundering" section)
690              
691             2) There sometimes exists a danger of needing to connect to the
692             same remote host:port. Using the same IP/port on both sides
693             will cause connect() to fail if the old socket is still being
694             broken down. This condition will not occur if using an ephemeral
695             port.
696              
697             http://groups.google.com/groups?selm=fa.epucqgv.1l2kl0e@ifi.uio.no
698             (See "unable to create socket" comment)
699              
700             3) Many hackers use source port 20 to blindly circumvent certain
701             naive firewalls. Using an ephemeral port (the default) may help
702             discourage such dangerous naivety.
703              
704             man nmap
705             (See the -g option)
706              
707             Default: 0
708              
709             Example: C
710              
711             =item max login attempts
712              
713             Maximum number of login attempts before we drop the connection
714             and issue a warning in the logs. Wu-ftpd defaults this to 5.
715              
716             Default: 3
717              
718             Example: C
719              
720             =item pam authentication
721              
722             Use PAM for authentication. Required on systems such as Red Hat Linux
723             and Solaris which use PAM for authentication rather than the normal
724             C mechanisms. You will need to have the C
725             Perl module installed for this to work.
726              
727             Default: 0
728              
729             Example: C
730              
731             =item pam application name
732              
733             If PAM authentication is enabled, then this is the PAM application
734             name. I have used C as the default which is the same name
735             that wu-ftpd chooses. FreeBSD users will want to use C here.
736              
737             Default: ftp
738              
739             Example: C
740              
741             =item password file
742              
743             Only in the C personality, this allows you to specify a password
744             file which is used for authentication. If you enable this option, then
745             normal PAM or C is bypassed and this password file is
746             used instead.
747              
748             Each line in the password file has the following format:
749              
750             username:crypted_password:unix_user[:root_directory]
751              
752             Comments and blank lines are ignored.
753              
754             For example, a line with:
755              
756             guest:ab01FAX.bQRSU:rich:/home/rich/guest-uploads
757              
758             would allow someone to log in as C with password
759             C<123456>. After logging in, the FTP server will assume the identity
760             of the real Unix user C, and will chroot itself into the
761             C directory.
762              
763             (Note that because ordinary PAM/C is bypassed, it would no
764             longer be possible for a user to log in directly with the username
765             C).
766              
767             Crypted passwords can be generated using the following command:
768              
769             perl -e 'print crypt ("123456", "ab"), "\n"'
770              
771             Replace C<123456> with the actual password, and replace C with two
772             random letters from the set C<[a-zA-Z0-9./]>. (The two random letters
773             are the so-called I and are used to make dictionary attacks
774             against the password file more difficult - see C).
775              
776             The userE<39>s home directory comes from the real Unix password file
777             (or nsswitch-configured source) for the real Unix user. You cannot
778             use password files to override this, and so if you are using the
779             optional C parameter, it would make sense to add
780             C into your configuration file.
781              
782             Anonymous logins are B affected by the C
783             option. Use the C flag to control whether anonymous
784             logins are permitted in the C back-end.
785              
786             Password files are not the height of security, but they are included
787             because they can sometimes be useful. In particular if the password
788             file can be read by untrusted users then it is likely that those same
789             users can run the I program and eventually find out your
790             passwords. Some small additional security is offered by having the
791             password file readable only by root (mode 0600). In future we may
792             offer MD5 or salted SHA-1 hashed passwords to make this harder.
793              
794             A curious artifact of the implementation allows you to list the same
795             user with multiple different passwords. Any of the passwords is then
796             valid for logins (and you could even have the user map to different
797             real Unix users in different chrooted directories!)
798              
799             Default: (none)
800              
801             Example: C
802              
803             =item pidfile
804              
805             Location of the file to store the process ID (PID).
806             Applies only to the deamonized process, not the child processes.
807              
808             Default: (no pidfile created)
809              
810             Example: C
811              
812             =item client logging
813              
814             Location to store all client commands sent to the server.
815             The format is the date, the pid, and the command.
816             Following the pid is a "-" if not authenticated the
817             username if the connection is authenticated.
818             Example of before and after authentication:
819              
820             [Wed Feb 21 18:41:32 2001][23818:-]USER rob
821             [Wed Feb 21 18:41:33 2001][23818:-]PASS 123456
822             [Wed Feb 21 18:41:33 2001][23818:*]SYST
823              
824             Default: (no logging)
825              
826             Examples:
827              
828             client logging: /var/log/ftpd.log
829             client logging: /tmp/ftpd_log.$hostname
830              
831             =item xfer logging
832              
833             Location of transfer log. The format was taken from
834             wu-ftpd and ProFTPD xferlog. (See also "man xferlog")
835              
836             Default: (no logging)
837              
838             Examples:
839              
840             xfer logging: /var/log/xferlog
841             xfer logging: /tmp/xferlog.$hostname
842              
843             =item hide passwords in client log
844              
845             If set to 1, then password (C) commands will not be
846             logged in the client log. This option has no effect unless
847             client logging is enabled.
848              
849             Default: 0 (PASS lines will be shown)
850              
851             Example: C
852              
853             =item enable syslog
854              
855             Enable syslogging. If set, then Net::FTPServer will send much
856             information to syslog. On many systems, this information will
857             be available in /var/log/messages or /var/adm/messages. If
858             clear, syslogging is disabled.
859              
860             Default: 1
861              
862             Example: C
863              
864             =item ident timeout
865              
866             Timeout for ident authentication lookups.
867             A timeout (in seconds) must be specified in order to
868             enable ident lookups. There is no way to specify an
869             infinite timeout. Use 0 to disable this feature.
870              
871             Default: 0
872              
873             Example: C
874              
875             =item access control rule
876              
877             =item user access control rule
878              
879             =item retrieve rule
880              
881             =item store rule
882              
883             =item delete rule
884              
885             =item list rule
886              
887             =item mkdir rule
888              
889             =item rename rule
890              
891             =item chdir rule
892              
893             Access control rules.
894              
895             Access control rules are all specified as short snippets of
896             Perl script. This allows the maximum configurability -- you
897             can express just about any rules you want -- but at the price
898             of learning a little Perl.
899              
900             You can use the following variables from the Perl:
901              
902             $hostname Resolved hostname of the client [1]
903             $ip IP address of the client
904             $user User name [2]
905             $class Class of user [2]
906             $user_is_anonymous True if the user is an anonymous user [2]
907             $pathname Full pathname of the file being affected [2]
908             $filename Filename of the file being affected [2,3]
909             $dirname Directory name containing file being affected [2]
910             $type 'A' for ASCII, 'B' for binary, 'L8' for local 8-bit
911             $form Always 'N'
912             $mode Always 'S'
913             $stru Always 'F'
914              
915             Notes:
916              
917             [1] May be undefined, particularly if C is not set.
918              
919             [2] Not available in C since the user has not
920             logged in at this point.
921              
922             [3] Not available for C.
923              
924             Access control rule. The FTP server will not accept any connections
925             from a site unless this rule succeeds. Note that only C<$hostname>
926             and C<$ip> are available to this rule, and unless C
927             and C are both set C<$hostname> may
928             be undefined.
929              
930             Default: 1
931              
932             Examples:
933              
934             (a) Deny connections from *.badguys.com:
935              
936             access control rule: defined ($hostname) && \
937             $hostname !~ /\.badguys\.com$/
938              
939             (b) Only allow connections from local network 10.0.0.0/24:
940              
941             access control rule: $ip =~ /^10\./
942              
943             User access control rule. After the user logs in successfully,
944             this rule is then called to determine if the user may be permitted
945             access.
946              
947             Default: 1
948              
949             Examples:
950              
951             (a) Only allow ``rich'' to log in from 10.x.x.x network:
952              
953             user access control rule: $user ne "rich" || \
954             $ip =~ /^10\./
955              
956             (b) Only allow anonymous users to log in if they come from
957             hosts with resolving hostnames (``resolve addresses'' must
958             also be set):
959              
960             user access control rule: !$user_is_anonymous || \
961             defined ($hostname)
962              
963             (c) Do not allow user ``jeff'' to log in at all:
964              
965             user access control rule: $user ne "jeff"
966              
967             Retrieve rule. This rule controls who may retrieve (download) files.
968              
969             Default: 1
970              
971             Examples:
972              
973             (a) Do not allow anyone to retrieve ``/etc/*'' or any file anywhere
974             called ``.htaccess'':
975              
976             retrieve rule: $dirname !~ m(^/etc/) && $filename ne ".htaccess"
977              
978             (b) Only allow anonymous users to retrieve files from under the
979             ``/pub'' directory.
980              
981             retrieve rule: !$user_is_anonymous || $dirname =~ m(^/pub/)
982              
983             Store rule. This rule controls who may store (upload) files.
984              
985             In the anonymous read-only (ro-ftpd) personality, it is not
986             possible to upload files anyway, so setting this rule has no
987             effect.
988              
989             Default: 1
990              
991             Examples:
992              
993             (a) Only allow users to upload files to the ``/incoming''
994             directory.
995              
996             store rule: $dirname =~ m(^/incoming/)
997              
998             (b) Anonymous users can only upload files to ``/incoming''
999             directory.
1000              
1001             store rule: !$user_is_anonymous || $dirname =~ m(^/incoming/)
1002              
1003             (c) Disable file upload.
1004              
1005             store rule: 0
1006              
1007             Delete rule. This rule controls who may delete files or rmdir directories.
1008              
1009             In the anonymous read-only (ro-ftpd) personality, it is not
1010             possible to delete files anyway, so setting this rule has no
1011             effect.
1012              
1013             Default: 1
1014              
1015             Example: C
1016              
1017             List rule. This rule controls who may list out the contents of a
1018             directory.
1019              
1020             Default: 1
1021              
1022             Example: C
1023              
1024             Mkdir rule. This rule controls who may create a subdirectory.
1025              
1026             In the anonymous read-only (ro-ftpd) personality, it is not
1027             possible to create directories anyway, so setting this rule has
1028             no effect.
1029              
1030             Default: 1
1031              
1032             Example: C
1033              
1034             Rename rule. This rule controls which files or directories can be renamed.
1035              
1036             Default: 1
1037              
1038             Example: C
1039              
1040             Chdir rule. This rule controls which directories are acceptable to a
1041             CWD or CDUP.
1042              
1043             Example: C
1044              
1045             =item chdir message file
1046              
1047             Change directory message file. If set, then the first time (per
1048             session) that a user goes into a directory which contains a file
1049             matching this name, that file will be displayed.
1050              
1051             The file may contain any of the following % escape sequences:
1052              
1053             %C current working directory
1054             %E maintainer's email address (from ``maintainer email''
1055             setting above)
1056             %G time in GMT
1057             %R remote hostname or IP address if ``resolve addresses''
1058             is not set
1059             %L local hostname
1060             %m user's home directory (see ``home directory'' below)
1061             %T local time
1062             %U username given when logging in
1063             %u currently a synonym for %U, but in future will be
1064             determined from RFC931 authentication, like wu-ftpd
1065             %% just an ordinary ``%''
1066              
1067             Default: (none)
1068              
1069             Example: C
1070              
1071             =item allow rename to overwrite
1072              
1073             Allow the rename (RNFR/RNTO) command to overwrite files. If unset,
1074             then we try to test whether the rename command would overwrite a
1075             file and disallow it. However there are some race conditions with
1076             this test.
1077              
1078             Default: 1
1079              
1080             Example: C
1081              
1082             =item allow store to overwrite
1083              
1084             Allow the store commands (STOR/STOU/APPE) to overwrite files. If unset,
1085             then we try to test whether the store command would overwrite a
1086             file and disallow it. However there are some race conditions with
1087             this test.
1088              
1089             Default: 1
1090              
1091             Example: C
1092              
1093             =item alias
1094              
1095             Define an alias C for directory C. For example, the command
1096             C would allow the user to access the
1097             C directory directly just by typing C.
1098              
1099             Aliases only apply to the cd (CWD) command. The C command checks
1100             for directories in the following order:
1101              
1102             foo in the current directory
1103             an alias called foo
1104             foo in each directory in the cdpath (see ``cdpath'' command below)
1105              
1106             You may list an many aliases as you want.
1107              
1108             Alias names cannot contain slashes (/).
1109              
1110             Although alias dirs may start without a slash (/), this is unwise and
1111             itE<39>s better that they always start with a slash (/) char.
1112              
1113             General format: C I>
1114              
1115             =item cdpath
1116              
1117             Define a search path which is used when changing directories. For
1118             example, the command C would allow
1119             the user to access the C directory
1120             directly by just typing C.
1121              
1122             The C command checks for directories in the following order:
1123              
1124             foo in the current directory
1125             an alias called foo (see ``alias'' command above)
1126             foo in each directory in the cdpath
1127              
1128             General format: C [I [I ...]]>
1129              
1130             =item allow site version command
1131              
1132             SITE VERSION command. If set, then the SITE VERSION command reveals
1133             the current Net::FTPServer version string. If unset, then the command
1134             is disabled.
1135              
1136             Default: 1
1137              
1138             Example: C
1139              
1140             =item allow site exec command
1141              
1142             SITE EXEC command. If set, then the SITE EXEC command allows arbitrary
1143             commands to be executed on the server as the current user. If unset,
1144             then this command is disabled. The default is disabled for obvious
1145             security reasons.
1146              
1147             If you do allow SITE EXEC, you may need to increase the per process
1148             memory, processes and files limits above.
1149              
1150             Default: 0
1151              
1152             Example: C
1153              
1154             =item enable archive mode
1155              
1156             Archive mode. If set (the default), then archive mode is
1157             enabled, allowing users to request, say, C and
1158             get a version of C which is gzip-compressed on the
1159             fly. If zero, then this feature is disabled. See the
1160             section ARCHIVE MODE elsewhere in this manual for details.
1161              
1162             Since archive mode is implemented using external commands,
1163             you need to ensure that programs such as C,
1164             C, C, C, etc. are available on
1165             the C<$PATH> (even in the chrooted environment), and you also
1166             need to substantially increase the normal per-process memory,
1167             processes and files limits.
1168              
1169             Default: 1
1170              
1171             Example: C
1172              
1173             =item archive zip temporaries
1174              
1175             Temporary directory for generating ZIP files in archive mode.
1176             In archive mode, when generating ZIP files, the FTP server is
1177             capable of either creating a temporary file on local disk
1178             containing the ZIP contents, or can generate the file completely
1179             in memory. The former method saves memory. The latter method
1180             (only practical on small ZIP files) allows the server to work
1181             more securely and in certain read-only chrooted environments.
1182              
1183             (Unfortunately the ZIP file format itself prevents ZIP files
1184             from being easily created on the fly).
1185              
1186             If not specified in the configuration file, this option
1187             defaults to using C. If there are local users on the
1188             FTP server box, then this can lead to various C races,
1189             so for maximum security you will probably want to change
1190             this.
1191              
1192             If specified, and set to a string, then the string is the
1193             name of a directory which is used for storing temporary zip
1194             files. This directory must be writable, and must exist inside
1195             the chrooted environment (if chroot is being used).
1196              
1197             If specified, but set to "0" or an empty string, then
1198             the server will always generate the ZIP file in memory.
1199              
1200             In any case, if the directory is found at runtime to be
1201             unwritable, then the server falls back to creating ZIP
1202             files in memory.
1203              
1204             Default: C
1205              
1206             Example: C
1207              
1208             Example: C
1209              
1210             =item site command
1211              
1212             Custom SITE commands. Use this command to define custom SITE
1213             commands. Please read the section LOADING CUSTOMIZED SITE
1214             COMMANDS in this manual page for more detailed information.
1215              
1216             The C command has the form:
1217              
1218             C I>
1219              
1220             I is the name of the command (eg. for SITE README you
1221             would set I == C). I is a file containing the
1222             code of the site command in the form of an anonymous Perl
1223             subroutine. The file should have the form:
1224              
1225             sub {
1226             my $self = shift; # The FTPServer object.
1227             my $cmd = shift; # Contains the command itself.
1228             my $rest = shift; # Contains any parameters passed by the user.
1229              
1230             : :
1231             : :
1232              
1233             $self->reply (RESPONSE_CODE, RESPONSE_TEXT);
1234             }
1235              
1236             You may define as many site commands as you want. You may also
1237             override site commands from the current personality here.
1238              
1239             Example:
1240              
1241             site command: quota /usr/local/lib/ftp/quota.pl
1242              
1243             and the file C contains:
1244              
1245             sub {
1246             my $self = shift; # The FTPServer object.
1247             my $cmd = shift; # Contains "QUOTA".
1248             my $rest = shift; # Contains parameters passed by user.
1249              
1250             # ... Some code to compute the user's quota ...
1251              
1252             $self->reply (200, "Your quota is $quota MB.");
1253             }
1254              
1255             The client types C and the server responds with:
1256              
1257             "200 Your quota is 12.5 MB.".
1258              
1259             =item EHost hostnameE ... E/HostE
1260              
1261             EHost hostnameE ... E/HostE encloses
1262             commands which are applicable only to a particular
1263             host. C may be either a fully-qualified
1264             domain name (for IP-less virtual hosts) or an IP
1265             address (for IP-based virtual hosts). You should read
1266             the section VIRTUAL HOSTS in this manual page for
1267             more information on the different types of virtual
1268             hosts and how to set it up in more detail.
1269              
1270             Note also that unless you have set C,
1271             all EHostE sections will be ignored.
1272              
1273             =item enable virtual hosts
1274              
1275             Unless this option is uncommented, virtual hosting is disabled
1276             and the EHostE sections in the configuration file have no effect.
1277              
1278             Default: 0
1279              
1280             Example: C
1281              
1282             =item virtual host multiplex
1283              
1284             IP-less virtual hosts. If you want to enable IP-less virtual
1285             hosts, then you must set up your DNS so that all hosts map
1286             to a single IP address, and place that IP address here. This
1287             is roughly equivalent to the Apache C option.
1288              
1289             IP-less virtual hosting is an experimental feature which
1290             requires changes to clients.
1291              
1292             Default: (none)
1293              
1294             Example: C
1295              
1296             Example EHostE section. Allow the dangerous SITE EXEC command
1297             on local connections. (Note that this is still dangerous).
1298              
1299            
1300             ip: 127.0.0.1
1301             allow site exec command: 1
1302            
1303              
1304             Example EHostE section. This shows you how to do IP-based
1305             virtual hosts. I assume that you have set up your DNS so that
1306             C maps to IP C<1.2.3.4> and C
1307             maps to IP C<1.2.3.5>, and you have set up suitable IP aliasing
1308             in the kernel.
1309              
1310             You do not need the C command if you have configured reverse
1311             DNS correctly AND you trust your local DNS servers.
1312              
1313            
1314             ip: 1.2.3.4
1315             root directory: /home/bob
1316             home directory: /
1317             user access control rule: $user eq "bob"
1318             maintainer email: bob@bob.example.com
1319            
1320              
1321            
1322             ip: 1.2.3.5
1323             root directory: /home/jane
1324             home directory: /
1325             allow anonymous: 1
1326             user access control rule: $user_is_anonymous
1327             maintainer email: jane@jane.example.com
1328            
1329              
1330             These rules set up two virtual hosts called C
1331             and C. The former is located under bob's
1332             home directory and only he is allowed to log in. The latter is
1333             located under jane's home directory and only allows anonymous
1334             access.
1335              
1336             Example EHostE section. This shows you how to do IP-less
1337             virtual hosts. Note that IP-less virtual hosts are a highly
1338             experimental feature, and require the client to support the
1339             HOST command.
1340              
1341             You need to set up your DNS so that both C
1342             and C point to your own IP address.
1343              
1344             virtual host multiplex: 1.2.3.4
1345              
1346            
1347             root directory: /home/bob
1348             home directory: /
1349             user access control rule: $user eq "bob"
1350            
1351              
1352            
1353             root directory: /home/jane
1354             home directory: /
1355             allow anonymous: 1
1356             user access control rule: $user_is_anonymous
1357            
1358              
1359             =item log socket type
1360              
1361             Socket type for contacting syslog. This is the argument to
1362             the C function.
1363              
1364             Default: unix
1365              
1366             Example: C
1367              
1368             =item listen queue
1369              
1370             Length of the listen queue when running in daemon mode.
1371              
1372             Default: 10
1373              
1374             Example: C
1375              
1376             =item tcp window
1377              
1378             Set TCP window. See RFC 2415
1379             I.
1380             This setting only affects the data
1381             socket. ItE<39>s not likely that you will need to or should change
1382             this setting from the system-specific default.
1383              
1384             Default: (system-specific TCP window size)
1385              
1386             Example: C
1387              
1388             =item tcp keepalive
1389              
1390             Set TCP keepalive.
1391              
1392             Default: (system-specific keepalive setting)
1393              
1394             Example: C
1395              
1396             =item command filter
1397              
1398             Command filter. If set, then all commands are checked against
1399             this regular expression before being executed. If a command
1400             doesnE<39>t match the filter, then the command connection is
1401             immediately dropped. This is equivalent to the C
1402             command in ProFTPD. Remember to include C<^...$> around the filter.
1403              
1404             Default: (no filter)
1405              
1406             Example: C
1407              
1408             =item restrict command
1409              
1410             Advanced command filtering. The C directive takes
1411             the form:
1412              
1413             restrict command: "COMMAND" perl code ...
1414              
1415             If the user tries to execute C, then the C is
1416             evaluated first. If it evaluates to true, then the command is allowed
1417             to proceed. Otherwise the server reports an error back to the user and
1418             does not execute the command.
1419              
1420             Note that the C is the FTP protocol command, which is not
1421             necessarily the same as the command which users will type in on their
1422             FTP clients. Please read RFC 959 to see some of the more common FTP
1423             protocol commands.
1424              
1425             The Perl code has the same variables available to it as for access
1426             control rules (eg. C<$user>, C<$class>, C<$ip>, etc.). The code
1427             I alter the global C<$_> variable (which contains the
1428             complete command).
1429              
1430             Default: all commands are allowed by default
1431              
1432             Examples:
1433              
1434             Only allow users in the class C to delete files and
1435             directories:
1436              
1437             restrict command: "DELE" $class eq "nukers"
1438             restrict command: "RMD" $class eq "nukers"
1439              
1440             Only allow staff to use the C command:
1441              
1442             restrict command: "SITE WHO" $class eq "staff"
1443              
1444             Only allow C to run the C command:
1445              
1446             allow site exec command: 1
1447             restrict command: "SITE EXEC" $user eq "rich"
1448              
1449             =item command wait
1450              
1451             Go slow. If set, then the server will sleep for this many seconds
1452             before beginning to process each command. This command would be
1453             a lot more useful if you could apply it only to particular
1454             classes of connection.
1455              
1456             Default: (no wait)
1457              
1458             Example: C
1459              
1460             =item no authentication commands
1461              
1462             The list of commands which a client may issue before they have
1463             authenticated themselves is very limited. Obviously C and
1464             C are allowed (otherwise a user would never be able to log
1465             in!), also C, C, C and C. C is also permitted
1466             (although dubious). Any other commands not on this list will
1467             result in a I<530 Not logged in.> error.
1468              
1469             This list ought to contain at least C, C and C
1470             otherwise the server wonE<39>t be very functional.
1471              
1472             Some commands cannot be added here -- eg. adding C or C
1473             to this list is likely to make the FTP server crash, or else enable
1474             users to read files only available to root. Hence use this with
1475             great care.
1476              
1477             Default: USER PASS QUIT LANG HOST FEAT HELP
1478              
1479             Example: C
1480              
1481             =item EPerlE ... E/PerlE
1482              
1483             Use the EPerlE directive to write Perl code directly
1484             into your configuration file. Here is a simple example:
1485              
1486            
1487             use Sys::Hostname;
1488             $config{'maintainer email'} = "root\@" . hostname ();
1489             $config{port} = 8000 + 21;
1490             $config{debug} = $ENV{FTP_DEBUG} ? 1 : 0;
1491            
1492              
1493             As shown in the example, to set a configuration option called
1494             C, you simply assign to the variable C<$config{foo}>.
1495              
1496             All normal Perl functionality is available to you, including
1497             use of C if you need to run an external Perl script.
1498              
1499             The EPerlE and E/PerlE directives must each appear
1500             on a single line on their own.
1501              
1502             To assign multiple configuration options with the same name,
1503             use an array ref:
1504              
1505            
1506             my @aliases = ( "foo /pub/foo",
1507             "bar /pub/bar",
1508             "baz /pub/baz" );
1509             $config{alias} = \@aliases;
1510            
1511              
1512             You cannot use a EPerlE section within a EHostE
1513             section. Instead, you must simulate it by assigning to the
1514             C<%host_config> variable like this:
1515              
1516            
1517             $host_config{'localhost.localdomain'}{ip} = "127.0.0.1";
1518             $host_config{'localhost.localdomain'}{'allow site exec command'}= 1;
1519            
1520              
1521             The above is equivalent to the following ordinary EHostE
1522             section:
1523              
1524            
1525             ip: 127.0.0.1
1526             allow site exec command: 1
1527            
1528              
1529             You may also assign to the C<$self> variable in order to set
1530             variables directly in the C object itself. This
1531             is pretty hairy, and hence not recommended, but you dig your own
1532             hole if you want. Here is a contrived example:
1533              
1534            
1535             $self->{version_string} = "my FTP server/1.0";
1536            
1537              
1538             A cleaner, but more complex way to do this would be to use
1539             a personality.
1540              
1541             The EPerlE directive is potentially quite powerful.
1542             Here is a good idea that Rob Brown had:
1543              
1544            
1545             my %H;
1546             dbmopen (%H, "/etc/ftpd.db", 0644);
1547             %config = %H;
1548             dbmclose (%H);
1549            
1550              
1551             Notice how this allows you to crunch a possibly very large
1552             configuration file into a hash, for very rapid loading at run time.
1553              
1554             Another useful way to use EPerlE is to set environment
1555             variables (particularly C<$PATH>).
1556              
1557            
1558             $ENV{PATH} = "/usr/local/bin:$ENV{PATH}"
1559            
1560              
1561             HereE<39>s yet another wonderful way to use EPerlE.
1562             Look in C for a list of site commands
1563             and load each one:
1564              
1565            
1566              
1567             my @files = glob "/usr/local/lib/ftp/*.pl";
1568             my @site_commands;
1569              
1570             foreach (@files)
1571             {
1572             push @site_commands, "$1 $_" if /([a-z]+)\.pl/;
1573             }
1574              
1575             $config{'site command'} = \@site_commands;
1576              
1577            
1578              
1579             To force a particular version of Net::FTPServer to be
1580             used, include the following code in your configuration
1581             file:
1582              
1583            
1584             die "requires Net::FTPServer version >= 1.025"
1585             unless $Net::FTPServer::VERSION !~ /\..*\./ &&
1586             $Net::FTPServer::VERSION >= 1.025;
1587            
1588              
1589             =back
1590              
1591             =head2 LOADING CUSTOMIZED SITE COMMANDS
1592              
1593             It is very simple to write custom SITE commands. These
1594             commands are available to users when they type "SITE XYZ"
1595             in a command line FTP client or when they define a custom
1596             SITE command in their graphical FTP client.
1597              
1598             SITE commands are unregulated by RFCs. You may define any commands and
1599             give them any names and any function you wish. However, over time
1600             various standard SITE commands have been recognized and implemented
1601             in many FTP servers. C also implements these. They
1602             are:
1603              
1604             SITE VERSION Display the server software version.
1605             SITE EXEC Execute a shell command on the server (in
1606             C this is disabled by default!)
1607             SITE ALIAS Display chdir aliases.
1608             SITE CDPATH Display chdir paths.
1609             SITE CHECKMETHOD Implement checksums.
1610             SITE CHECKSUM
1611             SITE IDLE Get or set the idle timeout.
1612             SITE SYNC Synchronize hard disks.
1613              
1614             The following commands are found in C, but not currently
1615             implemented by C: SITE CHMOD, SITE GPASS, SITE GROUP,
1616             SITE GROUPS, SITE INDEX, SITE MINFO, SITE NEWER, SITE UMASK.
1617              
1618             So when you are choosing a name for a SITE command, it is probably
1619             best not to choose one of the above names, unless you are specifically
1620             implementing or overriding that command.
1621              
1622             Custom SITE commands have to be written in Perl. However, there
1623             is very little you need to understand in order to write these
1624             commands -- you will only need a basic knowledge of Perl scripting.
1625              
1626             As our first example, we will implement a C command.
1627             This command just prints out some standard information.
1628              
1629             Firstly create a file called C (you
1630             may choose a different path if you want). The file should contain:
1631              
1632             sub {
1633             my $self = shift;
1634             my $cmd = shift;
1635             my $rest = shift;
1636              
1637             $self->reply (200,
1638             "This is the README file for mysite.example.com.",
1639             "Mirrors are contained in /pub/mirrors directory.",
1640             " : : : : :",
1641             "End of the README file.");
1642             }
1643              
1644             Edit C and add the following command:
1645              
1646             site command: readme /usr/local/lib/site_readme.pl
1647              
1648             and restart the FTP server (check your system log [/var/log/messages]
1649             for any syntax errors or other problems). Here is an example of a
1650             user running the SITE README command:
1651              
1652             ftp> quote help site
1653             214-The following commands are recognized:
1654             214- ALIAS CHECKMETHOD EXEC README
1655             214- CDPATH CHECKSUM IDLE VERSION
1656             214 You can also use HELP to list general commands.
1657             ftp> site readme
1658             200-This is the README file for mysite.example.com.
1659             200-Mirrors are contained in /pub/mirrors directory.
1660             200- : : : : :
1661             200 End of the README file.
1662              
1663             Our second example demonstrates how to use parameters
1664             (the C<$rest> argument). This is the C command.
1665              
1666             sub {
1667             my $self = shift;
1668             my $cmd = shift;
1669             my $rest = shift;
1670              
1671             # Split the parameters up.
1672             my @params = split /\s+/, $rest;
1673              
1674             # Quote each parameter.
1675             my $reply = join ", ", map { "'$_'" } @params;
1676              
1677             $self->reply (200, "You said: $reply");
1678             }
1679              
1680             Here is the C command in use:
1681              
1682             ftp> quote help site
1683             214-The following commands are recognized:
1684             214- ALIAS CHECKMETHOD ECHO IDLE
1685             214- CDPATH CHECKSUM EXEC VERSION
1686             214 You can also use HELP to list general commands.
1687             ftp> site echo hello how are you?
1688             200 You said: 'hello', 'how', 'are', 'you?'
1689              
1690             Our third example is more complex and shows how to interact
1691             with the virtual filesystem (VFS). The C command
1692             will be used to list text files directly (the user normally
1693             has to download the file and view it locally). Hence
1694             C should print the contents of the
1695             C file in the local directory (if it exists).
1696              
1697             All file accesses B be done through the VFS, not
1698             by directly accessing the disk. If you follow this convention
1699             then your commands will be secure and will work correctly
1700             with different back-end personalities (in particular when
1701             ``files'' are really blobs in a relational database).
1702              
1703             sub {
1704             my $self = shift;
1705             my $cmd = shift;
1706             my $rest = shift;
1707              
1708             # Get the file handle.
1709             my ($dirh, $fileh, $filename) = $self->_get ($rest);
1710              
1711             # File doesn't exist or not accessible. Return an error.
1712             unless ($fileh)
1713             {
1714             $self->reply (550, "File or directory not found.");
1715             return;
1716             }
1717              
1718             # Check it's a simple file.
1719             my ($mode) = $fileh->status;
1720              
1721             unless ($mode eq "f")
1722             {
1723             $self->reply (550,
1724             "SITE SHOW command is only supported on plain files.");
1725             return;
1726             }
1727              
1728             # Try to open the file.
1729             my $file = $fileh->open ("r");
1730              
1731             unless ($file)
1732             {
1733             $self->reply (550, "File or directory not found.");
1734             return;
1735             }
1736              
1737             # Copy data into memory.
1738             my @lines = ();
1739              
1740             while (defined ($_ = $file->getline))
1741             {
1742             # Remove any native line endings.
1743             s/[\n\r]+$//;
1744              
1745             push @lines, $_;
1746             }
1747              
1748             # Close the file handle.
1749             unless ($file->close)
1750             {
1751             $self->reply (550, "Close failed: ".$self->system_error_hook());
1752             return;
1753             }
1754              
1755             # Send the file back to the user.
1756             $self->reply (200, "File $filename:", @lines, "End of file.");
1757             }
1758              
1759             This code is not quite complete. A better implementation would
1760             also check the "retrieve rule" (so that people couldnE<39>t
1761             use C in order to get around access control limitations
1762             which the server administrator has put in place). It would also
1763             check the file more closely to make sure it was a text file and
1764             would refuse to list very large files.
1765              
1766             Here is an example (abbreviated) of a user using the
1767             C command:
1768              
1769             ftp> site show README
1770             200-File README:
1771             200-README
1772             200-======
1773             200-
1774             200-Biblio@Tech Net::FTPServer - A full-featured, secure, extensible
1775             [...]
1776             200-Copyright (C) 2000-2003 Richard Jones and other contributors.
1777             200 End of file.
1778              
1779             =head2 STANDARD PERSONALITIES
1780              
1781             Currently C is supplied with three standard
1782             personalities. These are:
1783              
1784             Full The complete read/write anonymous/authenticated FTP
1785             server which serves files from a standard Unix filesystem.
1786              
1787             RO A small read-only anonymous-only FTP server similar
1788             in functionality to Dan Bernstein's publicfile
1789             program.
1790              
1791             DBeg1 An example FTP server which serves files to a PostgreSQL
1792             database. This supports files and hierarchical
1793             directories, multiple users (but not file permissions)
1794             and file upload.
1795              
1796             The standard B personality will not be explained here.
1797              
1798             The B personality is the Full personality with all code
1799             related to writing files, creating directories, deleting, etc.
1800             removed. The RO personality also only permits anonymous
1801             logins and does not contain any code to do ordinary
1802             authentication. It is therefore safe to use the RO
1803             personality where you are only interested in serving
1804             files to anonymous users and do not want to worry about
1805             crackers discovering a way to trick the FTP server into
1806             writing over a file.
1807              
1808             The B personality is a complete read/write
1809             FTP server which stores files as BLOBs (Binary Large
1810             OBjects) in a PostgreSQL relational database. The
1811             personality supports file download and upload and
1812             contains code to authenticate users against a C
1813             table in the database (database ``users'' are thus
1814             completely unrelated to real Unix users). The
1815             B is intended only as an example. It does
1816             not support advanced features such as file
1817             permissions and quotas. As part of the schoolmaster.net
1818             project Bibliotech Ltd. have developed an even more
1819             advanced database personality which supports users,
1820             groups, access control lists, quotas, recursive
1821             moves and copies and many other features. However this
1822             database personality is not available as source.
1823              
1824             To use the DBeg1 personality you must first run a
1825             PostgreSQL server (version 6.4 or above) and ensure
1826             that you have access to it from your local user account.
1827             Use the C, C and C
1828             commands to create the appropriate user account and
1829             database (please consult the PostgreSQL administrators
1830             manual for further information about this -- I do
1831             not answer questions about basic PostgreSQL knowledge).
1832              
1833             Here is my correctly set up PostgreSQL server, accessed
1834             from my local user account ``rich'':
1835              
1836             cruiser:~$ psql
1837             Welcome to the POSTGRESQL interactive sql monitor:
1838             Please read the file COPYRIGHT for copyright terms of POSTGRESQL
1839              
1840             type \? for help on slash commands
1841             type \q to quit
1842             type \g or terminate with semicolon to execute query
1843             You are currently connected to the database: rich
1844              
1845             rich=> \d
1846             Couldn't find any tables, sequences or indices!
1847              
1848             You will also need the following Perl modules installed:
1849             DBI, DBD::Pg.
1850              
1851             Now you will need to create a database called ``ftp'' and
1852             populate it with data. This is how to do this:
1853              
1854             createdb ftp
1855             psql ftp < doc/eg1.sql
1856              
1857             Check that no ERRORs are reported by PostgreSQL.
1858              
1859             You should now be able to start the FTP server by running
1860             the following command (I as root):
1861              
1862             ./dbeg1-ftpd -S -p 2000 -C ftpd.conf
1863              
1864             If the FTP server doesnE<39>t start correctly, you should
1865             check the system log file [/var/log/messages].
1866              
1867             Connect to the FTP server as follows:
1868              
1869             ftp localhost 2000
1870              
1871             Log in as either rich/123456 or dan/123456 and then try
1872             to move around, upload and download files, create and
1873             delete directories, etc.
1874              
1875             =head2 SUBCLASSING THE Net::FTPServer CLASSES
1876              
1877             By subclassing C, C and/or
1878             C you can create custom
1879             personalities for the FTP server.
1880              
1881             Typically by overriding the hooks in the C class
1882             you can change the basic behaviour of the FTP server - turning
1883             it into an anonymous read-only server, for example.
1884              
1885             By overriding the hooks in C and
1886             C you can create virtual filesystems:
1887             serving files into and out of a database, for example.
1888              
1889             The current manual page contains information about the
1890             hooks in C which may be overridden.
1891              
1892             See C for information about
1893             the methods in C which may be
1894             overridden.
1895              
1896             See C for information about
1897             the methods in C which may be
1898             overridden.
1899              
1900             The most reasonable way to create your own personality is
1901             to extend one of the existing personalities. Choose the
1902             one which most closely matches the personality that you
1903             want to create. For example, suppose that you want to create
1904             another database personality. A good place to start would
1905             be by copying C to a new
1906             directory C (for example). Now
1907             edit these files and substitute "MyDB" for "DBeg1". Then
1908             examine each subroutine in these files and modify them,
1909             consulting the appropriate manual page if you need to.
1910              
1911             =head2 VIRTUAL HOSTS
1912              
1913             C is capable of hosting multiple FTP sites on
1914             a single machine. Because of the nature of the FTP protocol,
1915             virtual hosting is almost always done by allocating a single
1916             separate IP address per FTP site. However, C
1917             also supports an experimental IP-less virtual hosting
1918             system, although this requires modifications to the client.
1919              
1920             Normal (IP-based) virtual hosting is carried out as follows:
1921              
1922             * For each FTP site, allocate a separate IP address.
1923             * Configure IP aliasing on your normal interface so that
1924             the single physical interface responds to multiple
1925             virtual IP addresses.
1926             * Add entries (A records) in DNS mapping each site's
1927             name to a separate IP address.
1928             * Add reverse entries (PTR records) in DNS mapping each
1929             IP address back to the site hostname. It is important
1930             that both forward and reverse DNS is set up correctly,
1931             else virtual hosting may not work.
1932             * In /etc/ftpd.conf you will need to add a virtual host
1933             section for each site like this:
1934              
1935            
1936              
1937             ip: 1.2.3.4
1938             ... any specific configuration options for this site ...
1939              
1940            
1941              
1942             You don't in fact need the "ip:" part assuming that
1943             your forward and reverse DNS are set up correctly.
1944             * If you want to specify a lot of external sites, or
1945             generate the configuration file automatically from a
1946             database or a script, you may find the
1947             syntax useful.
1948              
1949             There are examples in C. Here is how
1950             IP-based virtual hosting works:
1951              
1952             * The server starts by listening on all interfaces.
1953             * A connection arrives at one of the IP addresses and a
1954             process is forked off.
1955             * The child process finds out which interface the
1956             client connected to and reverses the name.
1957             * If:
1958             the IP address matches one of the "ip:" declarations
1959             in any of the "Host" sections,
1960             or:
1961             there is a reversal for the name, and the name
1962             matches one of the "Host" sections in the configuration
1963             file,
1964             then:
1965             configuration options are read from that
1966             section of the file and override any global configuration
1967             options specified elsewhere in the file.
1968             * Otherwise, the global configuration options only
1969             are used.
1970              
1971             IP-less virtual hosting is an experimental feature. It
1972             requires the client to send a C command very early
1973             on in the command stream -- before C and C. The
1974             C command explicitly gives the hostname that the
1975             FTP client is attempting to connect to, and so allows
1976             many FTP sites to be multiplexed onto a single IP
1977             address. At the present time, I am not aware of I
1978             FTP clients which implement the C command, although
1979             they will undoubtedly become more common in future.
1980              
1981             This is how to set up IP-less virtual hosting:
1982              
1983             * Add entries (A or CNAME records) in DNS mapping the
1984             name of each site to a single IP address.
1985             * In /etc/ftpd.conf you will need to list the same single
1986             IP address to which all your sites map:
1987              
1988             virtual host multiplex: 1.2.3.4
1989              
1990             * In /etc/ftpd.conf you will need to add a virtual host
1991             section for each site like this:
1992              
1993            
1994              
1995             ... any specific configuration options for this site ...
1996              
1997            
1998              
1999             Here is how IP-less virtual hosting works:
2000              
2001             * The server starts by listening on one interface.
2002             * A connection arrives at the IP address and a
2003             process is forked off.
2004             * The IP address matches "virtual host multiplex"
2005             and so no IP-based virtual host processing is done.
2006             * One of the first commands that the client sends is
2007             "HOST" followed by the hostname of the site.
2008             * If there is a matching "Host" section in the
2009             configuration file, then configuration options are
2010             read from that section of the file and override any
2011             global configuration options specified elsewhere in
2012             the file.
2013             * If there is no matching "Host" section then the
2014             global configuration options alone are used.
2015              
2016             The client is not permitted to issue the C command
2017             more than once, and is not permitted to issue it after
2018             login.
2019              
2020             =head2 VIRTUAL HOSTING AND SECURITY
2021              
2022             Only certain configuration options are available inside
2023             the EHostE sections of the configuration file.
2024             Generally speaking, the only configuration options you
2025             can put here are ones which take effect after the
2026             site name has been determined -- hence "allow anonymous"
2027             is OK (since itE<39>s an option which is parsed after
2028             determining the site name and during log in), but
2029             "port" is not (since it is parsed long before any
2030             clients ever connect).
2031              
2032             Make sure your default global configuration is
2033             secure. If you are using IP-less virtual hosting,
2034             this is particularly important, since if the client
2035             never sends a C command, the client gets
2036             the global configuration. Even with IP-based virtual
2037             hosting it may be possible for clients to sometimes
2038             get the global configuration, for example if your
2039             local name server fails.
2040              
2041             IP-based virtual hosting always takes precedence
2042             above IP-less virtual hosting.
2043              
2044             With IP-less virtual hosting, access control cannot
2045             be performed on a per-site basis. This is because the
2046             client has to issue commands (ie. the C command
2047             at least) before the site name is known to the server.
2048             However you may still have a global "access control rule".
2049              
2050             =head2 ARCHIVE MODE
2051              
2052             Beginning with version 1.100, C is able
2053             to generate certain types of compressed and archived files
2054             on the fly. In practice what this means is that if a user
2055             requests, say, C and this file does not actually
2056             exist (but C I exist), then the server will
2057             dynamically generate a gzip-compressed version of C
2058             for the user. This also works on directories, so that a
2059             user might request C which does not exist
2060             (but directory C I exist), and the server tars
2061             up and compresses the entire contents of C and
2062             presents that back to the user.
2063              
2064             Archive mode is enabled by default. However, it will
2065             not work unless you substantially increase the per-process
2066             memory, processes and files limits. The reason for this
2067             is that archive mode works by forking external programs
2068             such as C to perform the compression. For the same
2069             reason you may also need to ensure that at least
2070             C, C, C and C programs
2071             are available on the current C<$PATH>, particularly if
2072             you are using a chrooted environment.
2073              
2074             To disable archive mode put C
2075             into the configuration file.
2076              
2077             The following file extensions are supported:
2078              
2079             .gz GZip compressed. Requires gzip program on PATH.
2080             .Z Unix compressed. Requires compress program on PATH.
2081             .bz2 BZip2 compressed. Requires bzip2 program on PATH.
2082             .uue UU-encoded. Requires uuencode program on PATH.
2083             .tar Tar archive. Requires Perl Archive::Tar module.
2084             .zip DOS ZIP archive. Requires Perl Archive::Zip module.
2085             .list Return a list of all the files in this directory.
2086              
2087             File extensions may be combined. Hence C<.tar.gz>,
2088             C<.tar.bz2> and even C<.tar.gz.uue> will all work
2089             as you expect.
2090              
2091             Archive mode is, of course, extensible. It is particularly
2092             simple to add another compression / filter format. In
2093             your personality (or in a EPerlE section in the configuration
2094             file) you need to add another key to the C
2095             hash.
2096              
2097             $ftps->{archive_filters}{".foo"} = &_foo_filter;
2098              
2099             The value of this key should be a function as defined below:
2100              
2101             \%filter = _foo_filter ($ftps, $sock);
2102              
2103             The filter should return a hash reference (undef if it fails).
2104             The hash should contain the following keys:
2105              
2106             sock Newly opened socket.
2107             pid PID of filter program.
2108              
2109             The C<_foo_filter> function takes the existing socket and
2110             filters it, providing a new socket which the FTP server will
2111             write to (for the data connection back to the client). If
2112             your filter is a Unix program, then the simplest thing is
2113             just to define C<_foo_filter> as:
2114              
2115             sub _foo_filter
2116             {
2117             return $_[0]->archive_filter_external ($_[1], "foo" [, args ...]);
2118             }
2119              
2120             The C function takes care of the
2121             tricky bits for you.
2122              
2123             Adding new I (akin to the existing tar and ZIP)
2124             is more tricky. I suggest you look closely at the code and
2125             consult the author for more information.
2126              
2127             =head1 METHODS
2128              
2129             =cut
2130              
2131             package Net::FTPServer;
2132              
2133 75     75   47383 use 5.005;
  75         242  
2134              
2135 75     75   339 use strict;
  75         137  
  75         1713  
2136              
2137 75     75   343 use vars qw($VERSION $RELEASE);
  75         146  
  75         3845  
2138              
2139             $VERSION = '1.125';
2140             $RELEASE = 1;
2141              
2142             # Non-optional modules.
2143 75     75   396 use Config;
  75         249  
  75         2812  
2144 75     75   32977 use Getopt::Long qw(GetOptions);
  75         736382  
  75         373  
2145 75     75   25776 use Sys::Hostname;
  75         51365  
  75         3371  
2146 75     75   23473 use Socket;
  75         195907  
  75         27194  
2147 75     75   804 use FileHandle;
  75         6952  
  75         486  
2148 75     75   36538 use IO::Socket;
  75         553582  
  75         300  
2149 75     75   46451 use IO::File;
  75         164  
  75         12986  
2150 75     75   19897 use IO::Select;
  75         93870  
  75         3397  
2151 75     75   21378 use IO::Scalar;
  75         214925  
  75         2866  
2152 75     75   511 use IO::Seekable;
  75         166  
  75         4412  
2153 75     75   18188 use IPC::Open2;
  75         191601  
  75         3602  
2154 75     75   474 use Carp;
  75         157  
  75         2974  
2155 75     75   17012 use Carp::Heavy ;
  75         9132  
  75         2258  
2156 75     75   676 use POSIX qw(setsid dup dup2 ceil strftime WNOHANG);
  75         5040  
  75         554  
2157 75     75   8875 use Fcntl qw(F_SETOWN F_SETFD FD_CLOEXEC);
  75         143  
  75         4770  
2158 75     75   406 use Errno qw(EADDRINUSE) ;
  75         140  
  75         6653  
2159              
2160 75     75   20044 use Net::FTPServer::FileHandle;
  75         160  
  75         1948  
2161 75     75   18936 use Net::FTPServer::DirHandle;
  75         159  
  75         2694  
2162              
2163             # We require this to suppress warning messages from going to the client
2164             # when it starts up, eg. Constant subroutine __need___va_list undefined ...
2165             # (Thanks to Rob Brown for this fix.)
2166              
2167             BEGIN {
2168 75     75   563 local $^W = 0;
2169 75         25205 require Sys::Syslog;
2170             }
2171              
2172             # The following modules are optional, and therefore we need
2173             # to eval the require/use statements. Before using the features
2174             # of an optional module, make sure it exists first by checking
2175             # ``exists $INC{"Module/Name.pm"}'' (see below for examples).
2176             #eval "use Archive::Tar;";
2177 75     75   6628 eval "use Archive::Zip;";
  0         0  
  0         0  
2178 75     75   23194 eval "use BSD::Resource;";
  75         201912  
  75         303  
2179 75     75   475 eval "use Digest::MD5;";
  75         141  
  75         1687  
2180 75     75   4901 eval "use File::Sync;";
  0         0  
  0         0  
2181              
2182             # Global variables and constants.
2183 75         9745 use vars qw(@_default_commands
2184             @_default_site_commands
2185             @_supported_mlst_facts
2186 75     75   484556 $_default_timeout);
  75         169  
2187              
2188             @_default_commands
2189             = (
2190             # Standard commands from RFC 959.
2191             "USER", "PASS", "ACCT", "CWD", "CDUP", "SMNT",
2192             "REIN", "QUIT", "PORT", "PASV", "TYPE", "STRU",
2193             "MODE", "RETR", "STOR", "STOU", "APPE", "ALLO",
2194             "REST", "RNFR", "RNTO", "ABOR", "DELE", "RMD",
2195             "MKD", "PWD", "LIST", "NLST", "SITE", "SYST",
2196             "STAT", "HELP", "NOOP",
2197             # RFC 1123 section 4.1.3.1 recommends implementing these.
2198             "XMKD", "XRMD", "XPWD", "XCUP", "XCWD",
2199             # From RFC 2389.
2200             "FEAT", "OPTS",
2201             # From ftpexts Internet Draft.
2202             "SIZE", "MDTM", "MLST", "MLSD",
2203             # Mail handling commands from obsolete RFC 765.
2204             "MLFL", "MAIL", "MSND", "MSOM", "MSAM", "MRSQ",
2205             "MRCP",
2206             # I18N support from RFC 2640.
2207             "LANG",
2208             # NcFTP sends the CLNT command, I know not from what RFC.
2209             "CLNT",
2210             # Experimental IP-less virtual hosting.
2211             "HOST",
2212             );
2213              
2214             @_default_site_commands
2215             = (
2216             # Common extensions.
2217             "EXEC", "VERSION",
2218             # Wu-FTPD compatible extensions.
2219             "ALIAS", "CDPATH", "CHECKMETHOD", "CHECKSUM",
2220             "IDLE",
2221             # Net::FTPServer compatible extensions.
2222             "SYNC", "ARCHIVE",
2223             );
2224              
2225             @_supported_mlst_facts
2226             = (
2227             "TYPE", "SIZE", "MODIFY", "PERM", "UNIX.MODE"
2228             );
2229              
2230             $_default_timeout = 900;
2231              
2232             # Allocate and initialize signal flags
2233 75     75   433 use vars qw($GOT_SIGURG $GOT_SIGCHLD $GOT_SIGHUP $GOT_SIGTERM);
  75         147  
  75         1177055  
2234             $GOT_SIGURG = 0;
2235             $GOT_SIGCHLD = 0;
2236             $GOT_SIGHUP = 0;
2237             $GOT_SIGTERM = 0;
2238              
2239             =pod
2240              
2241             =over 4
2242              
2243             =item Net::FTPServer->run ([\@ARGV]);
2244              
2245             This is the main entry point into the FTP server. It starts the
2246             FTP server running. This function never normally returns.
2247              
2248             If no arguments are given, then command line arguments are taken
2249             from the global C<@ARGV> array.
2250              
2251             =cut
2252              
2253             sub run
2254             {
2255 41     41 1 52836 my $class = shift;
2256 41   50     888 my $args = shift || [@ARGV];
2257              
2258             # Clean up the environment to allow tainting to work.
2259 41         1313 $ENV{PATH} = "/usr/bin:/bin";
2260 41         1228 $ENV{SHELL} = "/bin/sh";
2261 41         1715 delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
2262              
2263             # Create Net::FTPServer object.
2264 41         609 my $self = {};
2265 41         346 bless $self, $class;
2266              
2267             # Construct version string.
2268             $self->{version_string}
2269 41         1077 = "Net::FTPServer/" .
2270             $Net::FTPServer::VERSION . "-" .
2271             $Net::FTPServer::RELEASE;
2272              
2273             # Save the hostname.
2274 41         1100 $self->{hostname} = hostname;
2275 41 50       3088 $self->{hostname} = $1 if $self->{hostname} =~ /^([\w\-\.]+)$/;
2276              
2277             # Construct a table of commands to subroutines.
2278 41         303 $self->{command_table} = {};
2279 41         600 foreach (@_default_commands) {
2280 2214         4457 my $subname = "_${_}_command";
2281 2214         13441 $self->{command_table}{$_} = \&$subname;
2282             }
2283              
2284             # Construct a list of SITE commands.
2285 41         193 $self->{site_command_table} = {};
2286 41         195 foreach (@_default_site_commands) {
2287 369         796 my $subname = "_SITE_${_}_command";
2288 369         1886 $self->{site_command_table}{$_} = \&$subname;
2289             }
2290              
2291             # Construct a list of supported features (for FEAT command).
2292             $self->{features} = {
2293             SIZE => undef,
2294             REST => "STREAM",
2295             MDTM => undef,
2296             TVFS => undef,
2297             UTF8 => undef,
2298             MLST => join ("",
2299 41         264 map { "$_*;" } @_supported_mlst_facts),
  205         992  
2300             LANG => "EN*",
2301             HOST => undef,
2302             };
2303              
2304             # Construct a list of supported options (for OPTS command).
2305             $self->{options} = {
2306 41         342 MLST => \&_OPTS_MLST_command,
2307             };
2308              
2309 41         614 $self->pre_configuration_hook;
2310              
2311             # Global configuration.
2312 41         295 $self->{debug} = 0;
2313 41         250 $self->{_config_file} = "/etc/ftpd.conf";
2314              
2315 41         890 $self->options_hook ($args);
2316 41         738 $self->_get_configuration ($args);
2317              
2318 41         536 $self->post_configuration_hook;
2319              
2320             # Initialize Max Clients Settings
2321             $self->{_max_clients} =
2322 41   50     541 $self->config ("max clients") || 255;
2323             $self->{_max_clients_message} =
2324 41   50     221 $self->config ("max clients message") ||
2325             "Maximum connections reached";
2326              
2327             # Open syslog.
2328             $self->{_enable_syslog} =
2329             (!defined $self->config ("enable syslog") ||
2330             $self->config ("enable syslog")) &&
2331 41   33     182 !$self->{_test_mode};
2332              
2333 41 50       184 if ($self->{_enable_syslog})
2334             {
2335 0 0       0 if (defined $self->config ("log socket type")) {
2336 0         0 Sys::Syslog::setlogsock $self->config ("log socket type")
2337             } else {
2338 0         0 Sys::Syslog::setlogsock "unix";
2339             }
2340              
2341 0         0 Sys::Syslog::openlog "ftpd", "pid", "daemon";
2342             }
2343              
2344             # Handle error and warning messages. If error log is set (which
2345             # is highly recommended BTW), these are appended directly to
2346             # that file. If error log is not set, then we use a hack which
2347             # directs those messages to syslog.
2348              
2349 41 50       353 if (defined $self->config ("error log"))
2350             {
2351 0         0 $self->_open_error_log ;
2352              
2353             $SIG{__DIE__} = sub {
2354 0     0   0 $self->log ("err", $_[0]);
2355 0         0 confess $_[0];
2356 0         0 };
2357             }
2358             else
2359             {
2360             # Set up a hook for warn and die so that these cause messages to
2361             # be echoed to the syslog.
2362             $SIG{__WARN__} = sub {
2363 0     0   0 $self->log ("warning", $_[0]);
2364 0         0 warn $_[0];
2365 41         1592 };
2366             $SIG{__DIE__} = sub {
2367 0     0   0 $self->log ("err", $_[0]);
2368 0         0 confess $_[0];
2369 41         653 };
2370             }
2371              
2372             # Just set a flag in order to be "signal safe"
2373 41     2   2272 $SIG{URG} = sub { $GOT_SIGURG = 1; };
  2         146  
2374 41     0   559 $SIG{CHLD} = sub { $GOT_SIGCHLD = 1; };
  0         0  
2375 41     0   429 $SIG{HUP} = sub { $GOT_SIGHUP = 1; };
  0         0  
2376 41     0   408 $SIG{TERM} = sub { $GOT_SIGTERM = 1; };
  0         0  
2377              
2378             # The following signal handlers can be handled by Perl, since
2379             # all they are going to do is exit anyway.
2380             $SIG{PIPE} = sub {
2381 0 0   0   0 $self->log ("info", "client closed connection abruptly") if $self;
2382 0         0 exit;
2383 41         380 };
2384             $SIG{INT} = sub {
2385 0     0   0 $self->log ("info", "exiting on keyboard INT signal");
2386 0         0 exit;
2387 41         390 };
2388             $SIG{QUIT} = sub {
2389 0     0   0 $self->log ("info", "exiting on keyboard QUIT signal");
2390 0         0 exit;
2391 41         380 };
2392             $SIG{ALRM} = sub {
2393 1     1   36 $self->log ("info", "exiting on ALRM signal");
2394 1         18 print "421 Server closed the connection after idle timeout.\r\n";
2395 1         9 $self->_log_line ("[TIMED OUT!]");
2396 1         105 exit;
2397 41         432 };
2398              
2399             # Setup Client Logging.
2400 41         678 $self->_open_client_log ;
2401              
2402             # Setup xfer Logging.
2403 41         410 $self->_open_xfer_log ;
2404              
2405             # Convert FTP Data port service name to port number, if necessary.
2406 41 50       210 if (my $ftpdata = $self->config ("ftp data port"))
2407             {
2408 0 0       0 my $ftp_data_port =
2409             $ftpdata =~ /^\d+$/
2410             ? $ftpdata
2411             : scalar (getservbyname ($ftpdata, 'tcp'));
2412 0 0       0 die "Unable to locate '$ftpdata' service"
2413             unless defined $ftp_data_port;
2414 0         0 $self->{ftp_data_port} = $ftp_data_port;
2415             }
2416              
2417             # Load customized SITE commands.
2418 41         175 my @custom_site_commands = $self->config ("site command");
2419 41         148 foreach (@custom_site_commands)
2420             {
2421 0         0 my ($cmdname, $filename) = split /\s+/, $_;
2422 0         0 my $sub = do $filename;
2423 0 0       0 if ($sub)
2424             {
2425 0 0       0 if (ref $sub eq "CODE") {
2426 0         0 $self->{site_command_table}{uc $cmdname} = $sub;
2427             } else {
2428 0         0 $self->log ("err", "site command: $filename: must return an anonymous subroutine when evaluated (skipping)");
2429             }
2430             }
2431             else
2432             {
2433 0 0       0 if ($!) {
2434 0         0 $self->log ("err", "site command: $filename: $! (ignored)")
2435             } else {
2436 0         0 $self->log ("err", "site command: $filename: $@ (ignored)")
2437             }
2438             }
2439             }
2440              
2441 41         182 my $daemon_mode = $self->config ("daemon mode");
2442 41         151 my $run_in_background = $self->config ("run in background");
2443              
2444             # Display start-up string in syslog.
2445             $self->log ("info",
2446 41 50       374 $self->{version_string} . " running" .
    50          
    100          
2447             ($daemon_mode ? " daemon" : "") .
2448             ($run_in_background ? " background" : "") .
2449             ($self->config ("port") ? " on port " . $self->config ("port")
2450             : ""));
2451              
2452             # Daemon mode?
2453 41 50       148 if ($daemon_mode)
2454             {
2455             # Fork into the background?
2456 0 0       0 $self->_fork_into_background if $run_in_background;
2457              
2458 0         0 $self->_save_pid;
2459              
2460             # Run as a daemon.
2461 0         0 $self->_be_daemon;
2462             }
2463              
2464 41         222 $| = 1;
2465              
2466 41 50       278 $self->log ("info", "in post accept stage") if $self->{debug};
2467              
2468             # Hook just after accepting the connection.
2469 41         347 $self->post_accept_hook;
2470              
2471             # Get the sockname of the socket so we know which interface
2472             # the client is bound to.
2473 41         129 my ($sockname, $sockport, $sockaddr, $sockaddrstring);
2474              
2475 41 50       187 unless ($self->{_test_mode})
2476             {
2477 0 0       0 $self->log ("info", "get socket name") if $self->{debug};
2478              
2479 0         0 $sockname = getsockname STDIN;
2480 0 0       0 if (!defined $sockname)
2481             {
2482 0         0 $self->reply(500, "inet mode requires a socket - use '$0 -S' for standalone.");
2483 0         0 exit;
2484             }
2485 0         0 ($sockport, $sockaddr) = unpack_sockaddr_in ($sockname);
2486 0         0 $sockaddrstring = inet_ntoa ($sockaddr);
2487              
2488             # Added 21 Feb 2001 by Rob Brown
2489             # If MSG_OOB data arrives on STDIN send it inline and trigger SIGURG
2490 0 0       0 setsockopt (STDIN, SOL_SOCKET, SO_OOBINLINE, pack ("l", 1))
2491             or warn "setsockopt: SO_OOBINLINE: $!";
2492              
2493             # Note by RWMJ: The following code always generates an error, so
2494             # I have commented it out for the present.
2495             #my $pid = pack ("l", $$);
2496             #fcntl (STDIN, F_SETOWN, $pid)
2497             # or warn "fcntl: F_SETOWN $$: $!";
2498             }
2499              
2500             # Virtual hosts.
2501 41         158 my $sitename;
2502              
2503 41 50       172 if ($self->config ("enable virtual hosts"))
2504             {
2505 0 0       0 $self->log ("info", "virtual host configuration") if $self->{debug};
2506              
2507 0         0 my $virtual_host_multiplex = $self->config ("virtual host multiplex");
2508              
2509             # IP-based virtual hosting?
2510 0 0 0     0 unless ($virtual_host_multiplex &&
2511             $virtual_host_multiplex eq $sockaddrstring)
2512             {
2513             # Look for a matching "ip:" configuration option in
2514             # a section.
2515 0         0 $sitename = $self->ip_host_config ($sockaddrstring);
2516              
2517 0 0       0 unless ($sitename)
2518             {
2519             # Try reversing the IP address in DNS instead.
2520 0         0 $sitename = gethostbyaddr ($sockaddr, AF_INET);
2521             }
2522              
2523 0 0       0 if ($self->{debug})
2524             {
2525 0 0       0 if ($sitename)
2526             {
2527 0         0 $self->log ("info",
2528             "IP-based virtual hosts: ".
2529             "set site to $sitename");
2530             }
2531             else
2532             {
2533 0         0 $self->log ("info",
2534             "IP-based virtual hosts: ".
2535             "no site found");
2536             }
2537             }
2538             }
2539             }
2540              
2541 41 50       264 $self->log ("info", "get peer name") if $self->{debug};
2542              
2543             # Get the peername and other details of this socket.
2544 41         117 my ($peername, $peerport, $peeraddr, $peeraddrstring);
2545              
2546 41 50       492 if ( $peername = getpeername STDIN )
2547             {
2548 0         0 ($peerport, $peeraddr) = unpack_sockaddr_in ($peername);
2549 0         0 $peeraddrstring = inet_ntoa ($peeraddr);
2550             }
2551             else
2552             {
2553 41         109 $peerport = 0;
2554 41         1043 $peeraddr = inet_aton ( $peeraddrstring = "127.0.0.1" );
2555             }
2556              
2557 41         508 $self->_log_line ("[CONNECTION FROM $peeraddrstring:$peerport] \#".
2558             (1 + $self->concurrent_connections));
2559              
2560             # Resolve the address.
2561 41         107 my $peerhostname;
2562 41 50       179 if ($self->config ("resolve addresses"))
2563             {
2564 0         0 my $hostname = gethostbyaddr ($peeraddr, AF_INET);
2565              
2566 0 0       0 if ($hostname)
2567             {
2568 0         0 my $ipaddr = gethostbyname ($hostname);
2569              
2570 0 0 0     0 if ($ipaddr && inet_ntoa ($ipaddr) eq $peeraddrstring)
2571             {
2572 0         0 $peerhostname = $hostname;
2573             }
2574             }
2575              
2576 0 0 0     0 if ($self->config ("require resolved addresses") && !$peerhostname)
2577             {
2578 0         0 $self->log ("err",
2579             "cannot resolve address for connection from " .
2580             "$peeraddrstring:$peerport");
2581 0         0 exit 0;
2582             }
2583             }
2584              
2585             # Set up request information.
2586 41         412 $self->{sockname} = $sockname;
2587 41         333 $self->{sockport} = $sockport;
2588 41         247 $self->{sockaddr} = $sockaddr;
2589 41         257 $self->{sockaddrstring} = $sockaddrstring;
2590 41         120 $self->{sitename} = $sitename;
2591 41         240 $self->{peername} = $peername;
2592 41         265 $self->{peerport} = $peerport;
2593 41         249 $self->{peeraddr} = $peeraddr;
2594 41         168 $self->{peeraddrstring} = $peeraddrstring;
2595 41         127 $self->{peerhostname} = $peerhostname;
2596 41         119 $self->{authenticated} = 0;
2597 41         101 $self->{loginattempts} = 0;
2598              
2599             # Default port information, used if no PORT command is issued. This
2600             # is used by the open_data_connection function. See RFC 959 section 3.2.
2601 41         112 $self->{_hostport} = $peerport;
2602 41         122 $self->{_hostaddr} = $peeraddr;
2603 41         116 $self->{_hostaddrstring} = $peeraddrstring;
2604              
2605             # Default mode is active. Issuing the PASV command switches the
2606             # server into passive mode.
2607 41         105 $self->{_passive} = 0;
2608              
2609             # Set up default connection state.
2610 41         261 $self->{type} = 'A';
2611 41         193 $self->{form} = 'N';
2612 41         256 $self->{mode} = 'S';
2613 41         127 $self->{stru} = 'F';
2614              
2615             # Other per-connection state.
2616 41         155 $self->{_mlst_facts} = \@_supported_mlst_facts;
2617 41         158 $self->{_checksum_method} = "MD5";
2618 41   33     197 $self->{_idle_timeout} = $self->config ("timeout") || $_default_timeout;
2619             $self->{maintainer_email}
2620 41 100       150 = defined $self->config ("maintainer email") ?
2621             $self->config ("maintainer email") :
2622             "root\@$self->{hostname}";
2623 41         160 $self->{_chdir_message_cache} = {};
2624              
2625             # Support for archive mode.
2626             $self->{archive_mode} =
2627 41   33     158 !defined $self->config ("enable archive mode") ||
2628             $self->config ("enable archive mode");
2629 41 50       264 $self->{archive_filters} = {} unless exists $self->{archive_filters};
2630 41 50       216 $self->{archive_generators} = {} unless exists $self->{archive_generators};
2631 41 50       241 if ($self->{archive_mode})
2632             {
2633             # NB. Extension matching is case insensitive.
2634 41 50       392 $self->{archive_filters}{".z"} = \&_archive_filter_Z
2635             if $self->_find_prog ("compress");
2636 41 50       188 $self->{archive_filters}{".gz"} = \&_archive_filter_gz
2637             if $self->_find_prog ("gzip");
2638 41 50       194 $self->{archive_filters}{".bz2"} = \&_archive_filter_bz2
2639             if $self->_find_prog ("bzip2");
2640 41 50       235 $self->{archive_filters}{".uue"} = \&_archive_filter_uue
2641             if $self->_find_prog ("uuencode");
2642              
2643             $self->{archive_generators}{".zip"} = \&_archive_generator_zip
2644 41 50       207 if exists $INC{"Archive/Zip.pm"};
2645             # $self->{archive_generators}{".tar"} = \&_archive_generator_tar
2646             # if exists $INC{"Archive/Tar.pm"};
2647 41         188 $self->{archive_generators}{".list"} = \&_archive_generator_list;
2648              
2649 41 50       173 if ($self->{debug})
2650             {
2651             $self->log ("info",
2652             "archive mode enabled [%s]",
2653             join (", ",
2654 41         203 keys %{$self->{archive_filters}},
2655 41         128 keys %{$self->{archive_generators}}));
  41         345  
2656             }
2657             }
2658              
2659 41 50       602 $self->log ("info", "in access control stage") if $self->{debug};
2660              
2661 41         806 my $r = $self->access_control_hook;
2662 41 50       229 exit if $r == -1;
2663              
2664             # Perform normal access control.
2665 41 50       170 if ($r == 0)
2666             {
2667 41 50       1229 unless ($self->_eval_rule ("access control rule"))
2668             {
2669 0         0 $self->reply (421, "Client denied by server configuration. Goodbye.");
2670 0         0 exit;
2671             }
2672             }
2673              
2674             # Install per-process limits.
2675 41 50       274 $self->log ("info", "in process limits stage") if $self->{debug};
2676              
2677 41         385 $r = $self->process_limits_hook;
2678 41 50       249 exit if $r == -1;
2679              
2680             # Perform normal per-process limits.
2681 41 50       157 if ($r == 0)
2682             {
2683 41   100     169 my $limit = 1024 * ($self->config ("limit memory") || 16384);
2684 41 100       443 $self->_set_rlimit ("RLIMIT_DATA", $limit) if $limit >= 0;
2685              
2686 41   100     26809 $limit = $self->config ("limit nr processes") || 10;
2687 41 100       298 $self->_set_rlimit ("RLIMIT_NPROC", $limit) if $limit >= 0;
2688              
2689 41   100     4391 $limit = $self->config ("limit nr files") || 20;
2690 41 100       222 $self->_set_rlimit ("RLIMIT_NOFILE", $limit) if $limit >= 0;
2691             }
2692              
2693 41 50       4188 unless ($self->{_test_mode})
2694             {
2695             # Log the connection information available.
2696 0 0       0 my $peerinfodpy
2697             = $peerhostname ?
2698             "$peerhostname:$peerport ($peeraddrstring:$peerport)" :
2699             "$peeraddrstring:$peerport";
2700              
2701 0         0 $self->log ("info", "connection from $peerinfodpy");
2702              
2703             # Change name of process in process listing.
2704 0 0 0     0 unless (defined $self->config ("change process name") &&
2705             !$self->config ("change process name"))
2706             {
2707 0         0 $0 = "ftpd $peerinfodpy";
2708             }
2709             }
2710              
2711             # Send the greeting.
2712 41   100     416 my $greeting_type = $self->config ("greeting type") || "full";
2713              
2714 41 100       204 if ($greeting_type eq "full")
    100          
    100          
    50          
2715             {
2716 38         390 $self->reply (220, "$self->{hostname} FTP server ($self->{version_string}) ready.");
2717             }
2718             elsif ($greeting_type eq "brief")
2719             {
2720 1         7 $self->reply (220, "$self->{hostname} FTP server ready.");
2721             }
2722             elsif ($greeting_type eq "terse")
2723             {
2724 1         9 $self->reply (220, "FTP server ready.");
2725             }
2726             elsif ($greeting_type eq "text")
2727             {
2728 1 50       7 my $greeting_text = $self->config ("greeting text")
2729             or die "greeting type is text, but no greeting text configuration value";
2730 1         7 $self->reply (220, $greeting_text);
2731             }
2732             else
2733             {
2734 0         0 die "unknown greeting type: ${greeting_type}";
2735             }
2736              
2737             # Implement Identification Protocol as explained in RFC 1413.
2738             # Some firewalls block the auth port which could make this
2739             # operation slow. Wait until after the greeting is sent to the
2740             # client to signify that it is okay for commands to be sent while
2741             # the ident authentication is taking place. This timeout is used
2742             # for both the connection and the "patience" desired for the
2743             # remote ident response. Having a timeout also helps to avoid a
2744             # possible DoS on the FTP server. There is no way to specify an
2745             # infinite timeout. The directive "ident timeout: 0" will disable
2746             # this feature.
2747              
2748 41         255 my $ident_timeout = $self->config ("ident timeout");
2749 41 0 33     223 if (defined $ident_timeout && $ident_timeout > 0 &&
      33        
      0        
      0        
2750             defined $self->{peerport} && defined $self->{sockport} &&
2751             defined $self->{peeraddrstring})
2752             {
2753 0         0 my $got_bored = 0;
2754 0         0 my $ident;
2755             eval
2756 0         0 {
2757 0         0 local $SIG{__WARN__} = 'DEFAULT';
2758 0         0 local $SIG{__DIE__} = 'DEFAULT';
2759 0     0   0 local $SIG{ALRM} = sub { $got_bored = 1; die "timed out"; };
  0         0  
  0         0  
2760 0         0 alarm $ident_timeout;
2761 0         0 "0" =~ /(0)/; # Perl 5.7 / IO::Socket::INET bug workaround.
2762             $ident = new IO::Socket::INET
2763             (PeerAddr => $self->{peeraddrstring},
2764 0         0 PeerPort => "auth");
2765             };
2766              
2767 0 0       0 if ($got_bored)
2768             {
2769             # Took too long to connect to remote auth port
2770             # (probably because of a client-side firewall).
2771 0         0 $self->_log_line ("[Ident auth failed: connection timed out]");
2772 0         0 $self->log ("warning", "ident auth failed for $self->{peeraddrstring}: connection timed out");
2773             }
2774             else
2775             {
2776 0 0       0 if (defined $ident)
2777             {
2778 0         0 my $response;
2779             eval
2780 0         0 {
2781 0         0 local $SIG{__WARN__} = 'DEFAULT';
2782 0         0 local $SIG{__DIE__} = 'DEFAULT';
2783             local $SIG{ALRM}
2784 0     0   0 = sub { $got_bored = 1; die "timed out"; };
  0         0  
  0         0  
2785 0         0 alarm $ident_timeout;
2786 0         0 $ident->print ("$self->{peerport} , ",
2787             "$self->{sockport}\r\n");
2788 0         0 $response = $ident->getline;
2789             };
2790 0         0 $ident->close;
2791              
2792             # Took too long to respond?
2793 0 0       0 if ($got_bored)
2794             {
2795 0         0 $self->_log_line ("[Ident auth failed: response timed out]");
2796 0         0 $self->log ("warning", "ident auth failed for $self->{peeraddrstring}: response timed out");
2797             }
2798             else
2799             {
2800 0 0       0 if ($response =~ /:\s*USERID\s*:\s*OTHER\s*:\s*(\S+)/)
2801             {
2802 0         0 $self->{auth} = $1;
2803 0         0 $self->_log_line ("[IDENT AUTH VERIFIED: $self->{auth}\@$self->{peeraddrstring}]");
2804 0         0 $self->log ("info", "ident auth: $self->{auth}\@$self->{peeraddrstring}");
2805             }
2806             else
2807             {
2808 0         0 $self->_log_line ("[Ident auth failed: invalid response]");
2809 0         0 $self->log ("warning", "ident auth failed for $self->{peeraddrstring}: invalid response");
2810             }
2811             }
2812             }
2813             else
2814             {
2815 0         0 $self->_log_line ("[Ident auth failed: Connection refused]");
2816 0         0 $self->log ("warning", "ident auth failed for $self->{peeraddrstring}: Connection refused");
2817             }
2818             }
2819             }
2820              
2821             # Get command filter, if set.
2822 41         275 my $cmd_filter = $self->config ("command filter");
2823              
2824             # Get restrict commands, if set, and parse them into a simpler format.
2825 41         154 my @restrict_commands = $self->config ("restrict command");
2826              
2827 41         154 foreach (@restrict_commands)
2828             {
2829 1 50       10 unless (/^"([a-zA-Z\s]+)"\s+(.*)/)
2830             {
2831 0         0 die "bad restrict command directive: restrict command: $_";
2832             }
2833              
2834 1         4 my $pattern = uc $1;
2835 1         3 my $code = $2;
2836              
2837             # The pattern is something like "SITE WHO". Turn this into
2838             # a real regular expression "^SITE\s+WHO\b".
2839 1         7 $pattern =~ s/\s+/\\s+/g;
2840 1         4 $pattern = "^$pattern\\b";
2841              
2842 1         14 $_ = { pattern => $pattern, code => $code };
2843             }
2844              
2845             # Command the commands permitted when not authenticated.
2846 41         149 my %no_authentication_commands = ();
2847              
2848 41 50       157 if (defined $self->config ("no authentication commands"))
2849             {
2850 0         0 my @c = split /\s+/, $self->config ("no authentication commands");
2851              
2852 0         0 foreach (@c) { $no_authentication_commands{$_} = 1; }
  0         0  
2853             }
2854             else
2855             {
2856 41         320 %no_authentication_commands =
2857             ("USER" => 1, "PASS" => 1, "LANG" => 1, "FEAT" => 1,
2858             "HELP" => 1, "QUIT" => 1, "HOST" => 1);
2859             }
2860              
2861             # Start reading commands from the client.
2862             COMMAND:
2863 41         101 for (;;)
2864             {
2865             # Pre-command hook.
2866 327         1207 $self->pre_command_hook;
2867              
2868             # Set an alarm to go off after so many seconds of idleness.
2869 327         1712 alarm $self->{_idle_timeout};
2870              
2871             # Get next line of input from the client.
2872             # XXX This does not comply properly with RFC 2640 section 3.1 -
2873             # We should translate to and treat ONLY
2874             # as a line ending character.
2875 327 100       16625554 last unless defined ($_ = );
2876              
2877 311         2133 $self->_check_signals;
2878              
2879             # Immediately terminate if the parent died.
2880             # In standalone mode, this means the main daemon has terminated.
2881             # In inet mode, this means that inetd itself has terminated.
2882             # In either case, the system administrator may have new
2883             # configuration settings that need to be loaded so any current
2884             # FTP clients should not be able to run any new commands on the
2885             # old configuration for security reasons.
2886 311 50       1421 if (getppid == 1)
2887             {
2888 0         0 $self->reply (421, "Manual Server Shutdown. Reconnect required.");
2889 0         0 exit;
2890             }
2891              
2892             # Restart alarm clock timer.
2893 311         1226 alarm $self->{_idle_timeout};
2894              
2895             # When out-of-band data arrives (eg. when the client performs
2896             # an ABOR command), the client will send several telnet control
2897             # characters before the actual command. Drop those bytes now.
2898 311         1241 s/^\377.// while m/^\377./;
2899              
2900             # Log client command if logging is enabled.
2901 311 50 66     2307 $self->_log_line ($_)
2902             unless /^PASS /i && $self->config ("hide passwords in client log");
2903              
2904             # Go slow?
2905 311 50       871 sleep $self->config ("command wait")
2906             if $self->config ("command wait");
2907              
2908             # Remove trailing CRLF.
2909 311         2105 s/[\n\r]+$//;
2910              
2911             # Command filter hook.
2912 311         1055 $r = $self->command_filter_hook ($_);
2913 311 50       757 next if $r == -1;
2914              
2915             # Command filter.
2916 311 50       741 if ($r == 0)
2917             {
2918 311 100       679 if (defined $cmd_filter)
2919             {
2920 15 100       104 unless ($_ =~ m/$cmd_filter/)
2921             {
2922 2         5 $self->reply (500,
2923             "Command does not match command filter.");
2924 2         4 next;
2925             }
2926             }
2927              
2928 309         832 foreach my $rc (@restrict_commands)
2929             {
2930 13 100       78 if ($_ =~ /$rc->{pattern}/i)
2931             {
2932             # Set up the variables.
2933 10         23 my $hostname = $self->{peerhostname};
2934 10         21 my $ip = $self->{peeraddrstring};
2935 10         20 my $user = $self->{user};
2936 10         18 my $class = $self->{class};
2937 10         18 my $user_is_anonymous = $self->{user_is_anonymous};
2938 10         18 my $type = $self->{type};
2939 10         17 my $form = $self->{form};
2940 10         15 my $mode = $self->{mode};
2941 10         16 my $stru = $self->{stru};
2942              
2943 10         568 my $rv = eval $rc->{code};
2944 10 50       38 die if $@;
2945              
2946 10 100       24 unless ($rv)
2947             {
2948 7         20 $self->reply (500,
2949             "Command restricted by site administrator.");
2950 7         27 next COMMAND;
2951             }
2952             }
2953             }
2954             }
2955              
2956             # Get the command.
2957             # See also RFC 2640 section 3.1.
2958 302 50       1393 unless (m/^([A-Z]{3,4})\s?(.*)/i)
2959             {
2960 0         0 $self->log ("err",
2961             "badly formed command received: %s", _escape ($_));
2962 0         0 $self->_log_line ("[Badly formed command]", _escape ($_));
2963 0         0 exit 0;
2964             }
2965              
2966             # The following strange 'eval' is necessary to work around a
2967             # very odd bug in Perl 5.6.0. The following assignment to
2968             # $cmd will fail in some cases unless you use $1 in some sort
2969             # of an expression beforehand.
2970             # - RWMJ 2002-07-05.
2971 302         20866 eval '$1 eq $1';
2972              
2973 302         1780 my ($cmd, $rest) = (uc $1, $2);
2974              
2975             $self->log ("info", "command: (%s, %s)",
2976             _escape ($cmd), _escape ($rest))
2977 302 50       1466 if $self->{debug};
2978              
2979             # Command requires user to be authenticated?
2980 302 50 66     1151 unless ($self->{authenticated} ||
2981             exists $no_authentication_commands{$cmd})
2982             {
2983 0         0 $self->reply (530, "Not logged in.");
2984 0         0 next;
2985             }
2986              
2987             # Handle the QUIT command specially.
2988 302 100       920 if ($cmd eq "QUIT")
2989             {
2990 25         113 $self->reply (221, "Goodbye. Service closing connection.");
2991 25         83 last;
2992             }
2993              
2994             # Got a command which matches in the table?
2995 277 50       794 unless (exists $self->{command_table}{$cmd})
2996             {
2997 0         0 $self->reply (500, "Unrecognized command.");
2998 0         0 $self->log ("err",
2999             "unknown command received: %s", _escape ($_));
3000 0         0 next;
3001             }
3002              
3003             # Run the command.
3004 277         462 &{$self->{command_table}{$cmd}} ($self, $cmd, $rest);
  277         1428  
3005              
3006             # Post-command hook.
3007 277         4790 $self->post_command_hook ($cmd, $rest);
3008              
3009             # Write out any xferlog that may have built up from the command
3010 277 50       782 $self->xfer_flush if $self->{_xferlog};
3011             }
3012              
3013 40         616 $self->quit_hook ();
3014              
3015 40 50       262 unless ($self->{_test_mode})
3016             {
3017 0         0 $self->_log_line ("[ENDED BY CLIENT $self->{peeraddrstring}:$self->{peerport}]");
3018 0         0 $self->log ("info", "connection terminated normally");
3019             }
3020              
3021             # The return value is used by the test scripts.
3022 40         486 $self;
3023             }
3024              
3025             # Signals are handled synchronously to get around the problem
3026             # with unsafe signals which exists in Perl < 5.7.2. Call the
3027             # following function periodically to check signals.
3028             sub _check_signals
3029             {
3030 5048     5048   7388 my $self = shift;
3031              
3032 5048 100       8427 if ($GOT_SIGURG)
3033             {
3034 2         5 $GOT_SIGURG = 0;
3035 2         23 $self->_handle_sigurg;
3036             }
3037              
3038 5048 50       7678 if ($GOT_SIGCHLD)
3039             {
3040 0         0 $GOT_SIGCHLD = 0;
3041 0         0 $self->_handle_sigchld;
3042             }
3043              
3044 5048 50       7963 if ($GOT_SIGHUP)
3045             {
3046 0         0 $GOT_SIGHUP = 0;
3047 0         0 $self->_handle_sighup;
3048             }
3049              
3050 5048 50       8399 if ($GOT_SIGTERM)
3051             {
3052 0         0 $GOT_SIGTERM = 0;
3053 0         0 $self->_handle_sigterm;
3054             }
3055              
3056             }
3057              
3058             # Handle SIGURG signal in the parent process.
3059             sub _handle_sigurg
3060             {
3061 2     2   4 my $self = shift;
3062              
3063 2         5 $self->{_urgent} = 1;
3064             }
3065              
3066             # Handle SIGCHLD signal in the parent process.
3067             sub _handle_sigchld
3068             {
3069 0     0   0 my $self = shift;
3070              
3071             # Clear up any zombie processes.
3072 0         0 while ((my $pid = waitpid (-1, WNOHANG)) > 0)
3073             {
3074             # Remove this PID from the children hash.
3075 0         0 delete $self->{_children}->{$pid};
3076             }
3077             }
3078              
3079             # Handle SIGHUP signal synchronously in the parent process.
3080             # This code mostly by Rob, rewritten and simplified by Rich for
3081             # the new synchronous signal handling code. Note that this function
3082             # has to be called synchronously (not from a signal handler, even
3083             # in Perl >= 5.7.2) because otherwise the exec will happen with
3084             # most signals blocked.
3085             sub _handle_sighup
3086             {
3087 0     0   0 my $self = shift;
3088              
3089             # Clear FD_CLOEXEC bit on the listening socket because we are
3090             # intending to pass that socket to our exec'd child process.
3091 0         0 $self->{_ctrl_sock}->fcntl (F_SETFD, my $flags = "");
3092              
3093             # Make the socket available to the child process in the environment.
3094 0         0 $ENV{BIND} = $self->{_ctrl_sock}->fileno;
3095              
3096             # Print a message to syslog.
3097 0         0 $self->log ("info", "received SIGHUP, reloading");
3098 0         0 $self->_log_line ("[DAEMON Reloading]");
3099              
3100             # Restart self.
3101 0 0       0 exec ($0, @ARGV) or die "hup exec failed: $!";
3102             }
3103              
3104             # Handle SIGTERM signal in the parent process.
3105             sub _handle_sigterm
3106             {
3107 0     0   0 my $self = shift;
3108              
3109 0         0 $self->log ("info", "shutting down daemon");
3110 0         0 $self->_log_line ("[DAEMON Shutdown]");
3111 0         0 exit;
3112             }
3113              
3114             # Added 20 Oct 2003 by Yair Lenga
3115             # Rotating Log files - allow stftime '%' in the file name
3116              
3117             sub _rotate_log
3118             {
3119 0     0   0 my $self = shift ;
3120 0         0 my $prop = "rotate log files";
3121              
3122 0 0       0 if (defined ($self->config($prop)) ? $self->config($prop) : 0)
    0          
3123             {
3124 0         0 $self->_open_error_log ;
3125 0         0 $self->_open_client_log ;
3126 0         0 $self->_open_xfer_log ;
3127             }
3128             }
3129              
3130             sub _open_error_log
3131             {
3132 0     0   0 my $self = shift ;
3133              
3134             # Check for new error log (remember open log file in in _error_file)
3135              
3136 0 0       0 if ( my $log_file = $self->config("error log") ) {
3137 0         0 $log_file = $self->resolve_log_file_name($log_file) ;
3138 0 0 0     0 if (!defined $self->{_error_file} ||
3139             $log_file ne $self->{_error_file}) {
3140 0         0 $self->log( 'notice', "Switch error log to $log_file") ;
3141 0 0       0 open STDERR, ">>$log_file"
3142             or die "cannot append: $log_file: $!";
3143 0         0 $self->{_error_file} = $log_file;
3144             }
3145             }
3146 0         0 return 1
3147             }
3148              
3149             sub _open_xfer_log
3150             {
3151 41     41   132 my $self = shift ;
3152 41 50       144 if ( my $log_file = $self->config("xfer logging") ) {
3153 0         0 $log_file = $self->resolve_log_file_name($log_file) ;
3154 0 0 0     0 if ( !defined $self->{_xfer_file} ||
3155             $log_file ne $self->{_xfer_file} ) {
3156 0 0       0 if ( my $io = $self->{_xferlog} ) {
3157 0         0 $io->close ;
3158 0         0 delete $self->{_xferlog} ;
3159             } ;
3160 0         0 $self->{_xfer_file} = $log_file;
3161 0         0 my $io = new IO::File $log_file, "a";
3162 0 0       0 if (defined $io) {
3163 0         0 $io->autoflush (1);
3164 0         0 $self->{_xferlog} = $io;
3165 0         0 $self->log( 'notice', "Using xfer log: $log_file") ;
3166             } else {
3167 0         0 die "cannot append: $log_file: $!";
3168             }
3169             }
3170             }
3171 41         126 return 1
3172             }
3173              
3174             sub _open_client_log
3175             {
3176 41     41   135 my $self = shift ;
3177 41 50       150 if ( my $log_file = $self->config("client logging") ) {
3178 0         0 $log_file = $self->resolve_log_file_name($log_file) ;
3179 0 0 0     0 if (!defined $self->{_client_file} ||
3180             $log_file ne $self->{_client_file} ) {
3181 0 0       0 if ( my $io = $self->{_client_log} ) {
3182 0         0 $io->close ;
3183 0         0 delete $self->{_client_log} ;
3184             } ;
3185 0         0 $self->{_client_file} = $log_file;
3186 0         0 my $io = new IO::File $log_file, "a";
3187 0 0       0 if (defined $io) {
3188 0         0 $io->autoflush (1);
3189 0         0 $self->{_client_log} = $io;
3190 0         0 $self->log( 'notice', "Starting client log: $log_file") ;
3191             } else {
3192 0         0 die "cannot append: $log_file: $!";
3193             }
3194             }
3195             }
3196             }
3197              
3198             sub resolve_log_file_name
3199             {
3200 0     0 0 0 my ($self, $log_file) = @_ ;
3201              
3202 0 0       0 $log_file =~ s/\$(\w+)/$self->{$1}/g
3203             if $log_file =~ /\$/ ;
3204 0 0       0 $log_file = strftime($log_file, localtime())
3205             if $log_file =~ /\%/ ;
3206 0         0 return $log_file;
3207             }
3208              
3209             # Added 21 Feb 2001 by Rob Brown
3210             # Client command logging
3211             sub _log_line
3212             {
3213 353     353   639 my $self = shift;
3214 353 50       1100 return unless exists $self->{_client_log};
3215 0         0 my $message = join ("",@_);
3216 0         0 my $io = $self->{_client_log};
3217 0         0 my $time = scalar localtime;
3218 0 0       0 my $authenticated = $self->{authenticated} ? $self->{user} : "-";
3219 0         0 $message =~ s/\n*$/\n/;
3220 0         0 $io->print ("[$time][$$:$authenticated]$message");
3221             }
3222              
3223             # Added 08 Feb 2001 by Rob Brown
3224             # Safely saves the process id to the specified pidfile.
3225             # If no pidfile is specified, nothing happens.
3226             sub _save_pid
3227             {
3228 0     0   0 my $self = shift;
3229              
3230             # Store pid into pidfile?
3231 0         0 $self->{_pidfile} = $self->config ("pidfile");
3232              
3233 0 0       0 if (defined $self->{_pidfile})
3234             {
3235 0         0 my $pidfile = $self->{_pidfile};
3236              
3237             # Swap $VARIABLE with corresponding attribute (i.e., $hostname)
3238 0         0 $pidfile =~ s/\$(\w+)/$self->{$1}/g;
3239 0 0       0 if ($pidfile =~ m%^([/\w\-\.]+)$%)
3240             {
3241 0         0 $self->{_pidfile} = $1;
3242 0 0       0 open (PID, ">$self->{_pidfile}")
3243             or die "cannot write $pidfile: $!";
3244 0         0 print PID "$$\n";
3245 0         0 close PID;
3246 0         0 eval "END {unlink('$1') if \$\$ == $$;}";
3247             }
3248             else
3249             {
3250 0         0 die "Refusing to create weird looking pidfile: $pidfile";
3251             }
3252             }
3253             }
3254              
3255             # Set a resource limit, by using the BSD::Resource module, if available.
3256              
3257             sub _set_rlimit
3258             {
3259 120     120   227 my $self = shift;
3260 120         210 my $name = shift;
3261 120         185 my $value = shift;
3262              
3263             # The BSD::Resource module is optional, and may not be available.
3264 120 50 33     2791 if (exists $INC{"BSD/Resource.pm"} &&
    0          
3265             exists get_rlimits()->{$name})
3266             {
3267 120 50       29790 setrlimit (&{$ {BSD::Resource::}{$name}}, $value, $value)
  120         951  
3268             or die "setrlimit: $!";
3269             }
3270             elsif (not $ENV{NET_FTPSERVER_NO_BSD_RESOURCE_WARNING})
3271             {
3272 0         0 warn
3273             "Resource limit $name cannot be set. This may be because ",
3274             "the BSD::Resource module is not available on your ",
3275             "system, or it may be because your operating system ",
3276             "does not support $name. Without resource limits, the ",
3277             "FTP server may be open to denial of service (DoS) ",
3278             "attacks. The real error was: $@";
3279             }
3280             }
3281              
3282             # Check for an external program (eg. "gzip"). This test is not
3283             # bulletproof: In particular, it requires $PATH to be set correctly
3284             # at the top of this file or in the config file.
3285              
3286             sub _find_prog
3287             {
3288 164     164   335 my $self = shift;
3289 164         312 my $prog = shift;
3290              
3291 164         516 my @paths = split /:/, $ENV{PATH};
3292 164         449 foreach (@paths)
3293             {
3294 328 100       3204 return 1 if -x "$_/$prog";
3295             }
3296 82         407 return 0;
3297             }
3298              
3299             # This subroutine loads the command line options and configuration file
3300             # and resolves conflicts. Command line options have priority over
3301             # certain things in the configuration file.
3302              
3303             sub _get_configuration
3304             {
3305 41     41   128 my $self = shift;
3306 41         129 my $args = shift;
3307 41         250 local @ARGV = @$args;
3308              
3309 41         155 my ($debug, $help, $port, $s_option, $S_option,
3310             $pidfile, $show_version, @overrides);
3311              
3312 41         767 Getopt::Long::Configure ("no_ignore_case");
3313 41         3274 Getopt::Long::Configure ("pass_through");
3314              
3315             GetOptions (
3316             "C=s" => \$self->{_config_file},
3317             "d+" => \$debug,
3318             "help|?" => \$help,
3319             "o=s" => \@overrides,
3320             "p=i" => \$port,
3321             "P=s" => \$pidfile,
3322             "s" => \$s_option,
3323             "S" => \$S_option,
3324             "test" => \$self->{_test_mode},
3325 41         1885 "v+" => \$debug,
3326             "V" => \$show_version,
3327             );
3328              
3329             # Show version and exit?
3330 41 50       57125 if ($show_version)
3331             {
3332 0         0 print $self->{version_string}, "\n";
3333 0         0 exit 0;
3334             }
3335              
3336             # Show help and exit?
3337 41 50       230 if ($help)
3338             {
3339 0         0 my $name = $0;
3340 0         0 $name =~ s,.*/,,;
3341              
3342 0         0 print <
3343             $name: $self->{version_string}
3344              
3345             Usage:
3346             $name [-options]
3347              
3348             Options:
3349             -?, --help Print this help text and exit.
3350             -d, -v Debug mode on.
3351             -p port Specify listening port (defaults to FTP port, 21).
3352             -s Run in daemon mode (default: run from inetd).
3353             -S Run in background and in daemon mode.
3354             -V Show version information and exit.
3355             -C config_file Specify configuration file (default: /etc/ftpd.conf).
3356             -P pidfile Save process ID into pidfile.
3357             -o option=value Override configuration file options.
3358              
3359             Normal standalone usage:
3360              
3361             $name -S
3362              
3363             Normal usage from inetd:
3364              
3365             ftp stream tcp nowait root /usr/sbin/tcpd $name
3366              
3367             For further information, please read the full documentation in the
3368             Net::FTPServer(3) manual page.
3369             EOT
3370 0         0 exit 0;
3371             }
3372              
3373             # Read the configuration file.
3374 41         156 $self->{_config} = {};
3375 41         153 $self->{_config_ip_host} = {};
3376 41         851 $self->_open_config_file ($self->{_config_file});
3377              
3378             # Magically update configuration values with command line
3379             # argument values. Thus configuration entered on the command
3380             # line will override those present in the configuration file.
3381 41 100       2719 if ($port)
3382             {
3383 2         10 $self->_set_config ("port", $port, splat => 1);
3384             }
3385 41 50       172 if ($s_option)
3386             {
3387 0         0 $self->_set_config ("daemon mode", 1, splat => 1);
3388             }
3389 41 50       182 if ($S_option)
3390             {
3391 0         0 $self->_set_config ("daemon mode", 1, splat => 1);
3392 0         0 $self->_set_config ("run in background", 1, splat => 1);
3393             }
3394 41 50       165 if ($pidfile)
3395             {
3396 0         0 $self->_set_config ("pidfile", $pidfile, splat => 1);
3397             }
3398              
3399             # Override other configuration file options.
3400 41         148 foreach (@overrides)
3401             {
3402 39         163 my ($key, $value) = split /=/, $_, 2;
3403 39         363 $self->_set_config ($key, $value, splat => 1);
3404             }
3405              
3406             # Set debugging state.
3407 41 50       189 if (defined $debug) {
    0          
3408 41         173 $self->{debug} = 1
3409             } elsif (defined $self->config ("debug")) {
3410 0         0 $self->{debug} = $self->config ("debug")
3411             }
3412             }
3413              
3414             # Fork into the background (command line -S option).
3415              
3416             sub _fork_into_background
3417             {
3418 0     0   0 my $self = shift;
3419              
3420 0         0 my $pid = fork;
3421 0 0       0 die "fork: $!" unless defined $pid;
3422              
3423             # Parent process ends here.
3424 0 0       0 exit if $pid > 0;
3425              
3426             # Start a new session.
3427 0         0 setsid;
3428              
3429             # Close connection to tty and reopen 0, 1 as /dev/null.
3430             # Note that 2 points to the error log.
3431 0         0 open STDIN, "
3432 0         0 open STDOUT, ">>/dev/null";
3433              
3434             # $self->log ("info", "forked into background");
3435             }
3436              
3437             # Be a daemon (command line -s option).
3438              
3439             sub _be_daemon
3440             {
3441 0     0   0 my $self = shift;
3442              
3443             # $self->log ("info", "operating in daemon mode");
3444 0         0 $self->_log_line ("[DAEMON Started]");
3445              
3446             # Jump to a safe place because this is a deamon
3447 0         0 chdir "/";
3448              
3449             # If the process receives SIGHUP, then it passes in the socket
3450             # fd here through the BIND environment variable. Check for this,
3451             # because if so we don't need to open a new listening socket.
3452 0 0 0     0 if (exists $ENV{BIND} && $ENV{BIND} =~ /^(\d+)$/)
3453             {
3454 0         0 my $bind_fd = $1;
3455 0         0 "0" =~ /(0)/; # Perl 5.7 / IO::Socket::INET bug workaround.
3456 0         0 $self->{_ctrl_sock} = new IO::Socket::INET;
3457 0 0       0 $self->{_ctrl_sock}->fdopen ($bind_fd, "w")
3458             or die "socket: $!";
3459             }
3460             # Otherwise do open a new listening socket.
3461             else
3462             {
3463             # Discover the default FTP port from /etc/services or equivalent.
3464 0   0     0 my $default_port = getservbyname ("ftp", "tcp") || 21;
3465              
3466             # Construct argument list to socket.
3467 0 0       0 my @args = (Reuse => 1,
3468             Proto => "tcp",
3469             Type => SOCK_STREAM,
3470             LocalPort =>
3471             (defined $self->config ("port")
3472             ? $self->config ("port")
3473             : $default_port));
3474              
3475             # Get length of listen queue.
3476 0 0       0 if (defined $self->config ("listen queue")) {
3477 0         0 push @args, Listen => $self->config ("listen queue");
3478             } else {
3479 0         0 push @args, Listen => 10;
3480             }
3481              
3482             # Get the local bind address.
3483 0 0       0 if (defined $self->config ("local address")) {
3484 0         0 push @args, LocalAddr => $self->config ("local address")
3485             }
3486              
3487             # Open a socket on the control port.
3488 0         0 "0" =~ /(0)/; # Perl 5.7 / IO::Socket::INET bug workaround.
3489             $self->{_ctrl_sock} =
3490 0 0       0 new IO::Socket::INET (@args)
3491             or die "socket: $!";
3492             }
3493              
3494             # Set TCP keepalive?
3495 0 0       0 if (defined $self->config ("tcp keepalive"))
3496             {
3497 0 0       0 $self->{_ctrl_sock}->sockopt (SO_KEEPALIVE, 1)
3498             or warn "setsockopt: SO_KEEPALIVE: $!";
3499             }
3500              
3501             # Initialize the children hash ref for max clients enforcement
3502 0         0 $self->{_children} = {};
3503              
3504 0         0 $self->post_bind_hook;
3505              
3506             # Accept new connections and fork off new process to handle it.
3507 0         0 for (;;)
3508             {
3509             # Possibly rotate the log files to a new name.
3510 0         0 $self->_rotate_log ;
3511              
3512 0         0 $self->pre_accept_hook;
3513 0 0       0 if (!$self->{_ctrl_sock}->opened)
3514             {
3515 0         0 die "control socket crashed somehow";
3516             }
3517              
3518             # ACCEPT may be undefined if, for example, the TCP-level 3-way
3519             # handshake is not completed. If this happens, all we really want
3520             # to do is to retry the accept, not die. Thanks to
3521             # Rob Brown for pointing this one out :-)
3522              
3523             # Because we are now handling signals synchronously, and because
3524             # signals are restartable, we want to periodically check for
3525             # signals. Thus the following code swaps between blocking on the
3526             # accept for 3 seconds and checking signals. The load on the
3527             # processor is insignificant (if you're worried about the load,
3528             # perhaps you should be using inetd?).
3529              
3530 0         0 my $sock;
3531              
3532 0         0 my $selector = new IO::Select;
3533 0         0 $selector->add ($self->{_ctrl_sock});
3534              
3535 0         0 until (defined $sock)
3536             {
3537 0         0 my @ready = $selector->can_read (3);
3538              
3539 0         0 $self->_check_signals;
3540              
3541 0 0       0 if (@ready > 0)
3542             {
3543 0         0 $sock = $self->{_ctrl_sock}->accept;
3544 0 0       0 warn "accept: $!" unless defined $sock;
3545             }
3546             }
3547              
3548             # Possibly rotate the log files to a new name.
3549 0         0 $self->_rotate_log ;
3550              
3551 0 0       0 if ($self->concurrent_connections >= $self->{_max_clients})
3552             {
3553             $sock->print ("500 ".
3554 0         0 $self->_percent_substitutions ($self->{_max_clients_message}).
3555             "\r\n");
3556 0         0 $sock->close;
3557 0         0 warn "Max connections $self->{_max_clients} reached!";
3558 0         0 $self->_log_line ("[Max connections $self->{_max_clients} reached]");
3559 0         0 next;
3560             }
3561              
3562             # Fork off a process to handle this connection.
3563 0         0 my $pid = fork;
3564 0 0       0 if (defined $pid)
3565             {
3566 0 0       0 if ($pid == 0) # Child process.
3567             {
3568             $self->log ("info", "starting child process")
3569 0 0       0 if $self->{debug};
3570              
3571             # Don't handle SIGCHLD in the child process, in case the
3572             # personality tries to launch subprocesses.
3573 0         0 $SIG{CHLD} = "DEFAULT";
3574              
3575             # SIGHUP in the child process exits immediately.
3576             $SIG{HUP} = sub {
3577 0     0   0 $self->log ("info", "exiting on HUP signal");
3578 0         0 exit;
3579 0         0 };
3580              
3581             $SIG{TERM} = sub {
3582 0     0   0 $self->log ("info", "exiting on TERM signal");
3583 0         0 $self->reply (421, "Manual shutdown from server");
3584 0         0 $self->_log_line ("[TERM RECEIVED]");
3585 0         0 exit;
3586 0         0 };
3587              
3588             # Wipe the hash within the child process to save memory
3589 0         0 $self->{_children} = $self->concurrent_connections;
3590              
3591             # Shutdown accepting file descriptor to allow successful
3592             # port bind() in case of a future daemon restart
3593 0         0 $self->{_ctrl_sock}->close;
3594              
3595             # Duplicate the socket so it looks like we were called
3596             # from inetd.
3597 0         0 dup2 ($sock->fileno, 0);
3598 0         0 dup2 ($sock->fileno, 1);
3599              
3600             # Return to the main process to handle the rest of
3601             # the connection.
3602 0         0 return;
3603             } # End of child process.
3604             }
3605             else # Error during fork(2).
3606             {
3607 0         0 warn "fork: $!";
3608 0         0 sleep 5; # Back off in case system is overloaded.
3609             }
3610              
3611             # A child has been successfully spawned.
3612             # So don't forget the kid's birthday!
3613 0         0 $self->{_children}->{$pid} = time;
3614             } # End of for (;;) loop in ftpd parent process.
3615             }
3616              
3617             sub concurrent_connections
3618             {
3619 41     41 0 112 my $self = shift;
3620              
3621 41 50       209 if (exists $self->{_children})
3622             {
3623 0 0       0 if (ref $self->{_children})
3624             {
3625             # Main Parent Server (exactly accurate)
3626 0         0 return scalar keys %{$self->{_children}};
  0         0  
3627             }
3628             else
3629             {
3630             # Child Process (slightly outdated count)
3631 0         0 return $self->{_children};
3632             }
3633             }
3634             else
3635             {
3636             # Not running as a daemon (eg. running from inetd). We don't
3637             # know the number of connections, but it's not likely to be
3638             # high, so just return 1.
3639 41         485 return 1;
3640             }
3641             }
3642              
3643             # Open configuration file and prepare to read configuration.
3644              
3645             sub _open_config_file
3646             {
3647 47     47   155 my $self = shift;
3648 47         139 my $config_file = shift;
3649              
3650 47         965 my $config = new IO::File "<$config_file";
3651 47 50       7307 unless ($config)
3652             {
3653 0         0 die "cannot open configuration file: $config_file: $!";
3654             }
3655              
3656 47         131 my $lineno = 0;
3657 47         127 my $sitename;
3658              
3659             # Read in the configuration options from the file.
3660 47         4000 while (defined ($_ = $config->getline))
3661             {
3662 38         951 $lineno++;
3663              
3664             # Remove trailing \n and \r.
3665 38         154 s/[\n\r]+$//;
3666              
3667             # Ignore blank lines and comments.
3668 38 100       138 next if /^\s*\#/;
3669 37 50       94 next if /^\s*$/;
3670              
3671             # More lines?
3672 37         84 while (/\\$/)
3673             {
3674 2         6 $_ =~ s/\\$//;
3675 2         56 my $nextline = $config->getline;
3676 2         40 $nextline =~ s/^\s+//;
3677 2         6 $nextline =~ s/[\n\r]+$//;
3678 2         5 $_ .= $nextline;
3679 2         6 $lineno++;
3680             }
3681              
3682             # Special treatment: files.
3683 37 100       77 if (/^\s*\s*$/i)
3684             {
3685 3 50       7 if ($sitename)
3686             {
3687 0         0 die "$config_file:$lineno: cannot use inside a section. It will not do what you expect. See the Net::FTPServer(3) manual page for information.";
3688             }
3689              
3690 3         18 $self->_open_config_file ($1);
3691 3         111 next;
3692             }
3693              
3694             # Special treatment: files.
3695 34 100       62 if (/^\s*\s*$/i)
3696             {
3697 1 50       3 if ($sitename)
3698             {
3699 0         0 die "$config_file:$lineno: cannot use inside a section. It will not do what you expect. See the Net::FTPServer(3) manual page for information.";
3700             }
3701              
3702 1         85 my @files = sort glob $1;
3703 1         5 foreach (@files)
3704             {
3705 3         60 $self->_open_config_file ($_);
3706             }
3707 1         40 next;
3708             }
3709              
3710             # Special treatment: sections.
3711 33 100       68 if (/^\s*\s*$/i)
3712             {
3713 1 50       3 if ($sitename)
3714             {
3715 0         0 die "$config_file:$lineno: unfinished section";
3716             }
3717              
3718 1         2 $sitename = $1;
3719 1         15 next;
3720             }
3721              
3722 32 100       79 if (/^\s*<\/Host>\s*$/i)
3723             {
3724 1 50       4 unless ($sitename)
3725             {
3726 0         0 die "$config_file:$lineno: unmatched ";
3727             }
3728              
3729 1         2 $sitename = undef;
3730 1         15 next;
3731             }
3732              
3733             # Special treatment: sections.
3734 31 100       69 if (/^\s*\s*$/i)
3735             {
3736 1 50       3 if ($sitename)
3737             {
3738 0         0 die "$config_file:$lineno: cannot use inside a section. It will not do what you expect. See the Net::FTPServer(3) manual page for information on the %host_config variable.";
3739             }
3740              
3741             # Suck in lines verbatim until we reach the end of this section.
3742 1         2 my $perl_code = "";
3743              
3744 1         17 while (defined ($_ = $config->getline))
3745             {
3746 5         87 $lineno++;
3747 5 100       13 last if /^\s*<\/Perl>\s*$/i;
3748 4         52 $perl_code .= $_;
3749             }
3750              
3751 1 50       2 unless ($_)
3752             {
3753 0         0 die "$config_file:$lineno: unfinished section";
3754             }
3755              
3756             # Untaint this code: it comes from a trusted source, namely
3757             # the configuration file.
3758 1         2 $perl_code =~ /(.*)/s;
3759 1         3 $perl_code = $1;
3760              
3761             # warn "executing perl code:\n$perl_code\n";
3762              
3763             # Run it. It will write into local variables %config and
3764             # %host_config.
3765 1         2 my %config;
3766             my %host_config;
3767              
3768 1         75 eval $perl_code;
3769 1 50       5 if ($@)
3770             {
3771 0         0 die "$config_file:$lineno: $@";
3772             }
3773              
3774             # Examine what it's written into %config and %host_config
3775             # and add those to the configuration.
3776 1         4 foreach (keys %config)
3777             {
3778 2         3 my $value = $config{$_};
3779              
3780 2 100       5 unless (ref $value) {
3781 1         2 $self->_set_config ($_, $value,
3782             file => $config_file, line => $lineno);
3783             } else {
3784 1         2 foreach my $v (@$value) {
3785 2         5 $self->_set_config ($_, $v,
3786             file => $config_file, line =>$lineno);
3787             }
3788             }
3789             }
3790              
3791 1         3 my $host;
3792 1         3 foreach $host (keys %host_config)
3793             {
3794 1         2 foreach (keys %{$host_config{$host}})
  1         3  
3795             {
3796 1         2 my $value = $host_config{$host}{$_};
3797              
3798 1 50       3 unless (ref $value) {
3799 1         3 $self->_set_config ($_, $value,
3800             sitename => $host,
3801             file => $config_file,
3802             line => $lineno);
3803             } else {
3804 0         0 foreach my $v (@$value) {
3805 0         0 $self->_set_config ($_, $v,
3806             sitename => $host,
3807             file => $config_file,
3808             line => $lineno);
3809             }
3810             }
3811             }
3812             }
3813              
3814 1         20 next;
3815             }
3816              
3817 30 50       62 if (/^\s*<\/Perl>\s*$/i)
3818             {
3819 0         0 die "$config_file:$lineno: unmatched ";
3820             }
3821              
3822             # Split the line on the first : character.
3823 30 50       96 unless (/^(.*?):(.*)$/)
3824             {
3825 0         0 die "$config_file:$lineno: syntax error in configuration file";
3826             }
3827              
3828 30         63 my $key = $1;
3829 30         50 my $value = $2;
3830              
3831 30         53 $key =~ s/^\s+//;
3832 30         62 $key =~ s/\s+$//;
3833              
3834 30         68 $value =~ s/^\s+//;
3835 30         60 $value =~ s/\s+$//;
3836              
3837 30         103 $self->_set_config ($key, $value,
3838             sitename => $sitename,
3839             file => $config_file,
3840             line => $lineno);
3841             }
3842             }
3843              
3844             sub _set_config
3845             {
3846 75     75   138 my $self = shift;
3847 75         129 my $key = shift;
3848 75         127 my $value = shift;
3849 75         272 my %params = @_;
3850              
3851 75         148 my $sitename = $params{sitename};
3852 75   100     402 my $config_file = $params{file} || "no file";
3853 75   100     251 my $lineno = $params{line} || "0";
3854 75         125 my $splat = $params{splat};
3855              
3856             # Convert the key to standard form so that small errors in the
3857             # FTP config file won't matter too much.
3858 75         154 $key = lc ($key);
3859 75         172 $key =~ tr/ / /s;
3860              
3861             # If the key is ``ip:'' then we treat it specially - adding it
3862             # to a hash from IP addresses to sites.
3863 75 50       186 if ($key eq "ip")
3864             {
3865 0 0       0 unless ($sitename)
3866             {
3867 0         0 print STDERR "$config_file:$lineno: ``ip:'' must only appear inside a section. See the Net::FTPServer(3) manual page for more information.\n";
3868 0         0 exit 1;
3869             }
3870              
3871 0         0 $self->{_config_ip_host}{$value} = $sitename;
3872             }
3873              
3874             # Prefix the sitename, if defined.
3875 75 100       167 $key = "$sitename:$key" if $sitename;
3876              
3877             # warn "configuration ($key, $value)";
3878              
3879             # Save this.
3880 75 100 100     349 $self->{_config}{$key} = [] if $splat || ! exists $self->{_config}{$key};
3881 75         136 push @{$self->{_config}{$key}}, $value;
  75         799  
3882             }
3883              
3884             # Before printing something received from the user to syslog, escape
3885             # any strange characters using this function.
3886              
3887             sub _escape
3888             {
3889 604     604   1237 local $_ = shift;
3890 604         1287 s/([^ -~])/sprintf ("\\x%02x", ord ($1))/ge;
  0         0  
3891 604         1832 $_;
3892             }
3893              
3894             =item $regex = $ftps->wildcard_to_regex ($wildcard)
3895              
3896             This is a general library function shared between many of
3897             the back-end database personalities. It converts a general
3898             wildcard (eg. *.c) into a regular expression (eg. ^.*\.c$ ).
3899              
3900             Thanks to: Terrence Monroe Brannon Eterrence.brannon@oracle.comE.
3901              
3902             =cut
3903              
3904             sub wildcard_to_regex
3905             {
3906 2     2 1 5 my $self = shift;
3907 2         3 my $wildcard = shift;
3908              
3909 2         7 $wildcard =~ s,([^?*a-zA-Z0-9]),\\$1,g; # Escape punctuation.
3910 2         13 $wildcard =~ s,\*,.*,g; # Turn * into .*
3911 2         4 $wildcard =~ s,\?,.,g; # Turn ? into .
3912 2         6 $wildcard = "^$wildcard\$"; # Bracket it.
3913              
3914 2         5 $wildcard;
3915             }
3916              
3917             =item $regex = $ftps->wildcard_to_sql_like ($wildcard)
3918              
3919             This is a general library function shared between many of
3920             the back-end database personalities. It converts a general
3921             wildcard (eg. *.c) into the strange wildcardish format
3922             used by SQL LIKE operator (eg. %.c).
3923              
3924             =cut
3925              
3926             sub wildcard_to_sql_like
3927             {
3928 0     0 1 0 my $self = shift;
3929 0         0 my $wildcard = shift;
3930              
3931 0         0 $wildcard =~ s/%/\\%/g; # Escape any existing % and _.
3932 0         0 $wildcard =~ s/_/\\_/g;
3933 0         0 $wildcard =~ tr/*?/%_/; # Translate to wierdo format.
3934              
3935 0         0 $wildcard;
3936             }
3937              
3938             =item $ftps->reply ($code, $line, [$line, ...])
3939              
3940             This function sends a standard single line or multi-line FTP
3941             server reply to the client. The C<$code> should be one of the
3942             standard reply codes listed in RFC 959. The one or more
3943             C<$line> arguments are the (free text) of the reply. Do
3944             I include carriage returns at the end of each C<$line>.
3945             This function adds the correct line ending format as specified
3946             in the RFC.
3947              
3948             =cut
3949              
3950             sub reply
3951             {
3952 466     466 1 821 my $self = shift;
3953              
3954 466         770 my $code = shift;
3955 466 50       2448 die "response code $code is not in RFC 959 format"
3956             unless $code =~ /^[1-5][0-5][0-9]$/;
3957              
3958 466 50       1138 die "reply must contain one or more lines of text"
3959             unless @_ > 0;
3960              
3961 466 100       973 if (@_ == 1) # Single-line response.
3962             {
3963 459         11108 print $code, " ", $_[0], "\r\n";
3964             }
3965             else # Multi-line response.
3966             {
3967 7         27 for (my $i = 0; $i < @_-1; ++$i)
3968             {
3969 39         306 print $code, "-", $_[$i], "\r\n";
3970             }
3971 7         34 print $code, " ", $_[@_-1], "\r\n";
3972             }
3973              
3974 466 50       3002 $self->log ("info", "reply: $code") if $self->{debug};
3975             }
3976              
3977             =item $ftps->log ($level, $message, ...);
3978              
3979             This function is identical to the normal C function
3980             to be found in C. However, it only uses syslog
3981             if the C configuration option is set to true.
3982              
3983             Use this function instead of calling C directly.
3984              
3985             =cut
3986              
3987             sub log
3988             {
3989 1015     1015 1 1757 my $self = shift;
3990              
3991 1015 50       3671 Sys::Syslog::syslog @_ if $self->{_enable_syslog};
3992             }
3993              
3994             =pod
3995              
3996             =item $ftps->config ($name);
3997              
3998             Read configuration option C<$name> from the configuration file.
3999              
4000             =cut
4001              
4002             sub config
4003             {
4004 1969     1969 1 3954 my $self = shift;
4005 1969         3237 my $key = shift;
4006              
4007             # Convert the key to standard form.
4008 1969         3275 $key = lc ($key);
4009 1969         3738 $key =~ tr/ / /s;
4010              
4011             # Try site-specific configuration option.
4012 1969 100 100     4904 if ($self->{sitename} &&
4013             exists $self->{_config}{"$self->{sitename}:$key"})
4014             {
4015 2 50       7 unless (wantarray)
4016             {
4017             # Return scalar value, but warn if there are many values
4018             # for this configuration operation.
4019 2 50       4 if (@{$self->{_config}{"$self->{sitename}:$key"}} > 1)
  2         13  
4020             {
4021 0         0 warn "called config in scalar context for an array valued key: $key";
4022             }
4023              
4024 2         9 return $self->{_config}{"$self->{sitename}:$key"}[0];
4025             }
4026             else
4027             {
4028 0         0 return @{$self->{_config}{"$self->{sitename}:$key"}};
  0         0  
4029             }
4030             }
4031              
4032             # Try global configuration option.
4033 1967 100       3912 if (exists $self->{_config}{$key})
4034             {
4035 79 100       167 unless (wantarray)
4036             {
4037             # Return scalar value, but warn if there are many values
4038             # for this configuration operation.
4039 74 50       105 if (@{$self->{_config}{$key}} > 1)
  74         187  
4040             {
4041 0         0 warn "called config in scalar context for an array valued key: $key";
4042             }
4043              
4044 74         275 return $self->{_config}{$key}[0];
4045             }
4046             else
4047             {
4048 5         9 return @{$self->{_config}{$key}};
  5         29  
4049             }
4050             }
4051              
4052             # Nothing found.
4053 1888 100       3534 unless (wantarray) { return undef } else { return () }
  105         304  
  1783         8291  
4054             }
4055              
4056             =pod
4057              
4058             =item $ftps->ip_host_config ($ip_addr);
4059              
4060             Look for a EHostE section which contains "ip: $ip_addr".
4061             If one is found, return the site name of the Host section. Otherwise
4062             return undef.
4063              
4064             =cut
4065              
4066             sub ip_host_config
4067             {
4068 0     0 1 0 my $self = shift;
4069 0         0 my $ip_addr = shift;
4070              
4071 0 0       0 if (exists $self->{_config_ip_host}{$ip_addr})
4072             {
4073 0         0 return $self->{_config_ip_host}{$ip_addr};
4074             }
4075              
4076 0         0 return undef;
4077             }
4078              
4079             sub _archive_filter_Z
4080             {
4081 0     0   0 my $self = shift;
4082 0         0 my $sock = shift;
4083              
4084 0         0 return archive_filter_external ($self, $sock, "compress");
4085             }
4086              
4087             sub _archive_filter_gz
4088             {
4089 0     0   0 my $self = shift;
4090 0         0 my $sock = shift;
4091              
4092 0         0 return archive_filter_external ($self, $sock, "gzip");
4093             }
4094              
4095             sub _archive_filter_bz2
4096             {
4097 0     0   0 my $self = shift;
4098 0         0 my $sock = shift;
4099              
4100 0         0 return archive_filter_external ($self, $sock, "bzip2");
4101             }
4102              
4103             sub _archive_filter_uue
4104             {
4105 0     0   0 my $self = shift;
4106 0         0 my $sock = shift;
4107              
4108 0         0 return archive_filter_external ($self, $sock, "uuencode", "file");
4109             }
4110              
4111             =pod
4112              
4113             =item $filter = $ftps->archive_filter_external ($sock, $cmd [, $args]);
4114              
4115             Apply C<$cmd> as a filter to socket C<$sock>. Returns a hash reference
4116             which contains the following keys:
4117              
4118             sock Newly opened socket.
4119             pid PID of filter program.
4120              
4121             If it fails, returns C.
4122              
4123             See section ARCHIVE MODE elsewhere in this manual for more information.
4124              
4125             =cut
4126              
4127             sub archive_filter_external
4128             {
4129 0     0 1 0 my $self = shift;
4130 0         0 my $sock = shift;
4131              
4132 0         0 my ($new_sock, $pid) = (FileHandle->new);
4133              
4134             # Perl is forcing me to go through unnecessary hoops here ...
4135 0 0       0 open AFE_SOCK, ">&" . fileno ($sock) or die "dup: $!";
4136 0         0 close $sock;
4137              
4138 0         0 eval {
4139 0         0 $pid = open2 (">&AFE_SOCK", $new_sock, @_);
4140             };
4141 0 0       0 if ($@)
4142             {
4143 0 0       0 if ($@ =~ /^open2:/)
4144             {
4145 0         0 warn (join (" ", @_), ": ", $@);
4146 0         0 return undef;
4147             }
4148 0         0 die;
4149             }
4150              
4151             # According to the open2 documentation, it should close AFE_SOCK
4152             # for me. Apparently not, so I'll close it myself.
4153 0         0 close AFE_SOCK;
4154              
4155 0         0 my %filter_object = (sock => $new_sock, pid => $pid);
4156              
4157 0         0 return \%filter_object;
4158             }
4159              
4160             sub _archive_generator_list
4161             {
4162 1     1   3 my $self = shift;
4163 1         3 my $dirh = shift;
4164              
4165 1         4 my @files = ();
4166              
4167             # Recursively visit all files and directories contained in $dirh.
4168             $self->visit
4169             ($dirh,
4170             { 'f' =>
4171             sub {
4172 2     2   30 push @files, $_->pathname;
4173             },
4174             'd' =>
4175             sub {
4176 4     4   18 my $pathname = $_->pathname;
4177              
4178 4         14 push @files, $pathname;
4179              
4180             # Only visit a directory if we are allowed to by the list rule.
4181             # Otherwise this could be used as a backdoor way to list
4182             # forbidden directories.
4183 4         17 return $self->_eval_rule ("list rule",
4184             undef, undef, $pathname);
4185             }
4186             }
4187 1         42 );
4188              
4189 1         15 my $str = join ("\n", @files) . "\n";
4190              
4191 1         29 return new IO::Scalar \$str;
4192             }
4193              
4194             sub _archive_generator_zip
4195             {
4196 0     0   0 my $self = shift;
4197 0         0 my $dirh = shift;
4198              
4199             # Create the zip file.
4200 0         0 my $zip = Archive::Zip->new ();
4201              
4202             # Recursively visit all files and directories contained in $dirh.
4203             $self->visit
4204             ($dirh,
4205             { 'f' =>
4206             sub {
4207 0     0   0 my $fileh = $_;
4208              
4209 0 0       0 if ($self->_eval_rule ("retrieve rule",
4210             $fileh->pathname,
4211             $fileh->filename,
4212             $fileh->dirname))
4213             {
4214             # Add file to archive. Archive::Zip has a nice
4215             # extensible "Member" concept. We create our own
4216             # member type (Net::FTPServer::ZipMember) which understands
4217             # our own file handles and serves them back to the
4218             # main Archive::Zip program on demand. This means
4219             # that at most only a small part of the file is
4220             # held in memory at any one time.
4221 0         0 my $memb
4222             = Net::FTPServer::ZipMember->_newFromFileHandle ($fileh);
4223              
4224 0 0       0 unless ($memb)
4225             {
4226 0         0 warn "zip: error reading ", $fileh->filename, ": ",
4227             $self->system_error_hook, " (ignored)";
4228 0         0 return;
4229             }
4230              
4231 0         0 $zip->addMember ($memb);
4232 0         0 $memb->desiredCompressionMethod
4233             (&Archive::Zip::COMPRESSION_DEFLATED);
4234 0         0 $memb->desiredCompressionLevel (9);
4235             }
4236             },
4237             'd' =>
4238             sub {
4239             # Only visit a directory if we are allowed to by the list rule.
4240             # Otherwise this could be used as a backdoor way to list
4241             # forbidden directories.
4242 0     0   0 return $self->_eval_rule ("list rule", undef, undef, $_->pathname);
4243             }
4244             }
4245 0         0 );
4246              
4247             # Is a temporary directory available? Is it writable? If so, dump
4248             # the ZIP file there. Otherwise, write it to an IO::Scalar (ie. in
4249             # memory).
4250 0 0       0 my $tmpdir =
4251             defined $self->config ("archive zip temporaries")
4252             ? $self->config ("archive zip temporaries")
4253             : "/tmp";
4254              
4255 0         0 my $file;
4256              
4257 0 0       0 if ($tmpdir)
4258             {
4259 0         0 my $tmpname = "$tmpdir/ftps.az.tmp.$$";
4260 0         0 $file = new IO::File ($tmpname, "w+");
4261              
4262 0 0       0 if ($file)
4263             {
4264 0         0 unlink $tmpname;
4265 0 0       0 $zip->writeToFileHandle ($file, 1) == &Archive::Zip::AZ_OK
4266             or die "failed to write to zip file: $!";
4267 0         0 $file->seek (0, 0);
4268             }
4269             }
4270              
4271 0 0       0 unless ($file)
4272             {
4273 0         0 $file = new IO::Scalar;
4274 0 0       0 $zip->writeToFileHandle ($file, 1) == &Archive::Zip::AZ_OK
4275             or die "failed to write to zip file: $!";
4276 0         0 $file->seek (0, 0);
4277             }
4278              
4279 0         0 return $file;
4280             }
4281              
4282             =pod
4283              
4284             =item $ftps->visit ($dirh, \%functions);
4285              
4286             The C function recursively "visits" every file and directory
4287             contained in C<$dirh> (which must be a directory handle).
4288              
4289             C<\%functions> is a reference to a hash of file types to functions.
4290             For example:
4291              
4292             'f' => \&visit_file,
4293             'd' => \&visit_directory,
4294             'l' => \&visit_symlink,
4295             &c.
4296              
4297             When a file of the known type is encountered, the appropriate
4298             function is called with C<$_> set to the file handle. (All functions
4299             are optional: if C encounters a file with a type not listed
4300             in the C<%functions> hash, then that file is just ignored).
4301              
4302             The return value from functions is ignored, I for the
4303             return value from the directory ('d') function. The directory
4304             function should return 1 to indicate that C should recurse
4305             into that directory. If the directory function returns 0, then
4306             C will skip that directory.
4307              
4308             C will call the directory function once for C<$dirh>.
4309              
4310             =cut
4311              
4312             sub visit
4313             {
4314 4     4 1 11 my $self = shift;
4315 4         9 my $dirh = shift;
4316 4         9 my $functions = shift;
4317              
4318 4         9 my $recurse = 1;
4319              
4320 4 50       14 if (exists $functions->{d})
4321             {
4322 4         12 local $_ = $dirh;
4323 4         8 $recurse = &{$functions->{d}} ();
  4         15  
4324             }
4325              
4326 4 50       16 if ($recurse)
4327             {
4328 4         23 my $files = $dirh->list_status ();
4329              
4330 4         10 my $file;
4331 4         30 foreach $file (@$files)
4332             {
4333 5         16 my $mode = $file->[2][0];
4334 5         11 my $fileh = $file->[1];
4335              
4336 5 100       26 if ($mode eq 'd')
    50          
4337             {
4338 3         27 $self->visit ($fileh, $functions);
4339             }
4340             elsif (exists $functions->{$mode})
4341             {
4342 2         5 local $_ = $fileh;
4343 2         6 &{$functions->{$mode}} ();
  2         8  
4344             }
4345             }
4346             }
4347             }
4348              
4349             sub _HOST_command
4350             {
4351 0     0   0 my $self = shift;
4352 0         0 my $cmd = shift;
4353 0         0 my $rest = shift;
4354              
4355             # HOST with no parameters just prints out the current site name.
4356 0 0       0 if ($rest eq "")
4357             {
4358 0 0       0 if ($self->{sitename}) {
4359 0         0 $self->reply (200, "HOST is set to $self->{sitename}.");
4360             } else {
4361 0         0 $self->reply (200, "HOST is not set.");
4362             }
4363 0         0 return;
4364             }
4365              
4366             # The user may only issue HOST before log in.
4367 0 0       0 if ($self->{authenticated})
4368             {
4369 0         0 $self->reply (501, "Cannot issue HOST command after logging in.");
4370 0         0 return;
4371             }
4372              
4373             # You cannot change HOST.
4374 0 0 0     0 if ($self->{sitename} && $self->{sitename} ne $rest)
4375             {
4376 0         0 $self->reply (501, "HOST already set to $self->{sitename}.");
4377 0         0 return;
4378             }
4379              
4380             # Check that the name is reasonable.
4381 0 0       0 unless ($rest =~ /^[-a-z0-9.]+$/i)
4382             {
4383 0         0 $self->reply (501, "HOST syntax error.");
4384 0         0 return;
4385             }
4386              
4387             # Allow the change.
4388 0         0 $self->{sitename} = $rest;
4389 0         0 $self->reply (200, "HOST set to $self->{sitename}.");
4390             }
4391              
4392             sub _USER_command
4393             {
4394 27     27   61 my $self = shift;
4395 27         53 my $cmd = shift;
4396 27         66 my $rest = shift;
4397              
4398             # If the user issues this command when logged in, generate an error.
4399             # We have to do this basically because of chroot and setuid stuff we
4400             # can't ``relogin'' as a different user.
4401 27 50       136 if ($self->{authenticated})
4402             {
4403 0         0 $self->reply (503, "You are already logged in.");
4404 0         0 return;
4405             }
4406              
4407             # Just save the username for now.
4408 27         94 $self->{user} = $rest;
4409              
4410             # Tried to log in anonymously?
4411 27 100 66     255 if ($rest eq "ftp" || $rest eq "anonymous")
4412             {
4413 8 50       32 unless ($self->config ("allow anonymous"))
4414             {
4415 0         0 $self->reply (421, "Anonymous logins not permitted.");
4416 0         0 $self->_log_line ("[No anonymous allowed]");
4417 0         0 exit 0;
4418             }
4419              
4420 8         62 $self->{user_is_anonymous} = 1;
4421             }
4422             else
4423             {
4424 19         67 delete $self->{user_is_anonymous};
4425             }
4426              
4427 27 100       148 unless ($self->{user_is_anonymous})
4428             {
4429 19         78 $self->reply (331, "Username OK, please send password.");
4430             }
4431             else
4432             {
4433 8         33 $self->reply (331, "Anonymous login OK, please send your email address as password.");
4434             }
4435             }
4436              
4437             sub _PASS_command
4438             {
4439 27     27   85 my $self = shift;
4440 27         75 my $cmd = shift;
4441 27         73 my $rest = shift;
4442              
4443             # If the user issues this command when logged in, generate an error.
4444 27 50       151 if ($self->{authenticated})
4445             {
4446 0         0 $self->reply (503, "You are already logged in.");
4447 0         0 return;
4448             }
4449              
4450             # Have we received a username?
4451 27 50       124 unless ($self->{user})
4452             {
4453 0         0 $self->reply (503, "Please send your username first.");
4454 0         0 return;
4455             }
4456              
4457             # If this is an anonymous login, check that the password conforms.
4458 27         130 my @anon_passwd_warning = ();
4459              
4460 27 100       117 if ($self->{user_is_anonymous})
4461             {
4462 8   100     30 my $cktype = $self->config ("anonymous password check") || "none";
4463 8   100     43 my $enforce = $self->config ("anonymous password enforce") || 0;
4464              
4465             # If the password ends with @, append hostname.
4466             my $hostname
4467             = $self->{peerhostname} ?
4468             $self->{peerhostname} :
4469 8 50       35 $self->{peeraddrstring};
4470              
4471 8 100       73 $rest .= $hostname if $rest =~ /\@$/;
4472              
4473 8 100       28 if ($cktype ne "none")
4474             {
4475 6         14 my $valid;
4476              
4477 6 100       30 if ($cktype eq "rfc822")
    100          
    50          
4478             {
4479 2         31 $valid = $self->_anon_passwd_validate_rfc822 ($rest);
4480             }
4481             elsif ($cktype eq "nobrowser")
4482             {
4483 2         27 $valid = $self->_anon_passwd_validate_nobrowser ($rest);
4484             }
4485             elsif ($cktype eq "trivial")
4486             {
4487 2         31 $valid = $self->_anon_passwd_validate_trivial ($rest);
4488             }
4489             else
4490             {
4491 0         0 die "unknown password check type: $cktype";
4492             }
4493              
4494             # Defer the warning until later on in the function.
4495 6 100       29 unless ($valid)
4496             {
4497 3         25 push @anon_passwd_warning,
4498             "The response \"$rest\" is not valid.",
4499             "Please use your email address as your password.",
4500             " For example: joe\@$hostname",
4501             "($hostname will be added if password ends with \@).";
4502             }
4503              
4504             # ... unless we have been told to enforce it now.
4505 6 100 66     52 if ($enforce && !$valid)
4506             {
4507 3         14 $self->reply (530, @anon_passwd_warning);
4508 3         13 return;
4509             }
4510             }
4511             }
4512              
4513             # OK, now the real authentication check.
4514             my $fail_code =
4515             $self->authentication_hook ($self->{user}, $rest,
4516 24         216 $self->{user_is_anonymous}) ;
4517              
4518 24 100       139 if ( $fail_code < 0 )
4519             {
4520             # See RFC 2577 section 5.
4521 1 50       5000145 sleep 5 unless $fail_code == -2 ;
4522              
4523             # Login failed.
4524 1         27 $self->{loginattempts}++;
4525              
4526 1 50 50     21 if ($self->{loginattempts} >=
4527             ($self->config ("max login attempts") || 3))
4528             {
4529             $self->log ("notice", "repeated login attempts from %s:%d",
4530             $self->{peeraddrstring},
4531 0         0 $self->{peerport});
4532              
4533             # See RFC 2577 section 5.
4534 0         0 $self->reply (421, "Too many login attempts. Goodbye.");
4535 0         0 $self->_log_line ("[Max logins reached]");
4536 0         0 exit 0;
4537             }
4538              
4539 1         12 $self->reply (530, "Login failed.");
4540 1         5 return;
4541             }
4542              
4543             # Perform user access control step.
4544 23 50       184 unless ($self->_eval_rule ("user access control rule"))
4545             {
4546 0         0 $self->reply (421, "User denied by server configuration. Goodbye.");
4547 0         0 $self->_log_line ("[Client denied]");
4548 0         0 exit;
4549             }
4550              
4551             # Login was officially OK.
4552 23         75 $self->{authenticated} = 1;
4553              
4554             # Compute user's class.
4555             $self->{class} =
4556 23         455 $self->_username_to_class ($rest, $self->{user_is_anonymous});
4557              
4558             # Compute home directory. We may need it when we display the
4559             # welcome message.
4560 23 100       106 unless ($self->{user_is_anonymous})
4561             {
4562 18 50       66 if (defined $self->config ("home directory"))
4563             {
4564 0         0 $self->{home_directory} = $self->config ("home directory");
4565              
4566 0         0 $self->{home_directory} =~ s/%m/(getpwnam $self->{user})[7]/ge;
  0         0  
4567 0         0 $self->{home_directory} =~ s/%U/$self->{user}/ge;
  0         0  
4568 0         0 $self->{home_directory} =~ s/%%/%/g;
4569             }
4570             else
4571             {
4572 18   50     9781 $self->{home_directory} = (getpwnam $self->{user})[7] || "/";
4573             }
4574             }
4575             else
4576             {
4577             # Anonymous users always get "/" as their home directory.
4578 5         28 $self->{home_directory} = "/";
4579             }
4580              
4581             # Send a welcome message -- before the chroot since we may
4582             # need to read a file in the real root.
4583 23   100     147 my $welcome_type = $self->config ("welcome type") || "normal";
4584              
4585 23 100       115 if ($welcome_type eq "normal")
    100          
    50          
4586             {
4587 21 100       83 if (! $self->{user_is_anonymous})
4588             {
4589             $self->reply (230,
4590             @anon_passwd_warning,
4591 16         94 "Welcome " . $self->{user} . ".");
4592             }
4593             else
4594             {
4595 5         33 $self->reply (230,
4596             @anon_passwd_warning,
4597             "Welcome $rest.");
4598             }
4599             }
4600             elsif ($welcome_type eq "text")
4601             {
4602 1 50       3 my $welcome_text = $self->config ("welcome text")
4603             or die "welcome type is text, but no welcome text configuration value";
4604              
4605 1         29 $welcome_text = $self->_percent_substitutions ($welcome_text);
4606              
4607 1         4 $self->reply (230,
4608             @anon_passwd_warning,
4609             $welcome_text);
4610             }
4611             elsif ($welcome_type eq "file")
4612             {
4613 1 50       5 my $welcome_file = $self->config ("welcome file")
4614             or die "welcome type is file, but no welcome file configuration value";
4615              
4616 1         4 my @lines = ();
4617              
4618 1 50       6 if (my $io = new IO::File $welcome_file, "r")
4619             {
4620 1         111 while (<$io>) {
4621 1         12 s/[\n\r]+$//;
4622 1         11 push @lines, $self->_percent_substitutions ($_);
4623             }
4624 1         16 $io->close;
4625             }
4626             else
4627             {
4628 0         0 @lines =
4629             ( "The server administrator has configured a welcome file,",
4630             "but the file is missing." );
4631             }
4632              
4633 1         13 $self->reply (230, @anon_passwd_warning, @lines);
4634             }
4635             else
4636             {
4637 0         0 die "unknown welcome type: $welcome_type";
4638             }
4639              
4640             # Set the timezone for responses.
4641 23 50       106 $ENV{TZ} = defined $self->config ("time zone")
4642             ? $self->config ("time zone")
4643             : "GMT";
4644              
4645             # Patch fom John Jetmore . The following
4646             # line is necessary to open /etc/localtime in the chroot environment.
4647 23         1100 scalar (localtime (time));
4648              
4649             # Open /etc/protocols etc., in case we chroot. And yes, doing the
4650             # setprotoent _twice_ is necessary to work around a bug in Perl or
4651             # glibc (thanks Abraham Ingersoll ). Jamie Hill
4652             # says that the getprotobyname ("tcp") call
4653             # is necessary for Solaris too.
4654 23         3302 setprotoent 1;
4655 23         328 setprotoent 1;
4656 23         733 $_ = getprotobyname ("tcp");
4657 23         678 sethostent 1;
4658 23         565 setnetent 1;
4659 23         263 setservent 1;
4660 23         2231 setpwent;
4661 23         373 setgrent;
4662              
4663             # Perform chroot, etc., as required.
4664             $self->user_login_hook ($self->{user},
4665 23         207 $self->{user_is_anonymous});
4666              
4667             # Set CWD to /.
4668 23         118 $self->{cwd} = $self->root_directory_hook;
4669              
4670             # Move to home directory.
4671 23         60 my $new_cwd;
4672              
4673 23 50       194 if ($new_cwd = $self->_chdir ($self->{cwd}, $self->{home_directory}))
4674             {
4675 23         468 $self->{cwd} = $new_cwd;
4676             }
4677             else
4678             {
4679 0         0 $self->log ("warning",
4680             "no home directory for user: $self->{user}");
4681             }
4682              
4683             }
4684              
4685             # Convert a username to a class by using the class directives
4686             # in the configuration file.
4687              
4688             sub _username_to_class
4689             {
4690 23     23   78 my $self = shift;
4691 23         58 my $username = shift;
4692 23         66 my $user_is_anonymous = shift;
4693              
4694 23         83 my @classes = $self->config ("class");
4695              
4696 23         79 local $_;
4697              
4698 23         147 foreach my $class (@classes)
4699             {
4700             # class: CLASSNAME { perl code ... }
4701 0 0       0 if ($class =~ /^(\w+)\s+\{(.*)\}\s*$/)
    0          
4702             {
4703 0         0 my $classname = $1;
4704 0         0 my $code = $2;
4705              
4706 0         0 $_ = $username;
4707              
4708 0         0 my $rv = eval $code;
4709 0 0       0 die if $@;
4710              
4711 0 0       0 return $classname if $rv;
4712             }
4713             # class: CLASSNAME USERNAME[,USERNAME[,...]]
4714             elsif ($class =~ /^(\w*)\s+(.*)/)
4715             {
4716 0         0 my $classname = $1;
4717 0         0 my @users = split /[,\s]+/, $2;
4718              
4719 0         0 foreach (@users)
4720             {
4721 0 0       0 return $classname if $_ eq $username;
4722             }
4723             }
4724             else
4725             {
4726 0         0 die "bad class directive: class: $_";
4727             }
4728             }
4729              
4730             # Default cases.
4731 23 100       117 return "anonymous" if $user_is_anonymous;
4732 18         155 return "users";
4733             }
4734              
4735             sub _percent_substitutions
4736             {
4737 2     2   7 my $self = shift;
4738 2         15 local $_ = shift;
4739              
4740             # See CONFIGURATION section on ``welcome text'' for a list of
4741             # the substitutions available.
4742 2         21 s/%C/$self->{cwd}->pathname/ge;
  0         0  
4743 2         12 s/%E/$self->{maintainer_email}/ge;
  2         9  
4744 2         7 s/%G/gmtime/ge;
  0         0  
4745 2 0       4 s/%R/$self->{peerhostname} ? $self->{peerhostname} : $self->{peeraddrstring}/ge;
  0         0  
4746 2         8 s/%L/$self->{hostname}/ge;
  2         7  
4747 2         5 s/%m/$self->{home_directory}/ge;
  0         0  
4748 2         5 s/%T/localtime/ge;
  0         0  
4749 2         8 s/%U/$self->{user}/ge;
  2         7  
4750 2         7 s/%u/$self->{user}/ge;
  0         0  
4751 2         3 s/%x/$self->{_max_clients}/ge;
  0         0  
4752 2         11 s/%%/%/g;
4753              
4754 2         12 return $_;
4755             }
4756              
4757             sub _anon_passwd_validate_rfc822
4758             {
4759 4     4   10 my $self = shift;
4760 4         8 my $pass = shift;
4761              
4762             # RFC 822 section 6.1, ``addr-spec''.
4763             # But in fact this is not very careful about checking
4764             # the address. There's probably a Perl library I should
4765             # be using here ... XXX
4766 4         79 return $pass =~ /^\S+\@\S+\.\S+$/;
4767             }
4768              
4769             sub _anon_passwd_validate_nobrowser
4770             {
4771 2     2   5 my $self = shift;
4772 2         4 my $pass = shift;
4773              
4774             return
4775 2   66     15 $self->_anon_passwd_validate_rfc822 ($pass) &&
4776             $pass !~ /^mozilla@/ &&
4777             $pass !~ /^IE[0-9]+User@/ &&
4778             $pass !~ /^nobody@/;
4779             }
4780              
4781             sub _anon_passwd_validate_trivial
4782             {
4783 2     2   4 my $self = shift;
4784 2         4 my $pass = shift;
4785              
4786 2         18 return $pass =~ /\@/;
4787             }
4788              
4789             # Assuming we are running as root, drop privileges and change
4790             # to user called $username who has uid $uid and gid $gid. There
4791             # is no interface to initgroups, so we have to do that by
4792             # hand -- yuck.
4793             sub _drop_privs
4794             {
4795 0     0   0 my $self = shift;
4796 0         0 my $uid = shift;
4797 0         0 my $gid = shift;
4798 0         0 my $username = shift;
4799              
4800             # Get the list of extra groups to pass to setgroups(2).
4801 0         0 my @groups = ();
4802              
4803 0         0 my @g;
4804 0         0 while (@g = getgrent)
4805             {
4806 0         0 my ($gr_name, $gr_passwd, $gr_gid, $gr_members) = @g;
4807 0         0 my @members = split /\s+/, $gr_members;
4808              
4809 0         0 foreach (@members)
4810             {
4811 0 0       0 push @groups, $gr_gid if $_ eq $username;
4812             }
4813             }
4814              
4815 0         0 setgrent; # Rewind the pointer.
4816              
4817             # Set the effective GID/UID.
4818 0         0 $) = join (" ", $gid, $gid, @groups);
4819 0         0 $> = $uid;
4820              
4821             # set the real GID/UID if we are going to use non-priv port
4822             # Otherwise, keep root access so we can bind to the port
4823 0 0       0 if (my $ftpdata = $self->{ftp_data_port})
4824             {
4825 0 0       0 if ( $ftpdata >= 1024 )
4826             {
4827 0         0 $( = $gid;
4828 0         0 $< = $uid;
4829             }
4830             }
4831             }
4832              
4833             sub _ACCT_command
4834             {
4835 0     0   0 my $self = shift;
4836 0         0 my $cmd = shift;
4837 0         0 my $rest = shift;
4838              
4839             # Not likely that the ACCT command will ever be implemented,
4840             # unless there is some strange login method that needs to be
4841             # supported.
4842 0         0 $self->reply (500, "Command not implemented.");
4843             }
4844              
4845             sub _CWD_command
4846             {
4847 7     7   15 my $self = shift;
4848 7         16 my $cmd = shift;
4849 7         14 my $rest = shift;
4850              
4851 7         15 my $new_cwd;
4852              
4853             # Look relative to the current directory first.
4854 7 100       28 if ($new_cwd = $self->_chdir ($self->{cwd}, $rest))
4855             {
4856             # Access control
4857 6 50       27 unless ($self->_eval_rule ("chdir rule",
4858             $new_cwd->pathname, $new_cwd->filename,
4859             $new_cwd->pathname))
4860             {
4861 0         0 $self->reply (550, "CWD command denied by server configuration.");
4862 0         0 return;
4863             }
4864              
4865 6         28 $self->{cwd} = $new_cwd;
4866 6         40 $self->_chdir_message;
4867 6         23 return;
4868             }
4869              
4870             # Look for an alias called ``$rest''.
4871 1 50       12 if ($rest !~ /\//)
4872             {
4873 1         4 my @aliases = $self->config ("alias");
4874              
4875 1         3 foreach (@aliases)
4876             {
4877 0         0 my ($name, $dir) = split /\s+/, $_;
4878              
4879 0 0 0     0 if ($name eq $rest &&
4880             ($new_cwd = $self->_chdir ($self->{cwd}, $dir)))
4881             {
4882 0         0 $self->{cwd} = $new_cwd;
4883 0         0 $self->_chdir_message;
4884 0         0 return;
4885             }
4886             }
4887             }
4888              
4889             # Look for a directory on the cdpath.
4890 1 50       3 if ($self->config ("cdpath"))
4891             {
4892 0         0 my @cdpath = split /\s+/, $self->config ("cdpath");
4893              
4894 0         0 foreach (@cdpath)
4895             {
4896 0 0 0     0 if (($new_cwd = $self->_chdir ($self->{cwd}, $_)) &&
4897             ($new_cwd = $self->_chdir ($new_cwd, $rest)))
4898             {
4899 0         0 $self->{cwd} = $new_cwd;
4900 0         0 $self->_chdir_message;
4901 0         0 return;
4902             }
4903             }
4904             }
4905              
4906             # All change directory methods failed.
4907 1         6 $self->reply (550, "Directory not found.");
4908             }
4909              
4910             sub _CDUP_command
4911             {
4912 2     2   3 my $self = shift;
4913 2         4 my $cmd = shift;
4914 2         3 my $rest = shift;
4915              
4916 2 50       5 if (my $new_cwd = $self->_chdir ($self->{cwd}, ".."))
4917             {
4918             # Access control
4919 2 50       6 unless ($self->_eval_rule ("chdir rule",
4920             $new_cwd->pathname, $new_cwd->filename,
4921             $new_cwd->pathname))
4922             {
4923 0         0 $self->reply (550, "CDUP command denied by server configuration.");
4924 0         0 return;
4925             }
4926              
4927 2         6 $self->{cwd} = $new_cwd;
4928 2         5 $self->_chdir_message;
4929             }
4930             else
4931             {
4932 0         0 $self->reply (550, "Directory not found.");
4933             }
4934             }
4935              
4936             # This little function displays the contents of a special
4937             # message file the first time a user visits a directory,
4938             # if this capability has been configured in.
4939              
4940             sub _chdir_message
4941             {
4942 8     8   19 my $self = shift;
4943              
4944 8         19 my $filename = $self->config ("chdir message file");
4945 8         15 my $file;
4946              
4947 8 50 33     31 if ($filename &&
      33        
4948             ! exists $self->{_chdir_message_cache}{$self->{cwd}->pathname} &&
4949             ($file = $self->{cwd}->open ($filename, "r")))
4950             {
4951 0         0 my @lines = ();
4952 0         0 local $_;
4953              
4954             # Read the file into memory and perform % escaping.
4955 0         0 while (defined ($_ = $file->getline))
4956             {
4957 0         0 s/[\n\r]+$//;
4958 0         0 push @lines, $self->_percent_substitutions ($_);
4959             }
4960 0         0 $file->close;
4961              
4962             # Remember that we've visited this directory once in
4963             # this session.
4964 0         0 $self->{_chdir_message_cache}{$self->{cwd}->pathname} = 1;
4965              
4966 0         0 $self->reply (250, @lines, "Changed directory OK.");
4967             }
4968             else
4969             {
4970 8         29 $self->reply (250, "Changed directory OK.");
4971             }
4972             }
4973              
4974             sub _SMNT_command
4975             {
4976 0     0   0 my $self = shift;
4977 0         0 my $cmd = shift;
4978 0         0 my $rest = shift;
4979              
4980             # Not a very useful command.
4981 0         0 $self->reply (500, "Command not implemented.");
4982             }
4983              
4984             sub _REIN_command
4985             {
4986 0     0   0 my $self = shift;
4987 0         0 my $cmd = shift;
4988 0         0 my $rest = shift;
4989              
4990             # This command is not implemented, because we do not allow a
4991             # user to revoke permissions and relogin (without disconnecting
4992             # and reconnecting anyway).
4993 0         0 $self->reply (500, "The REIN command is not supported. You must QUIT and reconnect.");
4994             }
4995              
4996             sub _QUIT_command
4997             {
4998             # This function should never be called. The server main command loop
4999             # now deals with the "QUIT" command as a special case.
5000 0     0   0 die;
5001             }
5002              
5003             sub _PORT_command
5004             {
5005 6     6   13 my $self = shift;
5006 6         12 my $cmd = shift;
5007 6         11 my $rest = shift;
5008              
5009             # The arguments to PORT are a1,a2,a3,a4,p1,p2 where a1 is the
5010             # most significant part of the address (eg. 127,0,0,1) and
5011             # p1 is the most significant part of the port.
5012             #
5013             # Some clients (eg. IE 6.0.2600.0000 and IBM mainframes) send
5014             # leading zeroes in front of the numbers, and apparently the RFC
5015             # doesn't prevent this. So we must use the 'int' function to
5016             # remove these leading zeroes.
5017 6 50       48 unless ($rest =~ /^\s*(\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})/)
5018             {
5019 0         0 $self->reply (501, "Syntax error in PORT command.");
5020 0         0 return;
5021             }
5022              
5023 6         28 my $a1 = int ($1);
5024 6         12 my $a2 = int ($2);
5025 6         15 my $a3 = int ($3);
5026 6         27 my $a4 = int ($4);
5027 6         16 my $p1 = int ($5);
5028 6         12 my $p2 = int ($6);
5029              
5030             # Check host address.
5031 6 50 33     106 unless ($a1 > 0 && $a1 < 224 &&
      33        
      33        
      33        
      33        
      33        
      33        
5032             $a2 >= 0 && $a2 < 256 &&
5033             $a3 >= 0 && $a3 < 256 &&
5034             $a4 >= 0 && $a4 < 256)
5035             {
5036 0         0 $self->reply (501, "Invalid host address.");
5037 0         0 return;
5038             }
5039              
5040             # Construct host address.
5041 6         27 my $hostaddrstring = "$a1.$a2.$a3.$a4";
5042              
5043             # Are we connecting back to the client?
5044 6 50       16 unless ($self->config ("allow proxy ftp"))
5045             {
5046 6 50 33     21 if (!$self->{_test_mode} && $hostaddrstring ne $self->{peeraddrstring})
5047             {
5048             # See RFC 2577 section 3.
5049 0         0 $self->reply (504, "Proxy FTP is not allowed on this server.");
5050 0         0 return;
5051             }
5052             }
5053              
5054             # Construct port number.
5055 6         19 my $hostport = $p1 * 256 + $p2;
5056              
5057             # Check port number.
5058 6 50 33     35 unless ($hostport > 0 && $hostport < 65536)
5059             {
5060 0         0 $self->reply (501, "Invalid port number.");
5061             }
5062              
5063             # Allow connections back to ports < 1024?
5064 6 50       16 unless ($self->config ("allow connect low port"))
5065             {
5066 6 50       17 if ($hostport < 1024)
5067             {
5068             # See RFC 2577 section 3.
5069 0         0 $self->reply (504, "This server will not connect back to ports < 1024.");
5070 0         0 return;
5071             }
5072             }
5073              
5074 6         14 $self->{_hostaddrstring} = $hostaddrstring;
5075 6         30 $self->{_hostaddr} = inet_aton ($hostaddrstring);
5076 6         16 $self->{_hostport} = $hostport;
5077 6         12 $self->{_passive} = 0;
5078              
5079 6         17 $self->reply (200, "PORT command OK.");
5080             }
5081              
5082             sub _PASV_command
5083             {
5084 12     12   36 my $self = shift;
5085 12         29 my $cmd = shift;
5086 12         30 my $rest = shift;
5087              
5088             # Open a listening socket - but don't actually accept on it yet.
5089             # RFC 2577 section 8 suggests using random local port numbers.
5090             # In order to make firewall rules on FTP servers more sane, make
5091             # the range of local port numbers configurable, and default to
5092             # only opening ports in the range 49152-65535 (see:
5093             # http://www.isi.edu/in-notes/iana/assignments/port-numbers for
5094             # rationale).
5095 12         47 my $port_range = $self->config ("passive port range");
5096 12 50       99 $port_range = "49152-65535" unless defined $port_range;
5097              
5098 12         34 my $sock;
5099              
5100 12 50       47 if ($port_range eq "0")
5101             {
5102             # Use the standard kernel determined ephemeral port
5103             # by leaving off LocalPort parameter.
5104 0         0 "0" =~ /(0)/; # Perl 5.7 / IO::Socket::INET bug workaround.
5105             $sock = IO::Socket::INET->new
5106             (Listen => 1,
5107             LocalAddr => $self->{sockaddrstring},
5108 0         0 Reuse => 1,
5109             Proto => "tcp",
5110             Type => SOCK_STREAM);
5111             }
5112             else
5113             {
5114             # Parse the $port_range string and assign a port from the
5115             # range at random.
5116 12         74 my @ranges = split /\s*,\s*/, $port_range;
5117 12         37 my $total_width = 0;
5118 12         38 foreach (@ranges)
5119             {
5120 12         177 my ($min, $max) = split /\s*-\s*/, $_;
5121 12         83 $_ = [ $min, $max, $max - $min + 1 ];
5122 12         42 $total_width += $_->[2];
5123             }
5124              
5125             # XXX We need to use a secure source of random numbers here, otherwise
5126             # this is a little bit pointless.
5127 12         27 my $count = 100;
5128              
5129 12   66     125 until (defined $sock || --$count == 0)
5130             {
5131 12         421 my $n = int (rand $total_width);
5132 12         30 my $port;
5133 12         34 foreach (@ranges)
5134             {
5135 12 50       48 if ($n < $_->[2])
5136             {
5137 12         30 $port = $_->[0] + $n;
5138 12         28 last;
5139             }
5140 0         0 $n -= $_->[2];
5141             }
5142              
5143 12         76 "0" =~ /(0)/; # Perl 5.7 / IO::Socket::INET bug workaround.
5144             $sock = IO::Socket::INET->new
5145             (Listen => 1,
5146             LocalAddr => $self->{sockaddrstring},
5147 12         386 LocalPort => $port,
5148             Reuse => 1,
5149             Proto => "tcp",
5150             Type => SOCK_STREAM);
5151             }
5152             }
5153              
5154 12 50       5932 unless ($sock)
5155             {
5156             # Return a code 550 here, even though this is not in the RFC. XXX
5157 0         0 $self->reply (550, "Can't open a listening socket.");
5158 0         0 return;
5159             }
5160              
5161 12         39 $self->{_passive} = 1;
5162 12         89 $self->{_passive_sock} = $sock;
5163              
5164             # Get our port number.
5165 12         68 my $sockport = $sock->sockport;
5166              
5167             # Split the port number into high and low components.
5168 12         554 my $p1 = int ($sockport / 256);
5169 12         44 my $p2 = $sockport % 256;
5170              
5171 12 50       57 unless ($self->{_test_mode})
5172             {
5173 0         0 my $sockaddrstring = $self->{sockaddrstring};
5174              
5175             # We will need to revise this for IPv6 XXX
5176 0 0       0 die
5177             unless $sockaddrstring =~ /^([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$/;
5178              
5179             # Be very precise about this error message, since most clients
5180             # will have to parse the whole of it.
5181 0         0 $self->reply (227, "Entering Passive Mode ($1,$2,$3,$4,$p1,$p2)");
5182             }
5183             else
5184             {
5185             # Test mode: connect back to localhost.
5186 12         77 $self->reply (227, "Entering Passive Mode (127,0,0,1,$p1,$p2)");
5187             }
5188             }
5189              
5190             sub _TYPE_command
5191             {
5192 20     20   53 my $self = shift;
5193 20         55 my $cmd = shift;
5194 20         43 my $rest = shift;
5195              
5196             # See RFC 959 section 5.3.2.
5197 20 100       173 if ($rest =~ /^([AI])$/i)
    100          
    50          
5198             {
5199 17         75 $self->{type} = uc $1;
5200             }
5201             elsif ($rest =~ /^([AI])\sN$/i)
5202             {
5203 2         5 $self->{type} = uc $1;
5204             }
5205             elsif ($rest =~ /^L\s8$/i)
5206             {
5207 1         2 $self->{type} = 'L8';
5208             }
5209             else
5210             {
5211 0         0 $self->reply (504, "This server does not support TYPE $rest.");
5212 0         0 return;
5213             }
5214              
5215 20         112 $self->reply (200, "TYPE changed to $rest.");
5216             }
5217              
5218             sub _STRU_command
5219             {
5220 1     1   2 my $self = shift;
5221 1         2 my $cmd = shift;
5222 1         3 my $rest = shift;
5223              
5224             # See RFC 959 section 5.3.2.
5225             # Although this defies the RFC, I'm not going to support
5226             # record or page structure. TOPS-20 didn't really take off
5227             # as an operating system in the 90s ...
5228 1 50       4 if ($rest =~ /^F$/i)
5229             {
5230 1         3 $self->{stru} = 'F';
5231             }
5232             else
5233             {
5234 0         0 $self->reply (504, "This server does not support STRU $rest.");
5235 0         0 return;
5236             }
5237              
5238 1         5 $self->reply (200, "STRU changed to $rest.");
5239             }
5240              
5241             sub _MODE_command
5242             {
5243 1     1   2 my $self = shift;
5244 1         3 my $cmd = shift;
5245 1         2 my $rest = shift;
5246              
5247             # See RFC 959 section 5.3.2.
5248 1 50       4 if ($rest =~ /^S$/i)
5249             {
5250 1         3 $self->{mode} = 'S';
5251             }
5252             else
5253             {
5254 0         0 $self->reply (504, "This server does not support MODE $rest.");
5255 0         0 return;
5256             }
5257              
5258 1         6 $self->reply (200, "MODE changed to $rest.");
5259             }
5260              
5261             sub _RETR_command
5262             {
5263 52     52   111 my $self = shift;
5264 52         91 my $cmd = shift;
5265 52         136 my $rest = shift;
5266              
5267             # Find file by name.
5268 52         193 my ($dirh, $fileh, $filename) = $self->_get ($rest);
5269 52         124 my ($generator, @filters);
5270              
5271 52 100       173 unless ($fileh)
5272             {
5273             # No simple file by that name exists. Perhaps the user is
5274             # requesting an automatic archive download? You are not
5275             # expected to understand the following code unless you've
5276             # read doc/archives.txt.
5277              
5278             # Check archive mode is enabled.
5279 1 50       7 unless ($self->{archive_mode})
5280             {
5281 0         0 $self->reply (550, "File or directory not found.");
5282 0         0 return;
5283             }
5284              
5285             ARCHIVE_CHECK:
5286 1         4 for (;;)
5287             {
5288             # Matches filter extension?
5289 1         4 foreach (keys %{$self->{archive_filters}})
  1         8  
5290             {
5291 2 50       13 if (lc (substr ($rest, -length ($_))) eq lc ($_))
5292             {
5293 0         0 substr ($rest, -length ($_), length ($_), "");
5294 0         0 push @filters, $self->{archive_filters}{$_};
5295              
5296             # Does remainder of $rest correspond to a file?
5297 0         0 ($dirh, $fileh, $filename) = $self->_get ($rest);
5298              
5299 0 0       0 if ($fileh)
5300             {
5301 0         0 my ($mode) = $fileh->status;
5302              
5303 0 0       0 if ($mode eq "f")
5304             {
5305 0         0 last ARCHIVE_CHECK;
5306             }
5307             }
5308              
5309 0         0 next ARCHIVE_CHECK;
5310             }
5311             }
5312              
5313             # Matches directory + generator extension?
5314 1         3 foreach (keys %{$self->{archive_generators}})
  1         6  
5315             {
5316 1 50       9 if (lc (substr ($rest, -length ($_))) eq lc ($_))
5317             {
5318 1         5 my $tmp = substr ($rest, 0, -length ($_));
5319 1         5 my $tmp_gen = $self->{archive_generators}{$_};
5320              
5321 1         6 ($dirh, $fileh, $filename) = $self->_get ($tmp);
5322              
5323 1 50       7 if ($fileh)
5324             {
5325 1         9 my ($mode) = $fileh->status;
5326              
5327 1 50       8 if ($mode eq "d")
5328             {
5329 1         4 $rest = $tmp;
5330 1         2 $generator = $tmp_gen;
5331 1         6 last ARCHIVE_CHECK;
5332             }
5333             }
5334             }
5335             }
5336              
5337 0         0 $self->reply (550, "File or directory not found.");
5338 0         0 return;
5339             } # ARCHIVE_CHECK: for (;;)
5340             } # unless ($fileh)
5341              
5342             # Check access control.
5343 52 50       232 unless ($self->_eval_rule ("retrieve rule",
5344             $fileh->pathname, $filename, $dirh->pathname))
5345             {
5346 0         0 $self->reply (550, "RETR command denied by server configuration.");
5347 0         0 return;
5348             }
5349              
5350             # Check it's a simple file (unless we're using a generator to archive
5351             # a directory, in which case it's OK).
5352 52 100       144 unless ($generator)
5353             {
5354 51         206 my ($mode) = $fileh->status;
5355 51 50       194 unless ($mode eq "f")
5356             {
5357 0         0 $self->reply (550,
5358             "RETR command is only supported on plain files.");
5359 0         0 return;
5360             }
5361             }
5362              
5363             # Try to open the file.
5364 52 100       261 my $file = !$generator ? $fileh->open ("r") : &$generator ($self, $fileh);
5365              
5366 52 50       2155 unless ($file)
5367             {
5368 0         0 $self->reply (550, "File or directory not found.");
5369 0         0 return;
5370             }
5371              
5372             $self->reply (150,
5373             "Opening " .
5374 52 100       600 ($self->{type} eq 'A' ? "ASCII mode" : "BINARY mode") .
5375             " data connection for file $filename.");
5376              
5377             # Open a path back to the client.
5378 52         192 my $sock = $self->open_data_connection;
5379              
5380 52 50       186 unless ($sock)
5381             {
5382 0         0 $self->reply (425, "Can't open data connection.");
5383 0         0 return;
5384             }
5385              
5386             # If there are any filters to apply, do that now.
5387 52         106 my @filter_objects;
5388 52         126 foreach (@filters)
5389             {
5390 0         0 my $filter = &$_ ($self, $sock);
5391              
5392 0 0       0 unless ($filter)
5393             {
5394 0         0 $self->reply (500, "Can't open filter program in archive mode.");
5395 0         0 close $sock;
5396 0         0 $self->_cleanup_filters (@filter_objects);
5397 0         0 return;
5398             }
5399              
5400 0         0 unshift @filter_objects, $filter;
5401 0         0 $sock = $filter->{sock};
5402             }
5403              
5404             # Outgoing bandwidth
5405 52 50       143 $self->xfer_start ($fileh->pathname, "o") if $self->{_xferlog};
5406              
5407 52         82 my $transfer_hook;
5408              
5409             # What mode are we sending this file in?
5410 52 100       157 unless ($self->{type} eq 'A') # Binary type.
5411             {
5412 42         85 my ($r, $buffer, $n, $w);
5413              
5414             # Restart the connection from previous point?
5415 42 100       142 if ($self->{_restart})
5416             {
5417             # VFS seek method only required to support relative forward seeks
5418             #
5419             # In Perl = 5.00503, SEEK_CUR is exported by IO::Seekable,
5420             # in Perl >= 5.6, SEEK_CUR is exported by both IO::Seekable
5421             # and Fcntl. Hence we 'use IO::Seekable' at the top of the
5422             # file to get this symbol reliably in both cases.
5423 4         27 $file->sysseek ($self->{_restart}, SEEK_CUR);
5424 4         120 $self->{_restart} = 0;
5425             }
5426              
5427             # Copy data.
5428 42         180 while ($r = $file->sysread ($buffer, 65536))
5429             {
5430 73 50       2523 $self->xfer ($r) if $self->{_xferlog};
5431              
5432             # Restart alarm clock timer.
5433 73         316 alarm $self->{_idle_timeout};
5434              
5435 73 50       216 if ($transfer_hook
5436             = $self->transfer_hook ("r", $file, $sock, \$buffer))
5437             {
5438 0         0 close $sock;
5439 0         0 $file->close;
5440 0         0 $self->_cleanup_filters (@filter_objects);
5441 0         0 $self->reply (426,
5442             "File retrieval error: $transfer_hook",
5443             "Data connection has been closed.");
5444 0         0 return;
5445             }
5446              
5447 73         208 for ($n = 0; $n < $r; )
5448             {
5449             # $w = $sock->syswrite ($buffer, $r - $n, $n);
5450 73         3240 $w = syswrite $sock, $buffer, $r - $n, $n;
5451              
5452 73 50       267 unless (defined $w)
5453             {
5454             # There was an error.
5455 0         0 my $reason = $self->system_error_hook();
5456              
5457 0         0 close $sock;
5458 0         0 $file->close;
5459 0         0 $self->_cleanup_filters (@filter_objects);
5460 0         0 $self->reply (426,
5461             "File retrieval error: $reason",
5462             "Data connection has been closed.");
5463 0         0 return;
5464             }
5465              
5466 73         187 $n += $w;
5467             }
5468              
5469 73         226 $self->_check_signals;
5470              
5471             # Transfer aborted by client?
5472 73 100       302 if ($self->{_urgent})
5473             {
5474 1         13 close $sock;
5475 1         4 $file->close;
5476 1         11 $self->_cleanup_filters (@filter_objects);
5477 1         4 $self->reply (426, "Transfer aborted. Data connection closed.");
5478 1         2 $self->{_urgent} = 0;
5479 1         7 return;
5480             }
5481             }
5482              
5483 41 50       862 unless (defined $r)
5484             {
5485             # There was an error.
5486 0         0 my $reason = $self->system_error_hook();
5487              
5488 0         0 close $sock;
5489 0         0 $file->close;
5490 0         0 $self->_cleanup_filters (@filter_objects);
5491 0         0 $self->reply (426,
5492             "File retrieval error: $reason",
5493             "Data connection has been closed.");
5494 0         0 return;
5495             }
5496             }
5497             else # ASCII type.
5498             {
5499             # Restart the connection from previous point?
5500 10 100       33 if ($self->{_restart})
5501             {
5502 1         56 for (my $i = 0; $i < $self->{_restart}; ++$i)
5503             {
5504 33         466 $file->getc;
5505             }
5506 1         12 $self->{_restart} = 0;
5507             }
5508              
5509             # Copy data.
5510 10         60 while (defined ($_ = $file->getline))
5511             {
5512 4664 50       465139 $self->xfer (length $_) if $self->{_xferlog};
5513              
5514             # Remove any native line endings.
5515 4664         25640 s/[\n\r]+$//;
5516              
5517             # Restart alarm clock timer.
5518 4664         16483 alarm $self->{_idle_timeout};
5519              
5520 4664 50       10073 if ($transfer_hook = $self->transfer_hook ("r", $file, $sock, \$_))
5521             {
5522 0         0 close $sock;
5523 0         0 $file->close;
5524 0         0 $self->_cleanup_filters (@filter_objects);
5525 0         0 $self->reply (426,
5526             "File retrieval error: $transfer_hook",
5527             "Data connection has been closed.");
5528 0         0 return;
5529             }
5530              
5531 4664         10575 $self->_check_signals;
5532              
5533             # Write the line with telnet-format line endings.
5534 4664         14148 $sock->print ("$_\r\n");
5535 4664 100       165042 if ($self->{_urgent})
5536             {
5537 1         33 close $sock;
5538 1         8 $file->close;
5539 1         20 $self->_cleanup_filters (@filter_objects);
5540 1         6 $self->reply (426, "Transfer aborted. Data connection closed.");
5541 1         4 $self->{_urgent} = 0;
5542 1         18 return;
5543             }
5544             }
5545             }
5546              
5547 50 50 33     2179 unless (close ($sock) && $file->close)
5548             {
5549 0         0 my $reason = $self->system_error_hook();
5550 0         0 $self->reply (550, "File retrieval error: $reason");
5551 0         0 return;
5552             }
5553              
5554             # Clean up any outstanding filter objects.
5555 50         802 $self->_cleanup_filters (@filter_objects);
5556              
5557 50 50       153 $self->xfer_complete if $self->{_xferlog};
5558 50         157 $self->reply (226, "File retrieval complete. Data connection has been closed.");
5559             }
5560              
5561             sub _cleanup_filters
5562             {
5563 52     52   101 my $self = shift;
5564              
5565 52         194 foreach (@_)
5566             {
5567 0 0       0 if (exists $_->{pid})
5568             {
5569 0         0 waitpid $_->{pid}, 0;
5570             }
5571             }
5572             }
5573              
5574             sub _STOR_command
5575             {
5576 46     46   98 my $self = shift;
5577 46         77 my $cmd = shift;
5578 46         83 my $rest = shift;
5579              
5580 46         328 $self->_store ($rest);
5581             }
5582              
5583             sub _STOU_command
5584             {
5585 9     9   15 my $self = shift;
5586 9         20 my $cmd = shift;
5587 9         18 my $rest = shift;
5588              
5589 9         35 $self->_store ($rest, unique => 1);
5590             }
5591              
5592             sub _APPE_command
5593             {
5594 1     1   2 my $self = shift;
5595 1         2 my $cmd = shift;
5596 1         1 my $rest = shift;
5597              
5598 1         4 $self->_store ($rest, append => 1);
5599             }
5600              
5601             sub _ALLO_command
5602             {
5603 1     1   3 my $self = shift;
5604 1         3 my $cmd = shift;
5605 1         2 my $rest = shift;
5606              
5607             # RFC 959 Section 4.1.3: Treat this as a NOOP. Note that djb
5608             # recommends replying with 202 here [http://cr.yp.to/ftp/stor.html].
5609 1         4 $self->reply (200, "OK");
5610             }
5611              
5612             sub _REST_command
5613             {
5614 6     6   10 my $self = shift;
5615 6         9 my $cmd = shift;
5616 6         11 my $rest = shift;
5617              
5618 6 50       27 unless ($rest =~ /^([1-9][0-9]*|0)$/)
5619             {
5620 0         0 $self->reply (501, "REST command needs a numeric argument.");
5621 0         0 return;
5622             }
5623              
5624 6         21 $self->{_restart} = $1;
5625 6         23 $self->reply (350, "Restarting next transfer at $1.");
5626             }
5627              
5628             sub _RNFR_command
5629             {
5630 1     1   3 my $self = shift;
5631 1         3 my $cmd = shift;
5632 1         3 my $rest = shift;
5633              
5634 1         6 my ($dirh, $fileh, $filename) = $self->_get ($rest);
5635              
5636 1 50       9 unless ($fileh)
5637             {
5638 0         0 $self->reply (550, "File or directory not found.");
5639 0         0 return;
5640             }
5641              
5642             # Access control.
5643 1 50       5 unless ($self->_eval_rule ("rename rule",
5644             $dirh->pathname . $filename,
5645             $filename, $dirh->pathname))
5646             {
5647 0         0 $self->reply (550, "RNFR command denied by server configuration.");
5648 0         0 return;
5649             }
5650              
5651             # Store the file handle so we can complete the operation.
5652 1         4 $self->{_rename_fileh} = $fileh;
5653              
5654 1         6 $self->reply (350, "OK. Send RNTO command to complete rename operation.");
5655             }
5656              
5657             sub _RNTO_command
5658             {
5659 1     1   4 my $self = shift;
5660 1         3 my $cmd = shift;
5661 1         4 my $rest = shift;
5662              
5663             # Seen a previous RNFR command?
5664 1 50       10 unless ($self->{_rename_fileh})
5665             {
5666 0         0 $self->reply (503, "Send RNFR command first.");
5667 0         0 return;
5668             }
5669              
5670             # Get the directory name.
5671 1         5 my ($dirh, $fileh, $filename) = $self->_get ($rest);
5672              
5673 1 50       7 if (!$dirh)
5674             {
5675 0         0 $self->reply (550, "File or directory not found.");
5676 0         0 return;
5677             }
5678              
5679             # Access control.
5680 1 50       5 unless ($self->_eval_rule ("rename rule",
5681             $dirh->pathname . $filename,
5682             $filename, $dirh->pathname))
5683             {
5684 0         0 $self->reply (550, "RNTO command denied by server configuration.");
5685 0         0 return;
5686             }
5687              
5688             # Are we trying to overwrite a previously existing file?
5689 1 0 33     8 if (defined $fileh &&
      33        
5690             defined $self->config ("allow rename to overwrite") &&
5691             ! $self->config ("allow rename to overwrite"))
5692             {
5693 0         0 $self->reply (550, "Cannot rename file.");
5694 0         0 return;
5695             }
5696              
5697             # Attempt the rename operation.
5698 1 50       16 if ($self->{_rename_fileh}->move ($dirh, $filename) < 0)
5699             {
5700 0         0 $self->reply (550, "Cannot rename file.");
5701 0         0 return;
5702             }
5703              
5704 1         25 delete $self->{_rename_fileh};
5705              
5706 1         7 $self->reply (250, "File has been renamed.");
5707             }
5708              
5709             sub _ABOR_command
5710             {
5711 2     2   4 my $self = shift;
5712 2         3 my $cmd = shift;
5713 2         5 my $rest = shift;
5714              
5715 2         9 $self->reply (226, "Command aborted successfully.");
5716             }
5717              
5718             # Note that in the current implementation, DELE and RMD are synonyms.
5719             sub _DELE_command
5720             {
5721 9     9   17 my $self = shift;
5722 9         14 my $cmd = shift;
5723 9         14 my $rest = shift;
5724              
5725 9         22 my ($dirh, $fileh, $filename) = $self->_get ($rest);
5726              
5727 9 50       27 unless ($fileh)
5728             {
5729 0         0 $self->reply (550, "File or directory not found.");
5730 0         0 return;
5731             }
5732              
5733             # Check access control.
5734 9 50       27 unless ($self->_eval_rule ("delete rule",
5735             $fileh->pathname, $filename, $dirh->pathname))
5736             {
5737 0         0 $self->reply (550, "DELE command denied by server configuration.");
5738 0         0 return;
5739             }
5740              
5741             # Attempt to delete the file.
5742 9 50       35 if ($fileh->delete < 0)
5743             {
5744 0         0 $self->reply (550, "Cannot delete file.");
5745 0         0 return;
5746             }
5747              
5748 9         25 $self->reply (250, "File has been deleted.");
5749             }
5750              
5751             sub _RMD_command
5752             {
5753 1     1   3 my $self = shift;
5754 1         3 my $cmd = shift;
5755 1         2 my $rest = shift;
5756              
5757 1         9 my ($dirh, $fileh, $filename) = $self->_get ($rest);
5758              
5759 1 50       4 unless ($fileh)
5760             {
5761 0         0 $self->reply (550, "File or directory not found.");
5762 0         0 return;
5763             }
5764              
5765             # Check access control.
5766 1 50       4 unless ($self->_eval_rule ("delete rule",
5767             $fileh->pathname, $filename, $dirh->pathname))
5768             {
5769 0         0 $self->reply (550, "RMD command denied by server configuration.");
5770 0         0 return;
5771             }
5772              
5773             # Attempt to delete the file.
5774 1 50       6 if ($fileh->delete < 0)
5775             {
5776 0         0 $self->reply (550, "Cannot delete file.");
5777 0         0 return;
5778             }
5779              
5780 1         5 $self->reply (250, "File has been deleted.");
5781             }
5782              
5783             sub _MKD_command
5784             {
5785 11     11   25 my $self = shift;
5786 11         23 my $cmd = shift;
5787 11         23 my $rest = shift;
5788              
5789 11         61 my ($dirh, $fileh, $filename) = $self->_get ($rest);
5790              
5791 11 50       39 if (!$dirh)
5792             {
5793 0         0 $self->reply (550, "File or directory not found.");
5794 0         0 return;
5795             }
5796              
5797 11 50       27 if ($fileh)
5798             {
5799 0         0 $self->reply (550, "File or directory already exists.");
5800 0         0 return;
5801             }
5802              
5803             # Access control.
5804 11 50       59 unless ($self->_eval_rule ("mkdir rule",
5805             $dirh->pathname . $filename,
5806             $filename, $dirh->pathname))
5807             {
5808 0         0 $self->reply (550, "MKD command denied by server configuration.");
5809 0         0 return;
5810             }
5811              
5812             # Try to create a subdirectory with the appropriate filename.
5813 11 50       69 if ($dirh->mkdir ($filename) < 0)
5814             {
5815 0         0 $self->reply (550, "Could not create directory.");
5816 0         0 return;
5817             }
5818              
5819 11         41 $self->reply (250, "Directory has been created.");
5820             }
5821              
5822             sub _PWD_command
5823             {
5824 5     5   10 my $self = shift;
5825 5         7 my $cmd = shift;
5826 5         7 my $rest = shift;
5827              
5828             # See RFC 959 Appendix II and draft-ietf-ftpext-mlst-11.txt section 6.2.1.
5829 5         16 my $pathname = $self->{cwd}->pathname;
5830 5 100       33 $pathname =~ s,/+$,, unless $pathname eq "/";
5831 5         11 $pathname =~ tr,/,/,s;
5832              
5833 5         14 $self->reply (257, "\"$pathname\"");
5834             }
5835              
5836             sub _LIST_command
5837             {
5838 3     3   7 my $self = shift;
5839 3         6 my $cmd = shift;
5840 3         4 my $rest = shift;
5841              
5842             # This is something of a hack. Some clients expect a Unix server
5843             # to respond to flags on the 'ls command line'. Remove these flags
5844             # and ignore them. This is particularly an issue with ncftp 2.4.3.
5845 3         6 $rest =~ s/^-[a-zA-Z0-9]+\s?//;
5846              
5847 3         21 my ($dirh, $wildcard, $fileh, $filename)
5848             = $self->_list ($rest);
5849              
5850 3 50       8 unless ($dirh)
5851             {
5852 0         0 $self->reply (550, "File or directory not found.");
5853 0         0 return;
5854             }
5855              
5856             # Check access control.
5857 3 50       12 unless ($self->_eval_rule ("list rule",
5858             undef, undef, $dirh->pathname))
5859             {
5860 0         0 $self->reply (550, "LIST command denied by server configuration.");
5861 0         0 return;
5862             }
5863              
5864 3         17 $self->reply (150, "Opening data connection for file listing.");
5865              
5866             # Open a path back to the client.
5867 3         13 my $sock = $self->open_data_connection;
5868              
5869 3 50       11 unless ($sock)
5870             {
5871 0         0 $self->reply (425, "Can't open data connection.");
5872 0         0 return;
5873             }
5874              
5875             # Outgoing bandwidth
5876 3 50       16 $self->xfer_start ($dirh->pathname, "o") if $self->{_xferlog};
5877              
5878             # If the path ($rest) contains a directory name, extract it so that
5879             # we can prefix it to every filename listed. Thanks Rob Brown
5880             # for pointing this problem out.
5881 3 50 66     43 my $prefix = (($fileh || $wildcard) && $rest =~ /(.*\/).*/) ? $1 : "";
5882              
5883             # OK, we're either listing a full directory, listing a single
5884             # file or listing a wildcard.
5885 3 50       8 if ($fileh) # Single file in $dirh.
5886             {
5887 0         0 $self->_list_file ($sock, $fileh, $prefix . $filename);
5888             }
5889             else # Wildcard or full directory $dirh.
5890             {
5891 3 100       6 unless ($wildcard)
5892             {
5893             # Synthesize "total" field.
5894 1         2 my $header = "total 1\r\n";
5895 1         21 $self->xfer (length $header);
5896 1         12 $sock->print ($header);
5897             }
5898              
5899 3         69 my $r = $dirh->_list_status ($wildcard);
5900              
5901 3         9 foreach (@$r)
5902             {
5903 23         701 my $filename = $_->[0];
5904 23         32 my $handle = $_->[1];
5905 23         32 my $statusref = $_->[2];
5906              
5907 23         77 $self->_list_file ($sock, $handle, $prefix . $filename, $statusref);
5908             }
5909             }
5910              
5911 3 50       162 unless ($sock->close)
5912             {
5913 0         0 $self->reply (550, "Error closing data connection: $!");
5914 0         0 return;
5915             }
5916              
5917 3 50       158 $self->xfer_complete if $self->{_xferlog};
5918 3         12 $self->reply (226, "Listing complete. Data connection has been closed.");
5919             }
5920              
5921             sub _NLST_command
5922             {
5923 3     3   8 my $self = shift;
5924 3         5 my $cmd = shift;
5925 3         6 my $rest = shift;
5926              
5927             # This is something of a hack. Some clients expect a Unix server
5928             # to respond to flags on the 'ls command line'.
5929             # Handle the "-l" flag by just calling LIST instead of NLST.
5930             # This is particularly an issue with ncftp 2.4.3,
5931             # emacs / Ange-ftp, commandline "ftp" on Windows Platform,
5932             # netftp, and some old versions of WSFTP. I would think that if
5933             # the client wants a nice pretty listing, that they should use
5934             # the LIST command, but for some reasons they insist on trying
5935             # to pass arguments to NLST and expect them to work.
5936             # Examples:
5937             # NLST -al /.
5938             # NLST -AL *.htm
5939 3 50       10 return $self->_LIST_command ($cmd, $rest) if $rest =~ /^\-\w*l/i;
5940 3         4 $rest =~ s/^-\w+\s?//;
5941              
5942 3         13 my ($dirh, $wildcard, $fileh, $filename)
5943             = $self->_list ($rest);
5944              
5945 3 50       14 unless ($dirh)
5946             {
5947 0         0 $self->reply (550, "File or directory not found.");
5948 0         0 return;
5949             }
5950              
5951             # Check access control.
5952 3 50       17 unless ($self->_eval_rule ("list rule",
5953             undef, undef, $dirh->pathname))
5954             {
5955 0         0 $self->reply (550, "NLST command denied by server configuration.");
5956 0         0 return;
5957             }
5958              
5959 3         12 $self->reply (150, "Opening data connection for file listing.");
5960              
5961             # Open a path back to the client.
5962 3         12 my $sock = $self->open_data_connection;
5963              
5964 3 50       12 unless ($sock)
5965             {
5966 0         0 $self->reply (425, "Can't open data connection.");
5967 0         0 return;
5968             }
5969              
5970             # Outgoing bandwidth
5971 3 50       14 $self->xfer_start ($dirh->pathname, "o") if $self->{_xferlog};
5972              
5973             # If the path ($rest) contains a directory name, extract it so that
5974             # we can prefix it to every filename listed. Thanks Rob Brown
5975             # for pointing this problem out.
5976 3 50 66     48 my $prefix = (($fileh || $wildcard) && $rest =~ /(.*\/).*/) ? $1 : "";
5977              
5978             # OK, we're either listing a full directory, listing a single
5979             # file or listing a wildcard.
5980 3 50       14 if ($fileh) # Single file in $dirh.
5981             {
5982 0         0 $sock->print ($prefix . $filename, "\r\n");
5983             }
5984             else # Wildcard or full directory $dirh.
5985             {
5986 3         26 my $r = $dirh->list ($wildcard);
5987              
5988 3         10 foreach (@$r)
5989             {
5990 24         625 my $filename = $_->[0];
5991 24         41 my $handle = $_->[1]; # handle not used?
5992 24         41 my $line = "$prefix$filename\r\n";
5993 24         76 $self->xfer (length $line);
5994 24         64 $sock->print ($line);
5995             }
5996             }
5997              
5998 3 50       112 unless ($sock->close)
5999             {
6000 0         0 $self->reply (550, "Error closing data connection: $!");
6001 0         0 return;
6002             }
6003              
6004 3 50       160 $self->xfer_complete if $self->{_xferlog};
6005 3         14 $self->reply (226, "Listing complete. Data connection has been closed.");
6006             }
6007              
6008             sub _SITE_command
6009             {
6010 5     5   12 my $self = shift;
6011 5         11 my $cmd = shift;
6012 5         11 my $rest = shift;
6013              
6014             # Find the command.
6015             # See also RFC 2640 section 3.1.
6016             # "Brian Freeman" wants to be able to use
6017             # non-alpha characters in SITE command names. Fine by me as far as I can
6018             # tell.
6019 5 50       34 unless ($rest =~ /^(\S{3,})\s?(.*)/i)
6020             {
6021 0         0 $self->reply (501, "Syntax error in SITE command.");
6022 0         0 return;
6023             }
6024              
6025 5         20 ($cmd, $rest) = (uc $1, $2);
6026              
6027             # Find the appropriate command and run it.
6028 5 50       19 unless (exists $self->{site_command_table}{$cmd})
6029             {
6030 0         0 $self->reply (501, "Unknown SITE command.");
6031 0         0 return;
6032             }
6033              
6034 5         11 &{$self->{site_command_table}{$cmd}} ($self, $cmd, $rest);
  5         24  
6035             }
6036              
6037             sub _SITE_EXEC_command
6038             {
6039 0     0   0 my $self = shift;
6040 0         0 my $cmd = shift;
6041 0         0 my $rest = shift;
6042              
6043             # This command is DISABLED by default.
6044 0 0       0 unless ($self->config ("allow site exec command"))
6045             {
6046 0         0 $self->reply (502, "SITE EXEC is disabled at this site.");
6047 0         0 return;
6048             }
6049              
6050             # Don't allow this command for anonymous users.
6051 0 0       0 if ($self->{user_is_anonymous})
6052             {
6053 0         0 $self->reply (502, "SITE EXEC is not permitted for anonymous logins.");
6054 0         0 return;
6055             }
6056              
6057             # We trust everything the client sends us implicitly. Foolish? Probably.
6058 0 0       0 $rest = $1 if $rest =~ /(.*)/;
6059              
6060             # Run it and collect the output.
6061 0 0       0 unless (open OUTPUT, "$rest |")
6062             {
6063 0         0 $self->reply (451, "Error running command: $!");
6064 0         0 return;
6065             }
6066              
6067 0         0 my @result, ();
6068              
6069 0         0 while ()
6070             {
6071             # Remove trailing \n, \r.
6072 0         0 s/[\n\r]+$//;
6073              
6074 0         0 push @result, $_;
6075             }
6076              
6077 0         0 close OUTPUT;
6078              
6079             # Return the result to the client.
6080 0         0 $self->reply (200, "Result from command $rest:", @result);
6081             }
6082              
6083             sub _SITE_VERSION_command
6084             {
6085 4     4   6 my $self = shift;
6086 4         8 my $cmd = shift;
6087 4         5 my $rest = shift;
6088              
6089 4 50       9 my $enabled
6090             = defined $self->config ("allow site version command")
6091             ? $self->config ("allow site version command") : 1;
6092              
6093 4 50       10 unless ($enabled)
6094             {
6095 0         0 $self->reply (502, "SITE VERSION is disabled at this site.");
6096 0         0 return;
6097             }
6098              
6099             # Return the version string.
6100 4         12 $self->reply (200, $self->{version_string});
6101             }
6102              
6103             sub _SITE_ALIAS_command
6104             {
6105 0     0   0 my $self = shift;
6106 0         0 my $cmd = shift;
6107 0         0 my $rest = shift;
6108              
6109 0         0 my @aliases = $self->config ("alias");
6110              
6111             # List out all aliases?
6112 0 0       0 if ($rest eq "")
6113             {
6114 0         0 $self->reply (214,
6115             "The following aliases are defined:",
6116             @aliases,
6117             "End of alias list.");
6118 0         0 return;
6119             }
6120              
6121             # Find a particular alias.
6122 0         0 foreach (@aliases)
6123             {
6124 0         0 my ($name, $dir) = split /\s+/, $_;
6125 0 0       0 if ($name eq $rest)
6126             {
6127 0         0 $self->reply (214, "$name is an alias for $dir.");
6128 0         0 return;
6129             }
6130             }
6131              
6132             # No alias found.
6133 0         0 $self->reply (502,
6134             "Unknown alias $rest. Note that aliases are case sensitive.");
6135             }
6136              
6137             sub _SITE_CDPATH_command
6138             {
6139 0     0   0 my $self = shift;
6140 0         0 my $cmd = shift;
6141 0         0 my $rest = shift;
6142              
6143 0         0 my $cdpath = $self->config ("cdpath");
6144              
6145 0 0       0 unless (defined $cdpath)
6146             {
6147 0         0 $self->reply (502, "No CDPATH is defined in this server.");
6148 0         0 return;
6149             }
6150              
6151 0         0 my @cdpath = split /\s+/, $cdpath;
6152              
6153 0         0 $self->reply (214, "The current CDPATH is:", @cdpath, "End of CDPATH.");
6154             }
6155              
6156             sub _SITE_CHECKMETHOD_command
6157             {
6158 0     0   0 my $self = shift;
6159 0         0 my $cmd = shift;
6160 0         0 my $rest = shift;
6161              
6162 0         0 $rest = uc $rest;
6163              
6164 0 0       0 if ($rest eq "MD5")
    0          
6165             {
6166 0         0 $self->{_checksum_method} = $rest;
6167 0         0 $self->reply (200, "Checksum method is now: $rest");
6168             }
6169             elsif ($rest eq "")
6170             {
6171 0         0 $self->reply (200, "Checksum method is now: $self->{_checksum_method}");
6172             }
6173             else
6174             {
6175 0         0 $self->reply (500, "Unknown checksum method. I know about MD5.");
6176             }
6177             }
6178              
6179             sub _SITE_CHECKSUM_command
6180             {
6181 0     0   0 my $self = shift;
6182 0         0 my $cmd = shift;
6183 0         0 my $rest = shift;
6184              
6185 0 0       0 unless (exists $INC{"Digest/MD5.pm"})
6186             {
6187 0         0 $self->reply (500, "SITE CHECKSUM is not supported on this server.");
6188 0         0 return;
6189             }
6190              
6191 0         0 my ($dirh, $fileh, $filename) = $self->_get ($rest);
6192              
6193 0 0       0 unless ($fileh)
6194             {
6195 0         0 $self->reply (550, "File or directory not found.");
6196 0         0 return;
6197             }
6198              
6199 0         0 my ($mode) = $fileh->status;
6200              
6201 0 0       0 unless ($mode eq 'f')
6202             {
6203 0         0 $self->reply (550, "SITE CHECKSUM only works on plain files.");
6204 0         0 return;
6205             }
6206              
6207 0         0 my $file = $fileh->open ("r");
6208              
6209 0 0       0 unless ($file)
6210             {
6211 0         0 $self->reply (550, "File not found.");
6212 0         0 return;
6213             }
6214              
6215 0         0 my $ctx = "Digest::MD5"->new;
6216 0         0 $ctx->addfile ($file); # IO::Handles are also filehandle globs.
6217              
6218 0         0 $self->reply (200, $ctx->hexdigest . " " . $filename);
6219             }
6220              
6221             sub _SITE_IDLE_command
6222             {
6223 1     1   3 my $self = shift;
6224 1         2 my $cmd = shift;
6225 1         3 my $rest = shift;
6226              
6227 1 50       4 if ($rest eq "")
6228             {
6229 0         0 $self->reply (200, "Current idle timeout is $self->{_idle_timeout} seconds.");
6230 0         0 return;
6231             }
6232              
6233             # As with wu-ftpd, we only allow idle timeouts to be set between
6234             # 30 seconds and the current maximum set in the configuration file.
6235             # In test mode, allow the idle timeout to be set to as small as 1
6236             # second -- useful for testing without having to hang around.
6237 1 50       4 my $min_timeout = ! $self->{_test_mode} ? 30 : 1;
6238 1   33     5 my $max_timeout = $self->config ("timeout") || $_default_timeout;
6239              
6240 1 50 33     13 unless ($rest =~ /^[1-9][0-9]*$/ &&
      33        
6241             $rest >= $min_timeout && $rest <= $max_timeout)
6242             {
6243 0         0 $self->reply (500, "Idle timeout must be between $min_timeout and $max_timeout seconds.");
6244 0         0 return;
6245             }
6246              
6247 1         2 $self->{_idle_timeout} = $rest;
6248              
6249 1         5 $self->reply (200, "Current idle timeout set to $self->{_idle_timeout} seconds.");
6250             }
6251              
6252             sub _SITE_SYNC_command
6253             {
6254 0     0   0 my $self = shift;
6255 0         0 my $cmd = shift;
6256 0         0 my $rest = shift;
6257              
6258 0 0       0 unless (exists $INC{"File/Sync.pm"})
6259             {
6260 0         0 $self->reply (500, "Synchronization not available on this server.");
6261 0         0 return;
6262             }
6263              
6264 0         0 File::Sync::sync ();
6265              
6266 0         0 $self->reply (200, "Disks synchronized.");
6267             }
6268              
6269             sub _SITE_ARCHIVE_command
6270             {
6271 0     0   0 my $self = shift;
6272 0         0 my $cmd = shift;
6273 0         0 my $rest = shift;
6274              
6275 0 0 0     0 if (defined $self->config ("enable archive mode") &&
6276             !$self->config ("enable archive mode"))
6277             {
6278 0         0 $self->reply (500, "Archive mode is not enabled on this server.");
6279 0         0 return;
6280             }
6281              
6282 0 0       0 if (!$rest)
6283             {
6284             $self->reply (200,
6285             "Archive mode is ".
6286 0 0       0 ($self->{archive_mode} ? "ON" : "OFF"). ".");
6287 0         0 return;
6288             }
6289              
6290 0 0       0 if (uc ($rest) eq "ON")
6291             {
6292 0         0 $self->{archive_mode} = 1;
6293 0         0 $self->reply (200, "Archive mode turned ON.");
6294 0         0 return;
6295             }
6296              
6297 0 0       0 if (uc ($rest) eq "OFF")
6298             {
6299 0         0 $self->{archive_mode} = 0;
6300 0         0 $self->reply (200, "Archive mode turned OFF.");
6301 0         0 return;
6302             }
6303              
6304 0         0 $self->reply (500, "Usage: SITE ARCHIVE ON|OFF");
6305             }
6306              
6307             sub _SYST_command
6308             {
6309 1     1   3 my $self = shift;
6310 1         3 my $cmd = shift;
6311 1         3 my $rest = shift;
6312              
6313 1         3 $self->reply (215, "UNIX Type: L8");
6314             }
6315              
6316             sub _SIZE_command
6317             {
6318 0     0   0 my $self = shift;
6319 0         0 my $cmd = shift;
6320 0         0 my $rest = shift;
6321              
6322 0         0 my ($dirh, $fileh, $filename) = $self->_get ($rest);
6323              
6324 0 0       0 unless ($fileh)
6325             {
6326 0         0 $self->reply (550, "File or directory not found.");
6327 0         0 return;
6328             }
6329              
6330             # Get the mode, size etc. Remember to check the mode.
6331 0         0 my ($mode, $perms, $nlink, $user, $group, $size, $time)
6332             = $fileh->status;
6333              
6334 0 0       0 if ($mode ne "f")
6335             {
6336 0         0 $self->reply (550, "SIZE command is only supported on plain files.");
6337 0         0 return;
6338             }
6339              
6340 0 0       0 if ($self->{type} eq 'A')
6341             {
6342             # ASCII mode: we have to count the characters by hand.
6343 0 0       0 if (my $file = $fileh->open ("r"))
6344             {
6345 0         0 $size = 0;
6346 0         0 $size++ while (defined ($file->getc));
6347 0         0 $file->close;
6348             }
6349             }
6350              
6351 0         0 $self->reply (213, "$size");
6352             }
6353              
6354             sub _STAT_command
6355             {
6356 3     3   6 my $self = shift;
6357 3         6 my $cmd = shift;
6358 3         6 my $rest = shift;
6359              
6360             # STAT is a very strange command. It can either be used to show
6361             # general internal information about the server in a free format,
6362             # or else it can be used to list a directory over the control
6363             # connection. See RFC 959 Section 4.1.3.
6364              
6365 3 100       9 if ($rest eq "")
6366             {
6367             # Internal status.
6368 1         3 my %status = ();
6369              
6370 1 50 33     3 unless (defined $self->config ("allow site version command") &&
6371             ! $self->config ("allow site version command"))
6372             {
6373 1         3 $status{Version} = $self->{version_string};
6374             }
6375              
6376 1         4 $status{TYPE} = $self->{type};
6377 1         3 $status{MODE} = $self->{mode};
6378 1         4 $status{FORM} = $self->{form};
6379 1         2 $status{STRUcture} = $self->{stru};
6380              
6381 1         10 $status{"Data Connection"} = "None"; # XXX
6382              
6383 1 50 33     15 if ($self->{peeraddrstring} && $self->{peerport})
6384             {
6385 0         0 $status{Client} = "$self->{peeraddrstring}:$self->{peerport}";
6386             $status{Client} .= " ($self->{peerhostname}:$self->{peerport})"
6387 0 0       0 if $self->{peerhostname};
6388             }
6389              
6390 1 50       4 unless ($self->{user_is_anonymous})
6391             {
6392 1         4 $status{User} = $self->{user};
6393             }
6394             else
6395             {
6396 0         0 $status{User} = "anonymous";
6397             }
6398              
6399 1         10 my @status = map { $_ . ": " . $status{$_} } sort keys %status;
  7         19  
6400              
6401 1         4 $self->reply (211, "FTP server status:", @status, "End of status");
6402             }
6403             else
6404             {
6405             # Act like the LIST command.
6406 2         22 my ($dirh, $wildcard, $fileh, $filename)
6407             = $self->_list ($rest);
6408              
6409 2 100       10 unless ($dirh)
6410             {
6411 1         3 $self->reply (550, "File or directory not found.");
6412 1         4 return;
6413             }
6414              
6415 1         3 my @lines = ();
6416              
6417             # OK, we're either listing a full directory, listing a single
6418             # file or listing a wildcard.
6419 1 50       6 if ($fileh) # Single file in $dirh.
6420             {
6421 0         0 push @lines, $filename;
6422             }
6423             else # Wildcard or full directory $dirh.
6424             {
6425 1         10 my $r = $dirh->list_status ($wildcard);
6426              
6427 1         4 foreach (@$r)
6428             {
6429 0         0 my $filename = $_->[0];
6430              
6431 0         0 push @lines, $filename;
6432             }
6433             }
6434              
6435             # Send them back to the client.
6436 1         6 $self->reply (213, "Status of $rest:", @lines, "End of status");
6437             }
6438             }
6439              
6440             sub _HELP_command
6441             {
6442 2     2   6 my $self = shift;
6443 2         6 my $cmd = shift;
6444 2         4 my $rest = shift;
6445              
6446 2         5 my @version_info = ();
6447              
6448             # Dan Bernstein recommends sending the server version info here.
6449 2 50 33     9 unless (defined $self->config ("allow site version command") &&
6450             ! $self->config ("allow site version command"))
6451             {
6452 2         6 @version_info = ( $self->{version_string} );
6453             }
6454              
6455             # Without any arguments, return a list of commands supported.
6456 2 100       8 if ($rest eq "")
    50          
6457             {
6458 1         3 my @lines = _format_list (sort keys %{$self->{command_table}});
  1         51  
6459              
6460 1         6 $self->reply (214,
6461             @version_info,
6462             "The following commands are recognized:",
6463             @lines,
6464             "You can also use HELP SITE to list site specific commands.");
6465             }
6466             # HELP SITE.
6467             elsif (uc $rest eq "SITE")
6468             {
6469 1         2 my @lines = _format_list (sort keys %{$self->{site_command_table}});
  1         10  
6470              
6471 1         4 $self->reply (214,
6472             @version_info,
6473             "The following commands are recognized:",
6474             @lines,
6475             "You can also use HELP to list general commands.");
6476             }
6477             # No other form of HELP available right now.
6478             else
6479             {
6480 0         0 $self->reply (214,
6481             "No command-specific help is available right now. Use HELP or HELP SITE.");
6482             }
6483             }
6484              
6485             sub _format_list
6486             {
6487 2     2   7 my @lines = ();
6488 2         5 my ($r, $c);
6489 2         29 my $rows = int (ceil (@_ / 4.));
6490              
6491 2         8 for ($r = 0; $r < $rows; ++$r)
6492             {
6493 17         39 my @r = ();
6494              
6495 17         28 for ($c = 0; $c < 4; ++$c)
6496             {
6497 68         75 my $n = $c * $rows + $r;
6498              
6499 68 100       141 push @r, $_[$n] if $n < @_;
6500             }
6501              
6502 17         61 push @lines, "\t" . join "\t", @r;
6503             }
6504              
6505 2         8 return @lines;
6506             }
6507              
6508             sub _NOOP_command
6509             {
6510 1     1   3 my $self = shift;
6511 1         2 my $cmd = shift;
6512 1         2 my $rest = shift;
6513              
6514 1         4 $self->reply (200, "OK");
6515             }
6516              
6517             sub _XMKD_command
6518             {
6519 0     0   0 return shift->_MKD_command (@_);
6520             }
6521              
6522             sub _XRMD_command
6523             {
6524 0     0   0 return shift->_RMD_command (@_);
6525             }
6526              
6527             sub _XPWD_command
6528             {
6529 0     0   0 return shift->_PWD_command (@_);
6530             }
6531              
6532             sub _XCUP_command
6533             {
6534 0     0   0 return shift->_CDUP_command (@_);
6535             }
6536              
6537             sub _XCWD_command
6538             {
6539 0     0   0 return shift->_CWD_command (@_);
6540             }
6541              
6542             sub _FEAT_command
6543             {
6544 0     0   0 my $self = shift;
6545 0         0 my $cmd = shift;
6546 0         0 my $rest = shift;
6547              
6548 0 0       0 if ($rest ne "")
6549             {
6550 0         0 $self->reply (501, "Unexpected parameters to FEAT command.");
6551 0         0 return;
6552             }
6553              
6554             # Print out the extensions supported. Don't use $self->reply, since
6555             # it doesn't have the exact guaranteed behaviour (it instead immitates
6556             # wu-ftpd by putting the server code in each line).
6557             #
6558             # See RFC 2389 section 3.2.
6559 0         0 print "211-Extensions supported:\r\n";
6560              
6561 0         0 foreach (sort keys %{$self->{features}})
  0         0  
6562             {
6563 0 0       0 unless ($self->{features}{$_})
6564             {
6565 0         0 print " $_\r\n";
6566             }
6567             else
6568             {
6569 0         0 print " $_ ", $self->{features}{$_}, "\r\n";
6570             }
6571             }
6572              
6573 0         0 print "211 END\r\n";
6574             }
6575              
6576             sub _OPTS_command
6577             {
6578 0     0   0 my $self = shift;
6579 0         0 my $cmd = shift;
6580 0         0 my $rest = shift;
6581              
6582             # RFC 2389 section 4.
6583             # See also RFC 2640 section 3.1.
6584 0 0       0 unless ($rest =~ /^([A-Z]{3,4})\s?(.*)/i)
6585             {
6586 0         0 $self->reply (501, "Syntax error in OPTS command.");
6587 0         0 return;
6588             }
6589              
6590 0         0 ($cmd, $rest) = (uc $1, $2);
6591              
6592             # Find the appropriate command.
6593 0 0       0 unless (exists $self->{options}{$cmd})
6594             {
6595 0         0 $self->reply (501, "Command has no settable options.");
6596 0         0 return;
6597             }
6598              
6599             # The command should print either a 200 or a 451 reply.
6600 0         0 &{$self->{options}{$cmd}} ($self, $cmd, $rest);
  0         0  
6601             }
6602              
6603             sub _MSAM_command
6604             {
6605 1     1   4 my $self = shift;
6606 1         2 my $cmd = shift;
6607 1         3 my $rest = shift;
6608              
6609 1         3 $self->reply (502, "Obsolete RFC 765 mail commands not implemented.");
6610             }
6611              
6612             sub _MRSQ_command
6613             {
6614 1     1   3 my $self = shift;
6615 1         2 my $cmd = shift;
6616 1         3 my $rest = shift;
6617              
6618 1         3 $self->reply (502, "Obsolete RFC 765 mail commands not implemented.");
6619             }
6620              
6621             sub _MLFL_command
6622             {
6623 1     1   4 my $self = shift;
6624 1         5 my $cmd = shift;
6625 1         4 my $rest = shift;
6626              
6627 1         5 $self->reply (502, "Obsolete RFC 765 mail commands not implemented.");
6628             }
6629              
6630             sub _MRCP_command
6631             {
6632 1     1   4 my $self = shift;
6633 1         2 my $cmd = shift;
6634 1         2 my $rest = shift;
6635              
6636 1         4 $self->reply (502, "Obsolete RFC 765 mail commands not implemented.");
6637             }
6638              
6639             sub _MAIL_command
6640             {
6641 1     1   3 my $self = shift;
6642 1         2 my $cmd = shift;
6643 1         2 my $rest = shift;
6644              
6645 1         5 $self->reply (502, "Obsolete RFC 765 mail commands not implemented.");
6646             }
6647              
6648             sub _MSND_command
6649             {
6650 1     1   2 my $self = shift;
6651 1         3 my $cmd = shift;
6652 1         2 my $rest = shift;
6653              
6654 1         3 $self->reply (502, "Obsolete RFC 765 mail commands not implemented.");
6655             }
6656              
6657             sub _MSOM_command
6658             {
6659 1     1   2 my $self = shift;
6660 1         3 my $cmd = shift;
6661 1         2 my $rest = shift;
6662              
6663 1         3 $self->reply (502, "Obsolete RFC 765 mail commands not implemented.");
6664             }
6665              
6666             sub _LANG_command
6667             {
6668 3     3   5 my $self = shift;
6669 3         4 my $cmd = shift;
6670 3         4 my $rest = shift;
6671              
6672             # The beginnings of language support.
6673             #
6674             # XXX To complete language support we need to implement the FEAT
6675             # command for language properly, put gettext around all strings
6676             # and also arrange for strings to be translated. See RFC 2640.
6677              
6678             # If no argument, then we want to find the current language.
6679 3 100       8 if ($rest eq "")
6680             {
6681 2   100     16 my $lang = $ENV{LANGUAGE} || "en";
6682 2         11 $self->reply (200, "Language is $lang.");
6683 2         5 return;
6684             }
6685              
6686             # We limit the whole tag to 8 chars since (a) it's highly unlikely
6687             # that any genuine language code would be longer than this and
6688             # (b) there are all sorts of possible libc exploits available if
6689             # the user is allowed to set this to arbitrary values.
6690 1 50 33     12 unless (length ($rest) <= 8 &&
6691             $rest =~ /^[A-Z]{1,8}(-[A-Z]{1-8})*$/i)
6692             {
6693 0         0 $self->reply (504, "Incorrect language.");
6694 0         0 return;
6695             }
6696              
6697 1         14 $ENV{LANGUAGE} = $rest;
6698 1         9 $self->reply (200, "Language changed to $rest.");
6699             }
6700              
6701             sub _CLNT_command
6702             {
6703 1     1   3 my $self = shift;
6704 1         3 my $cmd = shift;
6705 1         3 my $rest = shift;
6706              
6707             # NcFTP sends the CLNT command. I don't know what RFC this
6708             # comes from.
6709 1         6 $self->reply (200, "Hello $rest.");
6710             }
6711              
6712             sub _MDTM_command
6713             {
6714 0     0   0 my $self = shift;
6715 0         0 my $cmd = shift;
6716 0         0 my $rest = shift;
6717              
6718 0         0 my ($dirh, $fileh, $filename) = $self->_get ($rest);
6719              
6720 0 0       0 unless ($fileh)
6721             {
6722 0         0 $self->reply (550, "File or directory not found.");
6723 0         0 return;
6724             }
6725              
6726             # Get the status.
6727 0         0 my ($mode, $perms, $nlink, $user, $group, $size, $time)
6728             = $fileh->status;
6729              
6730             # Format the modification time. See draft-ietf-ftpext-mlst-11.txt
6731             # sections 2.3 and 3.1.
6732 0         0 my $fmt_time = strftime "%Y%m%d%H%M%S", gmtime ($time);
6733              
6734 0         0 $self->reply (213, $fmt_time);
6735             }
6736              
6737             sub _MLST_command
6738             {
6739 0     0   0 my $self = shift;
6740 0         0 my $cmd = shift;
6741 0         0 my $rest = shift;
6742              
6743             # If not file name is given, then we need to return
6744             # status on the current directory. Else we return
6745             # status on the file or directory name given.
6746 0         0 my $fileh;
6747 0         0 my $dirh = $self->{cwd};
6748 0         0 my $filename = ".";
6749              
6750 0 0       0 if ($rest ne "")
6751             {
6752 0         0 ($dirh, $fileh, $filename) = $self->_get ($rest);
6753              
6754 0 0       0 unless ($fileh)
6755             {
6756 0         0 $self->reply (550, "File or directory not found.");
6757 0         0 return;
6758             }
6759             }
6760              
6761             # Check access control.
6762 0 0       0 unless ($self->_eval_rule ("list rule",
6763             undef, undef, $fileh->pathname))
6764             {
6765 0         0 $self->reply (550, "LIST command denied by server configuration.");
6766 0         0 return;
6767             }
6768              
6769             # Get the status.
6770 0         0 my ($mode, $perms, $nlink, $user, $group, $size, $time)
6771             = $fileh->status;
6772              
6773             # Return the requested information over the control connection.
6774 0         0 my $info = $self->_mlst_format ($filename, $fileh, $dirh);
6775              
6776             # Can't use $self->reply since it produces the wrong format.
6777 0         0 print "250-Listing of $filename:\r\n";
6778 0         0 print " ", $info, "\r\n";
6779 0         0 print "250 End of listing.\r\n";
6780             }
6781              
6782             sub _MLSD_command
6783             {
6784 0     0   0 my $self = shift;
6785 0         0 my $cmd = shift;
6786 0         0 my $rest = shift;
6787              
6788             # XXX Note that this is slightly wrong. According to the Internet
6789             # Draft we shouldn't handle wildcards in the MLST or MLSD commands.
6790 0         0 my ($dirh, $wildcard, $fileh, $filename)
6791             = $self->_list ($rest);
6792              
6793 0 0       0 unless ($dirh)
6794             {
6795 0         0 $self->reply (550, "File or directory not found.");
6796 0         0 return;
6797             }
6798              
6799             # Check access control.
6800 0 0       0 unless ($self->_eval_rule ("list rule",
6801             undef, undef, $dirh->pathname))
6802             {
6803 0         0 $self->reply (550, "MLSD command denied by server configuration.");
6804 0         0 return;
6805             }
6806              
6807 0         0 $self->reply (150, "Opening data connection for file listing.");
6808              
6809             # Open a path back to the client.
6810 0         0 my $sock = $self->open_data_connection;
6811              
6812 0 0       0 unless ($sock)
6813             {
6814 0         0 $self->reply (425, "Can't open data connection.");
6815 0         0 return;
6816             }
6817              
6818             # Outgoing bandwidth
6819 0 0       0 $self->xfer_start ($dirh->pathname, "o") if $self->{_xferlog};
6820              
6821             # OK, we're either listing a full directory, listing a single
6822             # file or listing a wildcard.
6823 0 0       0 if ($fileh) # Single file in $dirh.
6824             {
6825             # Do not bother logging xfer of the status of one file
6826 0         0 $sock->print ($self->_mlst_format ($filename, $fileh, $dirh), "\r\n");
6827             }
6828             else # Wildcard or full directory $dirh.
6829             {
6830 0         0 my $r = $dirh->list_status ($wildcard);
6831              
6832 0         0 foreach (@$r)
6833             {
6834 0         0 my $filename = $_->[0];
6835 0         0 my $handle = $_->[1];
6836 0         0 my $statusref = $_->[2];
6837 0         0 my $line = $self->_mlst_format ($filename,
6838             $handle, $dirh, $statusref).
6839             "\r\n";
6840 0 0       0 $self->xfer (length $line) if $self->{_xferlog};
6841 0         0 $sock->print ($line);
6842             }
6843             }
6844              
6845 0 0       0 unless ($sock->close)
6846             {
6847 0         0 $self->reply (550, "Error closing data connection: $!");
6848 0         0 return;
6849             }
6850              
6851 0 0       0 $self->xfer_complete if $self->{_xferlog};
6852 0         0 $self->reply (226, "Listing complete. Data connection has been closed.");
6853             }
6854              
6855             sub _OPTS_MLST_command
6856             {
6857 0     0   0 my $self = shift;
6858 0         0 my $cmd = shift;
6859 0         0 my $rest = shift;
6860              
6861             # Break up the list of facts.
6862 0         0 my @facts = split /;/, $rest;
6863              
6864 0         0 $self->{_mlst_facts} = [];
6865              
6866             # Check that all the facts asked for are supported.
6867 0         0 foreach (@facts)
6868             {
6869 0         0 $_ = uc;
6870              
6871 0 0       0 if ($_ ne "")
6872             {
6873 0 0       0 if ($self->_is_supported_mlst_fact ($_))
6874             {
6875 0         0 push @{$self->{_mlst_facts}}, $_;
  0         0  
6876             }
6877             }
6878             }
6879              
6880             # Return the list of facts enabled.
6881             $self->reply (200,
6882             "MLST OPTS " .
6883             join ("",
6884 0         0 map { "$_;" } @{$self->{_mlst_facts}}));
  0         0  
  0         0  
6885              
6886             # Update the FEAT list.
6887 0         0 $self->{features}{MLST} = $self->_mlst_features;
6888             }
6889              
6890             sub _is_supported_mlst_fact
6891             {
6892 0     0   0 my $self = shift;
6893 0         0 my $fact = shift;
6894              
6895 0         0 foreach my $supported_fact (@_supported_mlst_facts)
6896             {
6897 0 0       0 return 1 if $fact eq $supported_fact;
6898             }
6899              
6900 0         0 return 0;
6901             }
6902              
6903             sub _mlst_features
6904             {
6905 0     0   0 my $self = shift;
6906 0         0 my $out = "";
6907              
6908 0         0 foreach my $supported_fact (@_supported_mlst_facts)
6909             {
6910 0 0       0 if ($self->_is_enabled_fact ($supported_fact)) {
6911 0         0 $out .= "$supported_fact*;"
6912             } else {
6913 0         0 $out .= "$supported_fact;"
6914             }
6915             }
6916              
6917 0         0 return $out;
6918             }
6919              
6920             sub _is_enabled_fact
6921             {
6922 0     0   0 my $self = shift;
6923 0         0 my $fact = shift;
6924              
6925 0         0 foreach my $enabled_fact (@{$self->{_mlst_facts}})
  0         0  
6926             {
6927 0 0       0 return 1 if $fact eq $enabled_fact;
6928             }
6929 0         0 return 0;
6930             }
6931              
6932 75     75   1026 use vars qw(%_mode_to_mlst_unix_type);
  75         186  
  75         202903  
6933              
6934             # XXX I made these up. Is there a list anywhere?
6935             %_mode_to_mlst_unix_type = (
6936             l => "LINK",
6937             p => "PIPE",
6938             s => "SOCKET",
6939             b => "BLOCK",
6940             c => "CHAR",
6941             );
6942              
6943             sub _mlst_format
6944             {
6945 0     0   0 my $self = shift;
6946 0         0 my $filename = shift;
6947 0         0 my $fileh = shift;
6948 0         0 my $dirh = shift;
6949 0         0 my $statusref = shift;
6950 0         0 local $_;
6951              
6952             # Get the status information.
6953 0         0 my @status;
6954 0 0       0 if ($statusref) { @status = @$statusref }
  0         0  
6955 0         0 else { @status = $fileh->status }
6956              
6957             # Break out the fields of the status information.
6958 0         0 my ($mode, $perms, $nlink, $user, $group, $size, $mtime) = @status;
6959              
6960             # Get the directory status information.
6961 0         0 my ($dir_mode, $dir_perms) = ('d', $perms);
6962 0 0       0 ($dir_mode, $dir_perms) = $dirh->status if $dirh;
6963              
6964             # Return the requested facts.
6965 0         0 my @facts = ();
6966              
6967 0         0 foreach (@{$self->{_mlst_facts}})
  0         0  
6968             {
6969 0 0       0 if ($_ eq "TYPE")
    0          
    0          
    0          
    0          
6970             {
6971 0 0       0 if ($mode eq "f") {
    0          
6972 0         0 push @facts, "$_=file";
6973             } elsif ($mode eq "d") {
6974 0 0       0 if ($filename eq ".") {
    0          
6975 0         0 push @facts, "$_=cdir";
6976             } elsif ($filename eq "..") {
6977 0         0 push @facts, "$_=pdir";
6978             } else {
6979 0         0 push @facts, "$_=dir";
6980             }
6981             } else {
6982 0         0 push @facts, "$_=OS.UNIX=$_mode_to_mlst_unix_type{$mode}";
6983             }
6984             }
6985             elsif ($_ eq "SIZE")
6986             {
6987 0         0 push @facts, "$_=$size";
6988             }
6989             elsif ($_ eq "MODIFY")
6990             {
6991 0         0 my $fmt_time = strftime "%Y%m%d%H%M%S", localtime ($mtime);
6992 0         0 push @facts, "$_=$fmt_time";
6993             }
6994             elsif ($_ eq "PERM")
6995             {
6996 0 0       0 if ($mode eq "f")
    0          
6997             {
6998 0 0       0 push @facts,
    0          
    0          
    0          
    0          
6999             "$_=" . ($perms & 0400 ? "r" : "") . # read
7000             ($perms & 0200 ? "w" : "") . # write
7001             ($perms & 0200 ? "a" : "") . # append
7002             ($dir_perms & 0200 ? "f" : "") . # rename
7003             ($dir_perms & 0200 ? "d" : ""); # delete
7004             }
7005             elsif ($mode eq "d")
7006             {
7007 0 0       0 push @facts,
    0          
    0          
    0          
    0          
    0          
7008             "$_=" . ($perms & 0200 ? "c" : "") . # write
7009             ($dir_perms & 0200 ? "d" : "") . # delete
7010             ($perms & 0100 ? "e" : "") . # enter
7011             ($perms & 0500 ? "l" : "") . # list
7012             ($dir_perms & 0200 ? "f" : "") . # rename
7013             ($perms & 0200 ? "m" : ""); # mkdir
7014             }
7015             else
7016             {
7017             # Pipes, block specials, etc.
7018 0 0       0 push @facts,
    0          
    0          
    0          
7019             "$_=" . ($perms & 0400 ? "r" : "") . # read
7020             ($perms & 0200 ? "w" : "") . # write
7021             ($dir_perms & 0200 ? "f" : "") . # rename
7022             ($dir_perms & 0200 ? "d" : ""); # delete
7023             }
7024             }
7025             elsif ($_ eq "UNIX.MODE")
7026             {
7027 0 0       0 my $unix_mode = sprintf ("%s%s%s%s%s%s%s%s%s",
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
7028             ($perms & 0400 ? 'r' : '-'),
7029             ($perms & 0200 ? 'w' : '-'),
7030             ($perms & 0100 ? 'x' : '-'),
7031             ($perms & 040 ? 'r' : '-'),
7032             ($perms & 020 ? 'w' : '-'),
7033             ($perms & 010 ? 'x' : '-'),
7034             ($perms & 04 ? 'r' : '-'),
7035             ($perms & 02 ? 'w' : '-'),
7036             ($perms & 01 ? 'x' : '-'));
7037 0         0 push @facts, "$_=$unix_mode";
7038             }
7039             else
7040             {
7041 0         0 die "unknown MLST fact: $_";
7042             }
7043             }
7044              
7045             # Return the facts to the user in a string.
7046 0         0 return join (";", @facts) . "; " . $filename;
7047             }
7048              
7049             # Routine: xfer_start
7050             # Purpose: Initialize the beginning of a transfer.
7051             # PreCond:
7052             # Takes full pathname and direction as arguments.
7053             # _xferlog should be set to a writeable file handle.
7054             # Should not already have xfer_start'ed a transfer
7055             # or already finished it with a xfer_flush call.
7056             sub xfer_start
7057             {
7058 0     0 0 0 my $self = shift;
7059             # If old data still exists, write to log
7060             # (This should not happen.)
7061 0 0       0 $self->xfer_flush if $self->{xfer};
7062             $self->{xfer} = {
7063 0         0 status => "i", # Default to incomplete transfer status
7064             start => time, # Started right now
7065             bytes => 0, # Nothing transferred yet
7066             path => shift,
7067             direct => shift,
7068             };
7069             }
7070              
7071             # Routine: xfer
7072             # Purpose: Log transfer chunk.
7073             # PreCond:
7074             # Takes the number of bytes just transferring.
7075             # Should have called xfer_start first.
7076             sub xfer
7077             {
7078 25     25 1 38 my $self = shift;
7079 25 50       58 return unless $self->{xfer};
7080 0         0 $self->{xfer}->{bytes} += shift;
7081             }
7082              
7083             # Routine: xfer_complete
7084             # Purpose: Mark that the transfer completed successfully.
7085             # PreCond:
7086             # Should have called xfer_start first.
7087             sub xfer_complete
7088             {
7089 0     0 0 0 my $self = shift;
7090 0 0       0 return unless $self->{xfer};
7091 0         0 $self->{xfer}->{status} = 'c';
7092 0         0 $self->xfer_flush;
7093             }
7094              
7095             # Routine: xfer_flush
7096             # Purpose: Write to the xferlog and clean up.
7097             # PreCond:
7098             # Should have called xfer_start first.
7099             sub xfer_flush
7100             {
7101 0     0 0 0 my $self = shift;
7102             # If no xfer ref, then it's already flushed
7103 0 0       0 my $xfer = $self->{xfer} or return;
7104 0 0       0 return unless $self->{_xferlog};
7105              
7106             # Wipe xfer ref to signify that it's flushed
7107 0         0 delete $self->{xfer};
7108              
7109             # Never log if zero bytes transferred
7110 0 0       0 return unless $xfer->{bytes};
7111              
7112             # Send information in the right format
7113             $self->{_xferlog}->print
7114             (join " ",
7115             scalar(localtime($xfer->{start})), # current-time
7116             (time() - $xfer->{start}), # transfer-time
7117             ($self->{peerhostname} || $self->{peeraddrstring}), # remote-host
7118             $xfer->{bytes}, # file-size
7119             $xfer->{path}, # filename
7120             ($self->{type} eq 'A' ? "a" : "b"), # transfer-type
7121             "_", # Compression not implemented? # special-action-flag
7122             $xfer->{direct}, # direction
7123             ($self->{user_is_anonymous} ? "a" : "r"), # access-mode
7124             $self->{user}, # username
7125 0 0 0     0 "ftp", # service-name
    0          
7126             "0", # RFC931 stuff? # authentication-method
7127             "*", # RFC931 stuff? # authenticated-user-id
7128             "$xfer->{status}". # completion-status
7129             "\n");
7130 0         0 return;
7131             }
7132              
7133              
7134             # Evaluate an access control rule from the configuration file.
7135              
7136             sub _eval_rule
7137             {
7138 213     213   498 my $self = shift;
7139 213         606 my $rulename = shift;
7140 213         368 my $pathname = shift;
7141 213         407 my $filename = shift;
7142 213         745 my $dirname = shift;
7143              
7144 213 50       638 my $rule
7145             = defined $self->config ($rulename) ? $self->config ($rulename) : "1";
7146              
7147             # Set up the variables.
7148 213         473 my $hostname = $self->{peerhostname};
7149 213         440 my $ip = $self->{peeraddrstring};
7150 213         459 my $user = $self->{user};
7151 213         413 my $class = $self->{class};
7152 213         404 my $user_is_anonymous = $self->{user_is_anonymous};
7153 213         399 my $type = $self->{type};
7154 213         398 my $form = $self->{form};
7155 213         386 my $mode = $self->{mode};
7156 213         368 my $stru = $self->{stru};
7157              
7158 213         11247 my $rv = eval $rule;
7159 213 50       919 die if $@;
7160              
7161 213         827 return $rv;
7162             }
7163              
7164             # Move from one directory to another. Return the new directory handle.
7165              
7166             sub _chdir
7167             {
7168 32     32   74 my $self = shift;
7169 32         57 my $dirh = shift;
7170 32         67 my $path = shift;
7171 32         63 local $_;
7172              
7173             # If the path starts with a "/" then it's an absolute path.
7174 32 100       137 if (substr ($path, 0, 1) eq "/")
7175             {
7176 24         91 $dirh = $self->root_directory_hook;
7177 24         356 $path =~ s,^/+,,;
7178             }
7179              
7180             # Split the path into its component parts and process each separately.
7181 32         151 my @elems = split /\//, $path;
7182              
7183 32         111 foreach (@elems)
7184             {
7185 8 50 33     90 if ($_ eq "" || $_ eq ".") { next } # Ignore these.
  0 100       0  
7186             elsif ($_ eq "..")
7187             {
7188             # Go to parent directory.
7189 2         8 $dirh = $dirh->parent;
7190             }
7191             else
7192             {
7193             # Go into subdirectory, if it exists.
7194 6         27 $dirh = $dirh->get ($_);
7195              
7196             return undef
7197 6 100 66     77 unless $dirh && $dirh->isa ("Net::FTPServer::DirHandle");
7198             }
7199             }
7200              
7201 31         157 return $dirh;
7202             }
7203              
7204             # The list command understands the following forms for $path:
7205             #
7206             # <> List current directory.
7207             # file List single file in cwd.
7208             # wildcard List files by wildcard in cwd.
7209             # path/to/dir List contents of directory, relative to cwd.
7210             # /path/to/dir List contents of directory, absolute.
7211             # path/to/file List single file, relative to cwd.
7212             # /path/to/file List single file, absolute.
7213             # path/to/wildcard List files by wildcard, relative to cwd.
7214             # /path/to/wildcard List files by wildcard, absolute.
7215              
7216             sub _list
7217             {
7218 8     8   17 my $self = shift;
7219 8         13 my $path = shift;
7220              
7221 8         18 my $dirh = $self->{cwd};
7222              
7223             # Absolute path?
7224 8 50       36 if (substr ($path, 0, 1) eq "/")
7225             {
7226 0         0 $dirh = $self->root_directory_hook;
7227 0         0 $path =~ s,^/+,,;
7228             }
7229              
7230             # Parse the first elements of the path until we find the appropriate
7231             # working directory.
7232 8         26 my @elems = split /\//, $path;
7233 8         16 my ($wildcard, $fileh, $filename);
7234 8         15 local $_;
7235              
7236 8         25 for (my $i = 0; $i < @elems; ++$i)
7237             {
7238 6         15 $_ = $elems[$i];
7239 6         16 my $lastelement = $i == @elems-1;
7240              
7241 6 100 66     63 if ($_ eq "" || $_ eq ".") { next } # Ignore these.
  1 50       6  
7242             elsif ($_ eq "..")
7243             {
7244             # Go to parent directory.
7245 0         0 $dirh = $dirh->parent;
7246             }
7247             else
7248             {
7249             # What is it?
7250 5         29 my $handle = $dirh->get ($_);
7251              
7252 5 50       18 if (!$lastelement)
7253             {
7254 0 0       0 if (!$handle)
    0          
7255             {
7256 0         0 return ();
7257             }
7258             elsif (!$handle->isa ("Net::FTPServer::DirHandle"))
7259             {
7260 0         0 return ();
7261             }
7262             else
7263             {
7264 0         0 $dirh = $handle;
7265             }
7266             }
7267             else # it's the last element - treat it nicely.
7268             {
7269 5 50       17 if (!$handle)
    0          
7270             {
7271             # But it could be a wildcard ...
7272 5 100 66     46 if (/\*/ || /\?/)
7273             {
7274 4         20 $wildcard = $_;
7275             }
7276             else
7277             {
7278 1         4 return ();
7279             }
7280             }
7281             elsif (!$handle->isa ("Net::FTPServer::DirHandle"))
7282             {
7283             # So it's a file.
7284 0         0 $fileh = $handle;
7285 0         0 $filename = $_;
7286             }
7287             else
7288             {
7289 0         0 $dirh = $handle;
7290             }
7291             }
7292             }
7293             } # for
7294              
7295 7         36 return ($dirh, $wildcard, $fileh, $filename);
7296             }
7297              
7298             # The get command understands the following forms for $path:
7299             #
7300             # file List single file in cwd.
7301             # path/to/file List single file, relative to cwd.
7302             # /path/to/file List single file, absolute.
7303             #
7304             # Returns ($dirh, $fileh, $filename) where:
7305             #
7306             # $dirh is set if the directory exists
7307             # $fileh is set if the directory and the file exist
7308             # $filename is just the last component part of the path
7309             # and is always set if $dirh is set.
7310              
7311             sub _get
7312             {
7313 123     123   206 my $self = shift;
7314 123         219 my $path = shift;
7315              
7316 123         245 my $dirh = $self->{cwd};
7317              
7318             # Absolute path?
7319 123 50       389 if (substr ($path, 0, 1) eq "/")
7320             {
7321 0         0 $dirh = $self->root_directory_hook;
7322 0         0 $path =~ s,^/+,,;
7323 0 0       0 $path = "." if $path eq "";
7324             }
7325              
7326             # Parse the first elements of path until we find the appropriate
7327             # working directory.
7328 123         468 my @elems = split /\//, $path;
7329 123         281 my $filename = pop @elems;
7330              
7331 123 50 33     697 unless (defined $filename && length $filename)
7332             {
7333 0         0 return ();
7334             }
7335              
7336 123         340 foreach (@elems)
7337             {
7338 0 0 0     0 if ($_ eq "" || $_ eq ".") { next } # Ignore these.
  0 0       0  
7339             elsif ($_ eq "..")
7340             {
7341             # Go to parent directory.
7342 0         0 $dirh = $dirh->parent;
7343             }
7344             else
7345             {
7346 0         0 my $handle = $dirh->get ($_);
7347              
7348 0 0       0 if (!$handle)
    0          
7349             {
7350 0         0 return ();
7351             }
7352             elsif (!$handle->isa ("Net::FTPServer::DirHandle"))
7353             {
7354 0         0 return ();
7355             }
7356             else
7357             {
7358 0         0 $dirh = $handle;
7359             }
7360             }
7361             }
7362              
7363             # Get the file handle.
7364 123 50       765 my $fileh =
    50          
7365             ($filename eq ".") ? $dirh :
7366             ($filename eq "..") ? $dirh->parent :
7367             $dirh->get($filename);
7368              
7369 123         472 return ($dirh, $fileh, $filename);
7370             }
7371              
7372             =pod
7373              
7374             =item $sock = $self->open_data_connection;
7375              
7376             Open a data connection. Returns the socket (an instance of C) or undef if it fails for some reason.
7377              
7378             =cut
7379              
7380             sub open_data_connection
7381             {
7382 114     114 1 201 my $self = shift;
7383 114         170 my $sock;
7384              
7385 114 100       317 if (! $self->{_passive})
7386             {
7387             # Active mode - connect back to the client.
7388 12         26 my $source_addr = $self->{sockaddrstring};
7389 12         20 my $target_addr = $self->{_hostaddrstring};
7390 12         20 my $target_port = $self->{_hostport};
7391 12 50       28 if (my $source_port = $self->{ftp_data_port})
7392             {
7393             # Temporarily jump back to super user just
7394             # long enough to bind the privileged port.
7395 0         0 local $) = 0;
7396 0         0 local $> = 0;
7397 0         0 for (1..5) {
7398 0         0 "0" =~ /(0)/; # Perl 5.7 / IO::Socket::INET bug workaround.
7399 0 0       0 $sock = new IO::Socket::INET
7400             LocalAddr => $source_addr,
7401             LocalPort => $source_port,
7402             PeerAddr => $target_addr,
7403             PeerPort => $target_port,
7404             Proto => "tcp",
7405             Type => SOCK_STREAM,
7406             Reuse => 1,
7407             or warn "PID $$ Failed to bind() ($!)";
7408 0 0       0 last if $sock;
7409 0 0       0 print STDERR " PID $$ Socket [${source_addr}:${source_port}] to [${target_addr}:${target_port}]\n"
7410             if $_ == 1;
7411 0 0       0 last unless $!{EADDRINUSE};
7412 0         0 print STDERR
7413             " PID $$ Retrying data connection (Attempt $_)\n" ;
7414 0         0 sleep 1;
7415             }
7416 0 0       0 return undef unless $sock ;
7417             }
7418             else
7419             {
7420 12         55 "0" =~ /(0)/; # Perl 5.7 / IO::Socket::INET bug workaround.
7421             $sock = new IO::Socket::INET
7422             LocalAddr => $self->{sockaddrstring},
7423             PeerAddr => $self->{_hostaddrstring},
7424             PeerPort => $self->{_hostport},
7425 12 50       149 Proto => "tcp",
7426             Type => SOCK_STREAM,
7427             Reuse => 1,
7428             or return undef;
7429             }
7430             }
7431             else
7432             {
7433             # Passive mode - wait for a connection from the client.
7434 102 50       569 $sock = $self->{_passive_sock}->accept or return undef;
7435              
7436             # Check that the peer address of the connection is the
7437             # client's own IP address.
7438             # XXX This test is commented out because it causes Netscape 4
7439             # to fail on loopback connections.
7440             # unless ($self->config ("allow proxy ftp"))
7441             # {
7442             # my $peeraddrstring = inet_ntoa ($sock->peeraddr);
7443              
7444             # if ($peeraddrstring ne $self->{peeraddrstring})
7445             # {
7446             # $self->reply (504, "Proxy FTP is not allowed on this server.");
7447             # return;
7448             # }
7449             # }
7450             }
7451              
7452             # Set TCP keepalive?
7453 114 50       82268 if (defined $self->config ("tcp keepalive"))
7454             {
7455 0 0       0 $sock->sockopt (SO_KEEPALIVE, 1)
7456             or warn "setsockopt: SO_KEEPALIVE: $!";
7457             }
7458              
7459             # Set TCP initial window size?
7460 114 50       286 if (defined $self->config ("tcp window"))
7461             {
7462 0 0       0 $sock->sockopt (SO_SNDBUF, $self->config ("tcp window"))
7463             or warn "setsockopt: SO_SNDBUF: $!";
7464 0 0       0 $sock->sockopt (SO_RCVBUF, $self->config ("tcp window"))
7465             or warn "setsockopt: SO_RCVBUF: $!";
7466             }
7467              
7468 114         20241 return $sock;
7469             }
7470              
7471             # $self->_list_file ($sock, $fileh, [$filename, [$statusref]]);
7472             #
7473             # List a single file over the data connection $sock.
7474              
7475             sub _list_file
7476             {
7477 23     23   33 my $self = shift;
7478 23         31 my $sock = shift;
7479 23         28 my $fileh = shift;
7480 23         30 my $filename = shift;
7481 23         26 my $statusref = shift;
7482              
7483 23 50       50 $filename = $fileh->filename
7484             if $filename eq '';
7485              
7486             # Get the status information.
7487 23         29 my @status;
7488 23 100       40 if ($statusref) { @status = @$statusref }
  21         49  
7489 2         6 else { @status = $fileh->status }
7490              
7491             # Break out the fields of the status information.
7492 23         50 my ($mode, $perms, $nlink, $user, $group, $size, $mtime) = @status;
7493              
7494             # Generate printable date (this logic is taken from GNU fileutils:
7495             # src/ls.c: print_long_format).
7496 23         36 my $time = time;
7497 23         32 my $fmt;
7498 23 50 33     58 if ($time > $mtime + 6 * 30 * 24 * 60 * 60 || $time < $mtime - 60 * 60)
7499             {
7500 23         35 $fmt = "%b %e %Y";
7501             }
7502             else
7503             {
7504 0         0 $fmt = "%b %e %H:%M";
7505             }
7506              
7507 23         213 my $fmt_time = strftime $fmt, localtime ($mtime);
7508              
7509             # Generate printable permissions.
7510 23 50       146 my $fmt_perms = join "",
    50          
    100          
    50          
    50          
    100          
    50          
    50          
    100          
7511             ($perms & 0400 ? 'r' : '-'),
7512             ($perms & 0200 ? 'w' : '-'),
7513             ($perms & 0100 ? 'x' : '-'),
7514             ($perms & 040 ? 'r' : '-'),
7515             ($perms & 020 ? 'w' : '-'),
7516             ($perms & 010 ? 'x' : '-'),
7517             ($perms & 04 ? 'r' : '-'),
7518             ($perms & 02 ? 'w' : '-'),
7519             ($perms & 01 ? 'x' : '-');
7520              
7521             # Printable file type.
7522 23 100       43 my $fmt_mode = $mode eq 'f' ? '-' : $mode;
7523              
7524             # If it's a symbolic link, display the link.
7525 23         32 my $link;
7526 23 50       41 if ($mode eq 'l')
7527             {
7528 0         0 $link = $fileh->readlink;
7529 0 0       0 die "readlink: $!" unless defined $link;
7530             }
7531 23 50       45 my $fmt_link = defined $link ? " -> $link" : "";
7532              
7533             # Display the file.
7534 23         200 my $line = sprintf
7535             ("%s%s%4d %-8s %-8s %8.0f %s %s%s\r\n",
7536             $fmt_mode,
7537             $fmt_perms,
7538             $nlink,
7539             $user,
7540             $group,
7541             $size,
7542             $fmt_time,
7543             $filename,
7544             $fmt_link);
7545 23 50       58 $self->xfer (length $line) if $self->{_xferlog};
7546 23         72 $sock->print ($line);
7547             }
7548              
7549             # Implement the STOR, STOU (store unique) and APPE (append) commands.
7550              
7551             sub _store
7552             {
7553 56     56   133 my $self = shift;
7554 56         110 my $path = shift;
7555 56         143 my %params = @_;
7556              
7557 56   100     333 my $unique = $params{unique} || 0;
7558 56   100     256 my $append = $params{append} || 0;
7559              
7560 56         110 my ($dirh, $fileh, $filename, $transfer_hook);
7561              
7562 56 100       149 unless ($unique)
7563             {
7564             # Get the directory.
7565 47         232 ($dirh, $fileh, $filename) = $self->_get ($path);
7566              
7567 47 50       234 unless ($dirh)
7568             {
7569 0         0 $self->reply (550, "File or directory not found.");
7570 0         0 return;
7571             }
7572             }
7573             else # STOU command -- ignore any parameters.
7574             {
7575 9         24 $dirh = $self->{cwd};
7576              
7577             # Choose a unique name for this file.
7578 9         19 my $i = 0;
7579 9         54 while ($dirh->get ("X$i")) {
7580 36         178 $i++;
7581             }
7582              
7583 9         30 $filename = "X$i";
7584             }
7585              
7586             # Check access control.
7587 56 50       286 unless ($self->_eval_rule ("store rule",
7588             $dirh->pathname . $filename,
7589             $filename, $dirh->pathname))
7590             {
7591 0         0 $self->reply (550, "Store command denied by server configuration.");
7592 0         0 return;
7593             }
7594              
7595             # Are we trying to overwrite a previously existing file?
7596 56 50 100     363 if (! $append &&
      66        
      33        
7597             defined $fileh &&
7598             defined $self->config ("allow store to overwrite") &&
7599             ! $self->config ("allow store to overwrite"))
7600             {
7601 0         0 $self->reply (550, "Cannot rename file.");
7602 0         0 return;
7603             }
7604              
7605             # Try to open the file.
7606 56 100       349 my $file = $dirh->open ($filename, ($append ? "a" : "w"));
7607              
7608 56 50       3617 unless ($file)
7609             {
7610 0         0 $self->reply (550, "Cannot create file $filename.");
7611 0         0 return;
7612             }
7613              
7614 56 100       392 unless ($unique)
7615             {
7616             $self->reply (150,
7617             "Opening " .
7618 47 100       304 ($self->{type} eq 'A' ? "ASCII mode" : "BINARY mode") .
7619             " data connection for file $filename.");
7620             }
7621             else
7622             {
7623             # RFC 1123 section 4.1.2.9.
7624 9         39 $self->reply (150, "FILE: $filename");
7625             }
7626              
7627             # Open a path back to the client.
7628 56         342 my $sock = $self->open_data_connection;
7629              
7630 56 50       4986 unless ($sock)
7631             {
7632 0         0 $self->reply (425, "Can't open data connection.");
7633 0         0 return;
7634             }
7635              
7636             # Incoming bandwidth
7637 56 50       201 $self->xfer_start ($dirh->pathname . $filename, "i") if $self->{_xferlog};
7638              
7639             # What mode are we receiving this file in?
7640 56 100       186 unless ($self->{type} eq 'A') # Binary type.
7641             {
7642 40         83 my ($r, $buffer, $n, $w);
7643              
7644             # XXX Do we need to support REST?
7645              
7646             # Copy data.
7647 40         297 while ($r = $sock->sysread ($buffer, 65536))
7648             {
7649 3603 50       2327125 $self->xfer ($r) if $self->{_xferlog};
7650              
7651             # Restart alarm clock timer.
7652 3603         9359 alarm $self->{_idle_timeout};
7653              
7654 3603 50       8069 if ($transfer_hook
7655             = $self->transfer_hook ("w", $file, $sock, \$buffer))
7656             {
7657 0         0 $sock->close;
7658 0         0 $file->close;
7659 0         0 $self->reply (426,
7660             "File store error: $transfer_hook",
7661             "Data connection has been closed.");
7662 0         0 return;
7663             }
7664              
7665 3603         6496 for ($n = 0; $n < $r; )
7666             {
7667 3603         7892 $w = $file->syswrite ($buffer, $r - $n, $n);
7668              
7669 3603 50       123235 unless (defined $w)
7670             {
7671             # There was an error.
7672 0         0 my $reason = $self->system_error_hook();
7673              
7674 0         0 $sock->close;
7675 0         0 $file->close;
7676 0         0 $self->reply (426,
7677             "File store error: $reason",
7678             "Data connection has been closed.");
7679 0         0 return;
7680             }
7681              
7682 3603         10293 $n += $w;
7683             }
7684             }
7685              
7686 40 50       544 unless (defined $r)
7687             {
7688             # There was an error.
7689 0         0 my $reason = $self->system_error_hook();
7690              
7691 0         0 $sock->close;
7692 0         0 $file->close;
7693 0         0 $self->reply (426,
7694             "File store error: $reason",
7695             "Data connection has been closed.");
7696 0         0 return;
7697             }
7698             }
7699             else # ASCII type.
7700             {
7701             # XXX Do we need to support REST?
7702              
7703             # Copy data.
7704 16         5693 while (defined ($_ = $sock->getline))
7705             {
7706 54622 50       1614106 $self->xfer (length $_) if $self->{_xferlog};
7707              
7708             # Remove any telnet-format line endings.
7709 54622         4106166 s/[\n\r]*$//;
7710              
7711             # Restart alarm clock timer.
7712 54622         176881 alarm $self->{_idle_timeout};
7713              
7714 54622 50       118291 if ($transfer_hook = $self->transfer_hook ("w", $file, $sock, \$_))
7715             {
7716 0         0 $sock->close;
7717 0         0 $file->close;
7718 0         0 $self->reply (426,
7719             "File store error: $transfer_hook",
7720             "Data connection has been closed.");
7721 0         0 return;
7722             }
7723              
7724             # Write the line with native format line endings.
7725 54622         159334 my $w = $file->print ("$_\n");
7726 54622 50       1419521 unless (defined $w)
7727             {
7728 0         0 my $reason = $self->system_error_hook();
7729             # There was an error.
7730 0         0 $sock->close;
7731 0         0 $file->close;
7732 0         0 $self->reply (426,
7733             "File store error: $reason",
7734             "Data connection has been closed.");
7735 0         0 return;
7736             }
7737             }
7738             }
7739              
7740 56 50 33     761 unless ($sock->close && $file->close)
7741             {
7742 0         0 my $reason = $self->system_error_hook();
7743 0         0 $self->reply (550, "File retrieval error: $reason");
7744 0         0 return;
7745             }
7746              
7747 56 50       5681 $self->xfer_complete if $self->{_xferlog};
7748 56         232 $self->reply (226, "File store complete. Data connection has been closed.");
7749             }
7750              
7751             =pod
7752              
7753             =item $self->pre_configuration_hook ();
7754              
7755             Hook: Called before command line arguments and configuration file
7756             are read.
7757              
7758             Status: optional.
7759              
7760             Notes: You may append your own information to C<$self-E{version_string}>
7761             from this hook.
7762              
7763             =cut
7764              
7765             sub pre_configuration_hook
7766       0 1   {
7767             }
7768              
7769             =pod
7770              
7771             =item $self->options_hook (\@args);
7772              
7773             Hook: Called before command line arguments are parsed.
7774              
7775             Status: optional.
7776              
7777             Notes: You can use this hook to supply your own command line arguments.
7778             If you parse any arguments, you should remove them from the @args
7779             array.
7780              
7781             =cut
7782              
7783             sub options_hook
7784       41 1   {
7785             }
7786              
7787             =pod
7788              
7789             =item $self->post_configuration_hook ();
7790              
7791             Hook: Called after all command line arguments and configuration file
7792             have been read and parsed.
7793              
7794             Status: optional.
7795              
7796             =cut
7797              
7798             sub post_configuration_hook
7799       41 1   {
7800             }
7801              
7802             =pod
7803              
7804             =item $self->post_bind_hook ();
7805              
7806             Hook: Called only in daemon mode after the control port is bound
7807             but before starting the accept infinite loop block.
7808              
7809             Status: optional.
7810              
7811             =cut
7812              
7813             sub post_bind_hook
7814       0 1   {
7815             }
7816              
7817             =pod
7818              
7819             =item $self->pre_accept_hook ();
7820              
7821             Hook: Called in daemon mode only just before C is called
7822             in the parent FTP server process.
7823              
7824             Status: optional.
7825              
7826             =cut
7827              
7828             sub pre_accept_hook
7829       0 1   {
7830             }
7831              
7832             =pod
7833              
7834             =item $self->post_accept_hook ();
7835              
7836             Hook: Called both in daemon mode and in inetd mode just after the
7837             connection has been accepted. This is called in the child process.
7838              
7839             Status: optional.
7840              
7841             =cut
7842              
7843             sub post_accept_hook
7844       41 1   {
7845             }
7846              
7847             =pod
7848              
7849             =item $rv = $self->access_control_hook;
7850              
7851             Hook: Called after C-ing the connection to perform access
7852             control. Detailed request information is contained in the $self
7853             object. If the function returns -1 then the socket is immediately
7854             closed and no FTP processing happens on it. If the function returns 0,
7855             then normal access control is performed on the socket before FTP
7856             processing starts. If the function returns 1, then normal access
7857             control is I performed on the socket and FTP processing begins
7858             immediately.
7859              
7860             Status: optional.
7861              
7862             =cut
7863              
7864             sub access_control_hook
7865             {
7866 41     41 1 166 return 0;
7867             }
7868              
7869             =pod
7870              
7871             =item $rv = $self->process_limits_hook;
7872              
7873             Hook: Called after C-ing the connection to perform
7874             per-process limits (eg. by using the setrlimit(2) system
7875             call). Access control has already been performed and detailed
7876             request information is contained in the C<$self> object.
7877              
7878             If the function returns -1 then the socket is immediately closed and
7879             no FTP processing happens on it. If the function returns 0, then
7880             normal per-process limits are applied before any FTP processing
7881             starts. If the function returns 1, then normal per-process limits are
7882             I performed and FTP processing begins immediately.
7883              
7884             Status: optional.
7885              
7886             =cut
7887              
7888             sub process_limits_hook
7889             {
7890 41     41 1 741 return 0;
7891             }
7892              
7893             =pod
7894              
7895             =item $rv = $self->authentication_hook ($user, $pass, $user_is_anon)
7896              
7897             Hook: Called to perform authentication. If the authentication
7898             succeeds, this should return 0 (or any positive integer E= 0).
7899             If the authentication fails, this should return -1.
7900              
7901             Status: required.
7902              
7903             =cut
7904              
7905             sub authentication_hook
7906             {
7907 0     0 1 0 die "authentication_hook is required";
7908             }
7909              
7910             =pod
7911              
7912             =item $self->user_login_hook ($user, $user_is_anon)
7913              
7914             Hook: Called just after user C<$user> has successfully logged in. A good
7915             place to change uid and chroot if necessary.
7916              
7917             Status: optional.
7918              
7919             =cut
7920              
7921             sub user_login_hook
7922       0 1   {
7923             }
7924              
7925             =pod
7926              
7927             =item $dirh = $self->root_directory_hook;
7928              
7929             Hook: Return an instance of a subclass of Net::FTPServer::DirHandle
7930             corresponding to the root directory.
7931              
7932             Status: required.
7933              
7934             =cut
7935              
7936             sub root_directory_hook
7937             {
7938 0     0 1 0 die "root_directory_hook is required";
7939             }
7940              
7941             =pod
7942              
7943             =item $self->pre_command_hook;
7944              
7945             Hook: This hook is called just before the server begins to wait for
7946             the client to issue the next command over the control connection.
7947              
7948             Status: optional.
7949              
7950             =cut
7951              
7952             sub pre_command_hook
7953       327 1   {
7954             }
7955              
7956             =pod
7957              
7958             =item $rv = $self->command_filter_hook ($cmdline);
7959              
7960             Hook: This hook is called immediately after the client issues
7961             command C<$cmdline>, but B any checking or processing
7962             is performed on the command. If this function returns -1, then
7963             the server immediately goes back to waiting for the next
7964             command. If this function returns 0, then normal command filtering
7965             is carried out and the command is processed. If this function
7966             returns 1 then normal command filtering is B performed
7967             and the command processing begins immediately.
7968              
7969             Important Note: This hook must be careful B to overwrite
7970             the global C<$_> variable.
7971              
7972             Do not use this function to add your own commands. Instead
7973             use the C<$self-E{command_table}> and C<$self-E{site_command_table}>
7974             hashes.
7975              
7976             Status: optional.
7977              
7978             =cut
7979              
7980             sub command_filter_hook
7981             {
7982 311     311 1 613 return 0;
7983             }
7984              
7985              
7986             =pod
7987              
7988             =item $error = $self->transfer_hook ($mode, $file, $sock, \$buffer);
7989              
7990             $mode - Open mode on the File object (Either reading or writing)
7991             $file - File object as returned from DirHandle::open
7992             $sock - Data IO::Socket object used for transfering
7993             \$buffer - Reference to current buffer about to be written
7994              
7995             The \$buffer is passed by reference to minimize the stack overhead
7996             for efficiency purposes only. It is B meant to be modified by
7997             the transfer_hook subroutine. (It can cause corruption if the
7998             length of $buffer is modified.)
7999              
8000             Hook: This hook is called after reading $buffer and before writing
8001             $buffer to its destination. If arg1 is "r", $buffer was read
8002             from the File object and written to the Data socket. If arg1 is
8003             "w", $buffer will be written to the File object because it was
8004             read from the Data Socket. The return value is the error for not
8005             being able to perform the write. Return undef to avoid aborting
8006             the transfer process.
8007              
8008             Status: optional.
8009              
8010             =cut
8011              
8012             sub transfer_hook
8013             {
8014 62962     62962 1 116146 return undef;
8015             }
8016              
8017             =pod
8018              
8019             =item $self->post_command_hook ($cmd, $rest)
8020              
8021             Hook: This hook is called after all command processing has been
8022             carried out on this command. C<$cmd> is the command, and
8023             C<$rest> is the remainder of the command line.
8024              
8025             Status: optional.
8026              
8027             =cut
8028              
8029             sub post_command_hook
8030       277 1   {
8031             }
8032              
8033             =pod
8034              
8035             =item $self->system_error_hook
8036              
8037             Hook: This hook is used instead of $! when what looks like a system error
8038             occurs during a virtual filesystem handle method. It can be used by the
8039             virtual filesystem to provide explanatory text for a virtual filesystem
8040             failure which did not actually set the real $!.
8041              
8042             Status: optional.
8043              
8044             =cut
8045              
8046             sub system_error_hook
8047             {
8048 0     0 1   return "$!";
8049             }
8050              
8051             =pod
8052              
8053             =item $self->quit_hook
8054              
8055             Hook: This hook is called after the user has C or if the FTP
8056             client cleanly drops the connection. Please note, however, that this
8057             hook is I called whenever the FTP server exits, particularly in
8058             cases such as:
8059              
8060             * The FTP server, the Perl interpreter or the personality
8061             crashes unexpectedly.
8062             * The user fails to log in.
8063             * The FTP server detects a fatal error, sends a "421" error code,
8064             and abruptly exits.
8065             * Idle timeouts.
8066             * Access control violations.
8067             * Manual server shutdowns.
8068              
8069             Unfortunately it is not in general easily possible to catch these
8070             cases and cleanly call a hook. If your personality needs to do cleanup
8071             in all cases, then it is probably better to use an C block inside
8072             your Server object (see C). Even using an C block
8073             cannot catch cases where the Perl interpreter crashes.
8074              
8075             Status: optional.
8076              
8077             =cut
8078              
8079             sub quit_hook
8080       40 1   {
8081             }
8082              
8083             #----------------------------------------------------------------------
8084              
8085             # The Net::FTPServer::ZipMember class is used to implement the ZIP
8086             # file generator (in archive mode). This class is carefully and
8087             # cleverly designed so that it doesn't break if Archive::Zip is not
8088             # present. This class is mostly based on Archive::Zip::NewFileMember.
8089              
8090             package Net::FTPServer::ZipMember;
8091              
8092 75     75   680 use strict;
  75         196  
  75         2228  
8093              
8094 75     75   397 use vars qw(@ISA);
  75         184  
  75         3479  
8095             @ISA = qw(Archive::Zip::Member);
8096              
8097 75     75   474 use Net::FTPServer::FileHandle;
  75         185  
  75         28465  
8098              
8099             # Verify this exists first by using ``exists $INC{"Archive/Zip.pm"}''.
8100 75     75   4208 eval "use Archive::Zip";
  0         0  
  0         0  
8101              
8102             sub _newFromFileHandle
8103             {
8104 0     0     my $class = shift;
8105 0           my $fileh = shift;
8106              
8107 0 0         return undef unless exists $INC{"Archive/Zip.pm"};
8108              
8109 0           my $self = $class->new (@_);
8110              
8111 0           $self->{fileh} = $fileh;
8112              
8113 0           my $filename = $fileh->filename;
8114 0           $self->fileName ($filename);
8115 0           $self->{externalFileName} = $filename;
8116              
8117 0           $self->{compressionMethod} = &Archive::Zip::COMPRESSION_STORED;
8118              
8119 0           my ($mode, $perms, $nlink, $user, $group, $size, $time) = $fileh->status;
8120 0           $self->{compressedSize} = $self->{uncompressedSize} = $size;
8121 0 0         $self->desiredCompressionMethod
8122             ($self->compressedSize > 0
8123             ? &Archive::Zip::COMPRESSION_DEFLATED
8124             : &Archive::Zip::COMPRESSION_STORED);
8125 0           $self->unixFileAttributes ($perms);
8126 0 0         $self->setLastModFileDateTimeFromUnix ($time) if $time > 0;
8127 0           $self->isTextFile (0);
8128              
8129 0           $self;
8130             }
8131              
8132             sub externalFileName
8133             {
8134 0     0     shift->{externalFileName};
8135             }
8136              
8137             sub fh
8138             {
8139 0     0     my $self = shift;
8140              
8141 0 0         return $self->{fh} if $self->{fh};
8142              
8143 0 0         $self->{fh} = $self->{fileh}->open ("r")
8144             or return &Archive::Zip::AZ_IO_ERROR;
8145              
8146 0           $self->{fh};
8147             }
8148              
8149             sub rewindData
8150             {
8151 0     0     my $self = shift;
8152              
8153 0           my $status = $self->SUPER::rewindData (@_);
8154 0 0         return $status if $status != &Archive::Zip::AZ_OK;
8155              
8156 0 0         return &Archive::Zip::AZ_IO_ERROR unless $self->fh;
8157              
8158             # Not all personalities can seek backwards in the stream. Close
8159             # the file and reopen it instead.
8160 0 0         $self->endRead == &Archive::Zip::AZ_OK
8161             or return &Archive::Zip::AZ_IO_ERROR;
8162 0           $self->fh;
8163              
8164 0           return &Archive::Zip::AZ_OK;
8165             }
8166              
8167             sub _readRawChunk
8168             {
8169 0     0     my $self = shift;
8170 0           my $dataref = shift;
8171 0           my $chunksize = shift;
8172              
8173 0 0         return (0, &Archive::Zip::AZ_OK) unless $chunksize;
8174              
8175 0 0         my $bytesread = $self->fh->sysread ($$dataref, $chunksize)
8176             or return (0, &Archive::Zip::AZ_IO_ERROR);
8177              
8178 0           return ($bytesread, &Archive::Zip::AZ_OK);
8179             }
8180              
8181             sub endRead
8182             {
8183 0     0     my $self = shift;
8184              
8185 0 0         if ($self->{fh})
8186             {
8187             $self->{fh}->close
8188 0 0         or return &Archive::Zip::AZ_IO_ERROR;
8189 0           delete $self->{fh};
8190             }
8191 0           return &Archive::Zip::AZ_OK;
8192             }
8193              
8194             1 # So that the require or use succeeds.
8195              
8196             __END__