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