Skip to content

Commit

Permalink
Support IRCv3 CAP, Make SASL less hacky
Browse files Browse the repository at this point in the history
  • Loading branch information
itslukej committed Aug 27, 2016
1 parent fd2a2da commit 3f3831c
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 42 deletions.
6 changes: 2 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ class Bot(zirc.Client):
ident="bot",
realname="test bot",
channels=["##chat"],
sasl_user="NickServ_Username",
sasl_pass="NickServ_Password")
caps=zirc.Caps(zirc.Sasl(username="username", password="password")))

self.connect(self.config)
self.start()
Expand Down Expand Up @@ -70,9 +69,8 @@ self.connection = zirc.Socket(family=socket.AF_INET6)
Initialize `zirc.Socket` with argument `socket_class`:

```python
from zirc.ext import proxy

self.connection = zirc.Socket(socket_class=proxy.Proxy(host="localhost", port=1080, protocol=proxy.SOCKS5))
self.connection = zirc.Socket(socket_class=zirc.Proxy(host="localhost", port=1080, protocol=zirc.SOCKS5))
```


Expand Down
4 changes: 4 additions & 0 deletions zirc/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
from .client import Client
from .connection import Socket
from .config import IRCConfig
from .ext.sasl import Sasl
from .ext.proxy import *
from .ext.fifo import Fifo
from .caps import Caps
36 changes: 36 additions & 0 deletions zirc/caps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from .ext import sasl

class Caps(object):
def __init__(self, *args):
self.caps = list(args)
self.availablecaps = []
self.stringcaps = []
self.done = False
for cap in self.caps:
if not isinstance(cap, str):
self.stringcaps.append(cap.name)
else:
self.stringcaps.append(cap)

def handler(self,event):
if event.arguments[0] == "LS":
servcaps = event.arguments[1].split(' ')
for c in servcaps:
if c in self.stringcaps:
self.availablecaps.append(c)
if not self.availablecaps:
self.bot.send("CAP END")
else:
self.bot.send("CAP REQ :"+" ".join(self.availablecaps))
elif event.arguments[0] == "ACK":
for cap in self.caps:
if hasattr(cap, "run"):
cap.run(self.bot)

def run(self, bot):
self.bot = bot
self.bot.listen(self.handler,"cap")
self.bot.send("CAP LS")


__call__ = run
55 changes: 21 additions & 34 deletions zirc/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import sys,time

class Client(object):
listeners = []
def connect(self, config_class = None):

self.fp = floodProtect()
Expand All @@ -21,9 +22,8 @@ def connect(self, config_class = None):
self._config = config_class
self.socket = self.connection((self._config["host"], self._config["port"]))

if self._config["sasl_user"] is not None and self._config["sasl_pass"] is not None:
self.do_sasl(self._config["sasl_user"], self._config["sasl_pass"])

self._config["caps"](self)

self.send("NICK {0}".format(self._config["nickname"]))
self.send("USER {0} * * :{1}".format(self._config["ident"], self._config["realname"]))

Expand Down Expand Up @@ -53,14 +53,24 @@ def main_job(self, event):
for channel in self._channels:
self.send("JOIN {0}".format(channel))

to_call = ["on_all", "on_"+event.type.lower()]
to_call = []

if hasattr(self, "on_all"):
to_call.append(self.on_all)

if hasattr(self, "on_"+event.type.lower()):
to_call.append(getattr(self, "on_"+event.type.lower()))

if event.type != event.text_type:
to_call.append("on_"+event.text_type.lower())
if hasattr(self, "on_"+event.text_type.lower()):
to_call.append(getattr(self, "on_"+event.text_type.lower()))

for call_name in to_call:
if hasattr(self, call_name):
call_func = getattr(self, call_name)
util.function_argument_call(call_func, args)()
for event_name, func in self.listeners:
if event_name == event.text_type.lower() or event_name == event.type.lower():
to_call.append(func)
#Call the functions here
for call_func in to_call:
util.function_argument_call(call_func, args)()

if event.type == "PING":
self.send("PONG :{0}".format(" ".join(event.arguments)))
Expand All @@ -82,28 +92,5 @@ def privmsg(self, channel, message):
self.send("PRIVMSG {0} :{1}".format(channel, message))
def reply(self, event, message):
self.privmsg(event.target, message)
#SASL Auth
def do_sasl(self, user, passw):
self.send("CAP REQ :sasl")
while True:
for line in self.recv():
line = line.split()
if line[0] == "AUTHENTICATE":
if line[1] == "+":
saslstring = b64encode("{0}\x00{0}\x00{1}".format(
user,
passw).encode("UTF-8"))
self.send("AUTHENTICATE {0}".format(saslstring.decode("UTF-8")))
elif line[1] == "CAP":
if line[3] == "ACK":
line[4] = line[4].strip(":")
caps = line[4:]
if "sasl" in caps:
self.send("AUTHENTICATE PLAIN")
elif line[1] == "903":
self.send("CAP END")
return True
elif line[1] == "904" or line[1] == "905" or line[1] == "906":
error = " ".join(line[2:]).strip(":")
self.send("QUIT :[ERROR] {0}".format(error))
raise SASLError(error)
def listen(self, func, event_name):
self.listeners.append((event_name.lower(), func))
5 changes: 3 additions & 2 deletions zirc/config.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from .caps import Caps

class IRCConfig(object):
"""
Class for holding zIRC config infomation.
Expand All @@ -12,8 +14,7 @@ def __init__(self, **c):
"ident": "bot",
"realname": "zIRC Bot",
"channels": ["#zirc"],
"sasl_user": None,
"sasl_pass": None
"caps": Caps()
}
self.dict.update(c)

Expand Down
2 changes: 1 addition & 1 deletion zirc/ext/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
from . import proxy
from . import proxy, fifo
2 changes: 1 addition & 1 deletion zirc/fifo.py → zirc/ext/fifo.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ def fifo_while(send_function, name):
if len(line) > 0:
send_function(line)

def fifo(send_function, name="fifo"):
def Fifo(send_function, name="fifo"):
thread = threading.Thread(target=fifo_while, args=(send_function, name))
thread.daemon = True
thread.start()
31 changes: 31 additions & 0 deletions zirc/ext/sasl.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# SASL authentication for zirc

import base64
from ..errors import SASLError

class Sasl(object):
name = "sasl"
def __init__(self, username, password, method="plain"):
self.username = username
self.password = password
self.method = method
def run(self, bot):
self.bot = bot
bot.listen(self.on_authenticate,"authenticate")
bot.listen(self.on_saslfailed,"saslfailed")
bot.listen(self.on_saslsuccess,"saslsuccess")
if self.method == "plain":
bot.send("AUTHENTICATE PLAIN")
else:
raise SASLError("not implemented yet")

def on_authenticate(self,event):
if event.arguments[0] == "+":
password = base64.b64encode("{0}\x00{0}\x00{1}".format(self.username, self.password).encode("UTF-8")).decode("UTF-8")
self.bot.send("AUTHENTICATE {0}".format(password))

def on_saslfailed(self, event):
raise SASLError("SASL authentication failed!") # do something else later, like try another method

def on_saslsuccess(self, event):
self.bot.send("CAP END")

0 comments on commit 3f3831c

Please sign in to comment.