File Source: LoggingPlugin.java

         /* 
    P/P   *  Method: com.dmdirc.addons.logging.LoggingPlugin__static_init
          */
     1  /*
     2   * Copyright (c) 2006-2009 Chris Smith, Shane Mc Cormack, Gregory Holmes
     3   *
     4   * Permission is hereby granted, free of charge, to any person obtaining a copy
     5   * of this software and associated documentation files (the "Software"), to deal
     6   * in the Software without restriction, including without limitation the rights
     7   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     8   * copies of the Software, and to permit persons to whom the Software is
     9   * furnished to do so, subject to the following conditions:
    10   *
    11   * The above copyright notice and this permission notice shall be included in
    12   * all copies or substantial portions of the Software.
    13   *
    14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    20   * SOFTWARE.
    21   */
    22  
    23  package com.dmdirc.addons.logging;
    24  
    25  import com.dmdirc.Channel;
    26  import com.dmdirc.Main;
    27  import com.dmdirc.Query;
    28  import com.dmdirc.Server;
    29  import com.dmdirc.actions.ActionManager;
    30  import com.dmdirc.actions.CoreActionType;
    31  import com.dmdirc.actions.interfaces.ActionType;
    32  import com.dmdirc.commandparser.CommandManager;
    33  import com.dmdirc.config.IdentityManager;
    34  import com.dmdirc.config.prefs.PreferencesCategory;
    35  import com.dmdirc.config.prefs.PreferencesManager;
    36  import com.dmdirc.config.prefs.PreferencesSetting;
    37  import com.dmdirc.config.prefs.PreferencesType;
    38  import com.dmdirc.interfaces.ActionListener;
    39  import com.dmdirc.logger.ErrorLevel;
    40  import com.dmdirc.logger.Logger;
    41  import com.dmdirc.parser.irc.ChannelClientInfo;
    42  import com.dmdirc.parser.irc.ChannelInfo;
    43  import com.dmdirc.parser.irc.ClientInfo;
    44  import com.dmdirc.parser.irc.IRCParser;
    45  import com.dmdirc.plugins.Plugin;
    46  import com.dmdirc.ui.interfaces.InputWindow;
    47  import com.dmdirc.ui.interfaces.Window;
    48  import com.dmdirc.ui.messages.Styliser;
    49  
    50  import java.awt.Color;
    51  import java.io.BufferedWriter;
    52  import java.io.File;
    53  import java.io.FileNotFoundException;
    54  import java.io.FileWriter;
    55  import java.io.IOException;
    56  import java.math.BigInteger;
    57  import java.security.MessageDigest;
    58  import java.security.NoSuchAlgorithmException;
    59  import java.text.DateFormat;
    60  import java.text.SimpleDateFormat;
    61  import java.util.Date;
    62  import java.util.Hashtable;
    63  import java.util.Map;
    64  import java.util.Stack;
    65  
    66  import java.util.Timer;
    67  import java.util.TimerTask;
    68  
    69  /**
    70   * Adds logging facility to client.
    71   *
    72   * @author Shane 'Dataforce' McCormack
    73   */
    74  public class LoggingPlugin extends Plugin implements ActionListener {
    75  
    76  	/** The command we registered. */
    77  	private LoggingCommand command;
    78  
    79  	/** Open File */
    80  	protected class OpenFile {
    81  		public long lastUsedTime = System.currentTimeMillis();
    82  		public BufferedWriter writer = null;
        		 /* 
    P/P 		  *  Method: void com.dmdirc.addons.logging.LoggingPlugin$OpenFile(LoggingPlugin, BufferedWriter)
        		  * 
        		  *  Postconditions:
        		  *    init'ed(this.lastUsedTime)
        		  *    this.writer == writer
        		  *    init'ed(this.writer)
        		  */
    83  		public OpenFile(final BufferedWriter writer) {
    84  			this.writer = writer;
    85  		}
    86  	}
    87  
    88  	/** Timer used to close idle files */
    89  	protected Timer idleFileTimer;
    90  
    91  	/** Hashtable of open files. */
    92  	protected final Map<String, OpenFile> openFiles = new Hashtable<String, OpenFile>();
    93  
    94  	/** Date format used for "File Opened At" log. */
    95  	final DateFormat openedAtFormat = new SimpleDateFormat("EEEE MMMM dd, yyyy - HH:mm:ss");
    96  	
    97  	/**
    98  	 * Creates a new instance of the Logging Plugin.
    99  	 */
        	 /* 
    P/P 	  *  Method: void com.dmdirc.addons.logging.LoggingPlugin()
        	  * 
        	  *  Postconditions:
        	  *    this.openFiles == &amp;new Hashtable(LoggingPlugin#1)
        	  *    this.openedAtFormat == &amp;new SimpleDateFormat(LoggingPlugin#2)
        	  *    new Hashtable(LoggingPlugin#1) num objects == 1
        	  *    new SimpleDateFormat(LoggingPlugin#2) num objects == 1
        	  */
   100  	public LoggingPlugin() { super(); }
   101  
   102      /** {@inheritDoc} */
   103      @Override
   104      public void domainUpdated() {
                 /* 
    P/P           *  Method: void domainUpdated()
                  * 
                  *  Presumptions:
                  *    com.dmdirc.config.IdentityManager:getAddonIdentity(...)@105 != null
                  */
   105          IdentityManager.getAddonIdentity().setOption(getDomain(),
   106                  "general.directory", Main.getConfigDir() + "logs"
   107                  + System.getProperty("file.separator"));
   108      }
   109  
   110  	/**
   111  	 * Called when the plugin is loaded.
   112  	 */
   113  	@Override
   114  	public void onLoad() {
        		 /* 
    P/P 		  *  Method: void onLoad()
        		  * 
        		  *  Presumptions:
        		  *    init'ed(com.dmdirc.actions.CoreActionType.CHANNEL_ACTION)
        		  *    init'ed(com.dmdirc.actions.CoreActionType.CHANNEL_CLOSED)
        		  *    init'ed(com.dmdirc.actions.CoreActionType.CHANNEL_GOTTOPIC)
        		  *    init'ed(com.dmdirc.actions.CoreActionType.CHANNEL_JOIN)
        		  *    init'ed(com.dmdirc.actions.CoreActionType.CHANNEL_KICK)
        		  *    ...
        		  * 
        		  *  Postconditions:
        		  *    this.command == &amp;new LoggingCommand(onLoad#2)
        		  *    this.idleFileTimer == &amp;new Timer(onLoad#4)
        		  *    new LoggingCommand(onLoad#2) num objects == 1
        		  *    new Timer(onLoad#4) num objects == 1
        		  * 
        		  *  Test Vectors:
        		  *    java.io.File:exists(...)@116: {0}, {1}
        		  *    java.io.File:isDirectory(...)@117: {1}, {0}
        		  *    java.io.File:mkdirs(...)@121: {1}, {0}
        		  */
   115  		final File dir = new File(IdentityManager.getGlobalConfig().getOption(getDomain(), "general.directory"));
   116  		if (dir.exists()) {
   117  			if (!dir.isDirectory()) {
   118  				Logger.userError(ErrorLevel.LOW, "Unable to create logging dir (file exists instead)");
   119  			}
   120  		} else {
   121  			if (!dir.mkdirs()) {
   122  				Logger.userError(ErrorLevel.LOW, "Unable to create logging dir");
   123  			}
   124  		}
   125  
   126  		command = new LoggingCommand();
   127  		ActionManager.addListener(this,
   128  		                 CoreActionType.CHANNEL_OPENED,
   129  		                 CoreActionType.CHANNEL_CLOSED,
   130  		                 CoreActionType.CHANNEL_MESSAGE,
   131  		                 CoreActionType.CHANNEL_SELF_MESSAGE,
   132  		                 CoreActionType.CHANNEL_ACTION,
   133  		                 CoreActionType.CHANNEL_SELF_ACTION,
   134  		                 CoreActionType.CHANNEL_GOTTOPIC,
   135  		                 CoreActionType.CHANNEL_TOPICCHANGE,
   136  		                 CoreActionType.CHANNEL_JOIN,
   137  		                 CoreActionType.CHANNEL_PART,
   138  		                 CoreActionType.CHANNEL_QUIT,
   139  		                 CoreActionType.CHANNEL_KICK,
   140  		                 CoreActionType.CHANNEL_NICKCHANGE,
   141  		                 CoreActionType.CHANNEL_MODECHANGE,
   142  		                 CoreActionType.QUERY_OPENED,
   143  		                 CoreActionType.QUERY_CLOSED,
   144  		                 CoreActionType.QUERY_MESSAGE,
   145  		                 CoreActionType.QUERY_SELF_MESSAGE,
   146  		                 CoreActionType.QUERY_ACTION,
   147  		                 CoreActionType.QUERY_SELF_ACTION);
   148  		
   149  		// Close idle files every hour.
   150  		idleFileTimer = new Timer("LoggingPlugin Timer");
        		 /* 
    P/P 		  *  Method: void com.dmdirc.addons.logging.LoggingPlugin$1(LoggingPlugin)
        		  */
   151  		idleFileTimer.schedule(new TimerTask(){
   152              /** {@inheritDoc} */
   153              @Override
   154  			public void run() {
        				 /* 
    P/P 				  *  Method: void run()
        				  * 
        				  *  Preconditions:
        				  *    (soft) this.openFiles != null
        				  */
   155  				timerTask();
   156  			}
   157  		}, 3600000);
   158  	}
   159  	
   160  	/**
   161  	 * What to do every hour when the timer fires.
   162  	 */
   163  	protected void timerTask() {
   164  		// Oldest time to allow
        		 /* 
    P/P 		  *  Method: void timerTask()
        		  * 
        		  *  Preconditions:
        		  *    (soft) this.openFiles != null
        		  * 
        		  *  Presumptions:
        		  *    init'ed(com.dmdirc.logger.ErrorLevel.LOW)
        		  *    file.writer@169 != null
        		  *    java.lang.System:currentTimeMillis(...)@165 >= -9_223_372_036_851_295_808
        		  *    java.util.Map:get(...)@169 != null
        		  * 
        		  *  Test Vectors:
        		  *    java.util.Iterator:hasNext(...)@168: {1}, {0}
        		  */
   165  		final long oldestTime = System.currentTimeMillis() - 3480000;
   166  		
   167  		synchronized (openFiles) {
   168  			for (String filename : (new Hashtable<String, OpenFile>(openFiles)).keySet()) {
   169  				OpenFile file = openFiles.get(filename);
   170  				if (file.lastUsedTime < oldestTime) {
   171  					try {
   172  						file.writer.close();
   173  						openFiles.remove(filename);
   174  					} catch (IOException e) {
   175  						Logger.userError(ErrorLevel.LOW, "Unable to close idle file (File: "+filename+")");
   176  					}
   177  				}
   178  			}
   179  		}
   180  	}
   181  
   182  	/**
   183  	 * Called when this plugin is unloaded.
   184  	 */
   185  	@Override
   186  	public void onUnload() {
        		 /* 
    P/P 		  *  Method: void onUnload()
        		  * 
        		  *  Preconditions:
        		  *    init'ed(this.command)
        		  *    this.idleFileTimer != null
        		  *    this.openFiles != null
        		  * 
        		  *  Presumptions:
        		  *    init'ed(com.dmdirc.logger.ErrorLevel.LOW)
        		  *    file.writer@195 != null
        		  *    java.util.Map:get(...)@195 != null
        		  *    java.util.Map:keySet(...)@194 != null
        		  * 
        		  *  Test Vectors:
        		  *    java.util.Iterator:hasNext(...)@194: {0}, {1}
        		  */
   187  		idleFileTimer.cancel();
   188  		idleFileTimer.purge();
   189  		
   190  		CommandManager.unregisterCommand(command);
   191  		ActionManager.removeListener(this);
   192  		
   193  		synchronized (openFiles) {
   194  			for (String filename : openFiles.keySet()) {
   195  				OpenFile file = openFiles.get(filename);
   196  				try {
   197  					file.writer.close();
   198  				} catch (IOException e) {
   199  					Logger.userError(ErrorLevel.LOW, "Unable to close file (File: "+filename+")");
   200  				}
   201  			}
   202  			openFiles.clear();
   203  		}
   204  	}
   205  
   206  	/** {@inheritDoc} */
   207  	@Override
   208  	public void showConfig(final PreferencesManager manager) {
        		 /* 
    P/P 		  *  Method: void showConfig(PreferencesManager)
        		  * 
        		  *  Preconditions:
        		  *    manager != null
        		  * 
        		  *  Presumptions:
        		  *    com.dmdirc.config.prefs.PreferencesManager:getCategory(...)@232 != null
        		  *    init'ed(com.dmdirc.config.prefs.PreferencesType.BOOLEAN)
        		  *    init'ed(com.dmdirc.config.prefs.PreferencesType.COLOUR)
        		  *    init'ed(com.dmdirc.config.prefs.PreferencesType.INTEGER)
        		  *    init'ed(com.dmdirc.config.prefs.PreferencesType.TEXT)
        		  */
   209  		final PreferencesCategory general = new PreferencesCategory("Logging", "General configuration for Logging plugin.");
   210  		final PreferencesCategory backbuffer = new PreferencesCategory("Back Buffer", "Options related to the automatic backbuffer");
   211  		final PreferencesCategory advanced = new PreferencesCategory("Advanced", "Advanced configuration for Logging plugin. You shouldn't need to edit this unless you know what you are doing.");
   212  		
   213  		general.addSetting(new PreferencesSetting(PreferencesType.TEXT, getDomain(), "general.directory", "Directory", "Directory for log files"));
   214  		general.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN, getDomain(), "general.networkfolders", "Separate logs by network", "Should the files be stored in a sub-dir with the networks name?"));
   215  		general.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN, getDomain(), "general.addtime", "Timestamp logs", "Should a timestamp be added to the log files?"));
   216  		general.addSetting(new PreferencesSetting(PreferencesType.TEXT, getDomain(), "general.timestamp", "Timestamp format", "The String to pass to 'SimpleDateFormat' to format the timestamp"));
   217  		general.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN, getDomain(), "general.stripcodes", "Strip Control Codes", "Remove known irc control codes from lines before saving?"));
   218  		general.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN, getDomain(), "general.channelmodeprefix", "Show channel mode prefix", "Show the @,+ etc next to nicknames"));
   219  		
   220  		backbuffer.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN, getDomain(), "backbuffer.autobackbuffer", "Automatically display", "Automatically display the backbuffer when a channel is joined"));
   221  		backbuffer.addSetting(new PreferencesSetting(PreferencesType.COLOUR, getDomain(), "backbuffer.colour", "Colour to use for display", "Colour used when displaying the backbuffer"));
   222  		backbuffer.addSetting(new PreferencesSetting(PreferencesType.INTEGER, getDomain(), "backbuffer.lines", "Number of lines to show", "Number of lines used when displaying backbuffer"));
   223  		backbuffer.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN, getDomain(), "backbuffer.timestamp", "Show Formatter-Timestamp", "Should the line be added to the frame with the timestamp from the formatter aswell as the file contents"));
   224  
   225  		advanced.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN, getDomain(), "advanced.filenamehash", "Add Filename hash", "Add the MD5 hash of the channel/client name to the filename. (This is used to allow channels with similar names (ie a _ not a  -) to be logged separately)"));
   226  
   227  		advanced.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN, getDomain(), "advanced.usedate", "Use Date directories", "Should the log files be in separate directories based on the date?"));
   228  		advanced.addSetting(new PreferencesSetting(PreferencesType.TEXT, getDomain(), "advanced.usedateformat", "Archive format", "The String to pass to 'SimpleDateFormat' to format the directory name(s) for archiving"));
   229  
   230  		general.addSubCategory(backbuffer.setInline());
   231  		general.addSubCategory(advanced.setInline());
   232  		manager.getCategory("Plugins").addSubCategory(general.setInlineAfter());
   233  	}
   234  
   235  	/**
   236  	 * Log a query-related event
   237  	 *
   238  	 * @param type The type of the event to process
   239  	 * @param format Format of messages that are about to be sent. (May be null)
   240  	 * @param arguments The arguments for the event
   241  	 */
   242  	protected void handleQueryEvent(final CoreActionType type, final StringBuffer format, final Object... arguments) {
        		 /* 
    P/P 		  *  Method: void handleQueryEvent(CoreActionType, StringBuffer, Object[])
        		  * 
        		  *  Preconditions:
        		  *    arguments != null
        		  *    arguments[0] != null
        		  *    (soft) arguments.length >= 2
        		  *    (soft) init'ed(arguments[1])
        		  *    (soft) init'ed(com.dmdirc.addons.logging.LoggingPlugin$2__static_init.new int[](LoggingPlugin$2__static_init#1)[...])
        		  *    (soft) this.openFiles != null
        		  *    (soft) this.openedAtFormat != null
        		  *    (soft) type != null
        		  * 
        		  *  Presumptions:
        		  *    com.dmdirc.Query:getServer(...)@249 != null
        		  *    init'ed(com.dmdirc.actions.CoreActionType.QUERY_MESSAGE)
        		  *    init'ed(com.dmdirc.actions.CoreActionType.QUERY_SELF_ACTION)
        		  *    init'ed(com.dmdirc.actions.CoreActionType.QUERY_SELF_MESSAGE)
        		  *    com.dmdirc.actions.CoreActionType:ordinal(...)@268 >= 0
        		  *    ...
        		  * 
        		  *  Test Vectors:
        		  *    com.dmdirc.addons.logging.LoggingPlugin$2__static_init.new int[](LoggingPlugin$2__static_init#1)[...]: {1}, {2}, {3..6}, {-231..0, 7..232-1}
        		  *    com.dmdirc.Query:getServer(...)@244: Inverse{null}, Addr_Set{null}
        		  *    com.dmdirc.Server:getParser(...)@249: Inverse{null}, Addr_Set{null}
        		  *    com.dmdirc.config.ConfigManager:getOptionBool(...)@254: {0}, {1}
        		  *    com.dmdirc.config.ConfigManager:getOptionBool(...)@270: {0}, {1}
        		  *    com.dmdirc.parser.irc.IRCParser:getClientInfo(...)@260: Inverse{null}, Addr_Set{null}
        		  *    java.util.Map:containsKey(...)@280: {0}, {1}
        		  */
   243  		final Query query = (Query)arguments[0];
   244  		if (query.getServer() == null) {
   245  			Logger.appError(ErrorLevel.MEDIUM, "Query object has no server ("+type.toString()+")", new Exception("Query object has no server ("+type.toString()+")"));
   246  			return;
   247  		}
   248  		
   249  		final IRCParser parser = query.getServer().getParser();
   250  		ClientInfo client;
   251  		
   252  		if (parser == null) {
   253  			// Without a parser object, we might not be able to find the file to log this to.
   254  			if (IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "general.networkfolders")) {
   255  				// We *wont* be able to, so rather than logging to an incorrect file we just won't log.
   256  				return;
   257  			}
   258  			client = null;
   259  		} else {
   260  			client = parser.getClientInfo(query.getHost());
   261  			if (client == null) {
   262  				client = new ClientInfo(parser, query.getHost()).setFake(true);
   263  			}
   264  		}
   265  		
   266  		final String filename = getLogFile(client);
   267  		
        		 /* 
    P/P 		  *  Method: com.dmdirc.addons.logging.LoggingPlugin$2__static_init
        		  * 
        		  *  Presumptions:
        		  *    com.dmdirc.actions.CoreActionType.CHANNEL_ACTION != null
        		  *    com.dmdirc.actions.CoreActionType.CHANNEL_CLOSED != null
        		  *    com.dmdirc.actions.CoreActionType.CHANNEL_GOTTOPIC != null
        		  *    com.dmdirc.actions.CoreActionType.CHANNEL_JOIN != null
        		  *    com.dmdirc.actions.CoreActionType.CHANNEL_KICK != null
        		  *    ...
        		  * 
        		  *  Postconditions:
        		  *    new int[](LoggingPlugin$2__static_init#1) num objects == 1
        		  */
   268  		switch (type) {
   269  			case QUERY_OPENED:
   270  				if (IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "backbuffer.autobackbuffer")) {
   271  					showBackBuffer(query.getFrame(), filename);
   272  				}
   273  
   274  				appendLine(filename, "*** Query opened at: %s", openedAtFormat.format(new Date()));
   275  				appendLine(filename, "*** Query with User: %s", query.getHost());
   276  				appendLine(filename, "");
   277  				break;
   278  			case QUERY_CLOSED:
   279  				appendLine(filename, "*** Query closed at: %s", openedAtFormat.format(new Date()));
   280  				if (openFiles.containsKey(filename)) {
   281  					final BufferedWriter file = openFiles.get(filename).writer;
   282  					try {
   283  						file.close();
   284  					} catch (IOException e) {
   285  						Logger.userError(ErrorLevel.LOW, "Unable to close file (Filename: "+filename+")");
   286  					}
   287  					openFiles.remove(filename);
   288  				}
   289  				break;
   290  			case QUERY_MESSAGE:
   291  			case QUERY_SELF_MESSAGE:
   292  			case QUERY_ACTION:
   293  			case QUERY_SELF_ACTION:
   294  				final boolean isME = (type == CoreActionType.QUERY_SELF_MESSAGE || type == CoreActionType.QUERY_SELF_ACTION);
   295  				final String overrideNick = (isME) ? getDisplayName(parser.getMyself()) : "";
   296  				
   297  				if (type == CoreActionType.QUERY_MESSAGE || type == CoreActionType.QUERY_SELF_MESSAGE) {
   298  					appendLine(filename, "<%s> %s", getDisplayName(client, overrideNick), (String)arguments[1]);
   299  				} else {
   300  					appendLine(filename, "* %s %s", getDisplayName(client, overrideNick), (String)arguments[1]);
   301  				}
   302  				break;
   303  		}
   304  	}
   305  	
   306  	/**
   307  	 * Log a channel-related event
   308  	 *
   309  	 * @param type The type of the event to process
   310  	 * @param format Format of messages that are about to be sent. (May be null)
   311  	 * @param arguments The arguments for the event
   312  	 */
   313  	protected void handleChannelEvent(final CoreActionType type, final StringBuffer format, final Object... arguments) {
        		 /* 
    P/P 		  *  Method: void handleChannelEvent(CoreActionType, StringBuffer, Object[])
        		  * 
        		  *  Preconditions:
        		  *    arguments != null
        		  *    arguments[0] != null
        		  *    type != null
        		  *    (soft) arguments.length >= 4
        		  *    (soft) init'ed(arguments[1])
        		  *    (soft) init'ed(arguments[2])
        		  *    (soft) arguments[3] != null
        		  *    (soft) init'ed(com.dmdirc.addons.logging.LoggingPlugin$2__static_init.new int[](LoggingPlugin$2__static_init#1)[...])
        		  *    (soft) this.openFiles != null
        		  *    (soft) this.openedAtFormat != null
        		  * 
        		  *  Presumptions:
        		  *    com.dmdirc.Channel:getChannelInfo(...)@315 != null
        		  *    init'ed(com.dmdirc.actions.CoreActionType.CHANNEL_MESSAGE)
        		  *    init'ed(com.dmdirc.actions.CoreActionType.CHANNEL_SELF_MESSAGE)
        		  *    com.dmdirc.actions.CoreActionType:ordinal(...)@323 >= 0
        		  *    com.dmdirc.actions.CoreActionType:values(...).length >= 1
        		  *    ...
        		  * 
        		  *  Test Vectors:
        		  *    com.dmdirc.addons.logging.LoggingPlugin$2__static_init.new int[](LoggingPlugin$2__static_init#1)[...]: {7}, {8}, {9..12}, {13}, {14}, {15}, {16}, {17}, {18}, {19}, {20}, {-231..6, 21..232-1}
        		  *    com.dmdirc.config.ConfigManager:getOptionBool(...)@325: {0}, {1}
        		  *    java.lang.String:isEmpty(...)@369: {0}, {1}
        		  *    java.lang.String:isEmpty(...)@376: {0}, {1}
        		  *    java.lang.String:isEmpty(...)@386: {0}, {1}
        		  *    java.lang.String:isEmpty(...)@396: {0}, {1}
        		  *    java.util.Map:containsKey(...)@334: {0}, {1}
        		  */
   314  		final Channel chan = ((Channel)arguments[0]);
   315  		final ChannelInfo channel = chan.getChannelInfo();
   316  		final String filename = getLogFile(channel);
   317  		
   318  		final ChannelClientInfo channelClient = (arguments.length > 1 && arguments[1] instanceof ChannelClientInfo) ? (ChannelClientInfo)arguments[1] : null;
   319  		final ClientInfo client = (channelClient != null) ? channelClient.getClient() : null;
   320  
   321  		final String message = (arguments.length > 2 && arguments[2] instanceof String) ? (String)arguments[2] : null;
   322  		
   323  		switch (type) {
   324  			case CHANNEL_OPENED:
   325  				if (IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "backbuffer.autobackbuffer")) {
   326  					showBackBuffer(chan.getFrame(), filename);
   327  				}
   328  
   329  				appendLine(filename, "*** Channel opened at: %s", openedAtFormat.format(new Date()));
   330  				appendLine(filename, "");
   331  				break;
   332  			case CHANNEL_CLOSED:
   333  				appendLine(filename, "*** Channel closed at: %s", openedAtFormat.format(new Date()));
   334  				if (openFiles.containsKey(filename)) {
   335  					final BufferedWriter file = openFiles.get(filename).writer;
   336  					try {
   337  						file.close();
   338  					} catch (IOException e) {
   339  						Logger.userError(ErrorLevel.LOW, "Unable to close file (Filename: "+filename+")");
   340  					}
   341  					openFiles.remove(filename);
   342  				}
   343  				break;
   344  			case CHANNEL_MESSAGE:
   345  			case CHANNEL_SELF_MESSAGE:
   346  			case CHANNEL_ACTION:
   347  			case CHANNEL_SELF_ACTION:
   348  				if (type == CoreActionType.CHANNEL_MESSAGE || type == CoreActionType.CHANNEL_SELF_MESSAGE) {
   349  					appendLine(filename, "<%s> %s", getDisplayName(client), message);
   350  				} else {
   351  					appendLine(filename, "* %s %s", getDisplayName(client), message);
   352  				}
   353  				break;
   354  			case CHANNEL_GOTTOPIC:
   355  				// ActionManager.processEvent(CoreActionType.CHANNEL_GOTTOPIC, this);
   356  				final DateFormat timeFormat = new SimpleDateFormat("HH:mm:ss");
   357  				final DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
   358  				
   359  				appendLine(filename, "*** Topic is: %s", channel.getTopic());
   360  				appendLine(filename, "*** Set at: %s on %s by %s", timeFormat.format(1000 * channel.getTopicTime()), dateFormat.format(1000 * channel.getTopicTime()), channel.getTopicUser());
   361  				break;
   362  			case CHANNEL_TOPICCHANGE:
   363  				appendLine(filename, "*** %s Changed the topic to: %s", getDisplayName(channelClient), message);
   364  				break;
   365  			case CHANNEL_JOIN:
   366  				appendLine(filename, "*** %s (%s) joined the channel", getDisplayName(channelClient), client.toString());
   367  				break;
   368  			case CHANNEL_PART:
   369  				if (message.isEmpty()) {
   370  					appendLine(filename, "*** %s (%s) left the channel", getDisplayName(channelClient), client.toString());
   371  				} else {
   372  					appendLine(filename, "*** %s (%s) left the channel (%s)", getDisplayName(channelClient), client.toString(), message);
   373  				}
   374  				break;
   375  			case CHANNEL_QUIT:
   376  				if (message.isEmpty()) {
   377  					appendLine(filename, "*** %s (%s) Quit IRC", getDisplayName(channelClient), client.toString());
   378  				} else {
   379  					appendLine(filename, "*** %s (%s) Quit IRC (%s)", getDisplayName(channelClient), client.toString(), message);
   380  				}
   381  				break;
   382  			case CHANNEL_KICK:
   383  				final String kickReason = (String)arguments[3];
   384  				final ChannelClientInfo kickedClient = (ChannelClientInfo)arguments[2];
   385  				
   386  				if (kickReason.isEmpty()) {
   387  					appendLine(filename, "*** %s was kicked by %s", getDisplayName(kickedClient), getDisplayName(channelClient));
   388  				} else {
   389  					appendLine(filename, "*** %s was kicked by %s (%s)", getDisplayName(kickedClient), getDisplayName(channelClient), kickReason);
   390  				}
   391  				break;
   392  			case CHANNEL_NICKCHANGE:
   393  				appendLine(filename, "*** %s is now %s", getDisplayName(channelClient, message), getDisplayName(channelClient));
   394  				break;
   395  			case CHANNEL_MODECHANGE:
   396  				if (channelClient.getNickname().isEmpty()) {
   397  					appendLine(filename, "*** Channel modes are: %s", message);
   398  				} else {
   399  					appendLine(filename, "*** %s set modes: %s", getDisplayName(channelClient), message);
   400  				}
   401  				break;
   402  		}
   403  	}
   404  
   405  	/**
   406  	 * Process an event of the specified type.
   407  	 *
   408  	 * @param type The type of the event to process
   409  	 * @param format Format of messages that are about to be sent. (May be null)
   410  	 * @param arguments The arguments for the event
   411  	 */
   412  	@Override
   413  	public void processEvent(final ActionType type, final StringBuffer format, final Object ... arguments) {
        		 /* 
    P/P 		  *  Method: void processEvent(ActionType, StringBuffer, Object[])
        		  * 
        		  *  Preconditions:
        		  *    (soft) arguments != null
        		  *    (soft) arguments.length >= 4
        		  *    (soft) arguments[0] != null
        		  *    (soft) init'ed(arguments[1])
        		  *    (soft) init'ed(arguments[2])
        		  *    (soft) arguments[3] != null
        		  *    (soft) init'ed(com.dmdirc.addons.logging.LoggingPlugin$2__static_init.new int[](LoggingPlugin$2__static_init#1)[...])
        		  *    (soft) this.openFiles != null
        		  *    (soft) this.openedAtFormat != null
        		  * 
        		  *  Presumptions:
        		  *    com.dmdirc.actions.CoreActionType:ordinal(...)@417 >= 0
        		  *    com.dmdirc.actions.CoreActionType:values(...).length >= 1
        		  *    com.dmdirc.actions.CoreActionType:values(...).length - com.dmdirc.actions.CoreActionType:ordinal(...)@417 in range
        		  *    com.dmdirc.actions.CoreActionType:ordinal(...)@417 < com.dmdirc.actions.CoreActionType:values(...).length
        		  * 
        		  *  Test Vectors:
        		  *    com.dmdirc.addons.logging.LoggingPlugin$2__static_init.new int[](LoggingPlugin$2__static_init#1)[...]: {1..6}, {7..20}, {-231..0, 21..232-1}
        		  */
   414  		if (type instanceof CoreActionType) {
   415  			final CoreActionType thisType = (CoreActionType) type;
   416  
   417  			switch (thisType) {
   418  				case CHANNEL_OPENED:
   419  				case CHANNEL_CLOSED:
   420  				case CHANNEL_MESSAGE:
   421  				case CHANNEL_SELF_MESSAGE:
   422  				case CHANNEL_ACTION:
   423  				case CHANNEL_SELF_ACTION:
   424  				case CHANNEL_GOTTOPIC:
   425  				case CHANNEL_TOPICCHANGE:
   426  				case CHANNEL_JOIN:
   427  				case CHANNEL_PART:
   428  				case CHANNEL_QUIT:
   429  				case CHANNEL_KICK:
   430  				case CHANNEL_NICKCHANGE:
   431  				case CHANNEL_MODECHANGE:
   432  					handleChannelEvent(thisType, format, arguments);
   433  					break;
   434  				case QUERY_OPENED:
   435  				case QUERY_CLOSED:
   436  				case QUERY_MESSAGE:
   437  				case QUERY_SELF_MESSAGE:
   438  				case QUERY_ACTION:
   439  				case QUERY_SELF_ACTION:
   440  					handleQueryEvent(thisType, format, arguments);
   441  					break;
   442  				default:
   443  					break;
   444  			}
   445  		}
   446  	}
   447  
   448  	/**
   449  	 * Add a backbuffer to a frame.
   450  	 *
   451  	 * @param frame The frame to add the backbuffer lines to
   452  	 * @param filename File to get backbuffer from
   453  	 */
   454  	protected void showBackBuffer(final Window frame, final String filename) {
        		 /* 
    P/P 		  *  Method: void showBackBuffer(Window, String)
        		  * 
        		  *  Presumptions:
        		  *    com.dmdirc.config.ConfigManager:getOption(...)@456 != null
        		  *    com.dmdirc.config.IdentityManager:getGlobalConfig(...)@455 != null
        		  *    com.dmdirc.config.IdentityManager:getGlobalConfig(...)@456 != null
        		  *    com.dmdirc.config.IdentityManager:getGlobalConfig(...)@457 != null
        		  *    init'ed(com.dmdirc.logger.ErrorLevel.LOW)
        		  * 
        		  *  Test Vectors:
        		  *    frame: Inverse{null}, Addr_Set{null}
        		  *    java.io.File:exists(...)@464: {0}, {1}
        		  *    java.util.Stack:empty(...)@472: {1}, {0}
        		  */
   455  		final int numLines = IdentityManager.getGlobalConfig().getOptionInt(getDomain(), "backbuffer.lines");
   456  		final String colour = IdentityManager.getGlobalConfig().getOption(getDomain(), "backbuffer.colour");
   457  		final boolean showTimestamp = IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "backbuffer.timestamp");
   458  		if (frame == null) {
   459  			Logger.userError(ErrorLevel.LOW, "Given a null frame");
   460  			return;
   461  		}
   462  
   463  		final File testFile = new File(filename);
   464  		if (testFile.exists()) {
   465  			try {
   466  				final ReverseFileReader file = new ReverseFileReader(testFile);
   467  				// Because the file includes a newline char at the end, an empty line
   468  				// is returned by getLines. To counter this, we call getLines(1) and do
   469  				// nothing with the output.
   470  				file.getLines(1);
   471  				final Stack<String> lines = file.getLines(numLines);
   472  				while (!lines.empty()) {
   473  					frame.addLine(getColouredString(colour,lines.pop()), showTimestamp);
   474  				}
   475  				file.close();
   476  				frame.addLine(getColouredString(colour,"--- End of backbuffer\n"), showTimestamp);
   477  			} catch (FileNotFoundException e) {
   478  				Logger.userError(ErrorLevel.LOW, "Unable to show backbuffer (Filename: "+filename+"): " + e.getMessage());
   479  			} catch (IOException e) {
   480  				Logger.userError(ErrorLevel.LOW, "Unable to show backbuffer (Filename: "+filename+"): " + e.getMessage());
   481  			} catch (SecurityException e) {
   482  				Logger.userError(ErrorLevel.LOW, "Unable to show backbuffer (Filename: "+filename+"): " + e.getMessage());
   483  			}
   484  		}
   485  	}
   486  
   487  	/**
   488  	 * Get a coloured String.
   489  	 * If colour is invalid, IRC Colour 14 will be used.
   490  	 *
   491  	 * @param colour The colour the string should be (IRC Colour or 6-digit hex colour)
   492  	 * @param line the line to colour
   493  	 * @return The given line with the appropriate irc codes appended/prepended to colour it.
   494  	 */
   495  	protected static String getColouredString(final String colour, final String line) {
        		 /* 
    P/P 		  *  Method: String getColouredString(String, String)
        		  * 
        		  *  Preconditions:
        		  *    colour != null
        		  * 
        		  *  Postconditions:
        		  *    init'ed(return_value)
        		  * 
        		  *  Test Vectors:
        		  *    java.lang.Integer:parseInt(...)@501: {16..232-1}, {0..15}
        		  *    java.lang.String:length(...)@497: {3..232-1}, {0..2}
        		  *    java.lang.String:length(...)@509: {0..5, 7..232-1}, {6}
        		  */
   496  		String res = null;
   497  		if (colour.length() < 3) {
   498  			int num;
   499  
   500  			try {
   501  				num = Integer.parseInt(colour);
   502  			} catch (NumberFormatException ex) {
   503  				num = -1;
   504  			}
   505  
   506  			if (num >= 0 && num <= 15) {
   507  				res = String.format("%c%02d%s%1$c", Styliser.CODE_COLOUR, num, line);
   508  			}
   509  		} else if (colour.length() == 6) {
   510  			try {
   511  				Color.decode("#" + colour);
   512  				res = String.format("%c%s%s%1$c", Styliser.CODE_HEXCOLOUR, colour, line);
   513  			} catch (NumberFormatException ex) { /* Do Nothing */ }
   514  		}
   515  
   516  		if (res == null) {
   517  			res = String.format("%c%02d%s%1$c", Styliser.CODE_COLOUR, 14, line);
   518  		}
   519  		return res;
   520  	}
   521  
   522  	/**
   523  	 * Add a line to a file.
   524  	 *
   525  	 * @param filename Name of file to write to
   526  	 * @param format Format of line to add. (NewLine will be added Automatically)
   527  	 * @param args Arguments for format
   528  	 * @return true on success, else false.
   529  	 */
   530  	protected boolean appendLine(final String filename, final String format, final Object... args) {
        		 /* 
    P/P 		  *  Method: bool appendLine(String, String, Object[])
        		  * 
        		  *  Preconditions:
        		  *    (soft) this.openFiles != null
        		  * 
        		  *  Postconditions:
        		  *    init'ed(return_value)
        		  */
   531  		return appendLine(filename, String.format(format, args));
   532  	}
   533  
   534  	/**
   535  	 * Add a line to a file.
   536  	 *
   537  	 * @param filename Name of file to write to
   538  	 * @param line Line to add. (NewLine will be added Automatically)
   539  	 * @return true on success, else false.
   540  	 */
   541  	protected boolean appendLine(final String filename, final String line) {
        		 /* 
    P/P 		  *  Method: bool appendLine(String, String)
        		  * 
        		  *  Preconditions:
        		  *    (soft) this.openFiles != null
        		  * 
        		  *  Presumptions:
        		  *    com.dmdirc.config.IdentityManager:getGlobalConfig(...)@544 != null
        		  *    com.dmdirc.config.IdentityManager:getGlobalConfig(...)@546 != null
        		  *    com.dmdirc.config.IdentityManager:getGlobalConfig(...)@561 != null
        		  *    init'ed(com.dmdirc.logger.ErrorLevel.LOW)
        		  *    java.text.DateFormat:format(...)@549 != null
        		  *    ...
        		  * 
        		  *  Postconditions:
        		  *    init'ed(return_value)
        		  * 
        		  *  Test Vectors:
        		  *    com.dmdirc.config.ConfigManager:getOptionBool(...)@544: {0}, {1}
        		  *    com.dmdirc.config.ConfigManager:getOptionBool(...)@561: {0}, {1}
        		  *    java.util.Map:containsKey(...)@569: {0}, {1}
        		  */
   542  		final StringBuffer finalLine = new StringBuffer();
   543  		
   544  		if (IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "general.addtime")) {
   545  			String dateString;
   546  			final String dateFormatString = IdentityManager.getGlobalConfig().getOption(getDomain(), "general.timestamp");
   547  			try {
   548  				final DateFormat dateFormat = new SimpleDateFormat(dateFormatString);
   549  				dateString = dateFormat.format(new Date()).trim();
   550  			} catch (IllegalArgumentException iae) {
   551  				// Default to known good format
   552  				final DateFormat dateFormat = new SimpleDateFormat("[dd/MM/yyyy HH:mm:ss]");
   553  				dateString = dateFormat.format(new Date()).trim();
   554  				
   555  				Logger.userError(ErrorLevel.LOW, "Dateformat String '"+dateFormatString+"' is invalid. For more information: http://java.sun.com/javase/6/docs/api/java/text/SimpleDateFormat.html");
   556  			}
   557  			finalLine.append(dateString);
   558  			finalLine.append(" ");
   559  		}
   560  		
   561  		if (IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "general.stripcodes")) {
   562  			finalLine.append(Styliser.stipControlCodes(line));
   563  		} else {
   564  			finalLine.append(line);
   565  		}
   566  		//System.out.println("[Adding] "+filename+" => "+finalLine);
   567  		BufferedWriter out = null;
   568  		try {
   569  			if (openFiles.containsKey(filename)) {
   570  				OpenFile of = openFiles.get(filename);
   571  				of.lastUsedTime = System.currentTimeMillis();
   572  				out = of.writer;
   573  			} else {
   574  				out = new BufferedWriter(new FileWriter(filename, true));
   575  				openFiles.put(filename, new OpenFile(out));
   576  			}
   577  			out.write(finalLine.toString());
   578  			out.newLine();
   579  			out.flush();
   580  			return true;
   581  		} catch (IOException e) {
   582  			/*
   583  			 * Do Nothing
   584  			 *
   585  			 * Makes no sense to keep adding errors to the logger when we can't
   586  			 * write to the file, as chances are it will happen on every incomming
   587  			 * line.
   588  			 */
   589  		}
   590  		return false;
   591  	}
   592  
   593  	/**
   594  	 * Get the name of the log file for a specific object.
   595  	 *
   596  	 * @param obj Object to get name for
   597  	 * @return the name of the log file to use for this object.
   598  	 */
   599  	protected String getLogFile(final Object obj) {
        		 /* 
    P/P 		  *  Method: String getLogFile(Object)
        		  * 
        		  *  Presumptions:
        		  *    com.dmdirc.config.IdentityManager:getGlobalConfig(...)@604 != null
        		  *    com.dmdirc.config.IdentityManager:getGlobalConfig(...)@630 != null
        		  *    com.dmdirc.config.IdentityManager:getGlobalConfig(...)@631 != null
        		  *    com.dmdirc.config.IdentityManager:getGlobalConfig(...)@643 != null
        		  *    init'ed(com.dmdirc.logger.ErrorLevel.LOW)
        		  *    ...
        		  * 
        		  *  Postconditions:
        		  *    init'ed(java.lang.StringBuilder:toString(...)._tainted)
        		  *    return_value == &amp;java.lang.StringBuilder:toString(...)
        		  * 
        		  *  Test Vectors:
        		  *    obj: Inverse{null}, Addr_Set{null}
        		  *    com.dmdirc.config.ConfigManager:getOptionBool(...)@630: {0}, {1}
        		  *    com.dmdirc.config.ConfigManager:getOptionBool(...)@643: {0}, {1}
        		  *    com.dmdirc.parser.irc.ChannelInfo:getParser(...)@613: Addr_Set{null}, Inverse{null}
        		  *    com.dmdirc.parser.irc.ChannelInfo:instanceof(...)@611: {0}, {1}
        		  *    com.dmdirc.parser.irc.ClientInfo:getParser(...)@620: Addr_Set{null}, Inverse{null}
        		  *    com.dmdirc.parser.irc.ClientInfo:instanceof(...)@618: {0}, {1}
        		  *    java.io.File:exists(...)@638: {1}, {0}
        		  *    java.io.File:mkdirs(...)@638: {1}, {0}
        		  */
   600  		final StringBuffer directory = new StringBuffer();
   601  		final StringBuffer file = new StringBuffer();
   602  		String md5String = "";
   603  	
   604  		directory.append(IdentityManager.getGlobalConfig().getOption(getDomain(), "general.directory"));
   605  		if (directory.charAt(directory.length()-1) != File.separatorChar) {
   606  			directory.append(File.separatorChar);
   607  		}
   608  		
   609  		if (obj == null) {
   610  			file.append("null.log");
   611  		} else if (obj instanceof ChannelInfo) {
   612  			final ChannelInfo channel = (ChannelInfo) obj;
   613  			if (channel.getParser() != null) {
   614  				addNetworkDir(directory, file, channel.getParser().getNetworkName());
   615  			}
   616  			file.append(sanitise(channel.getName().toLowerCase()));
   617  			md5String = channel.getName();
   618  		} else if (obj instanceof ClientInfo) {
   619  			final ClientInfo client = (ClientInfo) obj;
   620  			if (client.getParser() != null) {
   621  				addNetworkDir(directory, file, client.getParser().getNetworkName());
   622  			}
   623  			file.append(sanitise(client.getNickname().toLowerCase()));
   624  			md5String = client.getNickname();
   625  		} else {
   626  			file.append(sanitise(obj.toString().toLowerCase()));
   627  			md5String = obj.toString();
   628  		}
   629  		
   630  		if (IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "advanced.usedate")) {
   631  			final String dateFormat = IdentityManager.getGlobalConfig().getOption(getDomain(), "advanced.usedateformat");
   632  			final String dateDir = (new SimpleDateFormat(dateFormat)).format(new Date());
   633  			directory.append(dateDir);
   634  			if (directory.charAt(directory.length()-1) != File.separatorChar) {
   635  				directory.append(File.separatorChar);
   636  			}
   637  			
   638  			if (!new File(directory.toString()).exists() && !(new File(directory.toString())).mkdirs()) {
   639  				Logger.userError(ErrorLevel.LOW, "Unable to create date dirs");
   640  			}
   641  		}
   642  		
   643  		if (IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "advanced.filenamehash")) {
   644  			file.append('.');
   645  			file.append(md5(md5String));
   646  		}
   647  		file.append(".log");
   648  		
   649  		return directory.toString() + file.toString();
   650  	}
   651  	
   652  	/**
   653  	 * This function adds the networkName to the log file.
   654  	 * It first tries to create a directory for each network, if that fails
   655  	 * it will prepend the networkName to the filename instead.
   656  	 *
   657  	 * @param directory Current directory name
   658  	 * @param file Current file name
   659  	 * @param networkName Name of network
   660  	 */
   661  	protected void addNetworkDir(final StringBuffer directory, final StringBuffer file, final String networkName) {
        		 /* 
    P/P 		  *  Method: void addNetworkDir(StringBuffer, StringBuffer, String)
        		  * 
        		  *  Preconditions:
        		  *    (soft) directory != null
        		  *    (soft) file != null
        		  *    (soft) networkName != null
        		  * 
        		  *  Presumptions:
        		  *    com.dmdirc.config.IdentityManager:getGlobalConfig(...)@662 != null
        		  *    init'ed(com.dmdirc.logger.ErrorLevel.LOW)
        		  * 
        		  *  Postconditions:
        		  *    init'ed(directory._tainted)
        		  * 
        		  *  Test Vectors:
        		  *    com.dmdirc.config.ConfigManager:getOptionBool(...)@662: {1}, {0}
        		  *    java.io.File:exists(...)@672: {0}, {1}
        		  *    java.io.File:exists(...)@676: {1}, {0}
        		  *    java.io.File:isDirectory(...)@672: {1}, {0}
        		  *    java.io.File:mkdirs(...)@676: {1}, {0}
        		  */
   662  		if (!IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "general.networkfolders")) {
   663  			return;
   664  		}
   665  	
   666  		final String network = sanitise(networkName.toLowerCase());
   667  		
   668  		boolean prependNetwork = false;
   669  		
   670  		// Check dir exists
   671  		final File dir = new File(directory.toString()+network+System.getProperty("file.separator"));
   672  		if (dir.exists() && !dir.isDirectory()) {
   673  			Logger.userError(ErrorLevel.LOW, "Unable to create networkfolders dir (file exists instead)");
   674  			// Prepend network name to file instead.
   675  			prependNetwork = true;
   676  		} else if (!dir.exists() && !dir.mkdirs()) {
   677  			Logger.userError(ErrorLevel.LOW, "Unable to create networkfolders dir");
   678  			prependNetwork = true;
   679  		}
   680  		
   681  		if (prependNetwork) {
   682  			file.insert(0, " -- ");
   683  			file.insert(0, network);
   684  		} else {
   685  			directory.append(network);
   686  			directory.append(System.getProperty("file.separator"));
   687  		}
   688  	}
   689  
   690  	/**
   691  	 * Sanitise a string to be used as a filename.
   692  	 *
   693  	 * @param name String to sanitise
   694  	 * @return Sanitised version of name that can be used as a filename.
   695  	 */
   696  	protected static String sanitise(final String name) {
   697  		// Replace illegal chars with
        		 /* 
    P/P 		  *  Method: String sanitise(String)
        		  * 
        		  *  Preconditions:
        		  *    name != null
        		  * 
        		  *  Postconditions:
        		  *    return_value != null
        		  */
   698  		return name.replaceAll("[^\\w\\.\\s\\-\\#\\&\\_]", "_");
   699  	}
   700  
   701  	/**
   702  	 * Get the md5 hash of a string.
   703  	 *
   704  	 * @param string String to hash
   705  	 * @return md5 hash of given string
   706  	 */
   707  	protected static String md5(final String string) {
   708  		try {
        			 /* 
    P/P 			  *  Method: String md5(String)
        			  * 
        			  *  Preconditions:
        			  *    (soft) string != null
        			  * 
        			  *  Presumptions:
        			  *    java.security.MessageDigest:getInstance(...)@709 != null
        			  * 
        			  *  Postconditions:
        			  *    return_value != null
        			  */
   709  			final MessageDigest m = MessageDigest.getInstance("MD5");
   710  			m.update(string.getBytes(), 0, string.length());
   711  			return new BigInteger(1, m.digest()).toString(16);
   712  		} catch (NoSuchAlgorithmException e) {
   713  			return "";
   714  		}
   715  	}
   716  	
   717  	/**
   718  	 * Get name to display for client.
   719  	 *
   720  	 * @param client The client to get the display name for
   721  	 * @return name to display
   722  	 */
   723  	protected String getDisplayName(final ClientInfo client) {
        		 /* 
    P/P 		  *  Method: String getDisplayName(ClientInfo)
        		  * 
        		  *  Postconditions:
        		  *    init'ed(return_value)
        		  */
   724  		return getDisplayName(client, "");
   725  	}
   726  
   727  	/**
   728  	 * Get name to display for client.
   729  	 *
   730  	 * @param client The client to get the display name for
   731  	 * @param overrideNick Nickname to display instead of real nickname
   732  	 * @return name to display
   733  	 */
   734  	protected String getDisplayName(final ClientInfo client, final String overrideNick) {
        		 /* 
    P/P 		  *  Method: String getDisplayName(ClientInfo, String)
        		  * 
        		  *  Preconditions:
        		  *    overrideNick != null
        		  * 
        		  *  Postconditions:
        		  *    init'ed(return_value)
        		  * 
        		  *  Test Vectors:
        		  *    java.lang.String:isEmpty(...)@735: {0}, {1}
        		  */
   735  		if (overrideNick.isEmpty()) {
   736  			return (client == null) ? "Unknown Client" : client.getNickname() ;
   737  		} else {
   738  			return overrideNick;
   739  		}
   740  	}
   741  
   742  	/**
   743  	 * Get name to display for channelClient (Taking into account the channelmodeprefix setting).
   744  	 *
   745  	 * @param channelClient The client to get the display name for
   746  	 * @return name to display
   747  	 */
   748  	protected String getDisplayName(final ChannelClientInfo channelClient) {
        		 /* 
    P/P 		  *  Method: String getDisplayName(ChannelClientInfo)
        		  * 
        		  *  Postconditions:
        		  *    init'ed(com.dmdirc.parser.irc.ChannelClientInfo:toString(...)._tainted)
        		  *    java.lang.StringBuilder:toString(...)._tainted == 0
        		  *    init'ed(return_value)
        		  */
   749  		return getDisplayName(channelClient, "");
   750  	}
   751  
   752  	/**
   753  	 * Get name to display for channelClient (Taking into account the channelmodeprefix setting).
   754  	 *
   755  	 * @param channelClient The client to get the display name for
   756  	 * @param overrideNick Nickname to display instead of real nickname
   757  	 * @return name to display
   758  	 */
   759  	protected String getDisplayName(final ChannelClientInfo channelClient, final String overrideNick) {
        		 /* 
    P/P 		  *  Method: String getDisplayName(ChannelClientInfo, String)
        		  * 
        		  *  Preconditions:
        		  *    overrideNick != null
        		  * 
        		  *  Presumptions:
        		  *    com.dmdirc.config.IdentityManager:getGlobalConfig(...)@760 != null
        		  * 
        		  *  Postconditions:
        		  *    init'ed(com.dmdirc.parser.irc.ChannelClientInfo:toString(...)._tainted)
        		  *    init'ed(java.lang.StringBuilder:toString(...)._tainted)
        		  *    init'ed(return_value)
        		  * 
        		  *  Test Vectors:
        		  *    channelClient: Inverse{null}, Addr_Set{null}
        		  *    com.dmdirc.config.ConfigManager:getOptionBool(...)@760: {1}, {0}
        		  *    java.lang.String:isEmpty(...)@764: {0}, {1}
        		  */
   760  		final boolean addModePrefix = (IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "general.channelmodeprefix"));
   761  		
   762  		if (channelClient == null) {
   763  			return (overrideNick.isEmpty()) ? "Unknown Client" : overrideNick;
   764  		} else if (overrideNick.isEmpty()) {
   765  			return (addModePrefix) ? channelClient.toString() : channelClient.getNickname();
   766  		} else {
   767  			return (addModePrefix) ? channelClient.getImportantModePrefix() + overrideNick : overrideNick;
   768  		}
   769  	}
   770  
   771  	/**
   772  	 * Shows the history window for the specified target, if available.
   773  	 *
   774  	 * @param target The window whose history we're trying to open
   775  	 * @return True if the history is available, false otherwise
   776  	 */
   777  	protected boolean showHistory(final InputWindow target) {
   778  		Object component;
   779  
        		 /* 
    P/P 		  *  Method: bool showHistory(InputWindow)
        		  * 
        		  *  Preconditions:
        		  *    target != null
        		  * 
        		  *  Presumptions:
        		  *    com.dmdirc.Query:getServer(...)@783 != null
        		  *    com.dmdirc.Server:getParser(...)@783 != null
        		  *    com.dmdirc.WritableFrameContainer:getServer(...)@789 != null
        		  *    com.dmdirc.config.IdentityManager:getGlobalConfig(...)@814 != null
        		  *    com.dmdirc.ui.interfaces.InputWindow:getContainer(...)@781 != null
        		  *    ...
        		  * 
        		  *  Postconditions:
        		  *    init'ed(return_value)
        		  * 
        		  *  Test Vectors:
        		  *    com.dmdirc.parser.irc.IRCParser:getClientInfo(...)@784: Inverse{null}, Addr_Set{null}
        		  *    java.io.File:exists(...)@797: {1}, {0}
        		  */
   780  		if (target.getContainer() instanceof Channel) {
   781  			component = ((Channel) target.getContainer()).getChannelInfo();
   782  		} else if (target.getContainer() instanceof Query) {
   783  			final IRCParser parser = ((Query) target.getContainer()).getServer().getParser();
   784  			component = parser.getClientInfo(((Query) target.getContainer()).getHost());
   785  			if (component == null) {
   786  				component = new ClientInfo(parser, ((Query) target.getContainer()).getHost()).setFake(true);
   787  			}
   788  		} else if (target.getContainer() instanceof Server) {
   789  			component = target.getContainer().getServer().getParser();
   790  		} else {
   791  			// Unknown component
   792  			return false;
   793  		}
   794  
   795  		final String log = getLogFile(component);
   796  
   797  		if (!new File(log).exists()) {
   798  			// File doesn't exist
   799  			return false;
   800  		}
   801  
   802  		ReverseFileReader reader;
   803  
   804  		try {
   805  			reader = new ReverseFileReader(log);
   806  		} catch (FileNotFoundException ex) {
   807  			return false;
   808  		} catch (IOException ex) {
   809  			return false;
   810  		} catch (SecurityException ex) {
   811  			return false;
   812  		}
   813  
   814  		new HistoryWindow("History", reader, target,
   815                  IdentityManager.getGlobalConfig().getOptionInt(getDomain(), "history.lines"));
   816  
   817  		return true;
   818  	}
   819  }








SofCheck Inspector Build Version : 2.17854
LoggingPlugin.java 2009-Jun-25 01:54:24
LoggingPlugin.class 2009-Sep-02 17:04:15
LoggingPlugin$1.class 2009-Sep-02 17:04:15
LoggingPlugin$2.class 2009-Sep-02 17:04:15
LoggingPlugin$OpenFile.class 2009-Sep-02 17:04:15