From 98558af438f09ec687f407593eebcd0efbc67383 Mon Sep 17 00:00:00 2001
From: Akira Ohgaki <akiraohgaki@gmail.com>
Date: Fri, 1 Jul 2016 01:11:06 +0900
Subject: [PATCH] Initial import

---
 README.md          | 144 ++++++++++++++++++-
 install.sh         |  11 ++
 src/xdgurl.desktop |   8 ++
 src/xdgurl.py      | 342 +++++++++++++++++++++++++++++++++++++++++++++
 uninstall.sh       |   7 +
 5 files changed, 511 insertions(+), 1 deletion(-)
 create mode 100644 install.sh
 create mode 100644 src/xdgurl.desktop
 create mode 100644 src/xdgurl.py
 create mode 100644 uninstall.sh

diff --git a/README.md b/README.md
index 75964d2..34e2581 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,143 @@
-# xdgurl
\ No newline at end of file
+# xdgurl
+
+An install helper program for desktop stuff.
+
+----
+
+## How to install
+
+For Ubuntu/Debian system,
+you can use "install.sh" in the project to install the program.
+
+    $ sh install.sh
+
+And you can use "uninstall.sh" to uninstall the program.
+
+    $ sh uninstall.sh
+
+----
+
+## How to use
+
+#### From web browser
+
+Open XDG-URL by click a links, or type XDG-URL in browser's address bar.
+
+In firefox,
+you can get a program selection window when first time to open XDG-URL,
+and you can choose "xdgurl" as custom URL handler.
+
+If you can not get program selection window,
+you need set the xdgurl program as custom URL handler for xdg:// scheme in browser's settings.
+
+#### From command-line terminal
+
+Execute xdgurl program with XDG-URL as argument.
+
+    $ xdgurl "XDG-URL"
+
+----
+
+## XDG-URL
+
+XDG-URL is a custom URL that represent the install method for desktop stuff.
+
+    [scheme]://[command]?[query string]
+
+    For example:
+    xdg://install?url=http%3A%2F%2Fexample.com%2Ficons.tar.gz&type=icons
+
+#### Scheme
+
+* xdg
+* xdgs
+
+For now, "xdgs" scheme is the same of the xdg scheme,
+it's reserved name for secure method in the future.
+
+#### Command
+
+* download
+* install
+
+Command "download" will download the file from specified URL to local destination of specified install-type.
+
+Command "install" will make file download and trigger install process.
+
+#### Query string
+
+XDG-URL's query string is an option of command.
+
+*All query string values should be urlencoded.*
+
+**url** = File URL of desktop stuff
+
+Must be set.
+
+**type** = Install-type
+
+Optional.
+Default is "downloads".
+
+Available install-type:
+
+Install-type | Local destination
+-------------|------------------
+downloads | ~/Downloads
+documents | ~/Documents
+pictures | ~/Pictures
+music | ~/Music
+videos | ~/Videos
+wallpapers | ~/.local/share/wallpapers
+fonts | ~/.fonts
+cursors | ~/.icons
+icons | ~/.local/share/icons
+emoticons | ~/.local/share/emoticons
+themes | ~/.themes
+emerald_themes | ~/.emerald/themes
+enlightenment_themes | ~/.e/e/themes
+enlightenment_backgrounds | ~/.e/e/backgrounds
+fluxbox_styles | ~/.fluxbox/styles
+pekwm_themes | ~/.pekwm/themes
+icewm_themes | ~/.icewm/themes
+plasma_plasmoids | ~/.local/share/plasma/plasmoids
+plasma_look_and_feel | ~/.local/share/plasma/look-and-feel
+plasma_desktopthemes | ~/.local/share/plasma/desktoptheme
+kwin_effects | ~/.local/share/kwin/effects
+kwin_scripts | ~/.local/share/kwin/scripts
+kwin_tabbox | ~/.local/share/kwin/tabbox
+aurorae_themes | ~/.local/share/aurorae/themes
+dekorator_themes | ~/.local/share/deKorator/themes
+qtcurve | ~/.local/share/QtCurve
+color_schemes | ~/.local/share/color-schemes
+gnome_shell_extensions | ~/.local/share/gnome-shell/extensions
+cinnamon_desklets | ~/.local/share/cinnamon/desklets
+nautilus_scripts | ~/.local/share/nautilus/scripts
+amarok_scripts | ~/.kde/share/apps/amarok/scripts
+yakuake_skins | ~/.kde/share/apps/yakuake/skins
+cairo_clock_themes | ~/.cairo-clock/themes
+
+Available alias name of the install-type:
+
+Alias | Install-type
+------|-------------
+gnome_shell_themes | themes
+cinnamon_themes | themes
+gtk2_themes | themes
+gtk3_themes | themes
+metacity_themes | themes
+xfwm4_themes | themes
+openbox_themes | themes
+kvantum_themes | themes
+compiz_themes | emerald_themes
+beryl_themes | emerald_themes
+plasma4_plasmoids | plasma_plasmoids
+plasma5_plasmoids | plasma_plasmoids
+plasma5_look_and_feel | plasma_look_and_feel
+plasma5_desktopthemes | plasma_desktopthemes
+plasma_color_schemes | color_schemes
+
+**filename** = Alternative file name
+
+Optional.
+This option is useful if the download URL does not represent the file name.
diff --git a/install.sh b/install.sh
new file mode 100644
index 0000000..1c49263
--- /dev/null
+++ b/install.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+# Install
+
+cd `dirname $0`
+
+sudo apt-get update
+sudo apt-get install python-tk
+sudo install -m 755 src/xdgurl.py /usr/bin/xdgurl
+sudo install -m 644 src/xdgurl.desktop /usr/share/applications/xdgurl.desktop
+sudo update-desktop-database
diff --git a/src/xdgurl.desktop b/src/xdgurl.desktop
new file mode 100644
index 0000000..ead1ee8
--- /dev/null
+++ b/src/xdgurl.desktop
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Name=xdgurl
+Exec=xdgurl %u
+Type=Application
+Terminal=false
+NoDisplay=true
+Categories=Application;Network;FileTransfer;
+MimeType=x-scheme-handler/xdg;x-scheme-handler/xdgs;
diff --git a/src/xdgurl.py b/src/xdgurl.py
new file mode 100644
index 0000000..d40b1a1
--- /dev/null
+++ b/src/xdgurl.py
@@ -0,0 +1,342 @@
+#!/usr/bin/env python
+
+import sys
+import os
+import json
+import urllib
+import urlparse
+import tempfile
+import mimetypes
+import subprocess
+import Tkinter
+import tkMessageBox
+
+class XdgUrl:
+    """Core class of xdgurl"""
+
+    def __init__(self, xdg_url=''):
+        self.xdg_url = xdg_url
+        self.meta = self.parse()
+
+        self.temp_dir = tempfile.gettempdir()
+        self.home_dir = os.path.expanduser('~')
+        #self.config_dir = os.path.join(self.home_dir, '.config', 'xdgurl')
+        self.data_dir = os.path.join(self.home_dir, '.local', 'share')
+        self.kde_data_dir = os.path.join(self.home_dir, '.kde', 'share')
+
+        #self.config = {}
+
+        self.destinations = {
+            'downloads': os.path.join(self.home_dir, 'Downloads'),
+            'documents': os.path.join(self.home_dir, 'Documents'),
+            'pictures': os.path.join(self.home_dir, 'Pictures'),
+            'music': os.path.join(self.home_dir, 'Music'),
+            'videos': os.path.join(self.home_dir, 'Videos'),
+            'wallpapers': os.path.join(self.data_dir, 'wallpapers'),
+            'fonts': os.path.join(self.home_dir, '.fonts'),
+            'cursors': os.path.join(self.home_dir, '.icons'),
+            'icons': os.path.join(self.data_dir, 'icons'),
+            'emoticons': os.path.join(self.data_dir, 'emoticons'),
+            'themes': os.path.join(self.home_dir, '.themes'),
+            'emerald_themes': os.path.join(self.home_dir, '.emerald', 'themes'),
+            'enlightenment_themes': os.path.join(self.home_dir, '.e', 'e', 'themes'),
+            'enlightenment_backgrounds': os.path.join(self.home_dir, '.e', 'e', 'backgrounds'),
+            'fluxbox_styles': os.path.join(self.home_dir, '.fluxbox', 'styles'),
+            'pekwm_themes': os.path.join(self.home_dir, '.pekwm', 'themes'),
+            'icewm_themes': os.path.join(self.home_dir, '.icewm', 'themes'),
+            'plasma_plasmoids': os.path.join(self.data_dir, 'plasma', 'plasmoids'),
+            'plasma_look_and_feel': os.path.join(self.data_dir, 'plasma', 'look-and-feel'),
+            'plasma_desktopthemes': os.path.join(self.data_dir, 'plasma', 'desktoptheme'),
+            'kwin_effects': os.path.join(self.data_dir, 'kwin', 'effects'),
+            'kwin_scripts': os.path.join(self.data_dir, 'kwin', 'scripts'),
+            'kwin_tabbox': os.path.join(self.data_dir, 'kwin', 'tabbox'),
+            'aurorae_themes': os.path.join(self.data_dir, 'aurorae', 'themes'),
+            'dekorator_themes': os.path.join(self.data_dir, 'deKorator', 'themes'),
+            'qtcurve': os.path.join(self.data_dir, 'QtCurve'),
+            'color_schemes': os.path.join(self.data_dir, 'color-schemes'),
+            'gnome_shell_extensions': os.path.join(self.data_dir, 'gnome-shell', 'extensions'),
+            'cinnamon_desklets': os.path.join(self.data_dir, 'cinnamon', 'desklets'),
+            'nautilus_scripts': os.path.join(self.data_dir, 'nautilus', 'scripts'),
+            'amarok_scripts': os.path.join(self.kde_data_dir, 'apps', 'amarok', 'scripts'),
+            'yakuake_skins': os.path.join(self.kde_data_dir, 'apps', 'yakuake', 'skins'),
+            'cairo_clock_themes': os.path.join(self.home_dir, '.cairo-clock', 'themes')
+        }
+        self.destinations_alias = {
+            'gnome_shell_themes': self.destinations['themes'],
+            'cinnamon_themes': self.destinations['themes'],
+            'gtk2_themes': self.destinations['themes'],
+            'gtk3_themes': self.destinations['themes'],
+            'metacity_themes': self.destinations['themes'],
+            'xfwm4_themes': self.destinations['themes'],
+            'openbox_themes': self.destinations['themes'],
+            'kvantum_themes': self.destinations['themes'],
+            'compiz_themes': self.destinations['emerald_themes'],
+            'beryl_themes': self.destinations['emerald_themes'],
+            'plasma4_plasmoids': self.destinations['plasma_plasmoids'],
+            'plasma5_plasmoids': self.destinations['plasma_plasmoids'],
+            'plasma5_look_and_feel': self.destinations['plasma_look_and_feel'],
+            'plasma5_desktopthemes': self.destinations['plasma_desktopthemes'],
+            'plasma_color_schemes': self.destinations['color_schemes']
+        }
+        self.destinations.update(self.destinations_alias)
+
+        self.archive_types = {
+            'tar': [
+                'application/x-tar',
+                'application/x-gzip',
+                'application/gzip',
+                'application/x-bzip',
+                'application/x-bzip2',
+                'application/x-xz',
+                'application/x-lzma',
+                'application/x-lzip',
+                'application/x-compressed-tar',
+                'application/x-bzip-compressed-tar',
+                'application/x-bzip2-compressed-tar',
+                'application/x-xz-compressed-tar',
+                'application/x-lzma-compressed-tar',
+                'application/x-lzip-compressed-tar'
+            ],
+            'zip': ['application/zip'],
+            '7z': ['application/x-7z-compressed'],
+            'rar': [
+                'application/x-rar',
+                'application/x-rar-compressed'
+            ]
+        }
+
+        #self.config.update(self.read_config('config'))
+        #self.destinations.update(self.read_config('destinations'))
+
+    def read_config(self, name):
+        path = os.path.join(self.config_dir, name + '.json')
+        data = {}
+        if os.path.isfile(path):
+            f = open(path)
+            data = json.load(f)
+            f.close()
+        return data
+
+    def write_config(self, name, data):
+        if not os.path.isdir(self.config_dir):
+            os.makedirs(self.config_dir)
+        path = os.path.join(self.config_dir, name + '.json')
+        f = open(path, 'w')
+        json.dump(data, f)
+        f.close()
+
+    def parse(self):
+        meta = {
+            'command': 'download',
+            'url': '',
+            'type': 'downloads',
+            'filename': ''
+        }
+
+        parse_result = urlparse.urlparse(self.xdg_url)
+        query = urlparse.parse_qs(parse_result.query)
+
+        if parse_result.netloc:
+            meta['command'] = parse_result.netloc
+
+        if 'url' in query and query['url'][0]:
+            meta['url'] = urllib.unquote(query['url'][0])
+
+        if 'type' in query and query['type'][0]:
+            meta['type'] = urllib.unquote(query['type'][0])
+
+        if 'filename' in query and query['filename'][0]:
+            meta['filename'] = urllib.unquote(query['filename'][0])
+
+        if meta['url'] and not meta['filename']:
+            meta['filename'] = os.path.basename(meta['url'])
+
+        return meta
+
+    """
+    def detect_desktop_environment(self):
+        desktop_environment = 'unknown'
+        if os.environ.get('KDE_FULL_SESSION') == 'true':
+            desktop_environment = 'kde'
+        elif os.environ.get('GNOME_DESKTOP_SESSION_ID'):
+            desktop_environment = 'gnome'
+        return desktop_environment
+    """
+
+    def install_plasmapkg(self, path, type='plasmoid'):
+        status = subprocess.call(['plasmapkg2', '-t', type, '-i', path])
+        if status == 0:
+            return True
+        return False
+
+    def uncompress_archive(self, path, target_dir):
+        (mimetype, encoding) = mimetypes.guess_type(path)
+        status = None
+
+        if mimetype in self.archive_types['tar']:
+            status = subprocess.call(['tar', '-xf', path, '-C', target_dir])
+        elif mimetype in self.archive_types['zip']:
+            status = subprocess.call(['unzip', '-o', path, '-d', target_dir])
+        elif mimetype in self.archive_types['7z']:
+            # No space between -o and directory
+            status = subprocess.call(['7z', 'x', path, '-o' + target_dir])
+        elif mimetype in self.archive_types['rar']:
+            status = subprocess.call(['unrar', 'e', path, target_dir])
+
+        if status == 0:
+            return True
+        return False
+
+    def download(self):
+        url = self.meta['url']
+        type = self.meta['type']
+        filename = self.meta['filename']
+        destination = self.destinations[type]
+
+        temp_path = os.path.join(self.temp_dir, filename)
+        path = os.path.join(destination, filename)
+
+        print('Retrieving a file from ' + url)
+        urllib.urlretrieve(url, temp_path)
+
+        print('Creating a directory ' + destination)
+        if not os.path.isdir(destination):
+            os.makedirs(destination)
+
+        print('Saving a file to ' + path)
+        os.rename(temp_path, path)
+
+        print('Done')
+
+    def install(self):
+        url = self.meta['url']
+        type = self.meta['type']
+        filename = self.meta['filename']
+        destination = self.destinations[type]
+
+        temp_path = os.path.join(self.temp_dir, filename)
+        path = os.path.join(destination, filename)
+
+        print('Retrieving a file from ' + url)
+        urllib.urlretrieve(url, temp_path)
+
+        print('Creating a directory ' + destination)
+        if not os.path.isdir(destination):
+            os.makedirs(destination)
+
+        print('Installing')
+        if (type in ['plasma_plasmoids', 'plasma4_plasmoids', 'plasma5_plasmoids']
+        and self.install_plasmapkg(temp_path, 'plasmoid')):
+            print('The plasmoid has been installed')
+            os.remove(temp_path)
+        elif (type in ['plasma_look_and_feel', 'plasma5_look_and_feel']
+        and self.install_plasmapkg(temp_path, 'lookandfeel')):
+            print('The plasma look and feel has been installed')
+            os.remove(temp_path)
+        elif (type in ['plasma_desktopthemes', 'plasma5_desktopthemes']
+        and self.install_plasmapkg(temp_path, 'theme')):
+            print('The plasma desktop theme has been installed')
+            os.remove(temp_path)
+        elif (type == 'kwin_effects'
+        and self.install_plasmapkg(temp_path, 'kwineffect')):
+            print('The KWin effect has been installed')
+            os.remove(temp_path)
+        elif (type == 'kwin_scripts'
+        and self.install_plasmapkg(temp_path, 'kwinscript')):
+            print('The KWin script has been installed')
+            os.remove(temp_path)
+        elif (type == 'kwin_tabbox'
+        and self.install_plasmapkg(temp_path, 'windowswitcher')):
+            print('The KWin window switcher has been installed')
+            os.remove(temp_path)
+        elif self.uncompress_archive(temp_path, destination):
+            print('The archive file has been uncompressed into ' + destination)
+            os.remove(temp_path)
+        else:
+            print('Saving a file to ' + path)
+            os.rename(temp_path, path)
+
+        print('Done')
+
+    def execute(self):
+        try:
+            if (self.meta['command'] in ['download', 'install']
+            and self.meta['url']
+            and self.meta['type'] in self.destinations
+            and self.meta['filename']):
+                if self.meta['command'] == 'download':
+                    self.download()
+                elif self.meta['command'] == 'install':
+                    self.install()
+                return True
+            print('Incorrect XDG-URL ' + self.xdg_url)
+            return False
+        except:
+            print('Execution error')
+            return False
+
+'''
+class XdgUrlApp(Tkinter.Frame):
+    """Confirmation dialog for xdgurl"""
+
+    def __init__(self, master=None, core=None):
+        Tkinter.Frame.__init__(self, master)
+        self.pack()
+        self.core = core
+        self.master.title('xdgurl')
+        self.master.geometry('300x120')
+        self.create_widget()
+
+    def create_widget(self):
+        execute_text = 'Download'
+        if self.core.meta['command'] == 'install':
+            execute_text = 'Install'
+
+        info_label = Tkinter.Label(self, text=execute_text + ': ' + self.core.meta['filename'])
+        info_label.pack(padx=10, pady=5, anchor=Tkinter.W)
+
+        message_label = Tkinter.Label(self, text='Do you want to continue?')
+        message_label.pack(padx=10, pady=5, anchor=Tkinter.W)
+
+        execute_button = Tkinter.Button(self, text=execute_text, command=self.execute)
+        execute_button.pack(padx=5, pady=10, side=Tkinter.RIGHT)
+
+        quit_button = Tkinter.Button(self, text='Cancel', command=self.quit)
+        quit_button.pack(padx=5, pady=10, side=Tkinter.RIGHT)
+
+    def execute(self):
+        self.core.execute()
+        sys.exit()
+
+    def quit(self):
+        sys.exit()
+'''
+
+if __name__ == '__main__':
+    if len(sys.argv) > 1:
+        """
+        window = Tkinter.Tk()
+        core = XdgUrl(sys.argv[1])
+        app = XdgUrlApp(window, core)
+        app.mainloop()
+        """
+
+        core = XdgUrl(sys.argv[1])
+
+        execute_text = 'Download'
+        if core.meta['command'] == 'install':
+            execute_text = 'Install'
+        info_text = execute_text + ': ' + core.meta['filename'] + '\nFrom: ' + core.meta['url']
+
+        window = Tkinter.Tk()
+        window.withdraw()
+
+        if tkMessageBox.askyesno('xdgurl', info_text + '\n\nDo you want to continue?'):
+            if core.execute():
+                tkMessageBox.showinfo('xdgurl', info_text + '\n\n' + execute_text + ' finished')
+            else:
+                tkMessageBox.showerror('xdgurl', info_text + '\n\n' + execute_text + ' failed')
+        sys.exit()
+    else:
+        print('xdgurl "XDG-URL"')
+        sys.exit()
diff --git a/uninstall.sh b/uninstall.sh
new file mode 100644
index 0000000..2bf0de5
--- /dev/null
+++ b/uninstall.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+# Uninstall
+
+sudo rm /usr/bin/xdgurl
+sudo rm /usr/share/applications/xdgurl.desktop
+sudo update-desktop-database
-- 
GitLab