/* * SheriffBot.java * * Copyright (C) 2008 AppleGrew * * This program 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 any later version. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.ag.sheriffbot; import java.util.Calendar; import java.util.Vector; import java.text.SimpleDateFormat; import java.io.*; import java.util.HashMap; import org.apache.tools.bzip2.CBZip2OutputStream; import org.elite.jdcbot.framework.BotException; import org.elite.jdcbot.util.TimerThread; import org.elite.jdcbot.framework.DUEntity; import org.elite.jdcbot.framework.GlobalObjects; import org.elite.jdcbot.framework.User; import org.elite.jdcbot.framework.jDCBot; import org.slf4j.Logger; /** * SheriffBot is a derived class from jDCBot apstract class overriding some methods. * <p> * This bot's task is to periodically download all the users' file list. It downloads all the * users' file list and packs them into a single xml file named in the format shown below.<br> * <br> * <code>Hub name-yyyy-MM-dd_HH.mm.ss.bz2</code><br><br> * where <i>yyyy-MM-dd_HH.mm.ss</i> is the date and time of creation of the file.<br> * This file is compressed using bzip2 algorithm. When any new user logs in for the first time * in-between the file list dumping cycle then that user's file list is downloaded and saved * separately and the file is named as<br> * <br> * <code>Hub name_User name-yyyy-MM-dd_HH.mm.ss.bz2</code><br> * <br> * The reason of creating this was that my college laid down the condition that DC++ will only * be allowed on college LAN network if the hub maintains a list of users' file list. So, that * later if required the originator of any illegal file can be traced back. * * @since 0.7.1 * @author AppleGrew * @version 1.2.1 */ public class SheriffBot extends jDCBot { public final String version = "1.2.1"; private static final Logger logger = GlobalObjects.getLogger(SheriffBot.class); private TimerThread tt; private int _updateInterval; private String _hubIP; private String _filelistDir; //Lists authorized users and the hub ips from where they are authorized to send command to SheriffBot. private HashMap<String, String> authorizedUsers; private UserListDownloadThread uth; private Vector<String> users2Index; private Vector<String> spareUsers; //The users' ips whose file lists shouldn't be downloaded. private Vector<String> indexedUsers; private Vector<String> downloadedUsers; private volatile boolean isShuttingDown = false; private volatile boolean listUpdatedAtleastOnce = false; private volatile boolean isUpdateInProgress = false; public SheriffBot() throws IOException { // constructs our bot with 0B share size and 3 slots, which updates // file list every 30mins. this("127.0.0.1", 1411, "127.0.0.1", 9000, 10000, "0", 3, 1000 * 60 * 30, "./", "", false); } public SheriffBot(String hubIP, int hubPort, String sharesizeInBytes, String hubpass, String botIP, int listenPort, int UDP_listenPort, String filelistDir, boolean active) throws IOException { // constructs our bot with 10GB share size and 3 slots, which updates // file list every 6hr. this(hubIP, hubPort, botIP, listenPort, UDP_listenPort, sharesizeInBytes, 3, 6 * 60 * 60 * 1000, filelistDir, hubpass, !active); } public SheriffBot(String hubIP, int hubPort, String botIP, int listenPort, int UDP_listenPort, String sharesizeInBytes, int noOfSlots, int updateInterval, String filelistDir, String hubPass, boolean passive) throws IOException { super("SheriffBot", botIP, listenPort, UDP_listenPort, hubPass, "Monitor Bot", "LAN(T1)1", "", sharesizeInBytes, noOfSlots, 4, passive); _updateInterval = updateInterval; _hubIP = hubIP; filelistDir = filelistDir.trim().replace('\\', '/'); _filelistDir = filelistDir.equals("") ? "" : filelistDir.substring(0, filelistDir.endsWith("/") ? filelistDir.length() - 1 : filelistDir .length()) + "/"; authorizedUsers = new HashMap<String, String>(); authorizedUsers.put("applegrew", "127.0.0.1"); //applegrew when logged-in into hub with 127.0.0.1 is authorized to command SheriffBot. users2Index = new Vector<String>(); downloadedUsers = new Vector<String>(); spareUsers = new Vector<String>(); spareUsers.add("127.0.0.1"); //The file list of the user with the ip 127.0.0.1 will be downloaded, but NOT saved. indexedUsers = new Vector<String>(); try { connect(hubIP, hubPort); } catch (BotException e) { logger.error("Error:" + e, e); terminate(); } catch (Exception e) { logger.error("Exception in SheriffBot.", e); if (e instanceof IOException) terminate(); } } /** * Prints on main chat that we are here and starts flood thread. */ public void onConnect() { tt = new TimerThread(this, _updateInterval, "UpdateFileListThread", 5 * 60 * 1000) {// The first list update will start after 5mins to // allow collection of user infos. protected void onTimer() { ((SheriffBot) _bot).updateFilelists(); ((SheriffBot) _bot).clearIndexedUsersList(); } }; tt.start(); uth = new UserListDownloadThread(); uth.start(); } protected void clearIndexedUsersList() { indexedUsers.removeAllElements(); } /** * Sends user who wants to talk to us a feedback that we're (still) stupid. */ public void onPrivateMessage(String user, String message) { if (authorized(user) && message.startsWith("+raw ")) { try { String cmd = message.substring(message.indexOf(' ') + 1).replaceAll("$", "\\$").replaceAll("|", "|"); logger.debug("Sending command: " + cmd); SendCommand(cmd); } catch (Exception e) {} } else if (message.equals("+version")) { try { SendPrivateMessage(user, "Version: " + version); } catch (Exception e) {} } else if (authorized(user) && message.equals("+quit")) { try { SendPrivateMessage(user, "Disabled."); } catch (Exception e) {} //quit(); } else { String msg = null; // msg = "Go away! I have nothing to talk about. I am on duty right now."; if (message.equals("+quit")) msg = "You are not authorized to command me. Go away!"; try { if (msg != null) SendPrivateMessage(user, msg); } catch (Exception e) { e.printStackTrace(); } } } public synchronized void onBotQuit() { isShuttingDown = true; tt.stopIt(); uth.stopIt(); } public void onDisconnect() { if (!isShuttingDown) logger.debug("Disconnected from hub"); while (!isShuttingDown && !isConnected()) { // Try to reconnect to the hub after waiting for sometime. try { Thread.sleep(6000); // Waiting for 6s connect(_hubIP, _port); } catch (BotException e) { logger.error("Error:" + e, e); } catch (Exception e) { logger.error("Exception in SheriffBot.", e); if (e instanceof IOException) quit(); } } } private void updateFilelists() { if (!isConnected() || isUpdateInProgress) return; if (uth.getState() == Thread.State.RUNNABLE) try { uth.join(); } catch (InterruptedException e) { e.printStackTrace(); } isUpdateInProgress = true; listUpdatedAtleastOnce = true; Calendar cal = Calendar.getInstance(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss"); String filename = _hubname + "-" + sdf.format(cal.getTime()); logger.debug("Filelist Refresh Started."); try { BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream(_filelistDir + filename + ".bz2")); bout.write(("BZ").getBytes("UTF-8")); CBZip2OutputStream bos = new CBZip2OutputStream(bout); bos.write((sdf.format(cal.getTime()) + "\n").getBytes("UTF-8")); bos.write((_hubname + "\n").getBytes("UTF-8")); bos.write(("<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>\n").getBytes("UTF-8")); User[] users = GetAllUsers(); if (users != null) { bos.write(("<DumpListing>\n").getBytes("UTF-8")); for (User user : users) { String reason = null; boolean skipuser = false; if (user.sharesize().equals("0")) { reason = "share is 0 (" + user.sharesize() + ")"; skipuser = true; } else if (user.username().equalsIgnoreCase(_botname)) { reason = "is me"; skipuser = true; } if (skipuser) { logger.info("Skipping user " + user.username() + ". Reason: " + reason); continue; } byte b[] = getFileListBytes(user, DUEntity.NO_SETTING); if (spareUsers.isEmpty() || spareUsers.indexOf(user.getUserIP()) == -1) { if (b != null) { bos.write(b, 0, b.length); b = null; } } b = null; System.gc(); Thread.yield(); } bos.write(("\n</DumpListing>\n").getBytes("UTF-8")); } bos.close(); } catch (FileNotFoundException fne) { logger.error("File not found.", fne); } catch (IOException ioe) { logger.error("IOException", ioe); } finally { isUpdateInProgress = false; logger.debug("Filelist Refresh Finished."); } } private boolean authorized(String user) { String ip = authorizedUsers.get(user.toLowerCase()); if (ip != null && ip.equals(_ip.getHostAddress())) return true; else return false; } public void onJoin(String user) { if (indexedUsers.indexOf(user) == -1) { users2Index.add(user); uth.process(); } } public byte[] getFileListBytes(User user, int settings) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); try { user.downloadFileList(bos, settings); } catch (BotException e1) { logger.error(e1.getMessage(), e1); return null; } int in; logger.info("Waiting for file list to finish downloading of user " + user.username()); while ((in = downloadedUsers.indexOf(user.username())) == -1 && UserExist(user.username()) && !isShuttingDown) { try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } } if (in != -1) { downloadedUsers.remove(in); byte b[] = bos.toByteArray(); /* * Trimming the first line of the file list which is * <?xml version="1.0" encoding="utf-8" standalone="yes"?>\n */ byte b2[] = new byte[b.length - 57]; System.arraycopy(b, 57, b2, 0, b2.length); b = null; bos.reset(); return b2; } else return null; } protected void onDownloadComplete(User user, DUEntity due, boolean sucess, BotException e) { logger.info("File list downloaded of user " + user.username() + ". Download was sucessful = " + sucess); downloadedUsers.add(user.username()); } private class UserListDownloadThread extends Thread { private volatile boolean halt = false; public UserListDownloadThread() { super("UserListDownloadThread"); } public void run() { while (!halt) { while (!users2Index.isEmpty() && !halt && !(!listUpdatedAtleastOnce || !isConnected() || isUpdateInProgress)) { String user = users2Index.get(0); if (indexedUsers.indexOf(user) == -1 && !user.equals(_botname)) { updateIndivisualUserList(user); indexedUsers.add(user); } users2Index.remove(0); } if (halt) return; try { Thread.sleep(1 * 60 * 60 * 1000); } catch (InterruptedException e) {} } } private void updateIndivisualUserList(String user) { logger.debug("Indivisual User FileList update Started."); try { Calendar cal = Calendar.getInstance(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss"); String filename = _hubname + "_" + user + "-" + sdf.format(cal.getTime()); byte b[] = getFileListBytes(getUser(user), DUEntity.NO_SETTING); if (spareUsers.isEmpty() || spareUsers.indexOf(getUser(user).getUserIP()) == -1) { BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream(_filelistDir + filename + ".bz2")); bout.write(("BZ").getBytes("UTF-8")); CBZip2OutputStream bos = new CBZip2OutputStream(bout); bos.write((sdf.format(cal.getTime()) + "\n").getBytes("UTF-8")); bos.write((_hubname + "\n").getBytes("UTF-8")); bos.write(("<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>\n").getBytes("UTF-8")); if (b != null) { bos.write(b, 0, b.length); b = null; } b = null; bos.close(); } System.gc(); Thread.yield(); } catch (Exception e) { e.printStackTrace(); } logger.debug("Indivisual User FileList update Finished."); } public void stopIt() { halt = true; this.interrupt(); } public void process() { this.interrupt(); } } public static void main(String[] args) { System.setErr(System.out); try { if (args.length == 0) new SheriffBot(); else if (args.length == 9) { new SheriffBot(args[0], Integer.parseInt(args[1]), args[7], args[6], args[2], Integer.parseInt(args[3]), Integer .parseInt(args[4]), args[5], Boolean.parseBoolean(args[8])); } else System.out .println("Wrong number of arguments.\n" + "Accepted arguments are:\n" + "none or\n" + "HubIP HubPort TheIPOnWhichTheBotIsRunning ThePortOnWhichTheBotShouldListen " + "ThePortToListenForUDPPackets TheDirectoryWhereFileListsShouldBeSaved BotPass ShareSize IsActive(give true/false)"); } catch (IOException e) { e.printStackTrace(); } } }