Don't create duplicate identities. LP 1180914.
[moonshot-ui.git] / src / moonshot-identities-manager.vala
1 using Gee;
2
3 public class Password {
4 #if GNOME_KEYRING
5     private unowned string _password;
6     public string password {
7         get {
8             return _password;
9         }
10         set {
11             if (_password != null) {
12                 GnomeKeyring.memory_free((void *)_password);
13                 _password = null;
14             }
15             if (value != null)
16                 _password = GnomeKeyring.memory_strdup(value); 
17         }
18     }
19 #else
20     public string password { get; set; default = null; }
21 #endif
22
23     public Password(string in_password) {
24         password = in_password;
25     }
26
27     ~Password() {
28         password = null;
29     }
30 }
31
32 public class PasswordHashTable : Object {
33     private HashTable<string, Password> password_table;
34
35     private static string ComputeHashKey(IdCard card, IIdentityCardStore store) {
36         return "%s_store_%d".printf( card.display_name, store.get_store_type() );
37     }
38
39     public void CachePassword(IdCard card, IIdentityCardStore store) {
40         password_table.replace(ComputeHashKey(card, store), new Password(card.password));
41     }
42
43     public void RemovePassword(IdCard card, IIdentityCardStore store) {
44         password_table.remove(ComputeHashKey(card, store));
45     }
46     public void RetrievePassword(IdCard card, IIdentityCardStore store) {
47         weak Password password = password_table.lookup(ComputeHashKey(card, store));
48         if (password != null) {
49             card.password = password.password;
50         }
51     }
52     public PasswordHashTable() {
53         password_table = new HashTable<string, Password>(GLib.str_hash, GLib.str_equal);
54     }
55 }
56
57 public class IdentityManagerModel : Object {
58     private const string FILE_NAME = "identities.txt";
59     private PasswordHashTable password_table;
60     private IIdentityCardStore store;
61     public LinkedList<IdCard>  get_card_list() {
62          var identities = store.get_card_list();
63          identities.sort( (a, b) => {
64              IdCard id_a = (IdCard )a;
65              IdCard id_b = (IdCard )b;
66              if (id_a.IsNoIdentity() && !id_b.IsNoIdentity()) {
67                 return -1;
68              } else if (id_b.IsNoIdentity() && !id_a.IsNoIdentity()) {
69                 return 1;
70              }
71              return strcmp(id_a.display_name, id_b.display_name);
72          });
73          if (identities.is_empty || !identities[0].IsNoIdentity())
74              identities.insert(0, IdCard.NewNoIdentity());
75          foreach (IdCard id_card in identities) {
76              if (!id_card.store_password) {
77                  password_table.RetrievePassword(id_card, store);
78              }
79          }
80          return identities;
81     }
82     public signal void card_list_changed();
83
84     /* This method finds a valid display name */
85     public bool display_name_is_valid (string name,
86                                        out string? candidate)
87     {
88         if (&candidate != null)
89           candidate = null;
90         foreach (IdCard id_card in this.get_card_list())
91         {
92           if (id_card.display_name == name)
93           {
94             if (&candidate != null)
95             {
96               for (int i=0; i<1000; i++)
97               {
98                 string tmp = "%s %d".printf (name, i);
99                 if (display_name_is_valid (tmp, null))
100                 {
101                   candidate = tmp;
102                   break;
103                 }
104               }
105             }
106             return false;
107           }
108         }
109         return true;
110     }
111
112     private bool remove_duplicates(IdCard card)
113     {
114         bool duplicate_found = false;
115         bool found = false;
116         do {
117            var cards = get_card_list();
118            found = false;
119            foreach (IdCard id_card in cards) {
120                if ((card != id_card) && (id_card.nai == card.nai)) {
121                   stdout.printf("removing duplicate id for '%s'\n", card.nai);
122                   remove_card_internal(id_card);
123                   found = duplicate_found = true;
124                   break;
125                }
126            }
127         } while (found);
128         return duplicate_found;
129     }
130
131     public IdCard? find_id_card(string nai, bool force_flat_file_store) {
132         IdCard? retval = null;
133         IIdentityCardStore.StoreType saved_store_type = get_store_type();
134         if (force_flat_file_store)
135             set_store_type(IIdentityCardStore.StoreType.FLAT_FILE);
136
137         foreach (IdCard id in get_card_list()) {
138             if (id.nai == nai) {
139                 retval = id;
140                 break;
141             }
142         }
143         set_store_type(saved_store_type);
144         if (force_flat_file_store && 
145             (saved_store_type != IIdentityCardStore.StoreType.FLAT_FILE))
146             card_list_changed();
147         return retval;
148     }
149
150     public void add_card(IdCard card, bool force_flat_file_store) {
151         if (card.temporary)
152             return;
153
154         string candidate;
155         IIdentityCardStore.StoreType saved_store_type = get_store_type();
156
157         if (force_flat_file_store)
158             set_store_type(IIdentityCardStore.StoreType.FLAT_FILE);
159
160         remove_duplicates(card);
161
162         if (!display_name_is_valid (card.display_name, out candidate))
163         {
164           card.display_name = candidate;
165         }
166
167         if (!card.store_password)
168             password_table.CachePassword(card, store);
169         store.add_card(card);
170         set_store_type(saved_store_type);
171         card_list_changed();
172      }
173
174      public IdCard update_card(IdCard card) {
175         IdCard retval;
176         if (card.temporary) {
177             retval = card;
178             return retval;
179         }
180             
181         if (!card.store_password)
182             password_table.CachePassword(card, store);
183         else
184             password_table.RemovePassword(card, store);
185         retval = store.update_card(card);
186         card_list_changed();
187         return retval;
188      }
189
190      private bool remove_card_internal(IdCard card) {
191          if (card.temporary)
192              return false;
193          password_table.RemovePassword(card, store);
194          return store.remove_card(card);
195      }
196
197      public bool remove_card(IdCard card) {
198          if (remove_card_internal(card)) {
199             card_list_changed();
200             return true;
201          }
202          return false;
203      }
204
205      public void set_store_type(IIdentityCardStore.StoreType type) {
206          if ((store != null) && (store.get_store_type() == type))
207              return;
208          switch (type) {
209 #if GNOME_KEYRING
210              case IIdentityCardStore.StoreType.KEYRING:
211                  store = new KeyringStore();
212                  break;
213 #endif
214              case IIdentityCardStore.StoreType.FLAT_FILE:
215              default:
216                  store = new LocalFlatFileStore();
217                  break;
218          }
219      }
220
221      public IIdentityCardStore.StoreType get_store_type() {
222          return store.get_store_type();
223      }
224
225      public bool HasNonTrivialIdentities() {
226          foreach (IdCard card in this.store.get_card_list()) {
227              // The 'NoIdentity' card is non-trivial if it has services or rules.
228              // All other cards are automatically non-trivial.
229              if ((!card.IsNoIdentity()) || 
230                  (card.services.length > 0) ||
231                  (card.rules.length > 0)) {
232                  return true;
233              }
234          }
235          return false;
236      }
237
238
239     private IdentityManagerApp parent;
240
241     public IdentityManagerModel(IdentityManagerApp parent_app, IIdentityCardStore.StoreType store_type) {
242         parent = parent_app;
243         password_table = new PasswordHashTable();
244         set_store_type(store_type);
245     }
246 }