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