home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2009 June / maximum-cd-2009-06.iso / DiscContents / digsby_setup.exe / lib / oscar / rendezvous / filetransfer.pyo (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2009-02-26  |  20.9 KB  |  543 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyo (Python 2.5)
  3.  
  4. from __future__ import with_statement
  5. __metaclass__ = type
  6. import struct
  7. import socket
  8. import time
  9. from struct import unpack, pack
  10. from pprint import pformat, pprint
  11. from util import Storage, enum, strlist, autoassign, lookup_table, removedupes, myip, FileChunker, to_hex
  12. from util.packable import Packable
  13. from path import path
  14. import util
  15. import oscar
  16. import oscar.snac as oscar
  17. import oscar.OscarUtil as oscar
  18. from oscar.rendezvous.peer import OscarPeer, ipstr
  19. from oscar.rendezvous.rendezvous import oscarcookie, map_intarg, rendezvous_tlvs
  20. import oscar.capabilities as capabilities
  21. import os
  22. import os.path as os
  23. import traceback
  24. import common
  25. from logging import getLogger
  26. log = getLogger('oscar.rz.ft')
  27. info = log.info
  28.  
  29. def prettyoft(oft):
  30.     return '\n'.join((lambda .0: for line in .0:
  31. '%20s: %r' % line)(list(iter(oft))))
  32.  
  33. rz = rendezvous_tlvs
  34. tlv = oscar.OscarUtil.tlv
  35. tlv_list = oscar.OscarUtil.tlv_list
  36.  
  37. def send(oscar, screenname, filestorage):
  38.     cookie = oscarcookie()
  39.     oscar.rdv_sessions[cookie] = transfer = OutgoingOscarFileTransfer(oscar, screenname, cookie, filestorage)
  40.     transfer.request()
  41.     return transfer
  42.  
  43.  
  44. class OFTHeader(Packable):
  45.     fmt = strlist('\n        protocol_version 4s  # Always \'OFT2\'\n        length           H   # includes all data, including version and length\n        type             H   # one of "types" below\n    ')
  46.     invars = [
  47.         (lambda self: self.protocol_version == 'OFT2'),
  48.         (lambda self: self.type in self.types.values())]
  49.     types = Storage(prompt = 257, ack = 514, done = 516, receiver_resume = 517, sender_resume = 262, rcv_resume_ack = 519)
  50.  
  51.  
  52. class OFTBody(Packable):
  53.     fmt = strlist("\n    cookie              Q\n    encryption          H\n    compression         H\n    num_files           H\n    files_left          H\n    num_parts           H\n    parts_left          H\n    total_size          I\n    file_size           I\n\n    modification_time   I   # since unix epoch\n    checksum            I   # see OscarFileTransferChecksum\n    recv_fork_checksum  I\n    fork_size           I\n    creation_time       I\n    fork_checksum       I\n    bytes_received      I\n    recv_checksum       I\n    id_string           32s # 32 byte right padded string: usually 'CoolFileXfer'\n\n    flags               B   # Flags: 0x20 - Negotiation (not complete), 0x01 - Done\n    list_name_offset    B   # always 0x1c\n    list_size_offset    B   # always 0x11\n\n    dummy_block         69s # Dummy Block - large null block for future expansion of OFT\n\n    mac_file_info       16s # Mac File Info\n\n    charset             H   # charset\n    subset              H   # subset: 0 for ASCII, 2 for UTF-16BE, 3 for ISO-8859-1\n    ")
  54.     default_checksum = 0xFFFF0000L
  55.     
  56.     def padfilename(filename):
  57.         if len(filename) < 64:
  58.             filename += '\x00' * (64 - len(filename))
  59.         
  60.         return filename
  61.  
  62.     padfilename = staticmethod(padfilename)
  63.  
  64. OFTId = 'Cool FileXfer'
  65. OFTId = OFTId + '\x00' * (32 - len(OFTId))
  66.  
  67. class OscarFileTransfer(OscarPeer):
  68.     
  69.     def __init__(self, protocol, screenname, cookie):
  70.         OscarPeer.__init__(self, protocol, screenname, cookie, capabilities.by_name['file_xfer'])
  71.         common.FileTransfer.__init__(self)
  72.         self.cancelled = False
  73.         self.resuming = False
  74.         self.on_get_buddy(self.buddy)
  75.  
  76.     
  77.     def ch2cancel(self, data):
  78.         log.info('%r cancelled by buddy', self)
  79.         self.cancelled = True
  80.         self.cancel_by_buddy()
  81.         if data:
  82.             log.info('data in a ch2 cancel: %s', to_hex(data))
  83.         
  84.  
  85.     
  86.     def cancel_by_buddy(self):
  87.         log.info('cancel_by_buddy')
  88.         if self.state != self.states.CANCELLED_BY_YOU:
  89.             self.state = self.states.CANCELLED_BY_BUDDY
  90.         
  91.         
  92.         try:
  93.             self.fileobj.close()
  94.         except Exception:
  95.             import traceback
  96.             traceback.print_exc()
  97.  
  98.  
  99.     
  100.     def on_close(self):
  101.         log.info('%r on_close', self)
  102.         
  103.         try:
  104.             self.fileobj.close()
  105.         except AttributeError:
  106.             pass
  107.  
  108.         if self.state == self.states.TRANSFERRING:
  109.             self.state = self.states.CONN_FAIL_XFER
  110.             self.on_error()
  111.         
  112.  
  113.     
  114.     def close(self):
  115.         log.info('close')
  116.         
  117.         try:
  118.             self.socket.close()
  119.             del self.socket
  120.         except Exception:
  121.             pass
  122.  
  123.         self.done = True
  124.         
  125.         try:
  126.             self.fileobj.close()
  127.         except AttributeError:
  128.             pass
  129.  
  130.  
  131.     
  132.     def cancel(self, msg = '', state = None):
  133.         if hasattr(self, 'socket') and self.socket:
  134.             
  135.             try:
  136.                 self.socket.cancel_timeout()
  137.             except Exception:
  138.                 traceback.print_exc()
  139.             except:
  140.                 None<EXCEPTION MATCH>Exception
  141.             
  142.  
  143.         None<EXCEPTION MATCH>Exception
  144.         
  145.         try:
  146.             self.filechunker.cancelled = True
  147.         except Exception:
  148.             traceback.print_exc()
  149.             info('$$$ sending cancel')
  150.             self.send_rdv('cancel')
  151.  
  152.         self.close()
  153.         self.setnotifyif('cancelled', True)
  154.         info('%r cancelled. %s', self, msg)
  155.         if state is None:
  156.             state = self.states.CANCELLED_BY_YOU
  157.         
  158.         self.state = state
  159.  
  160.     
  161.     def ch2accept(self, data):
  162.         if data:
  163.             log.error('got data in a channel 2 accept: data = %r', data)
  164.             self.cancel()
  165.         else:
  166.             log.info('ch2accept data = %r', data)
  167.  
  168.     
  169.     def decline_transfer(self, reason = None):
  170.         log.info('decline_transfer: reason = %r', reason)
  171.         self.cancel()
  172.  
  173.     
  174.     def send_oft(self, type, setvalues = True):
  175.         oft = self.oft
  176.         if not oft:
  177.             return None
  178.         
  179.         if setvalues:
  180.             oft.id_string = OFTId
  181.             oft.cookie = self.cookie
  182.             oft.list_name_offset = 28
  183.             oft.list_size_offset = 17
  184.             oft.flags = None if 'type' == 'done' else 32
  185.             oft.checksum = OFTBody.default_checksum
  186.         
  187.         info('sending oft %s for %s', type, self.filename)
  188.         info(prettyoft(oft) + '\n  filename: %s', self.filename)
  189.         self.socket.push(oftpacket(type, oft, self.filename))
  190.  
  191.  
  192.  
  193. class OutgoingOscarFileTransfer(OscarFileTransfer, common.OutgoingFileTransfer):
  194.     
  195.     def __init__(self, o, screenname, cookie, fileinfo):
  196.         OscarFileTransfer.__init__(self, o, screenname, cookie)
  197.         [ setattr(self, a, fileinfo[a]) for a in 'name files size numfiles'.split() ]
  198.         self.filepath = path(fileinfo.path)
  199.         self.fileobj = None
  200.         self.oft = self.next_file()
  201.         self.accepted = False
  202.         self.connected = False
  203.         self.completed = 0
  204.         self.state = self.states.WAITING_FOR_BUDDY
  205.  
  206.     
  207.     def next_file(self):
  208.         if self.files:
  209.             self.file = self.files.pop(0)
  210.             if hasattr(self, 'rootpath'):
  211.                 p = self.rootpath.relpathto(self.file)
  212.                 self.filename = p.normpath().replace('\\', '/').replace('/', chr(1))
  213.             else:
  214.                 self.filename = self.file.name
  215.             return self.oft_for_file(self.file)
  216.         else:
  217.             self.file = None
  218.             return False
  219.  
  220.     
  221.     def __repr__(self):
  222.         if not getattr(self.file, 'name', None):
  223.             pass
  224.         return '<OutgoingOscarFileTransfer to %s (%r)>' % (self.screenname, '')
  225.  
  226.     
  227.     def request(self, message = '<html>'):
  228.         xdata = xdata_block(self.name, self.size, self.numfiles)
  229.         self.establish_out_dc(message = message, extratlvs = [
  230.             (rz.extended_data, xdata),
  231.             (rz.filename_encoding, 'utf-8')])
  232.  
  233.     
  234.     def on_odc_connection(self):
  235.         info('%r connected!', self)
  236.         self.connected = True
  237.         self.maybe_start()
  238.  
  239.     
  240.     def received_oft_header(self, data):
  241.         (header, data) = OFTHeader.unpack(data)
  242.         log.debug('received OFT header: oft=%(protocol_version)s length=0x%(length)x type=0x%(type)x', dict(header))
  243.         bytes_left = header.length - OFTHeader._struct.size
  244.         if header.type in (OFTHeader.types.ack, OFTHeader.types.rcv_resume_ack):
  245.             self.open_file()
  246.         elif header.type == OFTHeader.types.done:
  247.             
  248.             try:
  249.                 self.fileobj.close()
  250.             except AttributeError:
  251.                 pass
  252.  
  253.             self.fileobj = None
  254.             self.oft = self.next_file()
  255.             if not self.oft:
  256.                 if self.completed:
  257.                     self.state = self.states.FINISHED
  258.                     self._ondone()
  259.                 else:
  260.                     self.cancel_by_buddy()
  261.                 self.close()
  262.             
  263.             return None
  264.         elif header.type == OFTHeader.types.receiver_resume:
  265.             log.info('Going to resume file %s' % self.file)
  266.             self.resuming = True
  267.         else:
  268.             log.warning('Error! OFT type %r', header.type)
  269.         self.socket.receive_next(bytes_left, self.received_oft_body)
  270.  
  271.     
  272.     def open_file(self):
  273.         
  274.         try:
  275.             self.fileobj = self.file.open('rb')
  276.             return True
  277.         except IOError:
  278.             self.cancel('Could not open file %s' % self.file)
  279.             return False
  280.  
  281.  
  282.     
  283.     def received_oft_body(self, data):
  284.         (oft, data) = OFTBody.unpack(data)
  285.         (filename, data) = read_cstring(data)
  286.         info(prettyoft(oft) + '\n  filename: %s', filename)
  287.         self.oft = oft
  288.         self.socket.receive_next(OFTHeader, self.received_oft_header)
  289.         if self.fileobj:
  290.             if self.resuming:
  291.                 self.fileobj.seek(oft.bytes_received)
  292.                 self._setcompleted(self.fileobj.tell())
  293.             
  294.             self.filechunker = FileChunker(self.fileobj, close_when_done = True, progress_cb = self._setcompleted)
  295.             log.info('Pushing FileChunker onto socket')
  296.             self.socket.push_with_producer(self.filechunker)
  297.         elif self.oft:
  298.             if self.resuming:
  299.                 self.send_oft('sender_resume', False)
  300.             else:
  301.                 self.send_oft('prompt')
  302.         else:
  303.             info('DONE!')
  304.             self.state = self.states.FINISHED
  305.             self.close()
  306.  
  307.     
  308.     def ch2accept(self, data):
  309.         info('received CH2 accept')
  310.         self.accepted = True
  311.         self.maybe_start()
  312.  
  313.     
  314.     def maybe_start(self):
  315.         if not (self.accepted) or not (self.connected):
  316.             if not self.accepted:
  317.                 info('no RDV accept yet')
  318.             
  319.             if not self.connected:
  320.                 info('no connection yet')
  321.             
  322.             return None
  323.         
  324.         self.state = self.states.TRANSFERRING
  325.         if not getattr(self, 'sent_first', False):
  326.             info('sending first oft prompt')
  327.             self.sent_first = True
  328.             self.send_oft('prompt')
  329.         
  330.         self.socket.receive_next(OFTHeader, self.received_oft_header)
  331.  
  332.     
  333.     def oft_for_file(self, file):
  334.         return OFTBody(cookie = self.cookie, num_files = self.numfiles, files_left = 1 + len(self.files), num_parts = 1, parts_left = 1, total_size = self.size, file_size = file.size, modification_time = int(file.mtime), creation_time = int(file.ctime), checksum = OFTBody.default_checksum, recv_fork_checksum = OFTBody.default_checksum, fork_checksum = OFTBody.default_checksum, recv_checksum = OFTBody.default_checksum, id_string = OFTId, dummy_block = '\x00' * 69, mac_file_info = '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', charset = 0, subset = 0)
  335.  
  336.  
  337.  
  338. class IncomingOscarFileTransfer(OscarFileTransfer, common.IncomingFileTransfer):
  339.     direction = 'incoming'
  340.     
  341.     def __init__(self, o, screenname, cookie):
  342.         OscarFileTransfer.__init__(self, o, screenname, cookie)
  343.         self.completed = 0
  344.  
  345.     
  346.     def handle_request(self, rendtlvs):
  347.         (info, data) = unpack_extended_data(rendtlvs.extended_data)
  348.         self.__dict__.update(info)
  349.         self._onrequest()
  350.  
  351.     
  352.     def accept(self, file_obj):
  353.         if self.cancelled:
  354.             e = OscarFileTransferError('The sender has already cancelled this file request.')
  355.             self.protocol.hub.on_error(e)
  356.         elif isinstance(file_obj, file) or self.numfiles == 1:
  357.             self.filepath = path(file_obj.name)
  358.             self.rootpath = self.filepath.parent
  359.             file_obj.close()
  360.         else:
  361.             self.rootpath = os.path.join(file_obj, self.name)
  362.             if not os.path.exists(self.rootpath):
  363.                 os.makedirs(self.rootpath)
  364.             
  365.             self.filepath = path(self.rootpath)
  366.         info('self.rootpath = %r', self.rootpath)
  367.         None(info, 'accepting incoming file transfer, saving %s to %s' if self.numfiles == 1 else 'files', self.rootpath)
  368.         self.state = self.states.CONNECTING
  369.         self.establish_dc()
  370.  
  371.     
  372.     def decline(self):
  373.         self.state = self.states.CANCELLED_BY_YOU
  374.         self.send_rdv('cancel')
  375.         common.IncomingFileTransfer.decline(self)
  376.  
  377.     
  378.     def on_odc_connection(self):
  379.         self.state = self.states.TRANSFERRING
  380.         self.socket.receive_next(OFTHeader, self.received_oft_header)
  381.  
  382.     
  383.     def received_oft_header(self, data):
  384.         (header, data) = OFTHeader.unpack(data)
  385.         log.debug('received OFT header: %r', dict(header))
  386.         if header.type == OFTHeader.types.prompt:
  387.             bytes_left = header.length - OFTHeader._struct.size
  388.             log.debug('receiving %d more OFT body bytes', bytes_left)
  389.             self.socket.receive_next(bytes_left, self.received_oft_body)
  390.         else:
  391.             self.fail('Error! OFT type ' + str(header.type))
  392.  
  393.     
  394.     def received_oft_body(self, data):
  395.         (oft, data) = OFTBody.unpack(data)
  396.         self.oft = oft
  397.         info('incoming oft body...\n' + prettyoft(oft))
  398.         nullindex = data.find('\x00')
  399.         if nullindex == -1:
  400.             raise AssertionError("couldn't find a null byte in the padded filename")
  401.         
  402.         self.filename = filename = data[:nullindex]
  403.         self.fileobj = openpath(self.rootpath, self.filepath.name)
  404.         info('incoming file: %s (%d bytes), %d left', filename, oft.file_size, oft.files_left)
  405.         self.send_oft('ack')
  406.         self.socket.push_collector(self.collect_file_bytes)
  407.         if oft.file_size > 0:
  408.             self.socket.receive_next(oft.file_size, self.received_file)
  409.         else:
  410.             self.received_file()
  411.  
  412.     
  413.     def collect_file_bytes(self, data):
  414.         
  415.         try:
  416.             self.fileobj.write(data)
  417.         except IOError:
  418.             import traceback
  419.             traceback.print_exc()
  420.             return None
  421.         else:
  422.             self._setcompleted(self.fileobj.tell())
  423.         finally:
  424.             self.data = ''
  425.  
  426.  
  427.     
  428.     def received_file(self, *data):
  429.         self.socket.pop_collector()
  430.         info('received file %s', self.fileobj.name)
  431.         self.fileobj.close()
  432.         self.send_oft('done')
  433.         if self.oft.files_left == 1:
  434.             info('done!')
  435.             self.socket.close()
  436.             self._ondone()
  437.         else:
  438.             self.socket.receive_next(OFTHeader, self.received_oft_header)
  439.  
  440.     
  441.     def fail(self, msg):
  442.         log.error(msg)
  443.         self.close()
  444.  
  445.     
  446.     def __repr__(self):
  447.         return '<IncomingOFT from %s>' % self.screenname
  448.  
  449.  
  450.  
  451. def oftpacket(type, body, filename):
  452.     if isinstance(filename, unicode):
  453.         filename = filename.encode('utf8')
  454.     else:
  455.         filename = str(filename)
  456.     padded = OFTBody.padfilename(filename)
  457.     return ''.join([
  458.         OFTHeader('OFT2', length = 192 + len(padded), type = map_intarg(type, OFTHeader.types)).pack(),
  459.         body.pack(),
  460.         padded])
  461.  
  462.  
  463. def oft_filename(fn):
  464.     fn += chr(0)
  465.     if len(fn) < 64:
  466.         fn += (64 - len(fn)) * chr(0)
  467.     
  468.     return fn
  469.  
  470.  
  471. def read_cstring(data):
  472.     i = data.find(chr(0))
  473.     if i == -1:
  474.         raise ValueError('not a null terminated string')
  475.     
  476.     return (data[:i], data[i + 1:])
  477.  
  478.  
  479. def unpack_extended_data(data):
  480.     (multiple, filecount, totalbytes) = struct.unpack('!HHI', data[:8])
  481.     data = data[8:]
  482.     (filename, data) = read_cstring(data)
  483.     return (util.Storage(numfiles = filecount, multiple = multiple == 2, size = totalbytes, name = filename), data)
  484.  
  485.  
  486. def xdata_block(filename, filesize, filecount = 1):
  487.     return None(struct.pack, '!HHI' if filecount == 1 else 2, filecount, filesize) + filename.encode('utf-8') + '\x00'
  488.  
  489.  
  490. def openpath(rootpath, filename):
  491.     PATHSEP = chr(1)
  492.     needdirs = filename.find(PATHSEP) != -1
  493.     path = filename.split(PATHSEP)
  494.     filename = path.pop(-1)
  495.     path = [
  496.         rootpath] + path
  497.     if needdirs:
  498.         pathstr = os.path.join(*path)
  499.         if not os.path.exists(pathstr):
  500.             info('calling makedirs(%s)', pathstr)
  501.             os.makedirs(pathstr)
  502.         
  503.     
  504.     filepath = path + [
  505.         filename]
  506.     return open(os.path.join(*filepath), 'wb')
  507.  
  508.  
  509. class OscarFileTransferError(Exception):
  510.     pass
  511.  
  512.  
  513. class OFTChecksum(object):
  514.     starting_value = 0xFFFF0000L
  515.     
  516.     def __init__(self, data = None):
  517.         self.checksum = self.starting_value
  518.         if data is not None:
  519.             self.update(data)
  520.         
  521.  
  522.     
  523.     def update(self, data, offset = 0):
  524.         check = self.checksum >> 16 & 0xFFFFL
  525.         for i in xrange(len(data)):
  526.             oldcheck = check
  527.             byteval = ord(data[offset + i]) & 255
  528.             None -= check if i & 1 != 0 else byteval << 8
  529.             if check > oldcheck:
  530.                 check -= 1
  531.                 continue
  532.         
  533.         check = (check & 65535) + (check >> 16)
  534.         check = (check & 65535) + (check >> 16)
  535.         checksum = check << 16 & 0xFFFFFFFFL
  536.  
  537.  
  538. if __name__ == '__main__':
  539.     p = 'c:\\users\\kevin\\desktop\\apple_evolution.jpg'
  540.     bytes = open(p, 'rb').read()
  541.     print OFTChecksum(bytes).checksum
  542.  
  543.