<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[181145] trunk/Tools</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/181145">181145</a></dd>
<dt>Author</dt> <dd>ossy@webkit.org</dd>
<dt>Date</dt> <dd>2015-03-06 01:23:17 -0800 (Fri, 06 Mar 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>setup-git-clone fails due to the failure to download python-irclib-0.4.8
https://bugs.webkit.org/show_bug.cgi?id=141946

Reviewed by Ryosuke Niwa.

Check in ircbot.py and irclib.py to avoid network problems in the future.

* Scripts/webkitpy/common/net/irc/ircbot.py:
* Scripts/webkitpy/thirdparty/__init__.py:
(AutoinstallImportHook.find_module):
(AutoinstallImportHook._install_irc): Deleted.
* Scripts/webkitpy/thirdparty/__init___unittest.py:
(ThirdpartyTest.test_imports):
* Scripts/webkitpy/thirdparty/irc/__init__.py: Added.
* Scripts/webkitpy/thirdparty/irc/ircbot.py: Added.
* Scripts/webkitpy/thirdparty/irc/irclib.py: Added.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkToolsChangeLog">trunk/Tools/ChangeLog</a></li>
<li><a href="#trunkToolsScriptswebkitpycommonnetircircbotpy">trunk/Tools/Scripts/webkitpy/common/net/irc/ircbot.py</a></li>
<li><a href="#trunkToolsScriptswebkitpythirdparty__init__py">trunk/Tools/Scripts/webkitpy/thirdparty/__init__.py</a></li>
<li><a href="#trunkToolsScriptswebkitpythirdparty__init___unittestpy">trunk/Tools/Scripts/webkitpy/thirdparty/__init___unittest.py</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li>trunk/Tools/Scripts/webkitpy/thirdparty/irc/</li>
<li><a href="#trunkToolsScriptswebkitpythirdpartyirc__init__py">trunk/Tools/Scripts/webkitpy/thirdparty/irc/__init__.py</a></li>
<li><a href="#trunkToolsScriptswebkitpythirdpartyircircbotpy">trunk/Tools/Scripts/webkitpy/thirdparty/irc/ircbot.py</a></li>
<li><a href="#trunkToolsScriptswebkitpythirdpartyircirclibpy">trunk/Tools/Scripts/webkitpy/thirdparty/irc/irclib.py</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkToolsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Tools/ChangeLog (181144 => 181145)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/ChangeLog        2015-03-06 09:20:52 UTC (rev 181144)
+++ trunk/Tools/ChangeLog        2015-03-06 09:23:17 UTC (rev 181145)
</span><span class="lines">@@ -1,3 +1,22 @@
</span><ins>+2015-03-06  Csaba Osztrogonác  &lt;ossy@webkit.org&gt;
+
+        setup-git-clone fails due to the failure to download python-irclib-0.4.8
+        https://bugs.webkit.org/show_bug.cgi?id=141946
+
+        Reviewed by Ryosuke Niwa.
+
+        Check in ircbot.py and irclib.py to avoid network problems in the future.
+
+        * Scripts/webkitpy/common/net/irc/ircbot.py:
+        * Scripts/webkitpy/thirdparty/__init__.py:
+        (AutoinstallImportHook.find_module):
+        (AutoinstallImportHook._install_irc): Deleted.
+        * Scripts/webkitpy/thirdparty/__init___unittest.py:
+        (ThirdpartyTest.test_imports):
+        * Scripts/webkitpy/thirdparty/irc/__init__.py: Added.
+        * Scripts/webkitpy/thirdparty/irc/ircbot.py: Added.
+        * Scripts/webkitpy/thirdparty/irc/irclib.py: Added.
+
</ins><span class="cx"> 2015-03-06  David Kilzer  &lt;ddkilzer@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Dashboard: Perf bot status is missing for Apple {Mavericks,Yosemite} Release builds
</span></span></pre></div>
<a id="trunkToolsScriptswebkitpycommonnetircircbotpy"></a>
<div class="modfile"><h4>Modified: trunk/Tools/Scripts/webkitpy/common/net/irc/ircbot.py (181144 => 181145)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/Scripts/webkitpy/common/net/irc/ircbot.py        2015-03-06 09:20:52 UTC (rev 181144)
+++ trunk/Tools/Scripts/webkitpy/common/net/irc/ircbot.py        2015-03-06 09:23:17 UTC (rev 181145)
</span><span class="lines">@@ -29,8 +29,8 @@
</span><span class="cx"> from webkitpy.common.config import irc as config_irc
</span><span class="cx"> 
</span><span class="cx"> from webkitpy.common.thread.messagepump import MessagePump, MessagePumpDelegate
</span><del>-from webkitpy.thirdparty.autoinstalled.irc import ircbot
-from webkitpy.thirdparty.autoinstalled.irc import irclib
</del><ins>+from webkitpy.thirdparty.irc import ircbot
+from webkitpy.thirdparty.irc import irclib
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> class IRCBotDelegate(object):
</span></span></pre></div>
<a id="trunkToolsScriptswebkitpythirdparty__init__py"></a>
<div class="modfile"><h4>Modified: trunk/Tools/Scripts/webkitpy/thirdparty/__init__.py (181144 => 181145)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/Scripts/webkitpy/thirdparty/__init__.py        2015-03-06 09:20:52 UTC (rev 181144)
+++ trunk/Tools/Scripts/webkitpy/thirdparty/__init__.py        2015-03-06 09:23:17 UTC (rev 181145)
</span><span class="lines">@@ -87,8 +87,6 @@
</span><span class="cx">             self._install_coverage()
</span><span class="cx">         elif '.eliza' in fullname:
</span><span class="cx">             self._install_eliza()
</span><del>-        elif '.irc' in fullname:
-            self._install_irc()
</del><span class="cx">         elif '.buildbot' in fullname:
</span><span class="cx">             self._install_buildbot()
</span><span class="cx">         elif '.keyring' in fullname:
</span><span class="lines">@@ -150,17 +148,6 @@
</span><span class="cx">     def _install_eliza(self):
</span><span class="cx">         self._install(url=&quot;http://www.adambarth.com/webkit/eliza&quot;, target_name=&quot;eliza.py&quot;)
</span><span class="cx"> 
</span><del>-    def _install_irc(self):
-        # Since irclib and ircbot are two top-level packages, we need to import
-        # them separately.  We group them into an irc package for better
-        # organization purposes.
-        irc_dir = self._fs.join(_AUTOINSTALLED_DIR, &quot;irc&quot;)
-        installer = AutoInstaller(target_dir=irc_dir)
-        installer.install(url=&quot;http://downloads.sourceforge.net/project/python-irclib/python-irclib/0.4.8/python-irclib-0.4.8.tar.gz&quot;,
-                                                url_subpath=&quot;python-irclib-0.4.8/irclib.py&quot;)
-        installer.install(url=&quot;http://downloads.sourceforge.net/project/python-irclib/python-irclib/0.4.8/python-irclib-0.4.8.tar.gz&quot;,
-                          url_subpath=&quot;python-irclib-0.4.8/ircbot.py&quot;)
-
</del><span class="cx">     def _install(self, url, url_subpath=None, target_name=None):
</span><span class="cx">         installer = AutoInstaller(target_dir=_AUTOINSTALLED_DIR)
</span><span class="cx">         installer.install(url=url, url_subpath=url_subpath, target_name=target_name)
</span></span></pre></div>
<a id="trunkToolsScriptswebkitpythirdparty__init___unittestpy"></a>
<div class="modfile"><h4>Modified: trunk/Tools/Scripts/webkitpy/thirdparty/__init___unittest.py (181144 => 181145)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/Scripts/webkitpy/thirdparty/__init___unittest.py        2015-03-06 09:20:52 UTC (rev 181144)
+++ trunk/Tools/Scripts/webkitpy/thirdparty/__init___unittest.py        2015-03-06 09:23:17 UTC (rev 181145)
</span><span class="lines">@@ -61,8 +61,6 @@
</span><span class="cx">         import webkitpy.thirdparty.autoinstalled.buildbot
</span><span class="cx">         import webkitpy.thirdparty.autoinstalled.coverage
</span><span class="cx">         import webkitpy.thirdparty.autoinstalled.eliza
</span><del>-        import webkitpy.thirdparty.autoinstalled.irc.ircbot
-        import webkitpy.thirdparty.autoinstalled.irc.irclib
</del><span class="cx">         import webkitpy.thirdparty.autoinstalled.mechanize
</span><span class="cx">         import webkitpy.thirdparty.autoinstalled.pylint
</span><span class="cx">         import webkitpy.thirdparty.autoinstalled.pep8
</span></span></pre></div>
<a id="trunkToolsScriptswebkitpythirdpartyirc__init__py"></a>
<div class="addfile"><h4>Added: trunk/Tools/Scripts/webkitpy/thirdparty/irc/__init__.py (0 => 181145)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/Scripts/webkitpy/thirdparty/irc/__init__.py                                (rev 0)
+++ trunk/Tools/Scripts/webkitpy/thirdparty/irc/__init__.py        2015-03-06 09:23:17 UTC (rev 181145)
</span><span class="lines">@@ -0,0 +1 @@
</span><ins>+# Required for Python to search this directory for module files
</ins></span></pre></div>
<a id="trunkToolsScriptswebkitpythirdpartyircircbotpy"></a>
<div class="addfile"><h4>Added: trunk/Tools/Scripts/webkitpy/thirdparty/irc/ircbot.py (0 => 181145)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/Scripts/webkitpy/thirdparty/irc/ircbot.py                                (rev 0)
+++ trunk/Tools/Scripts/webkitpy/thirdparty/irc/ircbot.py        2015-03-06 09:23:17 UTC (rev 181145)
</span><span class="lines">@@ -0,0 +1,438 @@
</span><ins>+# Copyright (C) 1999--2002  Joel Rosdahl
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+#
+# Joel Rosdahl &lt;joel@rosdahl.net&gt;
+#
+# $Id: ircbot.py,v 1.23 2008/09/11 07:38:30 keltus Exp $
+
+&quot;&quot;&quot;ircbot -- Simple IRC bot library.
+
+This module contains a single-server IRC bot class that can be used to
+write simpler bots.
+&quot;&quot;&quot;
+
+import sys
+from UserDict import UserDict
+
+from irclib import SimpleIRCClient
+from irclib import nm_to_n, irc_lower, all_events
+from irclib import parse_channel_modes, is_channel
+from irclib import ServerConnectionError
+
+class SingleServerIRCBot(SimpleIRCClient):
+    &quot;&quot;&quot;A single-server IRC bot class.
+
+    The bot tries to reconnect if it is disconnected.
+
+    The bot keeps track of the channels it has joined, the other
+    clients that are present in the channels and which of those that
+    have operator or voice modes.  The &quot;database&quot; is kept in the
+    self.channels attribute, which is an IRCDict of Channels.
+    &quot;&quot;&quot;
+    def __init__(self, server_list, nickname, realname, reconnection_interval=60):
+        &quot;&quot;&quot;Constructor for SingleServerIRCBot objects.
+
+        Arguments:
+
+            server_list -- A list of tuples (server, port) that
+                           defines which servers the bot should try to
+                           connect to.
+
+            nickname -- The bot's nickname.
+
+            realname -- The bot's realname.
+
+            reconnection_interval -- How long the bot should wait
+                                     before trying to reconnect.
+
+            dcc_connections -- A list of initiated/accepted DCC
+            connections.
+        &quot;&quot;&quot;
+
+        SimpleIRCClient.__init__(self)
+        self.channels = IRCDict()
+        self.server_list = server_list
+        if not reconnection_interval or reconnection_interval &lt; 0:
+            reconnection_interval = 2**31
+        self.reconnection_interval = reconnection_interval
+
+        self._nickname = nickname
+        self._realname = realname
+        for i in [&quot;disconnect&quot;, &quot;join&quot;, &quot;kick&quot;, &quot;mode&quot;,
+                  &quot;namreply&quot;, &quot;nick&quot;, &quot;part&quot;, &quot;quit&quot;]:
+            self.connection.add_global_handler(i,
+                                               getattr(self, &quot;_on_&quot; + i),
+                                               -10)
+    def _connected_checker(self):
+        &quot;&quot;&quot;[Internal]&quot;&quot;&quot;
+        if not self.connection.is_connected():
+            self.connection.execute_delayed(self.reconnection_interval,
+                                            self._connected_checker)
+            self.jump_server()
+
+    def _connect(self):
+        &quot;&quot;&quot;[Internal]&quot;&quot;&quot;
+        password = None
+        if len(self.server_list[0]) &gt; 2:
+            password = self.server_list[0][2]
+        try:
+            self.connect(self.server_list[0][0],
+                         self.server_list[0][1],
+                         self._nickname,
+                         password,
+                         ircname=self._realname)
+        except ServerConnectionError:
+            pass
+
+    def _on_disconnect(self, c, e):
+        &quot;&quot;&quot;[Internal]&quot;&quot;&quot;
+        self.channels = IRCDict()
+        self.connection.execute_delayed(self.reconnection_interval,
+                                        self._connected_checker)
+
+    def _on_join(self, c, e):
+        &quot;&quot;&quot;[Internal]&quot;&quot;&quot;
+        ch = e.target()
+        nick = nm_to_n(e.source())
+        if nick == c.get_nickname():
+            self.channels[ch] = Channel()
+        self.channels[ch].add_user(nick)
+
+    def _on_kick(self, c, e):
+        &quot;&quot;&quot;[Internal]&quot;&quot;&quot;
+        nick = e.arguments()[0]
+        channel = e.target()
+
+        if nick == c.get_nickname():
+            del self.channels[channel]
+        else:
+            self.channels[channel].remove_user(nick)
+
+    def _on_mode(self, c, e):
+        &quot;&quot;&quot;[Internal]&quot;&quot;&quot;
+        modes = parse_channel_modes(&quot; &quot;.join(e.arguments()))
+        t = e.target()
+        if is_channel(t):
+            ch = self.channels[t]
+            for mode in modes:
+                if mode[0] == &quot;+&quot;:
+                    f = ch.set_mode
+                else:
+                    f = ch.clear_mode
+                f(mode[1], mode[2])
+        else:
+            # Mode on self... XXX
+            pass
+
+    def _on_namreply(self, c, e):
+        &quot;&quot;&quot;[Internal]&quot;&quot;&quot;
+
+        # e.arguments()[0] == &quot;@&quot; for secret channels,
+        #                     &quot;*&quot; for private channels,
+        #                     &quot;=&quot; for others (public channels)
+        # e.arguments()[1] == channel
+        # e.arguments()[2] == nick list
+
+        ch = e.arguments()[1]
+        for nick in e.arguments()[2].split():
+            if nick[0] == &quot;@&quot;:
+                nick = nick[1:]
+                self.channels[ch].set_mode(&quot;o&quot;, nick)
+            elif nick[0] == &quot;+&quot;:
+                nick = nick[1:]
+                self.channels[ch].set_mode(&quot;v&quot;, nick)
+            self.channels[ch].add_user(nick)
+
+    def _on_nick(self, c, e):
+        &quot;&quot;&quot;[Internal]&quot;&quot;&quot;
+        before = nm_to_n(e.source())
+        after = e.target()
+        for ch in self.channels.values():
+            if ch.has_user(before):
+                ch.change_nick(before, after)
+
+    def _on_part(self, c, e):
+        &quot;&quot;&quot;[Internal]&quot;&quot;&quot;
+        nick = nm_to_n(e.source())
+        channel = e.target()
+
+        if nick == c.get_nickname():
+            del self.channels[channel]
+        else:
+            self.channels[channel].remove_user(nick)
+
+    def _on_quit(self, c, e):
+        &quot;&quot;&quot;[Internal]&quot;&quot;&quot;
+        nick = nm_to_n(e.source())
+        for ch in self.channels.values():
+            if ch.has_user(nick):
+                ch.remove_user(nick)
+
+    def die(self, msg=&quot;Bye, cruel world!&quot;):
+        &quot;&quot;&quot;Let the bot die.
+
+        Arguments:
+
+            msg -- Quit message.
+        &quot;&quot;&quot;
+
+        self.connection.disconnect(msg)
+        sys.exit(0)
+
+    def disconnect(self, msg=&quot;I'll be back!&quot;):
+        &quot;&quot;&quot;Disconnect the bot.
+
+        The bot will try to reconnect after a while.
+
+        Arguments:
+
+            msg -- Quit message.
+        &quot;&quot;&quot;
+        self.connection.disconnect(msg)
+
+    def get_version(self):
+        &quot;&quot;&quot;Returns the bot version.
+
+        Used when answering a CTCP VERSION request.
+        &quot;&quot;&quot;
+        return &quot;ircbot.py by Joel Rosdahl &lt;joel@rosdahl.net&gt;&quot;
+
+    def jump_server(self, msg=&quot;Changing servers&quot;):
+        &quot;&quot;&quot;Connect to a new server, possibly disconnecting from the current.
+
+        The bot will skip to next server in the server_list each time
+        jump_server is called.
+        &quot;&quot;&quot;
+        if self.connection.is_connected():
+            self.connection.disconnect(msg)
+
+        self.server_list.append(self.server_list.pop(0))
+        self._connect()
+
+    def on_ctcp(self, c, e):
+        &quot;&quot;&quot;Default handler for ctcp events.
+
+        Replies to VERSION and PING requests and relays DCC requests
+        to the on_dccchat method.
+        &quot;&quot;&quot;
+        if e.arguments()[0] == &quot;VERSION&quot;:
+            c.ctcp_reply(nm_to_n(e.source()),
+                         &quot;VERSION &quot; + self.get_version())
+        elif e.arguments()[0] == &quot;PING&quot;:
+            if len(e.arguments()) &gt; 1:
+                c.ctcp_reply(nm_to_n(e.source()),
+                             &quot;PING &quot; + e.arguments()[1])
+        elif e.arguments()[0] == &quot;DCC&quot; and e.arguments()[1].split(&quot; &quot;, 1)[0] == &quot;CHAT&quot;:
+            self.on_dccchat(c, e)
+
+    def on_dccchat(self, c, e):
+        pass
+
+    def start(self):
+        &quot;&quot;&quot;Start the bot.&quot;&quot;&quot;
+        self._connect()
+        SimpleIRCClient.start(self)
+
+
+class IRCDict:
+    &quot;&quot;&quot;A dictionary suitable for storing IRC-related things.
+
+    Dictionary keys a and b are considered equal if and only if
+    irc_lower(a) == irc_lower(b)
+
+    Otherwise, it should behave exactly as a normal dictionary.
+    &quot;&quot;&quot;
+
+    def __init__(self, dict=None):
+        self.data = {}
+        self.canon_keys = {}  # Canonical keys
+        if dict is not None:
+            self.update(dict)
+    def __repr__(self):
+        return repr(self.data)
+    def __cmp__(self, dict):
+        if isinstance(dict, IRCDict):
+            return cmp(self.data, dict.data)
+        else:
+            return cmp(self.data, dict)
+    def __len__(self):
+        return len(self.data)
+    def __getitem__(self, key):
+        return self.data[self.canon_keys[irc_lower(key)]]
+    def __setitem__(self, key, item):
+        if key in self:
+            del self[key]
+        self.data[key] = item
+        self.canon_keys[irc_lower(key)] = key
+    def __delitem__(self, key):
+        ck = irc_lower(key)
+        del self.data[self.canon_keys[ck]]
+        del self.canon_keys[ck]
+    def __iter__(self):
+        return iter(self.data)
+    def __contains__(self, key):
+        return self.has_key(key)
+    def clear(self):
+        self.data.clear()
+        self.canon_keys.clear()
+    def copy(self):
+        if self.__class__ is UserDict:
+            return UserDict(self.data)
+        import copy
+        return copy.copy(self)
+    def keys(self):
+        return self.data.keys()
+    def items(self):
+        return self.data.items()
+    def values(self):
+        return self.data.values()
+    def has_key(self, key):
+        return irc_lower(key) in self.canon_keys
+    def update(self, dict):
+        for k, v in dict.items():
+            self.data[k] = v
+    def get(self, key, failobj=None):
+        return self.data.get(key, failobj)
+
+
+class Channel:
+    &quot;&quot;&quot;A class for keeping information about an IRC channel.
+
+    This class can be improved a lot.
+    &quot;&quot;&quot;
+
+    def __init__(self):
+        self.userdict = IRCDict()
+        self.operdict = IRCDict()
+        self.voiceddict = IRCDict()
+        self.modes = {}
+
+    def users(self):
+        &quot;&quot;&quot;Returns an unsorted list of the channel's users.&quot;&quot;&quot;
+        return self.userdict.keys()
+
+    def opers(self):
+        &quot;&quot;&quot;Returns an unsorted list of the channel's operators.&quot;&quot;&quot;
+        return self.operdict.keys()
+
+    def voiced(self):
+        &quot;&quot;&quot;Returns an unsorted list of the persons that have voice
+        mode set in the channel.&quot;&quot;&quot;
+        return self.voiceddict.keys()
+
+    def has_user(self, nick):
+        &quot;&quot;&quot;Check whether the channel has a user.&quot;&quot;&quot;
+        return nick in self.userdict
+
+    def is_oper(self, nick):
+        &quot;&quot;&quot;Check whether a user has operator status in the channel.&quot;&quot;&quot;
+        return nick in self.operdict
+
+    def is_voiced(self, nick):
+        &quot;&quot;&quot;Check whether a user has voice mode set in the channel.&quot;&quot;&quot;
+        return nick in self.voiceddict
+
+    def add_user(self, nick):
+        self.userdict[nick] = 1
+
+    def remove_user(self, nick):
+        for d in self.userdict, self.operdict, self.voiceddict:
+            if nick in d:
+                del d[nick]
+
+    def change_nick(self, before, after):
+        self.userdict[after] = 1
+        del self.userdict[before]
+        if before in self.operdict:
+            self.operdict[after] = 1
+            del self.operdict[before]
+        if before in self.voiceddict:
+            self.voiceddict[after] = 1
+            del self.voiceddict[before]
+
+    def set_mode(self, mode, value=None):
+        &quot;&quot;&quot;Set mode on the channel.
+
+        Arguments:
+
+            mode -- The mode (a single-character string).
+
+            value -- Value
+        &quot;&quot;&quot;
+        if mode == &quot;o&quot;:
+            self.operdict[value] = 1
+        elif mode == &quot;v&quot;:
+            self.voiceddict[value] = 1
+        else:
+            self.modes[mode] = value
+
+    def clear_mode(self, mode, value=None):
+        &quot;&quot;&quot;Clear mode on the channel.
+
+        Arguments:
+
+            mode -- The mode (a single-character string).
+
+            value -- Value
+        &quot;&quot;&quot;
+        try:
+            if mode == &quot;o&quot;:
+                del self.operdict[value]
+            elif mode == &quot;v&quot;:
+                del self.voiceddict[value]
+            else:
+                del self.modes[mode]
+        except KeyError:
+            pass
+
+    def has_mode(self, mode):
+        return mode in self.modes
+
+    def is_moderated(self):
+        return self.has_mode(&quot;m&quot;)
+
+    def is_secret(self):
+        return self.has_mode(&quot;s&quot;)
+
+    def is_protected(self):
+        return self.has_mode(&quot;p&quot;)
+
+    def has_topic_lock(self):
+        return self.has_mode(&quot;t&quot;)
+
+    def is_invite_only(self):
+        return self.has_mode(&quot;i&quot;)
+
+    def has_allow_external_messages(self):
+        return self.has_mode(&quot;n&quot;)
+
+    def has_limit(self):
+        return self.has_mode(&quot;l&quot;)
+
+    def limit(self):
+        if self.has_limit():
+            return self.modes[l]
+        else:
+            return None
+
+    def has_key(self):
+        return self.has_mode(&quot;k&quot;)
+
+    def key(self):
+        if self.has_key():
+            return self.modes[&quot;k&quot;]
+        else:
+            return None
</ins></span></pre></div>
<a id="trunkToolsScriptswebkitpythirdpartyircirclibpy"></a>
<div class="addfile"><h4>Added: trunk/Tools/Scripts/webkitpy/thirdparty/irc/irclib.py (0 => 181145)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/Scripts/webkitpy/thirdparty/irc/irclib.py                                (rev 0)
+++ trunk/Tools/Scripts/webkitpy/thirdparty/irc/irclib.py        2015-03-06 09:23:17 UTC (rev 181145)
</span><span class="lines">@@ -0,0 +1,1560 @@
</span><ins>+# Copyright (C) 1999--2002  Joel Rosdahl
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+#
+# keltus &lt;keltus@users.sourceforge.net&gt;
+#
+# $Id: irclib.py,v 1.47 2008/09/25 22:00:59 keltus Exp $
+
+&quot;&quot;&quot;irclib -- Internet Relay Chat (IRC) protocol client library.
+
+This library is intended to encapsulate the IRC protocol at a quite
+low level.  It provides an event-driven IRC client framework.  It has
+a fairly thorough support for the basic IRC protocol, CTCP, DCC chat,
+but DCC file transfers is not yet supported.
+
+In order to understand how to make an IRC client, I'm afraid you more
+or less must understand the IRC specifications.  They are available
+here: [IRC specifications].
+
+The main features of the IRC client framework are:
+
+  * Abstraction of the IRC protocol.
+  * Handles multiple simultaneous IRC server connections.
+  * Handles server PONGing transparently.
+  * Messages to the IRC server are done by calling methods on an IRC
+    connection object.
+  * Messages from an IRC server triggers events, which can be caught
+    by event handlers.
+  * Reading from and writing to IRC server sockets are normally done
+    by an internal select() loop, but the select()ing may be done by
+    an external main loop.
+  * Functions can be registered to execute at specified times by the
+    event-loop.
+  * Decodes CTCP tagging correctly (hopefully); I haven't seen any
+    other IRC client implementation that handles the CTCP
+    specification subtilties.
+  * A kind of simple, single-server, object-oriented IRC client class
+    that dispatches events to instance methods is included.
+
+Current limitations:
+
+  * The IRC protocol shines through the abstraction a bit too much.
+  * Data is not written asynchronously to the server, i.e. the write()
+    may block if the TCP buffers are stuffed.
+  * There are no support for DCC file transfers.
+  * The author haven't even read RFC 2810, 2811, 2812 and 2813.
+  * Like most projects, documentation is lacking...
+
+.. [IRC specifications] http://www.irchelp.org/irchelp/rfc/
+&quot;&quot;&quot;
+
+import bisect
+import re
+import select
+import socket
+import string
+import sys
+import time
+import types
+
+VERSION = 0, 4, 8
+DEBUG = 0
+
+# TODO
+# ----
+# (maybe) thread safety
+# (maybe) color parser convenience functions
+# documentation (including all event types)
+# (maybe) add awareness of different types of ircds
+# send data asynchronously to the server (and DCC connections)
+# (maybe) automatically close unused, passive DCC connections after a while
+
+# NOTES
+# -----
+# connection.quit() only sends QUIT to the server.
+# ERROR from the server triggers the error event and the disconnect event.
+# dropping of the connection triggers the disconnect event.
+
+class IRCError(Exception):
+    &quot;&quot;&quot;Represents an IRC exception.&quot;&quot;&quot;
+    pass
+
+
+class IRC:
+    &quot;&quot;&quot;Class that handles one or several IRC server connections.
+
+    When an IRC object has been instantiated, it can be used to create
+    Connection objects that represent the IRC connections.  The
+    responsibility of the IRC object is to provide an event-driven
+    framework for the connections and to keep the connections alive.
+    It runs a select loop to poll each connection's TCP socket and
+    hands over the sockets with incoming data for processing by the
+    corresponding connection.
+
+    The methods of most interest for an IRC client writer are server,
+    add_global_handler, remove_global_handler, execute_at,
+    execute_delayed, process_once and process_forever.
+
+    Here is an example:
+
+        irc = irclib.IRC()
+        server = irc.server()
+        server.connect(\&quot;irc.some.where\&quot;, 6667, \&quot;my_nickname\&quot;)
+        server.privmsg(\&quot;a_nickname\&quot;, \&quot;Hi there!\&quot;)
+        irc.process_forever()
+
+    This will connect to the IRC server irc.some.where on port 6667
+    using the nickname my_nickname and send the message \&quot;Hi there!\&quot;
+    to the nickname a_nickname.
+    &quot;&quot;&quot;
+
+    def __init__(self, fn_to_add_socket=None,
+                 fn_to_remove_socket=None,
+                 fn_to_add_timeout=None):
+        &quot;&quot;&quot;Constructor for IRC objects.
+
+        Optional arguments are fn_to_add_socket, fn_to_remove_socket
+        and fn_to_add_timeout.  The first two specify functions that
+        will be called with a socket object as argument when the IRC
+        object wants to be notified (or stop being notified) of data
+        coming on a new socket.  When new data arrives, the method
+        process_data should be called.  Similarly, fn_to_add_timeout
+        is called with a number of seconds (a floating point number)
+        as first argument when the IRC object wants to receive a
+        notification (by calling the process_timeout method).  So, if
+        e.g. the argument is 42.17, the object wants the
+        process_timeout method to be called after 42 seconds and 170
+        milliseconds.
+
+        The three arguments mainly exist to be able to use an external
+        main loop (for example Tkinter's or PyGTK's main app loop)
+        instead of calling the process_forever method.
+
+        An alternative is to just call ServerConnection.process_once()
+        once in a while.
+        &quot;&quot;&quot;
+
+        if fn_to_add_socket and fn_to_remove_socket:
+            self.fn_to_add_socket = fn_to_add_socket
+            self.fn_to_remove_socket = fn_to_remove_socket
+        else:
+            self.fn_to_add_socket = None
+            self.fn_to_remove_socket = None
+
+        self.fn_to_add_timeout = fn_to_add_timeout
+        self.connections = []
+        self.handlers = {}
+        self.delayed_commands = [] # list of tuples in the format (time, function, arguments)
+
+        self.add_global_handler(&quot;ping&quot;, _ping_ponger, -42)
+
+    def server(self):
+        &quot;&quot;&quot;Creates and returns a ServerConnection object.&quot;&quot;&quot;
+
+        c = ServerConnection(self)
+        self.connections.append(c)
+        return c
+
+    def process_data(self, sockets):
+        &quot;&quot;&quot;Called when there is more data to read on connection sockets.
+
+        Arguments:
+
+            sockets -- A list of socket objects.
+
+        See documentation for IRC.__init__.
+        &quot;&quot;&quot;
+        for s in sockets:
+            for c in self.connections:
+                if s == c._get_socket():
+                    c.process_data()
+
+    def process_timeout(self):
+        &quot;&quot;&quot;Called when a timeout notification is due.
+
+        See documentation for IRC.__init__.
+        &quot;&quot;&quot;
+        t = time.time()
+        while self.delayed_commands:
+            if t &gt;= self.delayed_commands[0][0]:
+                self.delayed_commands[0][1](*self.delayed_commands[0][2])
+                del self.delayed_commands[0]
+            else:
+                break
+
+    def process_once(self, timeout=0):
+        &quot;&quot;&quot;Process data from connections once.
+
+        Arguments:
+
+            timeout -- How long the select() call should wait if no
+                       data is available.
+
+        This method should be called periodically to check and process
+        incoming data, if there are any.  If that seems boring, look
+        at the process_forever method.
+        &quot;&quot;&quot;
+        sockets = map(lambda x: x._get_socket(), self.connections)
+        sockets = filter(lambda x: x != None, sockets)
+        if sockets:
+            (i, o, e) = select.select(sockets, [], [], timeout)
+            self.process_data(i)
+        else:
+            time.sleep(timeout)
+        self.process_timeout()
+
+    def process_forever(self, timeout=0.2):
+        &quot;&quot;&quot;Run an infinite loop, processing data from connections.
+
+        This method repeatedly calls process_once.
+
+        Arguments:
+
+            timeout -- Parameter to pass to process_once.
+        &quot;&quot;&quot;
+        while 1:
+            self.process_once(timeout)
+
+    def disconnect_all(self, message=&quot;&quot;):
+        &quot;&quot;&quot;Disconnects all connections.&quot;&quot;&quot;
+        for c in self.connections:
+            c.disconnect(message)
+
+    def add_global_handler(self, event, handler, priority=0):
+        &quot;&quot;&quot;Adds a global handler function for a specific event type.
+
+        Arguments:
+
+            event -- Event type (a string).  Check the values of the
+            numeric_events dictionary in irclib.py for possible event
+            types.
+
+            handler -- Callback function.
+
+            priority -- A number (the lower number, the higher priority).
+
+        The handler function is called whenever the specified event is
+        triggered in any of the connections.  See documentation for
+        the Event class.
+
+        The handler functions are called in priority order (lowest
+        number is highest priority).  If a handler function returns
+        \&quot;NO MORE\&quot;, no more handlers will be called.
+        &quot;&quot;&quot;
+        if not event in self.handlers:
+            self.handlers[event] = []
+        bisect.insort(self.handlers[event], ((priority, handler)))
+
+    def remove_global_handler(self, event, handler):
+        &quot;&quot;&quot;Removes a global handler function.
+
+        Arguments:
+
+            event -- Event type (a string).
+
+            handler -- Callback function.
+
+        Returns 1 on success, otherwise 0.
+        &quot;&quot;&quot;
+        if not event in self.handlers:
+            return 0
+        for h in self.handlers[event]:
+            if handler == h[1]:
+                self.handlers[event].remove(h)
+        return 1
+
+    def execute_at(self, at, function, arguments=()):
+        &quot;&quot;&quot;Execute a function at a specified time.
+
+        Arguments:
+
+            at -- Execute at this time (standard \&quot;time_t\&quot; time).
+
+            function -- Function to call.
+
+            arguments -- Arguments to give the function.
+        &quot;&quot;&quot;
+        self.execute_delayed(at-time.time(), function, arguments)
+
+    def execute_delayed(self, delay, function, arguments=()):
+        &quot;&quot;&quot;Execute a function after a specified time.
+
+        Arguments:
+
+            delay -- How many seconds to wait.
+
+            function -- Function to call.
+
+            arguments -- Arguments to give the function.
+        &quot;&quot;&quot;
+        bisect.insort(self.delayed_commands, (delay+time.time(), function, arguments))
+        if self.fn_to_add_timeout:
+            self.fn_to_add_timeout(delay)
+
+    def dcc(self, dcctype=&quot;chat&quot;):
+        &quot;&quot;&quot;Creates and returns a DCCConnection object.
+
+        Arguments:
+
+            dcctype -- &quot;chat&quot; for DCC CHAT connections or &quot;raw&quot; for
+                       DCC SEND (or other DCC types). If &quot;chat&quot;,
+                       incoming data will be split in newline-separated
+                       chunks. If &quot;raw&quot;, incoming data is not touched.
+        &quot;&quot;&quot;
+        c = DCCConnection(self, dcctype)
+        self.connections.append(c)
+        return c
+
+    def _handle_event(self, connection, event):
+        &quot;&quot;&quot;[Internal]&quot;&quot;&quot;
+        h = self.handlers
+        for handler in h.get(&quot;all_events&quot;, []) + h.get(event.eventtype(), []):
+            if handler[1](connection, event) == &quot;NO MORE&quot;:
+                return
+
+    def _remove_connection(self, connection):
+        &quot;&quot;&quot;[Internal]&quot;&quot;&quot;
+        self.connections.remove(connection)
+        if self.fn_to_remove_socket:
+            self.fn_to_remove_socket(connection._get_socket())
+
+_rfc_1459_command_regexp = re.compile(&quot;^(:(?P&lt;prefix&gt;[^ ]+) +)?(?P&lt;command&gt;[^ ]+)( *(?P&lt;argument&gt; .+))?&quot;)
+
+class Connection:
+    &quot;&quot;&quot;Base class for IRC connections.
+
+    Must be overridden.
+    &quot;&quot;&quot;
+    def __init__(self, irclibobj):
+        self.irclibobj = irclibobj
+
+    def _get_socket():
+        raise IRCError, &quot;Not overridden&quot;
+
+    ##############################
+    ### Convenience wrappers.
+
+    def execute_at(self, at, function, arguments=()):
+        self.irclibobj.execute_at(at, function, arguments)
+
+    def execute_delayed(self, delay, function, arguments=()):
+        self.irclibobj.execute_delayed(delay, function, arguments)
+
+
+class ServerConnectionError(IRCError):
+    pass
+
+class ServerNotConnectedError(ServerConnectionError):
+    pass
+
+
+# Huh!?  Crrrrazy EFNet doesn't follow the RFC: their ircd seems to
+# use \n as message separator!  :P
+_linesep_regexp = re.compile(&quot;\r?\n&quot;)
+
+class ServerConnection(Connection):
+    &quot;&quot;&quot;This class represents an IRC server connection.
+
+    ServerConnection objects are instantiated by calling the server
+    method on an IRC object.
+    &quot;&quot;&quot;
+
+    def __init__(self, irclibobj):
+        Connection.__init__(self, irclibobj)
+        self.connected = 0  # Not connected yet.
+        self.socket = None
+        self.ssl = None
+
+    def connect(self, server, port, nickname, password=None, username=None,
+                ircname=None, localaddress=&quot;&quot;, localport=0, ssl=False, ipv6=False):
+        &quot;&quot;&quot;Connect/reconnect to a server.
+
+        Arguments:
+
+            server -- Server name.
+
+            port -- Port number.
+
+            nickname -- The nickname.
+
+            password -- Password (if any).
+
+            username -- The username.
+
+            ircname -- The IRC name (&quot;realname&quot;).
+
+            localaddress -- Bind the connection to a specific local IP address.
+
+            localport -- Bind the connection to a specific local port.
+
+            ssl -- Enable support for ssl.
+
+            ipv6 -- Enable support for ipv6.
+
+        This function can be called to reconnect a closed connection.
+
+        Returns the ServerConnection object.
+        &quot;&quot;&quot;
+        if self.connected:
+            self.disconnect(&quot;Changing servers&quot;)
+
+        self.previous_buffer = &quot;&quot;
+        self.handlers = {}
+        self.real_server_name = &quot;&quot;
+        self.real_nickname = nickname
+        self.server = server
+        self.port = port
+        self.nickname = nickname
+        self.username = username or nickname
+        self.ircname = ircname or nickname
+        self.password = password
+        self.localaddress = localaddress
+        self.localport = localport
+        self.localhost = socket.gethostname()
+        if ipv6:
+            self.socket = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
+        else:
+            self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        try:
+            self.socket.bind((self.localaddress, self.localport))
+            self.socket.connect((self.server, self.port))
+            if ssl:
+                self.ssl = socket.ssl(self.socket)
+        except socket.error, x:
+            self.socket.close()
+            self.socket = None
+            raise ServerConnectionError, &quot;Couldn't connect to socket: %s&quot; % x
+        self.connected = 1
+        if self.irclibobj.fn_to_add_socket:
+            self.irclibobj.fn_to_add_socket(self.socket)
+
+        # Log on...
+        if self.password:
+            self.pass_(self.password)
+        self.nick(self.nickname)
+        self.user(self.username, self.ircname)
+        return self
+
+    def close(self):
+        &quot;&quot;&quot;Close the connection.
+
+        This method closes the connection permanently; after it has
+        been called, the object is unusable.
+        &quot;&quot;&quot;
+
+        self.disconnect(&quot;Closing object&quot;)
+        self.irclibobj._remove_connection(self)
+
+    def _get_socket(self):
+        &quot;&quot;&quot;[Internal]&quot;&quot;&quot;
+        return self.socket
+
+    def get_server_name(self):
+        &quot;&quot;&quot;Get the (real) server name.
+
+        This method returns the (real) server name, or, more
+        specifically, what the server calls itself.
+        &quot;&quot;&quot;
+
+        if self.real_server_name:
+            return self.real_server_name
+        else:
+            return &quot;&quot;
+
+    def get_nickname(self):
+        &quot;&quot;&quot;Get the (real) nick name.
+
+        This method returns the (real) nickname.  The library keeps
+        track of nick changes, so it might not be the nick name that
+        was passed to the connect() method.  &quot;&quot;&quot;
+
+        return self.real_nickname
+
+    def process_data(self):
+        &quot;&quot;&quot;[Internal]&quot;&quot;&quot;
+
+        try:
+            if self.ssl:
+                new_data = self.ssl.read(2**14)
+            else:
+                new_data = self.socket.recv(2**14)
+        except socket.error, x:
+            # The server hung up.
+            self.disconnect(&quot;Connection reset by peer&quot;)
+            return
+        if not new_data:
+            # Read nothing: connection must be down.
+            self.disconnect(&quot;Connection reset by peer&quot;)
+            return
+
+        lines = _linesep_regexp.split(self.previous_buffer + new_data)
+
+        # Save the last, unfinished line.
+        self.previous_buffer = lines.pop()
+
+        for line in lines:
+            if DEBUG:
+                print &quot;FROM SERVER:&quot;, line
+
+            if not line:
+                continue
+
+            prefix = None
+            command = None
+            arguments = None
+            self._handle_event(Event(&quot;all_raw_messages&quot;,
+                                     self.get_server_name(),
+                                     None,
+                                     [line]))
+
+            m = _rfc_1459_command_regexp.match(line)
+            if m.group(&quot;prefix&quot;):
+                prefix = m.group(&quot;prefix&quot;)
+                if not self.real_server_name:
+                    self.real_server_name = prefix
+
+            if m.group(&quot;command&quot;):
+                command = m.group(&quot;command&quot;).lower()
+
+            if m.group(&quot;argument&quot;):
+                a = m.group(&quot;argument&quot;).split(&quot; :&quot;, 1)
+                arguments = a[0].split()
+                if len(a) == 2:
+                    arguments.append(a[1])
+
+            # Translate numerics into more readable strings.
+            if command in numeric_events:
+                command = numeric_events[command]
+
+            if command == &quot;nick&quot;:
+                if nm_to_n(prefix) == self.real_nickname:
+                    self.real_nickname = arguments[0]
+            elif command == &quot;welcome&quot;:
+                # Record the nickname in case the client changed nick
+                # in a nicknameinuse callback.
+                self.real_nickname = arguments[0]
+
+            if command in [&quot;privmsg&quot;, &quot;notice&quot;]:
+                target, message = arguments[0], arguments[1]
+                messages = _ctcp_dequote(message)
+
+                if command == &quot;privmsg&quot;:
+                    if is_channel(target):
+                        command = &quot;pubmsg&quot;
+                else:
+                    if is_channel(target):
+                        command = &quot;pubnotice&quot;
+                    else:
+                        command = &quot;privnotice&quot;
+
+                for m in messages:
+                    if type(m) is types.TupleType:
+                        if command in [&quot;privmsg&quot;, &quot;pubmsg&quot;]:
+                            command = &quot;ctcp&quot;
+                        else:
+                            command = &quot;ctcpreply&quot;
+
+                        m = list(m)
+                        if DEBUG:
+                            print &quot;command: %s, source: %s, target: %s, arguments: %s&quot; % (
+                                command, prefix, target, m)
+                        self._handle_event(Event(command, prefix, target, m))
+                        if command == &quot;ctcp&quot; and m[0] == &quot;ACTION&quot;:
+                            self._handle_event(Event(&quot;action&quot;, prefix, target, m[1:]))
+                    else:
+                        if DEBUG:
+                            print &quot;command: %s, source: %s, target: %s, arguments: %s&quot; % (
+                                command, prefix, target, [m])
+                        self._handle_event(Event(command, prefix, target, [m]))
+            else:
+                target = None
+
+                if command == &quot;quit&quot;:
+                    arguments = [arguments[0]]
+                elif command == &quot;ping&quot;:
+                    target = arguments[0]
+                else:
+                    target = arguments[0]
+                    arguments = arguments[1:]
+
+                if command == &quot;mode&quot;:
+                    if not is_channel(target):
+                        command = &quot;umode&quot;
+
+                if DEBUG:
+                    print &quot;command: %s, source: %s, target: %s, arguments: %s&quot; % (
+                        command, prefix, target, arguments)
+                self._handle_event(Event(command, prefix, target, arguments))
+
+    def _handle_event(self, event):
+        &quot;&quot;&quot;[Internal]&quot;&quot;&quot;
+        self.irclibobj._handle_event(self, event)
+        if event.eventtype() in self.handlers:
+            for fn in self.handlers[event.eventtype()]:
+                fn(self, event)
+
+    def is_connected(self):
+        &quot;&quot;&quot;Return connection status.
+
+        Returns true if connected, otherwise false.
+        &quot;&quot;&quot;
+        return self.connected
+
+    def add_global_handler(self, *args):
+        &quot;&quot;&quot;Add global handler.
+
+        See documentation for IRC.add_global_handler.
+        &quot;&quot;&quot;
+        self.irclibobj.add_global_handler(*args)
+
+    def remove_global_handler(self, *args):
+        &quot;&quot;&quot;Remove global handler.
+
+        See documentation for IRC.remove_global_handler.
+        &quot;&quot;&quot;
+        self.irclibobj.remove_global_handler(*args)
+
+    def action(self, target, action):
+        &quot;&quot;&quot;Send a CTCP ACTION command.&quot;&quot;&quot;
+        self.ctcp(&quot;ACTION&quot;, target, action)
+
+    def admin(self, server=&quot;&quot;):
+        &quot;&quot;&quot;Send an ADMIN command.&quot;&quot;&quot;
+        self.send_raw(&quot; &quot;.join([&quot;ADMIN&quot;, server]).strip())
+
+    def ctcp(self, ctcptype, target, parameter=&quot;&quot;):
+        &quot;&quot;&quot;Send a CTCP command.&quot;&quot;&quot;
+        ctcptype = ctcptype.upper()
+        self.privmsg(target, &quot;\001%s%s\001&quot; % (ctcptype, parameter and (&quot; &quot; + parameter) or &quot;&quot;))
+
+    def ctcp_reply(self, target, parameter):
+        &quot;&quot;&quot;Send a CTCP REPLY command.&quot;&quot;&quot;
+        self.notice(target, &quot;\001%s\001&quot; % parameter)
+
+    def disconnect(self, message=&quot;&quot;):
+        &quot;&quot;&quot;Hang up the connection.
+
+        Arguments:
+
+            message -- Quit message.
+        &quot;&quot;&quot;
+        if not self.connected:
+            return
+
+        self.connected = 0
+
+        self.quit(message)
+
+        try:
+            self.socket.close()
+        except socket.error, x:
+            pass
+        self.socket = None
+        self._handle_event(Event(&quot;disconnect&quot;, self.server, &quot;&quot;, [message]))
+
+    def globops(self, text):
+        &quot;&quot;&quot;Send a GLOBOPS command.&quot;&quot;&quot;
+        self.send_raw(&quot;GLOBOPS :&quot; + text)
+
+    def info(self, server=&quot;&quot;):
+        &quot;&quot;&quot;Send an INFO command.&quot;&quot;&quot;
+        self.send_raw(&quot; &quot;.join([&quot;INFO&quot;, server]).strip())
+
+    def invite(self, nick, channel):
+        &quot;&quot;&quot;Send an INVITE command.&quot;&quot;&quot;
+        self.send_raw(&quot; &quot;.join([&quot;INVITE&quot;, nick, channel]).strip())
+
+    def ison(self, nicks):
+        &quot;&quot;&quot;Send an ISON command.
+
+        Arguments:
+
+            nicks -- List of nicks.
+        &quot;&quot;&quot;
+        self.send_raw(&quot;ISON &quot; + &quot; &quot;.join(nicks))
+
+    def join(self, channel, key=&quot;&quot;):
+        &quot;&quot;&quot;Send a JOIN command.&quot;&quot;&quot;
+        self.send_raw(&quot;JOIN %s%s&quot; % (channel, (key and (&quot; &quot; + key))))
+
+    def kick(self, channel, nick, comment=&quot;&quot;):
+        &quot;&quot;&quot;Send a KICK command.&quot;&quot;&quot;
+        self.send_raw(&quot;KICK %s %s%s&quot; % (channel, nick, (comment and (&quot; :&quot; + comment))))
+
+    def links(self, remote_server=&quot;&quot;, server_mask=&quot;&quot;):
+        &quot;&quot;&quot;Send a LINKS command.&quot;&quot;&quot;
+        command = &quot;LINKS&quot;
+        if remote_server:
+            command = command + &quot; &quot; + remote_server
+        if server_mask:
+            command = command + &quot; &quot; + server_mask
+        self.send_raw(command)
+
+    def list(self, channels=None, server=&quot;&quot;):
+        &quot;&quot;&quot;Send a LIST command.&quot;&quot;&quot;
+        command = &quot;LIST&quot;
+        if channels:
+            command = command + &quot; &quot; + &quot;,&quot;.join(channels)
+        if server:
+            command = command + &quot; &quot; + server
+        self.send_raw(command)
+
+    def lusers(self, server=&quot;&quot;):
+        &quot;&quot;&quot;Send a LUSERS command.&quot;&quot;&quot;
+        self.send_raw(&quot;LUSERS&quot; + (server and (&quot; &quot; + server)))
+
+    def mode(self, target, command):
+        &quot;&quot;&quot;Send a MODE command.&quot;&quot;&quot;
+        self.send_raw(&quot;MODE %s %s&quot; % (target, command))
+
+    def motd(self, server=&quot;&quot;):
+        &quot;&quot;&quot;Send an MOTD command.&quot;&quot;&quot;
+        self.send_raw(&quot;MOTD&quot; + (server and (&quot; &quot; + server)))
+
+    def names(self, channels=None):
+        &quot;&quot;&quot;Send a NAMES command.&quot;&quot;&quot;
+        self.send_raw(&quot;NAMES&quot; + (channels and (&quot; &quot; + &quot;,&quot;.join(channels)) or &quot;&quot;))
+
+    def nick(self, newnick):
+        &quot;&quot;&quot;Send a NICK command.&quot;&quot;&quot;
+        self.send_raw(&quot;NICK &quot; + newnick)
+
+    def notice(self, target, text):
+        &quot;&quot;&quot;Send a NOTICE command.&quot;&quot;&quot;
+        # Should limit len(text) here!
+        self.send_raw(&quot;NOTICE %s :%s&quot; % (target, text))
+
+    def oper(self, nick, password):
+        &quot;&quot;&quot;Send an OPER command.&quot;&quot;&quot;
+        self.send_raw(&quot;OPER %s %s&quot; % (nick, password))
+
+    def part(self, channels, message=&quot;&quot;):
+        &quot;&quot;&quot;Send a PART command.&quot;&quot;&quot;
+        if type(channels) == types.StringType:
+            self.send_raw(&quot;PART &quot; + channels + (message and (&quot; &quot; + message)))
+        else:
+            self.send_raw(&quot;PART &quot; + &quot;,&quot;.join(channels) + (message and (&quot; &quot; + message)))
+
+    def pass_(self, password):
+        &quot;&quot;&quot;Send a PASS command.&quot;&quot;&quot;
+        self.send_raw(&quot;PASS &quot; + password)
+
+    def ping(self, target, target2=&quot;&quot;):
+        &quot;&quot;&quot;Send a PING command.&quot;&quot;&quot;
+        self.send_raw(&quot;PING %s%s&quot; % (target, target2 and (&quot; &quot; + target2)))
+
+    def pong(self, target, target2=&quot;&quot;):
+        &quot;&quot;&quot;Send a PONG command.&quot;&quot;&quot;
+        self.send_raw(&quot;PONG %s%s&quot; % (target, target2 and (&quot; &quot; + target2)))
+
+    def privmsg(self, target, text):
+        &quot;&quot;&quot;Send a PRIVMSG command.&quot;&quot;&quot;
+        # Should limit len(text) here!
+        self.send_raw(&quot;PRIVMSG %s :%s&quot; % (target, text))
+
+    def privmsg_many(self, targets, text):
+        &quot;&quot;&quot;Send a PRIVMSG command to multiple targets.&quot;&quot;&quot;
+        # Should limit len(text) here!
+        self.send_raw(&quot;PRIVMSG %s :%s&quot; % (&quot;,&quot;.join(targets), text))
+
+    def quit(self, message=&quot;&quot;):
+        &quot;&quot;&quot;Send a QUIT command.&quot;&quot;&quot;
+        # Note that many IRC servers don't use your QUIT message
+        # unless you've been connected for at least 5 minutes!
+        self.send_raw(&quot;QUIT&quot; + (message and (&quot; :&quot; + message)))
+
+    def send_raw(self, string):
+        &quot;&quot;&quot;Send raw string to the server.
+
+        The string will be padded with appropriate CR LF.
+        &quot;&quot;&quot;
+        if self.socket is None:
+            raise ServerNotConnectedError, &quot;Not connected.&quot;
+        try:
+            if self.ssl:
+                self.ssl.write(string + &quot;\r\n&quot;)
+            else:
+                self.socket.send(string + &quot;\r\n&quot;)
+            if DEBUG:
+                print &quot;TO SERVER:&quot;, string
+        except socket.error, x:
+            # Ouch!
+            self.disconnect(&quot;Connection reset by peer.&quot;)
+
+    def squit(self, server, comment=&quot;&quot;):
+        &quot;&quot;&quot;Send an SQUIT command.&quot;&quot;&quot;
+        self.send_raw(&quot;SQUIT %s%s&quot; % (server, comment and (&quot; :&quot; + comment)))
+
+    def stats(self, statstype, server=&quot;&quot;):
+        &quot;&quot;&quot;Send a STATS command.&quot;&quot;&quot;
+        self.send_raw(&quot;STATS %s%s&quot; % (statstype, server and (&quot; &quot; + server)))
+
+    def time(self, server=&quot;&quot;):
+        &quot;&quot;&quot;Send a TIME command.&quot;&quot;&quot;
+        self.send_raw(&quot;TIME&quot; + (server and (&quot; &quot; + server)))
+
+    def topic(self, channel, new_topic=None):
+        &quot;&quot;&quot;Send a TOPIC command.&quot;&quot;&quot;
+        if new_topic is None:
+            self.send_raw(&quot;TOPIC &quot; + channel)
+        else:
+            self.send_raw(&quot;TOPIC %s :%s&quot; % (channel, new_topic))
+
+    def trace(self, target=&quot;&quot;):
+        &quot;&quot;&quot;Send a TRACE command.&quot;&quot;&quot;
+        self.send_raw(&quot;TRACE&quot; + (target and (&quot; &quot; + target)))
+
+    def user(self, username, realname):
+        &quot;&quot;&quot;Send a USER command.&quot;&quot;&quot;
+        self.send_raw(&quot;USER %s 0 * :%s&quot; % (username, realname))
+
+    def userhost(self, nicks):
+        &quot;&quot;&quot;Send a USERHOST command.&quot;&quot;&quot;
+        self.send_raw(&quot;USERHOST &quot; + &quot;,&quot;.join(nicks))
+
+    def users(self, server=&quot;&quot;):
+        &quot;&quot;&quot;Send a USERS command.&quot;&quot;&quot;
+        self.send_raw(&quot;USERS&quot; + (server and (&quot; &quot; + server)))
+
+    def version(self, server=&quot;&quot;):
+        &quot;&quot;&quot;Send a VERSION command.&quot;&quot;&quot;
+        self.send_raw(&quot;VERSION&quot; + (server and (&quot; &quot; + server)))
+
+    def wallops(self, text):
+        &quot;&quot;&quot;Send a WALLOPS command.&quot;&quot;&quot;
+        self.send_raw(&quot;WALLOPS :&quot; + text)
+
+    def who(self, target=&quot;&quot;, op=&quot;&quot;):
+        &quot;&quot;&quot;Send a WHO command.&quot;&quot;&quot;
+        self.send_raw(&quot;WHO%s%s&quot; % (target and (&quot; &quot; + target), op and (&quot; o&quot;)))
+
+    def whois(self, targets):
+        &quot;&quot;&quot;Send a WHOIS command.&quot;&quot;&quot;
+        self.send_raw(&quot;WHOIS &quot; + &quot;,&quot;.join(targets))
+
+    def whowas(self, nick, max=&quot;&quot;, server=&quot;&quot;):
+        &quot;&quot;&quot;Send a WHOWAS command.&quot;&quot;&quot;
+        self.send_raw(&quot;WHOWAS %s%s%s&quot; % (nick,
+                                         max and (&quot; &quot; + max),
+                                         server and (&quot; &quot; + server)))
+
+class DCCConnectionError(IRCError):
+    pass
+
+
+class DCCConnection(Connection):
+    &quot;&quot;&quot;This class represents a DCC connection.
+
+    DCCConnection objects are instantiated by calling the dcc
+    method on an IRC object.
+    &quot;&quot;&quot;
+    def __init__(self, irclibobj, dcctype):
+        Connection.__init__(self, irclibobj)
+        self.connected = 0
+        self.passive = 0
+        self.dcctype = dcctype
+        self.peeraddress = None
+        self.peerport = None
+
+    def connect(self, address, port):
+        &quot;&quot;&quot;Connect/reconnect to a DCC peer.
+
+        Arguments:
+            address -- Host/IP address of the peer.
+
+            port -- The port number to connect to.
+
+        Returns the DCCConnection object.
+        &quot;&quot;&quot;
+        self.peeraddress = socket.gethostbyname(address)
+        self.peerport = port
+        self.socket = None
+        self.previous_buffer = &quot;&quot;
+        self.handlers = {}
+        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        self.passive = 0
+        try:
+            self.socket.connect((self.peeraddress, self.peerport))
+        except socket.error, x:
+            raise DCCConnectionError, &quot;Couldn't connect to socket: %s&quot; % x
+        self.connected = 1
+        if self.irclibobj.fn_to_add_socket:
+            self.irclibobj.fn_to_add_socket(self.socket)
+        return self
+
+    def listen(self):
+        &quot;&quot;&quot;Wait for a connection/reconnection from a DCC peer.
+
+        Returns the DCCConnection object.
+
+        The local IP address and port are available as
+        self.localaddress and self.localport.  After connection from a
+        peer, the peer address and port are available as
+        self.peeraddress and self.peerport.
+        &quot;&quot;&quot;
+        self.previous_buffer = &quot;&quot;
+        self.handlers = {}
+        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        self.passive = 1
+        try:
+            self.socket.bind((socket.gethostbyname(socket.gethostname()), 0))
+            self.localaddress, self.localport = self.socket.getsockname()
+            self.socket.listen(10)
+        except socket.error, x:
+            raise DCCConnectionError, &quot;Couldn't bind socket: %s&quot; % x
+        return self
+
+    def disconnect(self, message=&quot;&quot;):
+        &quot;&quot;&quot;Hang up the connection and close the object.
+
+        Arguments:
+
+            message -- Quit message.
+        &quot;&quot;&quot;
+        if not self.connected:
+            return
+
+        self.connected = 0
+        try:
+            self.socket.close()
+        except socket.error, x:
+            pass
+        self.socket = None
+        self.irclibobj._handle_event(
+            self,
+            Event(&quot;dcc_disconnect&quot;, self.peeraddress, &quot;&quot;, [message]))
+        self.irclibobj._remove_connection(self)
+
+    def process_data(self):
+        &quot;&quot;&quot;[Internal]&quot;&quot;&quot;
+
+        if self.passive and not self.connected:
+            conn, (self.peeraddress, self.peerport) = self.socket.accept()
+            self.socket.close()
+            self.socket = conn
+            self.connected = 1
+            if DEBUG:
+                print &quot;DCC connection from %s:%d&quot; % (
+                    self.peeraddress, self.peerport)
+            self.irclibobj._handle_event(
+                self,
+                Event(&quot;dcc_connect&quot;, self.peeraddress, None, None))
+            return
+
+        try:
+            new_data = self.socket.recv(2**14)
+        except socket.error, x:
+            # The server hung up.
+            self.disconnect(&quot;Connection reset by peer&quot;)
+            return
+        if not new_data:
+            # Read nothing: connection must be down.
+            self.disconnect(&quot;Connection reset by peer&quot;)
+            return
+
+        if self.dcctype == &quot;chat&quot;:
+            # The specification says lines are terminated with LF, but
+            # it seems safer to handle CR LF terminations too.
+            chunks = _linesep_regexp.split(self.previous_buffer + new_data)
+
+            # Save the last, unfinished line.
+            self.previous_buffer = chunks[-1]
+            if len(self.previous_buffer) &gt; 2**14:
+                # Bad peer! Naughty peer!
+                self.disconnect()
+                return
+            chunks = chunks[:-1]
+        else:
+            chunks = [new_data]
+
+        command = &quot;dccmsg&quot;
+        prefix = self.peeraddress
+        target = None
+        for chunk in chunks:
+            if DEBUG:
+                print &quot;FROM PEER:&quot;, chunk
+            arguments = [chunk]
+            if DEBUG:
+                print &quot;command: %s, source: %s, target: %s, arguments: %s&quot; % (
+                    command, prefix, target, arguments)
+            self.irclibobj._handle_event(
+                self,
+                Event(command, prefix, target, arguments))
+
+    def _get_socket(self):
+        &quot;&quot;&quot;[Internal]&quot;&quot;&quot;
+        return self.socket
+
+    def privmsg(self, string):
+        &quot;&quot;&quot;Send data to DCC peer.
+
+        The string will be padded with appropriate LF if it's a DCC
+        CHAT session.
+        &quot;&quot;&quot;
+        try:
+            self.socket.send(string)
+            if self.dcctype == &quot;chat&quot;:
+                self.socket.send(&quot;\n&quot;)
+            if DEBUG:
+                print &quot;TO PEER: %s\n&quot; % string
+        except socket.error, x:
+            # Ouch!
+            self.disconnect(&quot;Connection reset by peer.&quot;)
+
+class SimpleIRCClient:
+    &quot;&quot;&quot;A simple single-server IRC client class.
+
+    This is an example of an object-oriented wrapper of the IRC
+    framework.  A real IRC client can be made by subclassing this
+    class and adding appropriate methods.
+
+    The method on_join will be called when a &quot;join&quot; event is created
+    (which is done when the server sends a JOIN messsage/command),
+    on_privmsg will be called for &quot;privmsg&quot; events, and so on.  The
+    handler methods get two arguments: the connection object (same as
+    self.connection) and the event object.
+
+    Instance attributes that can be used by sub classes:
+
+        ircobj -- The IRC instance.
+
+        connection -- The ServerConnection instance.
+
+        dcc_connections -- A list of DCCConnection instances.
+    &quot;&quot;&quot;
+    def __init__(self):
+        self.ircobj = IRC()
+        self.connection = self.ircobj.server()
+        self.dcc_connections = []
+        self.ircobj.add_global_handler(&quot;all_events&quot;, self._dispatcher, -10)
+        self.ircobj.add_global_handler(&quot;dcc_disconnect&quot;, self._dcc_disconnect, -10)
+
+    def _dispatcher(self, c, e):
+        &quot;&quot;&quot;[Internal]&quot;&quot;&quot;
+        m = &quot;on_&quot; + e.eventtype()
+        if hasattr(self, m):
+            getattr(self, m)(c, e)
+
+    def _dcc_disconnect(self, c, e):
+        self.dcc_connections.remove(c)
+
+    def connect(self, server, port, nickname, password=None, username=None,
+                ircname=None, localaddress=&quot;&quot;, localport=0, ssl=False, ipv6=False):
+        &quot;&quot;&quot;Connect/reconnect to a server.
+
+        Arguments:
+
+            server -- Server name.
+
+            port -- Port number.
+
+            nickname -- The nickname.
+
+            password -- Password (if any).
+
+            username -- The username.
+
+            ircname -- The IRC name.
+
+            localaddress -- Bind the connection to a specific local IP address.
+
+            localport -- Bind the connection to a specific local port.
+
+            ssl -- Enable support for ssl.
+
+            ipv6 -- Enable support for ipv6.
+
+        This function can be called to reconnect a closed connection.
+        &quot;&quot;&quot;
+        self.connection.connect(server, port, nickname,
+                                password, username, ircname,
+                                localaddress, localport, ssl, ipv6)
+
+    def dcc_connect(self, address, port, dcctype=&quot;chat&quot;):
+        &quot;&quot;&quot;Connect to a DCC peer.
+
+        Arguments:
+
+            address -- IP address of the peer.
+
+            port -- Port to connect to.
+
+        Returns a DCCConnection instance.
+        &quot;&quot;&quot;
+        dcc = self.ircobj.dcc(dcctype)
+        self.dcc_connections.append(dcc)
+        dcc.connect(address, port)
+        return dcc
+
+    def dcc_listen(self, dcctype=&quot;chat&quot;):
+        &quot;&quot;&quot;Listen for connections from a DCC peer.
+
+        Returns a DCCConnection instance.
+        &quot;&quot;&quot;
+        dcc = self.ircobj.dcc(dcctype)
+        self.dcc_connections.append(dcc)
+        dcc.listen()
+        return dcc
+
+    def start(self):
+        &quot;&quot;&quot;Start the IRC client.&quot;&quot;&quot;
+        self.ircobj.process_forever()
+
+
+class Event:
+    &quot;&quot;&quot;Class representing an IRC event.&quot;&quot;&quot;
+    def __init__(self, eventtype, source, target, arguments=None):
+        &quot;&quot;&quot;Constructor of Event objects.
+
+        Arguments:
+
+            eventtype -- A string describing the event.
+
+            source -- The originator of the event (a nick mask or a server).
+
+            target -- The target of the event (a nick or a channel).
+
+            arguments -- Any event specific arguments.
+        &quot;&quot;&quot;
+        self._eventtype = eventtype
+        self._source = source
+        self._target = target
+        if arguments:
+            self._arguments = arguments
+        else:
+            self._arguments = []
+
+    def eventtype(self):
+        &quot;&quot;&quot;Get the event type.&quot;&quot;&quot;
+        return self._eventtype
+
+    def source(self):
+        &quot;&quot;&quot;Get the event source.&quot;&quot;&quot;
+        return self._source
+
+    def target(self):
+        &quot;&quot;&quot;Get the event target.&quot;&quot;&quot;
+        return self._target
+
+    def arguments(self):
+        &quot;&quot;&quot;Get the event arguments.&quot;&quot;&quot;
+        return self._arguments
+
+_LOW_LEVEL_QUOTE = &quot;\020&quot;
+_CTCP_LEVEL_QUOTE = &quot;\134&quot;
+_CTCP_DELIMITER = &quot;\001&quot;
+
+_low_level_mapping = {
+    &quot;0&quot;: &quot;\000&quot;,
+    &quot;n&quot;: &quot;\n&quot;,
+    &quot;r&quot;: &quot;\r&quot;,
+    _LOW_LEVEL_QUOTE: _LOW_LEVEL_QUOTE
+}
+
+_low_level_regexp = re.compile(_LOW_LEVEL_QUOTE + &quot;(.)&quot;)
+
+def mask_matches(nick, mask):
+    &quot;&quot;&quot;Check if a nick matches a mask.
+
+    Returns true if the nick matches, otherwise false.
+    &quot;&quot;&quot;
+    nick = irc_lower(nick)
+    mask = irc_lower(mask)
+    mask = mask.replace(&quot;\\&quot;, &quot;\\\\&quot;)
+    for ch in &quot;.$|[](){}+&quot;:
+        mask = mask.replace(ch, &quot;\\&quot; + ch)
+    mask = mask.replace(&quot;?&quot;, &quot;.&quot;)
+    mask = mask.replace(&quot;*&quot;, &quot;.*&quot;)
+    r = re.compile(mask, re.IGNORECASE)
+    return r.match(nick)
+
+_special = &quot;-[]\\`^{}&quot;
+nick_characters = string.ascii_letters + string.digits + _special
+_ircstring_translation = string.maketrans(string.ascii_uppercase + &quot;[]\\^&quot;,
+                                          string.ascii_lowercase + &quot;{}|~&quot;)
+
+def irc_lower(s):
+    &quot;&quot;&quot;Returns a lowercased string.
+
+    The definition of lowercased comes from the IRC specification (RFC
+    1459).
+    &quot;&quot;&quot;
+    return s.translate(_ircstring_translation)
+
+def _ctcp_dequote(message):
+    &quot;&quot;&quot;[Internal] Dequote a message according to CTCP specifications.
+
+    The function returns a list where each element can be either a
+    string (normal message) or a tuple of one or two strings (tagged
+    messages).  If a tuple has only one element (ie is a singleton),
+    that element is the tag; otherwise the tuple has two elements: the
+    tag and the data.
+
+    Arguments:
+
+        message -- The message to be decoded.
+    &quot;&quot;&quot;
+
+    def _low_level_replace(match_obj):
+        ch = match_obj.group(1)
+
+        # If low_level_mapping doesn't have the character as key, we
+        # should just return the character.
+        return _low_level_mapping.get(ch, ch)
+
+    if _LOW_LEVEL_QUOTE in message:
+        # Yup, there was a quote.  Release the dequoter, man!
+        message = _low_level_regexp.sub(_low_level_replace, message)
+
+    if _CTCP_DELIMITER not in message:
+        return [message]
+    else:
+        # Split it into parts.  (Does any IRC client actually *use*
+        # CTCP stacking like this?)
+        chunks = message.split(_CTCP_DELIMITER)
+
+        messages = []
+        i = 0
+        while i &lt; len(chunks)-1:
+            # Add message if it's non-empty.
+            if len(chunks[i]) &gt; 0:
+                messages.append(chunks[i])
+
+            if i &lt; len(chunks)-2:
+                # Aye!  CTCP tagged data ahead!
+                messages.append(tuple(chunks[i+1].split(&quot; &quot;, 1)))
+
+            i = i + 2
+
+        if len(chunks) % 2 == 0:
+            # Hey, a lonely _CTCP_DELIMITER at the end!  This means
+            # that the last chunk, including the delimiter, is a
+            # normal message!  (This is according to the CTCP
+            # specification.)
+            messages.append(_CTCP_DELIMITER + chunks[-1])
+
+        return messages
+
+def is_channel(string):
+    &quot;&quot;&quot;Check if a string is a channel name.
+
+    Returns true if the argument is a channel name, otherwise false.
+    &quot;&quot;&quot;
+    return string and string[0] in &quot;#&amp;+!&quot;
+
+def ip_numstr_to_quad(num):
+    &quot;&quot;&quot;Convert an IP number as an integer given in ASCII
+    representation (e.g. '3232235521') to an IP address string
+    (e.g. '192.168.0.1').&quot;&quot;&quot;
+    n = long(num)
+    p = map(str, map(int, [n &gt;&gt; 24 &amp; 0xFF, n &gt;&gt; 16 &amp; 0xFF,
+                           n &gt;&gt; 8 &amp; 0xFF, n &amp; 0xFF]))
+    return &quot;.&quot;.join(p)
+
+def ip_quad_to_numstr(quad):
+    &quot;&quot;&quot;Convert an IP address string (e.g. '192.168.0.1') to an IP
+    number as an integer given in ASCII representation
+    (e.g. '3232235521').&quot;&quot;&quot;
+    p = map(long, quad.split(&quot;.&quot;))
+    s = str((p[0] &lt;&lt; 24) | (p[1] &lt;&lt; 16) | (p[2] &lt;&lt; 8) | p[3])
+    if s[-1] == &quot;L&quot;:
+        s = s[:-1]
+    return s
+
+def nm_to_n(s):
+    &quot;&quot;&quot;Get the nick part of a nickmask.
+
+    (The source of an Event is a nickmask.)
+    &quot;&quot;&quot;
+    return s.split(&quot;!&quot;)[0]
+
+def nm_to_uh(s):
+    &quot;&quot;&quot;Get the userhost part of a nickmask.
+
+    (The source of an Event is a nickmask.)
+    &quot;&quot;&quot;
+    return s.split(&quot;!&quot;)[1]
+
+def nm_to_h(s):
+    &quot;&quot;&quot;Get the host part of a nickmask.
+
+    (The source of an Event is a nickmask.)
+    &quot;&quot;&quot;
+    return s.split(&quot;@&quot;)[1]
+
+def nm_to_u(s):
+    &quot;&quot;&quot;Get the user part of a nickmask.
+
+    (The source of an Event is a nickmask.)
+    &quot;&quot;&quot;
+    s = s.split(&quot;!&quot;)[1]
+    return s.split(&quot;@&quot;)[0]
+
+def parse_nick_modes(mode_string):
+    &quot;&quot;&quot;Parse a nick mode string.
+
+    The function returns a list of lists with three members: sign,
+    mode and argument.  The sign is \&quot;+\&quot; or \&quot;-\&quot;.  The argument is
+    always None.
+
+    Example:
+
+    &gt;&gt;&gt; irclib.parse_nick_modes(\&quot;+ab-c\&quot;)
+    [['+', 'a', None], ['+', 'b', None], ['-', 'c', None]]
+    &quot;&quot;&quot;
+
+    return _parse_modes(mode_string, &quot;&quot;)
+
+def parse_channel_modes(mode_string):
+    &quot;&quot;&quot;Parse a channel mode string.
+
+    The function returns a list of lists with three members: sign,
+    mode and argument.  The sign is \&quot;+\&quot; or \&quot;-\&quot;.  The argument is
+    None if mode isn't one of \&quot;b\&quot;, \&quot;k\&quot;, \&quot;l\&quot;, \&quot;v\&quot; or \&quot;o\&quot;.
+
+    Example:
+
+    &gt;&gt;&gt; irclib.parse_channel_modes(\&quot;+ab-c foo\&quot;)
+    [['+', 'a', None], ['+', 'b', 'foo'], ['-', 'c', None]]
+    &quot;&quot;&quot;
+
+    return _parse_modes(mode_string, &quot;bklvo&quot;)
+
+def _parse_modes(mode_string, unary_modes=&quot;&quot;):
+    &quot;&quot;&quot;[Internal]&quot;&quot;&quot;
+    modes = []
+    arg_count = 0
+
+    # State variable.
+    sign = &quot;&quot;
+
+    a = mode_string.split()
+    if len(a) == 0:
+        return []
+    else:
+        mode_part, args = a[0], a[1:]
+
+    if mode_part[0] not in &quot;+-&quot;:
+        return []
+    for ch in mode_part:
+        if ch in &quot;+-&quot;:
+            sign = ch
+        elif ch == &quot; &quot;:
+            collecting_arguments = 1
+        elif ch in unary_modes:
+            if len(args) &gt;= arg_count + 1:
+                modes.append([sign, ch, args[arg_count]])
+                arg_count = arg_count + 1
+            else:
+                modes.append([sign, ch, None])
+        else:
+            modes.append([sign, ch, None])
+    return modes
+
+def _ping_ponger(connection, event):
+    &quot;&quot;&quot;[Internal]&quot;&quot;&quot;
+    connection.pong(event.target())
+
+# Numeric table mostly stolen from the Perl IRC module (Net::IRC).
+numeric_events = {
+    &quot;001&quot;: &quot;welcome&quot;,
+    &quot;002&quot;: &quot;yourhost&quot;,
+    &quot;003&quot;: &quot;created&quot;,
+    &quot;004&quot;: &quot;myinfo&quot;,
+    &quot;005&quot;: &quot;featurelist&quot;,  # XXX
+    &quot;200&quot;: &quot;tracelink&quot;,
+    &quot;201&quot;: &quot;traceconnecting&quot;,
+    &quot;202&quot;: &quot;tracehandshake&quot;,
+    &quot;203&quot;: &quot;traceunknown&quot;,
+    &quot;204&quot;: &quot;traceoperator&quot;,
+    &quot;205&quot;: &quot;traceuser&quot;,
+    &quot;206&quot;: &quot;traceserver&quot;,
+    &quot;207&quot;: &quot;traceservice&quot;,
+    &quot;208&quot;: &quot;tracenewtype&quot;,
+    &quot;209&quot;: &quot;traceclass&quot;,
+    &quot;210&quot;: &quot;tracereconnect&quot;,
+    &quot;211&quot;: &quot;statslinkinfo&quot;,
+    &quot;212&quot;: &quot;statscommands&quot;,
+    &quot;213&quot;: &quot;statscline&quot;,
+    &quot;214&quot;: &quot;statsnline&quot;,
+    &quot;215&quot;: &quot;statsiline&quot;,
+    &quot;216&quot;: &quot;statskline&quot;,
+    &quot;217&quot;: &quot;statsqline&quot;,
+    &quot;218&quot;: &quot;statsyline&quot;,
+    &quot;219&quot;: &quot;endofstats&quot;,
+    &quot;221&quot;: &quot;umodeis&quot;,
+    &quot;231&quot;: &quot;serviceinfo&quot;,
+    &quot;232&quot;: &quot;endofservices&quot;,
+    &quot;233&quot;: &quot;service&quot;,
+    &quot;234&quot;: &quot;servlist&quot;,
+    &quot;235&quot;: &quot;servlistend&quot;,
+    &quot;241&quot;: &quot;statslline&quot;,
+    &quot;242&quot;: &quot;statsuptime&quot;,
+    &quot;243&quot;: &quot;statsoline&quot;,
+    &quot;244&quot;: &quot;statshline&quot;,
+    &quot;250&quot;: &quot;luserconns&quot;,
+    &quot;251&quot;: &quot;luserclient&quot;,
+    &quot;252&quot;: &quot;luserop&quot;,
+    &quot;253&quot;: &quot;luserunknown&quot;,
+    &quot;254&quot;: &quot;luserchannels&quot;,
+    &quot;255&quot;: &quot;luserme&quot;,
+    &quot;256&quot;: &quot;adminme&quot;,
+    &quot;257&quot;: &quot;adminloc1&quot;,
+    &quot;258&quot;: &quot;adminloc2&quot;,
+    &quot;259&quot;: &quot;adminemail&quot;,
+    &quot;261&quot;: &quot;tracelog&quot;,
+    &quot;262&quot;: &quot;endoftrace&quot;,
+    &quot;263&quot;: &quot;tryagain&quot;,
+    &quot;265&quot;: &quot;n_local&quot;,
+    &quot;266&quot;: &quot;n_global&quot;,
+    &quot;300&quot;: &quot;none&quot;,
+    &quot;301&quot;: &quot;away&quot;,
+    &quot;302&quot;: &quot;userhost&quot;,
+    &quot;303&quot;: &quot;ison&quot;,
+    &quot;305&quot;: &quot;unaway&quot;,
+    &quot;306&quot;: &quot;nowaway&quot;,
+    &quot;311&quot;: &quot;whoisuser&quot;,
+    &quot;312&quot;: &quot;whoisserver&quot;,
+    &quot;313&quot;: &quot;whoisoperator&quot;,
+    &quot;314&quot;: &quot;whowasuser&quot;,
+    &quot;315&quot;: &quot;endofwho&quot;,
+    &quot;316&quot;: &quot;whoischanop&quot;,
+    &quot;317&quot;: &quot;whoisidle&quot;,
+    &quot;318&quot;: &quot;endofwhois&quot;,
+    &quot;319&quot;: &quot;whoischannels&quot;,
+    &quot;321&quot;: &quot;liststart&quot;,
+    &quot;322&quot;: &quot;list&quot;,
+    &quot;323&quot;: &quot;listend&quot;,
+    &quot;324&quot;: &quot;channelmodeis&quot;,
+    &quot;329&quot;: &quot;channelcreate&quot;,
+    &quot;331&quot;: &quot;notopic&quot;,
+    &quot;332&quot;: &quot;currenttopic&quot;,
+    &quot;333&quot;: &quot;topicinfo&quot;,
+    &quot;341&quot;: &quot;inviting&quot;,
+    &quot;342&quot;: &quot;summoning&quot;,
+    &quot;346&quot;: &quot;invitelist&quot;,
+    &quot;347&quot;: &quot;endofinvitelist&quot;,
+    &quot;348&quot;: &quot;exceptlist&quot;,
+    &quot;349&quot;: &quot;endofexceptlist&quot;,
+    &quot;351&quot;: &quot;version&quot;,
+    &quot;352&quot;: &quot;whoreply&quot;,
+    &quot;353&quot;: &quot;namreply&quot;,
+    &quot;361&quot;: &quot;killdone&quot;,
+    &quot;362&quot;: &quot;closing&quot;,
+    &quot;363&quot;: &quot;closeend&quot;,
+    &quot;364&quot;: &quot;links&quot;,
+    &quot;365&quot;: &quot;endoflinks&quot;,
+    &quot;366&quot;: &quot;endofnames&quot;,
+    &quot;367&quot;: &quot;banlist&quot;,
+    &quot;368&quot;: &quot;endofbanlist&quot;,
+    &quot;369&quot;: &quot;endofwhowas&quot;,
+    &quot;371&quot;: &quot;info&quot;,
+    &quot;372&quot;: &quot;motd&quot;,
+    &quot;373&quot;: &quot;infostart&quot;,
+    &quot;374&quot;: &quot;endofinfo&quot;,
+    &quot;375&quot;: &quot;motdstart&quot;,
+    &quot;376&quot;: &quot;endofmotd&quot;,
+    &quot;377&quot;: &quot;motd2&quot;,        # 1997-10-16 -- tkil
+    &quot;381&quot;: &quot;youreoper&quot;,
+    &quot;382&quot;: &quot;rehashing&quot;,
+    &quot;384&quot;: &quot;myportis&quot;,
+    &quot;391&quot;: &quot;time&quot;,
+    &quot;392&quot;: &quot;usersstart&quot;,
+    &quot;393&quot;: &quot;users&quot;,
+    &quot;394&quot;: &quot;endofusers&quot;,
+    &quot;395&quot;: &quot;nousers&quot;,
+    &quot;401&quot;: &quot;nosuchnick&quot;,
+    &quot;402&quot;: &quot;nosuchserver&quot;,
+    &quot;403&quot;: &quot;nosuchchannel&quot;,
+    &quot;404&quot;: &quot;cannotsendtochan&quot;,
+    &quot;405&quot;: &quot;toomanychannels&quot;,
+    &quot;406&quot;: &quot;wasnosuchnick&quot;,
+    &quot;407&quot;: &quot;toomanytargets&quot;,
+    &quot;409&quot;: &quot;noorigin&quot;,
+    &quot;411&quot;: &quot;norecipient&quot;,
+    &quot;412&quot;: &quot;notexttosend&quot;,
+    &quot;413&quot;: &quot;notoplevel&quot;,
+    &quot;414&quot;: &quot;wildtoplevel&quot;,
+    &quot;421&quot;: &quot;unknowncommand&quot;,
+    &quot;422&quot;: &quot;nomotd&quot;,
+    &quot;423&quot;: &quot;noadmininfo&quot;,
+    &quot;424&quot;: &quot;fileerror&quot;,
+    &quot;431&quot;: &quot;nonicknamegiven&quot;,
+    &quot;432&quot;: &quot;erroneusnickname&quot;, # Thiss iz how its speld in thee RFC.
+    &quot;433&quot;: &quot;nicknameinuse&quot;,
+    &quot;436&quot;: &quot;nickcollision&quot;,
+    &quot;437&quot;: &quot;unavailresource&quot;,  # &quot;Nick temporally unavailable&quot;
+    &quot;441&quot;: &quot;usernotinchannel&quot;,
+    &quot;442&quot;: &quot;notonchannel&quot;,
+    &quot;443&quot;: &quot;useronchannel&quot;,
+    &quot;444&quot;: &quot;nologin&quot;,
+    &quot;445&quot;: &quot;summondisabled&quot;,
+    &quot;446&quot;: &quot;usersdisabled&quot;,
+    &quot;451&quot;: &quot;notregistered&quot;,
+    &quot;461&quot;: &quot;needmoreparams&quot;,
+    &quot;462&quot;: &quot;alreadyregistered&quot;,
+    &quot;463&quot;: &quot;nopermforhost&quot;,
+    &quot;464&quot;: &quot;passwdmismatch&quot;,
+    &quot;465&quot;: &quot;yourebannedcreep&quot;, # I love this one...
+    &quot;466&quot;: &quot;youwillbebanned&quot;,
+    &quot;467&quot;: &quot;keyset&quot;,
+    &quot;471&quot;: &quot;channelisfull&quot;,
+    &quot;472&quot;: &quot;unknownmode&quot;,
+    &quot;473&quot;: &quot;inviteonlychan&quot;,
+    &quot;474&quot;: &quot;bannedfromchan&quot;,
+    &quot;475&quot;: &quot;badchannelkey&quot;,
+    &quot;476&quot;: &quot;badchanmask&quot;,
+    &quot;477&quot;: &quot;nochanmodes&quot;,  # &quot;Channel doesn't support modes&quot;
+    &quot;478&quot;: &quot;banlistfull&quot;,
+    &quot;481&quot;: &quot;noprivileges&quot;,
+    &quot;482&quot;: &quot;chanoprivsneeded&quot;,
+    &quot;483&quot;: &quot;cantkillserver&quot;,
+    &quot;484&quot;: &quot;restricted&quot;,   # Connection is restricted
+    &quot;485&quot;: &quot;uniqopprivsneeded&quot;,
+    &quot;491&quot;: &quot;nooperhost&quot;,
+    &quot;492&quot;: &quot;noservicehost&quot;,
+    &quot;501&quot;: &quot;umodeunknownflag&quot;,
+    &quot;502&quot;: &quot;usersdontmatch&quot;,
+}
+
+generated_events = [
+    # Generated events
+    &quot;dcc_connect&quot;,
+    &quot;dcc_disconnect&quot;,
+    &quot;dccmsg&quot;,
+    &quot;disconnect&quot;,
+    &quot;ctcp&quot;,
+    &quot;ctcpreply&quot;,
+]
+
+protocol_events = [
+    # IRC protocol events
+    &quot;error&quot;,
+    &quot;join&quot;,
+    &quot;kick&quot;,
+    &quot;mode&quot;,
+    &quot;part&quot;,
+    &quot;ping&quot;,
+    &quot;privmsg&quot;,
+    &quot;privnotice&quot;,
+    &quot;pubmsg&quot;,
+    &quot;pubnotice&quot;,
+    &quot;quit&quot;,
+    &quot;invite&quot;,
+    &quot;pong&quot;,
+]
+
+all_events = generated_events + protocol_events + numeric_events.values()
</ins></span></pre>
</div>
</div>

</body>
</html>