0001: /*
0002: * This file is part of DrFTPD, Distributed FTP Daemon.
0003: *
0004: * DrFTPD is free software; you can redistribute it and/or modify
0005: * it under the terms of the GNU General Public License as published by
0006: * the Free Software Foundation; either version 2 of the License, or
0007: * (at your option) any later version.
0008: *
0009: * DrFTPD is distributed in the hope that it will be useful,
0010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0012: * GNU General Public License for more details.
0013: *
0014: * You should have received a copy of the GNU General Public License
0015: * along with DrFTPD; if not, write to the Free Software
0016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0017: */
0018: package net.sf.drftpd.master.command.plugins;
0019:
0020: import java.io.FileNotFoundException;
0021: import java.io.IOException;
0022: import java.text.SimpleDateFormat;
0023: import java.util.Collection;
0024: import java.util.Date;
0025: import java.util.Iterator;
0026: import java.util.ResourceBundle;
0027: import java.util.StringTokenizer;
0028:
0029: import net.sf.drftpd.FileExistsException;
0030: import net.sf.drftpd.NoAvailableSlaveException;
0031: import net.sf.drftpd.ObjectNotFoundException;
0032: import net.sf.drftpd.event.DirectoryFtpEvent;
0033: import net.sf.drftpd.master.BaseFtpConnection;
0034: import net.sf.drftpd.master.FtpRequest;
0035: import net.sf.drftpd.master.GroupPosition;
0036: import net.sf.drftpd.master.UploaderPosition;
0037: import net.sf.drftpd.master.command.CommandManager;
0038: import net.sf.drftpd.master.command.CommandManagerFactory;
0039: import net.sf.drftpd.master.queues.NukeLog;
0040:
0041: import org.apache.log4j.Level;
0042: import org.apache.log4j.Logger;
0043: import org.drftpd.Bytes;
0044: import org.drftpd.Checksum;
0045: import org.drftpd.SFVFile;
0046: import org.drftpd.commands.CommandHandler;
0047: import org.drftpd.commands.CommandHandlerFactory;
0048: import org.drftpd.commands.Nuke;
0049: import org.drftpd.commands.Reply;
0050: import org.drftpd.commands.UnhandledCommandException;
0051: import org.drftpd.id3.ID3Tag;
0052: import org.drftpd.plugins.DIZFile;
0053: import org.drftpd.plugins.DIZPlugin;
0054: import org.drftpd.plugins.SiteBot;
0055: import org.drftpd.remotefile.FileStillTransferringException;
0056: import org.drftpd.remotefile.LinkedRemoteFile;
0057: import org.drftpd.remotefile.LinkedRemoteFileInterface;
0058: import org.drftpd.remotefile.ListUtils;
0059: import org.drftpd.remotefile.StaticRemoteFile;
0060: import org.drftpd.remotefile.LinkedRemoteFile.NonExistingFile;
0061: import org.drftpd.usermanager.NoSuchUserException;
0062: import org.drftpd.usermanager.User;
0063: import org.drftpd.usermanager.UserFileException;
0064: import org.tanesha.replacer.FormatterException;
0065: import org.tanesha.replacer.ReplacerEnvironment;
0066: import org.tanesha.replacer.ReplacerFormat;
0067: import org.tanesha.replacer.SimplePrintf;
0068:
0069: /**
0070: * @author mog
0071: * @version $Id: Dir.java 1512 2006-10-06 21:34:50Z tdsoul $
0072: */
0073: public class Dir implements CommandHandler, CommandHandlerFactory,
0074: Cloneable {
0075: private final static SimpleDateFormat DATE_FMT = new SimpleDateFormat(
0076: "yyyyMMddHHmmss.SSS");
0077: private static final Logger logger = Logger.getLogger(Dir.class);
0078: protected LinkedRemoteFileInterface _renameFrom = null;
0079:
0080: public Dir() {
0081: super ();
0082: }
0083:
0084: /**
0085: * <code>CDUP <CRLF></code><br>
0086: *
0087: * This command is a special case of CWD, and is included to
0088: * simplify the implementation of programs for transferring
0089: * directory trees between operating systems having different
0090: * syntaxes for naming the parent directory. The reply codes
0091: * shall be identical to the reply codes of CWD.
0092: */
0093: private Reply doCDUP(BaseFtpConnection conn) {
0094: // change directory
0095: try {
0096: conn.setCurrentDirectory(conn.getCurrentDirectory()
0097: .getParentFile());
0098: } catch (FileNotFoundException ex) {
0099: }
0100:
0101: return new Reply(200, "Directory changed to "
0102: + conn.getCurrentDirectory().getPath());
0103: }
0104:
0105: /**
0106: * <code>CWD <SP> <pathname> <CRLF></code><br>
0107: *
0108: * This command allows the user to work with a different
0109: * directory for file storage or retrieval without
0110: * altering his login or accounting information. Transfer
0111: * parameters are similarly unchanged. The argument is a
0112: * pathname specifying a directory.
0113: */
0114: private Reply doCWD(BaseFtpConnection conn) {
0115: FtpRequest request = conn.getRequest();
0116:
0117: if (!request.hasArgument()) {
0118: return Reply.RESPONSE_501_SYNTAX_ERROR;
0119: }
0120:
0121: LinkedRemoteFile newCurrentDirectory;
0122:
0123: try {
0124: newCurrentDirectory = conn.getCurrentDirectory()
0125: .lookupFile(request.getArgument());
0126: } catch (FileNotFoundException ex) {
0127: return new Reply(550, ex.getMessage());
0128: }
0129:
0130: if (!conn.getGlobalContext().getConfig().checkPathPermission(
0131: "privpath", conn.getUserNull(), newCurrentDirectory,
0132: true)) {
0133: return new Reply(550, request.getArgument() + ": Not found");
0134:
0135: // reply identical to FileNotFoundException.getMessage() above
0136: }
0137:
0138: if (!newCurrentDirectory.isDirectory()) {
0139: return new Reply(550, request.getArgument()
0140: + ": Not a directory");
0141: }
0142:
0143: conn.setCurrentDirectory(newCurrentDirectory);
0144:
0145: Reply response = new Reply(250, "Directory changed to "
0146: + newCurrentDirectory.getPath());
0147: conn.getGlobalContext().getConfig().directoryMessage(response,
0148: conn.getUserNull(), newCurrentDirectory);
0149:
0150: // show cwd_mp3.txt if this is an mp3 release
0151: ResourceBundle bundle = ResourceBundle.getBundle(Dir.class
0152: .getName());
0153: if (conn.getGlobalContext().getZsConfig().id3Enabled()) {
0154: try {
0155: ID3Tag id3tag = newCurrentDirectory.lookupFile(
0156: newCurrentDirectory.lookupMP3File())
0157: .getID3v1Tag();
0158: String mp3text = bundle.getString("cwd.id3info.text");
0159: ReplacerEnvironment env = BaseFtpConnection
0160: .getReplacerEnvironment(null, conn
0161: .getUserNull());
0162: ReplacerFormat id3format = null;
0163:
0164: try {
0165: id3format = ReplacerFormat.createFormat(mp3text);
0166: } catch (FormatterException e1) {
0167: logger.warn(e1);
0168: }
0169:
0170: env.add("artist", id3tag.getArtist().trim());
0171: env.add("album", id3tag.getAlbum().trim());
0172: env.add("genre", id3tag.getGenre());
0173: env.add("year", id3tag.getYear());
0174:
0175: try {
0176: if (id3format == null) {
0177: response.addComment("broken 1");
0178: } else {
0179: response.addComment(SimplePrintf.jprintf(
0180: id3format, env));
0181: }
0182: } catch (FormatterException e) {
0183: response.addComment("broken 2");
0184: logger.warn("", e);
0185: }
0186: } catch (FileNotFoundException e) {
0187: // no mp3 found
0188: //logger.warn("",e);
0189: } catch (IOException e) {
0190: logger.warn("", e);
0191: } catch (NoAvailableSlaveException e) {
0192: logger.warn("", e);
0193: }
0194: }
0195: // diz files
0196: if (conn.getGlobalContext().getZsConfig().dizEnabled()) {
0197: if (DIZPlugin.zipFilesOnline(newCurrentDirectory) > 0) {
0198: try {
0199: DIZFile diz = new DIZFile(DIZPlugin
0200: .getZipFile(newCurrentDirectory));
0201:
0202: ReplacerFormat format = null;
0203: ReplacerEnvironment env = BaseFtpConnection
0204: .getReplacerEnvironment(null, conn
0205: .getUserNull());
0206:
0207: if (diz.getDiz() != null) {
0208: try {
0209: format = ReplacerFormat.createFormat(diz
0210: .getDiz());
0211: response.addComment(SimplePrintf.jprintf(
0212: format, env));
0213: } catch (FormatterException e) {
0214: logger.warn(e);
0215: }
0216: }
0217: } catch (FileNotFoundException e) {
0218: // do nothing, continue on
0219: } catch (NoAvailableSlaveException e) {
0220: // do nothing, continue on
0221: }
0222: }
0223: }
0224:
0225: // show race stats
0226: if (conn.getGlobalContext().getZsConfig().raceStatsEnabled()) {
0227: try {
0228: SFVFile sfvfile = newCurrentDirectory.lookupSFVFile();
0229: Collection racers = SiteBot.userSort(
0230: sfvfile.getFiles(), "bytes", "high");
0231: Collection groups = SiteBot.topFileGroup(sfvfile
0232: .getFiles());
0233:
0234: String racerline = bundle.getString("cwd.racers.body");
0235: //logger.debug("racerline = " + racerline);
0236: String groupline = bundle.getString("cwd.groups.body");
0237:
0238: ReplacerEnvironment env = BaseFtpConnection
0239: .getReplacerEnvironment(null, conn
0240: .getUserNull());
0241:
0242: //Start building race message
0243: String racetext = bundle
0244: .getString("cwd.racestats.header")
0245: + "\n";
0246: racetext += bundle.getString("cwd.racers.header")
0247: + "\n";
0248:
0249: ReplacerFormat raceformat = null;
0250:
0251: //Add racer stats
0252: int position = 1;
0253:
0254: for (Iterator iter = racers.iterator(); iter.hasNext();) {
0255: UploaderPosition stat = (UploaderPosition) iter
0256: .next();
0257: User raceuser;
0258:
0259: try {
0260: raceuser = conn.getGlobalContext()
0261: .getUserManager().getUserByName(
0262: stat.getUsername());
0263: } catch (NoSuchUserException e2) {
0264: continue;
0265: } catch (UserFileException e2) {
0266: logger.log(Level.FATAL,
0267: "Error reading userfile", e2);
0268:
0269: continue;
0270: }
0271:
0272: ReplacerEnvironment raceenv = new ReplacerEnvironment();
0273:
0274: raceenv.add("speed", Bytes.formatBytes(stat
0275: .getXferspeed())
0276: + "/s");
0277: raceenv.add("user", stat.getUsername());
0278: raceenv.add("group", raceuser.getGroup());
0279: raceenv.add("files", "" + stat.getFiles());
0280: raceenv.add("bytes", Bytes.formatBytes(stat
0281: .getBytes()));
0282: raceenv.add("position", String.valueOf(position));
0283: raceenv.add("percent", Integer.toString((stat
0284: .getFiles() * 100)
0285: / sfvfile.size())
0286: + "%");
0287:
0288: try {
0289: racetext += (SimplePrintf.jprintf(racerline,
0290: raceenv) + "\n");
0291: position++;
0292: } catch (FormatterException e) {
0293: logger.warn(e);
0294: }
0295: }
0296:
0297: racetext += bundle.getString("cwd.racers.footer")
0298: + "\n";
0299: racetext += bundle.getString("cwd.groups.header")
0300: + "\n";
0301:
0302: //add groups stats
0303: position = 1;
0304:
0305: for (Iterator iter = groups.iterator(); iter.hasNext();) {
0306: GroupPosition stat = (GroupPosition) iter.next();
0307:
0308: ReplacerEnvironment raceenv = new ReplacerEnvironment();
0309:
0310: raceenv.add("group", stat.getGroupname());
0311: raceenv.add("position", String.valueOf(position));
0312: raceenv.add("bytes", Bytes.formatBytes(stat
0313: .getBytes()));
0314: raceenv.add("files", Integer.toString(stat
0315: .getFiles()));
0316: raceenv.add("percent", Integer.toString((stat
0317: .getFiles() * 100)
0318: / sfvfile.size())
0319: + "%");
0320: raceenv.add("speed", Bytes.formatBytes(stat
0321: .getXferspeed())
0322: + "/s");
0323:
0324: try {
0325: racetext += (SimplePrintf.jprintf(groupline,
0326: raceenv) + "\n");
0327: position++;
0328: } catch (FormatterException e) {
0329: logger.warn(e);
0330: }
0331: }
0332:
0333: racetext += bundle.getString("cwd.groups.footer")
0334: + "\n";
0335:
0336: env.add("totalfiles", Integer.toString(sfvfile.size()));
0337: env.add("totalbytes", Bytes.formatBytes(sfvfile
0338: .getTotalBytes()));
0339: env.add("totalspeed", Bytes.formatBytes(sfvfile
0340: .getXferspeed())
0341: + "/s");
0342: env.add("totalpercent", Integer.toString((sfvfile
0343: .getStatus().getPresent() * 100)
0344: / sfvfile.size())
0345: + "%");
0346:
0347: racetext += bundle.getString("cwd.totals.body") + "\n";
0348: racetext += bundle.getString("cwd.racestats.footer")
0349: + "\n";
0350:
0351: try {
0352: raceformat = ReplacerFormat.createFormat(racetext);
0353: } catch (FormatterException e1) {
0354: logger.warn(e1);
0355: }
0356:
0357: try {
0358: if (raceformat == null) {
0359: response.addComment("cwd.uploaders");
0360: } else {
0361: response.addComment(SimplePrintf.jprintf(
0362: raceformat, env));
0363: }
0364: } catch (FormatterException e) {
0365: response.addComment("cwd.uploaders");
0366: logger.warn("", e);
0367: }
0368: } catch (RuntimeException ex) {
0369: logger.error("", ex);
0370: } catch (IOException e) {
0371: //Error fetching SFV, ignore
0372: } catch (NoAvailableSlaveException e) {
0373: //Error fetching SFV, ignore
0374: } catch (FileStillTransferringException e) {
0375: response
0376: .addComment("SFVFile still being transferred, no info available");
0377: }
0378: }
0379:
0380: return response;
0381: }
0382:
0383: /**
0384: * <code>DELE <SP> <pathname> <CRLF></code><br>
0385: *
0386: * This command causes the file specified in the pathname to be
0387: * deleted at the server site.
0388: */
0389: private Reply doDELE(BaseFtpConnection conn) {
0390: FtpRequest request = conn.getRequest();
0391:
0392: // argument check
0393: if (!request.hasArgument()) {
0394: //out.print(FtpResponse.RESPONSE_501_SYNTAX_ERROR);
0395: return Reply.RESPONSE_501_SYNTAX_ERROR;
0396: }
0397:
0398: // get filenames
0399: String fileName = request.getArgument();
0400: LinkedRemoteFile requestedFile;
0401:
0402: try {
0403: //requestedFile = getVirtualDirectory().lookupFile(fileName);
0404: requestedFile = conn.getCurrentDirectory().lookupFile(
0405: fileName, false);
0406: } catch (FileNotFoundException ex) {
0407: return new Reply(550, "File not found: " + ex.getMessage());
0408: }
0409:
0410: // check permission
0411: if (requestedFile.getUsername().equals(
0412: conn.getUserNull().getName())) {
0413: if (!conn.getGlobalContext().getConfig()
0414: .checkPathPermission("deleteown",
0415: conn.getUserNull(), requestedFile)) {
0416: return Reply.RESPONSE_530_ACCESS_DENIED;
0417: }
0418: } else if (!conn.getGlobalContext().getConfig()
0419: .checkPathPermission("delete", conn.getUserNull(),
0420: requestedFile)) {
0421: return Reply.RESPONSE_530_ACCESS_DENIED;
0422: }
0423:
0424: if (requestedFile.isDirectory()
0425: && requestedFile.getMap().size() != 0) {
0426: return new Reply(550, requestedFile.getPath()
0427: + ": Directory not empty");
0428: }
0429:
0430: Reply reply = (Reply) Reply.RESPONSE_250_ACTION_OKAY.clone();
0431:
0432: User uploader;
0433:
0434: try {
0435: uploader = conn.getGlobalContext().getUserManager()
0436: .getUserByName(requestedFile.getUsername());
0437: uploader
0438: .updateCredits((long) -(requestedFile.length() * conn
0439: .getGlobalContext().getConfig()
0440: .getCreditCheckRatio(requestedFile,
0441: uploader)));
0442: if (!conn.getGlobalContext().getConfig()
0443: .checkPathPermission("nostatsup", uploader,
0444: conn.getCurrentDirectory())) {
0445: uploader.updateUploadedBytes(-requestedFile.length());
0446: }
0447: } catch (UserFileException e) {
0448: reply.addComment("Error removing credits & stats: "
0449: + e.getMessage());
0450: } catch (NoSuchUserException e) {
0451: reply
0452: .addComment("User "
0453: + requestedFile.getUsername()
0454: + " does not exist, cannot remove credits on deletion");
0455: }
0456:
0457: conn.getGlobalContext().dispatchFtpEvent(
0458: new DirectoryFtpEvent(conn.getUserNull(), "DELE",
0459: requestedFile));
0460: requestedFile.delete();
0461:
0462: return reply;
0463: }
0464:
0465: /**
0466: * <code>MDTM <SP> <pathname> <CRLF></code><br>
0467: *
0468: * Returns the date and time of when a file was modified.
0469: */
0470: private Reply doMDTM(BaseFtpConnection conn) {
0471: FtpRequest request = conn.getRequest();
0472:
0473: // argument check
0474: if (!request.hasArgument()) {
0475: return Reply.RESPONSE_501_SYNTAX_ERROR;
0476: }
0477:
0478: // get filenames
0479: String fileName = request.getArgument();
0480: LinkedRemoteFile reqFile;
0481:
0482: try {
0483: reqFile = conn.getCurrentDirectory().lookupFile(fileName);
0484: } catch (FileNotFoundException ex) {
0485: return Reply.RESPONSE_550_REQUESTED_ACTION_NOT_TAKEN;
0486: }
0487:
0488: //fileName = user.getVirtualDirectory().getAbsoluteName(fileName);
0489: //String physicalName =
0490: // user.getVirtualDirectory().getPhysicalName(fileName);
0491: //File reqFile = new File(physicalName);
0492: // now print date
0493: //if (reqFile.exists()) {
0494: return new Reply(213, DATE_FMT.format(new Date(reqFile
0495: .lastModified())));
0496:
0497: //out.print(ftpStatus.getResponse(213, request, user, args));
0498: //} else {
0499: // out.write(ftpStatus.getResponse(550, request, user, null));
0500: //}
0501: }
0502:
0503: /**
0504: * <code>MKD <SP> <pathname> <CRLF></code><br>
0505: *
0506: * This command causes the directory specified in the pathname
0507: * to be created as a directory (if the pathname is absolute)
0508: * or as a subdirectory of the current working directory (if
0509: * the pathname is relative).
0510: *
0511: *
0512: * MKD
0513: * 257
0514: * 500, 501, 502, 421, 530, 550
0515: */
0516: private Reply doMKD(BaseFtpConnection conn) {
0517: FtpRequest request = conn.getRequest();
0518:
0519: // argument check
0520: if (!request.hasArgument()) {
0521: return Reply.RESPONSE_501_SYNTAX_ERROR;
0522: }
0523:
0524: if (!conn.getGlobalContext().getSlaveManager()
0525: .hasAvailableSlaves()) {
0526: return Reply.RESPONSE_450_SLAVE_UNAVAILABLE;
0527: }
0528:
0529: // arg cleanup extra /'s, take any / off the end
0530: String arg = request.getArgument();
0531: arg = arg.replaceAll("/{2,}", "/");
0532: if (arg.endsWith("/")) {
0533: arg = arg.substring(0, arg.length() - 1);
0534: }
0535:
0536: // are we making one for this directory?
0537: String currentPath = conn.getCurrentDirectory().getPath();
0538: String toPath = null;
0539: if (!arg.startsWith("/")) {
0540: if (currentPath.length() == 1) /* // isn't / */
0541: toPath = currentPath + arg;
0542: else
0543: toPath = currentPath + "/" + arg;
0544: } else {
0545: toPath = arg;
0546: }
0547:
0548: // get absolute path
0549: toPath = conn.getGlobalContext().getRoot().lookupPath(toPath);
0550:
0551: // lookup
0552: LinkedRemoteFile.NonExistingFile ret = conn
0553: .getCurrentDirectory().lookupNonExistingFile(arg);
0554: LinkedRemoteFile dir = ret.getFile();
0555:
0556: // does it already exist?
0557: if (ret.exists()) {
0558: return new Reply(550, "Requested action not taken. " + arg
0559: + " already exists");
0560: }
0561:
0562: // is this a legal name?
0563: String createdDirName = conn.getGlobalContext().getConfig()
0564: .getDirName(ret.getPath());
0565: if (!ListUtils.isLegalFileName(createdDirName)) {
0566: return Reply.RESPONSE_553_REQUESTED_ACTION_NOT_TAKEN;
0567: }
0568:
0569: if (!conn.getGlobalContext().getConfig().checkPathPermission(
0570: "makedir", conn.getUserNull(), dir)) {
0571: return Reply.RESPONSE_530_ACCESS_DENIED;
0572: }
0573:
0574: // check nukelog
0575: NukeLog _nukelog = Nuke.getNukeLog();
0576: if (_nukelog != null && _nukelog.find_fullpath(toPath)) {
0577: try {
0578: String reason = _nukelog.get(toPath).getReason();
0579: return new Reply(530,
0580: "Access denied - Directory already nuked for '"
0581: + reason + "'");
0582: } catch (ObjectNotFoundException e) {
0583: return new Reply(530,
0584: "Access denied - Directory already nuked, reason unavailable - "
0585: + e.getMessage());
0586: }
0587: }
0588:
0589: // ok, create it
0590: try {
0591: LinkedRemoteFile createdDir = dir.createDirectory(conn
0592: .getUserNull().getName(), conn.getUserNull()
0593: .getGroup(), createdDirName);
0594:
0595: conn.getGlobalContext().dispatchFtpEvent(
0596: new DirectoryFtpEvent(conn.getUserNull(), "MKD",
0597: createdDir));
0598:
0599: return new Reply(257, "\"" + createdDir.getPath()
0600: + "\" created.");
0601: } catch (FileExistsException ex) {
0602: return new Reply(550, "directory " + createdDirName
0603: + " already exists");
0604: }
0605: }
0606:
0607: /**
0608: * <code>PWD <CRLF></code><br>
0609: *
0610: * This command causes the name of the current working
0611: * directory to be returned in the reply.
0612: */
0613: private Reply doPWD(BaseFtpConnection conn) {
0614: return new Reply(257, "\""
0615: + conn.getCurrentDirectory().getPath()
0616: + "\" is current directory");
0617: }
0618:
0619: /**
0620: * <code>RMD <SP> <pathname> <CRLF></code><br>
0621: *
0622: * This command causes the directory specified in the pathname
0623: * to be removed as a directory (if the pathname is absolute)
0624: * or as a subdirectory of the current working directory (if
0625: * the pathname is relative).
0626: */
0627: private Reply doRMD(BaseFtpConnection conn) {
0628: FtpRequest request = conn.getRequest();
0629:
0630: // argument check
0631: if (!request.hasArgument()) {
0632: return Reply.RESPONSE_501_SYNTAX_ERROR;
0633: }
0634:
0635: // get file names
0636: String fileName = request.getArgument();
0637: LinkedRemoteFile requestedFile;
0638:
0639: try {
0640: requestedFile = conn.getCurrentDirectory().lookupFile(
0641: fileName);
0642: } catch (FileNotFoundException e) {
0643: return new Reply(550, fileName + ": " + e.getMessage());
0644: }
0645:
0646: if (requestedFile.getUsername().equals(
0647: conn.getUserNull().getName())) {
0648: if (!conn.getGlobalContext().getConfig()
0649: .checkPathPermission("deleteown",
0650: conn.getUserNull(), requestedFile)) {
0651: return Reply.RESPONSE_530_ACCESS_DENIED;
0652: }
0653: } else if (!conn.getGlobalContext().getConfig()
0654: .checkPathPermission("delete", conn.getUserNull(),
0655: requestedFile)) {
0656: return Reply.RESPONSE_530_ACCESS_DENIED;
0657: }
0658:
0659: if (!requestedFile.isDirectory()) {
0660: return new Reply(550, fileName + ": Not a directory");
0661: }
0662:
0663: if (requestedFile.dirSize() != 0) {
0664: return new Reply(550, fileName + ": Directory not empty");
0665: }
0666:
0667: // now delete
0668: //if (conn.getConfig().checkDirLog(conn.getUserNull(), requestedFile)) {
0669: conn.getGlobalContext().dispatchFtpEvent(
0670: new DirectoryFtpEvent(conn.getUserNull(), "RMD",
0671: requestedFile));
0672:
0673: //}
0674: requestedFile.delete();
0675:
0676: return Reply.RESPONSE_250_ACTION_OKAY;
0677: }
0678:
0679: /**
0680: * <code>RNFR <SP> <pathname> <CRLF></code><br>
0681: *
0682: * This command specifies the old pathname of the file which is
0683: * to be renamed. This command must be immediately followed by
0684: * a "rename to" command specifying the new file pathname.
0685: *
0686: * RNFR
0687: 450, 550
0688: 500, 501, 502, 421, 530
0689: 350
0690:
0691: */
0692: private Reply doRNFR(BaseFtpConnection conn) {
0693: FtpRequest request = conn.getRequest();
0694:
0695: // argument check
0696: if (!request.hasArgument()) {
0697: return Reply.RESPONSE_501_SYNTAX_ERROR;
0698: }
0699:
0700: // set state variable
0701: // get filenames
0702: //String fileName = request.getArgument();
0703: //fileName = user.getVirtualDirectory().getAbsoluteName(fileName);
0704: //mstRenFr = user.getVirtualDirectory().getPhysicalName(fileName);
0705: try {
0706: _renameFrom = conn.getCurrentDirectory().lookupFile(
0707: request.getArgument());
0708: } catch (FileNotFoundException e) {
0709: return Reply.RESPONSE_550_REQUESTED_ACTION_NOT_TAKEN;
0710: }
0711:
0712: //check permission
0713: if (_renameFrom.getUsername().equals(
0714: conn.getUserNull().getName())) {
0715: if (!conn.getGlobalContext().getConfig()
0716: .checkPathPermission("renameown",
0717: conn.getUserNull(), _renameFrom)) {
0718: return Reply.RESPONSE_530_ACCESS_DENIED;
0719: }
0720: } else if (!conn.getGlobalContext().getConfig()
0721: .checkPathPermission("rename", conn.getUserNull(),
0722: _renameFrom)) {
0723: return Reply.RESPONSE_530_ACCESS_DENIED;
0724: }
0725:
0726: return new Reply(350, "File exists, ready for destination name");
0727: }
0728:
0729: /**
0730: * <code>RNTO <SP> <pathname> <CRLF></code><br>
0731: *
0732: * This command specifies the new pathname of the file
0733: * specified in the immediately preceding "rename from"
0734: * command. Together the two commands cause a file to be
0735: * renamed.
0736: */
0737: private Reply doRNTO(BaseFtpConnection conn) {
0738: FtpRequest request = conn.getRequest();
0739:
0740: // argument check
0741: if (!request.hasArgument()) {
0742: return Reply.RESPONSE_501_SYNTAX_ERROR;
0743: }
0744:
0745: // set state variables
0746: if (_renameFrom == null) {
0747: return Reply.RESPONSE_503_BAD_SEQUENCE_OF_COMMANDS;
0748: }
0749:
0750: NonExistingFile ret = conn.getCurrentDirectory()
0751: .lookupNonExistingFile(request.getArgument());
0752: LinkedRemoteFileInterface toDir = ret.getFile();
0753: String name = ret.getPath();
0754: LinkedRemoteFileInterface fromFile = _renameFrom;
0755:
0756: if (name == null) {
0757: name = fromFile.getName();
0758: }
0759:
0760: // check permission
0761: if (_renameFrom.getUsername().equals(
0762: conn.getUserNull().getName())) {
0763: if (!conn.getGlobalContext().getConfig()
0764: .checkPathPermission("renameown",
0765: conn.getUserNull(), toDir)) {
0766: return Reply.RESPONSE_530_ACCESS_DENIED;
0767: }
0768: } else if (!conn.getGlobalContext().getConfig()
0769: .checkPathPermission("rename", conn.getUserNull(),
0770: toDir)) {
0771: return Reply.RESPONSE_530_ACCESS_DENIED;
0772: }
0773:
0774: try {
0775: fromFile.renameTo(toDir.getPath(), name);
0776: } catch (FileNotFoundException e) {
0777: logger.info("FileNotFoundException on renameTo()", e);
0778:
0779: return new Reply(500, "FileNotFound - " + e.getMessage());
0780: } catch (IOException e) {
0781: logger.info("IOException on renameTo()", e);
0782:
0783: return new Reply(500, "IOException - " + e.getMessage());
0784: }
0785:
0786: //out.write(FtpResponse.RESPONSE_250_ACTION_OKAY.toString());
0787: return new Reply(250, request.getCommand()
0788: + " command successful.");
0789: }
0790:
0791: private Reply doSITE_CHOWN(BaseFtpConnection conn)
0792: throws UnhandledCommandException {
0793: FtpRequest req = conn.getRequest();
0794: StringTokenizer st = new StringTokenizer(conn.getRequest()
0795: .getArgument());
0796: String owner = st.nextToken();
0797: String group = null;
0798: int pos = owner.indexOf('.');
0799:
0800: if (pos != -1) {
0801: group = owner.substring(pos + 1);
0802: owner = owner.substring(0, pos);
0803: } else if ("SITE CHGRP".equals(req.getCommand())) {
0804: group = owner;
0805: owner = null;
0806: } else if (!"SITE CHOWN".equals(req.getCommand())) {
0807: throw UnhandledCommandException.create(Dir.class, req);
0808: }
0809:
0810: Reply reply = new Reply(200);
0811:
0812: while (st.hasMoreTokens()) {
0813: try {
0814: LinkedRemoteFileInterface file = conn
0815: .getCurrentDirectory().lookupFile(
0816: st.nextToken());
0817:
0818: if (owner != null) {
0819: file.setOwner(owner);
0820: }
0821:
0822: if (group != null) {
0823: file.setGroup(group);
0824: }
0825: } catch (FileNotFoundException e) {
0826: reply.addComment(e.getMessage());
0827: }
0828: }
0829:
0830: return Reply.RESPONSE_200_COMMAND_OK;
0831: }
0832:
0833: private Reply doSITE_LINK(BaseFtpConnection conn) {
0834: if (!conn.getRequest().hasArgument()) {
0835: return Reply.RESPONSE_501_SYNTAX_ERROR;
0836: }
0837:
0838: StringTokenizer st = new StringTokenizer(conn.getRequest()
0839: .getArgument(), " ");
0840:
0841: if (st.countTokens() != 2) {
0842: return Reply.RESPONSE_501_SYNTAX_ERROR;
0843: }
0844:
0845: String targetName = st.nextToken();
0846: String linkName = st.nextToken();
0847: LinkedRemoteFile target;
0848:
0849: try {
0850: target = conn.getCurrentDirectory().lookupFile(targetName);
0851: } catch (FileNotFoundException e) {
0852: return Reply.RESPONSE_550_REQUESTED_ACTION_NOT_TAKEN;
0853: }
0854:
0855: if (!target.isDirectory()) {
0856: return new Reply(501, "Only link to directories for now.");
0857: }
0858:
0859: StaticRemoteFile link = new StaticRemoteFile(linkName, null,
0860: targetName);
0861: conn.getCurrentDirectory().addFile(link);
0862:
0863: return Reply.RESPONSE_200_COMMAND_OK;
0864: }
0865:
0866: /**
0867: * USAGE: site wipe [-r] <file/directory>
0868: *
0869: * This is similar to the UNIX rm command.
0870: * In glftpd, if you just delete a file, the uploader loses credits and
0871: * upload stats for it. There are many people who didn't like that and
0872: * were unable/too lazy to write a shell script to do it for them, so I
0873: * wrote this command to get them off my back.
0874: *
0875: * If the argument is a file, it will simply be deleted. If it's a
0876: * directory, it and the files it contains will be deleted. If the
0877: * directory contains other directories, the deletion will be aborted.
0878: *
0879: * To remove a directory containing subdirectories, you need to use
0880: * "site wipe -r dirname". BE CAREFUL WHO YOU GIVE ACCESS TO THIS COMMAND.
0881: * Glftpd will check if the parent directory of the file/directory you're
0882: * trying to delete is writable by its owner. If not, wipe will not
0883: * execute, so to protect directories from being wiped, make their parent
0884: * 555.
0885: *
0886: * Also, wipe will only work where you have the right to delete (in
0887: * glftpd.conf). Delete right and parent directory's mode of 755/777/etc
0888: * will cause glftpd to SWITCH TO ROOT UID and wipe the file/directory.
0889: * "site wipe -r /" will not work, but "site wipe -r /incoming" WILL, SO
0890: * BE CAREFUL.
0891: *
0892: * This command will remove the deleted files/directories from the dirlog
0893: * and dupefile databases.
0894: *
0895: * To give access to this command, add "-wipe -user flag =group" to the
0896: * config file (similar to other site commands).
0897: *
0898: * @param request
0899: * @param out
0900: */
0901: private Reply doSITE_WIPE(BaseFtpConnection conn) {
0902: if (!conn.getRequest().hasArgument()) {
0903: return Reply.RESPONSE_501_SYNTAX_ERROR;
0904: }
0905:
0906: String arg = conn.getRequest().getArgument();
0907:
0908: boolean recursive;
0909:
0910: if (arg.startsWith("-r ")) {
0911: arg = arg.substring(3);
0912: recursive = true;
0913: } else {
0914: recursive = false;
0915: }
0916:
0917: LinkedRemoteFile wipeFile;
0918:
0919: try {
0920: wipeFile = conn.getCurrentDirectory().lookupFile(arg);
0921: } catch (FileNotFoundException e) {
0922: return new Reply(
0923: 200,
0924: "Can't wipe: "
0925: + arg
0926: + " does not exist or it's not a plain file/directory");
0927: }
0928:
0929: if (wipeFile.isDirectory() && (wipeFile.dirSize() != 0)
0930: && !recursive) {
0931: return new Reply(200, "Can't wipe, directory not empty");
0932: }
0933:
0934: //if (conn.getConfig().checkDirLog(conn.getUserNull(), wipeFile)) {
0935: conn.getGlobalContext().dispatchFtpEvent(
0936: new DirectoryFtpEvent(conn.getUserNull(), "WIPE",
0937: wipeFile));
0938:
0939: //}
0940: wipeFile.delete();
0941:
0942: return Reply.RESPONSE_200_COMMAND_OK;
0943: }
0944:
0945: /**
0946: * <code>SIZE <SP> <pathname> <CRLF></code><br>
0947: *
0948: * Returns the size of the file in bytes.
0949: */
0950: private Reply doSIZE(BaseFtpConnection conn) {
0951: FtpRequest request = conn.getRequest();
0952:
0953: if (!request.hasArgument()) {
0954: return Reply.RESPONSE_501_SYNTAX_ERROR;
0955: }
0956:
0957: LinkedRemoteFile file;
0958:
0959: try {
0960: file = conn.getCurrentDirectory().lookupFile(
0961: request.getArgument());
0962: } catch (FileNotFoundException ex) {
0963: return Reply.RESPONSE_550_REQUESTED_ACTION_NOT_TAKEN;
0964: }
0965:
0966: return new Reply(213, Long.toString(file.length()));
0967: }
0968:
0969: /**
0970: * http://www.southrivertech.com/support/titanftp/webhelp/xcrc.htm
0971: *
0972: * Originally implemented by CuteFTP Pro and Globalscape FTP Server
0973: */
0974: private Reply doXCRC(BaseFtpConnection conn) {
0975: FtpRequest request = conn.getRequest();
0976:
0977: if (!request.hasArgument()) {
0978: return Reply.RESPONSE_501_SYNTAX_ERROR;
0979: }
0980:
0981: StringTokenizer st = new StringTokenizer(request.getArgument());
0982: LinkedRemoteFile myFile;
0983:
0984: try {
0985: myFile = conn.getCurrentDirectory().lookupFile(
0986: st.nextToken());
0987: } catch (FileNotFoundException e) {
0988: return Reply.RESPONSE_550_REQUESTED_ACTION_NOT_TAKEN;
0989: }
0990:
0991: if (st.hasMoreTokens()) {
0992: if (!st.nextToken().equals("0")
0993: || !st.nextToken().equals(
0994: Long.toString(myFile.length()))) {
0995: return Reply.RESPONSE_504_COMMAND_NOT_IMPLEMENTED_FOR_PARM;
0996: }
0997: }
0998:
0999: try {
1000: return new Reply(250, "XCRC Successful. "
1001: + Checksum.formatChecksum(myFile.getCheckSum()));
1002: } catch (NoAvailableSlaveException e1) {
1003: logger.warn("", e1);
1004:
1005: return new Reply(550, "NoAvailableSlaveException: "
1006: + e1.getMessage());
1007: }
1008: }
1009:
1010: public Reply execute(BaseFtpConnection conn)
1011: throws UnhandledCommandException {
1012: FtpRequest request = conn.getRequest();
1013: String cmd = request.getCommand();
1014:
1015: if ("CDUP".equals(cmd)) {
1016: return doCDUP(conn);
1017: }
1018:
1019: if ("CWD".equals(cmd)) {
1020: return doCWD(conn);
1021: }
1022:
1023: if ("MKD".equals(cmd)) {
1024: return doMKD(conn);
1025: }
1026:
1027: if ("PWD".equals(cmd)) {
1028: return doPWD(conn);
1029: }
1030:
1031: if ("RMD".equals(cmd)) {
1032: return doRMD(conn);
1033: }
1034:
1035: if ("RNFR".equals(cmd)) {
1036: return doRNFR(conn);
1037: }
1038:
1039: if ("RNTO".equals(cmd)) {
1040: return doRNTO(conn);
1041: }
1042:
1043: if ("SITE LINK".equals(cmd)) {
1044: return doSITE_LINK(conn);
1045: }
1046:
1047: if ("SITE WIPE".equals(cmd)) {
1048: return doSITE_WIPE(conn);
1049: }
1050:
1051: if ("XCRC".equals(cmd)) {
1052: return doXCRC(conn);
1053: }
1054:
1055: if ("MDTM".equals(cmd)) {
1056: return doMDTM(conn);
1057: }
1058:
1059: if ("SIZE".equals(cmd)) {
1060: return doSIZE(conn);
1061: }
1062:
1063: if ("DELE".equals(cmd)) {
1064: return doDELE(conn);
1065: }
1066:
1067: if ("SITE CHOWN".equals(cmd) || "SITE CHGRP".equals(cmd)) {
1068: return doSITE_CHOWN(conn);
1069: }
1070:
1071: throw UnhandledCommandException.create(Dir.class, request);
1072: }
1073:
1074: // public String getHelp(String cmd) {
1075: // ResourceBundle bundle = ResourceBundle.getBundle(Dir.class.getName());
1076: // if ("".equals(cmd))
1077: // return bundle.getString("help.general")+"\n";
1078: // else if("link".equals(cmd) || "link".equals(cmd) || "wipe".equals(cmd))
1079: // return bundle.getString("help."+cmd)+"\n";
1080: // else
1081: // return "";
1082: // }
1083:
1084: public String[] getFeatReplies() {
1085: return null;
1086: }
1087:
1088: public CommandHandler initialize(BaseFtpConnection conn,
1089: CommandManager initializer) {
1090: try {
1091: return (Dir) clone();
1092: } catch (CloneNotSupportedException e) {
1093: throw new RuntimeException(e);
1094: }
1095: }
1096:
1097: public void load(CommandManagerFactory initializer) {
1098: }
1099:
1100: public void unload() {
1101: }
1102: }
|