Source code for hanyuu.ircbot.irclib.dcc

from __future__ import unicode_literals
from __future__ import print_function
from __future__ import absolute_import
import socket
from . import utils
from . import connection

from . import logger

logger = logger.getChild(__name__)

# TODO: set this somewhere else?
DEBUG = False

[docs]class DCCConnectionError(connection.IRCError): pass
[docs]class DCCConnection(connection.Connection): """This class represents a DCC connection. DCCConnection objects are instantiated by calling :meth:`session.Session.dcc`. For usage, see :meth:`connect` and :meth:`listen`. """ def __init__(self, irclibobj, dcctype, dccinfo=(None, 0)): connection.Connection.__init__(self, irclibobj) self.connected = 0 self.passive = 0 self.dcctype = dcctype self.dccfile = dccinfo[0] self.total = long(dccinfo[1]) self.peeraddress = None self.peerport = None
[docs] def connect(self, address, port): """Connect/reconnect to a DCC peer. :param address: Host/IP address of the peer. :param port: The port number to connect to. Returns the DCCConnection object. """ if (self.dcctype == "send"): self.fileobj = open(self.dccfile, "wb") self.current = 0 self.peeraddress = socket.gethostbyname(address) self.peerport = port self.socket = None self.previous_buffer = "" 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("Couldn't connect to socket: {}".format(x)) self.connected = 1 self.irclibobj.register_socket(self.socket, self) return self
[docs] def listen(self): """Wait for a connection/reconnection from a DCC peer. Returns the DCCConnection object. The local IP address and port are available as :attr:`localaddress` and :attr:`localport`. After connection from a peer, the peer address and port are available as :attr:`peeraddress` and :attr:`peerport`. """ self.previous_buffer = b"" 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("Couldn't bind socket: {}".format(x)) self.irclibobj.register_socket(self.socket, self) return self
[docs] def disconnect(self, message=""): """Hang up the connection and close the object. :param message: Quit message. .. note:: After calling this method, the object becomes unusable. """ 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, connection.Event("dcc_disconnect", self.peeraddress, "", [message])) self.irclibobj._remove_connection(self)
[docs] def process_data(self): """[Internal]""" 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: logger.debug("DCC connection from {}:{}" .format(self.peeraddress, self.peerport)) self.irclibobj._handle_event( self, connection.Event("dcc_connect", self.peeraddress, None, None)) return try: new_data = self.socket.recv(2**14) except socket.error, x: # The server hung up. self.disconnect("Connection reset by peer") return if not new_data: # Read nothing: connection must be down. self.disconnect("Connection reset by peer") return if self.dcctype == "chat": # The specification says lines are terminated with LF, but # it seems safer to handle CR LF terminations too. chunks = utils._linesep_regexp.split(self.previous_buffer + new_data) # Save the last, unfinished line. self.previous_buffer = chunks[-1] if len(self.previous_buffer) > 2**14: # Bad peer! Naughty peer! self.disconnect() return chunks = chunks[:-1] elif self.dcctype == "send": # We are going to sidestep the events a bit size = len(new_data) self.current += size try: self.fileobj.write(new_data) except (AttributeError): self.disconnect("Invalid file object") except (IOError): self.disconnect("Invalid file object") chunks = ["send"] else: chunks = [new_data] command = "dccmsg" prefix = self.peeraddress target = None for chunk in chunks: if DEBUG: logger.debug("FROM PEER:" + chunk) arguments = [chunk] if DEBUG: logger.debug("command: {}, source: {}, target: {}, arguments: {}".format( command, prefix, target, arguments)) self.irclibobj._handle_event( self, connection.Event(command, prefix, target, arguments))
def _get_socket(self): """[Internal]""" return self.socket
[docs] def privmsg(self, string): """Send data to DCC peer. The string will be padded with appropriate LF if it's a DCC CHAT session. """ try: self.socket.send(string) if self.dcctype == "chat": self.socket.send("\n") if DEBUG: print("TO PEER: {}\n".format(string)) except socket.error, x: # Ouch! self.disconnect("Connection reset by peer.")

Project Versions

This Page