1 17 package org.drftpd.commands; 18 import java.io.FileNotFoundException ; 19 import java.text.SimpleDateFormat ; 20 import java.util.ArrayList ; 21 import java.util.Arrays ; 22 import java.util.Collection ; 23 import java.util.Date ; 24 import java.util.HashMap ; 25 import java.util.Iterator ; 26 import java.util.Set ; 27 import java.util.regex.Matcher ; 28 import java.util.regex.Pattern ; 29 import net.sf.drftpd.Bytes; 30 import net.sf.drftpd.ObjectNotFoundException; 31 import net.sf.drftpd.master.BaseFtpConnection; 32 import net.sf.drftpd.master.FtpReply; 33 import net.sf.drftpd.master.FtpRequest; 34 import net.sf.drftpd.master.RemoteSlave; 35 import net.sf.drftpd.master.command.CommandManager; 36 import net.sf.drftpd.master.command.CommandManagerFactory; 37 import net.sf.drftpd.master.usermanager.NoSuchUserException; 38 import net.sf.drftpd.master.usermanager.User; 39 import net.sf.drftpd.master.usermanager.UserFileException; 40 import net.sf.drftpd.remotefile.LinkedRemoteFile; 41 import net.sf.drftpd.remotefile.LinkedRemoteFileInterface; 42 import net.sf.drftpd.remotefile.MLSTSerialize; 43 44 53 public class Find implements CommandHandlerFactory, CommandHandler { 54 public void unload() { 55 } 56 public void load(CommandManagerFactory initializer) { 57 } 58 private static void findFile(BaseFtpConnection conn, FtpReply response, 59 LinkedRemoteFileInterface dir, Collection options, 60 Collection actions, boolean files, boolean dirs) { 61 if (!conn.getConfig().checkPrivPath(conn.getUserNull(), dir)) { 63 return; 65 } 66 for (Iterator iter = dir.getFiles().iterator(); iter.hasNext();) { 67 LinkedRemoteFileInterface file = (LinkedRemoteFileInterface) iter 68 .next(); 69 if (file.isDirectory()) { 70 findFile(conn, response, file, options, actions, files, dirs); 71 } 72 if (dirs && file.isDirectory() || files && file.isFile()) { 73 boolean checkIt = true; 74 for (Iterator iterator = options.iterator(); iterator.hasNext();) { 75 if (response.size() >= 100) 76 return; 77 FindOption findOption = (FindOption) iterator.next(); 78 if (!findOption.isTrueFor(file)) { 79 checkIt = false; 80 break; 81 } 82 } 83 if (!checkIt) 84 continue; 85 for (Iterator i = actions.iterator(); i.hasNext();) { 86 FindAction findAction = (FindAction) i.next(); 87 response.addComment(findAction.exec(conn, file)); 88 if (response.size() >= 100) { 89 response.addComment("<snip>"); 90 return; 91 } 92 } 93 } 94 } 95 } 96 private FindAction getAction(String actionName) { 97 if (actionName.equals("print")) { 98 return new ActionPrint(); 99 } else if (actionName.equals("wipe")) { 100 return new ActionWipe(); 101 } else if (actionName.equals("delete")) { 102 return new ActionDelete(); 103 } else if (actionName.equals("printf")) { 104 return new ActionPrintf(); 105 } else { 106 return null; 107 } 108 } 109 private FindAction getActionWithArgs(String actionName, String args) { 110 if (actionName.equals("printf")) 111 return new ActionPrintf(args); 112 else 113 return null; 114 } 115 private static FtpReply getHelpMsg() { 116 FtpReply response = (FtpReply) FtpReply.RESPONSE_200_COMMAND_OK.clone(); 117 response.addComment("SITE FIND <options> -action <action>"); 118 response 119 .addComment("Options: -user <user> -group <group> -nogroup -nouser"); 120 response 121 .addComment("Options: -mtime [-]n -type [f|d] -slave <slave> -size [-]size"); 122 response 123 .addComment("Options: -name <name>(* for wildcard) -incomplete -offline"); 124 response.addComment("Actions: print, printf[(format)], wipe, delete"); 125 response.addComment("Options for printf format:"); 126 response.addComment("#f - filename"); 127 response.addComment("#s - filesize"); 128 response.addComment("#u - user"); 129 response.addComment("#g - group"); 130 response.addComment("#x - slave"); 131 response.addComment("#t - last modified"); 132 response.addComment("#h - parent"); 133 response.addComment("Example: SITE FIND -action printf(filename: #f size: #s"); 134 response.addComment("Multipe options and actions"); 135 response 136 .addComment("are allowed. If multiple options are given a file must match all"); 137 response.addComment("options for action to be taken."); 138 return response; 139 } 140 private static FtpReply getShortHelpMsg() { 141 FtpReply response = (FtpReply) FtpReply.RESPONSE_200_COMMAND_OK.clone(); 142 response.addComment("Usage: SITE FIND <options> -action <action>"); 143 response.addComment("SITE FIND -help for more info."); 144 return response; 145 } 146 private static String getArgs(String str) { 147 int start = str.indexOf("("); 148 int end = str.indexOf(")"); 149 if (start == -1 || end == -1) 150 return null; 151 if (start > end) 152 return null; 153 return str.substring(start + 1, end); 154 } 155 public FtpReply execute(BaseFtpConnection conn) { 156 FtpRequest request = conn.getRequest(); 157 if (!request.hasArgument()) { 158 return getShortHelpMsg(); 160 } 161 String args[] = request.getArgument().split(" "); 162 if (args.length == 0) { 163 return getShortHelpMsg(); 165 } 166 Collection c = Arrays.asList(args); 167 ArrayList options = new ArrayList (); 168 ArrayList actions = new ArrayList (); 169 boolean files = true; 170 boolean dirs = true; 171 boolean forceFilesOnly = false; 172 boolean forceDirsOnly = false; 173 for (Iterator iter = c.iterator(); iter.hasNext();) { 174 String arg = iter.next().toString(); 175 if (arg.toLowerCase().equals("-user")) { 176 if (!iter.hasNext()) 177 return FtpReply.RESPONSE_501_SYNTAX_ERROR; 178 options.add(new OptionUser(iter.next().toString())); 179 } else if (arg.toLowerCase().equals("-group")) { 180 if (!iter.hasNext()) 181 return FtpReply.RESPONSE_501_SYNTAX_ERROR; 182 options.add(new OptionGroup(iter.next().toString())); 183 } else if (arg.toLowerCase().equals("-name")) { 184 if (!iter.hasNext()) 185 return FtpReply.RESPONSE_501_SYNTAX_ERROR; 186 options.add(new OptionName(iter.next().toString())); 187 } else if (arg.toLowerCase().equals("-slave")) { 188 if (!iter.hasNext()) 189 return FtpReply.RESPONSE_501_SYNTAX_ERROR; 190 RemoteSlave rs = null; 191 String slaveName = iter.next().toString(); 192 try { 193 rs = conn.getSlaveManager().getSlave(slaveName); 194 } catch (ObjectNotFoundException e) { 195 return new FtpReply(500, "Slave " + slaveName 196 + " was not found."); 197 } 198 forceFilesOnly = true; 199 options.add(new OptionSlave(rs)); 200 } else if (arg.toLowerCase().equals("-mtime")) { 201 if (!iter.hasNext()) 202 return FtpReply.RESPONSE_501_SYNTAX_ERROR; 203 int offset = 0; 204 try { 205 offset = Integer.parseInt(iter.next().toString()); 206 } catch (NumberFormatException e) { 207 return FtpReply.RESPONSE_501_SYNTAX_ERROR; 208 } 209 options.add(new OptionMTime(offset)); 210 } else if (arg.toLowerCase().equals("-size")) { 211 if (!iter.hasNext()) 212 return FtpReply.RESPONSE_501_SYNTAX_ERROR; 213 long size = 0; 214 boolean bigger = true; 215 String bytes = iter.next().toString(); 216 if (bytes.startsWith("-")) { 217 bigger = false; 218 bytes = bytes.substring(1); 219 } 220 try { 221 size = Bytes.parseBytes(bytes); 222 } catch (NumberFormatException e) { 223 return FtpReply.RESPONSE_501_SYNTAX_ERROR; 224 } 225 options.add(new OptionSize(size, bigger)); 226 } else if (arg.toLowerCase().equals("-type")) { 227 if (!iter.hasNext()) 228 return FtpReply.RESPONSE_501_SYNTAX_ERROR; 229 String type = iter.next().toString().toLowerCase(); 230 if (type.equals("f")) 231 dirs = false; 232 else if (type.equals("d")) 233 files = false; 234 else 235 return FtpReply.RESPONSE_501_SYNTAX_ERROR; 236 } else if (arg.toLowerCase().equals("-help")) { 237 return getHelpMsg(); 238 } else if (arg.toLowerCase().equals("-nouser")) { 239 options.add(new OptionUser("nobody")); 240 } else if (arg.toLowerCase().equals("-incomplete")) { 241 forceDirsOnly = true; 242 options.add(new OptionIncomplete()); 243 } else if (arg.toLowerCase().equals("-offline")) { 244 forceDirsOnly = true; 245 options.add(new OptionOffline()); 246 } else if (arg.toLowerCase().equals("-nogroup")) { 247 options.add(new OptionGroup("drftpd")); 248 } else if (arg.toLowerCase().equals("-action")) { 249 if (!iter.hasNext()) 250 return FtpReply.RESPONSE_501_SYNTAX_ERROR; 251 String action = iter.next().toString(); 252 if (action.indexOf("(") != -1) { 253 String cmd = action.substring(0, action.indexOf("(")); 254 boolean go = true; 255 while (go) { 256 if (action.endsWith(")")) { 257 FindAction findAction = getActionWithArgs(cmd, 258 getArgs(action)); 259 actions.add(findAction); 260 go = false; 261 continue; 262 } else if (!iter.hasNext()) { 263 return FtpReply.RESPONSE_501_SYNTAX_ERROR; 264 } else { 265 action += " " + iter.next().toString(); 266 } 267 } 268 } else { 269 FindAction findAction = getAction(action.toLowerCase()); 270 if (findAction == null) 271 return FtpReply.RESPONSE_501_SYNTAX_ERROR; 272 if (findAction instanceof ActionWipe) { 273 if (!conn.getUserNull().isAdmin()) 274 return FtpReply.RESPONSE_530_ACCESS_DENIED; 275 } 276 actions.add(findAction); 277 } 278 } else { 279 return FtpReply.RESPONSE_501_SYNTAX_ERROR; 280 } 281 } 282 FtpReply response = (FtpReply) FtpReply.RESPONSE_200_COMMAND_OK.clone(); 283 if (actions.size() == 0) 286 actions.add(new ActionPrint()); 287 if (!dirs && !files) { 288 dirs = true; 289 files = true; 290 } 291 if (forceFilesOnly && forceDirsOnly) { 294 return new FtpReply(500, 295 "Option conflict. Possibly -slave and -incomplete."); 296 } else if (forceFilesOnly) { 297 dirs = false; 298 response 299 .addComment("Forcing a file only search because of -slave option."); 300 } else if (forceDirsOnly) { 301 files = false; 302 response.addComment("Forcing a dir only search."); 303 } 304 options.add(new OptionType(files, dirs)); 305 findFile(conn, response, conn.getCurrentDirectory(), options, actions, 306 files, dirs); 307 return response; 308 } 309 public CommandHandler initialize(BaseFtpConnection conn, 310 CommandManager initializer) { 311 return this; 312 } 313 public String [] getFeatReplies() { 314 return null; 315 } 316 private interface FindAction { 317 public String exec(BaseFtpConnection conn, 318 LinkedRemoteFileInterface file); 319 } 320 private interface FindOption { 321 public boolean isTrueFor(LinkedRemoteFileInterface file); 322 } 323 private static class ActionDelete implements FindAction { 324 private String doDELE(BaseFtpConnection conn, 325 LinkedRemoteFileInterface file) { 326 String fileName = file.getName(); 334 LinkedRemoteFile requestedFile = (LinkedRemoteFile) file; 335 if (requestedFile.getUsername().equals( 343 conn.getUserNull().getUsername())) { 344 if (!conn.getConfig().checkDeleteOwn(conn.getUserNull(), 345 requestedFile)) { 346 return "Access denied for " + file.getPath(); 348 } 349 } else if (!conn.getConfig().checkDelete(conn.getUserNull(), 350 requestedFile)) { 351 return "Access denied for " + file.getPath(); 353 } 354 String reply = "Deleted " + requestedFile.getPath(); 357 User uploader; 358 try { 359 uploader = conn.getConnectionManager().getUserManager() 360 .getUserByName(requestedFile.getUsername()); 361 uploader 362 .updateCredits((long) -(requestedFile.length() * uploader 363 .getRatio())); 364 } catch (UserFileException e) { 365 reply += "Error removing credits: " + e.getMessage(); 366 } catch (NoSuchUserException e) { 367 reply += "Error removing credits: " + e.getMessage(); 368 } 369 requestedFile.delete(); 374 return reply; 375 } 376 382 public String exec(BaseFtpConnection conn, 383 LinkedRemoteFileInterface file) { 384 return doDELE(conn, file); 385 } 386 } 387 private static class ActionPrint implements FindAction { 388 393 public String exec(BaseFtpConnection conn, 394 LinkedRemoteFileInterface file) { 395 return file.getPath(); 396 } 397 } 398 class ActionPrintf implements FindAction { 399 private String format; 400 private String parent; 401 private boolean useFormat; 402 public ActionPrintf() { 403 useFormat = false; 404 } 405 public ActionPrintf(String f) { 406 format = f; 407 if(format == null) 408 useFormat = false; 409 else 410 useFormat = true; 411 } 412 417 public String exec(BaseFtpConnection conn, 418 LinkedRemoteFileInterface file) { 419 try { 420 parent = file.getParent(); 421 } catch(FileNotFoundException e) { 422 parent = "/"; 423 } 424 String mlst = MLSTSerialize.toMLST(file); 425 String retval = null; 426 try { 427 retval = formatMLST(mlst); 428 } catch (NumberFormatException e) { 429 return mlst; 430 } 431 return retval; 432 } 433 private String formatMLST(String mlst) throws NumberFormatException { 434 String strDate = getValue(mlst, "modify="); 435 long date = Long.parseLong(strDate.replaceAll("[.]", "")); 436 SimpleDateFormat sdf = new SimpleDateFormat ("MMM d H:mm z"); 437 String retval = mlst 438 .replaceAll(strDate, sdf.format(new Date (date))); 439 String strSize = getValue(mlst, "size="); 440 long size = Long.parseLong(strSize); 441 retval = retval.replaceAll(strSize, Bytes.formatBytes(size)); 442 Matcher mlstMatch = Pattern 458 .compile( 459 "type=(.*);size=(.*);modify=(.*);unix[.]owner=(.*);unix[.]group=(.*);.*=(.*); (.*)") 460 .matcher(retval); 461 Matcher mlstDirMatch = Pattern 464 .compile( 465 "type=(.*);size=(.*);modify=(.*);unix[.]owner=(.*);unix[.]group=(.*); (.*)") 466 .matcher(retval); 467 String mrRegex = null; 468 if (mlstMatch.matches()) { 469 if (!useFormat) { 474 mrRegex = mlstMatch.group(7) + " | " + mlstMatch.group(2) 475 + " | " + mlstMatch.group(3) + " | " 476 + mlstMatch.group(4) + " | " + mlstMatch.group(5) 477 + " | " + mlstMatch.group(6); 478 } else { 479 HashMap formats = new HashMap (); 480 formats.put("#f", mlstMatch.group(7)); 481 formats.put("#s", mlstMatch.group(2)); 482 formats.put("#u", mlstMatch.group(4)); 483 formats.put("#g", mlstMatch.group(5)); 484 formats.put("#t", mlstMatch.group(3)); 485 formats.put("#x", mlstMatch.group(6)); 486 formats.put("#h", parent); 487 Set keys = formats.keySet(); 488 String temp = format; 489 for (Iterator iter = keys.iterator(); iter.hasNext();) { 490 String form = iter.next().toString(); 491 temp = temp.replaceAll(form, formats.get(form) 492 .toString()); 493 } 494 mrRegex = temp; 495 } 496 return mrRegex; 497 } else if (mlstDirMatch.matches()) { 498 if(!useFormat) { 499 mrRegex = mlstDirMatch.group(6) + " | " + mlstDirMatch.group(2) 500 + " | " + mlstDirMatch.group(3) + " | " 501 + mlstDirMatch.group(4) + " | " + mlstDirMatch.group(5); 502 } else { 503 HashMap formats = new HashMap (); 504 formats.put("#f", mlstDirMatch.group(6)); 505 formats.put("#s", mlstDirMatch.group(2)); 506 formats.put("#u", mlstDirMatch.group(4)); 507 formats.put("#g", mlstDirMatch.group(5)); 508 formats.put("#t", mlstDirMatch.group(3)); 509 formats.put("#x", ""); 510 formats.put("#h", parent); 511 Set keys = formats.keySet(); 512 String temp = format; 513 for (Iterator iter = keys.iterator(); iter.hasNext();) { 514 String form = iter.next().toString(); 515 temp = temp.replaceAll(form, formats.get(form) 516 .toString()); 517 } 518 mrRegex = temp; 519 } 520 return mrRegex; 521 } 522 return retval; 523 } 524 private String getValue(String main, String sub) { 525 int index = main.indexOf(sub); 526 int endIndex = main.indexOf(";", index + 1); 527 String retval = main.substring(index + sub.length(), endIndex); 528 return retval; 529 } 530 } 531 private static class ActionWipe implements FindAction { 532 538 public String exec(BaseFtpConnection conn, 539 LinkedRemoteFileInterface file) { 540 User user = conn.getUserNull(); 541 file.delete(); 544 return "Wiped " + file.getPath(); 545 } 546 } 547 private static class OptionGroup implements FindOption { 548 553 private String groupname; 554 public OptionGroup(String g) { 555 groupname = g; 556 } 557 public boolean isTrueFor(LinkedRemoteFileInterface file) { 558 if (file.getGroupname().equals(groupname)) 559 return true; 560 else 561 return false; 562 } 563 } 564 private static class OptionIncomplete implements FindOption { 565 570 public boolean isTrueFor(LinkedRemoteFileInterface file) { 571 try { 572 return !file.lookupSFVFile().getStatus().isFinished(); 573 } catch (Exception e) { 574 return false; 575 } 576 580 } 581 } 582 private static class OptionOffline implements FindOption { 583 588 public boolean isTrueFor(LinkedRemoteFileInterface file) { 589 try { 590 return file.lookupSFVFile().getStatus().getOffline() != 0; 591 } catch (Exception e) { 592 return false; 593 } 594 598 } 599 } 600 private static class OptionMTime implements FindOption { 601 606 private Date date; 607 boolean after; 608 public OptionMTime(int h) { 609 after = true; 610 if (h < 0) { 611 after = false; 612 h = Math.abs(h); 613 } 614 long t = (long) h * 24 * 60 * 60 * 1000; 615 Date currentDate = new Date (); 616 date = new Date (currentDate.getTime() - t); 617 } 618 public boolean isTrueFor(LinkedRemoteFileInterface file) { 619 Date fileDate = new Date (file.lastModified()); 620 if (after) 621 return fileDate.after(date); 622 else 623 return fileDate.before(date); 624 } 625 } 626 private static class OptionName implements FindOption { 627 Pattern pattern; 628 public OptionName(String str) { 629 pattern = Pattern.compile(str.replaceAll("[*]", ".*")); 630 } 631 636 public boolean isTrueFor(LinkedRemoteFileInterface file) { 637 Matcher m = pattern.matcher(file.getName()); 638 return m.matches(); 639 } 640 } 641 private static class OptionSize implements FindOption { 642 boolean bigger; 643 long size; 644 public OptionSize(long s, boolean b) { 645 bigger = b; 646 size = s; 647 } 648 653 public boolean isTrueFor(LinkedRemoteFileInterface file) { 654 if (bigger) 655 return file.length() >= size; 656 else 657 return file.length() <= size; 658 } 659 } 660 private static class OptionSlave implements FindOption { 661 RemoteSlave slave; 662 public OptionSlave(RemoteSlave s) { 663 slave = s; 664 } 665 670 public boolean isTrueFor(LinkedRemoteFileInterface file) { 671 if (file.hasSlave(slave)) 672 return true; 673 else 674 return false; 675 } 676 } 677 private static class OptionType implements FindOption { 678 boolean files; 679 boolean dirs; 680 public OptionType(boolean f, boolean d) { 681 files = f; 682 dirs = d; 683 } 684 689 public boolean isTrueFor(LinkedRemoteFileInterface file) { 690 if (files && dirs) 691 return true; 692 else if (files && !dirs) 693 return file.isFile(); 694 else if (!files && dirs) 695 return file.isDirectory(); 696 else 697 return true; 698 } 699 } 700 private static class OptionUser implements FindOption { 701 706 private String username; 707 public OptionUser(String u) { 708 username = u; 709 } 710 public boolean isTrueFor(LinkedRemoteFileInterface file) { 711 if (file.getUsername().equals(username)) 712 return true; 713 else 714 return false; 715 } 716 } 717 } | Popular Tags |