05a2dad9c96a44068ae4c787ae9a6c788e957340
[platform/upstream/folks.git] / backends / ofono / ofono-persona-store.vala
1 /*
2  * Copyright (C) 2012 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  *          Jeremy Whiting <jeremy.whiting@collabora.co.uk>
19  *
20  * Based on kf-persona-store.vala by:
21  *       Travis Reitter <travis.reitter@collabora.co.uk>
22  *       Philip Withnall <philip.withnall@collabora.co.uk>
23  */
24
25 using GLib;
26 using Gee;
27 using Folks;
28 using Folks.Backends.Ofono;
29
30 /**
31  * A persona store which is associated with a single Ofono device. It will 
32  * create a {@link Persona} for each contact on the SIM card phonebook.
33  *
34  * @since UNRELEASED
35  */
36 public class Folks.Backends.Ofono.PersonaStore : Folks.PersonaStore
37 {
38   private HashMap<string, Persona> _personas;
39   private Map<string, Persona> _personas_ro;
40   private bool _is_prepared = false;
41   private bool _prepare_pending = false;
42   private bool _is_quiescent = false;
43
44   private static string[] _always_writeable_properties = {};
45   private ObjectPath? _path = null;
46
47   private org.ofono.Phonebook? _ofono_phonebook = null;
48
49   /**
50    * {@inheritDoc}
51    */
52   public override string type_id { get { return BACKEND_NAME; } }
53
54   /**
55    * {@inheritDoc}
56    */
57   public override MaybeBool can_add_personas
58     {
59       get { return MaybeBool.FALSE; }
60     }
61
62   /**
63    * {@inheritDoc}
64    */
65   public override MaybeBool can_alias_personas
66     {
67       get { return MaybeBool.FALSE; }
68     }
69
70   /**
71    * {@inheritDoc}
72    */
73   public override MaybeBool can_group_personas
74     {
75       get { return MaybeBool.FALSE; }
76     }
77
78   /**
79    * {@inheritDoc}
80    */
81   public override MaybeBool can_remove_personas
82     {
83       get { return MaybeBool.FALSE; }
84     }
85
86   /**
87    * {@inheritDoc}
88    */
89   public override bool is_prepared
90     {
91       get { return this._is_prepared; }
92     }
93
94   /**
95    * {@inheritDoc}
96    */
97   public override bool is_quiescent
98     {
99       get { return this._is_quiescent; }
100     }
101
102   /**
103    * {@inheritDoc}
104    */
105   public override string[] always_writeable_properties
106     {
107       get { return Ofono.PersonaStore._always_writeable_properties; }
108     }
109
110   /**
111    * {@inheritDoc}
112    */
113   public override Map<string, Persona> personas
114     {
115       get { return this._personas_ro; }
116     }
117
118   /**
119    * Create a new PersonaStore.
120    *
121    * Create a new persona store to expose the {@link Persona}s provided by the
122    * modem with the given address.
123    *
124    * @param path the D-Bus object path of this modem
125    * @param alias the name this modem should display to users
126    *
127    * @since UNRELEASED
128    */
129   public PersonaStore (ObjectPath path, string alias)
130     {
131       Object (id: path,
132               display_name: alias);
133
134       this.trust_level = PersonaStoreTrust.FULL;
135       this._personas = new HashMap<string, Persona> ();
136       this._personas_ro = this._personas.read_only_view;
137       this._path = path;
138     }
139
140   private void _property_changed (string property, Variant value)
141     {
142       if (property == "Present" && value.get_boolean () == false)
143         {
144           this._remove_self ();
145         }
146     }
147     
148   private void _remove_self ()
149     {
150       /* Marshal the personas from a Collection to a Set. */
151       var removed_personas = new HashSet<Persona> ();
152       var iter = this._personas.map_iterator ();
153       
154       while (iter.next () == true)
155       {
156         removed_personas.add (iter.get_value ());
157       }
158       
159       this._emit_personas_changed (null, removed_personas);
160       this.removed ();
161     }
162     
163   private string[] _split_all_vcards (string all_vcards)
164     {
165       /* Ofono vcards are in vcard 3.0 format and can include the following:
166        * FN, CATEGORIES, EMAIL and IMPP fields. */
167       string[] lines = all_vcards.split ("\n");
168       string[] vcards = {};
169       string vcard = "";
170
171       foreach (string line in lines)
172         {
173           vcard += line;
174           vcard += "\n";
175
176           if (line.strip () == "END:VCARD")
177             {
178               vcards += vcard;
179               vcard = "";
180             }
181         }
182
183       return vcards;
184     }
185
186   /**
187    * {@inheritDoc}
188    */
189   public override async void prepare () throws IOError, DBusError
190     {
191       Internal.profiling_start ("preparing Ofono.PersonaStore (ID: %s)",
192           this.id);
193
194       if (this._is_prepared || this._prepare_pending)
195         {
196           return;
197         }
198
199       try
200         {
201           this._prepare_pending = true;
202           this._ofono_phonebook = yield Bus.get_proxy (BusType.SYSTEM,
203                                                              "org.ofono",
204                                                              this._path);
205
206           org.ofono.SimManager sim_manager = yield Bus.get_proxy (BusType.SYSTEM,
207                                                                         "org.ofono",
208                                                                         this._path);
209           sim_manager.PropertyChanged.connect (this._property_changed);
210
211           string all_vcards = this._ofono_phonebook.Import ();
212
213           string[] vcards = this._split_all_vcards (all_vcards);
214
215           HashSet<Persona> added_personas = new HashSet<Persona> ();
216
217           foreach (string vcard in vcards)
218             {
219               Persona persona = new Persona (vcard, this);
220               this._personas.set (persona.iid, persona);
221               added_personas.add (persona);
222             }
223
224           if (this._personas.size > 0)
225             {
226               this._emit_personas_changed (added_personas, null);
227             }
228         }
229       catch (GLib.DBusError e)
230         {
231           warning ("DBus Error has occurred when fetching ofono phonebook, %s", e.message);
232           this._remove_self ();
233         }
234       catch (GLib.IOError e)
235         {
236           warning ("IO Error has occurred when fetching ofono phonebook, %s", e.message);
237           this._remove_self ();
238         }
239       finally
240         {
241           this._is_prepared = true;
242           this.notify_property ("is-prepared");
243
244           /* We've finished loading all the personas we know about */
245           this._is_quiescent = true;
246           this.notify_property ("is-quiescent");
247
248           this._prepare_pending = false;
249         }
250
251       Internal.profiling_end ("preparing Ofono.PersonaStore (ID: %s)", this.id);
252     }
253
254   /**
255    * Remove a {@link Persona} from the PersonaStore.
256    *
257    * See {@link Folks.PersonaStore.remove_persona}.
258    *
259    * @throws Folks.PersonaStoreError.READ_ONLY every time since the
260    * Ofono backend is read-only.
261    *
262    * @param persona the {@link Persona} to remove.
263    *
264    * @since UNRELEASED
265    */
266   public override async void remove_persona (Folks.Persona persona)
267       throws Folks.PersonaStoreError
268     {
269       throw new PersonaStoreError.READ_ONLY (
270           "Personas cannot be removed from this store.");
271     }
272
273   /**
274    * Add a new {@link Persona} to the PersonaStore.
275    *
276    * See {@link Folks.PersonaStore.add_persona_from_details}.
277    *
278    * @throws Folks.PersonaStoreError.READ_ONLY every time since the
279    * Ofono backend is read-only.
280    *
281    * @param details the details of the {@link Persona} to add.
282    *
283    * @since UNRELEASED
284    */
285   public override async Folks.Persona? add_persona_from_details (
286       HashTable<string, Value?> details) throws Folks.PersonaStoreError
287     {
288       throw new PersonaStoreError.READ_ONLY (
289           "Personas cannot be added to this store.");
290     }
291 }