/* * This file is part of DrFTPD, Distributed FTP Daemon. * * DrFTPD is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * DrFTPD is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with DrFTPD; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package org.drftpd.commands.nuke; import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.ResourceBundle; import java.util.StringTokenizer; import org.apache.log4j.Logger; import org.drftpd.Bytes; import org.drftpd.GlobalContext; import org.drftpd.commands.nuke.metadata.NukeData; import org.drftpd.master.BaseFtpConnection; import org.drftpd.master.Session; import org.drftpd.sections.SectionInterface; import org.drftpd.commandmanager.CommandInterface; import org.drftpd.commandmanager.CommandRequest; import org.drftpd.commandmanager.CommandResponse; import org.drftpd.commandmanager.ImproperUsageException; import org.drftpd.commandmanager.StandardCommandManager; import org.drftpd.commands.UserManagement; import org.drftpd.event.NukeEvent; import org.drftpd.exceptions.ObjectNotFoundException; import org.drftpd.usermanager.NoSuchUserException; import org.drftpd.usermanager.User; import org.drftpd.usermanager.UserFileException; import org.drftpd.vfs.DirectoryHandle; import org.drftpd.vfs.ObjectNotValidException; import org.drftpd.vfs.VirtualFileSystem; import org.tanesha.replacer.ReplacerEnvironment; /** * nukedamount -> amount after multiplier * amount -> amount before multiplier * * @author mog * @version $Id$ */ public class Nuke extends CommandInterface { private ResourceBundle _bundle; private String _keyPrefix; private static final Logger logger = Logger.getLogger(Nuke.class); public void initialize(String method, String pluginName, StandardCommandManager cManager) { super.initialize(method, pluginName, cManager); _bundle = cManager.getResourceBundle(); _keyPrefix = this.getClass().getName()+"."; } /** * USAGE: site nuke <directory> <multiplier> <message> * Nuke a directory * * ex. site nuke shit 2 CRAP * * This will nuke the directory 'shit' and remove x2 credits with the * comment 'CRAP'. * * NOTE: You can enclose the directory in braces if you have spaces in the name * ex. site NUKE {My directory name} 1 because_i_dont_like_it * * Q) What does the multiplier in 'site nuke' do? * A) Multiplier is a penalty measure. If it is 0, the user doesn't lose any * credits for the stuff being nuked. If it is 1, user only loses the * amount of credits he gained by uploading the files (which is calculated * by multiplying total size of file by his/her ratio). If multiplier is more * than 1, the user loses the credits he/she gained by uploading, PLUS some * extra credits. The formula is this: size * ratio + size * (multiplier - 1). * This way, multiplier of 2 causes user to lose size * ratio + size * 1, * so the additional penalty in this case is the size of nuked files. If the * multiplier is 3, user loses size * ratio + size * 2, etc. * @throws ImproperUsageException */ public CommandResponse doSITE_NUKE(CommandRequest request) throws ImproperUsageException { if (!request.hasArgument()) { throw new ImproperUsageException(); } StringTokenizer st = new StringTokenizer(request.getArgument()); if (!st.hasMoreTokens()) { throw new ImproperUsageException(); } Session session = request.getSession(); int multiplier; DirectoryHandle currentDir = request.getCurrentDirectory(); String nukeDirName = st.nextToken(); User requestUser = session.getUserNull(request.getUser()); String nukeDirPath = VirtualFileSystem.fixPath(nukeDirName); if (!(nukeDirPath.startsWith(VirtualFileSystem.separator))) { // Not a full path, let's make it one if (request.getCurrentDirectory().isRoot()) { boolean searchIndex = request.getProperties().getProperty("search","true"). equalsIgnoreCase("true"); if (searchIndex) { // Get dirs from index system ArrayList<DirectoryHandle> dirsToNuke; try { dirsToNuke = NukeUtils.findNukeDirs(currentDir, requestUser, nukeDirPath); } catch (FileNotFoundException e) { logger.warn(e); return new CommandResponse(550, e.getMessage()); } ReplacerEnvironment env = new ReplacerEnvironment(); if (dirsToNuke.isEmpty()) { env.add("searchstr", nukeDirPath); return new CommandResponse(550, session.jprintf(_bundle,_keyPrefix+"nuke.search.empty", env, requestUser)); } else if (dirsToNuke.size() == 1) { nukeDirPath = dirsToNuke.get(0).getPath(); } else { CommandResponse response = new CommandResponse(200); for (DirectoryHandle nukeDir : dirsToNuke) { try { env.add("name", nukeDir.getName()); env.add("path", nukeDir.getPath()); env.add("owner", nukeDir.getUsername()); env.add("group", nukeDir.getGroup()); env.add("size", Bytes.formatBytes(nukeDir.getSize())); response.addComment(session.jprintf(_bundle,_keyPrefix+"nuke.search.item", env, requestUser)); } catch (FileNotFoundException e) { logger.warn("Dir deleted after index search?, skip and continue: " + nukeDir.getPath()); } } response.addComment(session.jprintf(_bundle,_keyPrefix+"nuke.search.end", env, requestUser)); // Return matching dirs and let user decide what to nuke return response; } } else { nukeDirPath = VirtualFileSystem.separator + nukeDirPath; } } else { nukeDirPath = request.getCurrentDirectory().getPath() + VirtualFileSystem.separator + nukeDirPath; } } DirectoryHandle nukeDir; try { nukeDir = request.getCurrentDirectory().getDirectory(nukeDirPath, requestUser); } catch (FileNotFoundException e) { return StandardCommandManager.genericResponse("RESPONSE_550_REQUESTED_ACTION_NOT_TAKEN"); } catch (ObjectNotValidException e) { return new CommandResponse(550, nukeDirPath + " is not a directory"); } nukeDirName = nukeDir.getName(); if (!st.hasMoreTokens()) { throw new ImproperUsageException(); } try { multiplier = Integer.parseInt(st.nextToken()); } catch (NumberFormatException ex) { logger.warn(ex, ex); return new CommandResponse(501, "Invalid multiplier: " + ex.getMessage()); } String reason = ""; if (st.hasMoreTokens()) { reason = st.nextToken("").trim(); } NukeData nd; try { nd = NukeUtils.nuke(nukeDir, multiplier, reason, requestUser); } catch (NukeException e) { return new CommandResponse(500, "Nuke failed: " + e.getMessage()); } CommandResponse response = new CommandResponse(200, "Nuke succeeded"); GlobalContext.getEventService().publishAsync(new NukeEvent(requestUser, "NUKE", nd)); ReplacerEnvironment env = new ReplacerEnvironment(); String section = GlobalContext.getGlobalContext().getSectionManager().lookup(nukeDir).getName(); env.add("section", section); env.add("dir", nukeDirName); env.add("path", nukeDirPath); env.add("relpath", nukeDirPath.replaceAll("/"+section+"/","")); env.add("multiplier", multiplier); env.add("nukedamount", Bytes.formatBytes(nd.getAmount())); env.add("reason", reason); env.add("size", Bytes.formatBytes(nd.getSize())); if (session instanceof BaseFtpConnection) { response.addComment(session.jprintf(_bundle, _keyPrefix+"nuke", env, requestUser)); for (NukedUser nukeeObj : NukeBeans.getNukeeList(nd)) { ReplacerEnvironment nukeeenv = new ReplacerEnvironment(); User nukee; try { nukee = GlobalContext.getGlobalContext().getUserManager().getUserByName(nukeeObj.getUsername()); } catch (NoSuchUserException e1) { // Unable to get user, does not exist.. skip announce for this user continue; } catch (UserFileException e1) { // Error in user file.. skip announce for this user continue; } long debt = NukeUtils.calculateNukedAmount(nukeeObj.getAmount(), nukee.getKeyedMap().getObjectFloat(UserManagement.RATIO), multiplier); nukeeenv.add("nukedamount", Bytes.formatBytes(debt)); response.addComment(session.jprintf(_bundle, _keyPrefix+"nuke.nukees", nukeeenv, nukee)); } } return response; } public CommandResponse doSITE_NUKES(CommandRequest request) { CommandResponse response = StandardCommandManager.genericResponse("RESPONSE_200_COMMAND_OK"); ReplacerEnvironment env = new ReplacerEnvironment(); SectionInterface section = GlobalContext.getGlobalContext().getSectionManager().getSection(request.getArgument()); if (request.hasArgument() && section.getName().equalsIgnoreCase("")) { return new CommandResponse(501, "Invalid section!"); } if (NukeBeans.getNukeBeans().getAll().isEmpty()) { response.addComment(request.getSession().jprintf(_bundle, _keyPrefix+"nukes.empty", env, request.getUser())); } for (NukeData nd : NukeBeans.getNukeBeans().getAll()) { if (nd.getPath().startsWith(request.getArgument(), 1)) { env.add("path", nd.getPath()); env.add("multiplier", nd.getMultiplier()); env.add("usersnuked", nd.getNukees().size()); env.add("size", nd.getSize()); env.add("reason", nd.getReason()); env.add("amount", nd.getAmount()); env.add("nuker", nd.getUser()); response.addComment(request.getSession().jprintf(_bundle, _keyPrefix+"nukes", env, request.getUser())); } } if (response.getComment().isEmpty()) { env.add("section", section.getName()); response.addComment(request.getSession().jprintf(_bundle, _keyPrefix+"nukes.empty.section", env, request.getUser())); } return response; } /** * USAGE: site unnuke <directory> <message> * Unnuke a directory. * * ex. site unnuke shit NOT CRAP * * This will unnuke the directory 'shit' with the comment 'NOT CRAP'. * * NOTE: You can enclose the directory in braces if you have spaces in the name * ex. site unnuke {My directory name} justcause * * You need to configure glftpd to keep nuked files if you want to unnuke. * See the section about glftpd.conf. * @throws ImproperUsageException */ public CommandResponse doSITE_UNNUKE(CommandRequest request) throws ImproperUsageException { if (!request.hasArgument()) { throw new ImproperUsageException(); } StringTokenizer st = new StringTokenizer(request.getArgument()); Session session = request.getSession(); DirectoryHandle currentDir = request.getCurrentDirectory(); User user = session.getUserNull(request.getUser()); String toName = st.nextToken(); String toDir; String nukeName; if (!toName.startsWith(VirtualFileSystem.separator)) { // Not a full path, let's make it one and append [NUKED]- if needed. if (toName.startsWith("[NUKED]-")) { nukeName = toName; toName = toName.substring(8); } else { nukeName = "[NUKED]-" + toName; } if (request.getCurrentDirectory().isRoot()) { boolean searchIndex = request.getProperties().getProperty("search","true"). equalsIgnoreCase("true"); if (searchIndex) { // Get dirs from index system ArrayList<DirectoryHandle> dirsToUnNuke; try { dirsToUnNuke = NukeUtils.findNukeDirs(currentDir, user, nukeName); } catch (FileNotFoundException e) { logger.warn(e); return new CommandResponse(550, e.getMessage()); } ReplacerEnvironment env = new ReplacerEnvironment(); if (dirsToUnNuke.isEmpty()) { env.add("searchstr", nukeName); return new CommandResponse(550, session.jprintf(_bundle,_keyPrefix+"unnuke.search.empty", env, user)); } else if (dirsToUnNuke.size() == 1) { toDir = dirsToUnNuke.get(0).getParent().getPath() + VirtualFileSystem.separator; } else { CommandResponse response = new CommandResponse(200); for (DirectoryHandle nukeDir : dirsToUnNuke) { try { env.add("name", nukeDir.getName()); env.add("path", nukeDir.getPath()); env.add("owner", nukeDir.getUsername()); env.add("group", nukeDir.getGroup()); env.add("size", Bytes.formatBytes(nukeDir.getSize())); response.addComment(session.jprintf(_bundle,_keyPrefix+"unnuke.search.item", env, user)); } catch (FileNotFoundException e) { logger.warn("Dir deleted after index search?, skip and continue: " + nukeDir.getPath()); } } response.addComment(session.jprintf(_bundle,_keyPrefix+"unnuke.search.end", env, user)); // Return matching dirs and let user decide what to unnuke return response; } } else { toDir = VirtualFileSystem.separator; } } else { toDir = currentDir.getPath() + VirtualFileSystem.separator; } } else { // Full path to Nuked dir provided, append [NUKED]- if needed. toDir = VirtualFileSystem.fixPath(toName); toName = toDir.substring(toDir.lastIndexOf(VirtualFileSystem.separator)+1); toDir = toDir.substring(0,toDir.lastIndexOf(VirtualFileSystem.separator)+1); if (toName.startsWith("[NUKED]-")) { nukeName = toName; toName = toName.substring(8); } else { nukeName = "[NUKED]-" + toName; } } String reason; if (st.hasMoreTokens()) { reason = st.nextToken("").trim(); } else { reason = ""; } DirectoryHandle nukeDir; try { nukeDir = currentDir.getDirectory(toDir+nukeName, user); } catch (FileNotFoundException e) { // Maybe dir was deleted/wiped, lets remove it from nukelog. try { NukeBeans.getNukeBeans().remove(toDir+toName); } catch (ObjectNotFoundException ex) { return new CommandResponse(500, toDir+nukeName + " doesnt exist and no nukelog for this path was found."); } return new CommandResponse(200, toDir+nukeName + " doesnt exist, removed nuke from nukelog."); } catch (ObjectNotValidException e) { return new CommandResponse(550, toDir+nukeName + " is not a directory"); } NukeData nd; try { nd = NukeUtils.unnuke(nukeDir, reason); } catch (NukeException e) { return new CommandResponse(500, "Unnuke failed: " + e.getMessage()); } CommandResponse response = new CommandResponse(200, "Unnuke succeeded"); NukeEvent nukeEvent = new NukeEvent(session.getUserNull(request.getUser()), "UNNUKE", nd); GlobalContext.getEventService().publishAsync(nukeEvent); ReplacerEnvironment env = new ReplacerEnvironment(); String section = GlobalContext.getGlobalContext().getSectionManager().lookup(nukeDir).getName(); env.add("section", section); env.add("dir", nukeDir.getName()); env.add("path", nukeDir.getPath()); env.add("relpath", nukeDir.getPath().replaceAll("/"+section+"/","")); env.add("multiplier", nd.getMultiplier()); env.add("nukedamount", Bytes.formatBytes(nd.getAmount())); env.add("reason", reason); env.add("size", Bytes.formatBytes(nd.getSize())); if (session instanceof BaseFtpConnection) { response.addComment(session.jprintf(_bundle, _keyPrefix+"unnuke", env, user)); for (NukedUser nukeeObj : NukeBeans.getNukeeList(nd)) { ReplacerEnvironment nukeeenv = new ReplacerEnvironment(); User nukee; try { nukee = GlobalContext.getGlobalContext().getUserManager().getUserByName(nukeeObj.getUsername()); } catch (NoSuchUserException e1) { // Unable to get user, does not exist.. skip announce for this user continue; } catch (UserFileException e1) { // Error in user file.. skip announce for this user continue; } long debt = NukeUtils.calculateNukedAmount(nukeeObj.getAmount(), nukee.getKeyedMap().getObjectFloat(UserManagement.RATIO), nd.getMultiplier()); nukeeenv.add("nukedamount", Bytes.formatBytes(debt)); response.addComment(session.jprintf(_bundle, _keyPrefix+"unnuke.nukees", env, nukee)); } } return response; } public CommandResponse doSITE_NUKESCLEAN(CommandRequest request) { CommandResponse response = StandardCommandManager.genericResponse("RESPONSE_200_COMMAND_OK"); if (NukeBeans.getNukeBeans().getAll().isEmpty()) { response.addComment("Nukelog empty."); } ArrayList<String> entriesToRemove = new ArrayList<String>(); for (NukeData nd : NukeBeans.getNukeBeans().getAll()) { // Construct new path with [NUKED]- String newPath = VirtualFileSystem.fixPath(nd.getPath()); String fixedName = "[NUKED]-" + newPath.substring(newPath.lastIndexOf(VirtualFileSystem.separator)+1); newPath = newPath.substring(0,newPath.lastIndexOf(VirtualFileSystem.separator)+1) + fixedName; try { request.getCurrentDirectory().getDirectoryUnchecked(newPath); // Still here? .. all ok then, just continue with next item in nukelog } catch (FileNotFoundException e) { // Dir was deleted/wiped, lets remove it from nukelog. // Add path to list so we can delete it after going through entire nukelog entriesToRemove.add(nd.getPath()); } catch (ObjectNotValidException e) { return new CommandResponse(550, newPath + " is not a directory"); } } if (entriesToRemove.isEmpty()) { response.addComment("No entries to delete from nukelog."); } else { int deleted = 0; for (String path : entriesToRemove) { try { NukeBeans.getNukeBeans().remove(path); deleted++; } catch (ObjectNotFoundException e) { response.addComment("Error removing nukelog entry: " + path); } } response.addComment("Removed " + deleted + " invalid entries from the nukelog."); } return response; } }