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 org.drftpd.plugins;
0019:
0020: import java.io.File;
0021: import java.io.FileInputStream;
0022: import java.io.FileNotFoundException;
0023: import java.io.FileOutputStream;
0024: import java.io.FileReader;
0025: import java.io.IOException;
0026: import java.io.LineNumberReader;
0027: import java.io.PrintStream;
0028: import java.io.UnsupportedEncodingException;
0029: import java.lang.ref.WeakReference;
0030: import java.lang.reflect.Array;
0031: import java.lang.reflect.Method;
0032: import java.net.UnknownHostException;
0033: import java.util.ArrayList;
0034: import java.util.Collection;
0035: import java.util.Collections;
0036: import java.util.Comparator;
0037: import java.util.Enumeration;
0038: import java.util.HashMap;
0039: import java.util.Hashtable;
0040: import java.util.Iterator;
0041: import java.util.Map;
0042: import java.util.Observable;
0043: import java.util.Observer;
0044: import java.util.Properties;
0045: import java.util.ResourceBundle;
0046: import java.util.StringTokenizer;
0047:
0048: import net.sf.drftpd.FatalException;
0049: import net.sf.drftpd.NoAvailableSlaveException;
0050: import net.sf.drftpd.Nukee;
0051: import net.sf.drftpd.ObjectNotFoundException;
0052: import net.sf.drftpd.SlaveUnavailableException;
0053: import net.sf.drftpd.event.DirectoryFtpEvent;
0054: import net.sf.drftpd.event.Event;
0055: import net.sf.drftpd.event.FtpListener;
0056: import net.sf.drftpd.event.InviteEvent;
0057: import net.sf.drftpd.event.MessageEvent;
0058: import net.sf.drftpd.event.NukeEvent;
0059: import net.sf.drftpd.event.SlaveEvent;
0060: import net.sf.drftpd.event.TransferEvent;
0061: import net.sf.drftpd.master.FtpRequest;
0062: import net.sf.drftpd.master.GroupPosition;
0063: import net.sf.drftpd.master.UploaderPosition;
0064: import net.sf.drftpd.master.config.FtpConfig;
0065: import net.sf.drftpd.util.Blowfish;
0066: import net.sf.drftpd.util.ReplacerUtils;
0067:
0068: import org.apache.log4j.Level;
0069: import org.apache.log4j.Logger;
0070: import org.drftpd.Bytes;
0071: import org.drftpd.GlobalContext;
0072: import org.drftpd.PropertyHelper;
0073: import org.drftpd.SFVFile;
0074: import org.drftpd.Time;
0075: import org.drftpd.SFVFile.SFVStatus;
0076: import org.drftpd.commands.Nuke;
0077: import org.drftpd.commands.TransferStatistics;
0078: import org.drftpd.commands.UserManagement;
0079: import org.drftpd.id3.ID3Tag;
0080: import org.drftpd.master.SlaveManager;
0081: import org.drftpd.misc.CaseInsensitiveHashMap;
0082: import org.drftpd.permissions.Permission;
0083: import org.drftpd.remotefile.FileStillTransferringException;
0084: import org.drftpd.remotefile.FileUtils;
0085: import org.drftpd.remotefile.LinkedRemoteFile;
0086: import org.drftpd.remotefile.LinkedRemoteFileInterface;
0087: import org.drftpd.remotefile.LinkedRemoteFileUtils;
0088: import org.drftpd.sections.SectionInterface;
0089: import org.drftpd.sitebot.IRCCommand;
0090: import org.drftpd.slave.SlaveStatus;
0091: import org.drftpd.usermanager.NoSuchUserException;
0092: import org.drftpd.usermanager.User;
0093: import org.drftpd.usermanager.UserFileException;
0094: import org.tanesha.replacer.FormatterException;
0095: import org.tanesha.replacer.ReplacerEnvironment;
0096: import org.tanesha.replacer.ReplacerFormat;
0097: import org.tanesha.replacer.SimplePrintf;
0098:
0099: import f00f.net.irc.martyr.CommandRegister;
0100: import f00f.net.irc.martyr.Debug;
0101: import f00f.net.irc.martyr.IRCConnection;
0102: import f00f.net.irc.martyr.clientstate.Channel;
0103: import f00f.net.irc.martyr.commands.InviteCommand;
0104: import f00f.net.irc.martyr.commands.MessageCommand;
0105: import f00f.net.irc.martyr.commands.NickCommand;
0106: import f00f.net.irc.martyr.commands.NoticeCommand;
0107: import f00f.net.irc.martyr.commands.PartCommand;
0108: import f00f.net.irc.martyr.commands.RawCommand;
0109: import f00f.net.irc.martyr.commands.WhoisCommand;
0110: import f00f.net.irc.martyr.replies.WhoisUserReply;
0111: import f00f.net.irc.martyr.services.AutoJoin;
0112: import f00f.net.irc.martyr.services.AutoReconnect;
0113: import f00f.net.irc.martyr.services.AutoRegister;
0114: import f00f.net.irc.martyr.services.AutoResponder;
0115: import f00f.net.irc.martyr.util.FullNick;
0116:
0117: /**
0118: * @author mog
0119: * @version $Id: SiteBot.java 1562 2007-01-05 12:37:07Z zubov $
0120: */
0121: public class SiteBot extends FtpListener implements Observer {
0122: public static final ReplacerEnvironment GLOBAL_ENV = new ReplacerEnvironment();
0123:
0124: static {
0125: GLOBAL_ENV.add("bold", "\u0002");
0126: GLOBAL_ENV.add("coloroff", "\u000f");
0127: GLOBAL_ENV.add("color", "\u0003");
0128: GLOBAL_ENV.add("underline", "\u001f");
0129: }
0130:
0131: private static final Logger logger = Logger
0132: .getLogger(SiteBot.class);
0133:
0134: //private AutoJoin _autoJoin;
0135: private AutoReconnect _autoReconnect;
0136: private AutoRegister _autoRegister;
0137: // Object<Method, IRCCommand, IRCPermission>[3]
0138: private HashMap<String, Object[]> _methodMap;
0139: protected IRCConnection _conn;
0140: private boolean _enableAnnounce;
0141: private int _maxUserAnnounce;
0142: private int _maxGroupAnnounce;
0143: private CaseInsensitiveHashMap<String, ChannelConfig> _channelMap;
0144:
0145: //private String _key;
0146: private Hashtable<String, SectionSettings> _sections;
0147: protected String _server;
0148: protected int _port;
0149: private ArrayList<WhoisEntry> _identWhoisList = new ArrayList<WhoisEntry>();
0150:
0151: private String _primaryChannelName;
0152:
0153: public SiteBot() throws IOException {
0154: new File("logs").mkdirs();
0155: Debug.setOutputStream(new PrintStream(new FileOutputStream(
0156: "logs/sitebot.log", true)));
0157: }
0158:
0159: public static ArrayList<Nukee> map2nukees(Map nukees) {
0160: ArrayList<Nukee> ret = new ArrayList<Nukee>();
0161:
0162: for (Iterator iter = nukees.entrySet().iterator(); iter
0163: .hasNext();) {
0164: Map.Entry element = (Map.Entry) iter.next();
0165: ret.add(new Nukee((String) element.getKey(),
0166: ((Long) element.getValue()).longValue()));
0167: }
0168:
0169: Collections.sort(ret);
0170:
0171: return ret;
0172: }
0173:
0174: public static Collection<GroupPosition> topFileGroup(
0175: Collection files) {
0176: ArrayList<GroupPosition> ret = new ArrayList<GroupPosition>();
0177:
0178: for (Iterator iter = files.iterator(); iter.hasNext();) {
0179: LinkedRemoteFile file = (LinkedRemoteFile) iter.next();
0180: String groupname = file.getGroupname();
0181:
0182: GroupPosition stat = null;
0183:
0184: for (Iterator iter2 = ret.iterator(); iter2.hasNext();) {
0185: GroupPosition stat2 = (GroupPosition) iter2.next();
0186:
0187: if (stat2.getGroupname().equals(groupname)) {
0188: stat = stat2;
0189:
0190: break;
0191: }
0192: }
0193:
0194: if (stat == null) {
0195: stat = new GroupPosition(groupname, file.length(), 1,
0196: file.getXfertime());
0197: ret.add(stat);
0198: } else {
0199: stat.updateBytes(file.length());
0200: stat.updateFiles(1);
0201: stat.updateXfertime(file.getXfertime());
0202: }
0203: }
0204:
0205: Collections.sort(ret);
0206:
0207: return ret;
0208: }
0209:
0210: public static Collection userSort(Collection files, String type,
0211: String sort) {
0212: ArrayList<UploaderPosition> ret = new ArrayList<UploaderPosition>();
0213:
0214: for (Iterator iter = files.iterator(); iter.hasNext();) {
0215: LinkedRemoteFileInterface file = (LinkedRemoteFileInterface) iter
0216: .next();
0217: UploaderPosition stat = null;
0218:
0219: for (Iterator iter2 = ret.iterator(); iter2.hasNext();) {
0220: UploaderPosition stat2 = (UploaderPosition) iter2
0221: .next();
0222:
0223: if (stat2.getUsername().equals(file.getUsername())) {
0224: stat = stat2;
0225:
0226: break;
0227: }
0228: }
0229:
0230: if (stat == null) {
0231: stat = new UploaderPosition(file.getUsername(), file
0232: .length(), 1, file.getXfertime());
0233: ret.add(stat);
0234: } else {
0235: stat.updateBytes(file.length());
0236: stat.updateFiles(1);
0237: stat.updateXfertime(file.getXfertime());
0238: }
0239: }
0240:
0241: Collections.sort(ret, new UserComparator(type, sort));
0242:
0243: return ret;
0244: }
0245:
0246: public void actionPerformed(Event event) {
0247: try {
0248: if (event.getCommand().equals("RELOAD")) {
0249: try {
0250: reload();
0251: } catch (IOException e) {
0252: logger.log(Level.WARN, "", e);
0253: }
0254: } else if (event.getCommand().equals("SHUTDOWN")) {
0255: MessageEvent mevent = (MessageEvent) event;
0256: ReplacerEnvironment env = new ReplacerEnvironment(
0257: GLOBAL_ENV);
0258: env.add("message", mevent.getMessage());
0259:
0260: sayGlobal(ReplacerUtils.jprintf("shutdown", env,
0261: SiteBot.class));
0262: } else if (event instanceof InviteEvent) {
0263: actionPerformedInvite((InviteEvent) event);
0264: } else if (_enableAnnounce) {
0265: if (event instanceof DirectoryFtpEvent) {
0266: actionPerformedDirectory((DirectoryFtpEvent) event);
0267: } else if (event instanceof NukeEvent) {
0268: actionPerformedNuke((NukeEvent) event);
0269: } else if (event instanceof SlaveEvent) {
0270: actionPerformedSlave((SlaveEvent) event);
0271: }
0272: }
0273: } catch (FormatterException ex) {
0274: logger.warn("", ex);
0275: }
0276: }
0277:
0278: private void actionPerformedDirectory(DirectoryFtpEvent direvent)
0279: throws FormatterException {
0280: if (!getGlobalContext().getConfig().checkPathPermission(
0281: "dirlog", direvent.getUser(), direvent.getDirectory())) {
0282: return;
0283: }
0284:
0285: if ("MKD".equals(direvent.getCommand())) {
0286: sayDirectorySection(direvent, "mkdir");
0287: } else if ("REQUEST".equals(direvent.getCommand())) {
0288: sayDirectorySection(direvent, "request");
0289: } else if ("REQFILLED".equals(direvent.getCommand())) {
0290: sayDirectorySection(direvent, "reqfilled");
0291: } else if ("RMD".equals(direvent.getCommand())) {
0292: sayDirectorySection(direvent, "rmdir");
0293: } else if ("WIPE".equals(direvent.getCommand())) {
0294: if (direvent.getDirectory().isDirectory()) {
0295: sayDirectorySection(direvent, "wipe");
0296: }
0297: } else if ("PRE".equals(direvent.getCommand())) {
0298: sayDirectorySection(direvent, "pre");
0299: } else if ("STOR".equals(direvent.getCommand())) {
0300: actionPerformedDirectorySTOR((TransferEvent) direvent);
0301: }
0302: }
0303:
0304: private void actionPerformedDirectoryID3(TransferEvent direvent)
0305: throws FormatterException {
0306: ReplacerEnvironment env = new ReplacerEnvironment(GLOBAL_ENV);
0307: LinkedRemoteFile dir;
0308:
0309: try {
0310: dir = direvent.getDirectory().getParentFile();
0311: } catch (FileNotFoundException e) {
0312: throw new FatalException(e);
0313: }
0314:
0315: ID3Tag id3tag;
0316:
0317: try {
0318: id3tag = dir.lookupFile(dir.lookupMP3File()).getID3v1Tag();
0319: } catch (FileNotFoundException ex) {
0320: logger.info("No id3tag info for "
0321: + direvent.getDirectory().getPath()
0322: + ", can't publish id3tag info");
0323:
0324: return;
0325: } catch (NoAvailableSlaveException e) {
0326: logger.info("No available slave with id3 info");
0327:
0328: return;
0329: } catch (IOException e) {
0330: logger.warn("IO error reading id3 info", e);
0331:
0332: return;
0333: }
0334:
0335: env.add("path", dir.getName());
0336: env.add("genre", id3tag.getGenre().trim());
0337: env.add("year", id3tag.getYear().trim());
0338: env.add("album", id3tag.getAlbum().trim());
0339: env.add("artist", id3tag.getArtist().trim());
0340: env.add("title", id3tag.getTitle().trim());
0341:
0342: Ret ret = getPropertyFileSuffix("id3tag", dir);
0343: fillEnvSection(env, direvent, ret.getSection(), direvent
0344: .getDirectory());
0345: say(ret.getSection(), SimplePrintf
0346: .jprintf(ret.getFormat(), env));
0347: }
0348:
0349: private void actionPerformedDirectorySTOR(TransferEvent direvent)
0350: throws FormatterException {
0351:
0352: ReplacerEnvironment env = new ReplacerEnvironment(GLOBAL_ENV);
0353: LinkedRemoteFile dir;
0354:
0355: try {
0356: dir = direvent.getDirectory().getParentFile();
0357: } catch (FileNotFoundException e) {
0358: throw new FatalException(e);
0359: }
0360:
0361: // ANNOUNCE NFO FILE
0362: if (direvent.getDirectory().getName().toLowerCase().endsWith(
0363: ".nfo")) {
0364: Ret ret = getPropertyFileSuffix("store.nfo", dir);
0365: fillEnvSection(env, direvent, ret.getSection());
0366: say(ret.getSection(), SimplePrintf.jprintf(ret.getFormat(),
0367: env));
0368: }
0369:
0370: SFVFile sfvfile;
0371:
0372: try {
0373: sfvfile = dir.lookupSFVFile();
0374:
0375: // throws IOException, ObjectNotFoundException, NoAvailableSlaveException
0376: } catch (FileNotFoundException ex) {
0377: logger.info("No sfv file in "
0378: + direvent.getDirectory().getPath()
0379: + ", can't publish race info");
0380:
0381: return;
0382: } catch (NoAvailableSlaveException e) {
0383: logger.info("No available slave with .sfv");
0384:
0385: return;
0386: } catch (IOException e) {
0387: logger.warn("IO error reading .sfv", e);
0388:
0389: return;
0390: } catch (FileStillTransferringException e) {
0391: logger.info("SFVFile still transferring");
0392:
0393: return;
0394: }
0395:
0396: if (!sfvfile.hasFile(direvent.getDirectory().getName())) {
0397: return;
0398: }
0399:
0400: int halfway = (int) Math.floor((double) sfvfile.size() / 2);
0401:
0402: // we don't need to have an sfv in order to announce the mp3 info
0403: // TODO
0404: if (sfvfile.getStatus().getPresent() == 1) {
0405: actionPerformedDirectoryID3(direvent);
0406: }
0407:
0408: ///// start ///// start ////
0409: String username = direvent.getUser().getName();
0410: SFVStatus sfvstatus = sfvfile.getStatus();
0411: // ANNOUNCE FIRST FILE RCVD
0412: // and EXPECTING xxxMB in xxx Files on same line.
0413: if (sfvstatus.getAvailable() == 1
0414: && !direvent.getDirectory().isDeleted()) {
0415: Ret ret = getPropertyFileSuffix("store.first", dir);
0416: fillEnvSection(env, direvent, ret.getSection(), direvent
0417: .getDirectory());
0418: env.add("files", Integer.toString(sfvfile.size()));
0419: env.add("expectedsize", (Bytes.formatBytes(sfvfile
0420: .getTotalBytes()
0421: * sfvfile.size())));
0422: say(ret.getSection(), SimplePrintf.jprintf(ret.getFormat(),
0423: env));
0424: }
0425: //check if new racer
0426: if ((sfvfile.size() - sfvstatus.getMissing()) != 1) {
0427: for (Iterator iter = sfvfile.getFiles().iterator(); iter
0428: .hasNext();) {
0429: LinkedRemoteFile sfvFileEntry = (LinkedRemoteFile) iter
0430: .next();
0431:
0432: // If file just uploaded was deleted (by ZipScript?), move on and do nothing.
0433: if (direvent.getDirectory().isDeleted())
0434: break;
0435:
0436: if (sfvFileEntry == direvent.getDirectory())
0437: continue;
0438:
0439: if (sfvFileEntry.getUsername().equals(username)
0440: && sfvFileEntry.getXfertime() >= 0) {
0441: break;
0442: }
0443:
0444: if (!iter.hasNext()) {
0445: Ret ret = getPropertyFileSuffix("store.embraces",
0446: direvent.getDirectory());
0447: String format = ret.getFormat();
0448: fillEnvSection(env, direvent, ret.getSection(),
0449: direvent.getDirectory());
0450: env.add("filesleft", Integer.toString(sfvstatus
0451: .getMissing()));
0452:
0453: say(ret.getSection(), SimplePrintf.jprintf(format,
0454: env));
0455: }
0456: }
0457: }
0458:
0459: // env.add(
0460: // "averagespeed",
0461: // Bytes.formatBytes(
0462: // direvent.getDirectory().length() / (racedtimeMillis / 1000)));
0463: //COMPLETE
0464: if (sfvstatus.isFinished()) {
0465: Collection racers = userSort(sfvfile.getFiles(), "bytes",
0466: "high");
0467: Collection groups = topFileGroup(sfvfile.getFiles());
0468:
0469: //Collection fast = userSort(sfvfile.getFiles(), "xferspeed", "high");
0470: //Collection slow = userSort(sfvfile.getFiles(), "xferspeed", "low");
0471: //// store.complete ////
0472: Ret ret = getPropertyFileSuffix("store.complete", dir);
0473:
0474: try {
0475: fillEnvSection(env, direvent, ret.getSection(),
0476: direvent.getDirectory().getParentFile());
0477: } catch (FileNotFoundException e) {
0478: throw new RuntimeException(e);
0479: }
0480:
0481: env.add("racers", Integer.toString(racers.size()));
0482: env.add("groups", Integer.toString(groups.size()));
0483: env.add("files", Integer.toString(sfvfile.size()));
0484: env.add("size", Bytes.formatBytes(sfvfile.getTotalBytes()));
0485: env.add("speed", Bytes.formatBytes(sfvfile.getXferspeed())
0486: + "/s");
0487: say(ret.getSection(), SimplePrintf.jprintf(ret.getFormat(),
0488: env));
0489:
0490: //// store.complete.racer ////
0491: ret = getPropertyFileSuffix("store.complete.racer", dir);
0492:
0493: ReplacerFormat raceformat;
0494:
0495: // already have section from ret.section
0496: raceformat = ReplacerFormat.createFormat(ret.getFormat());
0497:
0498: try {
0499: fillEnvSection(env, direvent, ret.getSection(),
0500: direvent.getDirectory().getParentFile());
0501: } catch (FileNotFoundException e) {
0502: throw new RuntimeException(e);
0503: }
0504:
0505: int position = 1;
0506:
0507: for (Iterator iter = racers.iterator(); iter.hasNext()
0508: && (position <= _maxUserAnnounce); position++) {
0509: UploaderPosition stat = (UploaderPosition) iter.next();
0510:
0511: User raceuser;
0512:
0513: try {
0514: raceuser = getGlobalContext().getUserManager()
0515: .getUserByName(stat.getUsername());
0516: } catch (NoSuchUserException e2) {
0517: continue;
0518: } catch (UserFileException e2) {
0519: logger.log(Level.FATAL, "Error reading userfile",
0520: e2);
0521:
0522: continue;
0523: }
0524:
0525: ReplacerEnvironment raceenv = new ReplacerEnvironment(
0526: GLOBAL_ENV);
0527:
0528: raceenv.add("section", ret.getSection().getName());
0529: raceenv.add("user", raceuser.getName());
0530: raceenv.add("group", raceuser.getGroup());
0531:
0532: raceenv.add("position", new Integer(position));
0533: raceenv.add("size", Bytes.formatBytes(stat.getBytes()));
0534: raceenv.add("files", Integer.toString(stat.getFiles()));
0535: raceenv.add("percent", Integer.toString((stat
0536: .getFiles() * 100)
0537: / sfvfile.size())
0538: + "%");
0539: raceenv.add("speed", Bytes.formatBytes(stat
0540: .getXferspeed())
0541: + "/s");
0542: raceenv.add("alup", new Integer(TransferStatistics
0543: .getStatsPlace("ALUP", raceuser,
0544: getGlobalContext().getUserManager())));
0545: raceenv.add("monthup", new Integer(TransferStatistics
0546: .getStatsPlace("MONTHUP", raceuser,
0547: getGlobalContext().getUserManager())));
0548: raceenv.add("wkup", new Integer(TransferStatistics
0549: .getStatsPlace("WKUP", raceuser,
0550: getGlobalContext().getUserManager())));
0551: raceenv.add("dayup", new Integer(TransferStatistics
0552: .getStatsPlace("DAYUP", raceuser,
0553: getGlobalContext().getUserManager())));
0554: raceenv.add("aldn", new Integer(TransferStatistics
0555: .getStatsPlace("ALDN", raceuser,
0556: getGlobalContext().getUserManager())));
0557: raceenv.add("monthdn", new Integer(TransferStatistics
0558: .getStatsPlace("MONTHDN", raceuser,
0559: getGlobalContext().getUserManager())));
0560: raceenv.add("wkdn", new Integer(TransferStatistics
0561: .getStatsPlace("WKDN", raceuser,
0562: getGlobalContext().getUserManager())));
0563: raceenv.add("daydn", new Integer(TransferStatistics
0564: .getStatsPlace("DAYDN", raceuser,
0565: getGlobalContext().getUserManager())));
0566:
0567: say(ret.getSection(), SimplePrintf.jprintf(raceformat,
0568: raceenv));
0569: }
0570:
0571: Ret ret3 = getPropertyFileSuffix("store.complete.group",
0572: dir);
0573:
0574: // already have section from ret.section
0575: raceformat = ReplacerFormat.createFormat(ret3.getFormat());
0576: say(ret.getSection(), SimplePrintf.jprintf(
0577: getPropertyFileSuffix(
0578: "store.complete.group.header", dir)
0579: .getFormat(), env));
0580:
0581: position = 1;
0582:
0583: for (Iterator iter = groups.iterator(); iter.hasNext()
0584: && (position <= _maxGroupAnnounce); position++) {
0585: GroupPosition stat = (GroupPosition) iter.next();
0586:
0587: ReplacerEnvironment raceenv = new ReplacerEnvironment(
0588: GLOBAL_ENV);
0589:
0590: raceenv.add("section", ret.getSection().getName());
0591: raceenv.add("group", stat.getGroupname());
0592:
0593: raceenv.add("position", new Integer(position));
0594: raceenv.add("size", Bytes.formatBytes(stat.getBytes()));
0595: raceenv.add("files", Integer.toString(stat.getFiles()));
0596: raceenv.add("percent", Integer.toString((stat
0597: .getFiles() * 100)
0598: / sfvfile.size())
0599: + "%");
0600: raceenv.add("speed", Bytes.formatBytes(stat
0601: .getXferspeed())
0602: + "/s");
0603:
0604: say(ret.getSection(), SimplePrintf.jprintf(raceformat,
0605: raceenv));
0606: }
0607:
0608: //HALFWAY
0609: } else if ((sfvfile.size() >= 4)
0610: && (sfvstatus.getMissing() == halfway)) {
0611: Collection uploaders = userSort(sfvfile.getFiles(),
0612: "bytes", "high");
0613:
0614: // ReplacerEnvironment env = new ReplacerEnvironment(globalEnv);
0615: UploaderPosition stat = (UploaderPosition) uploaders
0616: .iterator().next();
0617:
0618: env.add("leadspeed", Bytes.formatBytes(stat.getXferspeed())
0619: + "/s");
0620: env.add("leadfiles", Integer.toString(stat.getFiles()));
0621: env.add("leadsize", Bytes.formatBytes(stat.getBytes()));
0622: env.add("leadpercent", Integer
0623: .toString((stat.getFiles() * 100) / sfvfile.size())
0624: + "%");
0625: env.add("filesleft", Integer.toString(sfvstatus
0626: .getMissing()));
0627:
0628: User leaduser = null;
0629: try {
0630: leaduser = getGlobalContext().getUserManager()
0631: .getUserByName(stat.getUsername());
0632: } catch (NoSuchUserException e3) {
0633: logger.log(Level.WARN, "", e3);
0634: } catch (UserFileException e3) {
0635: logger.log(Level.WARN, "", e3);
0636: }
0637: env.add("leaduser", leaduser != null ? leaduser.getName()
0638: : stat.getUsername());
0639: env.add("leadgroup", leaduser != null ? leaduser.getGroup()
0640: : "");
0641:
0642: Ret ret = getPropertyFileSuffix("store.halfway", dir);
0643: fillEnvSection(env, direvent, ret.getSection());
0644:
0645: say(ret.getSection(), SimplePrintf.jprintf(ret.getFormat(),
0646: env));
0647:
0648: // for (Iterator iter =
0649: // topFileUploaders2(sfvfile.getFiles()).iterator();
0650: // iter.hasNext();
0651: // ) {
0652: // UploaderPosition stat = (UploaderPosition) iter.next();
0653: // String str1;
0654: // try {
0655: // str1 =
0656: // formatUser(
0657: // _cm.getUsermanager().getUserByName(
0658: // stat.getUsername()));
0659: // } catch (NoSuchUserException e2) {
0660: // continue;
0661: // } catch (IOException e2) {
0662: // logger.log(
0663: // Level.FATAL,
0664: // "Error reading userfile",
0665: // e2);
0666: // continue;
0667: // }
0668: // say(
0669: // str1
0670: // + " ["
0671: // + stat.getFiles()
0672: // + "f/"
0673: // + Bytes.formatBytes(stat.getBytes())
0674: // + "]");
0675: // }
0676: }
0677: }
0678:
0679: private void actionPerformedInvite(InviteEvent event)
0680: throws FormatterException {
0681: String nick = event.getIrcNick();
0682:
0683: ReplacerEnvironment env = new ReplacerEnvironment(GLOBAL_ENV);
0684: env.add("user", event.getUser());
0685: env.add("nick", event.getIrcNick());
0686:
0687: if ("INVITE".equals(event.getCommand())
0688: || "SITE INVITE".equals(event.getCommand())) {
0689:
0690: ReplacerFormat format = ReplacerUtils.finalFormat(
0691: SiteBot.class, "invite.success");
0692: logger.info("Invited " + nick);
0693: for (Enumeration e = getIRCConnection().getClientState()
0694: .getChannels(); e.hasMoreElements();) {
0695: Channel chan = (Channel) e.nextElement();
0696:
0697: if (chan.findMember(
0698: getIRCConnection().getClientState().getNick()
0699: .getNick()).hasOps()) {
0700: ChannelConfig cc = _channelMap.get(chan.getName());
0701: if (cc != null) {
0702: if (cc.checkPerms(event.getUser())) {
0703: _conn.sendCommand(new InviteCommand(nick,
0704: chan.getName()));
0705: say(chan.getName(), SimplePrintf.jprintf(
0706: format, env));
0707: try {
0708: notice(nick, "Channel key for "
0709: + chan.getName()
0710: + " is "
0711: + cc.getChannelKey(event
0712: .getUser()));
0713: } catch (ObjectNotFoundException execption) {
0714: // no key or not enough permissions
0715: }
0716: } else {
0717: logger
0718: .warn("User does not have enough permissions to invite into "
0719: + chan.getName());
0720: }
0721: } else {
0722: logger
0723: .error(
0724: "Could not find ChannelConfig for "
0725: + chan.getName()
0726: + " this is a bug, please report it!",
0727: new Throwable());
0728: }
0729: }
0730: }
0731: synchronized (_identWhoisList) {
0732: _identWhoisList.add(new WhoisEntry(nick, event
0733: .getUser()));
0734: }
0735: logger.info("Looking up " + nick + " to set IRCIdent");
0736: _conn.sendCommand(new WhoisCommand(nick));
0737: } else if ("BINVITE".equals(event.getCommand())) {
0738: ReplacerFormat format = ReplacerUtils.finalFormat(
0739: SiteBot.class, "invite.failed");
0740: sayGlobal(SimplePrintf.jprintf(format, env));
0741: }
0742:
0743: }
0744:
0745: private void actionPerformedNuke(NukeEvent event)
0746: throws FormatterException {
0747: String cmd = event.getCommand();
0748: SectionInterface section = getGlobalContext()
0749: .getSectionManager().lookup(event.getPath());
0750: ReplacerEnvironment env = new ReplacerEnvironment(GLOBAL_ENV);
0751: env.add("size", Bytes.formatBytes(event.getSize()));
0752: env.add("section", section.getName());
0753:
0754: //Ret ret = getPropertyFileSuffix("nuke", event.getPath());
0755: env.add("path", strippath(event.getPath().substring(
0756: section.getPath().length())));
0757: env.add("reason", event.getReason());
0758: env.add("multiplier", String.valueOf(event.getMultiplier()));
0759:
0760: env.add("user", event.getUser().getName());
0761: env.add("group", event.getUser().getGroup());
0762:
0763: //env.add("nukees", event.getNukees().keySet());
0764: if (cmd.equals("NUKE")) {
0765: say(section, ReplacerUtils.jprintf("nuke", env,
0766: SiteBot.class));
0767:
0768: ReplacerFormat raceformat = ReplacerUtils.finalFormat(
0769: SiteBot.class, "nuke.nukees");
0770:
0771: int position = 1;
0772: long nobodyAmount = 0;
0773:
0774: for (Iterator iter = event.getNukees2().iterator(); iter
0775: .hasNext();) {
0776: Nukee stat = (Nukee) iter.next();
0777:
0778: User raceuser;
0779:
0780: try {
0781: raceuser = getGlobalContext().getUserManager()
0782: .getUserByName(stat.getUsername());
0783: } catch (NoSuchUserException e2) {
0784: nobodyAmount += stat.getAmount();
0785:
0786: continue;
0787: } catch (UserFileException e2) {
0788: logger.log(Level.FATAL, "Error reading userfile",
0789: e2);
0790:
0791: continue;
0792: }
0793:
0794: ReplacerEnvironment raceenv = new ReplacerEnvironment(
0795: GLOBAL_ENV);
0796:
0797: raceenv.add("user", raceuser.getName());
0798: raceenv.add("group", raceuser.getGroup());
0799:
0800: raceenv.add("position", "" + position++);
0801: raceenv
0802: .add("size", Bytes
0803: .formatBytes(stat.getAmount()));
0804:
0805: long nukedamount = Nuke.calculateNukedAmount(stat
0806: .getAmount(),
0807: getGlobalContext().getConfig()
0808: .getCreditCheckRatio(event.getPath(),
0809: raceuser), event
0810: .getMultiplier());
0811: raceenv.add("nukedamount", Bytes
0812: .formatBytes(nukedamount));
0813: say(section, SimplePrintf.jprintf(raceformat, raceenv));
0814: }
0815:
0816: if (nobodyAmount != 0) {
0817: ReplacerEnvironment raceenv = new ReplacerEnvironment(
0818: GLOBAL_ENV);
0819:
0820: raceenv.add("user", "nobody");
0821: raceenv.add("group", "nogroup");
0822:
0823: raceenv.add("position", "?");
0824: raceenv.add("size", Bytes.formatBytes(nobodyAmount));
0825:
0826: say(section, SimplePrintf.jprintf(raceformat, raceenv));
0827: }
0828: } else if (cmd.equals("UNNUKE")) {
0829: say(section, ReplacerUtils.jprintf("unnuke", env,
0830: SiteBot.class));
0831:
0832: ReplacerFormat raceformat = ReplacerUtils.finalFormat(
0833: SiteBot.class, "unnuke.nukees");
0834:
0835: int position = 1;
0836: long nobodyAmount = 0;
0837:
0838: for (Iterator iter = event.getNukees2().iterator(); iter
0839: .hasNext();) {
0840: Nukee stat = (Nukee) iter.next();
0841:
0842: User raceuser;
0843:
0844: try {
0845: raceuser = getGlobalContext().getUserManager()
0846: .getUserByName(stat.getUsername());
0847: } catch (NoSuchUserException e2) {
0848: nobodyAmount += stat.getAmount();
0849:
0850: continue;
0851: } catch (UserFileException e2) {
0852: logger.log(Level.FATAL, "Error reading userfile",
0853: e2);
0854:
0855: continue;
0856: }
0857:
0858: ReplacerEnvironment raceenv = new ReplacerEnvironment(
0859: GLOBAL_ENV);
0860:
0861: raceenv.add("user", raceuser.getName());
0862: raceenv.add("group", raceuser.getGroup());
0863:
0864: raceenv.add("position", "" + position++);
0865: raceenv
0866: .add("size", Bytes
0867: .formatBytes(stat.getAmount()));
0868:
0869: long nukedamount = Nuke.calculateNukedAmount(stat
0870: .getAmount(),
0871: getGlobalContext().getConfig()
0872: .getCreditCheckRatio(event.getPath(),
0873: raceuser), event
0874: .getMultiplier());
0875: raceenv.add("nukedamount", Bytes
0876: .formatBytes(nukedamount));
0877: say(section, SimplePrintf.jprintf(raceformat, raceenv));
0878: }
0879:
0880: if (nobodyAmount != 0) {
0881: ReplacerEnvironment raceenv = new ReplacerEnvironment(
0882: GLOBAL_ENV);
0883:
0884: raceenv.add("user", "nobody");
0885: raceenv.add("group", "nogroup");
0886:
0887: raceenv.add("position", "?");
0888: raceenv.add("size", Bytes.formatBytes(nobodyAmount));
0889:
0890: say(section, SimplePrintf.jprintf(raceformat, raceenv));
0891: }
0892:
0893: }
0894: }
0895:
0896: private void actionPerformedSlave(SlaveEvent event)
0897: throws FormatterException {
0898: ReplacerEnvironment env = new ReplacerEnvironment(GLOBAL_ENV);
0899: env.add("slave", event.getRSlave().getName());
0900: env.add("message", event.getMessage());
0901:
0902: if (event.getCommand().equals("ADDSLAVE")) {
0903: SlaveStatus status;
0904:
0905: try {
0906: status = event.getRSlave().getSlaveStatusAvailable();
0907: } catch (SlaveUnavailableException e) {
0908: logger.warn("in ADDSLAVE event handler", e);
0909:
0910: return;
0911: }
0912:
0913: fillEnvSlaveStatus(env, status, getSlaveManager());
0914:
0915: sayGlobal(ReplacerUtils.jprintf("addslave", env,
0916: SiteBot.class));
0917: } else if (event.getCommand().equals("DELSLAVE")) {
0918: sayGlobal(ReplacerUtils.jprintf("delslave", env,
0919: SiteBot.class));
0920: }
0921: }
0922:
0923: private AutoRegister addAutoRegister(Properties ircCfg) {
0924: return new AutoRegister(_conn, PropertyHelper.getProperty(
0925: ircCfg, "irc.nick"), PropertyHelper.getProperty(ircCfg,
0926: "irc.user"), PropertyHelper.getProperty(ircCfg,
0927: "irc.name"));
0928: }
0929:
0930: public void connect() throws UnknownHostException, IOException {
0931: logger.info("connecting to " + _server + ":" + _port);
0932: _conn.connect(_server, _port);
0933: }
0934:
0935: public class ChannelConfig {
0936: private AutoJoin _autoJoin = null;
0937: private String _blowKey = null;
0938: private String _chanKey = null;
0939: private WeakReference<Blowfish> _blowFish = null;
0940: private String _permissions = null;
0941:
0942: private ChannelConfig(String blowKey, String chanKey,
0943: String permissions) {
0944: _chanKey = chanKey;
0945: _permissions = (permissions == null) ? "*" : permissions;
0946: if (blowKey != null) {
0947: _blowKey = blowKey;
0948: _blowFish = new WeakReference<Blowfish>(new Blowfish(
0949: _blowKey));
0950: }
0951: }
0952:
0953: public void setAutoJoin(AutoJoin aj) {
0954: _autoJoin = aj;
0955: }
0956:
0957: public AutoJoin getAutoJoin() {
0958: return _autoJoin;
0959: }
0960:
0961: public String getBlowfishKey(User user)
0962: throws ObjectNotFoundException {
0963: if (checkPerms(user)) {
0964: if (_blowKey == null) {
0965: throw new ObjectNotFoundException(
0966: "Blowfish not enabled");
0967: }
0968: return _blowKey;
0969: }
0970: throw new ObjectNotFoundException("No Permissions");
0971: }
0972:
0973: private Blowfish getBlowFish() {
0974: if (_blowFish.get() == null) {
0975: _blowFish = new WeakReference<Blowfish>(new Blowfish(
0976: _blowKey));
0977: }
0978: return _blowFish.get();
0979: }
0980:
0981: public MessageCommand decrypt(MessageCommand cmd)
0982: throws UnsupportedEncodingException {
0983: if (_blowKey == null) {
0984: return cmd;
0985: }
0986: try {
0987: return new MessageCommand(cmd.getSource(), cmd
0988: .getDest(), getBlowFish().Decrypt(
0989: cmd.getMessage()));
0990: } catch (StringIndexOutOfBoundsException e) {
0991: throw new UnsupportedEncodingException();
0992: }
0993: }
0994:
0995: public String encrypt(String message) {
0996: if (_blowKey == null) {
0997: return message;
0998: }
0999: return getBlowFish().Encrypt(message);
1000: }
1001:
1002: private boolean checkPerms(User user) {
1003: Permission p = new Permission(FtpConfig
1004: .makeUsers(new StringTokenizer(_permissions)));
1005: return p.check(user);
1006: }
1007:
1008: public String getChannelKey(User user)
1009: throws ObjectNotFoundException {
1010: if (checkPerms(user) && _chanKey != null) {
1011: return _chanKey;
1012: }
1013: throw new ObjectNotFoundException("No Permissions");
1014: }
1015: }
1016:
1017: /**
1018: * Loads settings that require the IRCConnection _conn
1019: */
1020: private synchronized void connect(Properties ircCfg)
1021: throws UnknownHostException, IOException {
1022: disconnect();
1023:
1024: _conn = new IRCConnection();
1025:
1026: String initialCommand = null;
1027: try {
1028: initialCommand = PropertyHelper.getProperty(ircCfg,
1029: "irc.initial.command");
1030: } catch (NullPointerException e) {
1031: // null is handled below
1032: }
1033:
1034: try {
1035: _conn.setSendDelay(Integer.parseInt(ircCfg
1036: .getProperty("irc.sendDelay")));
1037: } catch (NumberFormatException e1) {
1038: logger.warn("irc.sendDelay not set, defaulting to 300ms");
1039: }
1040:
1041: _autoReconnect = new AutoReconnect(_conn);
1042: _autoRegister = addAutoRegister(ircCfg);
1043:
1044: new AutoResponder(_conn);
1045: _conn.addCommandObserver(this );
1046:
1047: for (int i = 1;; i++) {
1048: String classname = ircCfg
1049: .getProperty("martyr.plugins." + i);
1050:
1051: if (classname == null) {
1052: break;
1053: }
1054:
1055: Observer obs;
1056:
1057: try {
1058: logger.info("Loading " + Class.forName(classname));
1059: obs = (Observer) Class.forName(classname)
1060: .getConstructor(new Class[] { SiteBot.class })
1061: .newInstance(new Object[] { this });
1062: } catch (Exception e) {
1063: logger.warn("", e);
1064: throw new RuntimeException(
1065: "Error loading Martyr plugin :" + classname, e);
1066: }
1067: }
1068:
1069: connect();
1070:
1071: if (initialCommand != null) {
1072: _conn.sendCommand(new RawCommand(initialCommand));
1073: }
1074:
1075: }
1076:
1077: public void disconnect() {
1078: if (_autoReconnect != null) {
1079: _autoReconnect.disable();
1080: }
1081: if (_conn != null) {
1082: _conn.disconnect();
1083: }
1084: }
1085:
1086: private void fillEnvSection(ReplacerEnvironment env,
1087: DirectoryFtpEvent direvent, SectionInterface section) {
1088: fillEnvSection(env, direvent, section, direvent.getDirectory());
1089: }
1090:
1091: private void fillEnvSection(ReplacerEnvironment env,
1092: DirectoryFtpEvent direvent, SectionInterface section,
1093: LinkedRemoteFileInterface file) {
1094: env.add("user", direvent.getUser().getName());
1095: env.add("group", direvent.getUser().getGroup());
1096: env.add("section", section.getName());
1097:
1098: LinkedRemoteFileInterface dir = file;
1099:
1100: if (dir.isFile()) {
1101: dir = dir.getParentFileNull();
1102: }
1103:
1104: long starttime;
1105:
1106: try {
1107: starttime = FileUtils.getOldestFile(dir).lastModified();
1108: } catch (ObjectNotFoundException e) {
1109: starttime = dir.lastModified();
1110: }
1111:
1112: //starttime = System.currentTimeMillis() - starttime;
1113: //env.add("elapsed", "" + starttime);
1114: env.add("size", Bytes.formatBytes(file.length()));
1115: env.add("path", strippath(dir.getPath().substring(
1116: section.getPath().length())));
1117: env.add("file", file.getName());
1118:
1119: if (file.isFile()) {
1120: env.add("speed", Bytes
1121: .formatBytes(file.getXferspeed() * 1000)
1122: + "/s");
1123: file = file.getParentFileNull(); // files always have parent dirs.
1124: }
1125:
1126: long elapsed = (direvent.getTime() - starttime);
1127:
1128: env.add("secondstocomplete", Time.formatTime(elapsed));
1129:
1130: long elapsedSeconds = elapsed / 1000;
1131: env.add("averagespeed", (elapsedSeconds == 0) ? "n/a" : (Bytes
1132: .formatBytes(dir.length() / elapsedSeconds) + "/s"));
1133:
1134: SFVFile sfvfile;
1135:
1136: int totalsfv = 0;
1137: int totalfiles = 0;
1138: long totalbytes = 0;
1139: long totalxfertime = 0;
1140:
1141: if (direvent.getCommand().equals("PRE") == false) {
1142: try {
1143: sfvfile = file.lookupSFVFile();
1144: totalsfv += 1;
1145: totalfiles += sfvfile.size();
1146: totalbytes += sfvfile.getTotalBytes();
1147: totalxfertime += sfvfile.getTotalXfertime();
1148: } catch (Exception e) {
1149: }
1150: } else {
1151: /* For the PRE command totalfiles and totalspeed should reflect the
1152: * totals in all sub-directories.
1153: */
1154:
1155: if (direvent.getCommand().equals("PRE") == true) {
1156: for (LinkedRemoteFileInterface aFile : LinkedRemoteFileUtils
1157: .getAllFiles(file)) {
1158: if (aFile.getName().toLowerCase().endsWith(".sfv")) {
1159: try {
1160: sfvfile = aFile.getSFVFile();
1161: totalsfv += 1;
1162: totalfiles += sfvfile.size();
1163: totalbytes += sfvfile.getTotalBytes();
1164: totalxfertime += sfvfile.getTotalXfertime();
1165: } catch (Exception e) {
1166: continue;
1167: }
1168: }
1169: }
1170: }
1171: }
1172:
1173: if (totalsfv > 0) {
1174: env.add("totalfiles", "" + totalfiles);
1175: env.add("totalsize", Bytes.formatBytes(totalbytes));
1176:
1177: if (totalxfertime > 0) {
1178: env
1179: .add(
1180: "totalspeed",
1181: Bytes
1182: .formatBytes((totalbytes / totalxfertime) * 1000));
1183: } else {
1184: env.add("totalspeed", Bytes.formatBytes(0));
1185: }
1186: } else {
1187: env.add("totalfiles", "" + 0);
1188: env.add("totalsize", Bytes.formatBytes(0));
1189: env.add("totalspeed", Bytes.formatBytes(0));
1190:
1191: logger.warn("Couldn't get SFV file in announce");
1192: }
1193: }
1194:
1195: public static void fillEnvSlaveStatus(ReplacerEnvironment env,
1196: SlaveStatus status, SlaveManager slaveManager) {
1197: env.add("disktotal", Bytes.formatBytes(status
1198: .getDiskSpaceCapacity()));
1199: env.add("diskfree", Bytes.formatBytes(status
1200: .getDiskSpaceAvailable()));
1201: env.add("diskused", Bytes
1202: .formatBytes(status.getDiskSpaceUsed()));
1203:
1204: if (status.getDiskSpaceCapacity() == 0) {
1205: env.add("diskfreepercent", "n/a");
1206: env.add("diskusedpercent", "n/a");
1207: } else {
1208: env.add("diskfreepercent",
1209: ((status.getDiskSpaceAvailable() * 100) / status
1210: .getDiskSpaceCapacity())
1211: + "%");
1212: env.add("diskusedpercent",
1213: ((status.getDiskSpaceUsed() * 100) / status
1214: .getDiskSpaceCapacity())
1215: + "%");
1216: }
1217:
1218: env.add("xfers", "" + status.getTransfers());
1219: env.add("xfersdn", "" + status.getTransfersSending());
1220: env.add("xfersup", "" + status.getTransfersReceiving());
1221: env.add("xfersdown", "" + status.getTransfersSending());
1222:
1223: env.add("throughput", Bytes.formatBytes(status.getThroughput())
1224: + "/s");
1225:
1226: env.add("throughputup", Bytes.formatBytes(status
1227: .getThroughputReceiving())
1228: + "/s");
1229:
1230: env.add("throughputdown", Bytes.formatBytes(status
1231: .getThroughputSending())
1232: + "/s");
1233:
1234: try {
1235: env.add("slaves", ""
1236: + slaveManager.getAvailableSlaves().size());
1237: } catch (NoAvailableSlaveException e2) {
1238: env.add("slaves", "0");
1239: }
1240: }
1241:
1242: public String getPrimaryChannel() {
1243: return _primaryChannelName;
1244: }
1245:
1246: public IRCConnection getIRCConnection() {
1247: return _conn;
1248: }
1249:
1250: public Ret getPropertyFileSuffix(String prefix,
1251: LinkedRemoteFileInterface dir) {
1252: SectionInterface sectionObj = getGlobalContext()
1253: .getSectionManager().lookup(dir.getPath());
1254:
1255: return new Ret(ResourceBundle
1256: .getBundle(SiteBot.class.getName()).getString(prefix),
1257: sectionObj);
1258: }
1259:
1260: public SlaveManager getSlaveManager() {
1261: return getGlobalContext().getSlaveManager();
1262: }
1263:
1264: public void init(GlobalContext gctx) {
1265: super .init(gctx);
1266: try {
1267: reload();
1268: } catch (Exception e) {
1269: throw new RuntimeException(e);
1270: }
1271: }
1272:
1273: public void reconnect() {
1274: _conn.disconnect();
1275: }
1276:
1277: protected void reload() throws FileNotFoundException, IOException {
1278: Properties ircCfg = new Properties();
1279: synchronized (this ) {
1280: FileInputStream fis = null;
1281: try {
1282: fis = new FileInputStream("conf/irc.conf");
1283: ircCfg.load(fis);
1284: } finally {
1285: if (fis != null) {
1286: fis.close();
1287: }
1288: }
1289: reloadIRCCommands();
1290: }
1291: reload(ircCfg);
1292: }
1293:
1294: private void reloadIRCCommands() throws IOException {
1295: _methodMap = new HashMap<String, Object[]>();
1296: HashMap<String, IRCCommand> ircCommands = new HashMap<String, IRCCommand>();
1297: LineNumberReader lineReader = null;
1298: try {
1299: lineReader = new LineNumberReader(new FileReader(
1300: "conf/irccommands.conf"));
1301: String line = null;
1302: while ((line = lineReader.readLine()) != null) {
1303: if (line.startsWith("#") || line.trim().equals("")) {
1304: continue;
1305: }
1306: StringTokenizer st = new StringTokenizer(line);
1307: if (st.countTokens() < 4) {
1308: logger
1309: .error("Line is invalid -- not enough parameters \""
1310: + line + "\"");
1311: continue;
1312: }
1313: String trigger = st.nextToken();
1314: String methodString = st.nextToken();
1315: String scopeList = st.nextToken();
1316: String permissions = st.nextToken("").trim();
1317:
1318: int index = methodString.lastIndexOf(".");
1319: String className = methodString.substring(0, index);
1320: methodString = methodString.substring(index + 1);
1321: Method m = null;
1322: try {
1323: IRCCommand ircCommand = ircCommands.get(className);
1324: if (ircCommand == null) {
1325: ircCommand = (IRCCommand) Class
1326: .forName(className)
1327: .getConstructor(
1328: new Class[] { GlobalContext.class })
1329: .newInstance(
1330: new Object[] { getGlobalContext() });
1331: ircCommands.put(className, ircCommand);
1332: }
1333: m = ircCommand.getClass().getMethod(
1334: methodString,
1335: new Class[] { String.class,
1336: MessageCommand.class });
1337: _methodMap
1338: .put(trigger, new Object[] {
1339: m,
1340: ircCommand,
1341: new IRCPermission(scopeList,
1342: permissions) });
1343: } catch (Exception e) {
1344: logger.error(
1345: "Invalid class/method listed in irccommands.conf - "
1346: + line, e);
1347: throw new RuntimeException(e);
1348: }
1349: }
1350: } finally {
1351: if (lineReader != null) {
1352: lineReader.close();
1353: }
1354: }
1355: }
1356:
1357: /**
1358: * Loads irc settings, then passes it off to connect(Properties)
1359: */
1360: protected void reload(Properties ircCfg) throws IOException {
1361: _server = PropertyHelper.getProperty(ircCfg, "irc.server");
1362: _port = Integer.parseInt(PropertyHelper.getProperty(ircCfg,
1363: "irc.port"));
1364:
1365: _enableAnnounce = ircCfg.getProperty("irc.enable.announce",
1366: "false").equals("true");
1367: Debug.setDebugLevel(Integer.parseInt(ircCfg.getProperty(
1368: "irc.debuglevel", "15")));
1369: CaseInsensitiveHashMap<String, ChannelConfig> oldChannelMap = null;
1370: if (_channelMap != null) { // reload config
1371: oldChannelMap = _channelMap;
1372: } else {
1373: oldChannelMap = new CaseInsensitiveHashMap<String, ChannelConfig>();
1374: }
1375: synchronized (this ) {
1376: _channelMap = new CaseInsensitiveHashMap<String, ChannelConfig>();
1377:
1378: for (int i = 1;; i++) {
1379: String channelName = ircCfg.getProperty("irc.channel."
1380: + i);
1381: String blowKey = ircCfg.getProperty("irc.channel." + i
1382: + ".blowkey");
1383: String chanKey = ircCfg.getProperty("irc.channel." + i
1384: + ".chankey");
1385: String permissions = ircCfg.getProperty("irc.channel."
1386: + i + ".perms");
1387: if (channelName == null) {
1388: break;
1389: }
1390: if (i == 1) {
1391: _primaryChannelName = channelName.toUpperCase();
1392: }
1393:
1394: _channelMap.put(channelName, new ChannelConfig(blowKey,
1395: chanKey, permissions));
1396: }
1397:
1398: _sections = new Hashtable<String, SectionSettings>();
1399: for (int i = 1;; i++) {
1400: String name = ircCfg.getProperty("irc.section." + i);
1401:
1402: if (name == null) {
1403: break;
1404: }
1405:
1406: String chan = ircCfg.getProperty("irc.section." + i
1407: + ".channel");
1408:
1409: if (chan == null) {
1410: chan = _primaryChannelName;
1411: }
1412:
1413: _sections.put(name,
1414: new SectionSettings(ircCfg, i, chan));
1415: }
1416:
1417: if (_conn == null) {
1418: connect(ircCfg);
1419: }
1420: if ((!_conn.getClientState().getServer().equals(_server))
1421: || (_conn.getClientState().getPort() != _port)) {
1422: logger.info("Reconnecting due to server change");
1423: connect(ircCfg);
1424: }
1425:
1426: if (_conn.getClientState().getNick() != null
1427: && !_conn.getClientState().getNick().getNick()
1428: .equals(
1429: PropertyHelper.getProperty(ircCfg,
1430: "irc.nick"))) {
1431: logger.info("Switching to new nick");
1432: _autoRegister.disable();
1433: _autoRegister = addAutoRegister(ircCfg);
1434: _conn.sendCommand(new NickCommand(ircCfg
1435: .getProperty("irc.nick")));
1436: }
1437: for (Iterator iter = new ArrayList(Collections
1438: .list(getIRCConnection().getClientState()
1439: .getChannelNames())).iterator(); iter
1440: .hasNext();) {
1441: String currentChannel = (String) iter.next();
1442: if (_channelMap.containsKey(currentChannel)) { // still in
1443: // channel
1444: ChannelConfig newCC = _channelMap
1445: .get(currentChannel);
1446: ChannelConfig oldCC = oldChannelMap
1447: .get(currentChannel);
1448: if (newCC == null || oldCC == null) {
1449: logger.debug(
1450: "This is a bug! report me! -- channel="
1451: + currentChannel + " newCC="
1452: + newCC + " oldCC=" + oldCC,
1453: new Throwable());
1454: continue;
1455: }
1456: newCC.setAutoJoin(oldCC.getAutoJoin());
1457: } else { // removed from channel
1458: ChannelConfig oldCC = oldChannelMap
1459: .get(currentChannel);
1460: if (oldCC == null) {
1461: logger.debug(
1462: "This is a bug! report me! -- channel="
1463: + currentChannel
1464: + " oldCC=null",
1465: new Throwable());
1466: continue;
1467: }
1468: oldCC.getAutoJoin().disable();
1469: _conn.sendCommand(new PartCommand(currentChannel));
1470: _conn.getClientState()
1471: .removeChannel(currentChannel);
1472: _conn.removeCommandObserver(oldCC.getAutoJoin());
1473: }
1474: }
1475: }
1476: for (String channelName : _channelMap.keySet()) {
1477: ChannelConfig cc = _channelMap.get(channelName);
1478: if (cc == null) {
1479: logger.debug("This is a bug! report me! -- channel="
1480: + channelName + " cc=null", new Throwable());
1481: continue;
1482: }
1483: if (cc.getAutoJoin() == null) { // new channel!
1484: cc.setAutoJoin(new AutoJoin(_conn, channelName,
1485: cc._chanKey));
1486: }
1487: }
1488:
1489: //maximum announcements for race results
1490: try {
1491: _maxUserAnnounce = Integer.parseInt(ircCfg.getProperty(
1492: "irc.max.racers", "100"));
1493: } catch (NumberFormatException e) {
1494: logger.warn("Invalid setting in irc.conf: irc.max.racers",
1495: e);
1496: _maxUserAnnounce = 100;
1497: }
1498: try {
1499: _maxGroupAnnounce = Integer.parseInt(ircCfg.getProperty(
1500: "irc.max.groups", "100"));
1501: } catch (NumberFormatException e) {
1502: logger.warn("Invalid setting in irc.conf: irc.max.groups",
1503: e);
1504: _maxGroupAnnounce = 100;
1505: }
1506: }
1507:
1508: public void say(SectionInterface section, String message) {
1509: SectionSettings sn = null;
1510:
1511: if (section != null) {
1512: sn = (SectionSettings) _sections.get(section.getName());
1513: }
1514: say((sn != null) ? sn.getChannel() : _primaryChannelName,
1515: message);
1516: }
1517:
1518: public void say(String message) {
1519: say(_primaryChannelName, message);
1520: }
1521:
1522: public synchronized void say(String dest, String message) {
1523: if (message == null || message.equals("")) {
1524: return;
1525: }
1526: boolean isChan = dest.startsWith("#");
1527: String[] lines = message.split("\n");
1528: if (isChan) {
1529: ChannelConfig cc = _channelMap.get(dest);
1530: if (cc == null) {
1531: logger.debug("This is a bug! report me! -- channel="
1532: + dest + " ccMap=" + _channelMap,
1533: new Throwable());
1534: return;
1535: }
1536: for (String line : lines) {
1537: // don't encrypt private messages, at least not yet :)
1538: line = cc.encrypt(line);
1539: _conn.sendCommand(new MessageCommand(dest, line));
1540: }
1541: } else {
1542: for (String line : lines) {
1543: _conn.sendCommand(new MessageCommand(dest, line));
1544: }
1545: }
1546: }
1547:
1548: public synchronized void notice(String dest, String message) {
1549: if (message == null || message.equals("")) {
1550: return;
1551: }
1552: boolean isChan = dest.startsWith("#");
1553: String[] lines = message.split("\n");
1554: for (String line : lines) {
1555: // don't encrypt private notices, at least not yet :)
1556: if (isChan) {
1557: ChannelConfig cc = _channelMap.get(dest);
1558: if (cc == null) {
1559: logger.debug(
1560: "This is a bug! report me! -- channel="
1561: + dest + " cc=null",
1562: new Throwable());
1563: continue;
1564: }
1565: line = cc.encrypt(line);
1566: }
1567: _conn.sendCommand(new RawCommand("NOTICE " + dest + " :"
1568: + line));
1569: }
1570: }
1571:
1572: private void sayDirectorySection(DirectoryFtpEvent direvent,
1573: String string) throws FormatterException {
1574: Ret ret = getPropertyFileSuffix(string, direvent.getDirectory());
1575: String format = ret.getFormat();
1576:
1577: ReplacerEnvironment env = new ReplacerEnvironment(GLOBAL_ENV);
1578: fillEnvSection(env, direvent, ret.getSection());
1579:
1580: say(ret.getSection(), SimplePrintf.jprintf(format, env));
1581: }
1582:
1583: public void sayGlobal(String string) {
1584: for (Iterator iter = new ArrayList(Collections
1585: .list(getIRCConnection().getClientState()
1586: .getChannelNames())).iterator(); iter.hasNext();) {
1587: say((String) iter.next(), string);
1588: }
1589: }
1590:
1591: // why the hell is this here? don't we already have 10 methods that do this?
1592: private String strippath(String path) {
1593: if (!path.startsWith("/")) {
1594: return path;
1595: }
1596: return path.substring(1);
1597: }
1598:
1599: public void unload() {
1600: disconnect();
1601: }
1602:
1603: private class WhoisEntry {
1604: private long _created = System.currentTimeMillis();
1605: private String _nick = null;
1606: private User _user = null;
1607:
1608: private WhoisEntry(String nick, User user) {
1609: _nick = nick;
1610: _user = user;
1611: }
1612: }
1613:
1614: public void update(Observable observer, Object updated) {
1615: // add ident for those who use site invite
1616: if (updated instanceof WhoisUserReply) {
1617: WhoisUserReply whor = (WhoisUserReply) updated;
1618: String reply[] = whor.getParameter(whor.getSourceString(),
1619: 0).split(" ");
1620: String nick = reply[3];
1621: String fullIdent = reply[3] + "!" + reply[4] + "@"
1622: + reply[5];
1623: synchronized (_identWhoisList) {
1624: for (Iterator<WhoisEntry> iter = _identWhoisList
1625: .iterator(); iter.hasNext();) {
1626: WhoisEntry we = iter.next();
1627: if (we._nick.equalsIgnoreCase(nick)) {
1628: we._user.getKeyedMap().setObject(
1629: UserManagement.IRCIDENT, fullIdent);
1630: iter.remove();
1631: continue;
1632: }
1633: if ((System.currentTimeMillis() - we._created) > 60 * 1000) {
1634: iter.remove(); // bad nickname or very slow whois reply
1635: }
1636: }
1637: }
1638: } else if ((updated instanceof MessageCommand)) {
1639: synchronized (this ) {
1640: MessageCommand msgc = (MessageCommand) updated;
1641:
1642: // recreate the MessageCommand with the decrypted text
1643: if (!msgc.isPrivateToUs(_conn.getClientState())) {
1644: try {
1645: ChannelConfig cc = _channelMap.get(msgc
1646: .getDest());
1647: if (cc == null) {
1648: logger.debug(
1649: "This is a bug! report me! -- channel="
1650: + msgc.getDest()
1651: + " ccMap=" + _channelMap,
1652: new Throwable());
1653: return;
1654: }
1655: msgc = cc.decrypt(msgc);
1656: } catch (UnsupportedEncodingException e) {
1657: logger.warn("Unable to decrypt '"
1658: + msgc.getSourceString() + "'");
1659: return; // should not accept plaintext messages
1660: // encrypted channels
1661: }
1662: }
1663: int index = msgc.getMessage().indexOf(" ");
1664: String args = "";
1665: String trigger = "";
1666: if (index == -1) {
1667: trigger = msgc.getMessage().toLowerCase();
1668: } else {
1669: trigger = msgc.getMessage().substring(0, index);
1670: args = msgc.getMessage().substring(index + 1)
1671: .trim();
1672: }
1673: if (_methodMap.containsKey(trigger)) { // is a recognized
1674: // command
1675: Object[] objects = _methodMap.get(trigger);
1676: IRCPermission perm = (IRCPermission) objects[2];
1677: String scope = msgc.isPrivateToUs(_conn
1678: .getClientState()) ? "private" : msgc
1679: .getDest();
1680: if (!perm.checkScope(scope)) { // not a recognized command
1681: // on this channel or
1682: // through privmsg
1683: logger.warn(trigger + " is not in scope - "
1684: + scope);
1685: return;
1686: }
1687: if (!perm.checkPermission(msgc.getSource())) {
1688: ReplacerEnvironment env = new ReplacerEnvironment(
1689: SiteBot.GLOBAL_ENV);
1690: env.add("ircnick", msgc.getSource().getNick());
1691: say(msgc.getDest(), ReplacerUtils.jprintf(
1692: "ident.denymsg", env, SiteBot.class));
1693: logger
1694: .warn("Not enough permissions for user to execute "
1695: + trigger
1696: + " to "
1697: + msgc.getDest());
1698: return;
1699: }
1700: try {
1701: ArrayList<String> list = (ArrayList) ((Method) objects[0])
1702: .invoke(objects[1], new Object[] {
1703: args, msgc });
1704: if (list == null || list.isEmpty()) {
1705: logger
1706: .debug("There is no direct output to return for command "
1707: + trigger
1708: + " by "
1709: + msgc.getSource()
1710: .getNick());
1711: } else {
1712: for (String output : list) {
1713: say(getSource(msgc), output);
1714: }
1715: }
1716: } catch (Exception e) {
1717: logger.error(
1718: "Error in method invocation on IRCCommand "
1719: + trigger, e);
1720: say(getSource(msgc), e.getMessage());
1721: }
1722: }
1723: }
1724: }
1725: }
1726:
1727: /**
1728: * Returns the source of the message, (channel or nickname)
1729: */
1730: private String getSource(MessageCommand msgc) {
1731: return msgc.isPrivateToUs(_conn.getClientState()) ? msgc
1732: .getSource().getNick() : msgc.getDest();
1733: }
1734:
1735: public class IRCPermission {
1736:
1737: ArrayList<String> _scope = new ArrayList<String>();
1738: String _permissions = null;
1739:
1740: public IRCPermission(String scope, String permissions) {
1741: for (String s : scope.split(",")) {
1742: _scope.add(s);
1743: }
1744: _permissions = permissions;
1745: }
1746:
1747: /**
1748: * Accepts channel names, "public", or "private"
1749: */
1750: public boolean checkScope(String scope) {
1751: if (_scope.contains(scope)) { // matches private or channel names
1752: return true;
1753: }
1754: return scope.startsWith("#") && _scope.contains("public");
1755: }
1756:
1757: public boolean checkPermission(FullNick fn) {
1758: if (_permissions.equals("*")) {
1759: return true;
1760: }
1761: try {
1762: return new Permission(FtpConfig
1763: .makeUsers(new StringTokenizer(_permissions)))
1764: .check(lookupUser(fn));
1765: } catch (NoSuchUserException e) {
1766: logger.warn(e);
1767: return false;
1768: }
1769: }
1770:
1771: public User lookupUser(FullNick fn) throws NoSuchUserException {
1772: String ident = fn.getNick() + "!" + fn.getUser() + "@"
1773: + fn.getHost();
1774: try {
1775: return getGlobalContext().getUserManager()
1776: .getUserByIdent(ident);
1777: } catch (NoSuchUserException e3) {
1778: logger.warn("Could not identify " + ident);
1779: } catch (UserFileException e3) {
1780: logger.warn("Could not identify " + ident);
1781: }
1782: throw new NoSuchUserException("No user found");
1783: }
1784: }
1785:
1786: public static class Ret {
1787: private String _format;
1788: private SectionInterface _section;
1789:
1790: public Ret(String string, SectionInterface sectionObj) {
1791: _format = string;
1792: _section = sectionObj;
1793: }
1794:
1795: public String getFormat() {
1796: return _format;
1797: }
1798:
1799: public SectionInterface getSection() {
1800: return _section;
1801: }
1802: }
1803:
1804: public static class SectionSettings {
1805: private String _channel;
1806: private ReplacerEnvironment _env = new ReplacerEnvironment();
1807:
1808: public SectionSettings(Properties p, int i) {
1809: }
1810:
1811: public SectionSettings(Properties ircCfg, int i, String channel) {
1812: _channel = channel;
1813: }
1814:
1815: public String getChannel() {
1816: return _channel;
1817: }
1818:
1819: public ReplacerEnvironment getEnv() {
1820: return _env;
1821: }
1822: }
1823:
1824: /**
1825: * Returns the blowfish key for the channel specified
1826: */
1827: public synchronized String getBlowfishKey(String channel, User user)
1828: throws ObjectNotFoundException {
1829: ChannelConfig cc = _channelMap.get(channel);
1830: if (cc != null) {
1831: return cc.getBlowfishKey(user);
1832: }
1833: throw new ObjectNotFoundException();
1834: }
1835:
1836: /**
1837: *
1838: * Returns the blowfish key for the primary channel
1839: */
1840: public String getBlowfishKey(User user)
1841: throws ObjectNotFoundException {
1842: return getBlowfishKey(_primaryChannelName, user);
1843: }
1844: }
1845:
1846: class UserComparator implements Comparator {
1847: private String _sort;
1848: private String _type;
1849:
1850: public UserComparator(String type, String sort) {
1851: _type = type;
1852: _sort = sort;
1853: }
1854:
1855: static long getType(String type, UploaderPosition user) {
1856: if (type.equals("bytes")) {
1857: return user.getBytes();
1858: } else if (type.equals("xferspeed")) {
1859: return user.getXferspeed();
1860: } else if (type.equals("xfertime")) {
1861: return user.getXfertime();
1862: }
1863:
1864: return 0;
1865: }
1866:
1867: public int compare(Object o1, Object o2) {
1868: UploaderPosition u1 = (UploaderPosition) o1;
1869: UploaderPosition u2 = (UploaderPosition) o2;
1870:
1871: long this Val = getType(_type, u1);
1872: long anotherVal = getType(_type, u2);
1873:
1874: if (_sort.equals("low")) {
1875: return ((this Val < anotherVal) ? (-1)
1876: : ((this Val == anotherVal) ? 0 : 1));
1877: }
1878:
1879: return ((this Val > anotherVal) ? (-1)
1880: : ((this Val == anotherVal) ? 0 : 1));
1881: }
1882: }
|