1c338f878ffe4a70ec05bab1190263e26f3dbf92
[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     Aliasable,
33     IMable
34 {
35   private unowned GLib.KeyFile _key_file;
36   /* FIXME: As described in the IMable interface, we have to use
37    * GenericArray<string> here rather than just string[], as null-terminated
38    * arrays aren't supported as generic types. */
39   private HashTable<string, LinkedHashSet<string>> _im_addresses;
40   private string _alias;
41   private const string[] _linkable_properties = { "im-addresses" };
42
43   /**
44    * {@inheritDoc}
45    */
46   public override string[] linkable_properties
47     {
48       get { return this._linkable_properties; }
49     }
50
51   /**
52    * {@inheritDoc}
53    *
54    * @since 0.1.15
55    */
56   public string alias
57     {
58       get { return this._alias; }
59
60       set
61         {
62           if (this._alias == value)
63             return;
64
65           debug ("Setting alias of Kf.Persona '%s' to '%s'.", this.uid, value);
66
67           this._alias = value;
68           this._key_file.set_string (this.display_id, "__alias", value);
69
70           ((Kf.PersonaStore) this.store).save_key_file.begin ();
71         }
72     }
73
74   /**
75    * {@inheritDoc}
76    */
77   public HashTable<string, LinkedHashSet<string>> im_addresses
78     {
79       get
80         { return this._im_addresses; }
81
82       set
83         {
84           /* Remove the current IM addresses from the key file */
85           this._im_addresses.foreach ((protocol, v) =>
86             {
87               try
88                 {
89                   this._key_file.remove_key (this.display_id, protocol);
90                 }
91               catch (KeyFileError e)
92                 {
93                   /* Ignore the error, since it's just a group or key not found
94                    * error. */
95                 }
96             });
97
98           /* Add the new IM addresses to the key file and build a normalised
99            * table of them to set as the new property value */
100           HashTable<string, LinkedHashSet<string>> im_addresses =
101               new HashTable<string, LinkedHashSet<string>> (str_hash, str_equal);
102
103           value.foreach ((k, v) =>
104             {
105               unowned string protocol = (string) k;
106               unowned LinkedHashSet<string?> addresses =
107                   (LinkedHashSet<string?>) v;
108               LinkedHashSet<string> normalized_addresses =
109                   new LinkedHashSet<string> ();
110
111               foreach (string address in addresses)
112                 {
113                   string normalized_address;
114                   try
115                     {
116                       normalized_address = IMable.normalise_im_address (
117                           address, protocol);
118                     }
119                   catch (IMableError e)
120                     {
121                       /* Somehow an error has crept into the user's
122                        * relationships.ini. Warn of it and ignore the IM
123                        * address. */
124                       warning (e.message);
125                       continue;
126                     }
127
128                     normalized_addresses.add (normalized_address);
129                 }
130
131               string[] addrs = (string[]) normalized_addresses.to_array ();
132               addrs.length = normalized_addresses.size;
133
134               this._key_file.set_string_list (this.display_id, protocol, addrs);
135               im_addresses.insert (protocol, normalized_addresses);
136             });
137
138           this._im_addresses = im_addresses;
139
140           /* Get the PersonaStore to save the key file */
141           ((Kf.PersonaStore) this.store).save_key_file.begin ();
142         }
143     }
144
145   /**
146    * Create a new persona.
147    *
148    * Create a new persona for the {@link PersonaStore} `store`, representing
149    * the Persona given by the group `uid` in the key file `key_file`.
150    */
151   public Persona (KeyFile key_file, string id, Folks.PersonaStore store)
152     {
153       var iid = store.id + ":" + id;
154       var uid = this.build_uid ("key-file", store.id, id);
155
156       Object (display_id: id,
157               iid: iid,
158               uid: uid,
159               store: store,
160               is_user: false);
161
162       debug ("Adding key-file Persona '%s' (IID '%s', group '%s')", uid, iid,
163           id);
164
165       this._key_file = key_file;
166       this._im_addresses = new HashTable<string, LinkedHashSet<string>> (
167           str_hash, str_equal);
168
169       /* Load the IM addresses from the key file */
170       try
171         {
172           var keys = this._key_file.get_keys (this.display_id);
173           foreach (unowned string key in keys)
174             {
175               /* Alias */
176               if (key == "__alias")
177                 {
178                   this._alias = this._key_file.get_string (this.display_id,
179                       key);
180                   debug ("    Loaded alias '%s'.", this._alias);
181                   continue;
182                 }
183
184               /* IM addresses */
185               unowned string protocol = key;
186               var im_addresses = this._key_file.get_string_list (
187                   this.display_id, protocol);
188
189               var address_set = new LinkedHashSet<string> ();
190
191               foreach (var im_address in im_addresses)
192                 {
193                   string address;
194                   try
195                     {
196                       address = IMable.normalise_im_address (im_address,
197                           protocol);
198                     }
199                   catch (IMableError e)
200                     {
201                       /* Warn of and ignore any invalid IM addresses */
202                       warning (e.message);
203                       continue;
204                     }
205                     address_set.add (address);
206                 }
207
208               this._im_addresses.insert (protocol, address_set);
209             }
210         }
211       catch (KeyFileError e)
212         {
213           /* We get a GROUP_NOT_FOUND exception if we're creating a new
214            * Persona, since it doesn't yet exist in the key file. We shouldn't
215            * get any other exceptions, since we're iterating through a list of
216            * keys we've just retrieved. */
217           if (!(e is KeyFileError.GROUP_NOT_FOUND))
218             {
219               /* Translators: the parameter is an error message. */
220               warning (_("Couldn't load data from key file: %s"), e.message);
221             }
222         }
223     }
224
225   /**
226    * {@inheritDoc}
227    */
228   public override void linkable_property_to_links (string prop_name,
229       Folks.Persona.LinkablePropertyCallback callback)
230     {
231       if (prop_name == "im-addresses")
232         {
233           this.im_addresses.foreach ((k, v) =>
234             {
235               unowned string protocol = (string) k;
236               unowned LinkedHashSet<string> im_addresses =
237                   (LinkedHashSet<string>) v;
238
239               foreach (string address in im_addresses)
240                   callback (protocol + ":" + address);
241             });
242         }
243       else
244         {
245           /* Chain up */
246           base.linkable_property_to_links (prop_name, callback);
247         }
248     }
249 }