diff --git a/plugins/FServe.py b/plugins/FServe.py deleted file mode 100644 index 9f554867c..000000000 --- a/plugins/FServe.py +++ /dev/null @@ -1,596 +0,0 @@ -#!/usr/bin/env python - -import plugins - -import socket -import textwrap -import threading -import struct -import time -import os -import os.path -import glob - -import dcc -import conf -import utils -import world -import ircmsgs -import ircutils -import privmsgs -import callbacks -import registry - -conf.registerPlugin('FServe') -conf.registerGlobalValue(conf.supybot.plugins.FServe, 'packetSize', - registry.Integer(1024, 'Size of packets to send/receive with DCC')) -conf.registerGroup(conf.supybot.plugins.FServe, 'queues') -conf.registerChannelValue(conf.supybot.plugins.FServe, 'queueNames', - registry.SpaceSeparatedListOfStrings([], - 'List of queues for this channel')) - -# --- Exceptions --- - -class QueueException(Exception): - msg = "Could not queue file" - def __init__(self, m): - self.msg = m - - -# --- Handlers --- - -class SendHandler(dcc.SendHandler): - caller = None - - def clientConnected(self): - self.sock.settimeout(self.caller.timeout) - - def packetSent(self): - self.currentSpeed = (self.bytesSent / (time.time() - self.startTime)) - if (self.currentSpeed > self.caller.maxSpeed): - time.sleep(0.9) - if (self.currentSpeed < self.caller.minSpeed): - #Allow a little leeway - slow += 1 - if (slow > 5): - # Too Slow - self.log.info('\'%s\': Send too slow' % (self.filename)) - raise SendException('Too slow') - - def clientClosed(self): - # alert queue that we're done - self.caller.sendFinished(self) - - def registerPort(self): - pass - - - -class ChatHandler(dcc.ChatHandler): - - def __init__(self, irc, nick, mask, queue=None): - self.caller = queue - dcc.ChatHandler.__init__(self, irc, nick, mask) - if (queue): - self.currentDir = os.path.join(conf.supybot.directories.data(), - queue.sendDir) - else: - self.currentDir = os.path.join(conf.supybot.directories.data()) - self.baseDir = self.currentDir - - def cmd_ls(self, args): - """ Show files in current directory, using globs """ - if args: - files = [] - for a in args: - for i in glob.glob(os.path.join(self.currentDir, a)): - if (i[:2] == "./"): - i = i[2:] - if (i[:len(self.currentDir)] == self.currentDir): - i = i[len(self.currentDir)+1:] - if (not i in files): - files.append(i) - else: - files = os.listdir(self.currentDir) - if (not files): - self.sock.send("There are no matching files.\n"); - files.sort() - flist = [] - dirlist = [] - for f in files: - full = os.path.join(self.currentDir, f) - if (os.path.isdir(full)): - dirlist.append(ircutils.mircColor(' %s/' % f, 'blue')) - else: - size = os.path.getsize(full) - if (size > 1024000): - size = '%sMb' % (size / 1024000) - elif (size > 1024): - size = '%sKb' % (size / 1024) - flist.append('%s [%s]' % (f, size)) - self.sock.send('\n'.join(dirlist) + '\n' + '\n'.join(flist) + "\n") - - def cmd_dir(self, args): - return self.cmd_ls(args) - def cmd_list(self, args): - return self.cmd_ls(args) - - def cmd_cd(self, args): - """ Change current directory """ - d = args[0] - full = os.path.join(self.currentDir, d) - full = os.path.abspath(full) - if (len(full) < len(self.baseDir)): - self.sock.send("Already in root directory\n"); - return - if (os.path.exists(full)): - if (os.path.isdir(full)): - self.currentDir = full - self.sock.send('Current directory: %s\n' % - self.currentDir[len(self.baseDir)+1:]) - else: - self.sock.send('%s is not a directory\n' % d) - else: - self.sock.send('%s does not exist\n' % d) - - def cmd_get(self, args): - fn = ' '.join(args) - full = os.path.join(self.currentDir, fn) - - try: - size = os.path.getsize(full) - except OSError, e: - f = glob.glob(full) - if (len(f) == 1): - full = f[0] - size = os.path.getsize(full) - else: - self.sock.send('That matches more than one file\n') - return - min = self.caller.instantSendSize - if (size < min): - # Instant send - self.caller.spawnSend(self.irc, self.nick, self.hostmask, full) - else: - # Check queues - try: - posn = self.caller.addFile(self.irc, self.nick, self.hostmask, full) - self.sock.send('File queued at position %s\n' % - (posn)) - self.caller.maybeSend() - except QueueException, e: - self.sock.send(e.msg + "\n") - - def cmd_close(self, line): - self.sock.send('Bye!\n') - self.sock.close() - def cmd_quit(self, line): - self.cmd_close([]) - - def cmd_queues(self, args): - queues = self.caller.queuedFiles - lines = [] - for q in range(len(queues)): - if (not args or queues[q][0] == args[0]): - fn = os.path.split(queues[q][1])[1] - when = time.ctime(queues[q][3]) - lines.append('%s: %s (by %s@%s)' % (q, fn, queues[q][0], when[4:-5])) - if (lines): - self.sock.send('\n'.join(lines) + '\n') - else: - self.sock.send('There is currently nothing queued.\n') - - def cmd_myqueues(self, args): - self.cmd_queues([self.msg.nick]) - - def cmd_sends(self, line): - sends = self.caller.sends - lines = [] - for i in (range(len(sends))): - s = sends[i] - sender = s[4] - fn = os.path.split(s[1])[1] - secs = (sender.filesize - sender.currentPosition) / sender.currentSpeed - if (secs > 3600): - t = '%sh' % int(secs / 3600) - elif (secs > 60): - t = '%sm' % int(secs / 60) - else: - t = '%ss' % int(secs) - lines.append('%s: %s\n (to %s @ %sCPS, %s remaining)' % (i, fn, s[0], int(sender.currentSpeed), t)) - - if (lines): - self.sock.send('\n'.join(lines) + '\n') - else: - self.sock.send('There is currently nothing sending.\n') - - def cmd_remove(self, args): - # remove a queue by number - idx = args[0] - if (not idx.isdigit()): - self.sock.send('Usage: remove \n') - else: - posn = int(idx) - queues = self.caller.queuedFiles - if (len(queues) < posn or posn < 0): - self.sock.send('There is no such queue position\n') - else: - # XXX infintesimal race condition - del self.caller.queuedFiles[posn] - self.sock.send('Removed queue.\n') - - def cmd_help(self, line): - pass - - def chatConnected(self): - self.sock.send("Welcome.\n") - self.cmd_ls([]) - - def handleLine(self, line): - args = line.split() - if (not args): - return - if (not hasattr(self, 'cmd_%s' % args[0])): - self.sock.send('Unknown command: %s\n' % args[0]) - else: - fn = getattr(self, 'cmd_%s' % args[0]) - fn(args[1:]) - - -class AdminHandler(ChatHandler): - - def cmd_config(self, args): - pass - - - - -# --- Queue Object --- - -class Queue: - name = "" - plugin = None - channel = "" - timeout = 0 - maxSpeed = 0 - minSpeed = 0 - maxQueues = 0 - instantSendSize = 0 - maxSends = 0 - receiveDir = "" - sendDir = "" - allowedModes = [] - allowedMasks = [] - allowedNicks = [] - bannedNicks = [] - bannedMasks = [] - chatHandler = "" - sendHandler = "" - - sends = [] - queuedFiles = [] - filesSent = 0 - bytesSent = 0 - leeches = {} - - def __init__(self, plug, channel, name): - self.name = name - self.plugin = plug - self.channel = channel - - try: - self.timeout = plug.registryValue("queues.%s.timeout" % name, channel) - except: - conf.registerGroup(conf.supybot.plugins.FServe.queues, name) - group = conf.supybot.plugins.FServe.queues.get(name) - conf.registerChannelValue(group, 'timeout', registry.Integer(90, '')) - conf.registerChannelValue(group, 'maxSpeed', registry.Integer(0, '')) - conf.registerChannelValue(group, 'minSpeed', registry.Integer(0, '')) - conf.registerChannelValue(group, 'maxQueues', registry.Integer(5, '')) - conf.registerChannelValue(group, 'instantSendSize', - registry.Integer(50000, '')) - conf.registerChannelValue(group, 'maxSends', registry.Integer(2, '')) - conf.registerChannelValue(group, 'receiveDir', - registry.String('incoming', '')) - conf.registerChannelValue(group, 'sendDir', - registry.String('files', '')) - conf.registerChannelValue(group, 'allowedModes', - registry.SpaceSeparatedListOfStrings([], '')) - conf.registerChannelValue(group, 'allowedMasks', - registry.SpaceSeparatedListOfStrings([], '')) - conf.registerChannelValue(group, 'allowedNicks', - registry.SpaceSeparatedListOfStrings([], '')) - conf.registerChannelValue(group, 'bannedMasks', - registry.SpaceSeparatedListOfStrings([], '')) - conf.registerChannelValue(group, 'bannedNicks', - registry.SpaceSeparatedListOfStrings([], '')) - conf.registerChannelValue(group, 'chatHandler', - registry.String('ChatHandler', '')) - conf.registerChannelValue(group, 'sendHandler', - registry.String('SendHandler', '')) - - - self.timeout = plug.registryValue("queues.%s.timeout" % name, channel) - self.maxSpeed = plug.registryValue("queues.%s.maxSpeed" % name, - channel) - self.minSpeed = plug.registryValue("queues.%s.minSpeed" % name, - channel) - self.maxQueues = plug.registryValue("queues.%s.maxQueues" % name, - channel) - self.instantSendSize = plug.registryValue("queues.%s.instantSendSize" - % name, channel) - self.maxSends = plug.registryValue("queues.%s.maxSends" % name, - channel) - self.receiveDir = plug.registryValue("queues.%s.receiveDir" % name, - channel) - self.sendDir = plug.registryValue("queues.%s.sendDir" % name, channel) - self.allowedModes = plug.registryValue("queues.%s.allowedModes" - % name, channel) - self.allowedMasks = plug.registryValue("queues.%s.allowedMasks" - % name, channel) - self.allowedNicks = plug.registryValue("queues.%s.allowedNicks" - % name, channel) - self.bannedNicks = plug.registryValue("queues.%s.bannedNicks" % name, - channel) - self.bannedMasks = plug.registryValue("queues.%s.bannedMasks" % name, - channel) - self.chatHandler = plug.registryValue("queues.%s.chatHandler" % name, - channel) - self.sendHandler = plug.registryValue("queues.%s.sendHandler" % name, - channel) - - # End of Configurables - self.sends = [] - self.queuedFiles = [] - self.filesSent = 0 - self.bytesSent = 0 - self.leeches = {} - - def spawnChat(self, irc, nick): - # first check permitted in queue - hostmask = irc.state.nickToHostmask(nick) - if (hostmask in self.bannedMasks or - (self.allowedMasks and not hostmask in self.allowedMasks)): - irc.reply("You are not allowed to use this queue."); - return - if (nick in self.bannedNicks or - (self.allowedNicks and not nick in self.allowedNicks)): - irc.reply("You are not allowed to use this queue."); - return - if (self.allowedModes): - ops = irc.state.channels[self.channel].ops - hops = irc.state.channels[self.channel].halfops - voices = irc.state.channels[self.channel].voices - users = irc.state.channels[self.channel].users - okay = 0 - if (nick in ops and 'op' in self.allowedModes): - okay = 1 - elif (nick in hops and 'halfop' in self.allowedModes): - okay = 1 - elif (nick in voices and 'voice' in self.allowedModes): - okay = 1 - elif (nick in users and 'user' in self.allowedModes): - okay = 1 - if (not okay): - irc.reply("You are not allowed to use this queue."); - return - - # We're okay to talk to. Spawn DCC Chat - parent = self.plugin._getHandlerClass(self.chatHandler) - new = parent(irc, nick, hostmask, queue=self) - new.start() - - def spawnSend(self, irc, nick, hostmask, file): - parent = self.plugin._getHandlerClass(self.sendHandler) - cxn = parent(irc, nick, hostmask, file) - cxn.caller = self - self.sends.append([nick, hostmask, file, irc, cxn]) - cxn.start() - - def getConnectionByPort(self, port): - for s in self.sends: - if (s[-1].port == port): - return s[-1] - return None - - def addFile(self, irc, nick, mask, file, posn=-1): - for q in self.queuedFiles: - if (q[0] == nick): - if (q[1] == file): - raise(QueueException( - 'You have already queued that file')) - nqs += 1 - if (nqs >= self.maxQueues): - raise(QueueException( - 'You have already queued the maximum number of files')) - - if (posn < 0 or posn > len(self.queuedFiles)): - # add to the end - self.queuedFiles.append([nick, mask, file, irc, time.time()]) - return len(self.queuedFiles) - else: - self.queuedFiles = self.queuedFiles[:posn] + \ - [[nick, mask, file, irc, time.time()]] + self.queuedFiles[posn:] - return posn - - def sendFinished(self, handler): - # One send finished, so delete and check to see if we should start another - for s in range(len(self.sends)): - if (self.sends[s][-1] == handler): - del self.sends[s] - break - self.maybeSend() - - def maybeSend(self): - # maybe send another queued file - if (self.queuedFiles and len(self.sends) < self.maxSends): - # find a send to someone we're not sending to already - sendingTo = [] - for s in self.sends: - sendingTo.append(s[0]) - for q in range(len(self.queuedFiles)): - if (not self.queuedFiles[q][0] in sendingTo): - #found a file to send - qo = self.queuedFiles[q] - del self.queuedFiles[q] - self.spawnSend(qo[3], qo[0], qo[1], qo[2]) - break - - def updateNick(self, old, new, mask): - for q in range(len(self.queues)): - if self.queues[q][0] == old and self.queues[q][1] == mask: - self.queues[q][0] = new - -class ResumeReqHandler(dcc.ResumeReqHandler): - plugin = None - - def _getSendHandler(self): - return self.plugin.getConnectionByPort(self.port) - - -class AcceptHandler(dcc.DCCReqHandler): - pass - - - -# --- Plugin --- - -class FServe(callbacks.Privmsg): - queues = {} - handlers = {'ChatHandler' : ChatHandler, - 'SendHandler' : SendHandler} - requestHandlers = {'RESUME' : ResumeReqHandler, - 'ACCEPT' : AcceptHandler, - 'SEND' : dcc.SendReqHandler} - - def _getHandlerClass(self, type): - return self.handlers.get(type, None) - - def getConnectionByPort(self, port): - for chan in self.queues: - for q in self.queues[chan]: - qo = self.queues[chan][q] - s = qo.getConnectionByPort(port) - if s: - return s - - - def list(self, irc, msg, args): - """ Show list of queues for this channel """ - channel = privmsgs.getChannel(msg, args) - qs = self.queues[channel].keys() - irc.reply('Known queues: %s' % ' '.join(qs)) - - - def chat(self, irc, msg, args): - """ Start a DCC chat interface (FServe) """ - channel = privmsgs.getChannel(msg, args) - if (len(args) != 1): - irc.reply("Unknown queue.") - return - name = args[0] - if (not self.queues.has_key(channel)): - # Silently ignore as we have no Queues here - return - queue = self.queues[channel].get(name, None) - if (queue): - queue.spawnChat(irc, msg.nick) - - def add(self, irc, msg, args): - """ Add a queue """ - channel = privmsgs.getChannel(msg, args) - name = args[0] - # First add to per channel config - current = self.registryValue('queueNames', channel) - if (name in current): - irc.reply("That queue already exists on this channel.") - else: - current.append(name) - self.setRegistryValue('queueNames', current, channel) - try: - self.registryValue('queues.%s' % name) - except registry.NonExistentRegistryEntry, e: - # Register channel name group - conf.registerGroup(conf.supybot.plugins.FServe.queues, name) - - # And register configs - group = conf.supybot.plugins.FServe.queues.get(name) - conf.registerChannelValue(group, 'timeout', registry.Integer(90, '')) - conf.registerChannelValue(group, 'maxSpeed', registry.Integer(0, '')) - conf.registerChannelValue(group, 'minSpeed', registry.Integer(0, '')) - conf.registerChannelValue(group, 'maxQueues', registry.Integer(5, '')) - conf.registerChannelValue(group, 'instantSendSize', - registry.Integer(50000, '')) - conf.registerChannelValue(group, 'maxSends', registry.Integer(2, '')) - conf.registerChannelValue(group, 'receiveDir', - registry.String('incoming', '')) - conf.registerChannelValue(group, 'sendDir', - registry.String('files', '')) - conf.registerChannelValue(group, 'allowedModes', - registry.SpaceSeparatedListOfStrings([], '')) - conf.registerChannelValue(group, 'allowedMasks', - registry.SpaceSeparatedListOfStrings([], '')) - conf.registerChannelValue(group, 'allowedNicks', - registry.SpaceSeparatedListOfStrings([], '')) - conf.registerChannelValue(group, 'bannedMasks', - registry.SpaceSeparatedListOfStrings([], '')) - conf.registerChannelValue(group, 'bannedNicks', - registry.SpaceSeparatedListOfStrings([], '')) - conf.registerChannelValue(group, 'chatHandler', - registry.String('ChatHandler', '')) - conf.registerChannelValue(group, 'sendHandler', - registry.String('SendHandler', '')) - - self.queues[channel][name] = Queue(self, channel, name) - irc.reply('Created queue named %r' % name) - - add = privmsgs.checkCapability(add, 'owner') - - def remove(self, irc, msg, args): - """ Remove a queue """ - channel = privmsgs.getChannel(msg, args) - name = args[0] - # Remove config only from list of Queues to build - current = self.registryValue('queueNames', channel) - if (name in current): - current.remove(name) - irc.reply('Removed queue named %r' % name) - else: - irc.reply('There is no such queue') - - remove = privmsgs.checkCapability(remove, 'owner') - - def doJoin(self, irc, msg): - """ Maybe build some internal representations of our config """ - channel = msg.args[0] - if (ircutils.nickEqual(msg.nick, irc.nick)): - if (not self.queues.has_key(channel)): - queues = self.registryValue('queueNames', channel) - self.queues[channel] = {} - for q in queues: - self.queues[channel][q] = Queue(self, channel, q) - - def doPrivmsg(self, irc, msg): - """ Maybe respond to DCC request """ - if (ircutils.isCtcp(msg) and dcc.isDCC(msg)): - ctcpArgs = msg.args[1][1:-1].split() - dccType = ctcpArgs[1] - dccArgs = ctcpArgs[2:] - - parent = self.requestHandlers[dccType] - handler = parent(irc, msg, dccArgs) - handler.plugin = self - handler.start() - - - def doNick(self, irc, msg): - """ Let queues know that nick has changed """ - newNick = msg.args[0] - oldNick = msg.nick - hostmask = irc.state.nickToHostmask(msg.nick) - for chan in self.queues: - for queue in self.queues[chan]: - self.queues[chan][queue].updateNick(oldNick, newNick, hostmask) - -Class = FServe -# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: diff --git a/src/dcc.py b/src/dcc.py deleted file mode 100644 index 5169d47ac..000000000 --- a/src/dcc.py +++ /dev/null @@ -1,377 +0,0 @@ -#!/usr/bin/env python - -# Copyright (c) Robert Sanderson All rights reserved. -# See LICENCE file in this distribution for details. - -# Documentation on DCC specification -# http://www.mirc.co.uk/help/dccresum.txt -# www.irchelp.org/irchelp/rfc/dccspec.html -# Also several other lesser known/implemented/used commands are floating around -# but not implemented in major clients. - -import os -import time -import struct - -import log -import conf -import utils -import world -import socket -import ircmsgs -import ircutils -import threading - -def isDCC(msg): - return msg.command == 'PRIVMSG' and msg.args[1][:5] == '\x01DCC ' - - -conf.registerGroup(conf.supybot.protocols, 'dcc') -conf.registerGlobalValue(conf.supybot.protocols.dcc, 'timeout', - registry.Integer(120, "Timeout on DCC sockets")) -conf.registerGlobalValue(conf.supybot.protocols.dcc, 'packetSize', - registry.Integer(1024, "Size of packet to send")) -conf.registerGlobalValue(conf.supybot.protocols.dcc, 'chatLineLength', - registry.Integer(1024, "Max size of line to read")) - -# ---- Out Handlers ---- - -class DCCHandler: - def __init__(self, irc, nick, hostmask, logger=None): - self.irc = irc - self.nick = nick - self.hostmask = hostmask - self.sock = None - if logger: - self.log = logger - else: - self.log = log - - self.timeout = conf.supybot.protocols.dcc.timeout() - - def start(self): - t = threading.Thread(target=self.open) - world.threadsSpawned += 1 - t.setDaemon(True) - t.start() - - def open(self): - # Override in subclasses to do something - pass - - def clientConnected(self): - # Override in subclasses to do something when a client connects - pass - - def clientClosed(self): - # Override to do something when a client closes connection - pass - - -class SendHandler(DCCHandler): - """ Handle sending a file """ - def __init__(self, irc, nick, hostmask, filename, logger=None, start=0): - DCCHandler.__init__(self, irc, nick, hostmask, logger) - self.filename = filename - self.startPosition = start - self.currentPosition = 0 - self.filesize = 0 - self.currentSpeed = 0 - self.bytesSent = 0 - - def packetSent(self): - pass - - def registerPort(self): - pass - - def open(self): - ip = conf.supybot.externalIP() - try: - self.filesize = os.path.getsize(self.filename) - except OSError, e: - self.log.warning('Requested file does not exist: %r', - self.filename) - return - sock = utils.getSocket(ip) - try: - sock.bind((ip, 0)) - except socket.error, e: - self.log.warning('Could not bind a socket to send.') - return - port = sock.getsockname()[1] - self.port = port - self.registerPort() - i = ircutils.dccIP(ip) - sock.listen(1) - msg = ircmsgs.dcc(self.nick, 'SEND', self.filename, i, port, self.filesize) - self.irc.queueMsg(msg) - - # Wait for possible RESUME request to be handled which may change - # self.startPosition on self - # See Resume doc (URL in header) - time.sleep(1) - - try: - (realSock, addr) = sock.accept() - except socket.error, e: - sock.close() - self.log.info('%r: Send init errored with %s', - self.filename, utils.exnToString(e)) - return - self.log.info('%r: Sending to %s', self.filename, self.nick) - - self.sock = realSock - fh = file(self.filename) - try: - self.clientConnected() - self.startTime = time.time() - packetSize = conf.supybot.protocols.dcc.packetSize() - try: - slow = 0 - fh.seek(self.startPosition) - self.currentPosition = fh.tell() - while self.currentPosition < self.filesize: - data = fh.read(min(packetSize, self.filesize - \ - self.currentPosition)) - self.sock.send(data) - self.currentPosition = fh.tell() - self.bytesSent += 1024 - self.packetSent() - except socket.error, e: - exn = utils.exnToString(e) - self.log.info('%r: Send errored with %s', self.filename, exn) - except SendException, e: - exn = utils.exnToString(e) - self.log.info('%r: Send aborted with %s', self.filename, exn) - - self.endTime = time.time() - duration = self.endTime - self.startTime - self.log.info('%r: Sent %s/%s', self.filename, fh.tell(), - self.filesize) - # Sleep to allow client to finish reading data. - # This is needed as we'll be here immediately after the final - # packet - time.sleep(1.0) - self.clientClosed() - finally: - # Ensure that we're not leaking handles - fh.close() - self.sock.close() - - -class ChatHandler(DCCHandler): - """ Handle a DCC chat (initiate) """ - def lineReceived(self, line): - # Override in subclasses to process a line of text - pass - - def open(self): - ip = conf.supybot.externalIP() - sock = utils.getSocket(ip) - lineLength = conf.supybot.protocols.dcc.chatLineLength() - try: - sock.bind((ip, 0)) - except socket.error, e: - self.log.error('Could not bind chat socket.') - return - port = sock.getsockname()[1] - i = ircutils.dccIP(ip) - sock.listen(1) - msg = ircmsgs.dcc(self.nick, 'CHAT', 'chat', i, port) - self.irc.queueMsg(msg) - try: - (realSock, addr) = sock.accept() - except socket.timeout: - sock.close() - self.log.info('CHAT to %s timed out', self.nick) - return - self.log.info('CHAT accepted from %s', self.nick) - realSock.settimeout(self.timeout) - self.startTime = time.time() - self.sock = realSock - try: - self.clientConnected() - while 1: - line = realSock.recv(lineLength) - if line != "\n": - self.lineReceived(line) - except socket.error, e: - self.log.info('CHAT ended with %s', self.nick) - finally: - self.sock.close() - self.endTime = time.time() - self.clientClosed() - - -# ---- In Handlers ---- - - -class DCCReqHandler: - def __init__(self, irc, msg, args, logger=None): - self.irc = irc - self.msg = msg - self.args = args - if logger: - self.log = logger - else: - self.log = log - - def start(self): - t = threading.Thread(target=self.open) - world.threadsSpawned += 1 - t.setDaemon(True) - t.start() - - def clientConnected(self): - pass - - def clientClosed(self): - pass - - -class SendReqHandler(DCCReqHandler): - """ We're being sent a file """ - - def __init__(self, *args, **kw): - DCCReqHandler.__init__(self, *args, **kw) - # This should be added to by subclasses - self.incomingDir = conf.supybot.directories.data() - self.filename = self.args[0] - self.ip = ircutils.unDccIP(int(self.args[1])) - self.port = int(self.args[2]) - self.filesize = int(self.args[3]) - self.filemode = 'w' - - - def receivedPacket(self): - # Override in subclass to do something with each packet received - pass - - def open(self): - if (os.path.exists(self.filename)): - currsize = os.path.getsize(self.filename) - if (self.filesize > currsize): - # Send RESUME DCC message and wait for ACCEPT - # See AcceptReqHandler below - msg = ircutils.dcc(self.nick, 'RESUME', self.filename, - self.port, currsize) - self.irc.queueMsg(msg) - time.sleep(1) - if self.filemode != 'a': - # Didn't get an acknowledge for the RESUME - # Zero file and read from scratch - os.remove(self.filename) - - sock = utils.getSocket(self.ip) - try: - sock.connect((self.ip, self.port)) - except socket.error, e: - self.log.info('File receive could not connect') - return - - self.clientConnected() - rootedName = os.path.abspath(os.path.join(self.incomingDir, - self.filename)) - if not rootedName.startswith(self.incomingDir): - self.log.warning('%s tried to send relative file', self.msg.nick) - return - - fh = file(rootedName, self.filemode) - self.bytesReceived = 0 - self.startTime = time.time() - pktSize = conf.supybot.protocols.dcc.packetSize() - self.log.info('%r: Send starting from %s', self.filename, - self.msg.nick)) - try: - while self.bytesReceived < self.filesize: - amnt = min(self.filesize - self.bytesReceived, pktSize) - d = sock.recv(amnt) - self.bytesReceived += len(d) - # Required to send back packed integer to acknowledge receive - sock.send(struct.pack("!I", self.bytesReceived)) - f.write(d) - self.receivedPacket() - except socket.error, e: - exn = utils.exnToString(e) - self.log.info('%r: Send died with %s', filename, exn) - finally: - self.endTime = time.time() - sock.close() - f.close() - self.log.info('%r: Received %s/%s in %d seconds', - self.filename, self.bytesReceived, self.filesize, - self.endTime - self.startTime) - self.clientClosed() - - -class ResumeReqHandler(DCCReqHandler): - - def _getSendHandler(self): - # This will work in theory, BUT note well, if you instantiate - # and do not override this to return the REAL SendHandler - # the client may still get the original startPosition of 0 - # See RESUME documentation URL in header - hostmask = self.irc.state.nickToHostmask(self.msg.nick) - h = SendHandler(self.irc, self.msg.nick, hostmask, self.filename, - start=self.startPosition) - return h - - def open(self): - # filename is (apparently) ignored by mIRC - # so don't depend on it. - self.filename = self.args[0] - self.port = int(self.args[1]) - self.startPosition = int(self.args[2]) - - msg = ircutils.dcc(self.msg.nick, "ACCEPT", self.filename, self.port, - self.startPosition) - self.irc.queueMsg(msg) - cxn = self._getSendHandler() - cxn.startPosition = self.startPosition - self.log.info('%r: RESUME received for %s', self.filename, - self.startPosition) - -class AcceptReqHandler(DCCReqHandler): - - def _getReceiveHandler(self): - # We need the original SendReqHandler, which needs some cross request - # logic that we don't provide. - # The following may work, but this should be overridden - h = SendReqHandler(self.irc, self.msg, self.args) - return h - - def open(self): - self.filename = self.args[0] - self.port = int(self.args[1]) - cxn = self._getReceiveHandler() - cxn.filemode = 'a' - self.log.info('%r: Got ACCEPT to resume file', self.filename) - - -class ChatReqHandler(DCCReqHandler): - - def open(self): - ip = ircutils.unDccIP(int(self.args[1])) - port = int(self.args[2]) - lineLength = conf.supybot.protocols.dcc.chatLineLength() - - sock = utils.getSocket(ip) - try: - sock.connect((ip, port)) - except: - self.log.error('Could not connect to chat socket.') - return - self.sock = sock - sock.send('\n') - try: - while 1: - line = sock.recv(lineLength) - self.lineReceived(line) - except socket.error, e: - self.log.info('Chat finished') - finally: - sock.close() - - - diff --git a/test/test_dcc.py b/test/test_dcc.py deleted file mode 100644 index fb004f0d5..000000000 --- a/test/test_dcc.py +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env python - -### -# Copyright (c) 2002, Jeremiah Fincher -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions, and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions, and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of the author of this software nor the name of -# contributors to this software may be used to endorse or promote products -# derived from this software without specific prior written consent. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -### - - -from testsupport import * - -import supybot.dcc as dcc -import supybot.ircmsgs as ircmsgs - -class FunctionsTestCase(SupyTestCase): - def testIsDCC(self): - self.failIf(dcc.isDCC(ircmsgs.privmsg('#supybot', '?DCC'))) - self.failIf(dcc.isDCC(ircmsgs.topic('#supybot', '?DCC'))) - - -# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: -