[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