We're probably going to use Joel's irclib.py, stripped for its DCC stuff.

This commit is contained in:
Jeremy Fincher 2004-07-27 23:55:05 +00:00
parent 35bd48b9e8
commit 65c8b48d40
3 changed files with 0 additions and 1018 deletions

View File

@ -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 <queue position>\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:

View File

@ -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()

View File

@ -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: