Change WebServiceDetails.…addresses to be a MultiMap<string, string>
[platform/upstream/folks.git] / backends / key-file / kf-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  *       Philip Withnall <philip.withnall@collabora.co.uk>
19  */
20
21 using GLib;
22 using Gee;
23 using Folks;
24 using Folks.Backends.Kf;
25
26 /**
27  * A persona subclass which represents a single persona from a simple key file.
28  *
29  * @since 0.1.13
30  */
31 public class Folks.Backends.Kf.Persona : Folks.Persona,
32     AliasDetails,
33     ImDetails,
34     WebServiceDetails
35 {
36   private unowned GLib.KeyFile _key_file;
37   private HashMultiMap<string, string> _im_addresses;
38   private HashMultiMap<string, string> _web_service_addresses;
39   private string _alias;
40   private const string[] _linkable_properties =
41     {
42       "im-addresses",
43       "web-service-addresses"
44     };
45
46   /**
47    * {@inheritDoc}
48    */
49   public override string[] linkable_properties
50     {
51       get { return this._linkable_properties; }
52     }
53
54   /**
55    * {@inheritDoc}
56    *
57    * @since 0.1.15
58    */
59   public string alias
60     {
61       get { return this._alias; }
62
63       set
64         {
65           if (this._alias == value)
66             return;
67
68           debug ("Setting alias of Kf.Persona '%s' to '%s'.", this.uid, value);
69
70           this._alias = value;
71           this._key_file.set_string (this.display_id, "__alias", value);
72
73           ((Kf.PersonaStore) this.store).save_key_file.begin ();
74         }
75     }
76
77   /**
78    * {@inheritDoc}
79    */
80   public MultiMap<string, string> im_addresses
81     {
82       get
83         { return this._im_addresses; }
84
85       set
86         {
87           /* Remove the current IM addresses from the key file */
88           foreach (var protocol in this._im_addresses.get_keys ())
89             {
90               try
91                 {
92                   this._key_file.remove_key (this.display_id, protocol);
93                 }
94               catch (KeyFileError e)
95                 {
96                   /* Ignore the error, since it's just a group or key not found
97                    * error. */
98                 }
99             }
100
101           /* Add the new IM addresses to the key file and build a normalised
102            * table of them to set as the new property value */
103           var im_addresses = new HashMultiMap<string, string> ();
104
105           foreach (var protocol in value.get_keys ())
106             {
107               var addresses = value.get (protocol);
108               var normalised_addresses = new HashSet<string> ();
109
110               foreach (string address in addresses)
111                 {
112                   string normalised_address;
113                   try
114                     {
115                       normalised_address = ImDetails.normalise_im_address (
116                           address, protocol);
117                     }
118                   catch (ImDetailsError e)
119                     {
120                       /* Somehow an error has crept into the user's
121                        * relationships.ini. Warn of it and ignore the IM
122                        * address. */
123                       warning (e.message);
124                       continue;
125                     }
126
127                   normalised_addresses.add (normalised_address);
128                   im_addresses.set (protocol, normalised_address);
129                 }
130
131               string[] addrs = (string[]) normalised_addresses.to_array ();
132               addrs.length = normalised_addresses.size;
133
134               this._key_file.set_string_list (this.display_id, protocol, addrs);
135             }
136
137           this._im_addresses = im_addresses;
138
139           /* Get the PersonaStore to save the key file */
140           ((Kf.PersonaStore) this.store).save_key_file.begin ();
141         }
142     }
143
144   /**
145    * {@inheritDoc}
146    */
147   public MultiMap<string, string> web_service_addresses
148     {
149       get
150         { return this._web_service_addresses; }
151
152       set
153         {
154           /* Remove the current web service addresses from the key file */
155           foreach (var web_service in this._web_service_addresses.get_keys ())
156             {
157               try
158                 {
159                   this._key_file.remove_key (this.display_id, "web-service." + web_service);
160                 }
161               catch (KeyFileError e)
162                 {
163                   /* Ignore the error, since it's just a group or key not found
164                    * error. */
165                 }
166             }
167
168           /* Add the new web service addresses to the key file and build a
169            * table of them to set as the new property value */
170           var web_service_addresses = new HashMultiMap<string, string> ();
171
172           foreach (var web_service in value.get_keys ())
173             {
174               var addresses = value.get (web_service);
175
176               string[] addrs = (string[]) addresses.to_array ();
177               addrs.length = addresses.size;
178
179               this._key_file.set_string_list (this.display_id,
180                   "web-service." + web_service, addrs);
181
182               foreach (var address in addresses)
183                 {
184                   web_service_addresses.set (web_service, address);
185                 }
186             }
187
188           this._web_service_addresses = web_service_addresses;
189
190           /* Get the PersonaStore to save the key file */
191           ((Kf.PersonaStore) this.store).save_key_file.begin ();
192         }
193     }
194
195   /**
196    * Create a new persona.
197    *
198    * Create a new persona for the {@link PersonaStore} `store`, representing
199    * the Persona given by the group `uid` in the key file `key_file`.
200    */
201   public Persona (KeyFile key_file, string id, Folks.PersonaStore store)
202     {
203       var iid = store.id + ":" + id;
204       var uid = this.build_uid ("key-file", store.id, id);
205
206       Object (display_id: id,
207               iid: iid,
208               uid: uid,
209               store: store,
210               is_user: false);
211
212       debug ("Adding key-file Persona '%s' (IID '%s', group '%s')", uid, iid,
213           id);
214
215       this._key_file = key_file;
216       this._im_addresses = new HashMultiMap<string, string> ();
217       this._web_service_addresses = new HashMultiMap<string, string> ();
218
219       /* Load the IM addresses from the key file */
220       try
221         {
222           var keys = this._key_file.get_keys (this.display_id);
223           foreach (unowned string key in keys)
224             {
225               /* Alias */
226               if (key == "__alias")
227                 {
228                   this._alias = this._key_file.get_string (this.display_id,
229                       key);
230                   debug ("    Loaded alias '%s'.", this._alias);
231                   continue;
232                 }
233
234               /* Web service addresses */
235               var decomposed_key = key.split(".", 2);
236               if (decomposed_key.length == 2 &&
237                   decomposed_key[0] == "web-service")
238                 {
239                   unowned string web_service = decomposed_key[1];
240                   var web_service_addresses = this._key_file.get_string_list (
241                       this.display_id, web_service);
242
243                   foreach (var web_service_address in web_service_addresses)
244                     {
245                       this._web_service_addresses.set (web_service,
246                           web_service_address);
247                     }
248
249                   continue;
250                 }
251
252               /* IM addresses */
253               unowned string protocol = key;
254               var im_addresses = this._key_file.get_string_list (
255                   this.display_id, protocol);
256
257               foreach (var im_address in im_addresses)
258                 {
259                   string address;
260                   try
261                     {
262                       address = ImDetails.normalise_im_address (im_address,
263                           protocol);
264                     }
265                   catch (ImDetailsError e)
266                     {
267                       /* Warn of and ignore any invalid IM addresses */
268                       warning (e.message);
269                       continue;
270                     }
271
272                   this._im_addresses.set (protocol, address);
273                 }
274             }
275         }
276       catch (KeyFileError e)
277         {
278           /* We get a GROUP_NOT_FOUND exception if we're creating a new
279            * Persona, since it doesn't yet exist in the key file. We shouldn't
280            * get any other exceptions, since we're iterating through a list of
281            * keys we've just retrieved. */
282           if (!(e is KeyFileError.GROUP_NOT_FOUND))
283             {
284               /* Translators: the parameter is an error message. */
285               warning (_("Couldn't load data from key file: %s"), e.message);
286             }
287         }
288     }
289
290   /**
291    * {@inheritDoc}
292    */
293   public override void linkable_property_to_links (string prop_name,
294       Folks.Persona.LinkablePropertyCallback callback)
295     {
296       if (prop_name == "im-addresses")
297         {
298           foreach (var protocol in this._im_addresses.get_keys ())
299             {
300               var im_addresses = this._im_addresses.get (protocol);
301
302               foreach (string address in im_addresses)
303                   callback (protocol + ":" + address);
304             }
305         }
306       else if (prop_name == "web-service-addresses")
307         {
308           foreach (var web_service in this.web_service_addresses.get_keys ())
309             {
310               var web_service_addresses =
311                   this._web_service_addresses.get (web_service);
312
313               foreach (string address in web_service_addresses)
314                   callback (web_service + ":" + address);
315             }
316         }
317       else
318         {
319           /* Chain up */
320           base.linkable_property_to_links (prop_name, callback);
321         }
322     }
323 }