~ubuntu-branches/ubuntu/wily/epiphany-browser/wily

« back to all changes in this revision

Viewing changes to lib/ephy-web-app-utils.c

  • Committer: Package Import Robot
  • Author(s): Gustavo Noronha Silva, Jeremy Bicha, Emilio Pozuelo Monfort, Gustavo Noronha Silva
  • Date: 2012-12-10 11:40:01 UTC
  • mfrom: (1.8.8) (105.1.4 sid)
  • Revision ID: package-import@ubuntu.com-20121210114001-42jjyg3qw3eyz00a
Tags: 3.6.1-1
[ Jeremy Bicha ]
* New upstream unstable release
* debian/control.in:
  - Bump minimum intltool to 0.50
* debian/epiphany-browser-data.install:
  - The developers no longer provide help files to install since they were
    too outdated
* Dropped upstream patches:
  - 01_email-as-user-for-password-remembering.patch
  - 13_toolbar_size_fixes.patch
* debian/watch: Watch for unstable releases.

[ Emilio Pozuelo Monfort ]
* New upstream release.
  + debian/patches/14_pkglibdir.patch:
    - Removed, applied upstream.
  + debian/control.in:
    - Update (build-)dependencies.

[ Gustavo Noronha Silva ]
* debian/control.in:
- Build-Depend on gnome-common >= 3.6, needed for the code
  coverage feature

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 
2
/* vim: set sw=2 ts=2 sts=2 et: */
 
3
/*
 
4
 *  Copyright © 2011 Igalia S.L.
 
5
 *
 
6
 *  This program is free software; you can redistribute it and/or modify
 
7
 *  it under the terms of the GNU General Public License as published by
 
8
 *  the Free Software Foundation; either version 2, or (at your option)
 
9
 *  any later version.
 
10
 *
 
11
 *  This program is distributed in the hope that it will be useful,
 
12
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 *  GNU General Public License for more details.
 
15
 *
 
16
 *  You should have received a copy of the GNU General Public License
 
17
 *  along with this program; if not, write to the Free Software
 
18
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 
19
 *
 
20
 */
 
21
 
 
22
#include "config.h"
 
23
#include "ephy-web-app-utils.h"
 
24
 
 
25
#include "ephy-debug.h"
 
26
#include "ephy-file-helpers.h"
 
27
 
 
28
#include <glib/gstdio.h>
 
29
#include <libsoup/soup-gnome.h>
 
30
#ifdef HAVE_WEBKIT2
 
31
#include <webkit2/webkit2.h>
 
32
#else
 
33
#include <webkit/webkit.h>
 
34
#endif
 
35
 
 
36
#define EPHY_WEB_APP_DESKTOP_FILE_PREFIX "epiphany-"
 
37
 
 
38
/* This is necessary because of gnome-shell's guessing of a .desktop
 
39
   filename from WM_CLASS property. */
 
40
static char *
 
41
get_wm_class_from_app_title (const char *title)
 
42
{
 
43
  char *normal_title;
 
44
  char *wm_class;
 
45
  char *checksum;
 
46
 
 
47
  normal_title = g_utf8_strdown (title, -1);
 
48
  g_strdelimit (normal_title, " ", '-');
 
49
  g_strdelimit (normal_title, G_DIR_SEPARATOR_S, '-');
 
50
  checksum = g_compute_checksum_for_string (G_CHECKSUM_SHA1, title, -1);
 
51
  wm_class = g_strconcat (EPHY_WEB_APP_DESKTOP_FILE_PREFIX, normal_title, "-", checksum, NULL);
 
52
 
 
53
  g_free (checksum);
 
54
  g_free (normal_title);
 
55
 
 
56
  return wm_class;
 
57
}
 
58
 
 
59
/* Gets the proper .desktop filename from a WM_CLASS string,
 
60
   converting to the local charset when needed. */
 
61
static char *
 
62
desktop_filename_from_wm_class (char *wm_class)
 
63
{
 
64
  char *encoded;
 
65
  char *filename = NULL;
 
66
  GError *error = NULL;
 
67
 
 
68
  encoded = g_filename_from_utf8 (wm_class, -1, NULL, NULL, &error);
 
69
  if (error) {
 
70
    g_warning ("%s", error->message);
 
71
    g_error_free (error);
 
72
    return NULL;
 
73
  }
 
74
  filename = g_strconcat (encoded, ".desktop", NULL);
 
75
  g_free (encoded);
 
76
 
 
77
  return filename;
 
78
}
 
79
 
 
80
/**
 
81
 * ephy_web_application_get_profile_directory:
 
82
 * @name: the application name
 
83
 *
 
84
 * Gets the directory where the profile for @name is meant to be stored.
 
85
 *
 
86
 * Returns: (transfer full): A newly allocated string.
 
87
 **/
 
88
char *
 
89
ephy_web_application_get_profile_directory (const char *name)
 
90
{
 
91
  char *app_dir, *wm_class, *profile_dir, *encoded;
 
92
  GError *error = NULL;
 
93
 
 
94
  wm_class = get_wm_class_from_app_title (name);
 
95
  encoded = g_filename_from_utf8 (wm_class, -1, NULL, NULL, &error);
 
96
  g_free (wm_class);
 
97
 
 
98
  if (error) {
 
99
    g_warning ("%s", error->message);
 
100
    g_error_free (error);
 
101
    return NULL;
 
102
  }
 
103
 
 
104
  app_dir = g_strconcat (EPHY_WEB_APP_PREFIX, encoded, NULL);
 
105
  profile_dir = g_build_filename (ephy_dot_dir (), app_dir, NULL);
 
106
  g_free (encoded);
 
107
  g_free (app_dir);
 
108
 
 
109
  return profile_dir;
 
110
}
 
111
 
 
112
/**
 
113
 * ephy_web_application_delete:
 
114
 * @name: the name of the web application do delete
 
115
 * 
 
116
 * Deletes all the data associated with a Web Application created by
 
117
 * Epiphany.
 
118
 * 
 
119
 * Returns: %TRUE if the web app was succesfully deleted, %FALSE otherwise
 
120
 **/
 
121
gboolean
 
122
ephy_web_application_delete (const char *name)
 
123
{
 
124
  char *profile_dir = NULL;
 
125
  char *desktop_file = NULL, *desktop_path = NULL;
 
126
  char *wm_class;
 
127
  GFile *profile = NULL, *launcher = NULL;
 
128
  gboolean return_value = FALSE;
 
129
 
 
130
  g_return_val_if_fail (name, FALSE);
 
131
 
 
132
  profile_dir = ephy_web_application_get_profile_directory (name);
 
133
  if (!profile_dir)
 
134
    goto out;
 
135
 
 
136
  /* If there's no profile dir for this app, it means it does not
 
137
   * exist. */
 
138
  if (!g_file_test (profile_dir, G_FILE_TEST_IS_DIR)) {
 
139
    g_warning ("No application with name '%s' is installed.\n", name);
 
140
    goto out;
 
141
  }
 
142
 
 
143
  profile = g_file_new_for_path (profile_dir);
 
144
  if (!ephy_file_delete_dir_recursively (profile, NULL))
 
145
    goto out;
 
146
  LOG ("Deleted application profile.\n");
 
147
 
 
148
  wm_class = get_wm_class_from_app_title (name);
 
149
  desktop_file = desktop_filename_from_wm_class (wm_class);
 
150
  g_free (wm_class);
 
151
  if (!desktop_file)
 
152
    goto out;
 
153
  desktop_path = g_build_filename (g_get_user_data_dir (), "applications", desktop_file, NULL);
 
154
  launcher = g_file_new_for_path (desktop_path);
 
155
  if (!g_file_delete (launcher, NULL, NULL))
 
156
    goto out;
 
157
  LOG ("Deleted application launcher.\n");
 
158
 
 
159
  return_value = TRUE;
 
160
 
 
161
out:
 
162
 
 
163
  if (profile)
 
164
    g_object_unref (profile);
 
165
  g_free (profile_dir);
 
166
 
 
167
  if (launcher)
 
168
    g_object_unref (launcher);
 
169
  g_free (desktop_file);
 
170
  g_free (desktop_path);
 
171
 
 
172
  return return_value;
 
173
}
 
174
 
 
175
static char *
 
176
create_desktop_file (const char *address,
 
177
                     const char *profile_dir,
 
178
                     const char *title,
 
179
                     GdkPixbuf *icon)
 
180
{
 
181
  GKeyFile *file = NULL;
 
182
  char *exec_string;
 
183
  char *data = NULL;
 
184
  char *filename, *apps_path, *desktop_file_path = NULL;
 
185
  char *link_path;
 
186
  char *wm_class;
 
187
  GFile *link;
 
188
  GError *error = NULL;
 
189
 
 
190
  g_return_val_if_fail (profile_dir, NULL);
 
191
 
 
192
  wm_class = get_wm_class_from_app_title (title);
 
193
  filename = desktop_filename_from_wm_class (wm_class);
 
194
 
 
195
  if (!filename)
 
196
    goto out;
 
197
 
 
198
  file = g_key_file_new ();
 
199
  g_key_file_set_value (file, "Desktop Entry", "Name", title);
 
200
  exec_string = g_strdup_printf ("epiphany --application-mode --profile=\"%s\" %s",
 
201
                                 profile_dir,
 
202
                                 address);
 
203
  g_key_file_set_value (file, "Desktop Entry", "Exec", exec_string);
 
204
  g_free (exec_string);
 
205
  g_key_file_set_value (file, "Desktop Entry", "StartupNotify", "true");
 
206
  g_key_file_set_value (file, "Desktop Entry", "Terminal", "false");
 
207
  g_key_file_set_value (file, "Desktop Entry", "Type", "Application");
 
208
 
 
209
  if (icon) {
 
210
    GOutputStream *stream;
 
211
    char *path;
 
212
    GFile *image;
 
213
 
 
214
    path = g_build_filename (profile_dir, EPHY_WEB_APP_ICON_NAME, NULL);
 
215
    image = g_file_new_for_path (path);
 
216
 
 
217
    stream = (GOutputStream*)g_file_create (image, 0, NULL, NULL);
 
218
    gdk_pixbuf_save_to_stream (icon, stream, "png", NULL, NULL, NULL);
 
219
    g_key_file_set_value (file, "Desktop Entry", "Icon", path);
 
220
 
 
221
    g_object_unref (stream);
 
222
    g_object_unref (image);
 
223
    g_free (path);
 
224
  }
 
225
 
 
226
  g_key_file_set_value (file, "Desktop Entry", "StartupWMClass", wm_class);
 
227
  data = g_key_file_to_data (file, NULL, NULL);
 
228
 
 
229
  desktop_file_path = g_build_filename (profile_dir, filename, NULL);
 
230
 
 
231
  if (!g_file_set_contents (desktop_file_path, data, -1, NULL)) {
 
232
    g_free (desktop_file_path);
 
233
    desktop_file_path = NULL;
 
234
  }
 
235
 
 
236
  /* Create a symlink in XDG_DATA_DIR/applications for the Shell to
 
237
   * pick up this application. */
 
238
  apps_path = g_build_filename (g_get_user_data_dir (), "applications", NULL);
 
239
  if (ephy_ensure_dir_exists (apps_path, &error)) {
 
240
    link_path = g_build_filename (apps_path, filename, NULL);
 
241
    link = g_file_new_for_path (link_path);
 
242
    g_free (link_path);
 
243
    g_file_make_symbolic_link (link, desktop_file_path, NULL, NULL);
 
244
    g_object_unref (link);
 
245
  } else {
 
246
    g_warning ("Error creating application symlink: %s", error->message);
 
247
    g_error_free (error);
 
248
  }
 
249
  g_free (apps_path);
 
250
  g_free (filename);
 
251
 
 
252
out:
 
253
  g_free (wm_class);
 
254
  g_free (data);
 
255
  g_key_file_free (file);
 
256
 
 
257
  return desktop_file_path;
 
258
}
 
259
 
 
260
#ifdef HAVE_WEBKIT2
 
261
static SoupCookieJar *get_current_cookie_jar ()
 
262
{
 
263
  char *filename;
 
264
  SoupCookieJar *jar;
 
265
 
 
266
  /* FIXME: There's no API in WebKit2 to get all cookies, so we create a
 
267
   * temp read-only jar for the current cookies to read from it.
 
268
   * It would be better to have an API in WebKit to get the cookies instead.
 
269
   */
 
270
  filename = g_build_filename (ephy_dot_dir (), "cookies.sqlite", NULL);
 
271
  jar = (SoupCookieJar*)soup_cookie_jar_sqlite_new (filename, TRUE);
 
272
  g_free (filename);
 
273
 
 
274
  return jar;
 
275
}
 
276
#else
 
277
static SoupCookieJar *get_current_cookie_jar ()
 
278
{
 
279
  SoupSession *session = webkit_get_default_session ();
 
280
  SoupCookieJar *jar;
 
281
 
 
282
  jar = (SoupCookieJar*)soup_session_get_feature (session, SOUP_TYPE_COOKIE_JAR);
 
283
 
 
284
  /* WebKit might not have a cookie jar yet, if it has not needed one
 
285
   * and none has been set by Epiphany. */
 
286
  return jar ? g_object_ref (jar) : NULL;
 
287
}
 
288
#endif
 
289
 
 
290
static void
 
291
create_cookie_jar_for_domain (const char *address, const char *directory)
 
292
{
 
293
  GSList *cookies, *p;
 
294
  SoupCookieJar *current_jar, *new_jar;
 
295
  char *domain, *filename;
 
296
  SoupURI *uri;
 
297
 
 
298
  /* Create the new cookie jar */
 
299
  filename = g_build_filename (directory, "cookies.sqlite", NULL);
 
300
  new_jar = (SoupCookieJar*)soup_cookie_jar_sqlite_new (filename, FALSE);
 
301
  g_free (filename);
 
302
 
 
303
  /* The app domain for the current view */
 
304
  uri = soup_uri_new (address);
 
305
  domain = uri->host;
 
306
 
 
307
  /* The current cookies */
 
308
  current_jar = get_current_cookie_jar ();
 
309
  if (!current_jar) {
 
310
    soup_uri_free (uri);
 
311
    return;
 
312
  }
 
313
 
 
314
  cookies = soup_cookie_jar_all_cookies (current_jar);
 
315
 
 
316
  for (p = cookies; p; p = p->next) {
 
317
    SoupCookie *cookie = (SoupCookie*)p->data;
 
318
 
 
319
    if (soup_cookie_domain_matches (cookie, domain))
 
320
      soup_cookie_jar_add_cookie (new_jar, cookie);
 
321
    else
 
322
      soup_cookie_free (cookie);
 
323
  }
 
324
 
 
325
  soup_uri_free (uri);
 
326
  g_slist_free (cookies);
 
327
  g_object_unref (current_jar);
 
328
  g_object_unref (new_jar);
 
329
}
 
330
 
 
331
/**
 
332
 * ephy_web_application_create:
 
333
 * @address: the address of the new web application
 
334
 * @name: the name for the new web application
 
335
 * @icon: the icon for the new web application
 
336
 * 
 
337
 * Creates a new Web Application for @address.
 
338
 * 
 
339
 * Returns: (transfer-full): the path to the desktop file representing the new application
 
340
 **/
 
341
char *
 
342
ephy_web_application_create (const char *address, const char *name, GdkPixbuf *icon)
 
343
{
 
344
  char *profile_dir = NULL;
 
345
  char *desktop_file_path = NULL;
 
346
 
 
347
  /* If there's already a WebApp profile for the contents of this
 
348
   * view, do nothing. */
 
349
  profile_dir = ephy_web_application_get_profile_directory (name);
 
350
  if (g_file_test (profile_dir, G_FILE_TEST_IS_DIR)) {
 
351
    LOG ("Profile directory %s already exists", profile_dir);
 
352
    goto out;
 
353
  }
 
354
 
 
355
  /* Create the profile directory, populate it. */
 
356
  if (g_mkdir (profile_dir, 488) == -1) {
 
357
    LOG ("Failed to create directory %s", profile_dir);
 
358
    goto out;
 
359
  }
 
360
 
 
361
  /* Things we need in a WebApp's profile:
 
362
     - Our own cookies file, copying the relevant cookies for the
 
363
       app's domain.
 
364
  */
 
365
  create_cookie_jar_for_domain (address, profile_dir);
 
366
 
 
367
  /* Create the deskop file. */
 
368
  desktop_file_path = create_desktop_file (address, profile_dir, name, icon);
 
369
 
 
370
out:
 
371
  if (profile_dir)
 
372
    g_free (profile_dir);
 
373
 
 
374
  return desktop_file_path;
 
375
}
 
376
 
 
377
/**
 
378
 * ephy_web_application_get_application_list:
 
379
 *
 
380
 * Gets a list of the currently installed web applications.
 
381
 * Free the returned GList with
 
382
 * ephy_web_application_free_application_list.
 
383
 *
 
384
 * Returns: (transfer-full): a #GList of #EphyWebApplication objects
 
385
 **/
 
386
GList *
 
387
ephy_web_application_get_application_list ()
 
388
{
 
389
  GFileEnumerator *children = NULL;
 
390
  GFileInfo *info;
 
391
  GList *applications = NULL;
 
392
  GFile *dot_dir;
 
393
 
 
394
  dot_dir = g_file_new_for_path (ephy_dot_dir ());
 
395
  children = g_file_enumerate_children (dot_dir,
 
396
                                        "standard::name",
 
397
                                        0, NULL, NULL);
 
398
  g_object_unref (dot_dir);
 
399
 
 
400
  info = g_file_enumerator_next_file (children, NULL, NULL);
 
401
  while (info) {
 
402
    EphyWebApplication *app;
 
403
    const char *name;
 
404
    glong prefix_length = g_utf8_strlen (EPHY_WEB_APP_PREFIX, -1);
 
405
 
 
406
    name = g_file_info_get_name (info);
 
407
    if (g_str_has_prefix (name, EPHY_WEB_APP_PREFIX)) {
 
408
      char *profile_dir;
 
409
      guint64 created;
 
410
      GDate *date;
 
411
      char *desktop_file, *desktop_file_path;
 
412
      char *contents;
 
413
      GFileInfo *desktop_info;
 
414
 
 
415
      app = g_slice_new0 (EphyWebApplication);
 
416
 
 
417
      profile_dir = g_build_filename (ephy_dot_dir (), name, NULL);
 
418
      app->icon_url = g_build_filename (profile_dir, EPHY_WEB_APP_ICON_NAME, NULL);
 
419
 
 
420
      desktop_file = g_strconcat (name + prefix_length, ".desktop", NULL);
 
421
      desktop_file_path = g_build_filename (profile_dir, desktop_file, NULL);
 
422
      app->desktop_file = g_strdup (desktop_file);
 
423
 
 
424
      if (g_file_get_contents (desktop_file_path, &contents, NULL, NULL)) {
 
425
        char *exec;
 
426
        char **strings;
 
427
        GKeyFile *key;
 
428
        int i;
 
429
        GFile *file;
 
430
 
 
431
        key = g_key_file_new ();
 
432
        g_key_file_load_from_data (key, contents, -1, 0, NULL);
 
433
        app->name = g_key_file_get_string (key, "Desktop Entry", "Name", NULL);
 
434
        exec = g_key_file_get_string (key, "Desktop Entry", "Exec", NULL);
 
435
        strings = g_strsplit (exec, " ", -1);
 
436
 
 
437
        for (i = 0; strings[i]; i++);
 
438
        app->url = g_strdup (strings[i - 1]);
 
439
 
 
440
        g_strfreev (strings);
 
441
        g_free (exec);
 
442
        g_key_file_free (key);
 
443
 
 
444
        file = g_file_new_for_path (desktop_file_path);
 
445
 
 
446
        /* FIXME: this should use TIME_CREATED but it does not seem to be working. */
 
447
        desktop_info = g_file_query_info (file, G_FILE_ATTRIBUTE_TIME_MODIFIED, 0, NULL, NULL);
 
448
        created = g_file_info_get_attribute_uint64 (desktop_info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
 
449
 
 
450
        date = g_date_new ();
 
451
        g_date_set_time_t (date, (time_t)created);
 
452
        g_date_strftime (app->install_date, 127, "%x", date);
 
453
 
 
454
        g_date_free (date);
 
455
        g_object_unref (file);
 
456
        g_object_unref (desktop_info);
 
457
 
 
458
        applications = g_list_append (applications, app);
 
459
      }
 
460
 
 
461
      g_free (contents);
 
462
      g_free (desktop_file);
 
463
      g_free (profile_dir);
 
464
      g_free (desktop_file_path);
 
465
    }
 
466
 
 
467
    g_object_unref (info);
 
468
 
 
469
    info = g_file_enumerator_next_file (children, NULL, NULL);
 
470
  }
 
471
 
 
472
  g_object_unref (children);
 
473
 
 
474
  return applications;
 
475
}
 
476
 
 
477
static void
 
478
ephy_web_application_free (EphyWebApplication *app)
 
479
{
 
480
  g_free (app->name);
 
481
  g_free (app->icon_url);
 
482
  g_free (app->url);
 
483
  g_free (app->desktop_file);
 
484
  g_slice_free (EphyWebApplication, app);
 
485
}
 
486
 
 
487
/**
 
488
 * ephy_web_application_free_application_list:
 
489
 * @list: an #EphyWebApplication GList
 
490
 *
 
491
 * Frees a @list as given by ephy_web_application_get_application_list.
 
492
 **/
 
493
void
 
494
ephy_web_application_free_application_list (GList *list)
 
495
{
 
496
  GList *p;
 
497
 
 
498
  for (p = list; p; p = p->next)
 
499
    ephy_web_application_free ((EphyWebApplication*)p->data);
 
500
 
 
501
  g_list_free (list);
 
502
}
 
503
 
 
504
/**
 
505
 * ephy_web_application_exists:
 
506
 * @name: the potential name of the web application
 
507
 *
 
508
 * Returns: whether an application with @name exists.
 
509
 **/
 
510
gboolean
 
511
ephy_web_application_exists (const char *name)
 
512
{
 
513
  char *profile_dir;
 
514
  gboolean profile_exists;
 
515
 
 
516
  profile_dir = ephy_web_application_get_profile_directory (name);
 
517
  profile_exists = g_file_test (profile_dir, G_FILE_TEST_IS_DIR);
 
518
  g_free (profile_dir);
 
519
 
 
520
  return profile_exists;
 
521
}