/* Copyright (C) 2012 André Stösel 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-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 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 ", 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 ", 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 ", 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; }