midori/extensions/external-download-manager.vala

367 lines
14 KiB
Vala

/*
Copyright (C) 2012 André Stösel <andre@stoesel.de>
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.
*/
namespace EDM {
#if !HAVE_WIN32
[DBus (name = "net.launchpad.steadyflow.App")]
interface SteadyflowInterface : GLib.Object {
public abstract void AddFile (string url) throws IOError;
}
#endif
private class DownloadRequest : GLib.Object {
public string uri;
public string auth;
public string referer;
public string? cookie_header;
}
internal Manager manager;
private class Manager : GLib.Object {
private Soup.CookieJar cookie_jar;
private GLib.PtrArray download_managers = new GLib.PtrArray ();
public bool download_requested (Midori.View view, WebKit.Download download) {
Midori.DownloadType download_type = download.get_data<Midori.DownloadType> ("midori-download-type");
if (download_type == Midori.DownloadType.SAVE) {
var dlReq = new DownloadRequest ();
#if HAVE_WEBKIT2
dlReq.uri = download.request.get_uri ();
weak Soup.MessageHeaders headers = download.request.get_http_headers ();
#else
dlReq.uri = download.get_uri ();
var request = download.get_network_request ();
var message = request.get_message ();
weak Soup.MessageHeaders headers = message.request_headers;
#endif
dlReq.auth = headers.get ("Authorization");
dlReq.referer = headers.get ("Referer");
dlReq.cookie_header = this.cookie_jar.get_cookies (new Soup.URI (dlReq.uri), true);
for (var i = 0 ; i < download_managers.len; i++) {
var dm = download_managers.index (i) as ExternalDownloadManager;
if (dm.download (dlReq))
return true;
}
}
return false;
}
public void tab_added (Midori.Browser browser, Midori.View view) {
view.download_requested.connect (download_requested);
}
public void tab_removed (Midori.Browser browser, Midori.View view) {
view.download_requested.disconnect(download_requested);
}
public void browser_added (Midori.Browser browser) {
foreach (var tab in browser.get_tabs ())
tab_added (browser, tab);
browser.add_tab.connect (tab_added);
browser.remove_tab.connect (tab_removed);
}
public void browser_removed (Midori.Browser browser) {
foreach (var tab in browser.get_tabs ())
tab_removed (browser, tab);
browser.add_tab.disconnect (tab_added);
browser.remove_tab.disconnect (tab_removed);
}
public void activated (Midori.Extension extension, Midori.App app) {
this.download_managers.add (extension);
if (this.download_managers.len == 1) {
foreach (var browser in app.get_browsers ())
browser_added (browser);
app.add_browser.connect (browser_added);
}
}
public void deactivated (Midori.Extension extension) {
this.download_managers.remove (extension);
if (this.download_managers.len == 0) {
var app = extension.get_app ();
foreach (var browser in app.get_browsers ())
browser_removed (browser);
app.add_browser.disconnect (browser_added);
}
}
construct {
#if HAVE_WEBKIT2
var session= new Session ();
#else
var session = WebKit.get_default_session ();
#endif
this.cookie_jar = session.get_feature (typeof (Soup.CookieJar)) as Soup.CookieJar;
}
}
private abstract class ExternalDownloadManager : Midori.Extension {
public void activated (Midori.App app) {
manager.activated (this, app);
}
public void deactivated () {
manager.deactivated (this);
}
public void handle_exception (GLib.Error error) {
string ext_name;
this.get ("name",out ext_name);
var dialog = new Gtk.MessageDialog (null, Gtk.DialogFlags.MODAL,
Gtk.MessageType.ERROR, Gtk.ButtonsType.CLOSE,
_("An error occurred when attempting to download a file with the following plugin:\n" +
"%s\n\n" +
"Error:\n%s\n\n" +
"Carry on without this plugin."
),
ext_name, error.message);
dialog.response.connect ((a) => { dialog.destroy (); });
dialog.run ();
}
public abstract bool download (DownloadRequest dlReq);
}
#if !HAVE_WIN32
private class Aria2 : ExternalDownloadManager {
public override bool download (DownloadRequest dlReq) {
var url = Soup.value_array_new ();
Soup.value_array_insert (url, 0, typeof (string), dlReq.uri);
GLib.HashTable<string, GLib.Value?> options = Soup.value_hash_new ();
var referer = new GLib.Value (typeof (string));
referer.set_string (dlReq.referer);
options.insert ("referer", referer);
var headers = Soup.value_array_new ();
if (dlReq.cookie_header != null) {
Soup.value_array_insert (headers, 0, typeof (string), "Cookie: " + dlReq.cookie_header);
}
if (headers.n_values > 0)
options.insert ("header", headers);
var message = Soup.XMLRPC.request_new ("http://127.0.0.1:6800/rpc",
"aria2.addUri",
typeof (ValueArray), url,
typeof(HashTable), options);
var session = new Soup.SessionSync ();
session.send_message (message);
/* Check if the plug-in actually recieved an reply.
* The parse method do not warns us about it. And the exception
* never is launched.*/
if (message.status_code != 200) {
var dialog = new Gtk.MessageDialog (null, Gtk.DialogFlags.MODAL,
Gtk.MessageType.ERROR, Gtk.ButtonsType.CLOSE,
"%s",
_("The plug-in was unable to connect with aria2:\n" +
"Please make sure that aria2 is running with rpc enabled ie: aria2c --enable-rpc\n" +
"If it's so, check it also is using the port 6800.\n" +
"Lastly Check the configuration of your firewall.\n" +
"Whitelist aria2 and the port 6800 if they aren't."
));
dialog.response.connect ((a) => { dialog.destroy (); });
dialog.run ();
}
try {
Value v;
Soup.XMLRPC.parse_method_response ((string) message.response_body.flatten ().data, -1, out v);
return true;
} catch (Error e) {
this.handle_exception (e);
}
return false;
}
internal Aria2 () {
GLib.Object (name: _("External Download Manager - Aria2"),
description: _("Download files with Aria2"),
version: "0.1" + Midori.VERSION_SUFFIX,
authors: "André Stösel <andre@stoesel.de>",
key: "aria2");
this.activate.connect (activated);
this.deactivate.connect (deactivated);
}
}
private class SteadyFlow : ExternalDownloadManager {
public override bool download (DownloadRequest dlReq) {
try {
SteadyflowInterface dm = Bus.get_proxy_sync (
BusType.SESSION,
"net.launchpad.steadyflow.App",
"/net/launchpad/steadyflow/app");
dm.AddFile (dlReq.uri);
return true;
} catch (Error e) {
this.handle_exception (e);
}
return false;
}
internal SteadyFlow () {
GLib.Object (name: _("External Download Manager - SteadyFlow"),
description: _("Download files with SteadyFlow"),
version: "0.1" + Midori.VERSION_SUFFIX,
authors: "André Stösel <andre@stoesel.de>",
key: "steadyflow");
this.activate.connect (activated);
this.deactivate.connect (deactivated);
}
}
#endif
private class CommandLinePreferences : Gtk.Dialog {
protected Gtk.Entry input;
protected CommandLine commandline;
public CommandLinePreferences(CommandLine cl) {
this.commandline = cl;
string ext_name;
this.get ("name",out ext_name);
this.title = _("Preferences for %s").printf (ext_name);
if (this.get_class ().find_property ("has-separator") != null)
this.set ("has-separator", false);
this.border_width = 5;
this.set_modal (true);
this.set_default_size (400, 100);
this.create_widgets ();
this.response.connect (response_cb);
}
private void response_cb (Gtk.Dialog source, int response_id) {
switch (response_id) {
case Gtk.ResponseType.APPLY:
this.commandline.set_string ("commandline", this.input.get_text ());
this.commandline.update_description (this.commandline.get_app ());
this.destroy ();
break;
case Gtk.ResponseType.CANCEL:
this.destroy ();
break;
}
}
private void create_widgets () {
Gtk.Label text = new Gtk.Label (_("Command:"));
this.input = new Gtk.Entry ();
this.input.set_text (this.commandline.get_string ("commandline"));
#if HAVE_GTK3
Gtk.Box vbox = get_content_area () as Gtk.Box;
vbox.pack_start (text, false, false, 0);
vbox.pack_start (this.input, false, true, 0);
#else
this.vbox.pack_start (text, false, false, 0);
this.vbox.pack_start (this.input, false, true, 0);
#endif
this.add_button (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL);
this.add_button (Gtk.STOCK_APPLY, Gtk.ResponseType.APPLY);
this.show_all ();
}
}
private class CommandLine : ExternalDownloadManager {
private void show_preferences () {
CommandLinePreferences dialog = new CommandLinePreferences (this);
dialog.show ();
}
private string replace_quoted (string context, string replace, string? with) {
return context.replace(replace, with != null ? GLib.Shell.quote(with) : "\'\'");
}
public override bool download (DownloadRequest dlReq) {
try {
string cmd = this.get_string ("commandline");
cmd = replace_quoted(cmd, "{REFERER}", dlReq.referer);
cmd = replace_quoted(cmd, "{COOKIES}", dlReq.cookie_header != null ? "Cookie: " + dlReq.cookie_header : null);
cmd = cmd.replace("{URL}", GLib.Shell.quote (dlReq.uri));
GLib.Process.spawn_command_line_async (cmd);
return true;
} catch (Error e) {
this.handle_exception (e);
}
return false;
}
static string description_with_command (string commandline) {
string command;
try {
string[] argvp;
Shell.parse_argv (commandline, out argvp);
command = argvp[0];
}
catch (Error error) {
command = commandline.split (" ")[0];
}
return _("Download files with \"%s\" or a custom command").printf (command);
}
internal void update_description (Midori.App app) {
this.description = description_with_command (get_string ("commandline"));
}
internal CommandLine () {
#if HAVE_WIN32
string default_commandline = "\"%s\\FlashGet\\flashget.exe\" {URL}".printf (Environment.get_variable ("ProgramFiles"));
#elif HAVE_FREEBSD || HAVE_DRAGONFLY
string default_commandline = "fetch HTTP_REFERER={REFERER} {URL}";
#else
string default_commandline = "wget --no-check-certificate --referer={REFERER} --header={COOKIES} {URL}";
#endif
GLib.Object (name: _("External Download Manager - CommandLine"),
description: description_with_command (default_commandline),
version: "0.1" + Midori.VERSION_SUFFIX,
authors: "André Stösel <andre@stoesel.de>",
key: "commandline");
this.install_string ("commandline", default_commandline);
this.activate.connect (activated);
this.activate.connect (update_description);
this.deactivate.connect (deactivated);
this.open_preferences.connect (show_preferences);
}
}
}
public Katze.Array extension_init () {
EDM.manager = new EDM.Manager();
var extensions = new Katze.Array( typeof (Midori.Extension));
#if !HAVE_WIN32
extensions.add_item (new EDM.Aria2 ());
extensions.add_item (new EDM.SteadyFlow ());
#endif
extensions.add_item (new EDM.CommandLine ());
return extensions;
}