KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > alfresco > filesys > ftp > FTPSrvSession


1 /*
2  * Copyright (C) 2005 Alfresco, Inc.
3  *
4  * Licensed under the Mozilla Public License version 1.1
5  * with a permitted attribution clause. You may obtain a
6  * copy of the License at
7  *
8  * http://www.alfresco.org/legal/license.txt
9  *
10  * Unless required by applicable law or agreed to in writing,
11  * software distributed under the License is distributed on an
12  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13  * either express or implied. See the License for the specific
14  * language governing permissions and limitations under the
15  * License.
16  */

17 package org.alfresco.filesys.ftp;
18
19 import java.io.IOException JavaDoc;
20 import java.io.InputStream JavaDoc;
21 import java.io.InputStreamReader JavaDoc;
22 import java.io.OutputStream JavaDoc;
23 import java.io.OutputStreamWriter JavaDoc;
24 import java.io.Writer JavaDoc;
25 import java.net.InetAddress JavaDoc;
26 import java.net.Socket JavaDoc;
27 import java.net.SocketException JavaDoc;
28 import java.net.UnknownHostException JavaDoc;
29 import java.util.Date JavaDoc;
30 import java.util.Enumeration JavaDoc;
31 import java.util.StringTokenizer JavaDoc;
32 import java.util.Vector JavaDoc;
33
34 import javax.transaction.UserTransaction JavaDoc;
35
36 import org.alfresco.filesys.server.SrvSession;
37 import org.alfresco.filesys.server.auth.ClientInfo;
38 import org.alfresco.filesys.server.auth.acl.AccessControl;
39 import org.alfresco.filesys.server.auth.acl.AccessControlManager;
40 import org.alfresco.filesys.server.core.SharedDevice;
41 import org.alfresco.filesys.server.core.SharedDeviceList;
42 import org.alfresco.filesys.server.filesys.AccessDeniedException;
43 import org.alfresco.filesys.server.filesys.AccessMode;
44 import org.alfresco.filesys.server.filesys.DiskDeviceContext;
45 import org.alfresco.filesys.server.filesys.DiskFullException;
46 import org.alfresco.filesys.server.filesys.DiskInterface;
47 import org.alfresco.filesys.server.filesys.DiskSharedDevice;
48 import org.alfresco.filesys.server.filesys.FileAction;
49 import org.alfresco.filesys.server.filesys.FileAttribute;
50 import org.alfresco.filesys.server.filesys.FileInfo;
51 import org.alfresco.filesys.server.filesys.FileOpenParams;
52 import org.alfresco.filesys.server.filesys.FileStatus;
53 import org.alfresco.filesys.server.filesys.NetworkFile;
54 import org.alfresco.filesys.server.filesys.NotifyChange;
55 import org.alfresco.filesys.server.filesys.SearchContext;
56 import org.alfresco.filesys.server.filesys.SrvDiskInfo;
57 import org.alfresco.filesys.server.filesys.TreeConnection;
58 import org.alfresco.filesys.server.filesys.TreeConnectionHash;
59 import org.alfresco.filesys.smb.server.repo.ContentContext;
60 import org.alfresco.model.ContentModel;
61 import org.alfresco.repo.security.authentication.AuthenticationComponent;
62 import org.alfresco.service.cmr.repository.NodeRef;
63 import org.alfresco.service.cmr.repository.NodeService;
64 import org.alfresco.service.cmr.security.AuthenticationService;
65 import org.alfresco.service.cmr.security.PersonService;
66 import org.alfresco.service.transaction.TransactionService;
67 import org.apache.commons.logging.Log;
68 import org.apache.commons.logging.LogFactory;
69
70 import com.sun.star.uno.RuntimeException;
71
72 /**
73  * FTP Server Session Class
74  *
75  * @author GKSpencer
76  */

77 public class FTPSrvSession extends SrvSession implements Runnable JavaDoc
78 {
79
80     // Debug logging
81

82     private static final Log logger = LogFactory.getLog("org.alfresco.ftp.protocol");
83
84     // Constants
85
//
86
// Debug flag values
87

88     public static final int DBG_STATE = 0x00000001; // Session state changes
89

90     public static final int DBG_SEARCH = 0x00000002; // File/directory search
91

92     public static final int DBG_INFO = 0x00000004; // Information requests
93

94     public static final int DBG_FILE = 0x00000008; // File open/close/info
95

96     public static final int DBG_FILEIO = 0x00000010; // File read/write
97

98     public static final int DBG_ERROR = 0x00000020; // Errors
99

100     public static final int DBG_PKTTYPE = 0x00000040; // Received packet type
101

102     public static final int DBG_TIMING = 0x00000080; // Time packet
103

104     // processing
105

106     public static final int DBG_DATAPORT = 0x00000100; // Data port
107

108     public static final int DBG_DIRECTORY = 0x00000200; // Directory commands
109

110     // Anonymous user name
111

112     private static final String JavaDoc USER_ANONYMOUS = "anonymous";
113
114     // Root directory and FTP directory seperator
115

116     private static final String JavaDoc ROOT_DIRECTORY = "/";
117
118     private static final String JavaDoc FTP_SEPERATOR = "/";
119
120     private static final char FTP_SEPERATOR_CHAR = '/';
121
122     // Share relative path directory seperator
123

124     private static final String JavaDoc DIR_SEPERATOR = "\\";
125
126     private static final char DIR_SEPERATOR_CHAR = '\\';
127
128     // File transfer buffer size
129

130     private static final int DEFAULT_BUFFERSIZE = 64000;
131
132     // Carriage return/line feed combination required for response messages
133

134     protected final static String JavaDoc CRLF = "\r\n";
135
136     // LIST command options
137

138     protected final static String JavaDoc LIST_OPTION_HIDDEN = "-a";
139
140     // Session socket
141

142     private Socket JavaDoc m_sock;
143
144     // Input/output streams to remote client
145

146     private InputStreamReader JavaDoc m_in;
147
148     private char[] m_inbuf;
149
150     private OutputStreamWriter JavaDoc m_out;
151
152     private StringBuffer JavaDoc m_outbuf;
153
154     // Data connection
155

156     private FTPDataSession m_dataSess;
157
158     // Current working directory details
159
//
160
// First level is the share name then a path relative to the share root
161

162     private FTPPath m_cwd;
163
164     // Binary mode flag
165

166     private boolean m_binary = false;
167
168     // Restart position for binary file transfer
169

170     private long m_restartPos = 0;
171
172     // Rename from path details
173

174     private FTPPath m_renameFrom;
175
176     // Filtered list of shared filesystems available to this session
177

178     private SharedDeviceList m_shares;
179
180     // List of shared device connections used by this session
181

182     private TreeConnectionHash m_connections;
183
184     /**
185      * Class constructor
186      *
187      * @param sock
188      * Socket
189      * @param srv
190      * FTPServer
191      */

192     public FTPSrvSession(Socket JavaDoc sock, FTPNetworkServer srv)
193     {
194         super(-1, srv, "FTP", null);
195
196         // Save the local socket
197

198         m_sock = sock;
199
200         // Set the socket linger options, so the socket closes immediately when
201
// closed
202

203         try
204         {
205             m_sock.setSoLinger(false, 0);
206         }
207         catch (SocketException JavaDoc ex)
208         {
209         }
210
211         // Indicate that the user is not logged in
212

213         setLoggedOn(false);
214
215         // Allocate the FTP path
216

217         m_cwd = new FTPPath();
218
219         // Allocate the tree connection cache
220

221         m_connections = new TreeConnectionHash();
222     }
223
224     /**
225      * Close the FTP session, and associated data socket if active
226      */

227     public final void closeSession()
228     {
229
230         // Call the base class
231

232         super.closeSession();
233
234         // Close the data connection, if active
235

236         if (m_dataSess != null)
237         {
238             getFTPServer().releaseDataSession(m_dataSess);
239             m_dataSess = null;
240         }
241
242         // Close the socket first, if the client is still connected this should
243
// allow the
244
// input/output streams
245
// to be closed
246

247         if (m_sock != null)
248         {
249             try
250             {
251                 m_sock.close();
252             }
253             catch (Exception JavaDoc ex)
254             {
255             }
256             m_sock = null;
257         }
258
259         // Close the input/output streams
260

261         if (m_in != null)
262         {
263             try
264             {
265                 m_in.close();
266             }
267             catch (Exception JavaDoc ex)
268             {
269             }
270             m_in = null;
271         }
272
273         if (m_out != null)
274         {
275             try
276             {
277                 m_out.close();
278             }
279             catch (Exception JavaDoc ex)
280             {
281             }
282             m_out = null;
283         }
284
285         // Remove session from server session list
286

287         getFTPServer().removeSession(this);
288
289         // DEBUG
290

291         if (logger.isDebugEnabled() && hasDebug(DBG_STATE))
292             logger.debug("Session closed, " + getSessionId());
293     }
294
295     /**
296      * Return the current working directory
297      *
298      * @return String
299      */

300     public final String JavaDoc getCurrentWorkingDirectory()
301     {
302         return m_cwd.getFTPPath();
303     }
304
305     /**
306      * Return the server that this session is associated with.
307      *
308      * @return FTPServer
309      */

310     public final FTPNetworkServer getFTPServer()
311     {
312         return (FTPNetworkServer) getServer();
313     }
314
315     /**
316      * Return the client network address
317      *
318      * @return InetAddress
319      */

320     public final InetAddress JavaDoc getRemoteAddress()
321     {
322         return m_sock.getInetAddress();
323     }
324
325     /**
326      * Check if there is a current working directory
327      *
328      * @return boolean
329      */

330     public final boolean hasCurrentWorkingDirectory()
331     {
332         return m_cwd != null ? true : false;
333     }
334
335     /**
336      * Set the default path for the session
337      *
338      * @param rootPath
339      * FTPPath
340      */

341     public final void setRootPath(FTPPath rootPath)
342     {
343
344         // Initialize the current working directory using the root path
345

346         m_cwd = new FTPPath(rootPath);
347         if ( rootPath.hasSharedDevice())
348             m_cwd.setSharedDevice( rootPath.getSharedDevice());
349         else
350             m_cwd.setSharedDevice(getShareList(), this);
351     }
352
353     /**
354      * Get the path details for the current request
355      *
356      * @param req
357      * FTPRequest
358      * @param filePath
359      * boolean
360      * @return FTPPath
361      */

362     protected final FTPPath generatePathForRequest(FTPRequest req, boolean filePath)
363     {
364         return generatePathForRequest(req, filePath, true);
365     }
366
367     /**
368      * Get the path details for the current request
369      *
370      * @param req
371      * FTPRequest
372      * @param filePath
373      * boolean
374      * @param checkExists
375      * boolean
376      * @return FTPPath
377      */

378     protected final FTPPath generatePathForRequest(FTPRequest req, boolean filePath, boolean checkExists)
379     {
380         // Use the global share list for normal connections and the per session list for guest access
381

382         SharedDeviceList shareList = null;
383         
384         if ( getClientInformation().isGuest())
385             shareList = getDynamicShareList();
386         else
387             shareList = getShareList();
388         
389         // Convert the path to an FTP format path
390

391         String JavaDoc path = convertToFTPSeperators(req.getArgument());
392
393         // Check if the path is the root directory and there is a default root
394
// path configured
395

396         FTPPath ftpPath = null;
397
398         if (path.compareTo(ROOT_DIRECTORY) == 0)
399         {
400
401             // Check if the FTP server has a default root directory configured
402

403             FTPNetworkServer ftpSrv = (FTPNetworkServer) getServer();
404             if (ftpSrv.hasRootPath())
405                 ftpPath = ftpSrv.getRootPath();
406             else
407             {
408                 try
409                 {
410                     ftpPath = new FTPPath("/");
411                 }
412                 catch (Exception JavaDoc ex)
413                 {
414                 }
415                 return ftpPath;
416             }
417         }
418
419         // Check if the path is relative
420

421         else if (FTPPath.isRelativePath(path) == false)
422         {
423
424             // Create a new path for the directory
425

426             try
427             {
428                 ftpPath = new FTPPath(path);
429             }
430             catch (InvalidPathException ex)
431             {
432                 return null;
433             }
434
435             // Find the associated shared device
436

437             if (ftpPath.setSharedDevice( shareList, this) == false)
438                 return null;
439         }
440         else
441         {
442
443             // Check for the special '.' directory, just return the current
444
// working directory
445

446             if (path.equals("."))
447                 return m_cwd;
448
449             // Check for the special '..' directory, if already at the root
450
// directory return an
451
// error
452

453             if (path.equals(".."))
454             {
455
456                 // Check if we are already at the root path
457

458                 if (m_cwd.isRootPath() == false)
459                 {
460
461                     // Remove the last directory from the path
462

463                     m_cwd.removeDirectory();
464                     m_cwd.setSharedDevice( shareList, this);
465                     return m_cwd;
466                 }
467                 else
468                     return null;
469             }
470
471             // Create a copy of the current working directory and append the new
472
// file/directory name
473

474             ftpPath = new FTPPath(m_cwd);
475
476             // Check if the root directory/share has been set
477

478             if (ftpPath.isRootPath())
479             {
480
481                 // Path specifies the share name
482

483                 try
484                 {
485                     ftpPath.setSharePath(path, null);
486                 }
487                 catch (InvalidPathException ex)
488                 {
489                     return null;
490                 }
491             }
492             else
493             {
494                 if (filePath)
495                     ftpPath.addFile(path);
496                 else
497                     ftpPath.addDirectory(path);
498             }
499
500             // Find the associated shared device, if not already set
501

502             if (ftpPath.hasSharedDevice() == false && ftpPath.setSharedDevice( shareList, this) == false)
503                 return null;
504         }
505
506         // Check if the generated path exists
507

508         if (checkExists)
509         {
510
511             // Check if the new path exists and is a directory
512

513             DiskInterface disk = null;
514             TreeConnection tree = null;
515
516             try
517             {
518
519                 // Create a temporary tree connection
520

521                 tree = getTreeConnection(ftpPath.getSharedDevice());
522
523                 // Access the virtual filesystem driver
524

525                 disk = (DiskInterface) ftpPath.getSharedDevice().getInterface();
526
527                 // Check if the path exists
528

529                 int sts = disk.fileExists(this, tree, ftpPath.getSharePath());
530
531                 if (sts == FileStatus.NotExist)
532                 {
533
534                     // Get the path string, check if there is a leading
535
// seperator
536

537                     String JavaDoc pathStr = req.getArgument();
538                     if (pathStr.startsWith(FTP_SEPERATOR) == false)
539                         pathStr = FTP_SEPERATOR + pathStr;
540
541                     // Create the root path
542

543                     ftpPath = new FTPPath(pathStr);
544
545                     // Find the associated shared device
546

547                     if (ftpPath.setSharedDevice(getShareList(), this) == false)
548                         ftpPath = null;
549                     else
550                     {
551                         // Recheck if the path exists
552

553                         sts = disk.fileExists(this, tree, ftpPath.getSharePath());
554                         if ( sts == FileStatus.NotExist)
555                             ftpPath = null;
556                     }
557                 }
558                 else if ((sts == FileStatus.FileExists && filePath == false)
559                         || (sts == FileStatus.DirectoryExists && filePath == true))
560                 {
561
562                     // Path exists but is the wrong type (directory or file)
563

564                     ftpPath = null;
565                 }
566             }
567             catch (Exception JavaDoc ex)
568             {
569                 // DEBUG
570

571                 if ( logger.isErrorEnabled())
572                     logger.error("Error generating FTP path", ex);
573                 
574                 ftpPath = null;
575             }
576         }
577
578         // Return the new path
579

580         return ftpPath;
581     }
582
583     /**
584      * Convert a path string from share path seperators to FTP path seperators
585      *
586      * @param path
587      * String
588      * @return String
589      */

590     protected final String JavaDoc convertToFTPSeperators(String JavaDoc path)
591     {
592
593         // Check if the path is valid
594

595         if (path == null || path.indexOf(DIR_SEPERATOR) == -1)
596             return path;
597
598         // Replace the path seperators
599

600         return path.replace(DIR_SEPERATOR_CHAR, FTP_SEPERATOR_CHAR);
601     }
602
603     /**
604      * Find the required disk shared device
605      *
606      * @param name
607      * String
608      * @return DiskSharedDevice
609      */

610     protected final DiskSharedDevice findShare(String JavaDoc name)
611     {
612
613         // Check if the name is valid
614

615         if (name == null)
616             return null;
617
618         // Find the required disk share
619

620         SharedDevice shr = getFTPServer().getShareList().findShare(m_cwd.getShareName());
621
622         if (shr != null && shr instanceof DiskSharedDevice)
623             return (DiskSharedDevice) shr;
624
625         // Disk share not found
626

627         return null;
628     }
629
630     /**
631      * Set the binary mode flag
632      *
633      * @param bin
634      * boolean
635      */

636     protected final void setBinary(boolean bin)
637     {
638         m_binary = bin;
639     }
640
641     /**
642      * Send an FTP command response
643      *
644      * @param stsCode
645      * int
646      * @param msg
647      * String
648      * @exception IOException
649      */

650     protected final void sendFTPResponse(int stsCode, String JavaDoc msg) throws IOException JavaDoc
651     {
652
653         // Build the output record
654

655         m_outbuf.setLength(0);
656         m_outbuf.append(stsCode);
657         m_outbuf.append(" ");
658
659         if (msg != null)
660             m_outbuf.append(msg);
661
662         // DEBUG
663

664         if (logger.isDebugEnabled() && hasDebug(DBG_ERROR) && stsCode >= 500)
665             logger.debug("Error status=" + stsCode + ", msg=" + msg);
666
667         // Add the CR/LF
668

669         m_outbuf.append(CRLF);
670
671         // Output the FTP response
672

673         if (m_out != null)
674         {
675             m_out.write(m_outbuf.toString());
676             m_out.flush();
677         }
678     }
679
680     /**
681      * Send an FTP command response
682      *
683      * @param msg
684      * StringBuffer
685      * @exception IOException
686      */

687     protected final void sendFTPResponse(StringBuffer JavaDoc msg) throws IOException JavaDoc
688     {
689
690         // Output the FTP response
691

692         if (m_out != null)
693         {
694             m_out.write(msg.toString());
695             m_out.write(CRLF);
696             m_out.flush();
697         }
698     }
699
700     /**
701      * Process a user command
702      *
703      * @param req
704      * FTPRequest
705      * @exception IOException
706      */

707     protected final void procUser(FTPRequest req) throws IOException JavaDoc
708     {
709
710         // Clear the current client information
711

712         setClientInformation(null);
713         setLoggedOn(false);
714
715         // Check if a user name has been specified
716

717         if (req.hasArgument() == false)
718         {
719             sendFTPResponse(501, "Syntax error in parameters or arguments");
720             return;
721         }
722
723         // Check for an anonymous login
724

725         if (getFTPServer().allowAnonymous() == true
726                 && req.getArgument().equalsIgnoreCase(getFTPServer().getAnonymousAccount()))
727         {
728
729             // Anonymous login, create guest client information
730

731             ClientInfo cinfo = new ClientInfo(getFTPServer().getAnonymousAccount(), null);
732             cinfo.setGuest(true);
733             setClientInformation(cinfo);
734
735             // Return the anonymous login response
736

737             sendFTPResponse(331, "Guest login ok, send your complete e-mail address as password");
738             return;
739         }
740
741         // Create client information for the user
742

743         setClientInformation(new ClientInfo(req.getArgument(), null));
744
745         // Valid user, wait for the password
746

747         sendFTPResponse(331, "User name okay, need password for " + req.getArgument());
748     }
749
750     /**
751      * Process a password command
752      *
753      * @param req
754      * FTPRequest
755      * @exception IOException
756      */

757     protected final void procPassword(FTPRequest req) throws IOException JavaDoc
758     {
759
760         // Check if the client information has been set, this indicates a user
761
// command has been received
762

763         if (hasClientInformation() == false)
764         {
765             sendFTPResponse(500, "Syntax error, command "
766                     + FTPCommand.getCommandName(req.isCommand()) + " unrecognized");
767             return;
768         }
769
770         // Check for an anonymous login, accept any password string
771

772         ClientInfo cInfo = getClientInformation();
773         
774         if (cInfo.isGuest())
775         {
776             if ( getFTPServer().allowAnonymous() == true)
777             {
778                 // Authenticate as the guest user
779

780                 AuthenticationComponent authComponent = getServer().getConfiguration().getAuthenticationComponent();
781                 cInfo.setUserName( authComponent.getGuestUserName());
782             }
783             else
784             {
785                 // Return an access denied error
786

787                 sendFTPResponse(530, "Access denied");
788     
789                 // DEBUG
790

791                 if (logger.isDebugEnabled() && hasDebug(DBG_STATE))
792                     logger.debug("Anonymous logon not allowed");
793     
794                 // Close the connection
795

796                 closeSession();
797             }
798         }
799
800         // Get the client information and store the received plain text password
801

802         cInfo.setPassword(req.getArgument());
803
804         // Use the normal authentication service as we have the plaintext password
805

806         AuthenticationService authService = getServer().getConfiguration().getAuthenticationService();
807         
808         try
809         {
810             // Authenticate the user
811

812             if ( cInfo.isGuest())
813             {
814                 // Authenticate as the guest user
815

816                 authService.authenticateAsGuest();
817             }
818             else
819             {
820                 // Authenticate as a normal user
821

822                 authService.authenticate( cInfo.getUserName(), cInfo.getPasswordAsCharArray());
823             }
824
825             // User successfully logged on
826

827             sendFTPResponse(230, "User logged in, proceed");
828             setLoggedOn(true);
829
830             // DEBUG
831

832             if (logger.isDebugEnabled() && hasDebug(DBG_STATE))
833                 logger.debug("User " + getClientInformation().getUserName() + ", logon successful");
834         }
835         catch (org.alfresco.repo.security.authentication.AuthenticationException ex)
836         {
837             // DEBUG
838

839             if ( logger.isDebugEnabled())
840                 logger.debug("Logon failed", ex);
841         }
842
843         // Check if the logon was successful
844

845         if ( isLoggedOn() == true)
846         {
847             // If the user has successfully logged on to the FTP server then inform listeners
848

849             getFTPServer().sessionLoggedOn(this);
850             
851             // If this is a guest logon then we need to set the root folder to the guest user home folder
852
// as guest is not allowed access to other areas
853

854             if ( cInfo.isGuest())
855             {
856                 // Generate a dynamic share with the guest users home folder as the root
857

858                 DiskSharedDevice guestShare = createHomeDiskShare( cInfo);
859                 addDynamicShare( guestShare);
860                 
861                 // Set the root path for the guest logon to the guest share
862

863                 StringBuilder JavaDoc rootPath = new StringBuilder JavaDoc();
864                 
865                 rootPath.append(FTP_SEPERATOR);
866                 rootPath.append( guestShare.getName());
867                 rootPath.append(FTP_SEPERATOR);
868                 
869                 FTPPath guestRoot = null;
870                 
871                 try
872                 {
873                     // Set the root path for this FTP session
874

875                     guestRoot = new FTPPath( rootPath.toString());
876                     guestRoot.setSharedDevice( guestShare);
877                     setRootPath( guestRoot);
878                 }
879                 catch ( InvalidPathException ex)
880                 {
881                     if ( logger.isErrorEnabled())
882                         logger.error("Error setting guest FTP root path", ex);
883                 }
884                 
885                 // DEBUG
886

887                 if (logger.isDebugEnabled() && hasDebug(DBG_STATE))
888                     logger.debug(" Using root path " + guestRoot);
889             }
890         }
891         else
892         {
893
894             // Return an access denied error
895

896             sendFTPResponse(530, "Access denied");
897
898             // DEBUG
899

900             if (logger.isDebugEnabled() && hasDebug(DBG_STATE))
901                 logger.debug("User " + getClientInformation().getUserName() + ", logon failed");
902
903             // Close the connection
904

905             closeSession();
906         }
907     }
908
909     /**
910      * Process a port command
911      *
912      * @param req
913      * FTPRequest
914      * @exception IOException
915      */

916     protected final void procPort(FTPRequest req) throws IOException JavaDoc
917     {
918
919         // Check if the user is logged in
920

921         if (isLoggedOn() == false)
922         {
923             sendFTPResponse(500, "");
924             return;
925         }
926
927         // Check if the parameter has been specified
928

929         if (req.hasArgument() == false)
930         {
931             sendFTPResponse(501, "Required argument missing");
932             return;
933         }
934
935         // Parse the address/port string into a IP address and port
936

937         StringTokenizer JavaDoc token = new StringTokenizer JavaDoc(req.getArgument(), ",");
938         if (token.countTokens() != 6)
939         {
940             sendFTPResponse(501, "Invalid argument");
941             return;
942         }
943
944         // Parse the client address
945

946         String JavaDoc addrStr = token.nextToken()
947                 + "." + token.nextToken() + "." + token.nextToken() + "." + token.nextToken();
948         InetAddress JavaDoc addr = null;
949
950         try
951         {
952             addr = InetAddress.getByName(addrStr);
953         }
954         catch (UnknownHostException JavaDoc ex)
955         {
956             sendFTPResponse(501, "Invalid argument (address)");
957             return;
958         }
959
960         // Parse the client port
961

962         int port = -1;
963
964         try
965         {
966             port = Integer.parseInt(token.nextToken()) * 256;
967             port += Integer.parseInt(token.nextToken());
968         }
969         catch (NumberFormatException JavaDoc ex)
970         {
971             sendFTPResponse(501, "Invalid argument (port)");
972             return;
973         }
974
975         // Create an active data session, the actual socket connection will be
976
// made later
977

978         m_dataSess = getFTPServer().allocateDataSession(this, addr, port);
979
980         // Return a success response to the client
981

982         sendFTPResponse(200, "Port OK");
983
984         // DEBUG
985

986         if (logger.isDebugEnabled() && hasDebug(DBG_DATAPORT))
987             logger.debug("Port open addr=" + addr + ", port=" + port);
988     }
989
990     /**
991      * Process a passive command
992      *
993      * @param req
994      * FTPRequest
995      * @exception IOException
996      */

997     protected final void procPassive(FTPRequest req) throws IOException JavaDoc
998     {
999
1000        // Check if the user is logged in
1001

1002        if (isLoggedOn() == false)
1003        {
1004            sendFTPResponse(500, "");
1005            return;
1006        }
1007
1008        // Create a passive data session
1009

1010        try
1011        {
1012            m_dataSess = getFTPServer().allocateDataSession(this, null, 0);
1013        }
1014        catch (IOException JavaDoc ex)
1015        {
1016            m_dataSess = null;
1017        }
1018
1019        // Check if the data session is valid
1020

1021        if (m_dataSess == null)
1022        {
1023            sendFTPResponse(550, "Requested action not taken");
1024            return;
1025        }
1026
1027        // Get the passive connection address/port and return to the client
1028

1029        int pasvPort = m_dataSess.getPassivePort();
1030
1031        StringBuffer JavaDoc msg = new StringBuffer JavaDoc();
1032
1033        msg.append("227 Entering Passive Mode (");
1034        msg.append(getFTPServer().getLocalFTPAddressString());
1035        msg.append(",");
1036        msg.append(pasvPort >> 8);
1037        msg.append(",");
1038        msg.append(pasvPort & 0xFF);
1039        msg.append(")");
1040
1041        sendFTPResponse(msg);
1042
1043        // DEBUG
1044

1045        if (logger.isDebugEnabled() && hasDebug(DBG_DATAPORT))
1046            logger.debug("Passive open addr=" + getFTPServer().getLocalFTPAddressString() + ", port=" + pasvPort);
1047    }
1048
1049    /**
1050     * Process a print working directory command
1051     *
1052     * @param req
1053     * FTPRequest
1054     * @exception IOException
1055     */

1056    protected final void procPrintWorkDir(FTPRequest req) throws IOException JavaDoc
1057    {
1058
1059        // Check if the user is logged in
1060

1061        if (isLoggedOn() == false)
1062        {
1063            sendFTPResponse(500, "");
1064            return;
1065        }
1066
1067        // Return the current working directory virtual path
1068

1069        sendFTPResponse(257, "\"" + m_cwd.getFTPPath() + "\"");
1070
1071        // DEBUG
1072

1073        if (logger.isDebugEnabled() && hasDebug(DBG_DIRECTORY))
1074            logger.debug("Pwd ftp="
1075                    + m_cwd.getFTPPath() + ", share=" + m_cwd.getShareName() + ", path=" + m_cwd.getSharePath());
1076    }
1077
1078    /**
1079     * Process a change working directory command
1080     *
1081     * @param req
1082     * FTPRequest
1083     * @exception IOException
1084     */

1085    protected final void procChangeWorkDir(FTPRequest req) throws IOException JavaDoc
1086    {
1087
1088        // Check if the user is logged in
1089

1090        if (isLoggedOn() == false)
1091        {
1092            sendFTPResponse(500, "");
1093            return;
1094        }
1095
1096        // Check if the request has a valid argument
1097

1098        if (req.hasArgument() == false)
1099        {
1100            sendFTPResponse(501, "Path not specified");
1101            return;
1102        }
1103
1104        // Create the new working directory path
1105

1106        FTPPath newPath = generatePathForRequest(req, false);
1107        if (newPath == null)
1108        {
1109            sendFTPResponse(550, "Invalid path " + req.getArgument());
1110            return;
1111        }
1112
1113        // Set the new current working directory
1114

1115        m_cwd = newPath;
1116
1117        // Return a success status
1118

1119        sendFTPResponse(250, "Requested file action OK");
1120
1121        // DEBUG
1122

1123        if (logger.isDebugEnabled() && hasDebug(DBG_DIRECTORY))
1124            logger.debug("Cwd ftp="
1125                    + m_cwd.getFTPPath() + ", share=" + m_cwd.getShareName() + ", path=" + m_cwd.getSharePath());
1126    }
1127
1128    /**
1129     * Process a change directory up command
1130     *
1131     * @param req
1132     * FTPRequest
1133     * @exception IOException
1134     */

1135    protected final void procCdup(FTPRequest req) throws IOException JavaDoc
1136    {
1137
1138        // Check if the user is logged in
1139

1140        if (isLoggedOn() == false)
1141        {
1142            sendFTPResponse(500, "");
1143            return;
1144        }
1145
1146        // Check if there is a current working directory path
1147

1148        if (m_cwd.isRootPath())
1149        {
1150
1151            // Already at the root directory, return an error status
1152

1153            sendFTPResponse(550, "Already at root directory");
1154            return;
1155        }
1156        else
1157        {
1158
1159            // Remove the last directory from the path
1160

1161            m_cwd.removeDirectory();
1162            if (m_cwd.isRootPath() == false && m_cwd.getSharedDevice() == null)
1163                m_cwd.setSharedDevice(getShareList(), this);
1164        }
1165
1166        // Return a success status
1167

1168        sendFTPResponse(250, "Requested file action OK");
1169
1170        // DEBUG
1171

1172        if (logger.isDebugEnabled() && hasDebug(DBG_DIRECTORY))
1173            logger.debug("Cdup ftp="
1174                    + m_cwd.getFTPPath() + ", share=" + m_cwd.getShareName() + ", path=" + m_cwd.getSharePath());
1175    }
1176
1177    /**
1178     * Process a long directory listing command
1179     *
1180     * @param req
1181     * FTPRequest
1182     * @exception IOException
1183     */

1184    protected final void procList(FTPRequest req) throws IOException JavaDoc
1185    {
1186
1187        // Check if the user is logged in
1188

1189        if (isLoggedOn() == false)
1190        {
1191            sendFTPResponse(500, "");
1192            return;
1193        }
1194
1195        // Check if the client has requested hidden files, via the '-a' option
1196

1197        boolean hidden = false;
1198
1199        if (req.hasArgument() && req.getArgument().startsWith(LIST_OPTION_HIDDEN))
1200        {
1201            // Indicate that we want hidden files in the listing
1202

1203            hidden = true;
1204
1205            // Remove the option from the command argument, and update the
1206
// request
1207

1208            String JavaDoc arg = req.getArgument();
1209            int pos = arg.indexOf(" ");
1210            if (pos > 0)
1211                arg = arg.substring(pos + 1);
1212            else
1213                arg = null;
1214
1215            req.updateArgument(arg);
1216        }
1217
1218        // Create the path for the file listing
1219

1220        FTPPath ftpPath = m_cwd;
1221        if ( req.hasArgument())
1222            ftpPath = generatePathForRequest(req, true);
1223
1224        if (ftpPath == null)
1225        {
1226            sendFTPResponse(500, "Invalid path");
1227            return;
1228        }
1229
1230        // Check if the session has the required access
1231

1232        if (ftpPath.isRootPath() == false)
1233        {
1234
1235            // Check if the session has access to the filesystem
1236

1237            TreeConnection tree = getTreeConnection(ftpPath.getSharedDevice());
1238            if (tree == null || tree.hasReadAccess() == false)
1239            {
1240
1241                // Session does not have access to the filesystem
1242

1243                sendFTPResponse(550, "Access denied");
1244                return;
1245            }
1246        }
1247
1248        // Send the intermediate response
1249

1250        sendFTPResponse(150, "File status okay, about to open data connection");
1251
1252        // Check if there is an active data session
1253

1254        if (m_dataSess == null)
1255        {
1256            sendFTPResponse(425, "Can't open data connection");
1257            return;
1258        }
1259
1260        // Get the data connection socket
1261

1262        Socket JavaDoc dataSock = null;
1263
1264        try
1265        {
1266            dataSock = m_dataSess.getSocket();
1267        }
1268        catch (Exception JavaDoc ex)
1269        {
1270            logger.debug(ex);
1271        }
1272
1273        if (dataSock == null)
1274        {
1275            sendFTPResponse(426, "Connection closed; transfer aborted");
1276            return;
1277        }
1278
1279        // Output the directory listing to the client
1280

1281        Writer JavaDoc dataWrt = null;
1282
1283        try
1284        {
1285
1286            // Open an output stream to the client
1287

1288            dataWrt = new OutputStreamWriter JavaDoc(dataSock.getOutputStream());
1289
1290            // Check if a path has been specified to list
1291

1292            Vector JavaDoc<FileInfo> files = null;
1293
1294            if (req.hasArgument())
1295            {
1296            }
1297
1298            // Get a list of file information objects for the current directory
1299

1300            files = listFilesForPath(ftpPath, false, hidden);
1301
1302            // Output the file list to the client
1303

1304            if (files != null)
1305            {
1306
1307                // DEBUG
1308

1309                if (logger.isDebugEnabled() && hasDebug(DBG_SEARCH))
1310                    logger.debug("List found " + files.size() + " files in " + ftpPath.getFTPPath());
1311
1312                // Output the file information to the client
1313

1314                StringBuffer JavaDoc str = new StringBuffer JavaDoc(256);
1315
1316                for (FileInfo finfo : files)
1317                {
1318
1319                    // Build the output record
1320

1321                    str.setLength(0);
1322
1323                    str.append(finfo.isDirectory() ? "d" : "-");
1324                    str.append("rw-rw-rw- 1 user group ");
1325                    str.append(finfo.getSize());
1326                    str.append(" ");
1327
1328                    FTPDate.packUnixDate(str, new Date JavaDoc(finfo.getModifyDateTime()));
1329
1330                    str.append(" ");
1331                    str.append(finfo.getFileName());
1332                    str.append(CRLF);
1333
1334                    // Output the file information record
1335

1336                    dataWrt.write(str.toString());
1337                }
1338
1339                // Flush the data stream
1340

1341                dataWrt.flush();
1342            }
1343
1344            // Close the data stream and socket
1345

1346            dataWrt.close();
1347            dataWrt = null;
1348
1349            getFTPServer().releaseDataSession(m_dataSess);
1350            m_dataSess = null;
1351
1352            // End of file list transmission
1353

1354            sendFTPResponse(226, "Closing data connection");
1355        }
1356        catch (Exception JavaDoc ex)
1357        {
1358
1359            // Failed to send file listing
1360

1361            sendFTPResponse(451, "Error reading file list");
1362        } finally
1363        {
1364
1365            // Close the data stream to the client
1366

1367            if (dataWrt != null)
1368                dataWrt.close();
1369
1370            // Close the data connection to the client
1371

1372            if (m_dataSess != null)
1373            {
1374                getFTPServer().releaseDataSession(m_dataSess);
1375                m_dataSess = null;
1376            }
1377        }
1378    }
1379
1380    /**
1381     * Process a short directory listing command
1382     *
1383     * @param req
1384     * FTPRequest
1385     * @exception IOException
1386     */

1387    protected final void procNList(FTPRequest req) throws IOException JavaDoc
1388    {
1389
1390        // Check if the user is logged in
1391

1392        if (isLoggedOn() == false)
1393        {
1394            sendFTPResponse(500, "");
1395            return;
1396        }
1397
1398        // Create the path for the file listing
1399

1400        FTPPath ftpPath = m_cwd;
1401        if (req.hasArgument())
1402            ftpPath = generatePathForRequest(req, true);
1403
1404        if (ftpPath == null)
1405        {
1406            sendFTPResponse(500, "Invalid path");
1407            return;
1408        }
1409
1410        // Check if the session has the required access
1411

1412        if (ftpPath.isRootPath() == false)
1413        {
1414
1415            // Check if the session has access to the filesystem
1416

1417            TreeConnection tree = getTreeConnection(ftpPath.getSharedDevice());
1418            if (tree == null || tree.hasReadAccess() == false)
1419            {
1420
1421                // Session does not have access to the filesystem
1422

1423                sendFTPResponse(550, "Access denied");
1424                return;
1425            }
1426        }
1427
1428        // Send the intermediate response
1429

1430        sendFTPResponse(150, "File status okay, about to open data connection");
1431
1432        // Check if there is an active data session
1433

1434        if (m_dataSess == null)
1435        {
1436            sendFTPResponse(425, "Can't open data connection");
1437            return;
1438        }
1439
1440        // Get the data connection socket
1441

1442        Socket JavaDoc dataSock = null;
1443
1444        try
1445        {
1446            dataSock = m_dataSess.getSocket();
1447        }
1448        catch (Exception JavaDoc ex)
1449        {
1450            logger.error("Data socket error", ex);
1451        }
1452
1453        if (dataSock == null)
1454        {
1455            sendFTPResponse(426, "Connection closed; transfer aborted");
1456            return;
1457        }
1458
1459        // Output the directory listing to the client
1460

1461        Writer JavaDoc dataWrt = null;
1462
1463        try
1464        {
1465
1466            // Open an output stream to the client
1467

1468            dataWrt = new OutputStreamWriter JavaDoc(dataSock.getOutputStream());
1469
1470            // Check if a path has been specified to list
1471

1472            Vector JavaDoc<FileInfo> files = null;
1473
1474            if (req.hasArgument())
1475            {
1476            }
1477
1478            // Get a list of file information objects for the current directory
1479

1480            files = listFilesForPath(ftpPath, false, false);
1481
1482            // Output the file list to the client
1483

1484            if (files != null)
1485            {
1486
1487                // DEBUG
1488

1489                if (logger.isDebugEnabled() && hasDebug(DBG_SEARCH))
1490                    logger.debug("List found " + files.size() + " files in " + ftpPath.getFTPPath());
1491
1492                // Output the file information to the client
1493

1494                for (FileInfo finfo : files)
1495                {
1496
1497                    // Output the file information record
1498

1499                    dataWrt.write(finfo.getFileName());
1500                    dataWrt.write(CRLF);
1501                }
1502            }
1503
1504            // End of file list transmission
1505

1506            sendFTPResponse(226, "Closing data connection");
1507        }
1508        catch (Exception JavaDoc ex)
1509        {
1510
1511            // Failed to send file listing
1512

1513            sendFTPResponse(451, "Error reading file list");
1514        } finally
1515        {
1516
1517            // Close the data stream to the client
1518

1519            if (dataWrt != null)
1520                dataWrt.close();
1521
1522            // Close the data connection to the client
1523

1524            if (m_dataSess != null)
1525            {
1526                getFTPServer().releaseDataSession(m_dataSess);
1527                m_dataSess = null;
1528            }
1529        }
1530    }
1531
1532    /**
1533     * Process a system status command
1534     *
1535     * @param req
1536     * FTPRequest
1537     * @exception IOException
1538     */

1539    protected final void procSystemStatus(FTPRequest req) throws IOException JavaDoc
1540    {
1541
1542        // Return the system type
1543

1544        sendFTPResponse(215, "UNIX Type: Java FTP Server");
1545    }
1546
1547    /**
1548     * Process a server status command
1549     *
1550     * @param req
1551     * FTPRequest
1552     * @exception IOException
1553     */

1554    protected final void procServerStatus(FTPRequest req) throws IOException JavaDoc
1555    {
1556
1557        // Return server status information
1558

1559        sendFTPResponse(211, "JLAN Server - Java FTP Server");
1560    }
1561
1562    /**
1563     * Process a help command
1564     *
1565     * @param req
1566     * FTPRequest
1567     * @exception IOException
1568     */

1569    protected final void procHelp(FTPRequest req) throws IOException JavaDoc
1570    {
1571
1572        // Return help information
1573

1574        sendFTPResponse(211, "HELP text");
1575    }
1576
1577    /**
1578     * Process a no-op command
1579     *
1580     * @param req
1581     * FTPRequest
1582     * @exception IOException
1583     */

1584    protected final void procNoop(FTPRequest req) throws IOException JavaDoc
1585    {
1586
1587        // Return a response
1588

1589        sendFTPResponse(200, "");
1590    }
1591
1592    /**
1593     * Process a quit command
1594     *
1595     * @param req
1596     * FTPRequest
1597     * @exception IOException
1598     */

1599    protected final void procQuit(FTPRequest req) throws IOException JavaDoc
1600    {
1601
1602        // Return a response
1603

1604        sendFTPResponse(221, "Bye");
1605
1606        // DEBUG
1607

1608        if (logger.isDebugEnabled() && hasDebug(DBG_STATE))
1609            logger.debug("Quit closing connection(s) to client");
1610
1611        // Close the session(s) to the client
1612

1613        closeSession();
1614    }
1615
1616    /**
1617     * Process a type command
1618     *
1619     * @param req
1620     * FTPRequest
1621     * @exception IOException
1622     */

1623    protected final void procType(FTPRequest req) throws IOException JavaDoc
1624    {
1625
1626        // Check if an argument has been specified
1627

1628        if (req.hasArgument() == false)
1629        {
1630            sendFTPResponse(501, "Syntax error, parameter required");
1631            return;
1632        }
1633
1634        // Check if ASCII or binary mode is enabled
1635

1636        String JavaDoc arg = req.getArgument().toUpperCase();
1637        if (arg.startsWith("A"))
1638            setBinary(false);
1639        else if (arg.startsWith("I") || arg.startsWith("L"))
1640            setBinary(true);
1641        else
1642        {
1643
1644            // Invalid argument
1645

1646            sendFTPResponse(501, "Syntax error, invalid parameter");
1647            return;
1648        }
1649
1650        // Return a success status
1651

1652        sendFTPResponse(200, "Command OK");
1653
1654        // DEBUG
1655

1656        if (logger.isDebugEnabled() && hasDebug(DBG_STATE))
1657            logger.debug("Type arg=" + req.getArgument() + ", binary=" + m_binary);
1658    }
1659
1660    /**
1661     * Process a restart command
1662     *
1663     * @param req
1664     * FTPRequest
1665     * @exception IOException
1666     */

1667    protected final void procRestart(FTPRequest req) throws IOException JavaDoc
1668    {
1669
1670        // Check if the user is logged in
1671

1672        if (isLoggedOn() == false)
1673        {
1674            sendFTPResponse(500, "");
1675            return;
1676        }
1677
1678        // Check if an argument has been specified
1679

1680        if (req.hasArgument() == false)
1681        {
1682            sendFTPResponse(501, "Syntax error, parameter required");
1683            return;
1684        }
1685
1686        // Validate the restart position
1687

1688        try
1689        {
1690            m_restartPos = Integer.parseInt(req.getArgument());
1691        }
1692        catch (NumberFormatException JavaDoc ex)
1693        {
1694            sendFTPResponse(501, "Invalid restart position");
1695            return;
1696        }
1697
1698        // Return a success status
1699

1700        sendFTPResponse(350, "Restart OK");
1701
1702        // DEBUG
1703

1704        if (logger.isDebugEnabled() && hasDebug(DBG_FILEIO))
1705            logger.debug("Restart pos=" + m_restartPos);
1706    }
1707
1708    /**
1709     * Process a return file command
1710     *
1711     * @param req
1712     * FTPRequest
1713     * @exception IOException
1714     */

1715    protected final void procReturnFile(FTPRequest req) throws IOException JavaDoc
1716    {
1717
1718        // Check if the user is logged in
1719

1720        if (isLoggedOn() == false)
1721        {
1722            sendFTPResponse(500, "");
1723            return;
1724        }
1725
1726        // Check if an argument has been specified
1727

1728        if (req.hasArgument() == false)
1729        {
1730            sendFTPResponse(501, "Syntax error, parameter required");
1731            return;
1732        }
1733
1734        // Create the path for the file listing
1735

1736        FTPPath ftpPath = generatePathForRequest(req, true);
1737        if (ftpPath == null)
1738        {
1739            sendFTPResponse(500, "Invalid path");
1740            return;
1741        }
1742
1743        // Check if the path is the root directory
1744

1745        if (ftpPath.isRootPath() || ftpPath.isRootSharePath())
1746        {
1747            sendFTPResponse(550, "That is a directory");
1748            return;
1749        }
1750
1751        // Send the intermediate response
1752

1753        sendFTPResponse(150, "Connection accepted");
1754
1755        // Check if there is an active data session
1756

1757        if (m_dataSess == null)
1758        {
1759            sendFTPResponse(425, "Can't open data connection");
1760            return;
1761        }
1762
1763        // Get the data connection socket
1764

1765        Socket JavaDoc dataSock = null;
1766
1767        try
1768        {
1769            dataSock = m_dataSess.getSocket();
1770        }
1771        catch (Exception JavaDoc ex)
1772        {
1773        }
1774
1775        if (dataSock == null)
1776        {
1777            sendFTPResponse(426, "Connection closed; transfer aborted");
1778            return;
1779        }
1780
1781        // DEBUG
1782

1783        if (logger.isDebugEnabled() && hasDebug(DBG_FILE))
1784            logger.debug("Returning ftp="
1785                    + ftpPath.getFTPPath() + ", share=" + ftpPath.getShareName() + ", path=" + ftpPath.getSharePath());
1786
1787        // Send the file to the client
1788

1789        OutputStream JavaDoc os = null;
1790        DiskInterface disk = null;
1791        TreeConnection tree = null;
1792        NetworkFile netFile = null;
1793
1794        try
1795        {
1796
1797            // Open an output stream to the client
1798

1799            os = dataSock.getOutputStream();
1800
1801            // Create a temporary tree connection
1802

1803            tree = getTreeConnection(ftpPath.getSharedDevice());
1804
1805            // Check if the file exists and it is a file, if so then open the
1806
// file
1807

1808            disk = (DiskInterface) ftpPath.getSharedDevice().getInterface();
1809
1810            // Create the file open parameters
1811

1812            FileOpenParams params = new FileOpenParams(ftpPath.getSharePath(), FileAction.OpenIfExists,
1813                    AccessMode.ReadOnly, 0);
1814
1815            // Check if the file exists and it is a file
1816

1817            int sts = disk.fileExists(this, tree, ftpPath.getSharePath());
1818
1819            if (sts == FileStatus.FileExists)
1820            {
1821
1822                // Open the file
1823

1824                netFile = disk.openFile(this, tree, params);
1825            }
1826
1827            // Check if the file has been opened
1828

1829            if (netFile == null)
1830            {
1831                sendFTPResponse(550, "File " + req.getArgument() + " not available");
1832                return;
1833            }
1834
1835            // Allocate the buffer for the file data
1836

1837            byte[] buf = new byte[DEFAULT_BUFFERSIZE];
1838            long filePos = m_restartPos;
1839
1840            int len = -1;
1841
1842            while (filePos < netFile.getFileSize())
1843            {
1844
1845                // Read another block of data from the file
1846

1847                len = disk.readFile(this, tree, netFile, buf, 0, buf.length, filePos);
1848
1849                // DEBUG
1850

1851                if (logger.isDebugEnabled() && hasDebug(DBG_FILEIO))
1852                    logger.debug(" Write len=" + len + " bytes");
1853
1854                // Write the current data block to the client, update the file
1855
// position
1856

1857                if (len > 0)
1858                {
1859
1860                    // Write the data to the client
1861

1862                    os.write(buf, 0, len);
1863
1864                    // Update the file position
1865

1866                    filePos += len;
1867                }
1868            }
1869
1870            // Close the output stream to the client
1871

1872            os.close();
1873            os = null;
1874
1875            // Indicate that the file has been transmitted
1876

1877            sendFTPResponse(226, "Closing data connection");
1878
1879            // Close the data session
1880

1881            getFTPServer().releaseDataSession(m_dataSess);
1882            m_dataSess = null;
1883
1884            // Close the network file
1885

1886            disk.closeFile(this, tree, netFile);
1887            netFile = null;
1888
1889            // DEBUG
1890

1891            if (logger.isDebugEnabled() && hasDebug(DBG_FILEIO))
1892                logger.debug(" Transfer complete, file closed");
1893        }
1894        catch (SocketException JavaDoc ex)
1895        {
1896
1897            // DEBUG
1898

1899            if (logger.isDebugEnabled() && hasDebug(DBG_ERROR))
1900                logger.debug(" Error during transfer", ex);
1901
1902            // Close the data socket to the client
1903

1904            if (m_dataSess != null)
1905            {
1906                m_dataSess.closeSession();
1907                m_dataSess = null;
1908            }
1909
1910            // Indicate that there was an error during transmission of the file
1911
// data
1912

1913            sendFTPResponse(426, "Data connection closed by client");
1914        }
1915        catch (Exception JavaDoc ex)
1916        {
1917
1918            // DEBUG
1919

1920            if (logger.isDebugEnabled() && hasDebug(DBG_ERROR))
1921                logger.debug(" Error during transfer", ex);
1922
1923            // Indicate that there was an error during transmission of the file
1924
// data
1925

1926            sendFTPResponse(426, "Error during transmission");
1927        } finally
1928        {
1929
1930            // Close the network file
1931

1932            if (netFile != null && disk != null && tree != null)
1933                disk.closeFile(this, tree, netFile);
1934
1935            // Close the output stream to the client
1936

1937            if (os != null)
1938                os.close();
1939
1940            // Close the data connection to the client
1941

1942            if (m_dataSess != null)
1943            {
1944                getFTPServer().releaseDataSession(m_dataSess);
1945                m_dataSess = null;
1946            }
1947        }
1948    }
1949
1950    /**
1951     * Process a store file command
1952     *
1953     * @param req
1954     * FTPRequest
1955     * @exception IOException
1956     */

1957    protected final void procStoreFile(FTPRequest req) throws IOException JavaDoc
1958    {
1959
1960        // Check if the user is logged in
1961

1962        if (isLoggedOn() == false)
1963        {
1964            sendFTPResponse(500, "");
1965            return;
1966        }
1967
1968        // Check if an argument has been specified
1969

1970        if (req.hasArgument() == false)
1971        {
1972            sendFTPResponse(501, "Syntax error, parameter required");
1973            return;
1974        }
1975
1976        // Create the path for the file listing
1977

1978        FTPPath ftpPath = generatePathForRequest(req, true, false);
1979        if (ftpPath == null)
1980        {
1981            sendFTPResponse(500, "Invalid path");
1982            return;
1983        }
1984
1985        // Send the file to the client
1986

1987        InputStream JavaDoc is = null;
1988        DiskInterface disk = null;
1989        TreeConnection tree = null;
1990        NetworkFile netFile = null;
1991
1992        try
1993        {
1994
1995            // Create a temporary tree connection
1996

1997            tree = getTreeConnection(ftpPath.getSharedDevice());
1998
1999            // Check if the session has the required access to the filesystem
2000

2001            if (tree == null || tree.hasWriteAccess() == false)
2002            {
2003
2004                // Session does not have write access to the filesystem
2005

2006                sendFTPResponse(550, "Access denied");
2007                return;
2008            }
2009
2010            // Check if the file exists
2011

2012            disk = (DiskInterface) ftpPath.getSharedDevice().getInterface();
2013            int sts = disk.fileExists(this, tree, ftpPath.getSharePath());
2014
2015            if (sts == FileStatus.DirectoryExists)
2016            {
2017
2018                // Return an error status
2019

2020                sendFTPResponse(500, "Invalid path (existing directory)");
2021                return;
2022            }
2023
2024            // Create the file open parameters
2025

2026            FileOpenParams params = new FileOpenParams(ftpPath.getSharePath(),
2027                    sts == FileStatus.FileExists ? FileAction.TruncateExisting : FileAction.CreateNotExist,
2028                    AccessMode.ReadWrite, 0);
2029
2030            // Create a new file to receive the data
2031

2032            if (sts == FileStatus.FileExists)
2033            {
2034
2035                // Overwrite the existing file
2036

2037                netFile = disk.openFile(this, tree, params);
2038            }
2039            else
2040            {
2041
2042                // Create a new file
2043

2044                netFile = disk.createFile(this, tree, params);
2045            }
2046
2047            // Notify change listeners that a new file has been created
2048

2049            DiskDeviceContext diskCtx = (DiskDeviceContext) tree.getContext();
2050
2051            if (diskCtx.hasChangeHandler())
2052                diskCtx.getChangeHandler().notifyFileChanged(NotifyChange.ActionAdded, ftpPath.getSharePath());
2053
2054            // Send the intermediate response
2055

2056            sendFTPResponse(150, "File status okay, about to open data connection");
2057
2058            // Check if there is an active data session
2059

2060            if (m_dataSess == null)
2061            {
2062                sendFTPResponse(425, "Can't open data connection");
2063                return;
2064            }
2065
2066            // Get the data connection socket
2067

2068            Socket JavaDoc dataSock = null;
2069
2070            try
2071            {
2072                dataSock = m_dataSess.getSocket();
2073            }
2074            catch (Exception JavaDoc ex)
2075            {
2076            }
2077
2078            if (dataSock == null)
2079            {
2080                sendFTPResponse(426, "Connection closed; transfer aborted");
2081                return;
2082            }
2083
2084            // Open an input stream from the client
2085

2086            is = dataSock.getInputStream();
2087
2088            // DEBUG
2089

2090            if (logger.isDebugEnabled() && hasDebug(DBG_FILE))
2091                logger.debug("Storing ftp="
2092                        + ftpPath.getFTPPath() + ", share=" + ftpPath.getShareName() + ", path="
2093                        + ftpPath.getSharePath());
2094
2095            // Allocate the buffer for the file data
2096

2097            byte[] buf = new byte[DEFAULT_BUFFERSIZE];
2098            long filePos = 0;
2099            int len = is.read(buf, 0, buf.length);
2100
2101            while (len > 0)
2102            {
2103
2104                // DEBUG
2105

2106                if (logger.isDebugEnabled() && hasDebug(DBG_FILEIO))
2107                    logger.debug(" Receive len=" + len + " bytes");
2108
2109                // Write the current data block to the file, update the file
2110
// position
2111

2112                disk.writeFile(this, tree, netFile, buf, 0, len, filePos);
2113                filePos += len;
2114
2115                // Read another block of data from the client
2116

2117                len = is.read(buf, 0, buf.length);
2118            }
2119
2120            // Close the input stream from the client
2121

2122            is.close();
2123            is = null;
2124
2125            // Close the network file
2126

2127            disk.closeFile(this, tree, netFile);
2128            netFile = null;
2129
2130            // Indicate that the file has been received
2131

2132            sendFTPResponse(226, "Closing data connection");
2133
2134            // DEBUG
2135

2136            if (logger.isDebugEnabled() && hasDebug(DBG_FILEIO))
2137                logger.debug(" Transfer complete, file closed");
2138        }
2139        catch( AccessDeniedException ex)
2140        {
2141            // DEBUG
2142

2143            if (logger.isDebugEnabled() && hasDebug(DBG_ERROR))
2144                logger.debug(" Access denied", ex);
2145
2146            // Session does not have write access to the filesystem
2147

2148            sendFTPResponse(550, "Access denied");
2149        }
2150        catch (SocketException JavaDoc ex)
2151        {
2152            // DEBUG
2153

2154            if (logger.isDebugEnabled() && hasDebug(DBG_ERROR))
2155                logger.debug(" Error during transfer", ex);
2156
2157            // Close the data socket to the client
2158

2159            if (m_dataSess != null)
2160            {
2161                getFTPServer().releaseDataSession(m_dataSess);
2162                m_dataSess = null;
2163            }
2164
2165            // Indicate that there was an error during transmission of the file
2166
// data
2167

2168            sendFTPResponse(426, "Data connection closed by client");
2169        }
2170        catch (DiskFullException ex)
2171        {
2172
2173            // DEBUG
2174

2175            if (logger.isDebugEnabled() && hasDebug(DBG_ERROR))
2176                logger.debug(" Error during transfer", ex);
2177
2178            // Close the data socket to the client
2179

2180            if (m_dataSess != null)
2181            {
2182                getFTPServer().releaseDataSession(m_dataSess);
2183                m_dataSess = null;
2184            }
2185
2186            // Indicate that there was an error during writing of the file
2187

2188            sendFTPResponse(451, "Disk full");
2189        }
2190        catch (Exception JavaDoc ex)
2191        {
2192
2193            // DEBUG
2194

2195            if (logger.isDebugEnabled() && hasDebug(DBG_ERROR))
2196                logger.debug(" Error during transfer", ex);
2197
2198            // Indicate that there was an error during transmission of the file
2199
// data
2200

2201            sendFTPResponse(426, "Error during transmission");
2202        }
2203        finally
2204        {
2205
2206            // Close the network file
2207

2208            if (netFile != null && disk != null && tree != null)
2209                disk.closeFile(this, tree, netFile);
2210
2211            // Close the input stream to the client
2212

2213            if (is != null)
2214                is.close();
2215
2216            // Close the data connection to the client
2217

2218            if (m_dataSess != null)
2219            {
2220                getFTPServer().releaseDataSession(m_dataSess);
2221                m_dataSess = null;
2222            }
2223        }
2224    }
2225
2226    /**
2227     * Process a delete file command
2228     *
2229     * @param req
2230     * FTPRequest
2231     * @exception IOException
2232     */

2233    protected final void procDeleteFile(FTPRequest req) throws IOException JavaDoc
2234    {
2235
2236        // Check if the user is logged in
2237

2238        if (isLoggedOn() == false)
2239        {
2240            sendFTPResponse(500, "");
2241            return;
2242        }
2243
2244        // Check if an argument has been specified
2245

2246        if (req.hasArgument() == false)
2247        {
2248            sendFTPResponse(501, "Syntax error, parameter required");
2249            return;
2250        }
2251
2252        // Create the path for the file
2253

2254        FTPPath ftpPath = generatePathForRequest(req, true);
2255        if (ftpPath == null)
2256        {
2257            sendFTPResponse(550, "Invalid path specified");
2258            return;
2259        }
2260
2261        // Delete the specified file
2262

2263        DiskInterface disk = null;
2264        TreeConnection tree = null;
2265
2266        try
2267        {
2268
2269            // Create a temporary tree connection
2270

2271            tree = getTreeConnection(ftpPath.getSharedDevice());
2272
2273            // Check if the session has the required access to the filesystem
2274

2275            if (tree == null || tree.hasWriteAccess() == false)
2276            {
2277
2278                // Session does not have write access to the filesystem
2279

2280                sendFTPResponse(550, "Access denied");
2281                return;
2282            }
2283
2284            // Check if the file exists and it is a file
2285

2286            disk = (DiskInterface) ftpPath.getSharedDevice().getInterface();
2287            int sts = disk.fileExists(this, tree, ftpPath.getSharePath());
2288
2289            if (sts == FileStatus.FileExists)
2290            {
2291
2292                // Delete the file
2293

2294                disk.deleteFile(this, tree, ftpPath.getSharePath());
2295
2296                // Check if there are any file/directory change notify requests
2297
// active
2298

2299                DiskDeviceContext diskCtx = (DiskDeviceContext) tree.getContext();
2300                if (diskCtx.hasChangeHandler())
2301                    diskCtx.getChangeHandler().notifyFileChanged(NotifyChange.ActionRemoved, ftpPath.getSharePath());
2302
2303                // DEBUG
2304

2305                if (logger.isDebugEnabled() && hasDebug(DBG_FILE))
2306                    logger.debug("Deleted ftp="
2307                            + ftpPath.getFTPPath() + ", share=" + ftpPath.getShareName() + ", path="
2308                            + ftpPath.getSharePath());
2309            }
2310            else
2311            {
2312
2313                // File does not exist or is a directory
2314

2315                sendFTPResponse(550, "File "
2316                        + req.getArgument() + (sts == FileStatus.NotExist ? " not available" : " is a directory"));
2317                return;
2318            }
2319        }
2320        catch (AccessDeniedException ex)
2321        {
2322            // DEBUG
2323

2324            if (logger.isDebugEnabled() && hasDebug(DBG_ERROR))
2325                logger.debug(" Access denied", ex);
2326
2327            // Session does not have write access to the filesystem
2328

2329            sendFTPResponse(550, "Access denied");
2330        }
2331        catch (Exception JavaDoc ex)
2332        {
2333            sendFTPResponse(450, "File action not taken");
2334            return;
2335        }
2336
2337        // Return a success status
2338

2339        sendFTPResponse(250, "File " + req.getArgument() + " deleted");
2340    }
2341
2342    /**
2343     * Process a rename from command
2344     *
2345     * @param req
2346     * FTPRequest
2347     * @exception IOException
2348     */

2349    protected final void procRenameFrom(FTPRequest req) throws IOException JavaDoc
2350    {
2351
2352        // Check if the user is logged in
2353

2354        if (isLoggedOn() == false)
2355        {
2356            sendFTPResponse(500, "");
2357            return;
2358        }
2359
2360        // Check if an argument has been specified
2361

2362        if (req.hasArgument() == false)
2363        {
2364            sendFTPResponse(501, "Syntax error, parameter required");
2365            return;
2366        }
2367
2368        // Clear the current rename from path details, if any
2369

2370        m_renameFrom = null;
2371
2372        // Create the path for the file/directory
2373

2374        FTPPath ftpPath = generatePathForRequest(req, false, false);
2375        if (ftpPath == null)
2376        {
2377            sendFTPResponse(550, "Invalid path specified");
2378            return;
2379        }
2380
2381        // Check that the file exists, and it is a file
2382

2383        DiskInterface disk = null;
2384        TreeConnection tree = null;
2385
2386        try
2387        {
2388
2389            // Create a temporary tree connection
2390

2391            tree = getTreeConnection(ftpPath.getSharedDevice());
2392
2393            // Check if the session has the required access to the filesystem
2394

2395            if (tree == null || tree.hasWriteAccess() == false)
2396            {
2397
2398                // Session does not have write access to the filesystem
2399

2400                sendFTPResponse(550, "Access denied");
2401                return;
2402            }
2403
2404            // Check if the file exists and it is a file
2405

2406            disk = (DiskInterface) ftpPath.getSharedDevice().getInterface();
2407            int sts = disk.fileExists(this, tree, ftpPath.getSharePath());
2408
2409            if (sts != FileStatus.NotExist)
2410            {
2411
2412                // Save the rename from file details, rename to command should
2413
// follow
2414

2415                m_renameFrom = ftpPath;
2416
2417                // DEBUG
2418

2419                if (logger.isDebugEnabled() && hasDebug(DBG_FILE))
2420                    logger.debug("RenameFrom ftp="
2421                            + ftpPath.getFTPPath() + ", share=" + ftpPath.getShareName() + ", path="
2422                            + ftpPath.getSharePath());
2423            }
2424            else
2425            {
2426
2427                // File/directory does not exist
2428

2429                sendFTPResponse(550, "File "
2430                        + req.getArgument() + (sts == FileStatus.NotExist ? " not available" : " is a directory"));
2431                return;
2432            }
2433        }
2434        catch (Exception JavaDoc ex)
2435        {
2436            sendFTPResponse(450, "File action not taken");
2437            return;
2438        }
2439
2440        // Return a success status
2441

2442        sendFTPResponse(350, "File " + req.getArgument() + " OK");
2443    }
2444
2445    /**
2446     * Process a rename to command
2447     *
2448     * @param req
2449     * FTPRequest
2450     * @exception IOException
2451     */

2452    protected final void procRenameTo(FTPRequest req) throws IOException JavaDoc
2453    {
2454
2455        // Check if the user is logged in
2456

2457        if (isLoggedOn() == false)
2458        {
2459            sendFTPResponse(500, "");
2460            return;
2461        }
2462
2463        // Check if an argument has been specified
2464

2465        if (req.hasArgument() == false)
2466        {
2467            sendFTPResponse(501, "Syntax error, parameter required");
2468            return;
2469        }
2470
2471        // Check if the rename from has already been set
2472

2473        if (m_renameFrom == null)
2474        {
2475            sendFTPResponse(550, "Rename from not set");
2476            return;
2477        }
2478
2479        // Create the path for the new file name
2480

2481        FTPPath ftpPath = generatePathForRequest(req, true, false);
2482        if (ftpPath == null)
2483        {
2484            sendFTPResponse(550, "Invalid path specified");
2485            return;
2486        }
2487
2488        // Check that the rename is on the same share
2489

2490        if (m_renameFrom.getShareName().compareTo(ftpPath.getShareName()) != 0)
2491        {
2492            sendFTPResponse(550, "Cannot rename across shares");
2493            return;
2494        }
2495
2496        // Rename the file
2497

2498        DiskInterface disk = null;
2499        TreeConnection tree = null;
2500
2501        try
2502        {
2503
2504            // Create a temporary tree connection
2505

2506            tree = getTreeConnection(ftpPath.getSharedDevice());
2507
2508            // Check if the session has the required access to the filesystem
2509

2510            if (tree == null || tree.hasWriteAccess() == false)
2511            {
2512
2513                // Session does not have write access to the filesystem
2514

2515                sendFTPResponse(550, "Access denied");
2516                return;
2517            }
2518
2519            // Check if the file exists and it is a file
2520

2521            disk = (DiskInterface) ftpPath.getSharedDevice().getInterface();
2522            int sts = disk.fileExists(this, tree, ftpPath.getSharePath());
2523
2524            if (sts == FileStatus.NotExist)
2525            {
2526
2527                // Rename the file/directory
2528

2529                disk.renameFile(this, tree, m_renameFrom.getSharePath(), ftpPath.getSharePath());
2530
2531                // Check if there are any file/directory change notify requests
2532
// active
2533

2534                DiskDeviceContext diskCtx = (DiskDeviceContext) tree.getContext();
2535                if (diskCtx.hasChangeHandler())
2536                    diskCtx.getChangeHandler().notifyRename(m_renameFrom.getSharePath(), ftpPath.getSharePath());
2537
2538                // DEBUG
2539

2540                if (logger.isDebugEnabled() && hasDebug(DBG_FILE))
2541                    logger.debug("RenameTo ftp="
2542                            + ftpPath.getFTPPath() + ", share=" + ftpPath.getShareName() + ", path="
2543                            + ftpPath.getSharePath());
2544            }
2545            else
2546            {
2547
2548                // File does not exist or is a directory
2549

2550                sendFTPResponse(550, "File "
2551                        + req.getArgument() + (sts == FileStatus.NotExist ? " not available" : " is a directory"));
2552                return;
2553            }
2554        }
2555        catch (Exception JavaDoc ex)
2556        {
2557            sendFTPResponse(450, "File action not taken");
2558            return;
2559        }
2560        finally
2561        {
2562
2563            // Clear the rename details
2564

2565            m_renameFrom = null;
2566        }
2567
2568        // Return a success status
2569

2570        sendFTPResponse(250, "File renamed OK");
2571    }
2572
2573    /**
2574     * Process a create directory command
2575     *
2576     * @param req
2577     * FTPRequest
2578     * @exception IOException
2579     */

2580    protected final void procCreateDirectory(FTPRequest req) throws IOException JavaDoc
2581    {
2582
2583        // Check if the user is logged in
2584

2585        if (isLoggedOn() == false)
2586        {
2587            sendFTPResponse(500, "");
2588            return;
2589        }
2590
2591        // Check if an argument has been specified
2592

2593        if (req.hasArgument() == false)
2594        {
2595            sendFTPResponse(501, "Syntax error, parameter required");
2596            return;
2597        }
2598
2599        // Check if the new directory contains multiple directories
2600

2601        FTPPath ftpPath = generatePathForRequest(req, false, false);
2602        if (ftpPath == null)
2603        {
2604            sendFTPResponse(550, "Invalid path " + req.getArgument());
2605            return;
2606        }
2607
2608        // Create the new directory
2609

2610        DiskInterface disk = null;
2611        TreeConnection tree = null;
2612        NetworkFile netFile = null;
2613
2614        try
2615        {
2616
2617            // Create a temporary tree connection
2618

2619            tree = getTreeConnection(ftpPath.getSharedDevice());
2620
2621            // Check if the session has the required access to the filesystem
2622

2623            if (tree == null || tree.hasWriteAccess() == false)
2624            {
2625
2626                // Session does not have write access to the filesystem
2627

2628                sendFTPResponse(550, "Access denied");
2629                return;
2630            }
2631
2632            // Check if the directory exists
2633

2634            disk = (DiskInterface) ftpPath.getSharedDevice().getInterface();
2635            int sts = disk.fileExists(this, tree, ftpPath.getSharePath());
2636
2637            if (sts == FileStatus.NotExist)
2638            {
2639
2640                // Create the new directory
2641

2642                FileOpenParams params = new FileOpenParams(ftpPath.getSharePath(), FileAction.CreateNotExist,
2643                        AccessMode.ReadWrite, FileAttribute.NTDirectory);
2644
2645                disk.createDirectory(this, tree, params);
2646
2647                // Notify change listeners that a new directory has been created
2648

2649                DiskDeviceContext diskCtx = (DiskDeviceContext) tree.getContext();
2650
2651                if (diskCtx.hasChangeHandler())
2652                    diskCtx.getChangeHandler().notifyFileChanged(NotifyChange.ActionAdded, ftpPath.getSharePath());
2653
2654                // DEBUG
2655

2656                if (logger.isDebugEnabled() && hasDebug(DBG_DIRECTORY))
2657                    logger.debug("CreateDir ftp="
2658                            + ftpPath.getFTPPath() + ", share=" + ftpPath.getShareName() + ", path="
2659                            + ftpPath.getSharePath());
2660            }
2661            else
2662            {
2663
2664                // File/directory already exists with that name, return an error
2665

2666                sendFTPResponse(450, sts == FileStatus.FileExists ? "File exists with that name"
2667                        : "Directory already exists");
2668                return;
2669            }
2670        }
2671        catch (Exception JavaDoc ex)
2672        {
2673            sendFTPResponse(450, "Failed to create directory");
2674            return;
2675        }
2676
2677        // Return the FTP path to the client
2678

2679        sendFTPResponse(250, ftpPath.getFTPPath());
2680    }
2681
2682    /**
2683     * Process a delete directory command
2684     *
2685     * @param req
2686     * FTPRequest
2687     * @exception IOException
2688     */

2689    protected final void procRemoveDirectory(FTPRequest req) throws IOException JavaDoc
2690    {
2691
2692        // Check if the user is logged in
2693

2694        if (isLoggedOn() == false)
2695        {
2696            sendFTPResponse(500, "");
2697            return;
2698        }
2699
2700        // Check if an argument has been specified
2701

2702        if (req.hasArgument() == false)
2703        {
2704            sendFTPResponse(501, "Syntax error, parameter required");
2705            return;
2706        }
2707
2708        // Check if the directory path contains multiple directories
2709

2710        FTPPath ftpPath = generatePathForRequest(req, false);
2711        if (ftpPath == null)
2712        {
2713            sendFTPResponse(550, "Invalid path " + req.getArgument());
2714            return;
2715        }
2716
2717        // Check if the path is the root directory, cannot delete directories
2718
// from the root
2719
// directory
2720
// as it maps to the list of available disk shares.
2721

2722        if (ftpPath.isRootPath() || ftpPath.isRootSharePath())
2723        {
2724            sendFTPResponse(550, "Access denied, cannot delete directory in root");
2725            return;
2726        }
2727
2728        // Delete the directory
2729

2730        DiskInterface disk = null;
2731        TreeConnection tree = null;
2732        NetworkFile netFile = null;
2733
2734        try
2735        {
2736
2737            // Create a temporary tree connection
2738

2739            tree = getTreeConnection(ftpPath.getSharedDevice());
2740
2741            // Check if the session has the required access to the filesystem
2742

2743            if (tree == null || tree.hasWriteAccess() == false)
2744            {
2745
2746                // Session does not have write access to the filesystem
2747

2748                sendFTPResponse(550, "Access denied");
2749                return;
2750            }
2751
2752            // Check if the directory exists
2753

2754            disk = (DiskInterface) ftpPath.getSharedDevice().getInterface();
2755            int sts = disk.fileExists(this, tree, ftpPath.getSharePath());
2756
2757            if (sts == FileStatus.DirectoryExists)
2758            {
2759
2760                // Delete the new directory
2761

2762                disk.deleteDirectory(this, tree, ftpPath.getSharePath());
2763
2764                // Check if there are any file/directory change notify requests
2765
// active
2766

2767                DiskDeviceContext diskCtx = (DiskDeviceContext) tree.getContext();
2768                if (diskCtx.hasChangeHandler())
2769                    diskCtx.getChangeHandler().notifyFileChanged(NotifyChange.ActionRemoved, ftpPath.getSharePath());
2770
2771                // DEBUG
2772

2773                if (logger.isDebugEnabled() && hasDebug(DBG_DIRECTORY))
2774                    logger.debug("DeleteDir ftp="
2775                            + ftpPath.getFTPPath() + ", share=" + ftpPath.getShareName() + ", path="
2776                            + ftpPath.getSharePath());
2777            }
2778            else
2779            {
2780
2781                // File already exists with that name or directory does not
2782
// exist return an error
2783

2784                sendFTPResponse(550, sts == FileStatus.FileExists ? "File exists with that name"
2785                        : "Directory does not exist");
2786                return;
2787            }
2788        }
2789        catch (Exception JavaDoc ex)
2790        {
2791            sendFTPResponse(550, "Failed to delete directory");
2792            return;
2793        }
2794
2795        // Return a success status
2796

2797        sendFTPResponse(250, "Directory deleted OK");
2798    }
2799
2800    /**
2801     * Process a modify date/time command
2802     *
2803     * @param req
2804     * FTPRequest
2805     * @exception IOException
2806     */

2807    protected final void procModifyDateTime(FTPRequest req) throws IOException JavaDoc
2808    {
2809
2810        // Return a success response
2811

2812        sendFTPResponse(550, "Not implemented yet");
2813    }
2814
2815    /**
2816     * Process a file size command
2817     *
2818     * @param req
2819     * FTPRequest
2820     * @exception IOException
2821     */

2822    protected final void procFileSize(FTPRequest req) throws IOException JavaDoc
2823    {
2824
2825        // Check if the user is logged in
2826

2827        if (isLoggedOn() == false)
2828        {
2829            sendFTPResponse(500, "");
2830            return;
2831        }
2832
2833        // Check if an argument has been specified
2834

2835        if (req.hasArgument() == false)
2836        {
2837            sendFTPResponse(501, "Syntax error, parameter required");
2838            return;
2839        }
2840
2841        // Create the path for the file listing
2842

2843        FTPPath ftpPath = generatePathForRequest(req, true);
2844        if (ftpPath == null)
2845        {
2846            sendFTPResponse(500, "Invalid path");
2847            return;
2848        }
2849
2850        // Get the file information
2851

2852        DiskInterface disk = null;
2853        TreeConnection tree = null;
2854
2855        try
2856        {
2857
2858            // Create a temporary tree connection
2859

2860            tree = getTreeConnection(ftpPath.getSharedDevice());
2861
2862            // Access the virtual filesystem driver
2863

2864            disk = (DiskInterface) ftpPath.getSharedDevice().getInterface();
2865
2866            // Get the file information
2867

2868            FileInfo finfo = disk.getFileInformation(this, tree, ftpPath.getSharePath());
2869
2870            if (finfo == null)
2871            {
2872                sendFTPResponse(550, "File " + req.getArgument() + " not available");
2873                return;
2874            }
2875
2876            // Return the file size
2877

2878            sendFTPResponse(213, "" + finfo.getSize());
2879
2880            // DEBUG
2881

2882            if (logger.isDebugEnabled() && hasDebug(DBG_FILE))
2883                logger.debug("File size ftp="
2884                        + ftpPath.getFTPPath() + ", share=" + ftpPath.getShareName() + ", size=" + finfo.getSize());
2885        }
2886        catch (Exception JavaDoc ex)
2887        {
2888            sendFTPResponse(550, "Error retrieving file size");
2889        }
2890    }
2891
2892    /**
2893     * Process a structure command. This command is obsolete.
2894     *
2895     * @param req
2896     * FTPRequest
2897     * @exception IOException
2898     */

2899    protected final void procStructure(FTPRequest req) throws IOException JavaDoc
2900    {
2901
2902        // Check for the file structure argument
2903

2904        if (req.hasArgument() && req.getArgument().equalsIgnoreCase("F"))
2905            sendFTPResponse(200, "OK");
2906
2907        // Return an error response
2908

2909        sendFTPResponse(504, "Obsolete");
2910    }
2911
2912    /**
2913     * Process a mode command. This command is obsolete.
2914     *
2915     * @param req
2916     * FTPRequest
2917     * @exception IOException
2918     */

2919    protected final void procMode(FTPRequest req) throws IOException JavaDoc
2920    {
2921
2922        // Check for the stream transfer mode argument
2923

2924        if (req.hasArgument() && req.getArgument().equalsIgnoreCase("S"))
2925            sendFTPResponse(200, "OK");
2926
2927        // Return an error response
2928

2929        sendFTPResponse(504, "Obsolete");
2930    }
2931
2932    /**
2933     * Process an allocate command. This command is obsolete.
2934     *
2935     * @param req
2936     * FTPRequest
2937     * @exception IOException
2938     */

2939    protected final void procAllocate(FTPRequest req) throws IOException JavaDoc
2940    {
2941
2942        // Return a response
2943

2944        sendFTPResponse(202, "Obsolete");
2945    }
2946
2947    /**
2948     * Build a list of file name or file information objects for the specified
2949     * server path
2950     *
2951     * @param path
2952     * FTPPath
2953     * @param nameOnly
2954     * boolean
2955     * @param hidden
2956     * boolean
2957     * @return Vector<FileInfo>
2958     */

2959    protected final Vector JavaDoc<FileInfo> listFilesForPath(FTPPath path, boolean nameOnly, boolean hidden)
2960    {
2961
2962        // Check if the path is valid
2963

2964        if (path == null)
2965            return null;
2966
2967        // Check if the path is the root path
2968

2969        Vector JavaDoc<FileInfo> files = new Vector JavaDoc<FileInfo>();
2970
2971        if (path.hasSharedDevice() == false)
2972        {
2973
2974            // The first level of directories are mapped to the available shares
2975
// Guest users only see their own list of shares
2976

2977            SharedDeviceList shares = null;
2978            if ( getClientInformation().isGuest())
2979                shares = getDynamicShareList();
2980            else
2981                shares = getShareList();
2982            
2983            if (shares != null)
2984            {
2985
2986                // Search for disk shares
2987

2988                Enumeration JavaDoc<SharedDevice> enm = shares.enumerateShares();
2989
2990                while (enm.hasMoreElements())
2991                {
2992
2993                    // Get the current shared device
2994

2995                    SharedDevice shr = enm.nextElement();
2996
2997                    // Add the share name or full information to the list
2998

2999                    if (nameOnly == false)
3000                    {
3001
3002                        // Create a file information object for the top level
3003
// directory details
3004

3005                        FileInfo finfo = new FileInfo(shr.getName(), 0L, FileAttribute.Directory);
3006                        files.add(finfo);
3007                    }
3008                    else
3009                        files.add(new FileInfo(shr.getName(), 0L, FileAttribute.Directory));
3010                }
3011            }
3012        }
3013        else
3014        {
3015
3016            // Append a wildcard to the search path
3017

3018            String JavaDoc searchPath = path.getSharePath();
3019
3020            if (path.isDirectory())
3021                searchPath = path.makeSharePathToFile("*");
3022
3023            // Create a temporary tree connection
3024

3025            TreeConnection tree = new TreeConnection(path.getSharedDevice());
3026
3027            // Start a search on the specified disk share
3028

3029            DiskInterface disk = null;
3030            SearchContext ctx = null;
3031
3032            int searchAttr = FileAttribute.Directory + FileAttribute.Normal;
3033            if (hidden)
3034                searchAttr += FileAttribute.Hidden;
3035
3036            try
3037            {
3038                disk = (DiskInterface) path.getSharedDevice().getInterface();
3039                ctx = disk.startSearch(this, tree, searchPath, searchAttr);
3040            }
3041            catch (Exception JavaDoc ex)
3042            {
3043            }
3044
3045            // Add the files to the list
3046

3047            if (ctx != null)
3048            {
3049
3050                // Get the file names/information
3051

3052                while (ctx.hasMoreFiles())
3053                {
3054
3055                    // Check if a file name or file information is required
3056

3057                    if (nameOnly)
3058                    {
3059
3060                        // Add a file name to the list
3061

3062                        files.add(new FileInfo(ctx.nextFileName(), 0L, 0));
3063                    }
3064                    else
3065                    {
3066
3067                        // Create a file information object
3068

3069                        FileInfo finfo = new FileInfo();
3070
3071                        if (ctx.nextFileInfo(finfo) == false)
3072                            break;
3073                        if (finfo.getFileName() != null)
3074                            files.add(finfo);
3075                    }
3076                }
3077            }
3078        }
3079
3080        // Return the list of file names/information
3081

3082        return files;
3083    }
3084
3085    /**
3086     * Get the list of filtered shares that are available to this session
3087     *
3088     * @return SharedDeviceList
3089     */

3090    protected final SharedDeviceList getShareList()
3091    {
3092
3093        // Check if the filtered share list has been initialized
3094

3095        if (m_shares == null)
3096        {
3097
3098            // Get a list of shared filesystems
3099

3100            SharedDeviceList shares = getFTPServer().getShareMapper().getShareList(getFTPServer().getServerName(),
3101                    this, false);
3102
3103            // Search for disk shares
3104

3105            m_shares = new SharedDeviceList();
3106            Enumeration JavaDoc enm = shares.enumerateShares();
3107
3108            while (enm.hasMoreElements())
3109            {
3110
3111                // Get the current shared device
3112

3113                SharedDevice shr = (SharedDevice) enm.nextElement();
3114
3115                // Check if the share is a disk share
3116

3117                if (shr instanceof DiskSharedDevice)
3118                    m_shares.addShare(shr);
3119            }
3120
3121            // Check if there is an access control manager available, if so then
3122
// filter the list of
3123
// shared filesystems
3124

3125            if (getServer().hasAccessControlManager())
3126            {
3127
3128                // Get the access control manager
3129

3130                AccessControlManager aclMgr = getServer().getAccessControlManager();
3131
3132                // Filter the list of shared filesystems
3133

3134                m_shares = aclMgr.filterShareList(this, m_shares);
3135            }
3136        }
3137
3138        // Return the filtered shared filesystem list
3139

3140        return m_shares;
3141    }
3142
3143    /**
3144     * Get a tree connection for the specified shared device. Creates and caches
3145     * a new tree connection if required.
3146     *
3147     * @param share
3148     * SharedDevice
3149     * @return TreeConnection
3150     */

3151    protected final TreeConnection getTreeConnection(SharedDevice share)
3152    {
3153
3154        // Check if the share is valid
3155

3156        if (share == null)
3157            return null;
3158
3159        // Check if there is a tree connection in the cache
3160

3161        TreeConnection tree = m_connections.findConnection(share.getName());
3162        if (tree == null)
3163        {
3164
3165            // Create a new tree connection, do not add dynamic shares to the connection cache
3166

3167            tree = new TreeConnection(share);
3168            if ( share.isTemporary() == false)
3169                m_connections.addConnection(tree);
3170
3171            // Set the access permission for the shared filesystem
3172

3173            if (getServer().hasAccessControlManager())
3174            {
3175
3176                // Set the access permission to the shared filesystem
3177

3178                AccessControlManager aclMgr = getServer().getAccessControlManager();
3179
3180                int access = aclMgr.checkAccessControl(this, share);
3181                if (access != AccessControl.Default)
3182                    tree.setPermission(access);
3183            }
3184        }
3185
3186        // Return the connection
3187

3188        return tree;
3189    }
3190
3191    /**
3192     * Create a disk share for the home folder
3193     *
3194     * @param client ClientInfo
3195     * @return DiskSharedDevice
3196     */

3197    private final DiskSharedDevice createHomeDiskShare(ClientInfo client)
3198    {
3199        // Check if the home folder has been set for the user
3200

3201        if ( client.hasHomeFolder() == false)
3202        {
3203            // Get the required services
3204

3205            NodeService nodeService = getServer().getConfiguration().getNodeService();
3206            PersonService personService = getServer().getConfiguration().getPersonService();
3207            TransactionService transService = getServer().getConfiguration().getTransactionService();
3208            
3209            // Get the home folder for the user
3210

3211            UserTransaction JavaDoc tx = transService.getUserTransaction();
3212            NodeRef homeSpaceRef = null;
3213            
3214            try
3215            {
3216                tx.begin();
3217                homeSpaceRef = (NodeRef) nodeService.getProperty( personService.getPerson(client.getUserName()),
3218                        ContentModel.PROP_HOMEFOLDER);
3219                client.setHomeFolder( homeSpaceRef);
3220                tx.commit();
3221            }
3222            catch (Throwable JavaDoc ex)
3223            {
3224                try
3225                {
3226                    tx.rollback();
3227                }
3228                catch (Exception JavaDoc ex2)
3229                {
3230                    logger.error("Failed to rollback transaction", ex2);
3231                }
3232                
3233                if(ex instanceof RuntimeException JavaDoc)
3234                {
3235                    throw (RuntimeException JavaDoc)ex;
3236                }
3237                else
3238                {
3239                    throw new RuntimeException JavaDoc("Failed to get home folder", ex);
3240                }
3241            }
3242        }
3243        
3244        // Create the disk driver and context
3245

3246        DiskInterface diskDrv = getServer().getConfiguration().getDiskInterface();
3247        DiskDeviceContext diskCtx = new ContentContext("", "", client.getHomeFolder());
3248
3249        // Default the filesystem to look like an 80Gb sized disk with 90% free space
3250

3251        diskCtx.setDiskInformation(new SrvDiskInfo(2560, 64, 512, 2304));
3252        
3253        // Create a temporary shared device for the users home directory
3254

3255        return new DiskSharedDevice( client.getUserName(), diskDrv, diskCtx, SharedDevice.Temporary);
3256    }
3257    
3258    /**
3259     * Start the FTP session in a seperate thread
3260     */

3261    public void run()
3262    {
3263        
3264        try
3265        {
3266
3267            // Debug
3268

3269            if (logger.isDebugEnabled() && hasDebug(DBG_STATE))
3270                logger.debug("FTP session started");
3271
3272            // Create the input/output streams
3273

3274            m_in = new InputStreamReader JavaDoc(m_sock.getInputStream());
3275            m_out = new OutputStreamWriter JavaDoc(m_sock.getOutputStream());
3276
3277            m_inbuf = new char[512];
3278            m_outbuf = new StringBuffer JavaDoc(256);
3279
3280            // Return the initial response
3281

3282            sendFTPResponse(220, "FTP server ready");
3283
3284            // Start/end times if timing debug is enabled
3285

3286            long startTime = 0L;
3287            long endTime = 0L;
3288
3289            // Create an FTP request to hold command details
3290

3291            FTPRequest ftpReq = new FTPRequest();
3292
3293            // The server session loops until the NetBIOS hangup state is set.
3294

3295            int rdlen = -1;
3296            String JavaDoc cmd = null;
3297
3298            while (m_sock != null)
3299            {
3300
3301                // Wait for a data packet
3302

3303                rdlen = m_in.read(m_inbuf);
3304
3305                // Check if there is no more data, the other side has dropped
3306
// the connection
3307

3308                if (rdlen == -1)
3309                {
3310                    closeSession();
3311                    continue;
3312                }
3313
3314                // Trim the trailing <CR><LF>
3315

3316                if (rdlen > 0)
3317                {
3318                    while (rdlen > 0 && m_inbuf[rdlen - 1] == '\r' || m_inbuf[rdlen - 1] == '\n')
3319                        rdlen--;
3320                }
3321
3322                // Get the command string
3323

3324                cmd = new String JavaDoc(m_inbuf, 0, rdlen);
3325
3326                // Debug
3327

3328                if (logger.isDebugEnabled() && hasDebug(DBG_TIMING))
3329                    startTime = System.currentTimeMillis();
3330
3331                if (logger.isDebugEnabled() && hasDebug(DBG_PKTTYPE))
3332                    logger.debug("Cmd " + ftpReq);
3333
3334                // Parse the received command, and validate
3335

3336                ftpReq.setCommandLine(cmd);
3337                m_reqCount++;
3338
3339                switch (ftpReq.isCommand())
3340                {
3341
3342                // User command
3343

3344                case FTPCommand.User:
3345                    procUser(ftpReq);
3346                    break;
3347
3348                // Password command
3349

3350                case FTPCommand.Pass:
3351                    procPassword(ftpReq);
3352                    break;
3353
3354                // Quit command
3355

3356                case FTPCommand.Quit:
3357                    procQuit(ftpReq);
3358                    break;
3359
3360                // Type command
3361

3362                case FTPCommand.Type:
3363                    procType(ftpReq);
3364                    break;
3365
3366                // Port command
3367

3368                case FTPCommand.Port:
3369                    procPort(ftpReq);
3370                    break;
3371
3372                // Passive command
3373

3374                case FTPCommand.Pasv:
3375                    procPassive(ftpReq);
3376                    break;
3377
3378                // Restart position command
3379

3380                case FTPCommand.Rest:
3381                    procRestart(ftpReq);
3382                    break;
3383
3384                // Return file command
3385

3386                case FTPCommand.Retr:
3387                    procReturnFile(ftpReq);
3388
3389                    // Reset the restart position
3390

3391                    m_restartPos = 0;
3392                    break;
3393
3394                // Store file command
3395

3396                case FTPCommand.Stor:
3397                    procStoreFile(ftpReq);
3398                    break;
3399
3400                // Print working directory command
3401

3402                case FTPCommand.Pwd:
3403                case FTPCommand.XPwd:
3404                    procPrintWorkDir(ftpReq);
3405                    break;
3406
3407                // Change working directory command
3408

3409                case FTPCommand.Cwd:
3410                case FTPCommand.XCwd:
3411                    procChangeWorkDir(ftpReq);
3412                    break;
3413
3414                // Change to previous directory command
3415

3416                case FTPCommand.Cdup:
3417                case FTPCommand.XCup:
3418                    procCdup(ftpReq);
3419                    break;
3420
3421                // Full directory listing command
3422

3423                case FTPCommand.List:
3424                    procList(ftpReq);
3425                    break;
3426
3427                // Short directory listing command
3428

3429                case FTPCommand.Nlst:
3430                    procNList(ftpReq);
3431                    break;
3432
3433                // Delete file command
3434

3435                case FTPCommand.Dele:
3436                    procDeleteFile(ftpReq);
3437                    break;
3438
3439                // Rename file from command
3440

3441                case FTPCommand.Rnfr:
3442                    procRenameFrom(ftpReq);
3443                    break;
3444
3445                // Rename file to comand
3446

3447                case FTPCommand.Rnto:
3448                    procRenameTo(ftpReq);
3449                    break;
3450
3451                // Create new directory command
3452

3453                case FTPCommand.Mkd:
3454                case FTPCommand.XMkd:
3455                    procCreateDirectory(ftpReq);
3456                    break;
3457
3458                // Delete directory command
3459

3460                case FTPCommand.Rmd:
3461                case FTPCommand.XRmd:
3462                    procRemoveDirectory(ftpReq);
3463                    break;
3464
3465                // Return file size command
3466

3467                case FTPCommand.Size:
3468                    procFileSize(ftpReq);
3469                    break;
3470
3471                // Set modify date/time command
3472

3473                case FTPCommand.Mdtm:
3474                    procModifyDateTime(ftpReq);
3475                    break;
3476
3477                // System status command
3478

3479                case FTPCommand.Syst:
3480                    procSystemStatus(ftpReq);
3481                    break;
3482
3483                // Server status command
3484

3485                case FTPCommand.Stat:
3486                    procServerStatus(ftpReq);
3487                    break;
3488
3489                // Help command
3490

3491                case FTPCommand.Help:
3492                    procHelp(ftpReq);
3493                    break;
3494
3495                // No-op command
3496

3497                case FTPCommand.Noop:
3498                    procNoop(ftpReq);
3499                    break;
3500
3501                // Structure command (obsolete)
3502

3503                case FTPCommand.Stru:
3504                    procStructure(ftpReq);
3505                    break;
3506
3507                // Mode command (obsolete)
3508

3509                case FTPCommand.Mode:
3510                    procMode(ftpReq);
3511                    break;
3512
3513                // Allocate command (obsolete)
3514

3515                case FTPCommand.Allo:
3516                    procAllocate(ftpReq);
3517                    break;
3518
3519                // Unknown/unimplemented command
3520

3521                default:
3522                    if (ftpReq.isCommand() != FTPCommand.InvalidCmd)
3523                        sendFTPResponse(502, "Command "
3524                                + FTPCommand.getCommandName(ftpReq.isCommand()) + " not implemented");
3525                    else
3526                        sendFTPResponse(502, "Command not implemented");
3527                    break;
3528                }
3529
3530                // Debug
3531

3532                if (logger.isDebugEnabled() && hasDebug(DBG_TIMING))
3533                {
3534                    endTime = System.currentTimeMillis();
3535                    long duration = endTime - startTime;
3536                    if (duration > 20)
3537                        logger.debug("Processed cmd "
3538                                + FTPCommand.getCommandName(ftpReq.isCommand()) + " in " + duration + "ms");
3539                }
3540
3541                // Commit, or rollback, any active user transaction
3542

3543                try
3544                {
3545                    // Commit or rollback the transaction
3546

3547                    endTransaction();
3548                }
3549                catch ( Exception JavaDoc ex)
3550                {
3551                    // Debug
3552

3553                    if ( logger.isDebugEnabled())
3554                        logger.debug("Error committing transaction", ex);
3555                }
3556                
3557            } // end while state
3558
}
3559        catch (SocketException JavaDoc ex)
3560        {
3561
3562            // DEBUG
3563

3564            if (logger.isErrorEnabled() && hasDebug(DBG_STATE))
3565                logger.error("Socket closed by remote client");
3566        }
3567        catch (Exception JavaDoc ex)
3568        {
3569
3570            // Output the exception details
3571

3572            if (isShutdown() == false)
3573            {
3574                logger.debug(ex);
3575            }
3576        }
3577        finally
3578        {
3579            // If there is an active transaction then roll it back
3580

3581            if ( hasUserTransaction())
3582            {
3583                try
3584                {
3585                    getUserTransaction().rollback();
3586                }
3587                catch (Exception JavaDoc ex)
3588                {
3589                    logger.warn("Failed to rollback transaction", ex);
3590                }
3591            }
3592        }
3593
3594        // Cleanup the session, make sure all resources are released
3595

3596        closeSession();
3597
3598        // Debug
3599

3600        if (hasDebug(DBG_STATE))
3601            logger.debug("Server session closed");
3602    }
3603}
Popular Tags