1
/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set sw=2 ts=2 sts=2 et: */
4
* Copyright © 2011 Igalia S.L.
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)
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.
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.
23
#include "ephy-web-app-utils.h"
25
#include "ephy-debug.h"
26
#include "ephy-file-helpers.h"
28
#include <glib/gstdio.h>
29
#include <libsoup/soup-gnome.h>
31
#include <webkit2/webkit2.h>
33
#include <webkit/webkit.h>
36
#define EPHY_WEB_APP_DESKTOP_FILE_PREFIX "epiphany-"
38
/* This is necessary because of gnome-shell's guessing of a .desktop
39
filename from WM_CLASS property. */
41
get_wm_class_from_app_title (const char *title)
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);
54
g_free (normal_title);
59
/* Gets the proper .desktop filename from a WM_CLASS string,
60
converting to the local charset when needed. */
62
desktop_filename_from_wm_class (char *wm_class)
65
char *filename = NULL;
68
encoded = g_filename_from_utf8 (wm_class, -1, NULL, NULL, &error);
70
g_warning ("%s", error->message);
74
filename = g_strconcat (encoded, ".desktop", NULL);
81
* ephy_web_application_get_profile_directory:
82
* @name: the application name
84
* Gets the directory where the profile for @name is meant to be stored.
86
* Returns: (transfer full): A newly allocated string.
89
ephy_web_application_get_profile_directory (const char *name)
91
char *app_dir, *wm_class, *profile_dir, *encoded;
94
wm_class = get_wm_class_from_app_title (name);
95
encoded = g_filename_from_utf8 (wm_class, -1, NULL, NULL, &error);
99
g_warning ("%s", error->message);
100
g_error_free (error);
104
app_dir = g_strconcat (EPHY_WEB_APP_PREFIX, encoded, NULL);
105
profile_dir = g_build_filename (ephy_dot_dir (), app_dir, NULL);
113
* ephy_web_application_delete:
114
* @name: the name of the web application do delete
116
* Deletes all the data associated with a Web Application created by
119
* Returns: %TRUE if the web app was succesfully deleted, %FALSE otherwise
122
ephy_web_application_delete (const char *name)
124
char *profile_dir = NULL;
125
char *desktop_file = NULL, *desktop_path = NULL;
127
GFile *profile = NULL, *launcher = NULL;
128
gboolean return_value = FALSE;
130
g_return_val_if_fail (name, FALSE);
132
profile_dir = ephy_web_application_get_profile_directory (name);
136
/* If there's no profile dir for this app, it means it does not
138
if (!g_file_test (profile_dir, G_FILE_TEST_IS_DIR)) {
139
g_warning ("No application with name '%s' is installed.\n", name);
143
profile = g_file_new_for_path (profile_dir);
144
if (!ephy_file_delete_dir_recursively (profile, NULL))
146
LOG ("Deleted application profile.\n");
148
wm_class = get_wm_class_from_app_title (name);
149
desktop_file = desktop_filename_from_wm_class (wm_class);
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))
157
LOG ("Deleted application launcher.\n");
164
g_object_unref (profile);
165
g_free (profile_dir);
168
g_object_unref (launcher);
169
g_free (desktop_file);
170
g_free (desktop_path);
176
create_desktop_file (const char *address,
177
const char *profile_dir,
181
GKeyFile *file = NULL;
184
char *filename, *apps_path, *desktop_file_path = NULL;
188
GError *error = NULL;
190
g_return_val_if_fail (profile_dir, NULL);
192
wm_class = get_wm_class_from_app_title (title);
193
filename = desktop_filename_from_wm_class (wm_class);
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",
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");
210
GOutputStream *stream;
214
path = g_build_filename (profile_dir, EPHY_WEB_APP_ICON_NAME, NULL);
215
image = g_file_new_for_path (path);
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);
221
g_object_unref (stream);
222
g_object_unref (image);
226
g_key_file_set_value (file, "Desktop Entry", "StartupWMClass", wm_class);
227
data = g_key_file_to_data (file, NULL, NULL);
229
desktop_file_path = g_build_filename (profile_dir, filename, NULL);
231
if (!g_file_set_contents (desktop_file_path, data, -1, NULL)) {
232
g_free (desktop_file_path);
233
desktop_file_path = NULL;
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);
243
g_file_make_symbolic_link (link, desktop_file_path, NULL, NULL);
244
g_object_unref (link);
246
g_warning ("Error creating application symlink: %s", error->message);
247
g_error_free (error);
255
g_key_file_free (file);
257
return desktop_file_path;
261
static SoupCookieJar *get_current_cookie_jar ()
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.
270
filename = g_build_filename (ephy_dot_dir (), "cookies.sqlite", NULL);
271
jar = (SoupCookieJar*)soup_cookie_jar_sqlite_new (filename, TRUE);
277
static SoupCookieJar *get_current_cookie_jar ()
279
SoupSession *session = webkit_get_default_session ();
282
jar = (SoupCookieJar*)soup_session_get_feature (session, SOUP_TYPE_COOKIE_JAR);
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;
291
create_cookie_jar_for_domain (const char *address, const char *directory)
294
SoupCookieJar *current_jar, *new_jar;
295
char *domain, *filename;
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);
303
/* The app domain for the current view */
304
uri = soup_uri_new (address);
307
/* The current cookies */
308
current_jar = get_current_cookie_jar ();
314
cookies = soup_cookie_jar_all_cookies (current_jar);
316
for (p = cookies; p; p = p->next) {
317
SoupCookie *cookie = (SoupCookie*)p->data;
319
if (soup_cookie_domain_matches (cookie, domain))
320
soup_cookie_jar_add_cookie (new_jar, cookie);
322
soup_cookie_free (cookie);
326
g_slist_free (cookies);
327
g_object_unref (current_jar);
328
g_object_unref (new_jar);
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
337
* Creates a new Web Application for @address.
339
* Returns: (transfer-full): the path to the desktop file representing the new application
342
ephy_web_application_create (const char *address, const char *name, GdkPixbuf *icon)
344
char *profile_dir = NULL;
345
char *desktop_file_path = NULL;
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);
355
/* Create the profile directory, populate it. */
356
if (g_mkdir (profile_dir, 488) == -1) {
357
LOG ("Failed to create directory %s", profile_dir);
361
/* Things we need in a WebApp's profile:
362
- Our own cookies file, copying the relevant cookies for the
365
create_cookie_jar_for_domain (address, profile_dir);
367
/* Create the deskop file. */
368
desktop_file_path = create_desktop_file (address, profile_dir, name, icon);
372
g_free (profile_dir);
374
return desktop_file_path;
378
* ephy_web_application_get_application_list:
380
* Gets a list of the currently installed web applications.
381
* Free the returned GList with
382
* ephy_web_application_free_application_list.
384
* Returns: (transfer-full): a #GList of #EphyWebApplication objects
387
ephy_web_application_get_application_list ()
389
GFileEnumerator *children = NULL;
391
GList *applications = NULL;
394
dot_dir = g_file_new_for_path (ephy_dot_dir ());
395
children = g_file_enumerate_children (dot_dir,
398
g_object_unref (dot_dir);
400
info = g_file_enumerator_next_file (children, NULL, NULL);
402
EphyWebApplication *app;
404
glong prefix_length = g_utf8_strlen (EPHY_WEB_APP_PREFIX, -1);
406
name = g_file_info_get_name (info);
407
if (g_str_has_prefix (name, EPHY_WEB_APP_PREFIX)) {
411
char *desktop_file, *desktop_file_path;
413
GFileInfo *desktop_info;
415
app = g_slice_new0 (EphyWebApplication);
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);
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);
424
if (g_file_get_contents (desktop_file_path, &contents, NULL, NULL)) {
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);
437
for (i = 0; strings[i]; i++);
438
app->url = g_strdup (strings[i - 1]);
440
g_strfreev (strings);
442
g_key_file_free (key);
444
file = g_file_new_for_path (desktop_file_path);
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);
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);
455
g_object_unref (file);
456
g_object_unref (desktop_info);
458
applications = g_list_append (applications, app);
462
g_free (desktop_file);
463
g_free (profile_dir);
464
g_free (desktop_file_path);
467
g_object_unref (info);
469
info = g_file_enumerator_next_file (children, NULL, NULL);
472
g_object_unref (children);
478
ephy_web_application_free (EphyWebApplication *app)
481
g_free (app->icon_url);
483
g_free (app->desktop_file);
484
g_slice_free (EphyWebApplication, app);
488
* ephy_web_application_free_application_list:
489
* @list: an #EphyWebApplication GList
491
* Frees a @list as given by ephy_web_application_get_application_list.
494
ephy_web_application_free_application_list (GList *list)
498
for (p = list; p; p = p->next)
499
ephy_web_application_free ((EphyWebApplication*)p->data);
505
* ephy_web_application_exists:
506
* @name: the potential name of the web application
508
* Returns: whether an application with @name exists.
511
ephy_web_application_exists (const char *name)
514
gboolean profile_exists;
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);
520
return profile_exists;