summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--papyon/service/AddressBook/ab.py28
-rw-r--r--papyon/service/AddressBook/address_book.py90
-rw-r--r--papyon/service/AddressBook/scenario/contacts/accept_invite.py48
-rw-r--r--papyon/service/AddressBook/scenario/contacts/contact_delete.py7
-rw-r--r--papyon/service/AddressBook/scenario/contacts/contact_find.py48
-rw-r--r--papyon/service/AddressBook/scenario/contacts/messenger_contact_add.py26
-rw-r--r--papyon/service/AddressBook/scenario/contacts/update_memberships.py3
7 files changed, 111 insertions, 139 deletions
diff --git a/papyon/service/AddressBook/ab.py b/papyon/service/AddressBook/ab.py
index 048d220..ea4f7c9 100644
--- a/papyon/service/AddressBook/ab.py
+++ b/papyon/service/AddressBook/ab.py
@@ -27,6 +27,9 @@ from papyon.service.AddressBook.constants import *
from papyon.service.description.AB.constants import ContactGeneral
+import logging
+logger = logging.getLogger('papyon.service.address_book')
+
__all__ = ['AB']
class ABResult(object):
@@ -275,6 +278,31 @@ class AB(SOAPService):
def _HandleABContactAddResponse(self, callback, errback, response, user_data):
run(callback, response)
+ def _HandleABContactAddFault(self, callback, errback, response, user_data):
+ """Make sure that "contact exists" errors are handled gracefully."""
+ error = AddressBookError.from_fault(response.fault)
+ if error == AddressBookError.CONTACT_ALREADY_EXISTS:
+ # This error may occur when we try to re-add a previously
+ # deleted (and now hidden) contact.
+ # We ignore this error as the contact will be added (aka:
+ # made visible) anyway.
+ logger.warning('AddressBookError: Contact already exists!')
+ conflict_id = response.fault.detail. \
+ findtext('additionalDetails/conflictObjectId')
+ if conflict_id:
+ run(callback, conflict_id.lower(), True)
+ return True
+ elif error == AddressBookError.MEMBER_DOES_NOT_EXIST:
+ # This error may occur when the counterpart blocked us
+ # and we're trying to add.
+ # We ignore this error as the contact may have been added
+ # anyway. We'll discover the real content of our address
+ # book (including the GUID) via delta sync.
+ logger.warning('AddressBookError: Member does not exist!')
+ run(callback, None, True)
+ return True
+ return False
+
def ContactDelete(self, callback, errback, scenario,
contact_id):
"""Deletes a contact from the contact list.
diff --git a/papyon/service/AddressBook/address_book.py b/papyon/service/AddressBook/address_book.py
index 43e3b33..fa4b6d6 100644
--- a/papyon/service/AddressBook/address_book.py
+++ b/papyon/service/AddressBook/address_book.py
@@ -239,7 +239,7 @@ class AddressBook(gobject.GObject):
def profile(self):
return self._profile
- def sync(self, delta_only=False):
+ def sync(self, delta_only=False, done_cb=None):
# Avoid race conditions.
if self._state in \
(AddressBookState.INITIAL_SYNC, AddressBookState.RESYNC):
@@ -287,17 +287,31 @@ class AddressBook(gobject.GObject):
def accept_contact_invitation(self, pending_contact, add_to_contact_list=True,
done_cb=None, failed_cb=None):
- def callback(contact_infos, memberships):
- self.__update_contact(pending_contact, memberships, contact_infos)
- self.__common_callback('contact-accepted', done_cb, pending_contact)
- ai = scenario.AcceptInviteScenario(self._ab, self._sharing,
- (callback,),
- (self.__common_errback, failed_cb))
- ai.account = pending_contact.account
- ai.network = pending_contact.network_id
- ai.memberships = pending_contact.memberships
- ai.add_to_contact_list = add_to_contact_list
- ai()
+
+ if not pending_contact.is_member(Membership.PENDING) \
+ and pending_contact.is_member(Membership.REVERSE):
+ return
+
+ def callback(memberships, contact):
+ self.__update_contact(contact, memberships)
+ self.__common_callback('contact-accepted', done_cb, contact)
+
+ def contact_added(pending_contact):
+ ai = scenario.AcceptInviteScenario(self._sharing,
+ (callback, pending_contact),
+ (self.__common_errback, failed_cb),
+ pending_contact.account,
+ pending_contact.memberships,
+ pending_contact.network_id)
+ ai()
+
+ if add_to_contact_list \
+ and not pending_contact.memberships & Membership.FORWARD:
+ self.add_messenger_contact(account=pending_contact.account,
+ network_id=pending_contact.network_id,
+ done_cb=(contact_added,))
+ else:
+ contact_added(pending_contact)
def decline_contact_invitation(self, pending_contact, block=True,
done_cb=None, failed_cb=None):
@@ -316,12 +330,32 @@ class AddressBook(gobject.GObject):
def add_messenger_contact(self, account, invite_display_name='',
invite_message='', groups=[], network_id=NetworkID.MSN,
auto_allow=True, done_cb=None, failed_cb=None):
- def callback(contact_infos, memberships):
- c = self.__build_or_update_contact(account, network_id,
- memberships, contact_infos)
- self.__common_callback('messenger-contact-added', done_cb, c)
- for group in groups:
- self.add_contact_to_group(group, c)
+
+ def contact_added(was_hidden):
+ new_contact = None
+ for contact in self.contacts:
+ if contact.account == account:
+ new_contact = contact
+ break
+ if new_contact is not None:
+ allowed_or_blocked = new_contact.memberships & \
+ (Membership.BLOCK | Membership.ALLOW)
+ if auto_allow and not allowed_or_blocked:
+ new_contact._add_membership(Membership.ALLOW)
+ if not was_hidden:
+ new_contact._remove_membership(Membership.PENDING)
+ if new_contact.id != Contact.BLANK_ID:
+ if not new_contact.is_member(Membership.PENDING):
+ new_contact._add_membership(Membership.FORWARD)
+ self.__common_callback('messenger-contact-added',
+ done_cb, new_contact)
+ for group in groups:
+ self.add_contact_to_group(group, new_contact)
+ self.__unfreeze_address_book()
+
+ def sync(contact_guid, was_hidden=False):
+ self.__freeze_address_book()
+ self.sync(True, (contact_added, was_hidden))
contact = self.search_contact(account, network_id)
@@ -335,9 +369,9 @@ class AddressBook(gobject.GObject):
elif contact is None or not contact.is_member(Membership.FORWARD):
scenario_class = MessengerContactAddScenario
s = scenario_class(self._ab,
- (callback,),
+ (sync,),
(self.__common_errback, failed_cb),
- account, network_id, old_memberships)
+ account, network_id)
s.auto_manage_allow_list = auto_allow
s.invite_display_name = invite_display_name
s.invite_message = invite_message
@@ -363,10 +397,14 @@ class AddressBook(gobject.GObject):
def callback():
self.__remove_contact(contact, Membership.FORWARD, done_cb)
- dc = scenario.ContactDeleteScenario(self._ab,
+ dc = scenario.ContactDeleteScenario(self._sharing,
+ self._ab,
(callback,),
(self.__common_errback, failed_cb))
dc.contact_guid = contact.id
+ dc.account = contact.account
+ dc.memberships = contact.memberships
+ dc.network = contact.network_id
dc()
def update_contact_infos(self, contact, infos, done_cb=None, failed_cb=None):
@@ -624,7 +662,6 @@ class AddressBook(gobject.GObject):
if removed_memberships & Membership.FORWARD \
and contact.is_member(Membership.FORWARD):
emit_deleted = True
- removed_memberships |= Membership.REVERSE
contact._remove_membership(removed_memberships)
# Do not use __common_callback() here to avoid race
# conditions with the event-triggered contact._reset().
@@ -655,12 +692,9 @@ class AddressBook(gobject.GObject):
member_repr += ' %s-%s' % ('D' if deleted else 'A', role)
members.append(member_repr)
logger.info('[%s] Received sync request:\n'
- '...contacts:\n'
- '%s\n'
- '...groups:\n'
- '%s\n'
- '...memberships:\n'
- '%s'
+ '...contacts: %s\n'
+ '...groups: %s\n'
+ '...memberships: %s'
% (myself, str(contacts), str(groups), str(members)))
def __update_address_book(self, ab_storage):
diff --git a/papyon/service/AddressBook/scenario/contacts/accept_invite.py b/papyon/service/AddressBook/scenario/contacts/accept_invite.py
index 023ff51..fc13053 100644
--- a/papyon/service/AddressBook/scenario/contacts/accept_invite.py
+++ b/papyon/service/AddressBook/scenario/contacts/accept_invite.py
@@ -18,7 +18,6 @@
#
from papyon.service.AddressBook.scenario.base import BaseScenario
from papyon.service.AddressBook.scenario.base import Scenario
-from messenger_contact_add import MessengerContactAddScenario
from update_memberships import UpdateMembershipsScenario
from papyon.profile import NetworkID, Membership
@@ -26,62 +25,39 @@ from papyon.profile import NetworkID, Membership
__all__ = ['AcceptInviteScenario']
class AcceptInviteScenario(BaseScenario):
- def __init__(self, ab, sharing, callback, errback,
+ def __init__(self, sharing, callback, errback,
account='',
memberships=Membership.NONE,
- network=NetworkID.MSN,
- state='Accepted'):
+ network=NetworkID.MSN):
"""Accepts an invitation.
- @param ab: the address book service
@param sharing: the membership service
@param callback: tuple(callable, *args)
@param errback: tuple(callable, *args)
+ @param account: str
+ @param memberships: int
+ @param network: int
"""
BaseScenario.__init__(self, Scenario.CONTACT_MSGR_API, callback, errback)
- self.__ab = ab
self.__sharing = sharing
-
- self.add_to_contact_list = True
-
self.account = account
self.memberships = memberships
self.network = network
- self.state = state
def execute(self):
- if self.add_to_contact_list and not (self.memberships & Membership.FORWARD):
- self.__add_messenger_contact()
- else:
- new_membership = self.memberships | Membership.ALLOW
- self.__update_memberships(None, new_membership)
-
- def __add_messenger_contact(self):
- am = MessengerContactAddScenario(self.__ab,
- (self.__add_contact_callback,),
- self._errback,
- self.account,
- self.network)
- am()
-
- def __update_memberships(self, contact, new_membership):
+ new_membership = self.memberships
+ new_membership |= Membership.ALLOW | Membership.REVERSE
+ new_membership &= ~Membership.PENDING
um = UpdateMembershipsScenario(self.__sharing,
- (self.__update_memberships_callback, contact),
+ (self.__update_memberships_callback,),
self._errback,
self._scenario,
self.account,
self.network,
- self.state,
+ 'Accepted',
self.memberships,
new_membership)
um()
- def __add_contact_callback(self, contact, memberships):
- memberships &= ~Membership.PENDING
- memberships |= Membership.REVERSE
- self.callback(contact, memberships)
-
- def __update_memberships_callback(self, memberships, contact):
- memberships &= ~Membership.PENDING
- memberships |= Membership.REVERSE
- self.callback(contact, memberships)
+ def __update_memberships_callback(self, memberships):
+ self.callback(memberships)
diff --git a/papyon/service/AddressBook/scenario/contacts/contact_delete.py b/papyon/service/AddressBook/scenario/contacts/contact_delete.py
index 2af04e3..163819c 100644
--- a/papyon/service/AddressBook/scenario/contacts/contact_delete.py
+++ b/papyon/service/AddressBook/scenario/contacts/contact_delete.py
@@ -22,15 +22,18 @@ from papyon.service.AddressBook.scenario.base import Scenario
__all__ = ['ContactDeleteScenario']
class ContactDeleteScenario(BaseScenario):
- def __init__(self, ab, callback, errback, contact_guid=''):
+ def __init__(self, sharing, ab, callback, errback, contact_guid=''):
"""Deletes a contact from the address book.
@param ab: the address book service
@param callback: tuple(callable, *args)
@param errback: tuple(callable, *args)
- @param contact_guid: the guid of the contact to delete"""
+ @param contact_guid: the guid of the contact to delete
+ @param memberships: the current memberships of the
+ contact to delete"""
BaseScenario.__init__(self, Scenario.TIMER, callback, errback)
self.__ab = ab
+ self.__sharing = sharing
self.contact_guid = contact_guid
diff --git a/papyon/service/AddressBook/scenario/contacts/contact_find.py b/papyon/service/AddressBook/scenario/contacts/contact_find.py
deleted file mode 100644
index 8641ba6..0000000
--- a/papyon/service/AddressBook/scenario/contacts/contact_find.py
+++ /dev/null
@@ -1,48 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2009 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-#
-from papyon.service.AddressBook.scenario.base import BaseScenario
-
-__all__ = ['FindContactScenario']
-
-class FindContactScenario(BaseScenario):
- """Scenario used to find a contact in the address book"""
-
- def __init__(self, ab, callback, errback, scenario, id=''):
- """Updates contact memberships.
-
- @type scenario: L{Scenario<papyon.service.AddressBook.scenario.base.Scenario>}
- @type account: account name of the contact to find
- """
- BaseScenario.__init__(self, scenario, callback, errback)
- self.__ab = ab
-
- self.id = id
-
- def execute(self):
- self.__ab.FindAll((self.__find_all_callback, self.id),
- self._errback, self._scenario, True)
-
- def __find_all_callback(self, address_book_delta, id):
- found_contact = None
- contacts = address_book_delta.contacts
- for contact in contacts:
- if contact.Id == id:
- found_contact = contact
- break
- self.callback(found_contact)
diff --git a/papyon/service/AddressBook/scenario/contacts/messenger_contact_add.py b/papyon/service/AddressBook/scenario/contacts/messenger_contact_add.py
index dd61961..b3c219c 100644
--- a/papyon/service/AddressBook/scenario/contacts/messenger_contact_add.py
+++ b/papyon/service/AddressBook/scenario/contacts/messenger_contact_add.py
@@ -19,10 +19,9 @@
from papyon.service.AddressBook.scenario.base import BaseScenario
from papyon.service.AddressBook.scenario.base import Scenario
-from contact_find import FindContactScenario
from papyon.service.description.AB.constants import ContactEmailType
-from papyon.profile import ContactType, Membership, NetworkID
+from papyon.profile import ContactType, NetworkID
__all__ = ['MessengerContactAddScenario']
@@ -30,7 +29,6 @@ class MessengerContactAddScenario(BaseScenario):
def __init__(self, ab, callback, errback,
account='',
network_id=NetworkID.MSN,
- memberships=Membership.NONE,
contact_type=ContactType.REGULAR,
contact_info={},
invite_display_name='',
@@ -53,7 +51,6 @@ class MessengerContactAddScenario(BaseScenario):
self.invite_display_name = invite_display_name
self.invite_message = invite_message
self.auto_manage_allow_list = True
- self.memberships = memberships
def execute(self):
invite_info = { 'display_name' : self.invite_display_name,
@@ -70,28 +67,9 @@ class MessengerContactAddScenario(BaseScenario):
raise NotImplementedError("Network ID '%s' is not implemented" %
self.network_id)
- self._ab.ContactAdd((self.__contact_add_callback,),
+ self._ab.ContactAdd(self._callback,
self._errback,
self._scenario,
self.contact_info,
invite_info,
self.auto_manage_allow_list)
-
- def __contact_add_callback(self, contact_guid):
- self.memberships |= Membership.FORWARD
-
- # ContactAdd automatically added the contact to the allow list if
- # it wasn't already allowed or blocked
- allowed_or_blocked = self.memberships & (Membership.BLOCK | Membership.ALLOW)
- if self.auto_manage_allow_list and not allowed_or_blocked:
- self.memberships |= Membership.ALLOW
-
- fc = FindContactScenario(self._ab,
- (self.__find_contact_callback,),
- self._errback,
- self._scenario)
- fc.id = contact_guid
- fc()
-
- def __find_contact_callback(self, contact):
- self.callback(contact, self.memberships)
diff --git a/papyon/service/AddressBook/scenario/contacts/update_memberships.py b/papyon/service/AddressBook/scenario/contacts/update_memberships.py
index de2418c..72713d6 100644
--- a/papyon/service/AddressBook/scenario/contacts/update_memberships.py
+++ b/papyon/service/AddressBook/scenario/contacts/update_memberships.py
@@ -110,7 +110,8 @@ class UpdateMembershipsScenario(BaseScenario):
if self.__late_pending_delete:
membership = UpdateMembershipsScenario.__mapping[Membership.PENDING]
callback = list(self._callback)
- callback.insert(1, self.__done)
+ callback.insert(1, self.__done & ~Membership.PENDING)
+ callback = tuple(callback) # Required to pass type check in run()
self.__sharing.DeleteMember(callback,
(self.__common_errback, self.__done,
Membership.PENDING),