1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /*
3  *  Copyright © 2017 Gabriel Ivascu <gabrielivascu@gnome.org>
4  *
5  *  This file is part of Epiphany.
6  *
7  *  Epiphany is free software: you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation, either version 3 of the License, or
10  *  (at your option) any later version.
11  *
12  *  Epiphany is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with Epiphany.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include "config.h"
22 #include "ephy-password-record.h"
23 
24 #include "ephy-synchronizable.h"
25 
26 struct _EphyPasswordRecord {
27   GObject parent_instance;
28 
29   char *id;
30   char *origin;
31   char *target_origin;
32   char *username;
33   char *password;
34   char *username_field;
35   char *password_field;
36   guint64 time_created;
37   guint64 time_password_changed;
38   gint64 server_time_modified;
39 };
40 
41 static void json_serializable_iface_init (JsonSerializableIface *iface);
42 static void ephy_synchronizable_iface_init (EphySynchronizableInterface *iface);
43 
44 G_DEFINE_TYPE_WITH_CODE (EphyPasswordRecord, ephy_password_record, G_TYPE_OBJECT,
45                          G_IMPLEMENT_INTERFACE (JSON_TYPE_SERIALIZABLE,
46                                                 json_serializable_iface_init)
47                          G_IMPLEMENT_INTERFACE (EPHY_TYPE_SYNCHRONIZABLE,
48                                                 ephy_synchronizable_iface_init))
49 
50 enum {
51   PROP_0,
52   PROP_ID,                    /* Firefox Sync */
53   PROP_ORIGIN,                /* Epiphany && Firefox Sync */
54   PROP_TARGET_ORIGIN,         /* Epiphany && Firefox Sync */
55   PROP_USERNAME,              /* Epiphany && Firefox Sync */
56   PROP_PASSWORD,              /* Epiphany && Firefox Sync */
57   PROP_USERNAME_FIELD,        /* Epiphany && Firefox Sync */
58   PROP_PASSWORD_FIELD,        /* Epiphany && Firefox Sync */
59   PROP_TIME_CREATED,          /* Firefox Sync */
60   PROP_TIME_PASSWORD_CHANGED, /* Firefox Sync */
61   LAST_PROP
62 };
63 
64 static GParamSpec *obj_properties[LAST_PROP];
65 
66 static void
ephy_password_record_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)67 ephy_password_record_set_property (GObject      *object,
68                                    guint         prop_id,
69                                    const GValue *value,
70                                    GParamSpec   *pspec)
71 {
72   EphyPasswordRecord *self = EPHY_PASSWORD_RECORD (object);
73 
74   switch (prop_id) {
75     case PROP_ID:
76       g_free (self->id);
77       self->id = g_value_dup_string (value);
78       break;
79     case PROP_ORIGIN:
80       g_free (self->origin);
81       self->origin = g_value_dup_string (value);
82       break;
83     case PROP_TARGET_ORIGIN:
84       g_free (self->target_origin);
85       self->target_origin = g_value_dup_string (value);
86       break;
87     case PROP_USERNAME:
88       g_free (self->username);
89       self->username = g_value_dup_string (value);
90       break;
91     case PROP_PASSWORD:
92       g_free (self->password);
93       self->password = g_value_dup_string (value);
94       break;
95     case PROP_USERNAME_FIELD:
96       g_free (self->username_field);
97       self->username_field = g_value_dup_string (value);
98       break;
99     case PROP_PASSWORD_FIELD:
100       g_free (self->password_field);
101       self->password_field = g_value_dup_string (value);
102       break;
103     case PROP_TIME_CREATED:
104       self->time_created = g_value_get_uint64 (value);
105       break;
106     case PROP_TIME_PASSWORD_CHANGED:
107       self->time_password_changed = g_value_get_uint64 (value);
108       break;
109     default:
110       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
111   }
112 }
113 
114 static void
ephy_password_record_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)115 ephy_password_record_get_property (GObject    *object,
116                                    guint       prop_id,
117                                    GValue     *value,
118                                    GParamSpec *pspec)
119 {
120   EphyPasswordRecord *self = EPHY_PASSWORD_RECORD (object);
121 
122   switch (prop_id) {
123     case PROP_ID:
124       g_value_set_string (value, self->id);
125       break;
126     case PROP_ORIGIN:
127       g_value_set_string (value, self->origin);
128       break;
129     case PROP_TARGET_ORIGIN:
130       g_value_set_string (value, self->target_origin);
131       break;
132     case PROP_USERNAME:
133       g_value_set_string (value, self->username);
134       break;
135     case PROP_PASSWORD:
136       g_value_set_string (value, self->password);
137       break;
138     case PROP_USERNAME_FIELD:
139       g_value_set_string (value, self->username_field);
140       break;
141     case PROP_PASSWORD_FIELD:
142       g_value_set_string (value, self->password_field);
143       break;
144     case PROP_TIME_CREATED:
145       g_value_set_uint64 (value, self->time_created);
146       break;
147     case PROP_TIME_PASSWORD_CHANGED:
148       g_value_set_uint64 (value, self->time_password_changed);
149       break;
150     default:
151       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
152   }
153 }
154 
155 static void
ephy_password_record_finalize(GObject * object)156 ephy_password_record_finalize (GObject *object)
157 {
158   EphyPasswordRecord *self = EPHY_PASSWORD_RECORD (object);
159 
160   g_free (self->id);
161   g_free (self->origin);
162   g_free (self->target_origin);
163   g_free (self->username);
164   g_free (self->password);
165   g_free (self->username_field);
166   g_free (self->password_field);
167 
168   G_OBJECT_CLASS (ephy_password_record_parent_class)->finalize (object);
169 }
170 
171 static void
ephy_password_record_class_init(EphyPasswordRecordClass * klass)172 ephy_password_record_class_init (EphyPasswordRecordClass *klass)
173 {
174   GObjectClass *object_class = G_OBJECT_CLASS (klass);
175 
176   object_class->set_property = ephy_password_record_set_property;
177   object_class->get_property = ephy_password_record_get_property;
178   object_class->finalize = ephy_password_record_finalize;
179 
180   /* The property names must match Firefox password object structure, see
181    * https://mozilla-services.readthedocs.io/en/latest/sync/objectformats.html#passwords
182    */
183   obj_properties[PROP_ID] =
184     g_param_spec_string ("id",
185                          "Id",
186                          "Id of the password record",
187                          "Default id",
188                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
189   /* Origin matches hostname field from Firefox.
190    * Despite its name, it's actually a security origin (scheme + host + port), so call it appropriately, see
191    * https://dxr.mozilla.org/mozilla-central/rev/892c8916ba32b7733e06bfbfdd4083ffae3ca028/toolkit/components/passwordmgr/LoginManagerContent.jsm#922
192    * https://dxr.mozilla.org/mozilla-central/rev/892c8916ba32b7733e06bfbfdd4083ffae3ca028/toolkit/components/passwordmgr/LoginManagerContent.jsm#1380
193    */
194   obj_properties[PROP_ORIGIN] =
195     g_param_spec_string ("hostname",
196                          "Security origin",
197                          "Security origin of the URI that password is applicable at",
198                          "Default security origin",
199                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
200   /* Target origin matches formSubmitURL field from Firefox.
201    * Despite its name, it's actually a security origin, so call it appropriately, see
202    * https://dxr.mozilla.org/mozilla-central/rev/892c8916ba32b7733e06bfbfdd4083ffae3ca028/toolkit/components/passwordmgr/LoginManagerContent.jsm#928
203    */
204   obj_properties[PROP_TARGET_ORIGIN] =
205     g_param_spec_string ("formSubmitURL",
206                          "Target origin",
207                          "The target origin of the URI that password is applicable at",
208                          "Default target origin",
209                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
210   obj_properties[PROP_USERNAME] =
211     g_param_spec_string ("username",
212                          "Username",
213                          "Username to log in as",
214                          "Default username",
215                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
216   obj_properties[PROP_PASSWORD] =
217     g_param_spec_string ("password",
218                          "Password",
219                          "Password for the username",
220                          "Default password",
221                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
222   obj_properties[PROP_USERNAME_FIELD] =
223     g_param_spec_string ("usernameField",
224                          "Username field",
225                          "HTML field name of the username",
226                          "Default username field",
227                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
228   obj_properties[PROP_PASSWORD_FIELD] =
229     g_param_spec_string ("passwordField",
230                          "Password field",
231                          "HTML field name of the password",
232                          "Default password field",
233                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
234   obj_properties[PROP_TIME_CREATED] =
235     g_param_spec_uint64 ("timeCreated",
236                          "Time created",
237                          "Unix timestamp in milliseconds at which the password was created",
238                          0,
239                          G_MAXUINT64,
240                          0,
241                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
242   obj_properties[PROP_TIME_PASSWORD_CHANGED] =
243     g_param_spec_uint64 ("timePasswordChanged",
244                          "Time password changed",
245                          "Unix timestamp in milliseconds at which the password was changed",
246                          0,
247                          G_MAXUINT64,
248                          0,
249                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
250 
251   g_object_class_install_properties (object_class, LAST_PROP, obj_properties);
252 }
253 
254 static void
ephy_password_record_init(EphyPasswordRecord * self)255 ephy_password_record_init (EphyPasswordRecord *self)
256 {
257 }
258 
259 EphyPasswordRecord *
ephy_password_record_new(const char * id,const char * origin,const char * target_origin,const char * username,const char * password,const char * username_field,const char * password_field,guint64 time_created,guint64 time_password_changed)260 ephy_password_record_new (const char *id,
261                           const char *origin,
262                           const char *target_origin,
263                           const char *username,
264                           const char *password,
265                           const char *username_field,
266                           const char *password_field,
267                           guint64     time_created,
268                           guint64     time_password_changed)
269 {
270   return EPHY_PASSWORD_RECORD (g_object_new (EPHY_TYPE_PASSWORD_RECORD,
271                                              "id", id,
272                                              "hostname", origin,
273                                              "formSubmitURL", target_origin,
274                                              "username", username,
275                                              "password", password,
276                                              "usernameField", username_field,
277                                              "passwordField", password_field,
278                                              "timeCreated", time_created,
279                                              "timePasswordChanged", time_password_changed,
280                                              NULL));
281 }
282 
283 const char *
ephy_password_record_get_id(EphyPasswordRecord * self)284 ephy_password_record_get_id (EphyPasswordRecord *self)
285 {
286   g_assert (EPHY_IS_PASSWORD_RECORD (self));
287 
288   return self->id;
289 }
290 
291 const char *
ephy_password_record_get_origin(EphyPasswordRecord * self)292 ephy_password_record_get_origin (EphyPasswordRecord *self)
293 {
294   g_assert (EPHY_IS_PASSWORD_RECORD (self));
295 
296   return self->origin;
297 }
298 
299 const char *
ephy_password_record_get_target_origin(EphyPasswordRecord * self)300 ephy_password_record_get_target_origin (EphyPasswordRecord *self)
301 {
302   g_assert (EPHY_IS_PASSWORD_RECORD (self));
303 
304   return self->target_origin;
305 }
306 
307 const char *
ephy_password_record_get_username(EphyPasswordRecord * self)308 ephy_password_record_get_username (EphyPasswordRecord *self)
309 {
310   g_assert (EPHY_IS_PASSWORD_RECORD (self));
311 
312   return self->username;
313 }
314 
315 const char *
ephy_password_record_get_password(EphyPasswordRecord * self)316 ephy_password_record_get_password (EphyPasswordRecord *self)
317 {
318   g_assert (EPHY_IS_PASSWORD_RECORD (self));
319 
320   return self->password;
321 }
322 
323 void
ephy_password_record_set_password(EphyPasswordRecord * self,const char * password)324 ephy_password_record_set_password (EphyPasswordRecord *self,
325                                    const char         *password)
326 {
327   g_assert (EPHY_IS_PASSWORD_RECORD (self));
328 
329   g_free (self->password);
330   self->password = g_strdup (password);
331 }
332 
333 const char *
ephy_password_record_get_username_field(EphyPasswordRecord * self)334 ephy_password_record_get_username_field (EphyPasswordRecord *self)
335 {
336   g_assert (EPHY_IS_PASSWORD_RECORD (self));
337 
338   return self->username_field;
339 }
340 
341 const char *
ephy_password_record_get_password_field(EphyPasswordRecord * self)342 ephy_password_record_get_password_field (EphyPasswordRecord *self)
343 {
344   g_assert (EPHY_IS_PASSWORD_RECORD (self));
345 
346   return self->password_field;
347 }
348 
349 guint64
ephy_password_record_get_time_password_changed(EphyPasswordRecord * self)350 ephy_password_record_get_time_password_changed (EphyPasswordRecord *self)
351 {
352   g_assert (EPHY_IS_PASSWORD_RECORD (self));
353 
354   return self->time_password_changed;
355 }
356 
357 static JsonNode *
serializable_serialize_property(JsonSerializable * serializable,const char * name,const GValue * value,GParamSpec * pspec)358 serializable_serialize_property (JsonSerializable *serializable,
359                                  const char       *name,
360                                  const GValue     *value,
361                                  GParamSpec       *pspec)
362 {
363   /* Convert NULL to "", as Firefox expects empty strings for missing fields. */
364   if (G_VALUE_HOLDS_STRING (value) && g_value_get_string (value) == NULL) {
365     JsonNode *node = json_node_new (JSON_NODE_VALUE);
366     json_node_set_string (node, "");
367     return node;
368   }
369 
370   return json_serializable_default_serialize_property (serializable, name, value, pspec);
371 }
372 
373 static gboolean
serializable_deserialize_property(JsonSerializable * serializable,const char * name,GValue * value,GParamSpec * pspec,JsonNode * node)374 serializable_deserialize_property (JsonSerializable *serializable,
375                                    const char       *name,
376                                    GValue           *value,
377                                    GParamSpec       *pspec,
378                                    JsonNode         *node)
379 {
380   /* Convert "" back to NULL. */
381   if (G_VALUE_HOLDS_STRING (value) && !g_strcmp0 (json_node_get_string (node), "")) {
382     g_value_set_string (value, NULL);
383     return TRUE;
384   }
385 
386   return json_serializable_default_deserialize_property (serializable, name, value, pspec, node);
387 }
388 
389 static void
json_serializable_iface_init(JsonSerializableIface * iface)390 json_serializable_iface_init (JsonSerializableIface *iface)
391 {
392   iface->serialize_property = serializable_serialize_property;
393   iface->deserialize_property = serializable_deserialize_property;
394 }
395 
396 static const char *
synchronizable_get_id(EphySynchronizable * synchronizable)397 synchronizable_get_id (EphySynchronizable *synchronizable)
398 {
399   return ephy_password_record_get_id (EPHY_PASSWORD_RECORD (synchronizable));
400 }
401 
402 static gint64
synchronizable_get_server_time_modified(EphySynchronizable * synchronizable)403 synchronizable_get_server_time_modified (EphySynchronizable *synchronizable)
404 {
405   return EPHY_PASSWORD_RECORD (synchronizable)->server_time_modified;
406 }
407 
408 static void
synchronizable_set_server_time_modified(EphySynchronizable * synchronizable,gint64 server_time_modified)409 synchronizable_set_server_time_modified (EphySynchronizable *synchronizable,
410                                          gint64              server_time_modified)
411 {
412   EPHY_PASSWORD_RECORD (synchronizable)->server_time_modified = server_time_modified;
413 }
414 
415 static void
ephy_synchronizable_iface_init(EphySynchronizableInterface * iface)416 ephy_synchronizable_iface_init (EphySynchronizableInterface *iface)
417 {
418   iface->get_id = synchronizable_get_id;
419   iface->get_server_time_modified = synchronizable_get_server_time_modified;
420   iface->set_server_time_modified = synchronizable_set_server_time_modified;
421   iface->to_bso = ephy_synchronizable_default_to_bso;
422 }
423