Only warn about unknown personas if the account is already connected
[platform/upstream/folks.git] / backends / telepathy / tpf-persona.vala
1 /*
2  * Copyright (C) 2010 Collabora Ltd.
3  *
4  * This library is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License as published by
6  * the Free Software Foundation, either version 2.1 of the License, or
7  * (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this library.  If not, see <http://www.gnu.org/licenses/>.
16  *
17  * Authors:
18  *       Travis Reitter <travis.reitter@collabora.co.uk>
19  */
20
21 using Gee;
22 using GLib;
23 using Tp;
24 using Folks;
25
26 public class Tpf.Persona : Folks.Persona, Alias, Avatar, Folks.Capabilities,
27        Groups, Presence, Favourite
28 {
29   private HashTable<string, bool> _groups;
30   private bool _is_favourite;
31
32   /* interface Alias */
33   public override string alias { get; set; }
34
35   /* interface Avatar */
36   public override File avatar { get; set; }
37
38   /* interface Capabilities */
39   public override CapabilitiesFlags capabilities { get; private set; }
40
41   /* interface Presence */
42   public override Folks.PresenceType presence_type { get; private set; }
43   public override string presence_message { get; private set; }
44
45   /* interface Favourite */
46   public override bool is_favourite
47     {
48       get { return this._is_favourite; }
49
50       set
51         {
52           if (this._is_favourite == value)
53             return;
54
55           ((Tpf.PersonaStore) this.store).change_is_favourite (this, value);
56           this._is_favourite = value;
57         }
58     }
59
60   /* interface Groups */
61   public HashTable<string, bool> groups
62     {
63       get { return this._groups; }
64
65       set
66         {
67           value.foreach ((k, v) =>
68             {
69               var group = (string) k;
70               if (this._groups.lookup (group) == false)
71                 this._change_group (group, true);
72             });
73
74           this._groups.foreach ((k, v) =>
75             {
76               var group = (string) k;
77               if (value.lookup (group) == false)
78                 this._change_group (group, true);
79             });
80         }
81     }
82
83   public void change_group (string group, bool is_member)
84     {
85       if (_change_group (group, is_member))
86         {
87           ((Tpf.PersonaStore) this.store).change_group_membership (this, group,
88             is_member);
89
90           this.group_changed (group, is_member);
91         }
92     }
93
94   private bool _change_group (string group, bool is_member)
95     {
96       bool changed = false;
97
98       if (is_member)
99         {
100           if (this._groups.lookup (group) != true)
101             {
102               this._groups.insert (group, true);
103               changed = true;
104             }
105         }
106       else
107         changed = this._groups.remove (group);
108
109       return changed;
110     }
111
112   public Contact contact { get; construct; }
113
114   public Persona (Contact contact, PersonaStore store) throws Tp.Error
115     {
116       /* FIXME: There is the possibility of a crash in the error condition below
117        * due to bgo#604299, where the C self variable isn't initialised until we
118        * chain up to the Object constructor, below. */
119       var uid = contact.get_identifier ();
120       if (uid == null || uid == "")
121         throw new Tp.Error.INVALID_ARGUMENT ("contact has an invalid UID");
122
123       var account = account_for_connection (contact.get_connection ());
124       var account_id = ((Proxy) account).object_path;
125       /* this isn't meant to convey any real information, so no need to escape
126        * existing delimiters */
127       var iid = "telepathy:" + account_id + ":" + uid;
128
129       var alias = contact.get_alias ();
130       if (alias == null || alias == "")
131         alias = uid;
132
133       /* TODO: implement something like Empathy's tp_caps_to_capabilities() and
134        * fill in the capabilities as appropriate */
135       debug ("capabilities not implemented");
136
137       Object (alias: alias,
138               contact: contact,
139               iid: iid,
140               uid: uid,
141               store: store);
142
143       this._groups = new HashTable<string, bool> (str_hash, str_equal);
144
145       contact.notify["avatar-file"].connect ((s, p) =>
146         {
147           this.contact_notify_avatar ();
148         });
149       this.contact_notify_avatar ();
150
151       contact.notify["presence-message"].connect ((s, p) =>
152         {
153           this.contact_notify_presence_message ();
154         });
155       contact.notify["presence-type"].connect ((s, p) =>
156         {
157           this.contact_notify_presence_type ();
158         });
159       this.contact_notify_presence_message ();
160       this.contact_notify_presence_type ();
161
162       this.store.group_members_changed.connect ((s, group, added, removed) =>
163         {
164           if (added.find (this) != null)
165             this._change_group (group, true);
166
167           if (removed.find (this) != null)
168             this._change_group (group, false);
169         });
170
171       this.store.group_removed.connect ((s, group, error) =>
172         {
173           if (error != null)
174             warning ("group invalidated: %s", error.message);
175
176           this._change_group (group, false);
177         });
178     }
179
180   private static Account? account_for_connection (Connection conn)
181     {
182       var manager = AccountManager.dup ();
183       unowned GLib.List<Account> accounts = manager.get_valid_accounts ();
184
185       Account account_found = null;
186       accounts.foreach ((l) =>
187         {
188           var account = (Account) l;
189           if (account.get_connection () == conn)
190             {
191               account_found = account;
192               return;
193             }
194         });
195
196       return account_found;
197     }
198
199   private void contact_notify_presence_message ()
200     {
201       this.presence_message = this.contact.get_presence_message ();
202     }
203
204   private void contact_notify_presence_type ()
205     {
206       this.presence_type = folks_presence_type_from_tp (
207           this.contact.get_presence_type ());
208     }
209
210   private static PresenceType folks_presence_type_from_tp (
211       Tp.ConnectionPresenceType type)
212     {
213       switch (type)
214         {
215           case Tp.ConnectionPresenceType.AVAILABLE:
216             return PresenceType.AVAILABLE;
217           case Tp.ConnectionPresenceType.AWAY:
218             return PresenceType.AWAY;
219           case Tp.ConnectionPresenceType.BUSY:
220             return PresenceType.BUSY;
221           case Tp.ConnectionPresenceType.ERROR:
222             return PresenceType.ERROR;
223           case Tp.ConnectionPresenceType.EXTENDED_AWAY:
224             return PresenceType.EXTENDED_AWAY;
225           case Tp.ConnectionPresenceType.HIDDEN:
226             return PresenceType.HIDDEN;
227           case Tp.ConnectionPresenceType.OFFLINE:
228             return PresenceType.OFFLINE;
229           case Tp.ConnectionPresenceType.UNKNOWN:
230             return PresenceType.UNKNOWN;
231           case Tp.ConnectionPresenceType.UNSET:
232             return PresenceType.UNSET;
233           default:
234             return PresenceType.UNKNOWN;
235         }
236     }
237
238   private void contact_notify_avatar ()
239     {
240       var file = this.contact.get_avatar_file ();
241       if (this.avatar != file)
242         this.avatar = file;
243     }
244 }