[meego-commits] 6136: Changes to Trunk/papyon

Peter Zhu peter.j.zhu at intel.com
Fri Jul 30 11:55:09 UTC 2010


Hi,
I have made the following changes to papyon in project Trunk. Please review and accept ASAP.

Thank You,
Peter Zhu

[This message was auto-generated]

---

Request #6136:

  submit:   Trunk:Testing/papyon(r3) -> Trunk/papyon


Message:
    Move to Trunk

State:   new          2010-07-29T23:48:03 peter
Comment: None



changes files:
--------------
--- papyon.changes
+++ papyon.changes
@@ -0,0 +1,3 @@
+* Wed Jul 28 2010 - Zhang Qiang <qiang.z.zhang at intel.com> - 0.4.9
+- upgrade to 0.4.9, and add dependency of python-crypto.
+

old:
----
  papyon-0.4.4.tar.gz

new:
----
  Makefile
  papyon-0.4.9.tar.gz
  papyon.yaml

spec files:
-----------
--- papyon.spec
+++ papyon.spec
@@ -1,25 +1,27 @@
-%{!?python_sitelib: %global python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")}
+# 
+# Do not Edit! Generated by:
+# spectacle version 0.18
+# 
+# >> macros
+# << macros
 
+%{!?python_sitelib: %define python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")}
 Name:           papyon
-Version:        0.4.4
-Release:        1
 Summary:        Python libraries for MSN Messenger network
-
+Version:    0.4.9
+Release:    1
 Group:          System/Libraries
 License:        GPLv2+
+BuildArch:  noarch
 URL:            http://telepathy.freedesktop.org/wiki/Papyon
 Source0:        http://telepathy.freedesktop.org/releases/%{name}/%{name}-%{version}.tar.gz
-BuildRoot:      %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
-
-BuildArch:      noarch
-
-BuildRequires:	pyOpenSSL
-BuildRequires:  python-devel
-BuildRequires:	pygobject2
-
+Source100:  papyon.yaml
 Requires:	pyOpenSSL
 Requires:	pygobject2
-
+BuildRequires:  pyOpenSSL >= 0.6
+BuildRequires:  python-devel
+BuildRequires:  pygobject2
+BuildRequires:  python-crypto
 Provides:	pymsn = 0.3.4-1
 Obsoletes:	pymsn < 0.3.4
 
@@ -30,25 +32,43 @@
 asynchronous manner
 
 
+
+
 %prep
-%setup -q
-%{__sed} -i 's|\#!/usr/bin/env python||' papyon/msnp2p/test.py
+%setup -q -n %{name}-%{version}
 
+# >> setup
+# << setup
 
 %build
-%{__python} setup.py build
+# >> build pre
+# << build pre
 
+CFLAGS="$RPM_OPT_FLAGS" %{__python} setup.py build
 
+# >> build post
+# << build post
 %install
-rm -rf $RPM_BUILD_ROOT
-%{__python} setup.py install -O1 --skip-build --root $RPM_BUILD_ROOT
+rm -rf %{buildroot}
+# >> install pre
+# << install pre
+%{__python} setup.py install --root=%{buildroot} -O1
+
+# >> install post
+
+
+# << install post
+
+
 
 
-%clean
-rm -rf $RPM_BUILD_ROOT
 
 
 %files
 %defattr(-,root,root,-)
+# >> files
 %doc COPYING NEWS AUTHORS
 %{python_sitelib}/*
+# << files
+
+

other changes:
--------------

++++++ Makefile (new)
--- Makefile
+++ Makefile
+PKG_NAME := papyon
+SPECFILE = $(addsuffix .spec, $(PKG_NAME))
+YAMLFILE = $(addsuffix .yaml, $(PKG_NAME))
+
+include /usr/share/packaging-tools/Makefile.common
+

++++++ papyon-0.4.4.tar.gz -> papyon-0.4.9.tar.gz
--- NEWS
+++ NEWS
@@ -1,3 +1,52 @@
+papyon-0.4.9 (2010-07-09)
+=========================
+
+Fixes:
+  * Deal with Location and Friendly being missing from MSNObjects (fd.o#28854)
+  * ...and more miscellaneous fixes too specific for here.
+
+Enhancements:
+  * Added file transfer support (switchboard only).
+
+papyon-0.4.8 (2010-05-27)
+=========================
+
+The "Purple Python" release
+
+Fixes:
+  * Fix contact not displaying because of a bad function name (fd.o #28278)
+
+papyon-0.4.7 (2010-05-20)
+=========================
+
+The "Red Wire" release
+
+Fixes:
+  * Deal with bad SHA1{C,D} in MSNObjects and log the failures. (fd.o#24138)
+  * Use python-crypto instead of an embedded copy of pyDes. (fd.o#26638)
+  * Signal an error in IOChannel if resolving fails. (fd.o#27554)
+  * Don't choke on trailing semicolons in message headers. (fd.o#27556)
+  * Allow receiving gzip-encoded soap responses (fd.o#27673)
+  * Work around invalid SHA1D attributes appearing in MSN objects (fd.o#27672)
+
+papyon-0.4.6 (2010-04-08)
+=========================
+
+The "Blue Sofa" release.
+
+Fixes:
+  * Don't try to reconnect in an infinite loop in HTTP mode (fd.o #27119)
+  * Fix parse error in HTTPMessage (fd.o #26804)
+  * Display presence of contacts using web messenger (fd.o #22553)
+
+papyon-0.4.5 (2010-03-10)
+=========================
+
+The "Beige Curtain" release.
+
+Fixes:
+  * Don't display the password in the logs anymore (fd.o #25014)
+
 papyon-0.4.4 (2010-01-19)
 =========================
 
--- PKG-INFO
+++ PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.0
 Name: papyon
-Version: 0.4.4
+Version: 0.4.9
 Summary: Python msn client library
 Home-page: http://telepathy.freedesktop.org/wiki/Papyon
 Author: Youness Alaoui
--- papyon/__init__.py
+++ papyon/__init__.py
@@ -26,7 +26,7 @@
     @group Network Layer: gnet
 """
 
-version = (0, 4, 4)
+version = (0, 4, 9)
 
 __version__ = ".".join(str(x) for x in version)
 __author__ = "Youness Alaoui <kakaroto at users.sourceforge.net>"
--- papyon/client.py
+++ papyon/client.py
@@ -92,7 +92,7 @@
 from papyon.transport import *
 from papyon.switchboard_manager import SwitchboardManager
 from papyon.msnp2p import P2PSessionManager
-from papyon.p2p import MSNObjectStore, WebcamHandler
+from papyon.p2p import MSNObjectStore, FileTransferManager, WebcamHandler
 from papyon.sip import SIPConnectionManager
 from papyon.conversation import SwitchboardConversation, \
     ExternalNetworkConversation
@@ -150,6 +150,9 @@
         self._msn_object_store = MSNObjectStore(self)
         self._p2p_session_manager.register_handler(self._msn_object_store)
 
+        self._ft_manager = FileTransferManager(self)
+        self._p2p_session_manager.register_handler(self._ft_manager)
+
         self._external_conversations = {}
 
         self._sso = None
@@ -164,6 +167,7 @@
         self.__connect_switchboard_manager_signals()
         self.__connect_webcam_handler_signals()
         self.__connect_call_manager_signals()
+        self.__connect_ft_manager_signals()
 
     ### public:
     @property
@@ -195,6 +199,12 @@
         return self._call_manager
 
     @property
+    def ft_manager(self):
+        """The files transfer manager
+            @type: L{FileTransferManager<papyon.p2p.FileTransferManager>}"""
+        return self._ft_manager
+
+    @property
     def oim_box(self):
         """The offline IM for the current user
             @rtype: L{OfflineIM<papyon.service.OfflineIM>}"""
@@ -497,3 +507,10 @@
             self._dispatch("on_invite_conference", call)
 
         self._call_manager.connect("invite-received", invite_received)
+
+    def __connect_ft_manager_signals(self):
+        """Connect File Transfer Manager signals"""
+        def invite_received(ft_manager, session):
+            self._dispatch("on_invite_file_transfer", session)
+
+        self._ft_manager.connect("transfer-requested", invite_received)
--- papyon/conversation.py
+++ papyon/conversation.py
@@ -227,6 +227,8 @@
 
     def __parse(self, format):
         for property in format.split(';'):
+            if not property:
+                continue
             key, value =  [p.strip(' \t|').upper() \
                     for p in property.split('=', 1)]
             if key == 'FN':
--- papyon/event/invite.py
+++ papyon/event/invite.py
@@ -50,3 +50,9 @@
             @param call: the call
             @type call: L{SIPCall<papyon.sip.sip.SIPCall>}"""
         pass
+
+    def on_invite_file_transfer(self, session):
+        """Called when we get a file transfer request
+            @param session: the file transfer session
+            @type session: L{FileTransferSession<papyon.p2p.FileTransferSession>}"""
+        pass
--- papyon/gnet/io/iochannel.py
+++ papyon/gnet/io/iochannel.py
@@ -96,14 +96,21 @@
             return
         else:
             host = resolve_response.answer[0][1]
-        err = self._transport.connect_ex((host, port))
+
+        # Even though connect_ex *shouldn't* raise an exception,
+        # sometimes it does, which is just great.
+        try:
+            err = self._transport.connect_ex((host, port))
+        except socket.error, e:
+            err = e.errno
+
         self._watch_set_cond(gobject.IO_PRI | gobject.IO_IN | gobject.IO_OUT |
                 gobject.IO_HUP | gobject.IO_ERR | gobject.IO_NVAL,
                 lambda chan, cond: self._post_open())
         if err in (0, EINPROGRESS, EALREADY, EWOULDBLOCK, EISCONN):
             return
         elif err in (EHOSTUNREACH, EHOSTDOWN, ECONNREFUSED, ECONNABORTED,
-                ENETUNREACH, ENETDOWN):
+                ENETUNREACH, ENETDOWN, EBADFD):
             self.emit("error", IoError.CONNECTION_FAILED)
             self._transport.close()
 
--- papyon/gnet/io/sock.py
+++ papyon/gnet/io/sock.py
@@ -83,7 +83,14 @@
         if cond & gobject.IO_OUT:
             if len(self._outgoing_queue) > 0: # send next item
                 item = self._outgoing_queue[0]
-                item.sent(self._channel.write(item.read()))
+
+                # Deal with broken pipe from the socket.
+                try:
+                    item.sent(self._channel.write(item.read()))
+                except gobject.GError:
+                    self.emit("error", IoError.CONNECTION_FAILED)
+                    return True
+
                 if item.is_complete(): # sent item
                     self.emit("sent", item.buffer, item.size)
                     item.callback()
--- papyon/gnet/message/HTTP.py
+++ papyon/gnet/message/HTTP.py
@@ -64,7 +64,7 @@
 
         lines = chunk.split("\r\n")
         for i, line in enumerate(lines):
-            if line.strip() == "":
+            if line.strip() == "" or line == "\x00":
                 self.body = "\r\n".join(lines[i+1:])
                 break
             name, value = line.split(":", 1)
@@ -77,7 +77,10 @@
         #if "Content-Length" not in self.headers:
         #    result.append("Content-Length: %d" % len(body))
         result.append("")
-        result.append(str(self.body))
+        if "Content-Encoding" in self.headers:
+            result.append("<" + self.headers.get('Content-Encoding', '') + " encoded data>")
+        else:
+            result.append(str(self.body))
         return "\r\n".join(result)
 
     def __unicode__(self):
--- papyon/gnet/protocol/HTTP.py
+++ papyon/gnet/protocol/HTTP.py
@@ -68,6 +68,12 @@
         self._outgoing_queue = []
         self._waiting_response = False
 
+        self._errored = False
+        self.connect("error", self._on_self_error)
+
+    def _on_self_error(self, *args):
+        self._errored = True
+
     def _setup_transport(self):
         if self._transport is None:
             if self.__proxy is not None:
@@ -87,7 +93,8 @@
         if transport.get_property("status") == IoStatus.OPEN:
             self._process_queue()
         elif transport.get_property("status") == IoStatus.CLOSED and\
-                (self._waiting_response or len(self._outgoing_queue) > 0):
+                (self._waiting_response or len(self._outgoing_queue) > 0) and\
+                not self._errored:
             self._waiting_response = False
             self._setup_transport()
 
--- papyon/gnet/resolver.py
+++ papyon/gnet/resolver.py
@@ -55,7 +55,11 @@
         self._queries = {}
 
     def query(self, host, callback):
-        result = socket.getaddrinfo(host, None, socket.AF_INET, socket.SOCK_STREAM)
+        try:
+            result = socket.getaddrinfo(host, None, socket.AF_INET, socket.SOCK_STREAM)
+        except socket.gaierror:
+            result = []
+
         if len(result) == 0:
             status = 1
             cname = ''
--- papyon/msnp/mailbox.py
+++ papyon/msnp/mailbox.py
@@ -97,9 +97,10 @@
         self.notify("unread-mail-count")
 
     def _unread_mail_decreased(self, delta):
-        self._unread_mail_count -= delta
-        self.emit("unread-mail-count-changed", self._unread_mail_count, False)
-        self.notify("unread-mail-count")
+        if self._unread_mail_count > 0:
+            self._unread_mail_count -= delta
+            self.emit("unread-mail-count-changed", self._unread_mail_count, False)
+            self.notify("unread-mail-count")
 
     def _initial_set(self, unread_number):
         if unread_number > 0:
--- papyon/msnp/notification.py
+++ papyon/msnp/notification.py
@@ -448,7 +448,7 @@
         msn_object = None
         icon_url = None
         if len(command.arguments) > idx:
-            if command.arguments[idx] != '0':
+            if command.arguments[idx] not in ('0', '1'):
                 msn_object = papyon.p2p.MSNObject.parse(self._client,
                                urllib.unquote(command.arguments[idx]))
         idx += 1
--- papyon/msnp/switchboard.py
+++ papyon/msnp/switchboard.py
@@ -161,7 +161,7 @@
             @param message: the message to send
             @type message: L{message.Message}"""
         assert(self.state == ProtocolState.OPEN)
-        self._send_command('MSG',
+        return self._send_command('MSG',
                 (ack,),
                 message,
                 True,
@@ -260,10 +260,10 @@
         self.emit("message-received", message)
 
     def _handle_ACK(self, command):
-        self.emit("message-delivered", command)
+        self.emit("message-delivered", command.transaction_id)
 
     def _handle_NAK(self, command):
-        self.emit("message-undelivered", command)
+        self.emit("message-undelivered", command.transaction_id)
 
     def _error_handler(self, error):
         """Handles errors
--- papyon/msnp2p/SLP.py
+++ papyon/msnp2p/SLP.py
@@ -208,7 +208,7 @@
     def parse(self, data):
         if len(data) == 0:
             return
-        data.rstrip('\x00')
+        data = data.rstrip('\x00')
         HTTPMessage.parse(self, data)
 
     def __str__(self):
--- papyon/msnp2p/filetransfer.py
+++ papyon/msnp2p/filetransfer.py
+# -*- coding: utf-8 -*-
+#
+# papyon - a python client library for Msn
+#
+# Copyright (C) 2010 Collabora Ltd.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+from papyon.msnp2p.constants import EufGuid
+from papyon.msnp2p.session import P2PSession
+
+import struct
+
+__all__ = ['FileTransferSession']
+
+class FileTransferSession(P2PSession):
+
+    def __init__(self, session_manager, peer, application_id, message=None):
+        P2PSession.__init__(self, session_manager, peer,
+                EufGuid.FILE_TRANSFER, application_id, message)
+        self._filename = ""
+        self._size = 0
+        self._has_preview = False
+        self._preview = None
+        # data to be send if sending
+        self._data = None
+
+        if message is not None:
+            self.parse_context(message.body.context)
+
+    @property
+    def filename(self):
+        return self._filename
+
+    @property
+    def size(self):
+        return self._size
+
+    @property
+    def has_preview(self):
+        return self._has_preview
+
+    @property
+    def preview(self):
+        return self._preview
+
+    def invite(self, filename, size):
+        self._filename = filename
+        self._size = size
+        context = self.build_context()
+        self._invite(context)
+
+    def accept(self):
+        self._respond(200)
+
+    def reject(self):
+        self._respond(603)
+
+    def cancel(self):
+        self._close()
+
+    def send(self, data):
+        self._data = data
+        self._send_p2p_data("\x00" * 4)
+        self._send_p2p_data(self._data, True)
+
+    def parse_context(self, context):
+        info = struct.unpack("<5I", context[0:20])
+        self._size = info[2]
+        self._has_preview = not bool(info[4])
+        self._filename = unicode(context[20:570], "utf-16-le").rstrip("\x00")
+
+    def build_context(self):
+        filename = self._filename.decode('ascii').encode('utf-16_le')
+        context = struct.pack("<5I", 574, 2, self._size, 0, int(self._has_preview))
+        context += struct.pack("550s", filename)
+        context += "\xFF" * 4
+        return context
--- papyon/msnp2p/session.py
+++ papyon/msnp2p/session.py
@@ -224,19 +224,15 @@
                 else:
                     print "Unhandled signaling blob :", message
             elif isinstance(message, SLPResponseMessage):
-                if message.status is 200:
-                    self._on_session_accepted()
-                    self.emit("accepted")
-                elif message.status is 603:
-                    self._on_session_rejected(message)
-                else:
-                    print "Unhandled response blob :", message
+                if isinstance(message.body, SLPSessionRequestBody):
+                    if message.status is 200:
+                        self._on_session_accepted()
+                        self.emit("accepted")
+                    elif message.status is 603:
+                        self._on_session_rejected(message)
             return
 
-        if blob.total_size == 4 and data == ('\x00' * 4):
-            self._on_data_preparation_blob_received(blob)
-        else:
-            self._on_data_blob_received(blob)
+        self._on_data_blob_received(blob)
 
     def _on_data_chunk_transferred(self, chunk):
         if chunk.has_progressed():
--- papyon/msnp2p/transport/TLP.py
+++ papyon/msnp2p/transport/TLP.py
@@ -127,6 +127,11 @@
     def is_nonce_chunk(self):
         return self.header.flags & TLPFlag.KEY
 
+    def is_data_preparation_chunk(self):
+        return (self.header.chunk_size == 4 and self.header.blob_size == 4 and
+                self.body == "\x00\x00\x00\x00" and
+                not self.header.flags & TLPFlag.FILE)
+
     def has_progressed(self):
         return self.header.flags & TLPFlag.EACH
 
@@ -265,7 +270,7 @@
         header.chunk_size = len(data)
         header.dw1 = _chunk_id()
         if self.session_id != 0 and self.total_size != 4 and data != '\x00' * 4:
-            header.flags = TLPFlag.EACH
+            header.flags = TLPFlag.UNKNOWN | TLPFlag.EACH
             if self.is_file:
                 header.flags |= TLPFlag.FILE
 
--- papyon/msnp2p/transport/base.py
+++ papyon/msnp2p/transport/base.py
@@ -82,7 +82,7 @@
     def _reset(self):
         self._control_blob_queue = []
         self._data_blob_queue = []
-        self._pending_blob = {} # blob_id : (blob, callback, errback)
+        self._pending_blob = {} # last_chunk : (blob, callback, errback)
         self._pending_ack = {} # blob_id : [blob_offset1, blob_offset2 ...]
 
     def _add_pending_ack(self, blob_id, chunk_id=0):
@@ -119,6 +119,10 @@
 
     def _on_chunk_sent(self, chunk):
         self.emit("chunk-sent", chunk)
+        if chunk in self._pending_blob:
+            blob, callback, errback = self._pending_blob.pop(chunk)
+            if callback:
+                callback[0](*callback[1:])
         self._process_send_queues()
 
     def _process_send_queues(self):
@@ -133,10 +137,7 @@
         chunk = blob.get_chunk(self.max_chunk_size)
         if blob.is_complete():
             queue.pop(0)
-            if blob.is_data_blob():
-                self._pending_blob[blob.id] = (blob, callback, errback)
-            elif callback:
-                callback[0](*callback[1:])
+            self._pending_blob[chunk] = (blob, callback, errback)
 
         if chunk.require_ack() :
             self._add_pending_ack(chunk.header.blob_id, chunk.header.dw1)
--- papyon/msnp2p/transport/transport_manager.py
+++ papyon/msnp2p/transport/transport_manager.py
@@ -95,6 +95,9 @@
                     session_id, chunk.header.blob_id)
                 self._signaling_blobs[blob_id] = blob
         else: # data blob
+            if chunk.is_data_preparation_chunk():
+                return
+
             if session_id in self._data_blobs:
                 blob = self._data_blobs[session_id]
                 if blob.transferred == 0:
--- papyon/p2p.py
+++ papyon/p2p.py
@@ -25,6 +25,7 @@
 with a contact.
     @group MSNObject: MSNObjectStore, MSNObject, MSNObjectType
     @sort: MSNObjectStore, MSNObject, MSNObjectType"""
+from msnp2p.filetransfer import FileTransferSession
 from msnp2p.msnobject import MSNObjectSession
 from msnp2p.webcam import WebcamSession
 from msnp2p import EufGuid, ApplicationID
@@ -41,7 +42,8 @@
 import hashlib
 import logging
 
-__all__ = ['MSNObjectType', 'MSNObject', 'MSNObjectStore', 'WebcamHandler']
+__all__ = ['MSNObjectType', 'MSNObject', 'MSNObjectStore',
+           'FileTransferManager', 'WebcamHandler']
 
 logger = logging.getLogger('papyon.p2p')
 
@@ -65,6 +67,30 @@
     LOCATION = 14
     "Location"
 
+def _decode_shad(shad, warning=True):
+    try:
+        shad = base64.b64decode(shad)
+    except TypeError:
+        # See fd.o#27672 for details on this workaround.
+        if ' ' in shad:
+            parts = shad.split(' ')
+
+            # Try the first part.
+            shad = _decode_shad(parts[0], False)
+
+            # Try the second part.
+            if shad is None:
+                shad = _decode_shad(parts[1], False)
+
+        else:
+            # Only display this warning if we're not in a nested call otherwise the
+            # warning will be confusing.
+            if warning:
+                logger.warning("Invalid SHA1D in MSNObject: %s" % shad)
+            shad = None
+
+    return shad
+
 class MSNObject(object):
     "Represents an MSNObject."
     def __init__(self, creator, size, typ, location, friendly,
@@ -147,22 +173,33 @@
         try:
             element = ElementTree.parse(data).getroot().attrib
         except:
-            raise ParseError('Invalid MSNObject')
+            logger.warning('Invalid MSNObject: %s' % xml_data)
+            return
 
         creator = element["Creator"]
         size = int(element["Size"])
         type = int(element["Type"])
-        location = xml.unescape(element["Location"])
-        friendly = base64.b64decode(xml.unescape(element["Friendly"]))
+
+        if "Location" in element:
+            location = xml.unescape(element["Location"])
+        else:
+            location = "0"
+
+        if "Friendly" in element:
+            friendly = base64.b64decode(xml.unescape(element["Friendly"]))
+        else:
+            friendly = base64.b64decode('AAA=')
+
         shad = element.get("SHA1D", None)
         if shad is not None:
-            shad = base64.b64decode(shad)
+            shad = _decode_shad(shad)
         shac = element.get("SHA1C", None)
         if shac is not None:
             try:
                 shac = base64.b64decode(shac)
             except TypeError:
-                logger.warning("Invalid SHA1C in MSNObject")
+                logger.warning("Invalid SHA1C in MSNObject: %s" % shac)
+                shac = None
 
         result = MSNObject(creator, size, type, location, friendly, shad, shac)
         result._repr = xml_data
@@ -276,6 +313,51 @@
         session.disconnect(handle_id)
         del self._incoming_sessions[session]
 
+
+class FileTransferManager(gobject.GObject):
+
+    __gsignals__ = {
+            "transfer-requested" : (gobject.SIGNAL_RUN_FIRST,
+                gobject.TYPE_NONE,
+                (object,))
+    }
+
+    def __init__(self, client):
+        gobject.GObject.__init__(self)
+        self._client = client
+        self._sessions = {}
+
+    def _can_handle_message(self, message):
+        euf_guid = message.body.euf_guid
+        return (euf_guid == EufGuid.FILE_TRANSFER)
+
+    def _handle_message(self, peer, message):
+        session = FileTransferSession(self._client._p2p_session_manager,
+                peer, message.body.application_id, message)
+        self._connect_session(session)
+        self.emit("transfer-requested", session)
+        return session
+
+    def send(self, peer, filename, size):
+        session = FileTransferSession(self._client._p2p_session_manager,
+                peer, ApplicationID.FILE_TRANSFER)
+        session.invite(filename, size)
+        self._connect_session(session)
+        return session
+
+    def _on_transfer_completed(self, session, data):
+        self._disconnect_session(session)
+        del self._sessions[session]
+
+    def _connect_session(self, session):
+        handle_id = session.connect("completed", self._on_transfer_completed)
+        self._sessions[session] = handle_id
+
+    def _disconnect_session(self, session):
+        handle_id = self._sessions[session]
+        session.disconnect(handle_id)
+
+
 class WebcamHandler(gobject.GObject):
 
     __gsignals__ = {
--- papyon/service/AddressBook/address_book.py
+++ papyon/service/AddressBook/address_book.py
@@ -345,8 +345,8 @@
     def delete_contact(self, contact, done_cb=None, failed_cb=None):
         def callback():
             contact._remove_membership(Membership.FORWARD)
-            contact._reset()
             self.__common_callback('contact-deleted', done_cb, contact)
+            contact._reset()
             if contact.memberships == Membership.NONE:
                 self.contacts.discard(contact)
 
--- papyon/service/ContentRoaming/storage.py
+++ papyon/service/ContentRoaming/storage.py
@@ -120,14 +120,14 @@
         document_name = response.findtext('./st:Name')        
         callback[0](document_rid, document_name, *callback[1:])
 
-    @RequireSecurityTokens(LiveService.CONTACTS)
+    @RequireSecurityTokens(LiveService.STORAGE)
     def __soap_request(self, method, scenario, args, callback, errback):
-        token = str(self._tokens[LiveService.CONTACTS])
+        token = str(self._tokens[LiveService.STORAGE])
         self._soap_request(method, (scenario, token), args, callback, errback)
 
-    @RequireSecurityTokens(LiveService.CONTACTS)
+    @RequireSecurityTokens(LiveService.STORAGE)
     def get_display_picture(self, pre_auth_url, callback, errback):
-        token = str(self._tokens[LiveService.CONTACTS])
+        token = str(self._tokens[LiveService.STORAGE])
 
         scheme = 'http'
         host = 'byfiles.storage.msn.com'
--- papyon/service/SOAPService.py
+++ papyon/service/SOAPService.py
@@ -4,6 +4,7 @@
 #
 # Copyright (C) 2005-2007 Ali Sabil <ali.sabil at gmail.com>
 # Copyright (C) 2007 Johann Prieur <johann.prieur at gmail.com>
+# Copyright (C) 2010 Collabora Ltd.
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -25,6 +26,7 @@
 import papyon.gnet.protocol
 import papyon.util.element_tree as ElementTree
 import papyon.util.string_io as StringIO
+import gzip
 import re
 import logging
 
@@ -158,6 +160,9 @@
         self._active_transports = {}
         self._proxies = proxies or {}
 
+        # Regex to find password
+        self.password_regex = re.compile("<wsse:Password>.*?</wsse:Password>", re.S)
+
     def _send_request(self, name, url, soap_header, soap_body, soap_action,
             callback, errback=None, transport_headers={}, user_data=None):
 
@@ -171,6 +176,7 @@
             http_headers["Accept"] = "text/*"
         http_headers["Proxy-Connection"] = "Keep-Alive"
         http_headers["Connection"] = "Keep-Alive"
+        http_headers["Accept-Encoding"] = "gzip"
 
         request = compress_xml(soap_template % (soap_header, soap_body))
 
@@ -193,7 +199,20 @@
 
     def _response_handler(self, transport, http_response):
         logger.debug("<<< " + unicode(http_response))
-        soap_response = SOAPResponse(http_response.body)
+
+        soap_response = ""
+        if "Content-Encoding" in http_response.headers:
+            if http_response.headers["Content-Encoding"] == "gzip":
+                body_stream = StringIO.StringIO(http_response.body)
+                unzipper = gzip.GzipFile(fileobj=body_stream)
+                unzipped_data = unzipper.read()
+                soap_response = SOAPResponse(unzipped_data)
+            elif http_response.headers["Content-Encoding"] == "":
+                soap_response = SOAPResponse(http_response.body)
+            else:
+                raise Exception("Invalid or unknown encoding")
+        else:
+            soap_response = SOAPResponse(http_response.body)
         request_id, callback, errback, user_data = self._unref_transport(transport)
 
         if not soap_response.is_valid():
@@ -223,7 +242,10 @@
                                       user_data)
 
     def _request_handler(self, transport, http_request):
-        logger.debug(">>> " + unicode(http_request))
+        #hide password from logs
+        cleaned = self.password_regex.sub("<wsse:Password>*****</wsse:Password>", unicode(http_request))
+
+        logger.debug(">>> " + unicode(cleaned))
 
     def _error_handler(self, transport, error):
         logger.warning("Transport Error :" + str(error))
--- papyon/service/SingleSignOn.py
+++ papyon/service/SingleSignOn.py
@@ -20,16 +20,15 @@
 
 from SOAPService import *
 from description.SingleSignOn.RequestMultipleSecurityTokens import LiveService
-from papyon.util import pyDes
 
 import base64
 import struct
 import time
 import datetime
 import sys
-import random
-import hmac
-from hashlib import sha1
+import Crypto.Util.randpool as randpool
+from Crypto.Hash import HMAC, SHA
+from Crypto.Cipher import DES3
 
 __all__ = ['SingleSignOn', 'LiveService', 'RequireSecurityTokens']
 
@@ -57,15 +56,15 @@
         key3 = self._derive_key(key1, "WS-SecureConversationSESSION KEY ENCRYPTION")
 
         # Create a HMAC-SHA-1 hash of nonce using key2
-        hash = hmac.new(key2, nonce, sha1).digest()
+        hash = HMAC.new(key2, nonce, SHA).digest()
 
         #
         # Encrypt nonce with DES3 using key3
         #
 
         # IV (Initialization Vector): 8 bytes of random data
-        iv = struct.pack("Q", random.getrandbits(8 * 8))
-        obj = pyDes.triple_des(key3, pyDes.CBC, iv)
+        iv = randpool.RandomPool().get_bytes(8)
+        obj = DES3.new(key3, DES3.MODE_CBC, iv)
 
         # XXX: win32's Crypt API seems to pad the input with 0x08 bytes
         # to align on 72/36/18/9 boundary
@@ -78,11 +77,11 @@
         return base64.b64encode(blob)
 
     def _derive_key(self, key, magic):
-        hash1 = hmac.new(key, magic, sha1).digest()
-        hash2 = hmac.new(key, hash1 + magic, sha1).digest()
+        hash1 = HMAC.new(key, magic, SHA).digest()
+        hash2 = HMAC.new(key, hash1 + magic, SHA).digest()
 
-        hash3 = hmac.new(key, hash1, sha1).digest()            
-        hash4 = hmac.new(key, hash3 + magic, sha1).digest()
+        hash3 = HMAC.new(key, hash1, SHA).digest()
+        hash4 = HMAC.new(key, hash3 + magic, SHA).digest()
         return hash2 + hash4[0:4]
 
     def __str__(self):
--- papyon/service/__init__.py
+++ papyon/service/__init__.py
@@ -24,4 +24,4 @@
 
 # import AddressBook
 # import OfflineIM
-# import ContentRoaming
+import ContentRoaming
--- papyon/service/description/SingleSignOn/RequestMultipleSecurityTokens.py
+++ papyon/service/description/SingleSignOn/RequestMultipleSecurityTokens.py
@@ -26,6 +26,7 @@
     MESSENGER_CLEAR = ("messengerclear.live.com", "MBI_KEY_OLD")
     MESSENGER_SECURE = ("messengersecure.live.com", "MBI_SSL")
     SPACES = ("spaces.live.com", "MBI")
+    STORAGE = ("storage.msn.com", "MBI")
     TB = ("http://Passport.NET/tb", None)
     VOICE = ("voice.messenger.msn.com", "?id=69264")
 
--- papyon/switchboard_manager.py
+++ papyon/switchboard_manager.py
@@ -46,6 +46,7 @@
 
         self._pending_invites = set(contacts)
         self._pending_messages = []
+        self._delivery_callbacks = {}
 
         if self._client.protocol_version >= 16:
             self._pending_invites.add(self._client.profile)
@@ -77,8 +78,10 @@
                 lambda sb, contact: self.__on_user_left(contact))
         self.switchboard.connect("user-invitation-failed",
                 lambda sb, contact: self.__on_user_invitation_failed(contact))
+        self.switchboard.connect("message-delivered",
+                lambda sb, trid: self.__on_message_delivered(trid))
         self.switchboard.connect("message-undelivered",
-                lambda sb, command: self.__on_message_undelivered(command))
+                lambda sb, trid: self.__on_message_undelivered(trid))
         logger.info("New switchboard attached")
         def process_pending_queues():
             self._process_pending_queues()
@@ -149,7 +152,13 @@
         self._on_error(ConversationErrorType.CONTACT_INVITE,
                 ContactInviteError.NOT_AVAILABLE)
 
-    def __on_message_undelivered(self, command):
+    def __on_message_delivered(self, transaction_id):
+        if transaction_id in self._delivery_callbacks:
+            callback, cb_args = self._delivery_callbacks.pop(transaction_id)
+            if callback:
+                callback(*cb_args)
+
+    def __on_message_undelivered(self, transaction_id):
         self._on_error(ConversationErrorType.MESSAGE,
                 MessageError.DELIVERY_FAILED)
 
@@ -169,7 +178,14 @@
 
         if not self.switchboard.inviting:
             for message, ack, callback, cb_args in self._pending_messages:
-                self.switchboard.send_message(message, ack, callback, cb_args)
+                # if ack type is FULL or MSNC, wait for ACK before calling back
+                if ack in (msnp.MessageAcknowledgement.FULL,
+                           msnp.MessageAcknowledgement.MSNC):
+                    transaction_id = self.switchboard.send_message(message, ack)
+                    self._delivery_callbacks[transaction_id] = (callback, cb_args)
+                else:
+                    self.switchboard.send_message(message, ack, callback, cb_args)
+
             self._pending_messages = []
 
     def _request_switchboard(self):
--- papyon/transport.py
+++ papyon/transport.py
@@ -468,7 +468,14 @@
             self.emit("command-sent", command)
 
     def __extract_command(self, data):
-        first, rest = data.split('\r\n', 1)
+        try:
+            first, rest = data.split('\r\n', 1)
+        except ValueError:
+            logger.warning('Unable to extract a command: %s' % data)
+            self.__error = True
+            self.emit("connection-failure", None)
+            return []
+
         cmd = msnp.Command()
         cmd.parse(first.strip())
         if cmd.name in msnp.Command.INCOMING_PAYLOAD or \
--- papyon/util/element_tree.py
+++ papyon/util/element_tree.py
@@ -134,16 +134,28 @@
             self.tree = None
 
     def __getitem__(self, name):
-        return self.tree[name]
+        if self.tree:
+            return self.tree[name]
+        else:
+            raise KeyError(name)
 
     def find(self, path):
-        return self.tree.find(path)
+        if self.tree:
+            return self.tree.find(path)
+        else:
+            return None
 
     def findall(self, path):
-        return self.tree.findall(path)
-    
+        if self.tree:
+            return self.tree.findall(path)
+        else:
+            return []
+
     def findtext(self, path, type=None):
-        return self.tree.findtext(path, type)
+        if self.tree:
+            return self.tree.findtext(path, type)
+        else:
+            return ""
 
     def is_valid(self):
         return self.tree is not None
--- papyon/util/pyDes.py
+++ papyon/util/pyDes.py
-#############################################################################
-# 				Documentation				    #
-#############################################################################
-
-# Author:   Todd Whiteman
-# Date:     16th March, 2009
-# Verion:   2.0.0
-# License:  Public Domain - free to do as you wish
-# Homepage: http://twhiteman.netfirms.com/des.html
-#
-# This is a pure python implementation of the DES encryption algorithm.
-# It's pure python to avoid portability issues, since most DES 
-# implementations are programmed in C (for performance reasons).
-#
-# Triple DES class is also implemented, utilising the DES base. Triple DES
-# is either DES-EDE3 with a 24 byte key, or DES-EDE2 with a 16 byte key.
-#
-# See the README.txt that should come with this python module for the
-# implementation methods used.
-#
-# Thanks to:
-#  * David Broadwell for ideas, comments and suggestions.
-#  * Mario Wolff for pointing out and debugging some triple des CBC errors.
-#  * Santiago Palladino for providing the PKCS5 padding technique.
-#  * Shaya for correcting the PAD_PKCS5 triple des CBC errors.
-#
-"""A pure python implementation of the DES and TRIPLE DES encryption algorithms.
-
-Class initialization
---------------------
-pyDes.des(key, [mode], [IV], [pad], [padmode])
-pyDes.triple_des(key, [mode], [IV], [pad], [padmode])
-
-key     -> Bytes containing the encryption key. 8 bytes for DES, 16 or 24 bytes
-	   for Triple DES
-mode    -> Optional argument for encryption type, can be either
-	   pyDes.ECB (Electronic Code Book) or pyDes.CBC (Cypher Block Chaining)
-IV      -> Optional Initial Value bytes, must be supplied if using CBC mode.
-	   Length must be 8 bytes.
-pad     -> Optional argument, set the pad character (PAD_NORMAL) to use during
-	   all encrypt/decrpt operations done with this instance.
-padmode -> Optional argument, set the padding mode (PAD_NORMAL or PAD_PKCS5)
-	   to use during all encrypt/decrpt operations done with this instance.
-
-I recommend to use PAD_PKCS5 padding, as then you never need to worry about any
-padding issues, as the padding can be removed unambiguously upon decrypting
-data that was encrypted using PAD_PKCS5 padmode.
-
-Common methods
---------------
-encrypt(data, [pad], [padmode])
-decrypt(data, [pad], [padmode])
-
-data    -> Bytes to be encrypted/decrypted
-pad     -> Optional argument. Only when using padmode of PAD_NORMAL. For
-	   encryption, adds this characters to the end of the data block when
-	   data is not a multiple of 8 bytes. For decryption, will remove the
-	   trailing characters that match this pad character from the last 8
-	   bytes of the unencrypted data block.
-padmode -> Optional argument, set the padding mode, must be one of PAD_NORMAL
-	   or PAD_PKCS5). Defaults to PAD_NORMAL.
-	  
-
-Example
--------
-from pyDes import *
-
-data = "Please encrypt my data"
-k = des("DESCRYPT", CBC, "\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5)
-# For Python3, you'll need to use bytes, i.e.:
-#   data = b"Please encrypt my data"
-#   k = des(b"DESCRYPT", CBC, b"\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5)
-d = k.encrypt(data)
-print "Encrypted: %r" % d
-print "Decrypted: %r" % k.decrypt(d)
-assert k.decrypt(d, padmode=PAD_PKCS5) == data
-
-
-See the module source (pyDes.py) for more examples of use.
-You can also run the pyDes.py file without and arguments to see a simple test.
-
-Note: This code was not written for high-end systems needing a fast
-      implementation, but rather a handy portable solution with small usage.
-
-"""
-
-import sys
-
-# _pythonMajorVersion is used to handle Python2 and Python3 differences.
-_pythonMajorVersion = sys.version_info[0]
-
-# Modes of crypting / cyphering
-ECB =	0
-CBC =	1
-
-# Modes of padding
-PAD_NORMAL = 1
-PAD_PKCS5 = 2
-
-# PAD_PKCS5: is a method that will unambiguously remove all padding
-#            characters after decryption, when originally encrypted with
-#            this padding mode.
-# For a good description of the PKCS5 padding technique, see:
-# http://www.faqs.org/rfcs/rfc1423.html
-
-# The base class shared by des and triple des.
-class _baseDes(object):
-	def __init__(self, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL):
-		if IV:
-			IV = self._guardAgainstUnicode(IV)
-		if pad:
-			pad = self._guardAgainstUnicode(pad)
-		self.block_size = 8
-		# Sanity checking of arguments.
-		if pad and padmode == PAD_PKCS5:
-			raise ValueError("Cannot use a pad character with PAD_PKCS5")
-		if IV and len(IV) != self.block_size:
-			raise ValueError("Invalid Initial Value (IV), must be a multiple of " + str(self.block_size) + " bytes")
-
-		# Set the passed in variables
-		self._mode = mode
-		self._iv = IV
-		self._padding = pad
-		self._padmode = padmode
-
-	def getKey(self):
-		"""getKey() -> bytes"""
-		return self.__key
-
-	def setKey(self, key):
-		"""Will set the crypting key for this object."""
-		key = self._guardAgainstUnicode(key)
-		self.__key = key
-
-	def getMode(self):
-		"""getMode() -> pyDes.ECB or pyDes.CBC"""
-		return self._mode
-
-	def setMode(self, mode):
-		"""Sets the type of crypting mode, pyDes.ECB or pyDes.CBC"""
-		self._mode = mode
-
-	def getPadding(self):
-		"""getPadding() -> bytes of length 1. Padding character."""
-		return self._padding
-
-	def setPadding(self, pad):
-		"""setPadding() -> bytes of length 1. Padding character."""
-		if pad is not None:
-			pad = self._guardAgainstUnicode(pad)
-		self._padding = pad
-
-	def getPadMode(self):
-		"""getPadMode() -> pyDes.PAD_NORMAL or pyDes.PAD_PKCS5"""
-		return self._padmode
-		
-	def setPadMode(self, mode):
-		"""Sets the type of padding mode, pyDes.PAD_NORMAL or pyDes.PAD_PKCS5"""
-		self._padmode = mode
-
-	def getIV(self):
-		"""getIV() -> bytes"""
-		return self._iv
-
-	def setIV(self, IV):
-		"""Will set the Initial Value, used in conjunction with CBC mode"""
-		if not IV or len(IV) != self.block_size:
-			raise ValueError("Invalid Initial Value (IV), must be a multiple of " + str(self.block_size) + " bytes")
-		IV = self._guardAgainstUnicode(IV)
-		self._iv = IV
-
-	def _padData(self, data, pad, padmode):
-		# Pad data depending on the mode
-		if padmode is None:
-			# Get the default padding mode.
-			padmode = self.getPadMode()
-		if pad and padmode == PAD_PKCS5:
-			raise ValueError("Cannot use a pad character with PAD_PKCS5")
-
-		if padmode == PAD_NORMAL:
-			if len(data) % self.block_size == 0:
-				# No padding required.
-				return data
-
-			if not pad:
-				# Get the default padding.
-				pad = self.getPadding()
-			if not pad:
-				raise ValueError("Data must be a multiple of " + str(self.block_size) + " bytes in length. Use padmode=PAD_PKCS5 or set the pad character.")
-			data += (self.block_size - (len(data) % self.block_size)) * pad
-		
-		elif padmode == PAD_PKCS5:
-			pad_len = 8 - (len(data) % self.block_size)
-			if _pythonMajorVersion < 3:
-				data += pad_len * chr(pad_len)
-			else:
-				data += bytes([pad_len] * pad_len)
-
-		return data
-
-	def _unpadData(self, data, pad, padmode):
-		# Unpad data depending on the mode.
-		if not data:
-			return data
-		if pad and padmode == PAD_PKCS5:
-			raise ValueError("Cannot use a pad character with PAD_PKCS5")
-		if padmode is None:
-			# Get the default padding mode.
-			padmode = self.getPadMode()
-
-		if padmode == PAD_NORMAL:
-			if not pad:
-				# Get the default padding.
-				pad = self.getPadding()
-			if pad:
-				data = data[:-self.block_size] + \
-				       data[-self.block_size:].rstrip(pad)
-
-		elif padmode == PAD_PKCS5:
-			if _pythonMajorVersion < 3:
-				pad_len = ord(data[-1])
-			else:
-				pad_len = data[-1]
-			data = data[:-pad_len]
-
-		return data
-
-	def _guardAgainstUnicode(self, data):
-		# Only accept byte strings or ascii unicode values, otherwise
-		# there is no way to correctly decode the data into bytes.
-		if _pythonMajorVersion < 3:
-			if isinstance(data, unicode):
-				raise ValueError("pyDes can only work with bytes, not Unicode strings.")
-		else:
-			if isinstance(data, str):
-				# Only accept ascii unicode values.
-				try:
-					return data.encode('ascii')
-				except UnicodeEncodeError:
-					pass
-				raise ValueError("pyDes can only work with encoded strings, not Unicode.")
-		return data
-
-#############################################################################
-# 				    DES					    #
-#############################################################################
-class des(_baseDes):
-	"""DES encryption/decrytpion class
-
-	Supports ECB (Electronic Code Book) and CBC (Cypher Block Chaining) modes.
-
-	pyDes.des(key,[mode], [IV])
-
-	key  -> Bytes containing the encryption key, must be exactly 8 bytes
-	mode -> Optional argument for encryption type, can be either pyDes.ECB
-		(Electronic Code Book), pyDes.CBC (Cypher Block Chaining)
-	IV   -> Optional Initial Value bytes, must be supplied if using CBC mode.
-		Must be 8 bytes in length.
-	pad  -> Optional argument, set the pad character (PAD_NORMAL) to use
-		during all encrypt/decrpt operations done with this instance.
-	padmode -> Optional argument, set the padding mode (PAD_NORMAL or
-		PAD_PKCS5) to use during all encrypt/decrpt operations done
-		with this instance.
-	"""
-
-
-	# Permutation and translation tables for DES
-	__pc1 = [56, 48, 40, 32, 24, 16,  8,
-		  0, 57, 49, 41, 33, 25, 17,
-		  9,  1, 58, 50, 42, 34, 26,
-		 18, 10,  2, 59, 51, 43, 35,
-		 62, 54, 46, 38, 30, 22, 14,
-		  6, 61, 53, 45, 37, 29, 21,
-		 13,  5, 60, 52, 44, 36, 28,
-		 20, 12,  4, 27, 19, 11,  3
-	]
-
-	# number left rotations of pc1
-	__left_rotations = [
-		1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
-	]
-
-	# permuted choice key (table 2)
-	__pc2 = [
-		13, 16, 10, 23,  0,  4,
-		 2, 27, 14,  5, 20,  9,
-		22, 18, 11,  3, 25,  7,
-		15,  6, 26, 19, 12,  1,
-		40, 51, 30, 36, 46, 54,
-		29, 39, 50, 44, 32, 47,
-		43, 48, 38, 55, 33, 52,
-		45, 41, 49, 35, 28, 31
-	]
-
-	# initial permutation IP
-	__ip = [57, 49, 41, 33, 25, 17, 9,  1,
-		59, 51, 43, 35, 27, 19, 11, 3,
-		61, 53, 45, 37, 29, 21, 13, 5,
-		63, 55, 47, 39, 31, 23, 15, 7,
-		56, 48, 40, 32, 24, 16, 8,  0,
-		58, 50, 42, 34, 26, 18, 10, 2,
-		60, 52, 44, 36, 28, 20, 12, 4,
-		62, 54, 46, 38, 30, 22, 14, 6
-	]
-
-	# Expansion table for turning 32 bit blocks into 48 bits
-	__expansion_table = [
-		31,  0,  1,  2,  3,  4,
-		 3,  4,  5,  6,  7,  8,
-		 7,  8,  9, 10, 11, 12,
-		11, 12, 13, 14, 15, 16,
-		15, 16, 17, 18, 19, 20,
-		19, 20, 21, 22, 23, 24,
-		23, 24, 25, 26, 27, 28,
-		27, 28, 29, 30, 31,  0
-	]
-
-	# The (in)famous S-boxes
-	__sbox = [
-		# S1
-		[14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
-		 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
-		 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
-		 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13],
-
-		# S2
-		[15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
-		 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
-		 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
-		 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9],
-
-		# S3
-		[10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
-		 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
-		 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
-		 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12],
-
-		# S4
-		[7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
-		 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
-		 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
-		 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14],
-
-		# S5
-		[2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
-		 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
-		 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
-		 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3],
-
-		# S6
-		[12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
-		 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
-		 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
-		 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13],
-
-		# S7
-		[4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
-		 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
-		 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
-		 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12],
-
-		# S8
-		[13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
-		 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
-		 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
-		 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11],
-	]
-
-
-	# 32-bit permutation function P used on the output of the S-boxes
-	__p = [
-		15, 6, 19, 20, 28, 11,
-		27, 16, 0, 14, 22, 25,
-		4, 17, 30, 9, 1, 7,
-		23,13, 31, 26, 2, 8,
-		18, 12, 29, 5, 21, 10,
-		3, 24
-	]
-
-	# final permutation IP^-1
-	__fp = [
-		39,  7, 47, 15, 55, 23, 63, 31,
-		38,  6, 46, 14, 54, 22, 62, 30,
-		37,  5, 45, 13, 53, 21, 61, 29,
-		36,  4, 44, 12, 52, 20, 60, 28,
-		35,  3, 43, 11, 51, 19, 59, 27,
-		34,  2, 42, 10, 50, 18, 58, 26,
-		33,  1, 41,  9, 49, 17, 57, 25,
-		32,  0, 40,  8, 48, 16, 56, 24
-	]
-
-	# Type of crypting being done
-	ENCRYPT =	0x00
-	DECRYPT =	0x01
-
-	# Initialisation
-	def __init__(self, key, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL):
-		# Sanity checking of arguments.
-		if len(key) != 8:
-			raise ValueError("Invalid DES key size. Key must be exactly 8 bytes long.")
-		_baseDes.__init__(self, mode, IV, pad, padmode)
-		self.key_size = 8
-
-		self.L = []
-		self.R = []
-		self.Kn = [ [0] * 48 ] * 16	# 16 48-bit keys (K1 - K16)
-		self.final = []
-
-		self.setKey(key)
-
-	def setKey(self, key):
-		"""Will set the crypting key for this object. Must be 8 bytes."""
-		_baseDes.setKey(self, key)
-		self.__create_sub_keys()
-
-	def __String_to_BitList(self, data):
-		"""Turn the string data, into a list of bits (1, 0)'s"""
-		if _pythonMajorVersion < 3:
-			# Turn the strings into integers. Python 3 uses a bytes
-			# class, which already has this behaviour.
-			data = [ord(c) for c in data]
-		l = len(data) * 8
-		result = [0] * l
-		pos = 0
-		for ch in data:
-			i = 7
-			while i >= 0:
-				if ch & (1 << i) != 0:
-					result[pos] = 1
-				else:
-					result[pos] = 0
-				pos += 1
-				i -= 1
-
-		return result
-
-	def __BitList_to_String(self, data):
-		"""Turn the list of bits -> data, into a string"""
-		result = []
-		pos = 0
-		c = 0
-		while pos < len(data):
-			c += data[pos] << (7 - (pos % 8))
-			if (pos % 8) == 7:
-				result.append(c)
-				c = 0
-			pos += 1
-
-		if _pythonMajorVersion < 3:
-			return ''.join([ chr(c) for c in result ])
-		else:
-			return bytes(result)
-
-	def __permutate(self, table, block):
-		"""Permutate this block with the specified table"""
-		return list(map(lambda x: block[x], table))
-	
-	# Transform the secret key, so that it is ready for data processing
-	# Create the 16 subkeys, K[1] - K[16]
-	def __create_sub_keys(self):
-		"""Create the 16 subkeys K[1] to K[16] from the given key"""
-		key = self.__permutate(des.__pc1, self.__String_to_BitList(self.getKey()))
-		i = 0
-		# Split into Left and Right sections
-		self.L = key[:28]
-		self.R = key[28:]
-		while i < 16:
-			j = 0
-			# Perform circular left shifts
-			while j < des.__left_rotations[i]:
-				self.L.append(self.L[0])
-				del self.L[0]
-
-				self.R.append(self.R[0])
-				del self.R[0]
-
-				j += 1
-
-			# Create one of the 16 subkeys through pc2 permutation
-			self.Kn[i] = self.__permutate(des.__pc2, self.L + self.R)
-
-			i += 1
-
-	# Main part of the encryption algorithm, the number cruncher :)
-	def __des_crypt(self, block, crypt_type):
-		"""Crypt the block of data through DES bit-manipulation"""
-		block = self.__permutate(des.__ip, block)
-		self.L = block[:32]
-		self.R = block[32:]
-
-		# Encryption starts from Kn[1] through to Kn[16]
-		if crypt_type == des.ENCRYPT:
-			iteration = 0
-			iteration_adjustment = 1
-		# Decryption starts from Kn[16] down to Kn[1]
-		else:
-			iteration = 15
-			iteration_adjustment = -1
-
-		i = 0
-		while i < 16:
-			# Make a copy of R[i-1], this will later become L[i]
-			tempR = self.R[:]
-
-			# Permutate R[i - 1] to start creating R[i]
-			self.R = self.__permutate(des.__expansion_table, self.R)
-
-			# Exclusive or R[i - 1] with K[i], create B[1] to B[8] whilst here
-			self.R = list(map(lambda x, y: x ^ y, self.R, self.Kn[iteration]))
-			B = [self.R[:6], self.R[6:12], self.R[12:18], self.R[18:24], self.R[24:30], self.R[30:36], self.R[36:42], self.R[42:]]
-			# Optimization: Replaced below commented code with above
-			#j = 0
-			#B = []
-			#while j < len(self.R):
-			#	self.R[j] = self.R[j] ^ self.Kn[iteration][j]
-			#	j += 1
-			#	if j % 6 == 0:
-			#		B.append(self.R[j-6:j])
-
-			# Permutate B[1] to B[8] using the S-Boxes
-			j = 0
-			Bn = [0] * 32
-			pos = 0
-			while j < 8:
-				# Work out the offsets
-				m = (B[j][0] << 1) + B[j][5]
-				n = (B[j][1] << 3) + (B[j][2] << 2) + (B[j][3] << 1) + B[j][4]
-
-				# Find the permutation value
-				v = des.__sbox[j][(m << 4) + n]
-
-				# Turn value into bits, add it to result: Bn
-				Bn[pos] = (v & 8) >> 3
-				Bn[pos + 1] = (v & 4) >> 2
-				Bn[pos + 2] = (v & 2) >> 1
-				Bn[pos + 3] = v & 1
-
-				pos += 4
-				j += 1
-
-			# Permutate the concatination of B[1] to B[8] (Bn)
-			self.R = self.__permutate(des.__p, Bn)
-
-			# Xor with L[i - 1]
-			self.R = list(map(lambda x, y: x ^ y, self.R, self.L))
-			# Optimization: This now replaces the below commented code
-			#j = 0
-			#while j < len(self.R):
-			#	self.R[j] = self.R[j] ^ self.L[j]
-			#	j += 1
-
-			# L[i] becomes R[i - 1]
-			self.L = tempR
-
-			i += 1
-			iteration += iteration_adjustment
-		
-		# Final permutation of R[16]L[16]
-		self.final = self.__permutate(des.__fp, self.R + self.L)
-		return self.final
-
-
-	# Data to be encrypted/decrypted
-	def crypt(self, data, crypt_type):
-		"""Crypt the data in blocks, running it through des_crypt()"""
-
-		# Error check the data
-		if not data:
-			return ''
-		if len(data) % self.block_size != 0:
-			if crypt_type == des.DECRYPT: # Decryption must work on 8 byte blocks
-				raise ValueError("Invalid data length, data must be a multiple of " + str(self.block_size) + " bytes\n.")
-			if not self.getPadding():
-				raise ValueError("Invalid data length, data must be a multiple of " + str(self.block_size) + " bytes\n. Try setting the optional padding character")
-			else:
-				data += (self.block_size - (len(data) % self.block_size)) * self.getPadding()
-			# print "Len of data: %f" % (len(data) / self.block_size)
-
-		if self.getMode() == CBC:
-			if self.getIV():
-				iv = self.__String_to_BitList(self.getIV())
-			else:
-				raise ValueError("For CBC mode, you must supply the Initial Value (IV) for ciphering")
-
-		# Split the data into blocks, crypting each one seperately
-		i = 0
-		dict = {}
-		result = []
-		#cached = 0
-		#lines = 0
-		while i < len(data):
-			# Test code for caching encryption results
-			#lines += 1
-			#if dict.has_key(data[i:i+8]):
-				#print "Cached result for: %s" % data[i:i+8]
-			#	cached += 1
-			#	result.append(dict[data[i:i+8]])
-			#	i += 8
-			#	continue
-				
-			block = self.__String_to_BitList(data[i:i+8])
-
-			# Xor with IV if using CBC mode
-			if self.getMode() == CBC:
-				if crypt_type == des.ENCRYPT:
-					block = list(map(lambda x, y: x ^ y, block, iv))
-					#j = 0
-					#while j < len(block):
-					#	block[j] = block[j] ^ iv[j]
-					#	j += 1
-
-				processed_block = self.__des_crypt(block, crypt_type)
-
-				if crypt_type == des.DECRYPT:
-					processed_block = list(map(lambda x, y: x ^ y, processed_block, iv))
-					#j = 0
-					#while j < len(processed_block):
-					#	processed_block[j] = processed_block[j] ^ iv[j]
-					#	j += 1
-					iv = block
-				else:
-					iv = processed_block
-			else:
-				processed_block = self.__des_crypt(block, crypt_type)
-
-
-			# Add the resulting crypted block to our list
-			#d = self.__BitList_to_String(processed_block)
-			#result.append(d)
-			result.append(self.__BitList_to_String(processed_block))
-			#dict[data[i:i+8]] = d
-			i += 8
-
-		# print "Lines: %d, cached: %d" % (lines, cached)
-
-		# Return the full crypted string
-		if _pythonMajorVersion < 3:
-			return ''.join(result)
-		else:
-			return bytes.fromhex('').join(result)
-
-	def encrypt(self, data, pad=None, padmode=None):
-		"""encrypt(data, [pad], [padmode]) -> bytes
-
-		data : Bytes to be encrypted
-		pad  : Optional argument for encryption padding. Must only be one byte
-		padmode : Optional argument for overriding the padding mode.
-
-		The data must be a multiple of 8 bytes and will be encrypted
-		with the already specified key. Data does not have to be a
-		multiple of 8 bytes if the padding character is supplied, or
-		the padmode is set to PAD_PKCS5, as bytes will then added to
-		ensure the be padded data is a multiple of 8 bytes.
-		"""
-		data = self._guardAgainstUnicode(data)
-		if pad is not None:
-			pad = self._guardAgainstUnicode(pad)
-		data = self._padData(data, pad, padmode)
-		return self.crypt(data, des.ENCRYPT)
-
-	def decrypt(self, data, pad=None, padmode=None):
-		"""decrypt(data, [pad], [padmode]) -> bytes
-
-		data : Bytes to be encrypted
-		pad  : Optional argument for decryption padding. Must only be one byte
-		padmode : Optional argument for overriding the padding mode.
-
-		The data must be a multiple of 8 bytes and will be decrypted
-		with the already specified key. In PAD_NORMAL mode, if the
-		optional padding character is supplied, then the un-encrypted
-		data will have the padding characters removed from the end of
-		the bytes. This pad removal only occurs on the last 8 bytes of
-		the data (last data block). In PAD_PKCS5 mode, the special
-		padding end markers will be removed from the data after decrypting.
-		"""
-		data = self._guardAgainstUnicode(data)
-		if pad is not None:
-			pad = self._guardAgainstUnicode(pad)
-		data = self.crypt(data, des.DECRYPT)
-		return self._unpadData(data, pad, padmode)
-
-
-
-#############################################################################
-# 				Triple DES				    #
-#############################################################################
-class triple_des(_baseDes):
-	"""Triple DES encryption/decrytpion class
-
-	This algorithm uses the DES-EDE3 (when a 24 byte key is supplied) or
-	the DES-EDE2 (when a 16 byte key is supplied) encryption methods.
-	Supports ECB (Electronic Code Book) and CBC (Cypher Block Chaining) modes.
-
-	pyDes.des(key, [mode], [IV])
-
-	key  -> Bytes containing the encryption key, must be either 16 or
-	        24 bytes long
-	mode -> Optional argument for encryption type, can be either pyDes.ECB
-		(Electronic Code Book), pyDes.CBC (Cypher Block Chaining)
-	IV   -> Optional Initial Value bytes, must be supplied if using CBC mode.
-		Must be 8 bytes in length.
-	pad  -> Optional argument, set the pad character (PAD_NORMAL) to use
-		during all encrypt/decrpt operations done with this instance.
-	padmode -> Optional argument, set the padding mode (PAD_NORMAL or
-		PAD_PKCS5) to use during all encrypt/decrpt operations done
-		with this instance.
-	"""
-	def __init__(self, key, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL):
-		_baseDes.__init__(self, mode, IV, pad, padmode)
-		self.setKey(key)
-
-	def setKey(self, key):
-		"""Will set the crypting key for this object. Either 16 or 24 bytes long."""
-		self.key_size = 24  # Use DES-EDE3 mode
-		if len(key) != self.key_size:
-			if len(key) == 16: # Use DES-EDE2 mode
-				self.key_size = 16
-			else:
-				raise ValueError("Invalid triple DES key size. Key must be either 16 or 24 bytes long")
-		if self.getMode() == CBC:
-			if not self.getIV():
-				# Use the first 8 bytes of the key
-				self.setIV(key[:self.block_size])
-			if len(self.getIV()) != self.block_size:
-				raise ValueError("Invalid IV, must be 8 bytes in length")
-		self.__key1 = des(key[:8], self._mode, self._iv,
-				  self._padding, self._padmode)
-		self.__key2 = des(key[8:16], self._mode, self._iv,
-				  self._padding, self._padmode)
-		if self.key_size == 16:
-			self.__key3 = self.__key1
-		else:
-			self.__key3 = des(key[16:], self._mode, self._iv,
-					  self._padding, self._padmode)
-		_baseDes.setKey(self, key)
-
-	# Override setter methods to work on all 3 keys.
-
-	def setMode(self, mode):
-		"""Sets the type of crypting mode, pyDes.ECB or pyDes.CBC"""
-		_baseDes.setMode(self, mode)
-		for key in (self.__key1, self.__key2, self.__key3):
-			key.setMode(mode)
-
-	def setPadding(self, pad):
-		"""setPadding() -> bytes of length 1. Padding character."""
-		_baseDes.setPadding(self, pad)
-		for key in (self.__key1, self.__key2, self.__key3):
-			key.setPadding(pad)
-
-	def setPadMode(self, mode):
-		"""Sets the type of padding mode, pyDes.PAD_NORMAL or pyDes.PAD_PKCS5"""
-		_baseDes.setPadMode(self, mode)
-		for key in (self.__key1, self.__key2, self.__key3):
-			key.setPadMode(mode)
-
-	def setIV(self, IV):
-		"""Will set the Initial Value, used in conjunction with CBC mode"""
-		_baseDes.setIV(self, IV)
-		for key in (self.__key1, self.__key2, self.__key3):
-			key.setIV(IV)
-
-	def encrypt(self, data, pad=None, padmode=None):
-		"""encrypt(data, [pad], [padmode]) -> bytes
-
-		data : bytes to be encrypted
-		pad  : Optional argument for encryption padding. Must only be one byte
-		padmode : Optional argument for overriding the padding mode.
-
-		The data must be a multiple of 8 bytes and will be encrypted
-		with the already specified key. Data does not have to be a
-		multiple of 8 bytes if the padding character is supplied, or
-		the padmode is set to PAD_PKCS5, as bytes will then added to
-		ensure the be padded data is a multiple of 8 bytes.
-		"""
-		ENCRYPT = des.ENCRYPT
-		DECRYPT = des.DECRYPT
-		data = self._guardAgainstUnicode(data)
-		if pad is not None:
-			pad = self._guardAgainstUnicode(pad)
-		# Pad the data accordingly.
-		data = self._padData(data, pad, padmode)
-		if self.getMode() == CBC:
-			self.__key1.setIV(self.getIV())
-			self.__key2.setIV(self.getIV())
-			self.__key3.setIV(self.getIV())
-			i = 0
-			result = []
-			while i < len(data):
-				block = self.__key1.crypt(data[i:i+8], ENCRYPT)
-				block = self.__key2.crypt(block, DECRYPT)
-				block = self.__key3.crypt(block, ENCRYPT)
-				self.__key1.setIV(block)
-				self.__key2.setIV(block)
-				self.__key3.setIV(block)
-				result.append(block)
-				i += 8
-			if _pythonMajorVersion < 3:
-				return ''.join(result)
-			else:
-				return bytes.fromhex('').join(result)
-		else:
-			data = self.__key1.crypt(data, ENCRYPT)
-			data = self.__key2.crypt(data, DECRYPT)
-			return self.__key3.crypt(data, ENCRYPT)
-
-	def decrypt(self, data, pad=None, padmode=None):
-		"""decrypt(data, [pad], [padmode]) -> bytes
-
-		data : bytes to be encrypted
-		pad  : Optional argument for decryption padding. Must only be one byte
-		padmode : Optional argument for overriding the padding mode.
-
-		The data must be a multiple of 8 bytes and will be decrypted
-		with the already specified key. In PAD_NORMAL mode, if the
-		optional padding character is supplied, then the un-encrypted
-		data will have the padding characters removed from the end of
-		the bytes. This pad removal only occurs on the last 8 bytes of
-		the data (last data block). In PAD_PKCS5 mode, the special
-		padding end markers will be removed from the data after
-		decrypting, no pad character is required for PAD_PKCS5.
-		"""
-		ENCRYPT = des.ENCRYPT
-		DECRYPT = des.DECRYPT
-		data = self._guardAgainstUnicode(data)
-		if pad is not None:
-			pad = self._guardAgainstUnicode(pad)
-		if self.getMode() == CBC:
-			self.__key1.setIV(self.getIV())
-			self.__key2.setIV(self.getIV())
-			self.__key3.setIV(self.getIV())
-			i = 0
-			result = []
-			while i < len(data):
-				iv = data[i:i+8]
-				block = self.__key3.crypt(iv,    DECRYPT)
-				block = self.__key2.crypt(block, ENCRYPT)
-				block = self.__key1.crypt(block, DECRYPT)
-				self.__key1.setIV(iv)
-				self.__key2.setIV(iv)
-				self.__key3.setIV(iv)
-				result.append(block)
-				i += 8
-			if _pythonMajorVersion < 3:
-				data = ''.join(result)
-			else:
-				data = bytes.fromhex('').join(result)
-		else:
-			data = self.__key3.crypt(data, DECRYPT)
-			data = self.__key2.crypt(data, ENCRYPT)
-			data = self.__key1.crypt(data, DECRYPT)
-		return self._unpadData(data, pad, padmode)

++++++ papyon.yaml (new)
--- papyon.yaml
+++ papyon.yaml
+Name: papyon
+Summary: Python libraries for MSN Messenger network
+Version: 0.4.9
+Release: 1
+Group: System/Libraries
+License: GPLv2+
+URL: http://telepathy.freedesktop.org/wiki/Papyon
+Sources:
+    - http://telepathy.freedesktop.org/releases/%{name}/%{name}-%{version}.tar.gz
+Description: |
+    %{name} is the library behind the msn connection manager for telepathy.
+    %{name} uses the glib mainloop to process the network events in an
+    asynchronous manner
+
+Requires:
+    - pyOpenSSL
+    - pygobject2
+PkgBR:
+    - pyOpenSSL >= 0.6
+    - python-devel
+    - pygobject2
+    - python-crypto
+Provides:
+    - pymsn = 0.3.4-1
+Obsoletes:
+    - pymsn < 0.3.4
+Configure: none
+Builder: python
+BuildArch: noarch



More information about the MeeGo-commits mailing list