From e002a45ad2e1ebb0f0746735beb98d512790a4e0 Mon Sep 17 00:00:00 2001 From: "jerico.dev" Date: Mon, 20 Jun 2011 23:56:31 -0300 Subject: handle "contact already exists" exception when trying to add/accept a hidden contact (closes #38336) --- papyon/service/AddressBook/ab.py | 28 +++++++ papyon/service/AddressBook/address_book.py | 90 +++++++++++++++------- .../AddressBook/scenario/contacts/accept_invite.py | 48 +++--------- .../scenario/contacts/contact_delete.py | 7 +- .../AddressBook/scenario/contacts/contact_find.py | 48 ------------ .../scenario/contacts/messenger_contact_add.py | 26 +------ .../scenario/contacts/update_memberships.py | 3 +- 7 files changed, 111 insertions(+), 139 deletions(-) delete mode 100644 papyon/service/AddressBook/scenario/contacts/contact_find.py 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} - @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), -- cgit v1.2.3