midori/midori/main.c
Christian Dywan ff9085b400 Disable WebKit's authentication dialog if needed
We need to handle WebKit possibly having an authentication
dialog more expressly. Obviously we only ever want to
see one dialog and we choose our own simply because it
is localized.
2009-02-28 23:46:41 +01:00

2072 lines
66 KiB
C

/*
Copyright (C) 2007-2008 Christian Dywan <christian@twotoasts.de>
Copyright (C) 2008 Dale Whittaker <dayul@users.sf.net>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
See the file COPYING for the full license text.
*/
#if HAVE_CONFIG_H
#include <config.h>
#endif
#include "midori-addons.h"
#include "midori-app.h"
#include "midori-bookmarks.h"
#include "midori-browser.h"
#include "midori-console.h"
#include "midori-extension.h"
#include "midori-extensions.h"
#include "midori-history.h"
#include "midori-panel.h"
#include "midori-preferences.h"
#include "midori-plugins.h"
#include "midori-view.h"
#include "midori-websettings.h"
#include "sokoke.h"
#if HAVE_UNISTD_H
#include <unistd.h>
#define is_writable(_cfg_filename) \
!g_access (_cfg_filename, W_OK) || \
!g_file_test (_cfg_filename, G_FILE_TEST_EXISTS)
#else
#define is_writable(_cfg_filename) 1
#endif
#include <string.h>
#include <glib/gi18n.h>
#include <glib/gstdio.h>
#include <gtk/gtk.h>
#include <JavaScriptCore/JavaScript.h>
#if HAVE_LIBXML
#include <libxml/parser.h>
#include <libxml/tree.h>
#endif
#if HAVE_SQLITE
#include <sqlite3.h>
#endif
#if HAVE_LIBSOUP
#include <libsoup/soup.h>
#endif
#if ENABLE_NLS
#include <libintl.h>
#include <locale.h>
#endif
#define MIDORI_HISTORY_ERROR g_quark_from_string("MIDORI_HISTORY_ERROR")
typedef enum
{
MIDORI_HISTORY_ERROR_DB_OPEN, /* Error opening the database file */
MIDORI_HISTORY_ERROR_EXEC_SQL, /* Error executing SQL statement */
} MidoriHistoryError;
static gchar*
build_config_filename (const gchar* filename)
{
static gchar* path = NULL;
if (!path)
path = g_build_filename (g_get_user_config_dir (), PACKAGE_NAME, NULL);
g_mkdir_with_parents (path, 0700);
return g_build_filename (path, filename, NULL);
}
static MidoriWebSettings*
settings_new_from_file (const gchar* filename)
{
MidoriWebSettings* settings = midori_web_settings_new ();
GKeyFile* key_file = g_key_file_new ();
GError* error = NULL;
GObjectClass* class;
guint i, n_properties;
GParamSpec** pspecs;
GParamSpec* pspec;
GType type;
const gchar* property;
gchar* str;
gint integer;
gfloat number;
gboolean boolean;
if (!g_key_file_load_from_file (key_file, filename,
G_KEY_FILE_KEEP_COMMENTS, &error))
{
if (error->code != G_FILE_ERROR_NOENT)
printf (_("The configuration couldn't be loaded: %s\n"),
error->message);
g_error_free (error);
}
class = G_OBJECT_GET_CLASS (settings);
pspecs = g_object_class_list_properties (class, &n_properties);
for (i = 0; i < n_properties; i++)
{
pspec = pspecs[i];
if (!(pspec->flags & G_PARAM_WRITABLE))
continue;
type = G_PARAM_SPEC_TYPE (pspec);
property = g_param_spec_get_name (pspec);
if (type == G_TYPE_PARAM_STRING)
{
str = sokoke_key_file_get_string_default (key_file,
"settings", property,
G_PARAM_SPEC_STRING (pspec)->default_value, NULL);
g_object_set (settings, property, str, NULL);
g_free (str);
}
else if (type == G_TYPE_PARAM_INT)
{
integer = sokoke_key_file_get_integer_default (key_file,
"settings", property,
G_PARAM_SPEC_INT (pspec)->default_value, NULL);
g_object_set (settings, property, integer, NULL);
}
else if (type == G_TYPE_PARAM_FLOAT)
{
number = sokoke_key_file_get_double_default (key_file,
"settings", property,
G_PARAM_SPEC_FLOAT (pspec)->default_value, NULL);
g_object_set (settings, property, number, NULL);
}
else if (type == G_TYPE_PARAM_BOOLEAN)
{
boolean = sokoke_key_file_get_boolean_default (key_file,
"settings", property,
G_PARAM_SPEC_BOOLEAN (pspec)->default_value, NULL);
g_object_set (settings, property, boolean, NULL);
}
else if (type == G_TYPE_PARAM_ENUM)
{
GEnumClass* enum_class = G_ENUM_CLASS (
g_type_class_ref (pspec->value_type));
GEnumValue* enum_value = g_enum_get_value (enum_class,
G_PARAM_SPEC_ENUM (pspec)->default_value);
str = sokoke_key_file_get_string_default (key_file,
"settings", property,
enum_value->value_name, NULL);
enum_value = g_enum_get_value_by_name (enum_class, str);
if (enum_value)
g_object_set (settings, property, enum_value->value, NULL);
else
g_warning (_("Value '%s' is invalid for %s"),
str, property);
g_free (str);
g_type_class_unref (enum_class);
}
else
g_warning (_("Invalid configuration value '%s'"), property);
}
return settings;
}
static gboolean
settings_save_to_file (MidoriWebSettings* settings,
const gchar* filename,
GError** error)
{
GKeyFile* key_file;
GObjectClass* class;
guint i, n_properties;
GParamSpec** pspecs;
GParamSpec* pspec;
GType type;
const gchar* property;
gboolean saved;
key_file = g_key_file_new ();
class = G_OBJECT_GET_CLASS (settings);
pspecs = g_object_class_list_properties (class, &n_properties);
for (i = 0; i < n_properties; i++)
{
pspec = pspecs[i];
type = G_PARAM_SPEC_TYPE (pspec);
property = g_param_spec_get_name (pspec);
if (!(pspec->flags & G_PARAM_WRITABLE))
{
gchar* prop_comment = g_strdup_printf ("# %s", property);
g_key_file_set_string (key_file, "settings", prop_comment, "");
g_free (prop_comment);
continue;
}
if (type == G_TYPE_PARAM_STRING)
{
const gchar* string;
g_object_get (settings, property, &string, NULL);
g_key_file_set_string (key_file, "settings", property,
string ? string : "");
}
else if (type == G_TYPE_PARAM_INT)
{
gint integer;
g_object_get (settings, property, &integer, NULL);
g_key_file_set_integer (key_file, "settings", property, integer);
}
else if (type == G_TYPE_PARAM_FLOAT)
{
gfloat number;
g_object_get (settings, property, &number, NULL);
g_key_file_set_double (key_file, "settings", property, number);
}
else if (type == G_TYPE_PARAM_BOOLEAN)
{
gboolean boolean;
g_object_get (settings, property, &boolean, NULL);
g_key_file_set_boolean (key_file, "settings", property, boolean);
}
else if (type == G_TYPE_PARAM_ENUM)
{
GEnumClass* enum_class = G_ENUM_CLASS (
g_type_class_ref (pspec->value_type));
gint integer;
g_object_get (settings, property, &integer, NULL);
GEnumValue* enum_value = g_enum_get_value (enum_class, integer);
g_key_file_set_string (key_file, "settings", property,
enum_value->value_name);
}
else
g_warning (_("Invalid configuration value '%s'"), property);
}
saved = sokoke_key_file_save_to_file (key_file, filename, error);
g_key_file_free (key_file);
return saved;
}
static KatzeArray*
search_engines_new_from_file (const gchar* filename,
GError** error)
{
KatzeArray* search_engines;
GKeyFile* key_file;
gchar** engines;
guint i, j, n_properties;
KatzeItem* item;
GParamSpec** pspecs;
const gchar* property;
gchar* value;
search_engines = katze_array_new (KATZE_TYPE_ITEM);
key_file = g_key_file_new ();
g_key_file_load_from_file (key_file, filename,
G_KEY_FILE_KEEP_COMMENTS, error);
/*g_key_file_load_from_data_dirs(keyFile, sFilename, NULL
, G_KEY_FILE_KEEP_COMMENTS, error);*/
engines = g_key_file_get_groups (key_file, NULL);
pspecs = g_object_class_list_properties (G_OBJECT_GET_CLASS (search_engines),
&n_properties);
for (i = 0; engines[i] != NULL; i++)
{
item = katze_item_new ();
for (j = 0; j < n_properties; j++)
{
if (!G_IS_PARAM_SPEC_STRING (pspecs[j]))
continue;
property = g_param_spec_get_name (pspecs[j]);
value = g_key_file_get_string (key_file, engines[i],
property, NULL);
g_object_set (item, property, value, NULL);
g_free (value);
}
katze_array_add_item (search_engines, item);
}
g_strfreev (engines);
g_key_file_free (key_file);
return search_engines;
}
static gboolean
search_engines_save_to_file (KatzeArray* search_engines,
const gchar* filename,
GError** error)
{
GKeyFile* key_file;
guint n, i, j, n_properties;
KatzeItem* item;
const gchar* name;
GParamSpec** pspecs;
const gchar* property;
gchar* value;
gboolean saved;
key_file = g_key_file_new ();
n = katze_array_get_length (search_engines);
pspecs = g_object_class_list_properties (G_OBJECT_GET_CLASS (search_engines),
&n_properties);
for (i = 0; i < n; i++)
{
item = katze_array_get_nth_item (search_engines, i);
name = katze_item_get_name (item);
for (j = 0; j < n_properties; j++)
{
if (!G_IS_PARAM_SPEC_STRING (pspecs[j]))
continue;
property = g_param_spec_get_name (pspecs[j]);
g_object_get (item, property, &value, NULL);
if (value)
g_key_file_set_string (key_file, name, property, value);
g_free (value);
}
}
saved = sokoke_key_file_save_to_file (key_file, filename, error);
g_key_file_free (key_file);
return saved;
}
#if HAVE_LIBXML
static KatzeItem*
katze_item_from_xmlNodePtr (xmlNodePtr cur)
{
KatzeItem* item;
xmlChar* key;
item = katze_item_new ();
key = xmlGetProp (cur, (xmlChar*)"href");
katze_item_set_uri (item, (gchar*)key);
g_free (key);
cur = cur->xmlChildrenNode;
while (cur)
{
if (!xmlStrcmp (cur->name, (const xmlChar*)"title"))
{
key = xmlNodeGetContent (cur);
katze_item_set_name (item, g_strstrip ((gchar*)key));
g_free (key);
}
else if (!xmlStrcmp (cur->name, (const xmlChar*)"desc"))
{
key = xmlNodeGetContent (cur);
katze_item_set_text (item, g_strstrip ((gchar*)key));
g_free (key);
}
cur = cur->next;
}
return item;
}
/* Create an array from an xmlNodePtr */
static KatzeArray*
katze_array_from_xmlNodePtr (xmlNodePtr cur)
{
KatzeArray* array;
xmlChar* key;
KatzeItem* item;
array = katze_array_new (KATZE_TYPE_ARRAY);
key = xmlGetProp (cur, (xmlChar*)"folded");
if (key)
{
/* if (!g_ascii_strncasecmp ((gchar*)key, "yes", 3))
folder->folded = TRUE;
else if (!g_ascii_strncasecmp ((gchar*)key, "no", 2))
folder->folded = FALSE;
else
g_warning ("XBEL: Unknown value for folded."); */
xmlFree (key);
}
cur = cur->xmlChildrenNode;
while (cur)
{
if (!xmlStrcmp (cur->name, (const xmlChar*)"title"))
{
key = xmlNodeGetContent (cur);
katze_item_set_name (KATZE_ITEM (array), g_strstrip ((gchar*)key));
}
else if (!xmlStrcmp (cur->name, (const xmlChar*)"desc"))
{
key = xmlNodeGetContent (cur);
katze_item_set_text (KATZE_ITEM (array), g_strstrip ((gchar*)key));
}
else if (!xmlStrcmp (cur->name, (const xmlChar*)"folder"))
{
item = (KatzeItem*)katze_array_from_xmlNodePtr (cur);
katze_array_add_item (array, item);
}
else if (!xmlStrcmp (cur->name, (const xmlChar*)"bookmark"))
{
item = katze_item_from_xmlNodePtr (cur);
katze_array_add_item (array, item);
}
else if (!xmlStrcmp (cur->name, (const xmlChar*)"separator"))
{
item = katze_item_new ();
katze_array_add_item (array, item);
}
cur = cur->next;
}
return array;
}
/* Loads the contents from an xmlNodePtr into an array. */
static gboolean
katze_array_from_xmlDocPtr (KatzeArray* array,
xmlDocPtr doc)
{
xmlNodePtr cur;
xmlChar* version;
gchar* value;
KatzeItem* item;
cur = xmlDocGetRootElement (doc);
version = xmlGetProp (cur, (xmlChar*)"version");
if (xmlStrcmp (version, (xmlChar*)"1.0"))
g_warning ("XBEL version is not 1.0.");
xmlFree (version);
value = (gchar*)xmlGetProp (cur, (xmlChar*)"title");
katze_item_set_name (KATZE_ITEM (array), value);
g_free (value);
value = (gchar*)xmlGetProp (cur, (xmlChar*)"desc");
katze_item_set_text (KATZE_ITEM (array), value);
g_free (value);
if ((cur = xmlDocGetRootElement (doc)) == NULL)
{
/* Empty document */
return FALSE;
}
if (xmlStrcmp (cur->name, (const xmlChar*)"xbel"))
{
/* Wrong document kind */
return FALSE;
}
cur = cur->xmlChildrenNode;
while (cur)
{
item = NULL;
if (!xmlStrcmp (cur->name, (const xmlChar*)"folder"))
item = (KatzeItem*)katze_array_from_xmlNodePtr (cur);
else if (!xmlStrcmp (cur->name, (const xmlChar*)"bookmark"))
item = katze_item_from_xmlNodePtr (cur);
else if (!xmlStrcmp (cur->name, (const xmlChar*)"separator"))
item = katze_item_new ();
/*else if (!xmlStrcmp (cur->name, (const xmlChar*)"info"))
item = katze_xbel_parse_info (xbel, cur);*/
if (item)
katze_array_add_item (array, item);
cur = cur->next;
}
return TRUE;
}
static gboolean
katze_array_from_file (KatzeArray* array,
const gchar* filename,
GError** error)
{
xmlDocPtr doc;
g_return_val_if_fail (katze_array_is_a (array, KATZE_TYPE_ITEM), FALSE);
g_return_val_if_fail (filename != NULL, FALSE);
if (!g_file_test (filename, G_FILE_TEST_EXISTS))
{
/* File doesn't exist */
*error = g_error_new_literal (G_FILE_ERROR, G_FILE_ERROR_NOENT,
_("File not found."));
return FALSE;
}
if ((doc = xmlParseFile (filename)) == NULL)
{
/* No valid xml or broken encoding */
*error = g_error_new_literal (G_FILE_ERROR, G_FILE_ERROR_FAILED,
_("Malformed document."));
return FALSE;
}
if (!katze_array_from_xmlDocPtr (array, doc))
{
/* Parsing failed */
xmlFreeDoc (doc);
*error = g_error_new_literal (G_FILE_ERROR, G_FILE_ERROR_FAILED,
_("Malformed document."));
return FALSE;
}
xmlFreeDoc (doc);
return TRUE;
}
#endif
#if HAVE_SQLITE
/* Open database 'dbname' */
static sqlite3*
db_open (const char* dbname,
GError** error)
{
sqlite3* db;
if (sqlite3_open (dbname, &db))
{
if (error)
{
*error = g_error_new (MIDORI_HISTORY_ERROR,
MIDORI_HISTORY_ERROR_DB_OPEN,
_("Failed to open database: %s\n"),
sqlite3_errmsg (db));
}
sqlite3_close (db);
return NULL;
}
return (db);
}
/* Close database 'db' */
static void
db_close (sqlite3* db)
{
sqlite3_close (db);
}
/* Execute an SQL statement and run 'callback' on the result data */
static gboolean
db_exec_callback (sqlite3* db,
const char* sqlcmd,
int (*callback)(void*, int, char**, char**),
void* cbarg,
GError** error)
{
char* errmsg;
if (sqlite3_exec (db, sqlcmd, callback, cbarg, &errmsg) != SQLITE_OK)
{
if (error)
{
*error = g_error_new (MIDORI_HISTORY_ERROR,
MIDORI_HISTORY_ERROR_EXEC_SQL,
_("Failed to execute database statement: %s\n"),
errmsg);
}
sqlite3_free (errmsg);
return FALSE;
}
return TRUE;
}
/* Execute a SQL statement */
static gboolean
db_exec (sqlite3* db,
const char* sqlcmd,
GError** error)
{
return (db_exec_callback (db, sqlcmd, NULL, NULL, error));
}
/* sqlite method for retrieving the date/ time */
static int
gettimestr (void* data,
int argc,
char** argv,
char** colname)
{
KatzeItem* item = KATZE_ITEM (data);
(void) colname;
g_return_val_if_fail (argc == 1, 1);
katze_item_set_added (item, g_ascii_strtoull (argv[0], NULL, 10));
return 0;
}
static void
midori_history_remove_item_cb (KatzeArray* history,
KatzeItem* item,
sqlite3* db)
{
gchar* sqlcmd;
gboolean success = TRUE;
GError* error = NULL;
g_return_if_fail (KATZE_IS_ITEM (item));
sqlcmd = sqlite3_mprintf (
"DELETE FROM history WHERE uri = '%q' AND"
" title = '%q' AND date = %" G_GINT64_FORMAT,
katze_item_get_uri (item),
katze_item_get_name (item),
katze_item_get_added (item));
success = db_exec (db, sqlcmd, &error);
if (!success)
{
g_printerr (_("Failed to remove history item: %s\n"), error->message);
g_error_free (error);
return ;
}
sqlite3_free (sqlcmd);
}
static void
midori_history_clear_before_cb (KatzeArray* item,
sqlite3* db)
{
g_signal_handlers_block_by_func (item, midori_history_remove_item_cb, db);
}
static void
midori_history_clear_cb (KatzeArray* history,
sqlite3* db)
{
GError* error = NULL;
g_return_if_fail (KATZE_IS_ARRAY (history));
if (!db_exec (db, "DELETE FROM history", &error))
{
g_printerr (_("Failed to clear history: %s\n"), error->message);
g_error_free (error);
}
}
static void
midori_history_notify_item_cb (KatzeItem* item,
GParamSpec* pspec,
sqlite3* db)
{
gchar* sqlcmd;
gboolean success = TRUE;
GError* error = NULL;
sqlcmd = sqlite3_mprintf ("UPDATE history SET title='%q' WHERE "
"uri='%q' AND date=%" G_GUINT64_FORMAT,
katze_item_get_name (item),
katze_item_get_uri (item),
katze_item_get_added (item));
success = db_exec (db, sqlcmd, &error);
sqlite3_free (sqlcmd);
if (!success)
{
g_printerr (_("Failed to add history item: %s\n"), error->message);
g_error_free (error);
return ;
}
}
static void
midori_history_add_item_cb (KatzeArray* array,
KatzeItem* item,
sqlite3* db)
{
gchar* sqlcmd;
gboolean success = TRUE;
GError* error = NULL;
g_return_if_fail (KATZE_IS_ITEM (item));
if (KATZE_IS_ARRAY (item))
{
g_signal_connect_after (item, "add-item",
G_CALLBACK (midori_history_add_item_cb), db);
g_signal_connect (item, "remove-item",
G_CALLBACK (midori_history_remove_item_cb), db);
g_signal_connect (item, "clear",
G_CALLBACK (midori_history_clear_before_cb), db);
return;
}
/* New item, set added to the current date/ time */
if (!katze_item_get_added (item))
{
if (!db_exec_callback (db, "SELECT date('now')",
gettimestr, item, &error))
{
g_printerr (_("Failed to add history item: %s\n"), error->message);
g_error_free (error);
return;
}
}
sqlcmd = sqlite3_mprintf ("INSERT INTO history VALUES"
"('%q', '%q', %" G_GUINT64_FORMAT ","
" %" G_GUINT64_FORMAT ")",
katze_item_get_uri (item),
katze_item_get_name (item),
katze_item_get_added (item),
katze_item_get_added (KATZE_ITEM (array)));
success = db_exec (db, sqlcmd, &error);
sqlite3_free (sqlcmd);
if (!success)
{
g_printerr (_("Failed to add history item: %s\n"), error->message);
g_error_free (error);
return ;
}
/* The title is set after the item is added */
g_signal_connect_after (item, "notify::name",
G_CALLBACK (midori_history_notify_item_cb), db);
}
static int
midori_history_add_items (void* data,
int argc,
char** argv,
char** colname)
{
KatzeItem* item;
KatzeArray* parent;
KatzeArray* array;
gint64 date;
gint64 day;
gint i;
gint j;
gint n;
gint ncols = 4;
gchar token[50];
array = KATZE_ARRAY (data);
g_return_val_if_fail (KATZE_IS_ARRAY (array), 1);
/* Test whether have the right number of columns */
g_return_val_if_fail (argc % ncols == 0, 1);
for (i = 0; i < (argc - ncols) + 1; i++)
{
if (argv[i])
{
if (colname[i] && !g_ascii_strcasecmp (colname[i], "uri") &&
colname[i + 1] && !g_ascii_strcasecmp (colname[i + 1], "title") &&
colname[i + 2] && !g_ascii_strcasecmp (colname[i + 2], "date") &&
colname[i + 3] && !g_ascii_strcasecmp (colname[i + 3], "day"))
{
item = katze_item_new ();
katze_item_set_uri (item, argv[i]);
katze_item_set_name (item, argv[i + 1]);
date = g_ascii_strtoull (argv[i + 2], NULL, 10);
day = g_ascii_strtoull (argv[i + 3], NULL, 10);
katze_item_set_added (item, date);
n = katze_array_get_length (array);
for (j = n - 1; j >= 0; j--)
{
parent = katze_array_get_nth_item (array, j);
if (day == katze_item_get_added (KATZE_ITEM (parent)))
break;
}
if (j < 0)
{
parent = katze_array_new (KATZE_TYPE_ARRAY);
katze_item_set_added (KATZE_ITEM (parent), day);
strftime (token, sizeof (token), "%x",
localtime ((time_t *)&date));
katze_item_set_token (KATZE_ITEM (parent), token);
katze_array_add_item (array, parent);
}
katze_array_add_item (parent, item);
}
}
}
return 0;
}
static int
midori_history_test_day_column (void* data,
int argc,
char** argv,
char** colname)
{
gint i;
gboolean* has_day;
has_day = (gboolean*)data;
for (i = 0; i < argc; i++)
{
if (argv[i] &&
!g_ascii_strcasecmp (colname[i], "name") &&
!g_ascii_strcasecmp (argv[i], "day"))
{
*has_day = TRUE;
break;
}
}
return 0;
}
static sqlite3*
midori_history_initialize (KatzeArray* array,
const gchar* filename,
GError** error)
{
sqlite3* db;
KatzeItem* item;
gint i, n;
gboolean has_day;
has_day = FALSE;
if ((db = db_open (filename, error)) == NULL)
return db;
if (!db_exec (db,
"CREATE TABLE IF NOT EXISTS "
"history(uri text, title text, date integer, day integer)",
error))
return NULL;
if (!db_exec_callback (db,
"PRAGMA table_info(history)",
midori_history_test_day_column,
&has_day, error))
return NULL;
if (!has_day)
{
if (!db_exec (db,
"BEGIN TRANSACTION;"
"CREATE TEMPORARY TABLE backup (uri text, title text, date integer);"
"INSERT INTO backup SELECT uri,title,date FROM history;"
"DROP TABLE history;"
"CREATE TABLE history (uri text, title text, date integer, day integer);"
"INSERT INTO history SELECT uri,title,date,"
"julianday(date(date,'unixepoch','start of day','+1 day'))"
" - julianday('0001-01-01','start of day')"
"FROM backup;"
"DROP TABLE backup;"
"COMMIT;",
error))
return NULL;
}
if (!db_exec_callback (db,
"SELECT uri, title, date, day FROM history "
"ORDER BY date ASC",
midori_history_add_items,
array,
error))
return NULL;
n = katze_array_get_length (array);
for (i = 0; i < n; i++)
{
item = katze_array_get_nth_item (array, i);
g_signal_connect_after (item, "add-item",
G_CALLBACK (midori_history_add_item_cb), db);
g_signal_connect (item, "remove-item",
G_CALLBACK (midori_history_remove_item_cb), db);
g_signal_connect (item, "clear",
G_CALLBACK (midori_history_clear_before_cb), db);
}
return db;
}
static void
midori_history_terminate (sqlite3* db,
gint max_history_age)
{
gchar* sqlcmd;
gboolean success = TRUE;
GError* error = NULL;
sqlcmd = g_strdup_printf (
"DELETE FROM history WHERE "
"(julianday(date('now')) - julianday(date(date,'unixepoch')))"
" >= %d", max_history_age);
db_exec (db, sqlcmd, &error);
if (!success)
{
/* i18n: Couldn't remove items that are older than n days */
g_printerr (_("Failed to remove old history items: %s\n"), error->message);
g_error_free (error);
return ;
}
g_free (sqlcmd);
db_close (db);
}
#endif
static void
midori_app_quit_cb (MidoriApp* app)
{
gchar* config_file = build_config_filename ("running");
g_unlink (config_file);
g_free (config_file);
}
static void
settings_notify_cb (MidoriWebSettings* settings,
GParamSpec* pspec)
{
gchar* config_file;
GError* error;
config_file = build_config_filename ("config");
error = NULL;
if (!settings_save_to_file (settings, config_file, &error))
{
g_warning (_("The configuration couldn't be saved. %s"), error->message);
g_error_free (error);
}
g_free (config_file);
}
static void
accel_map_changed_cb (GtkAccelMap* accel_map,
gchar* accel_path,
guint accel_key,
GdkModifierType accel_mods)
{
gchar* config_file = build_config_filename ("accels");
gtk_accel_map_save (config_file);
g_free (config_file);
}
static void
midori_search_engines_modify_cb (KatzeArray* array,
gpointer item,
KatzeArray* search_engines)
{
gchar* config_file;
GError* error;
config_file = build_config_filename ("search");
error = NULL;
if (!search_engines_save_to_file (search_engines, config_file, &error))
{
g_warning (_("The search engines couldn't be saved. %s"),
error->message);
g_error_free (error);
}
g_free (config_file);
}
static gchar*
_simple_xml_element (const gchar* name,
const gchar* value)
{
gchar* value_escaped;
gchar* markup;
if (!value)
return g_strdup ("");
value_escaped = g_markup_escape_text (value, -1);
markup = g_strdup_printf ("<%s>%s</%s>\n", name, value_escaped, name);
g_free (value_escaped);
return markup;
}
static gchar*
katze_item_to_data (KatzeItem* item)
{
gchar* markup;
g_return_val_if_fail (KATZE_IS_ITEM (item), NULL);
markup = NULL;
if (KATZE_IS_ARRAY (item))
{
GString* _markup = g_string_new (NULL);
guint n = katze_array_get_length (KATZE_ARRAY (item));
guint i;
for (i = 0; i < n; i++)
{
KatzeItem* _item = katze_array_get_nth_item (KATZE_ARRAY (item), i);
gchar* item_markup = katze_item_to_data (_item);
g_string_append (_markup, item_markup);
g_free (item_markup);
}
/* gchar* folded = item->folded ? NULL : g_strdup_printf (" folded=\"no\""); */
gchar* title = _simple_xml_element ("title", katze_item_get_name (item));
gchar* desc = _simple_xml_element ("desc", katze_item_get_text (item));
markup = g_strdup_printf ("<folder%s>\n%s%s%s</folder>\n",
"" /* folded ? folded : "" */,
title, desc,
g_string_free (_markup, FALSE));
/* g_free (folded); */
g_free (title);
g_free (desc);
}
else if (katze_item_get_uri (item))
{
gchar* href_escaped = g_markup_escape_text (katze_item_get_uri (item), -1);
gchar* href = g_strdup_printf (" href=\"%s\"", href_escaped);
g_free (href_escaped);
gchar* title = _simple_xml_element ("title", katze_item_get_name (item));
gchar* desc = _simple_xml_element ("desc", katze_item_get_text (item));
markup = g_strdup_printf ("<bookmark%s>\n%s%s%s</bookmark>\n",
href,
title, desc,
"");
g_free (href);
g_free (title);
g_free (desc);
}
else
markup = g_strdup ("<separator/>\n");
return markup;
}
static gchar*
katze_array_to_xml (KatzeArray* array,
GError** error)
{
GString* inner_markup;
guint i, n;
KatzeItem* item;
gchar* item_xml;
gchar* title;
gchar* desc;
gchar* outer_markup;
g_return_val_if_fail (katze_array_is_a (array, KATZE_TYPE_ITEM), NULL);
inner_markup = g_string_new (NULL);
n = katze_array_get_length (array);
for (i = 0; i < n; i++)
{
item = katze_array_get_nth_item (array, i);
item_xml = katze_item_to_data (item);
g_string_append (inner_markup, item_xml);
g_free (item_xml);
}
title = _simple_xml_element ("title", katze_item_get_name (KATZE_ITEM (array)));
desc = _simple_xml_element ("desc", katze_item_get_text (KATZE_ITEM (array)));
outer_markup = g_strdup_printf (
"%s%s<xbel version=\"1.0\">\n%s%s%s</xbel>\n",
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
"<!DOCTYPE xbel PUBLIC \"+//IDN python.org//DTD "
"XML Bookmark Exchange Language 1.0//EN//XML\" "
"\"http://www.python.org/topics/xml/dtds/xbel-1.0.dtd\">\n",
title,
desc,
g_string_free (inner_markup, FALSE));
g_free (title);
g_free (desc);
return outer_markup;
}
static gboolean
katze_array_to_file (KatzeArray* array,
const gchar* filename,
GError** error)
{
gchar* data;
FILE* fp;
g_return_val_if_fail (katze_array_is_a (array, KATZE_TYPE_ITEM), FALSE);
g_return_val_if_fail (filename, FALSE);
if (!(data = katze_array_to_xml (array, error)))
return FALSE;
if (!(fp = fopen (filename, "w")))
{
*error = g_error_new_literal (G_FILE_ERROR, G_FILE_ERROR_ACCES,
_("Writing failed."));
return FALSE;
}
fputs (data, fp);
fclose (fp);
g_free (data);
return TRUE;
}
static void
midori_bookmarks_notify_item_cb (KatzeArray* folder,
GParamSpec* pspec,
KatzeArray* bookmarks)
{
gchar* config_file;
GError* error;
config_file = build_config_filename ("bookmarks.xbel");
error = NULL;
if (!katze_array_to_file (bookmarks, config_file, &error))
{
g_warning (_("The bookmarks couldn't be saved. %s"), error->message);
g_error_free (error);
}
g_free (config_file);
}
static void
midori_bookmarks_add_item_cb (KatzeArray* folder,
GObject* item,
KatzeArray* bookmarks);
static void
midori_bookmarks_remove_item_cb (KatzeArray* bookmarks,
GObject* item);
static void
midori_bookmarks_add_item_cb (KatzeArray* folder,
GObject* item,
KatzeArray* bookmarks)
{
gchar* config_file;
GError* error;
config_file = build_config_filename ("bookmarks.xbel");
error = NULL;
if (!katze_array_to_file (bookmarks, config_file, &error))
{
g_warning (_("The bookmarks couldn't be saved. %s"), error->message);
g_error_free (error);
}
g_free (config_file);
if (folder == bookmarks && KATZE_IS_ARRAY (item))
{
g_signal_connect_after (item, "add-item",
G_CALLBACK (midori_bookmarks_add_item_cb), bookmarks);
g_signal_connect_after (item, "remove-item",
G_CALLBACK (midori_bookmarks_remove_item_cb), NULL);
}
g_signal_connect_after (item, "notify",
G_CALLBACK (midori_bookmarks_notify_item_cb), bookmarks);
}
static void
midori_bookmarks_remove_item_cb (KatzeArray* bookmarks,
GObject* item)
{
gchar* config_file;
GError* error;
config_file = build_config_filename ("bookmarks.xbel");
error = NULL;
if (!katze_array_to_file (bookmarks, config_file, &error))
{
g_warning (_("The bookmarks couldn't be saved. %s"), error->message);
g_error_free (error);
}
g_free (config_file);
if (KATZE_IS_ARRAY (item))
g_signal_handlers_disconnect_by_func (item,
midori_bookmarks_add_item_cb, bookmarks);
}
static void
midori_trash_add_item_cb (KatzeArray* trash,
GObject* item)
{
gchar* config_file;
GError* error;
guint n;
GObject* obsolete_item;
config_file = build_config_filename ("tabtrash.xbel");
error = NULL;
if (!katze_array_to_file (trash, config_file, &error))
{
/* i18n: Trash, or wastebin, containing closed tabs */
g_warning (_("The trash couldn't be saved. %s"), error->message);
g_error_free (error);
}
g_free (config_file);
n = katze_array_get_length (trash);
if (n > 10)
{
obsolete_item = katze_array_get_nth_item (trash, 0);
katze_array_remove_item (trash, obsolete_item);
}
}
static void
midori_trash_remove_item_cb (KatzeArray* trash,
GObject* item)
{
gchar* config_file;
GError* error;
config_file = build_config_filename ("tabtrash.xbel");
error = NULL;
if (!katze_array_to_file (trash, config_file, &error))
{
g_warning (_("The trash couldn't be saved. %s"), error->message);
g_error_free (error);
}
g_free (config_file);
}
static void
midori_app_add_browser_cb (MidoriApp* app,
MidoriBrowser* browser,
KatzeNet* net)
{
GtkWidget* panel;
GtkWidget* addon;
panel = katze_object_get_object (browser, "panel");
/* Bookmarks */
addon = g_object_new (MIDORI_TYPE_BOOKMARKS, "app", app, NULL);
gtk_widget_show (addon);
midori_panel_append_page (MIDORI_PANEL (panel), MIDORI_VIEWABLE (addon));
/* History */
addon = g_object_new (MIDORI_TYPE_HISTORY, "app", app, NULL);
gtk_widget_show (addon);
midori_panel_append_page (MIDORI_PANEL (panel), MIDORI_VIEWABLE (addon));
/* Transfers */
#if 0
addon = midori_view_new (net);
gtk_widget_show (addon);
midori_panel_append_widget (MIDORI_PANEL (panel), addon,
STOCK_TRANSFERS, _("Transfers"), NULL);
#endif
/* Console */
addon = g_object_new (MIDORI_TYPE_CONSOLE, "app", app, NULL);
gtk_widget_show (addon);
midori_panel_append_page (MIDORI_PANEL (panel), MIDORI_VIEWABLE (addon));
/* Userscripts */
addon = midori_addons_new (MIDORI_ADDON_USER_SCRIPTS, GTK_WIDGET (browser));
gtk_widget_show (addon);
midori_panel_append_page (MIDORI_PANEL (panel), MIDORI_VIEWABLE (addon));
/* Userstyles */
addon = midori_addons_new (MIDORI_ADDON_USER_STYLES, GTK_WIDGET (browser));
gtk_widget_show (addon);
midori_panel_append_page (MIDORI_PANEL (panel), MIDORI_VIEWABLE (addon));
/* Plugins */
addon = g_object_new (MIDORI_TYPE_PLUGINS, "app", app, NULL);
gtk_widget_show (addon);
midori_panel_append_page (MIDORI_PANEL (panel), MIDORI_VIEWABLE (addon));
/* Extensions */
addon = g_object_new (MIDORI_TYPE_EXTENSIONS, "app", app, NULL);
gtk_widget_show (addon);
midori_panel_append_page (MIDORI_PANEL (panel), MIDORI_VIEWABLE (addon));
}
static void
midori_browser_session_cb (MidoriBrowser* browser,
gpointer pspec,
KatzeArray* session)
{
gchar* config_file;
GError* error;
config_file = build_config_filename ("session.xbel");
error = NULL;
if (!katze_array_to_file (session, config_file, &error))
{
g_warning (_("The session couldn't be saved. %s"), error->message);
g_error_free (error);
}
g_free (config_file);
}
static void
midori_browser_weak_notify_cb (MidoriBrowser* browser,
KatzeArray* session)
{
g_object_disconnect (browser, "any-signal",
G_CALLBACK (midori_browser_session_cb), session, NULL);
}
#if HAVE_LIBSOUP
typedef void (*GObjectConstructed) (GObject*);
static void
soup_session_settings_notify_http_proxy_cb (MidoriWebSettings* settings,
GParamSpec* pspec,
SoupSession* session)
{
gboolean auto_detect_proxy;
gchar* http_proxy;
SoupURI* proxy_uri;
auto_detect_proxy = katze_object_get_boolean (settings, "auto-detect-proxy");
if (auto_detect_proxy)
http_proxy = g_strdup (g_getenv ("http_proxy"));
else
http_proxy = katze_object_get_string (settings, "http-proxy");
/* soup_uri_new expects a non-NULL string */
proxy_uri = soup_uri_new (http_proxy ? http_proxy : "");
g_free (http_proxy);
g_object_set (session, "proxy-uri", proxy_uri, NULL);
if (proxy_uri)
soup_uri_free (proxy_uri);
}
static void
soup_session_settings_notify_ident_string_cb (MidoriWebSettings* settings,
GParamSpec* pspec,
SoupSession* session)
{
gchar* ident_string = katze_object_get_string (settings, "ident-string");
g_object_set (session, "user-agent", ident_string, NULL);
g_free (ident_string);
}
static void
midori_soup_session_debug (SoupSession* session)
{
const char* soup_debug = g_getenv ("MIDORI_SOUP_DEBUG");
if (soup_debug)
{
gint soup_debug_level = atoi (soup_debug);
SoupLogger* logger = soup_logger_new (soup_debug_level, -1);
soup_logger_attach (logger, session);
g_object_unref (logger);
}
}
/* The following code hooks up to any created soup session in order to
modify preferences. This is *not* a generally advisable technique
but merely a preliminary workaround until WebKit exposes its session. */
static GObjectConstructed old_session_constructed_cb;
static void
soup_session_constructed_cb (GObject* object)
{
MidoriApp* app;
MidoriWebSettings* settings;
SoupSession* session;
SoupSessionFeature* feature;
gchar* config_file;
if (old_session_constructed_cb)
old_session_constructed_cb (object);
app = g_type_get_qdata (SOUP_TYPE_SESSION,
g_quark_from_static_string ("midori-app"));
settings = katze_object_get_object (app, "settings");
session = SOUP_SESSION (object);
soup_session_settings_notify_http_proxy_cb (settings, NULL, session);
soup_session_settings_notify_ident_string_cb (settings, NULL, session);
g_signal_connect (settings, "notify::http-proxy",
G_CALLBACK (soup_session_settings_notify_http_proxy_cb), object);
g_signal_connect (settings, "notify::auto-detect-proxy",
G_CALLBACK (soup_session_settings_notify_http_proxy_cb), object);
g_signal_connect (settings, "notify::ident-string",
G_CALLBACK (soup_session_settings_notify_ident_string_cb), object);
soup_session_add_feature_by_type (session, KATZE_TYPE_HTTP_AUTH);
midori_soup_session_debug (session);
feature = g_object_new (KATZE_TYPE_HTTP_COOKIES, NULL);
config_file = build_config_filename ("cookies.txt");
g_object_set_data_full (G_OBJECT (feature), "filename",
config_file, (GDestroyNotify)g_free);
soup_session_add_feature (session, feature);
}
#endif
static void
button_modify_preferences_clicked_cb (GtkWidget* button,
MidoriWebSettings* settings)
{
GtkWidget* dialog = midori_preferences_new (
GTK_WINDOW (gtk_widget_get_toplevel (button)), settings);
if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_DELETE_EVENT)
gtk_widget_destroy (dialog);
}
static void
button_reset_session_clicked_cb (GtkWidget* button,
KatzeArray* session)
{
katze_array_clear (session);
gtk_widget_set_sensitive (button, FALSE);
}
static GtkWidget*
midori_create_diagnostic_dialog (MidoriWebSettings* settings,
KatzeArray* _session)
{
GtkWidget* dialog;
GdkScreen* screen;
GtkIconTheme* icon_theme;
GtkWidget* box;
GtkWidget* button;
dialog = gtk_message_dialog_new (
NULL, 0, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK,
_("Midori seems to have crashed the last time it was opened. "
"If this happend repeatedly, try one of the following options "
"to solve the problem."));
gtk_window_set_skip_taskbar_hint (GTK_WINDOW (dialog), FALSE);
gtk_window_set_title (GTK_WINDOW (dialog), g_get_application_name ());
screen = gtk_widget_get_screen (dialog);
if (screen)
{
icon_theme = gtk_icon_theme_get_for_screen (screen);
if (gtk_icon_theme_has_icon (icon_theme, "midori"))
gtk_window_set_icon_name (GTK_WINDOW (dialog), "midori");
else
gtk_window_set_icon_name (GTK_WINDOW (dialog), "web-browser");
}
box = gtk_hbox_new (FALSE, 0);
button = gtk_button_new_with_mnemonic (_("Modify _preferences"));
g_signal_connect (button, "clicked",
G_CALLBACK (button_modify_preferences_clicked_cb), settings);
gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 4);
button = gtk_button_new_with_mnemonic (_("Reset the last _session"));
g_signal_connect (button, "clicked",
G_CALLBACK (button_reset_session_clicked_cb), _session);
gtk_widget_set_sensitive (button, !katze_array_is_empty (_session));
gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 4);
button = gtk_button_new_with_mnemonic (_("Disable all _extensions"));
gtk_widget_set_sensitive (button, FALSE);
/* FIXME: Disable all extensions */
gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 4);
gtk_widget_show_all (box);
gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), box);
if (1)
{
/* GtkLabel can't wrap the text properly. Until some day
this works, we implement this hack to do it ourselves. */
GtkWidget* content_area;
GtkWidget* hbox;
GtkWidget* vbox;
GtkWidget* label;
GList* ch;
GtkRequisition req;
content_area = GTK_DIALOG (dialog)->vbox;
ch = gtk_container_get_children (GTK_CONTAINER (content_area));
hbox = (GtkWidget*)g_list_nth_data (ch, 0);
g_list_free (ch);
ch = gtk_container_get_children (GTK_CONTAINER (hbox));
vbox = (GtkWidget*)g_list_nth_data (ch, 1);
g_list_free (ch);
ch = gtk_container_get_children (GTK_CONTAINER (vbox));
label = (GtkWidget*)g_list_nth_data (ch, 0);
g_list_free (ch);
gtk_widget_size_request (content_area, &req);
gtk_widget_set_size_request (label, req.width * 0.9, -1);
}
return dialog;
}
static gboolean
midori_load_extensions (gpointer data)
{
MidoriApp* app = MIDORI_APP (data);
KatzeArray* extensions;
const gchar* filename;
MidoriExtension* extension;
guint n, i;
/* Load extensions */
extensions = katze_array_new (MIDORI_TYPE_EXTENSION);
if (g_module_supported ())
{
/* FIXME: Read extensions from system data dirs */
gchar* extension_path;
GDir* extension_dir;
if (!(extension_path = g_strdup (g_getenv ("MIDORI_EXTENSION_PATH"))))
extension_path = g_build_filename (LIBDIR, PACKAGE_NAME, NULL);
extension_dir = g_dir_open (extension_path, 0, NULL);
if (extension_dir != NULL)
{
while ((filename = g_dir_read_name (extension_dir)))
{
gchar* fullname;
GModule* module;
typedef MidoriExtension* (*extension_init_func)(void);
extension_init_func extension_init;
/* Ignore files which don't have the correct suffix */
if (!g_str_has_suffix (filename, G_MODULE_SUFFIX))
continue;
fullname = g_build_filename (extension_path, filename, NULL);
module = g_module_open (fullname, G_MODULE_BIND_LOCAL);
g_free (fullname);
if (module && g_module_symbol (module, "extension_init",
(gpointer) &extension_init))
extension = extension_init ();
else
{
extension = g_object_new (MIDORI_TYPE_EXTENSION,
"name", filename,
"description", g_module_error (),
NULL);
g_warning ("%s", g_module_error ());
}
katze_array_add_item (extensions, extension);
g_object_unref (extension);
}
g_dir_close (extension_dir);
}
g_free (extension_path);
}
g_object_set (app, "extensions", extensions, NULL);
n = katze_array_get_length (extensions);
for (i = 0; i < n; i++)
{
extension = katze_array_get_nth_item (extensions, i);
g_signal_emit_by_name (extension, "activate", app);
}
return FALSE;
}
static gboolean
midori_load_session (gpointer data)
{
KatzeArray* _session = KATZE_ARRAY (data);
MidoriBrowser* browser;
MidoriApp* app = katze_item_get_parent (KATZE_ITEM (_session));
gchar* config_file;
KatzeArray* session;
KatzeItem* item;
guint n, i;
browser = midori_app_create_browser (app);
midori_app_add_browser (app, browser);
gtk_widget_show (GTK_WIDGET (browser));
config_file = build_config_filename ("accels");
if (is_writable (config_file))
g_signal_connect_after (gtk_accel_map_get (), "changed",
G_CALLBACK (accel_map_changed_cb), NULL);
if (katze_array_is_empty (_session))
{
MidoriWebSettings* settings = katze_object_get_object (app, "settings");
MidoriStartup load_on_startup;
gchar* homepage;
item = katze_item_new ();
g_object_get (settings, "load-on-startup", &load_on_startup, NULL);
if (load_on_startup == MIDORI_STARTUP_BLANK_PAGE)
katze_item_set_uri (item, "");
else
{
g_object_get (settings, "homepage", &homepage, NULL);
katze_item_set_uri (item, homepage);
g_free (homepage);
}
katze_array_add_item (_session, item);
}
session = midori_browser_get_proxy_array (browser);
n = katze_array_get_length (_session);
for (i = 0; i < n; i++)
{
item = katze_array_get_nth_item (_session, i);
midori_browser_add_item (browser, item);
}
/* FIXME: Switch to the last active page */
item = katze_array_get_nth_item (_session, 0);
if (!strcmp (katze_item_get_uri (item), ""))
midori_browser_activate_action (browser, "Location");
g_object_unref (_session);
katze_assign (config_file, build_config_filename ("session.xbel"));
if (is_writable (config_file))
{
g_signal_connect_after (browser, "notify::uri",
G_CALLBACK (midori_browser_session_cb), session);
g_signal_connect_after (browser, "add-tab",
G_CALLBACK (midori_browser_session_cb), session);
g_signal_connect_after (browser, "remove-tab",
G_CALLBACK (midori_browser_session_cb), session);
g_object_weak_ref (G_OBJECT (session),
(GWeakNotify)(midori_browser_weak_notify_cb), browser);
}
return FALSE;
}
static gint
midori_run_script (const gchar* filename)
{
if (!(filename))
{
g_print ("%s - %s\n", _("Midori"), _("No filename specified"));
return 1;
}
JSGlobalContextRef js_context;
gchar* exception;
gchar* script;
GError* error = NULL;
#ifdef WEBKIT_CHECK_VERSION
#if WEBKIT_CHECK_VERSION (1, 0, 3)
#define HAVE_JSCONTEXTGROUP 1
#endif
#endif
#if HAVE_JSCONTEXTGROUP
js_context = JSGlobalContextCreateInGroup (NULL, NULL);
#else
js_context = JSGlobalContextCreate (NULL);
#endif
if (g_file_get_contents (filename, &script, NULL, &error))
{
if (sokoke_js_script_eval (js_context, script, &exception))
exception = NULL;
g_free (script);
}
else if (error)
{
exception = g_strdup (error->message);
g_error_free (error);
}
else
exception = g_strdup (_("An unknown error occured."));
JSGlobalContextRelease (js_context);
if (!exception)
return 0;
g_print ("%s - Exception: %s\n", filename, exception);
return 1;
}
int
main (int argc,
char** argv)
{
gboolean run;
gboolean version;
gchar** uris;
MidoriApp* app;
gboolean result;
GError* error;
GOptionEntry entries[] =
{
{ "run", 'r', 0, G_OPTION_ARG_NONE, &run,
N_("Run the specified filename as javascript"), NULL },
{ "version", 'V', 0, G_OPTION_ARG_NONE, &version,
N_("Display program version"), NULL },
{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &uris,
N_("Addresses"), NULL },
{ NULL }
};
GString* error_messages;
MidoriWebSettings* settings;
gchar* config_file;
MidoriStartup load_on_startup;
KatzeArray* search_engines;
KatzeArray* bookmarks;
KatzeArray* history;
KatzeArray* _session;
KatzeArray* trash;
guint i;
gchar* uri;
KatzeItem* item;
gchar* uri_ready;
#if HAVE_LIBSOUP
GObjectClass* webkit_class;
KatzeNet* net;
SoupSession* s_session;
#endif
#if HAVE_LIBSOUP_2_25_2
SoupCookieJar* jar;
#endif
#if HAVE_SQLITE
sqlite3* db;
gint max_history_age;
#endif
#if ENABLE_NLS
setlocale (LC_ALL, "");
if (g_getenv ("NLSPATH"))
bindtextdomain (GETTEXT_PACKAGE, g_getenv ("NLSPATH"));
else
bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
textdomain (GETTEXT_PACKAGE);
#endif
/* Parse cli options */
run = FALSE;
version = FALSE;
uris = NULL;
error = NULL;
if (!gtk_init_with_args (&argc, &argv, _("[Addresses]"), entries,
GETTEXT_PACKAGE, &error))
{
g_print ("%s - %s\n", _("Midori"), error->message);
g_error_free (error);
return 1;
}
/* libSoup uses threads, therefore if WebKit is built with libSoup
or Midori is using it, we need to initialize threads. */
if (!g_thread_supported ()) g_thread_init (NULL);
sokoke_register_stock_items ();
g_set_application_name (_("Midori"));
if (version)
{
g_print (
"%s %s\n\n"
"Copyright (c) 2007-2008 Christian Dywan\n\n"
"%s\n"
"\t%s\n\n"
"%s\n"
"\thttp://www.twotoasts.de\n",
_("Midori"), PACKAGE_VERSION,
_("Please report comments, suggestions and bugs to:"),
PACKAGE_BUGREPORT,
_("Check for new versions at:")
);
return 0;
}
/* Standalone javascript support */
if (run)
return midori_run_script (uris ? *uris : NULL);
app = midori_app_new ();
/* FIXME: The app might be 'running' but actually showing a dialog
after a crash, so running a new window isn't a good idea. */
if (midori_app_instance_is_running (app))
{
GtkWidget* dialog;
/* TODO: Open as many tabs as we have uris, seperated by pipes */
if (uris)
result = midori_app_instance_send_uris (app, uris);
else
result = midori_app_instance_send_new_browser (app);
if (result)
return 0;
dialog = gtk_message_dialog_new (NULL,
0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s",
_("An instance of Midori is already running but not responding.\n"));
if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_DELETE_EVENT)
gtk_widget_destroy (dialog);
/* FIXME: Allow killing the existing instance */
return 1;
}
#if HAVE_LIBSOUP
webkit_class = g_type_class_ref (WEBKIT_TYPE_WEB_VIEW);
if (!g_object_class_find_property (webkit_class, "session"))
{
/* This is a nasty trick that allows us to manipulate preferences
even without having a pointer to the session. */
soup_session_get_type ();
SoupSessionClass* session_class = g_type_class_ref (SOUP_TYPE_SESSION);
if (session_class)
{
g_type_set_qdata (SOUP_TYPE_SESSION,
g_quark_from_static_string ("midori-app"), app);
old_session_constructed_cb = G_OBJECT_CLASS (session_class)->constructed;
G_OBJECT_CLASS (session_class)->constructed = soup_session_constructed_cb;
}
}
#endif
/* Load configuration files */
error_messages = g_string_new (NULL);
config_file = build_config_filename ("config");
error = NULL;
settings = settings_new_from_file (config_file);
katze_assign (config_file, build_config_filename ("accels"));
gtk_accel_map_load (config_file);
katze_assign (config_file, build_config_filename ("search"));
error = NULL;
search_engines = search_engines_new_from_file (config_file, &error);
/* We ignore for instance empty files */
if (error && (error->code == G_KEY_FILE_ERROR_PARSE
|| error->code == G_FILE_ERROR_NOENT))
{
g_error_free (error);
error = NULL;
}
if (!error && katze_array_is_empty (search_engines))
{
const gchar* const * config_dirs = g_get_system_config_dirs ();
i = 0;
while (config_dirs[i])
{
g_object_unref (search_engines);
katze_assign (config_file,
g_build_filename (config_dirs[i], PACKAGE_NAME, "search", NULL));
search_engines = search_engines_new_from_file (config_file, NULL);
if (!katze_array_is_empty (search_engines))
break;
i++;
}
if (katze_array_is_empty (search_engines))
{
g_object_unref (search_engines);
katze_assign (config_file,
g_build_filename (SYSCONFDIR, "xdg", PACKAGE_NAME, "search", NULL));
search_engines = search_engines_new_from_file (config_file, NULL);
}
}
else if (error)
{
if (error->code != G_FILE_ERROR_NOENT)
g_string_append_printf (error_messages,
_("The search engines couldn't be loaded. %s\n"),
error->message);
g_error_free (error);
}
bookmarks = katze_array_new (KATZE_TYPE_ARRAY);
#if HAVE_LIBXML
katze_assign (config_file, build_config_filename ("bookmarks.xbel"));
error = NULL;
if (!katze_array_from_file (bookmarks, config_file, &error))
{
if (error->code != G_FILE_ERROR_NOENT)
g_string_append_printf (error_messages,
_("The bookmarks couldn't be loaded: %s\n"), error->message);
g_error_free (error);
}
#endif
_session = katze_array_new (KATZE_TYPE_ITEM);
#if HAVE_LIBXML
g_object_get (settings, "load-on-startup", &load_on_startup, NULL);
if (load_on_startup == MIDORI_STARTUP_LAST_OPEN_PAGES)
{
katze_assign (config_file, build_config_filename ("session.xbel"));
error = NULL;
if (!katze_array_from_file (_session, config_file, &error))
{
if (error->code != G_FILE_ERROR_NOENT)
g_string_append_printf (error_messages,
_("The session couldn't be loaded: %s\n"), error->message);
g_error_free (error);
}
}
#endif
trash = katze_array_new (KATZE_TYPE_ITEM);
#if HAVE_LIBXML
katze_assign (config_file, build_config_filename ("tabtrash.xbel"));
error = NULL;
if (!katze_array_from_file (trash, config_file, &error))
{
if (error->code != G_FILE_ERROR_NOENT)
g_string_append_printf (error_messages,
_("The trash couldn't be loaded: %s\n"), error->message);
g_error_free (error);
}
#endif
#if HAVE_SQLITE
katze_assign (config_file, build_config_filename ("history.db"));
#endif
history = katze_array_new (KATZE_TYPE_ARRAY);
#if HAVE_SQLITE
error = NULL;
if ((db = midori_history_initialize (history, config_file, &error)) == NULL)
{
g_string_append_printf (error_messages,
_("The history couldn't be loaded: %s\n"), error->message);
g_error_free (error);
}
#endif
/* In case of errors */
if (error_messages->len)
{
GdkScreen* screen;
GtkIconTheme* icon_theme;
GtkWidget* dialog = gtk_message_dialog_new (
NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_NONE,
_("The following errors occured:"));
gtk_window_set_skip_taskbar_hint (GTK_WINDOW (dialog), FALSE);
gtk_window_set_title (GTK_WINDOW (dialog), g_get_application_name ());
screen = gtk_widget_get_screen (dialog);
if (screen)
{
icon_theme = gtk_icon_theme_get_for_screen (screen);
if (gtk_icon_theme_has_icon (icon_theme, "midori"))
gtk_window_set_icon_name (GTK_WINDOW (dialog), "midori");
else
gtk_window_set_icon_name (GTK_WINDOW (dialog), "web-browser");
}
gtk_message_dialog_format_secondary_text (
GTK_MESSAGE_DIALOG (dialog), "%s", error_messages->str);
gtk_dialog_add_buttons (GTK_DIALOG (dialog),
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
_("_Ignore"), GTK_RESPONSE_ACCEPT,
NULL);
if (gtk_dialog_run (GTK_DIALOG (dialog)) != GTK_RESPONSE_ACCEPT)
{
g_object_unref (settings);
g_object_unref (search_engines);
g_object_unref (bookmarks);
g_object_unref (_session);
g_object_unref (trash);
g_object_unref (history);
g_string_free (error_messages, TRUE);
return 0;
}
gtk_widget_destroy (dialog);
/* FIXME: Since we will overwrite files that could not be loaded
, would we want to make backups? */
}
g_string_free (error_messages, TRUE);
#if HAVE_LIBSOUP
webkit_class = g_type_class_ref (WEBKIT_TYPE_WEB_VIEW);
if (g_object_class_find_property (webkit_class, "session"))
{
net = katze_net_new ();
s_session = katze_net_get_session (net);
#if HAVE_LIBSOUP_2_25_2
katze_assign (config_file, build_config_filename ("cookies.txt"));
jar = soup_cookie_jar_text_new (config_file, FALSE);
/* FIXME: Handle "accept-cookies" preference */
soup_session_add_feature (s_session, SOUP_SESSION_FEATURE (jar));
g_object_unref (jar);
#endif
soup_session_settings_notify_http_proxy_cb (settings, NULL, s_session);
soup_session_settings_notify_ident_string_cb (settings, NULL, s_session);
g_signal_connect (settings, "notify::http-proxy",
G_CALLBACK (soup_session_settings_notify_http_proxy_cb), s_session);
g_signal_connect (settings, "notify::ident-string",
G_CALLBACK (soup_session_settings_notify_ident_string_cb), s_session);
soup_session_add_feature_by_type (s_session, KATZE_TYPE_HTTP_AUTH);
midori_soup_session_debug (s_session);
g_object_unref (net);
}
#endif
/* Open as many tabs as we have uris, seperated by pipes */
i = 0;
while (uris && uris[i])
{
uri = strtok (g_strdup (uris[i]), "|");
while (uri != NULL)
{
item = katze_item_new ();
uri_ready = sokoke_magic_uri (uri, NULL);
katze_item_set_uri (item, uri_ready);
g_free (uri_ready);
katze_array_add_item (_session, item);
uri = strtok (NULL, "|");
}
g_free (uri);
i++;
}
katze_assign (config_file, build_config_filename ("config"));
if (is_writable (config_file))
g_signal_connect_after (settings, "notify",
G_CALLBACK (settings_notify_cb), NULL);
katze_assign (config_file, build_config_filename ("search"));
if (is_writable (config_file))
{
g_signal_connect_after (search_engines, "add-item",
G_CALLBACK (midori_search_engines_modify_cb), search_engines);
g_signal_connect_after (search_engines, "remove-item",
G_CALLBACK (midori_search_engines_modify_cb), search_engines);
if (!katze_array_is_empty (search_engines))
{
guint n = katze_array_get_length (search_engines);
for (i = 0; i < n; i++)
{
item = katze_array_get_nth_item (search_engines, i);
g_signal_connect_after (item, "notify",
G_CALLBACK (midori_search_engines_modify_cb), search_engines);
}
}
}
katze_assign (config_file, build_config_filename ("bookmarks.xbel"));
if (is_writable (config_file))
{
g_signal_connect_after (bookmarks, "add-item",
G_CALLBACK (midori_bookmarks_add_item_cb), bookmarks);
g_signal_connect_after (bookmarks, "remove-item",
G_CALLBACK (midori_bookmarks_remove_item_cb), NULL);
if (!katze_array_is_empty (bookmarks))
{
guint n = katze_array_get_length (bookmarks);
for (i = 0; i < n; i++)
{
item = katze_array_get_nth_item (bookmarks, i);
if (KATZE_IS_ARRAY (item))
{
g_signal_connect_after (item, "add-item",
G_CALLBACK (midori_bookmarks_add_item_cb), bookmarks);
g_signal_connect_after (item, "remove-item",
G_CALLBACK (midori_bookmarks_remove_item_cb), NULL);
}
g_signal_connect_after (item, "notify",
G_CALLBACK (midori_bookmarks_notify_item_cb), bookmarks);
}
}
}
katze_assign (config_file, build_config_filename ("tabtrash.xbel"));
if (is_writable (config_file))
{
g_signal_connect_after (trash, "add-item",
G_CALLBACK (midori_trash_add_item_cb), NULL);
g_signal_connect_after (trash, "remove-item",
G_CALLBACK (midori_trash_remove_item_cb), NULL);
}
#if HAVE_SQLITE
katze_assign (config_file, build_config_filename ("history.db"));
if (is_writable (config_file))
{
g_signal_connect_after (history, "add-item",
G_CALLBACK (midori_history_add_item_cb), db);
g_signal_connect_after (history, "clear",
G_CALLBACK (midori_history_clear_cb), db);
}
#endif
/* We test for the presence of a dummy file which is created once
and deleted during normal runtime, but persists in case of a crash. */
katze_assign (config_file, build_config_filename ("running"));
if (katze_object_get_boolean (settings, "show-crash-dialog")
&& g_file_test (config_file, G_FILE_TEST_EXISTS))
{
GtkWidget* dialog = midori_create_diagnostic_dialog (settings, _session);
gtk_dialog_run (GTK_DIALOG (dialog));
gtk_widget_destroy (dialog);
}
else
g_file_set_contents (config_file, "RUNNING", -1, NULL);
g_signal_connect (app, "quit", G_CALLBACK (midori_app_quit_cb), NULL);
g_object_set (app, "settings", settings,
"bookmarks", bookmarks,
"trash", trash,
"search-engines", search_engines,
"history", history,
NULL);
g_object_unref (history);
g_object_unref (search_engines);
g_object_unref (bookmarks);
g_object_unref (trash);
g_object_unref (settings);
g_signal_connect (app, "add-browser",
G_CALLBACK (midori_app_add_browser_cb), NULL);
g_idle_add (midori_load_extensions, app);
katze_item_set_parent (KATZE_ITEM (_session), app);
g_idle_add (midori_load_session, _session);
gtk_main ();
#if HAVE_SQLITE
settings = katze_object_get_object (app, "settings");
g_object_get (settings, "maximum-history-age", &max_history_age, NULL);
midori_history_terminate (db, max_history_age);
#endif
g_object_unref (app);
g_free (config_file);
return 0;
}