55ab0bbd19270e2fda3bd2173e5c109608463cc8
[moonshot-ui.git] / src / moonshot-identities-manager.vala
1 /*
2  * Copyright (c) 2011-2014, JANET(UK)
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * 3. Neither the name of JANET(UK) nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31 */
32 using Gee;
33
34 public class Password {
35 #if GNOME_KEYRING
36     private unowned string _password;
37     public string password {
38         get {
39             return _password;
40         }
41         set {
42             if (_password != null) {
43                 GnomeKeyring.memory_free((void *)_password);
44                 _password = null;
45             }
46             if (value != null)
47                 _password = GnomeKeyring.memory_strdup(value); 
48         }
49     }
50 #else
51     public string password { get; set; default = null; }
52 #endif
53
54     public Password(string in_password) {
55         password = in_password;
56     }
57
58     ~Password() {
59         password = null;
60     }
61 }
62
63 public class PasswordHashTable : Object {
64     private HashTable<string, Password> password_table;
65
66     private static string ComputeHashKey(IdCard card, IIdentityCardStore store) {
67         return "%s_store_%d".printf( card.display_name, store.get_store_type() );
68     }
69
70     public void CachePassword(IdCard card, IIdentityCardStore store) {
71         password_table.replace(ComputeHashKey(card, store), new Password(card.password));
72     }
73
74     public void RemovePassword(IdCard card, IIdentityCardStore store) {
75         password_table.remove(ComputeHashKey(card, store));
76     }
77     public void RetrievePassword(IdCard card, IIdentityCardStore store) {
78         weak Password password = password_table.lookup(ComputeHashKey(card, store));
79         if (password != null) {
80             card.password = password.password;
81         }
82     }
83     public PasswordHashTable() {
84         password_table = new HashTable<string, Password>(GLib.str_hash, GLib.str_equal);
85     }
86 }
87
88 public class IdentityManagerModel : Object {
89     private const string FILE_NAME = "identities.txt";
90     private PasswordHashTable password_table;
91     private IIdentityCardStore store;
92     public LinkedList<IdCard>  get_card_list() {
93          var identities = store.get_card_list();
94          identities.sort( (a, b) => {
95              IdCard id_a = (IdCard )a;
96              IdCard id_b = (IdCard )b;
97              if (id_a.IsNoIdentity() && !id_b.IsNoIdentity()) {
98                 return -1;
99              } else if (id_b.IsNoIdentity() && !id_a.IsNoIdentity()) {
100                 return 1;
101              }
102              return strcmp(id_a.display_name, id_b.display_name);
103          });
104          if (identities.is_empty || !identities[0].IsNoIdentity())
105              identities.insert(0, IdCard.NewNoIdentity());
106          foreach (IdCard id_card in identities) {
107              if (!id_card.store_password) {
108                  password_table.RetrievePassword(id_card, store);
109              }
110          }
111          return identities;
112     }
113     public signal void card_list_changed();
114
115     /* This method finds a valid display name */
116     public bool display_name_is_valid (string name,
117                                        out string? candidate)
118     {
119         if (&candidate != null)
120             candidate = null;
121         foreach (IdCard id_card in this.store.get_card_list())
122         {
123             if (id_card.display_name == name)
124             {
125                 if (&candidate != null)
126                 {
127                     for (int i=0; i<1000; i++)
128                     {
129                         string tmp = "%s %d".printf (name, i);
130                         if (display_name_is_valid (tmp, null))
131                         {
132                     
133                             candidate = tmp;
134                             break;
135                         }
136                     }
137                 }
138                 return false;
139             }
140         }
141         return true;
142     }
143
144     private bool remove_duplicates(IdCard card)
145     {
146         bool duplicate_found = false;
147         bool found = false;
148         do {
149            var cards = this.store.get_card_list();
150            found = false;
151            foreach (IdCard id_card in cards) {
152                if ((card != id_card) && (id_card.nai == card.nai)) {
153                   stdout.printf("removing duplicate id for '%s'\n", card.nai);
154                   remove_card_internal(id_card);
155                   found = duplicate_found = true;
156                   break;
157                }
158            }
159         } while (found);
160         return duplicate_found;
161     }
162
163     public IdCard? find_id_card(string nai, bool force_flat_file_store) {
164         IdCard? retval = null;
165         IIdentityCardStore.StoreType saved_store_type = get_store_type();
166         if (force_flat_file_store)
167             set_store_type(IIdentityCardStore.StoreType.FLAT_FILE);
168
169         foreach (IdCard id in get_card_list()) {
170             if (id.nai == nai) {
171                 retval = id;
172                 break;
173             }
174         }
175         set_store_type(saved_store_type);
176         if (force_flat_file_store && 
177             (saved_store_type != IIdentityCardStore.StoreType.FLAT_FILE))
178             card_list_changed();
179         return retval;
180     }
181
182     public void add_card(IdCard card, bool force_flat_file_store) {
183         if (card.temporary)
184             return;
185
186         string candidate;
187         IIdentityCardStore.StoreType saved_store_type = get_store_type();
188
189         if (force_flat_file_store)
190             set_store_type(IIdentityCardStore.StoreType.FLAT_FILE);
191
192         remove_duplicates(card);
193
194         if (!display_name_is_valid (card.display_name, out candidate))
195         {
196           card.display_name = candidate;
197         }
198
199         if (!card.store_password)
200             password_table.CachePassword(card, store);
201         store.add_card(card);
202         set_store_type(saved_store_type);
203         card_list_changed();
204      }
205
206      public IdCard update_card(IdCard card) {
207         IdCard retval;
208         if (card.temporary) {
209             retval = card;
210             return retval;
211         }
212             
213         if (!card.store_password)
214             password_table.CachePassword(card, store);
215         else
216             password_table.RemovePassword(card, store);
217         retval = store.update_card(card);
218         card_list_changed();
219         return retval;
220      }
221
222      private bool remove_card_internal(IdCard card) {
223          if (card.temporary)
224              return false;
225          password_table.RemovePassword(card, store);
226          return store.remove_card(card);
227      }
228
229      public bool remove_card(IdCard card) {
230          if (remove_card_internal(card)) {
231             card_list_changed();
232             return true;
233          }
234          return false;
235      }
236
237      public void set_store_type(IIdentityCardStore.StoreType type) {
238          if ((store != null) && (store.get_store_type() == type))
239              return;
240          switch (type) {
241 #if GNOME_KEYRING
242              case IIdentityCardStore.StoreType.KEYRING:
243                  store = new KeyringStore();
244                  break;
245 #endif
246              case IIdentityCardStore.StoreType.FLAT_FILE:
247              default:
248                  store = new LocalFlatFileStore();
249                  break;
250          }
251      }
252
253      public IIdentityCardStore.StoreType get_store_type() {
254          return store.get_store_type();
255      }
256
257      public bool HasNonTrivialIdentities() {
258          foreach (IdCard card in this.store.get_card_list()) {
259              // The 'NoIdentity' card is non-trivial if it has services or rules.
260              // All other cards are automatically non-trivial.
261              if ((!card.IsNoIdentity()) || 
262                  (card.services.length > 0) ||
263                  (card.rules.length > 0)) {
264                  return true;
265              }
266          }
267          return false;
268      }
269
270
271     private IdentityManagerApp parent;
272
273     public IdentityManagerModel(IdentityManagerApp parent_app, IIdentityCardStore.StoreType store_type) {
274         parent = parent_app;
275         password_table = new PasswordHashTable();
276         set_store_type(store_type);
277     }
278 }