commit 2d8cc2ebf931a1e6b6f1766e32f1741f7a73b670
Author: Leigh Scott <leigh123linux(a)gmail.com>
Date: Sun Nov 17 20:38:40 2019 +0000
Update to 4.4.0
nemo-dropbox.spec | 13 +-
remove_python.patch | 2797 +++++++++++++++++++++++++++++++++++++++++++++++++++
sources | 2 +-
use_python2.patch | 38 -
4 files changed, 2805 insertions(+), 45 deletions(-)
---
diff --git a/nemo-dropbox.spec b/nemo-dropbox.spec
index a79f620..8696cdf 100644
--- a/nemo-dropbox.spec
+++ b/nemo-dropbox.spec
@@ -1,20 +1,18 @@
Summary: Dropbox extension for nemo
Name: nemo-dropbox
-Version: 4.2.0
+Version: 4.4.0
Release: 1%{?dist}
License: GPLv2+ and LGPLv2+ and MIT
URL:
https://github.com/linuxmint/nemo-extensions
-Source0: %url/archive/%{version}.tar.gz#/nemo-extensions-%{version}.tar.gz
-Patch0: use_python2.patch
+Source0: %url/archive/%{version}/nemo-extensions-%{version}.tar.gz
+Patch0: %url/pull/341.patch#/remove_python.patch
ExclusiveArch: i686 x86_64
BuildRequires: nemo-devel
-BuildRequires: python2-docutils
BuildRequires: automake
BuildRequires: libtool
-BuildRequires: pygobject2-devel
-BuildRequires: pygtk2-devel
+
Requires: dropbox >= 1:2.10.0
@@ -53,6 +51,9 @@ rm -rf %{buildroot}%{_datadir}
%changelog
+* Sun Nov 17 2019 Leigh Scott <leigh123linux(a)gmail.com> - 4.4.0-1
+- Update to 4.4.0
+
* Sat Aug 10 2019 Leigh Scott <leigh123linux(a)gmail.com> - 4.2.0-1
- Update to 4.2.0
diff --git a/remove_python.patch b/remove_python.patch
new file mode 100644
index 0000000..4c7d1d1
--- /dev/null
+++ b/remove_python.patch
@@ -0,0 +1,2797 @@
+From 415b594e18d52a1c19abb2a030ee773d632e6628 Mon Sep 17 00:00:00 2001
+From: Michael Webster <miketwebster(a)gmail.com>
+Date: Sun, 17 Nov 2019 15:11:42 -0500
+Subject: [PATCH] nemo-dropbox: Get rid of stuff that we haven't been using,
+ because the dropbox package provides it all now.
+
+---
+ nemo-dropbox/Makefile.am | 15 -
+ nemo-dropbox/configure.ac | 30 -
+ nemo-dropbox/data/Makefile.am | 5 -
+ nemo-dropbox/data/dropbox.desktop | 11 -
+ nemo-dropbox/debian/control | 7 -
+ nemo-dropbox/debian/rules | 5 +-
+ nemo-dropbox/docgen.py | 25 -
+ nemo-dropbox/dropbox.in | 1406 -----------------------------
+ nemo-dropbox/dropbox.txt.in | 51 --
+ nemo-dropbox/rst2man.py | 1091 ----------------------
+ nemo-dropbox/serializeimages.py | 27 -
+ 11 files changed, 1 insertion(+), 2672 deletions(-)
+ delete mode 100644 nemo-dropbox/data/dropbox.desktop
+ delete mode 100644 nemo-dropbox/docgen.py
+ delete mode 100755 nemo-dropbox/dropbox.in
+ delete mode 100644 nemo-dropbox/dropbox.txt.in
+ delete mode 100644 nemo-dropbox/rst2man.py
+ delete mode 100644 nemo-dropbox/serializeimages.py
+
+diff --git a/nemo-dropbox/Makefile.am b/nemo-dropbox/Makefile.am
+index 41498471..3da2d71a 100644
+--- a/nemo-dropbox/Makefile.am
++++ b/nemo-dropbox/Makefile.am
+@@ -1,16 +1 @@
+-AUTOMAKE_OPTIONS = dist-bzip2 no-dist-gzip
+-
+-bin_SCRIPTS = dropbox
+-CLEANFILES = $(bin_SCRIPTS) dropbox.1 dropbox.txt
+-EXTRA_DIST = dropbox.in serializeimages.py dropbox.txt.in docgen.py rst2man.py
+-man_MANS = dropbox.1
+-
+-dropbox: dropbox.in serializeimages.py
+- python serializeimages.py $(PACKAGE_VERSION) $(datadir)/applications < dropbox.in
> dropbox
+- chmod +x dropbox
+-
+-dropbox.1: dropbox dropbox.txt.in docgen.py
+- python docgen.py $(PACKAGE_VERSION) < dropbox.txt.in > dropbox.txt
+- $(RST2MAN) dropbox.txt > dropbox.1
+-
+ SUBDIRS = data src
+diff --git a/nemo-dropbox/configure.ac b/nemo-dropbox/configure.ac
+index 7275f08e..24a6fcd6 100644
+--- a/nemo-dropbox/configure.ac
++++ b/nemo-dropbox/configure.ac
+@@ -29,36 +29,6 @@ fi
+ PKG_CHECK_MODULES(NEMO, libnemo-extension >= $NEMO_REQUIRED)
+ PKG_CHECK_MODULES(GLIB, glib-2.0 >= $GLIB_REQUIRED)
+
+-AC_PATH_PROG([PYTHON], [python])
+-
+-AC_PATH_PROG([RST2MAN], [rst2man], [python rst2man.py])
+-AC_SUBST(RST2MAN)
+-
+-# define module checking macro
+-AC_DEFUN([PYTHON_CHECK_MODULE], [
+-AC_MSG_CHECKING([for $1])
+-
+-cat <<EOF | python
+-try:
+- import $2
+-except:
+- exit(1)
+-else:
+- exit(0)
+-EOF
+-
+-if test $? -ne 0; then
+- AC_MSG_RESULT([no])
+- AC_MSG_ERROR([couldn't find $1])
+-else
+- AC_MSG_RESULT([yes])
+-fi
+-])
+-
+-PYTHON_CHECK_MODULE(pygtk, gtk)
+-PYTHON_CHECK_MODULE(gobject, gobject)
+-PYTHON_CHECK_MODULE(docutils, docutils)
+-
+ # Make dependency CFLAGS and LIBS available
+ AC_SUBST(NEMO_CFLAGS)
+ AC_SUBST(NEMO_LIBS)
+diff --git a/nemo-dropbox/data/Makefile.am b/nemo-dropbox/data/Makefile.am
+index 32669adb..86747662 100644
+--- a/nemo-dropbox/data/Makefile.am
++++ b/nemo-dropbox/data/Makefile.am
+@@ -1,6 +1 @@
+-applicationdir = $(datadir)/applications
+-application_DATA = dropbox.desktop
+-
+-EXTRA_DIST = $(application_DATA)
+-
+ SUBDIRS = icons emblems
+diff --git a/nemo-dropbox/data/dropbox.desktop b/nemo-dropbox/data/dropbox.desktop
+deleted file mode 100644
+index c2b6579b..00000000
+--- a/nemo-dropbox/data/dropbox.desktop
++++ /dev/null
+@@ -1,11 +0,0 @@
+-[Desktop Entry]
+-Name=Dropbox
+-GenericName=File Synchronizer
+-Comment=Sync your files across computers and to the web
+-Exec=dropbox start -i
+-Terminal=false
+-Type=Application
+-Icon=dropbox
+-Categories=Network;FileTransfer;
+-StartupNotify=false
+-X-GNOME-Autostart-Delay=10
+diff --git a/nemo-dropbox/debian/control b/nemo-dropbox/debian/control
+index f6904aa0..7a302c00 100644
+--- a/nemo-dropbox/debian/control
++++ b/nemo-dropbox/debian/control
+@@ -5,11 +5,7 @@ Maintainer: Clement Lefebvre <root(a)linuxmint.com>
+ Build-Depends: debhelper (>= 9),
+ libnemo-extension-dev (>= 1.0.0),
+ libglib2.0-dev (>= 2.14.0),
+- python (>= 2.7),
+- python-docutils (>= 0.6),
+- python-gtk2 (>= 2.12),
+ dh-autoreconf,
+- dh-python
+ Standards-Version: 3.9.6
+ XS-Autobuild: yes
+ Homepage:
http://www.dropbox.com/
+@@ -19,12 +15,9 @@ Architecture: i386 amd64
+ Depends: nemo,
+ policykit-1,
+ procps,
+- python-gtk2 (>= 2.12),
+ ${shlibs:Depends},
+- ${python:Depends},
+ ${misc:Depends},
+ dropbox
+-Suggests: python-gpgme (>= 0.1)
+ Description: Dropbox integration for Nemo
+ Nemo Dropbox is an extension that integrates the Dropbox web service with
+ your Cinnamon Desktop.
+diff --git a/nemo-dropbox/debian/rules b/nemo-dropbox/debian/rules
+index e582da0e..eabb0100 100755
+--- a/nemo-dropbox/debian/rules
++++ b/nemo-dropbox/debian/rules
+@@ -1,7 +1,7 @@
+ #!/usr/bin/make -f
+
+ %:
+- dh $@ --with=python2,autoreconf
++ dh $@ --with=autoreconf
+
+ override_dh_autoreconf:
+ dh_autoreconf --as-needed
+@@ -15,8 +15,5 @@ override_dh_auto_install:
+ # Drop useless files
+ rm -f debian/nemo-dropbox/usr/lib/nemo/extensions-*/libnemo-dropbox.a
+ rm -f debian/nemo-dropbox/usr/lib/nemo/extensions-*/libnemo-dropbox.la
+- rm -f debian/nemo-dropbox/usr/bin/dropbox
+- rm -f debian/nemo-dropbox/usr/share/applications/dropbox.desktop
+ rm -rf debian/nemo-dropbox/usr/share/icons/*
+- rm -rf debian/nemo-dropbox/usr/share/man/*
+
+diff --git a/nemo-dropbox/docgen.py b/nemo-dropbox/docgen.py
+deleted file mode 100644
+index 5a8eb7a2..00000000
+--- a/nemo-dropbox/docgen.py
++++ /dev/null
+@@ -1,25 +0,0 @@
+-import sys
+-import datetime
+-
+-# heeeheee
+-env = {"__name__":"__notmain__"}
+-execfile("dropbox", env)
+-commands = env["commands"]
+-
+-f = open("AUTHORS", "r")
+-authors = '| ' + f.read().replace('\n', '\n| ')
+-f.close()
+-
+-formatted_commands = ""
+-for cmd in commands:
+- split = commands[cmd].__doc__.split('\n', 2)
+- formatted_commands += split[1].decode('ascii').replace(cmd, "`%s`"
% cmd).replace("dropbox", "``dropbox``")
+- formatted_commands += split[2].decode('ascii').replace('\n', '\n
| ')
+- formatted_commands += '\n\n'
+-
+-sys.stdout.write(sys.stdin.read().replace\
+- ('@AUTHORS@', authors).replace\
+- ('@DATE@', datetime.date.today().isoformat()).replace\
+- ('@PACKAGE_VERSION@', sys.argv[1]).replace\
+- ('@SYNOPSIS@', '| '+'\n|
'.join(commands[cmd].__doc__.split('\n',
2)[1].decode('ascii').replace(cmd, "`%s`" %
cmd).replace("dropbox", "``dropbox``") for cmd in commands)).replace\
+- ('@COMMANDS@', formatted_commands))
+diff --git a/nemo-dropbox/dropbox.in b/nemo-dropbox/dropbox.in
+deleted file mode 100755
+index 585be640..00000000
+--- a/nemo-dropbox/dropbox.in
++++ /dev/null
+@@ -1,1406 +0,0 @@
+-#!/usr/bin/python2
+-#
+-# Copyright 2008 Evenflow, Inc.
+-#
+-# dropbox
+-# Dropbox frontend script
+-# This file is part of nemo-dropbox @PACKAGE_VERSION@.
+-#
+-# nemo-dropbox is free software: you can redistribute it and/or modify
+-# it under the terms of the GNU General Public License as published by
+-# the Free Software Foundation, either version 3 of the License, or
+-# (at your option) any later version.
+-#
+-# nemo-dropbox is distributed in the hope that it will be useful,
+-# but WITHOUT ANY WARRANTY; without even the implied warranty of
+-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-# GNU General Public License for more details.
+-#
+-# You should have received a copy of the GNU General Public License
+-# along with nemo-dropbox. If not, see <
http://www.gnu.org/licenses/>.
+-#
+-from __future__ import with_statement
+-
+-import errno
+-import fcntl
+-import locale
+-import optparse
+-import os
+-import platform
+-import shutil
+-import socket
+-import StringIO
+-import subprocess
+-import sys
+-import tarfile
+-import tempfile
+-import threading
+-import time
+-import urllib
+-
+-try:
+- import gpgme
+-except ImportError:
+- gpgme = None
+-
+-from contextlib import closing, contextmanager
+-from posixpath import curdir, sep, pardir, join, abspath, commonprefix
+-
+-INFO = u"Dropbox is the easiest way to share and store your files online. Want to
learn more? Head to"
+-LINK =
u"http://www.dropbox.com/"
+-WARNING = u"In order to use Dropbox, you must download the proprietary
daemon."
+-GPG_WARNING = u"Note: python-gpgme is not installed, we will not be able to verify
binary signatures."
+-
+-DOWNLOADING = u"Downloading Dropbox... %d%%"
+-UNPACKING = u"Unpacking Dropbox... %d%%"
+-
+-PARENT_DIR = os.path.expanduser("/var/lib/dropbox")
+-DROPBOXD_PATH = "%s/.dropbox-dist/dropboxd" % PARENT_DIR
+-DESKTOP_FILE = u"@DESKTOP_FILE_DIR(a)/dropbox.desktop"
+-
+-enc = locale.getpreferredencoding()
+-
+-# Available from
http://linux.dropbox.com/fedora/rpm-public-key.asc
+-DROPBOX_PUBLIC_KEY = """
+------BEGIN PGP PUBLIC KEY BLOCK-----
+-Version: SKS 1.1.0
+-
+-mQENBEt0ibEBCACv4hZRPqwtpU6z8+BB5YZU1a3yjEvg2W68+a6hEwxtCa2U++4dzQ+7EqaU
+-q5ybQnwtbDdpFpsOi9x31J+PCpufPUfIG694/0rlEpmzl2GWzY8NqfdBFGGm/SPSSwvKbeNc
+-FMRLu5neo7W9kwvfMbGjHmvUbzBUVpCVKD0OEEf1q/Ii0Qcekx9CMoLvWq7ZwNHEbNnij7ec
+-nvwNlE2MxNsOSJj+hwZGK+tM19kuYGSKw4b5mR8IyThlgiSLIfpSBh1n2KX+TDdk9GR+57TY
+-vlRu6nTPu98P05IlrrCP+KF0hYZYOaMvQs9Rmc09tc/eoQlN0kkaBWw9Rv/dvLVc0aUXABEB
+-AAG0MURyb3Bib3ggQXV0b21hdGljIFNpZ25pbmcgS2V5IDxsaW51eEBkcm9wYm94LmNvbT6J
+-ATYEEwECACAFAkt0ibECGwMGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAKCRD8kYszUESRLi/z
+-B/wMscEa15rS+0mIpsORknD7kawKwyda+LHdtZc0hD/73QGFINR2P23UTol/R4nyAFEuYNsF
+-0C4IAD6y4pL49eZ72IktPrr4H27Q9eXhNZfJhD7BvQMBx75L0F5gSQwuC7GdYNlwSlCD0AAh
+-Qbi70VBwzeIgITBkMQcJIhLvllYo/AKD7Gv9huy4RLaIoSeofp+2Q0zUHNPl/7zymOqu+5Ox
+-e1ltuJT/kd/8hU+N5WNxJTSaOK0sF1/wWFM6rWd6XQUP03VyNosAevX5tBo++iD1WY2/lFVU
+-JkvAvge2WFk3c6tAwZT/tKxspFy4M/tNbDKeyvr685XKJw9ei6GcOGHD
+-=5rWG
+------END PGP PUBLIC KEY BLOCK-----
+-"""
+-
+-# Futures
+-
+-def methodcaller(name, *args, **kwargs):
+- def caller(obj):
+- return getattr(obj, name)(*args, **kwargs)
+- return caller
+-
+-def relpath(path, start=curdir):
+- """Return a relative version of a path"""
+-
+- if not path:
+- raise ValueError("no path specified")
+-
+- if type(start) is unicode:
+- start_list = unicode_abspath(start).split(sep)
+- else:
+- start_list = abspath(start).split(sep)
+-
+- if type(path) is unicode:
+- path_list = unicode_abspath(path).split(sep)
+- else:
+- path_list = abspath(path).split(sep)
+-
+- # Work out how much of the filepath is shared by start and path.
+- i = len(commonprefix([start_list, path_list]))
+-
+- rel_list = [pardir] * (len(start_list)-i) + path_list[i:]
+- if not rel_list:
+- return curdir
+- return join(*rel_list)
+-
+-# End Futures
+-
+-
+-def console_print(st=u"", f=sys.stdout, linebreak=True):
+- global enc
+- assert type(st) is unicode
+- f.write(st.encode(enc))
+- if linebreak: f.write(os.linesep)
+-
+-def console_flush(f=sys.stdout):
+- f.flush()
+-
+-def yes_no_question(question):
+- while True:
+- console_print(question, linebreak=False)
+- console_print(u" [y/n] ", linebreak=False)
+- console_flush()
+- text = raw_input()
+- if text.lower().startswith("y"):
+- return True
+- elif text.lower().startswith("n"):
+- return False
+- else:
+- console_print(u"Sorry, I didn't understand that. Please type yes or
no.")
+-
+-def plat():
+- if sys.platform.lower().startswith('linux'):
+- arch = platform.machine()
+- if (arch[0] == 'i' and
+- arch[1].isdigit() and
+- arch[2:4] == '86'):
+- plat = "x86"
+- elif arch == 'x86_64':
+- plat = arch
+- else:
+- FatalVisibleError("Platform not supported")
+- return "lnx.%s" % plat
+- else:
+- FatalVisibleError("Platform not supported")
+-
+-def is_dropbox_running():
+- pidfile = os.path.expanduser("~/.dropbox/dropbox.pid")
+-
+- try:
+- with open(pidfile, "r") as f:
+- pid = int(f.read())
+- with open("/proc/%d/cmdline" % pid, "r") as f:
+- cmdline = f.read().lower()
+- except:
+- cmdline = ""
+-
+- return "dropbox" in cmdline
+-
+-def unicode_abspath(path):
+- global enc
+- assert type(path) is unicode
+- # shouldn't pass unicode to this craphead, it appends with os.getcwd() which is
always a str
+- return
os.path.abspath(path.encode(sys.getfilesystemencoding())).decode(sys.getfilesystemencoding())
+-
+-@contextmanager
+-def gpgme_context(keys):
+- gpg_conf_contents = ''
+- _gpghome = tempfile.mkdtemp(prefix='tmp.gpghome')
+-
+- try:
+- os.environ['GNUPGHOME'] = _gpghome
+- fp = open(os.path.join(_gpghome, 'gpg.conf'), 'wb')
+- fp.write(gpg_conf_contents)
+- fp.close()
+- ctx = gpgme.Context()
+-
+- loaded = []
+- for key_file in keys:
+- result = ctx.import_(key_file)
+- key = ctx.get_key(result.imports[0][0])
+- loaded.append(key)
+-
+- ctx.signers = loaded
+-
+- yield ctx
+- finally:
+- del os.environ['GNUPGHOME']
+- shutil.rmtree(_gpghome, ignore_errors=True)
+-
+-def verify_signature(key_file, sig_file, plain_file):
+- with gpgme_context([key_file]) as ctx:
+- sigs = ctx.verify(sig_file, plain_file, None)
+- return sigs[0].status == None
+-
+-def download_file_chunk(socket, buf, size):
+- progress = 0
+- with closing(socket) as f:
+- while True:
+- try:
+- chunk = f.read(4096)
+- progress += len(chunk)
+- buf.write(chunk)
+- yield (progress, True)
+- if progress == size:
+- break
+- except OSError, e:
+- if hasattr(e, 'errno') and e.errno == errno.EAGAIN:
+- # nothing left to read
+- yield (progress, False)
+- else:
+- raise
+-
+-def download_uri_to_buffer(uri):
+- try:
+- socket = urllib.urlopen(uri)
+- except IOError as e:
+- debug_info = get_download_debug_info(uri, e)
+- FatalVisibleError("Trouble connecting to Dropbox servers. Maybe your
internet connection is down, or you need to set your http_proxy environment
variable.\n%s" % debug_info)
+-
+- fcntl.fcntl(socket, fcntl.F_SETFL, os.O_NONBLOCK)
+- size = int(socket.info()['content-length'])
+-
+- buf = StringIO.StringIO()
+- download_chunk = download_file_chunk(socket, buf, size)
+-
+- for _ in download_chunk:
+- pass
+-
+- buf.seek(0)
+- return buf
+-
+-def get_download_debug_info(url, e):
+- msg = "\nURL that failed to download: %s\nError: %s\n" % (url,
e.strerror)
+- if "http_proxy" in os.environ:
+- msg += "http_proxy = %s" % os.environ["http_proxy"]
+- if "https_proxy" in os.environ:
+- msg += "https_proxy = %s" % os.environ["https_proxy"]
+- return msg
+-
+-# This sets a custom User-Agent
+-class DropboxURLopener(urllib.FancyURLopener):
+- version = "DropboxLinuxDownloader/@PACKAGE_VERSION@"
+-urllib._urlopener = DropboxURLopener()
+-
+-class DownloadState(object):
+- def __init__(self):
+- url = "http://www.dropbox.com/download?plat=%s" % plat()
+- try:
+- self.socket = urllib.urlopen(url)
+- except IOError as e:
+- debug_info = get_download_debug_info(url, e)
+- FatalVisibleError("Trouble connecting to Dropbox servers. Maybe your
internet connection is down, or you need to set your http_proxy environment
variable.\n%s" % debug_info)
+-
+- fcntl.fcntl(self.socket, fcntl.F_SETFL, os.O_NONBLOCK)
+- self.size = int(self.socket.info()['content-length'])
+-
+- self.local_file = StringIO.StringIO()
+- self.download_chunk = download_file_chunk(self.socket, self.local_file,
self.size)
+-
+- def copy_data(self):
+- return self.download_chunk
+-
+- def unpack(self):
+- # download signature
+- signature =
download_uri_to_buffer("http://www.dropbox.com/download?plat=%s&...
% plat())
+-
+- self.local_file.seek(0)
+- if gpgme:
+- if not verify_signature(StringIO.StringIO(DROPBOX_PUBLIC_KEY), signature,
self.local_file):
+- FatalVisibleError("Downloaded binary does not match Dropbox
signature, aborting install.")
+-
+- self.local_file.seek(0)
+- archive = tarfile.open(fileobj=self.local_file, mode='r:gz')
+- total_members = len(archive.getmembers())
+- for i, member in enumerate(archive.getmembers()):
+- filename = os.path.join(PARENT_DIR, member.name)
+- if os.path.exists(filename) and not os.path.isdir(filename):
+- os.unlink(filename)
+- archive.extract(member, PARENT_DIR)
+- os.lchown(filename, os.geteuid(), os.getegid())
+- yield member.name, i, total_members
+- archive.close()
+-
+- def cancel(self):
+- if not self.local_file.closed:
+- self.local_file.close()
+-
+-def load_serialized_images():
+- global box_logo_pixbuf, window_icon
+- import gtk
+- box_logo_pixbuf = @IMAGEDATA64@
+- window_icon = @IMAGEDATA16@
+-
+-GUI_AVAILABLE = os.environ.get("DISPLAY", '')
+-
+-if GUI_AVAILABLE:
+- def download():
+- import pygtk
+- pygtk.require("2.0")
+- import gtk
+- import gobject
+- import pango
+- import webbrowser
+-
+- load_serialized_images()
+-
+- global FatalVisibleError
+- def FatalVisibleError(s):
+- error = gtk.MessageDialog(parent = None,
+- flags = gtk.DIALOG_MODAL,
+- type = gtk.MESSAGE_ERROR,
+- buttons = gtk.BUTTONS_OK,
+- message_format = s)
+- error.set_title("Error")
+- error.run()
+- gtk.main_quit()
+- sys.exit(-1)
+-
+- def gtk_flush_events():
+- while gtk.events_pending():
+- gtk.main_iteration()
+-
+- class DownloadDialog(gtk.Dialog):
+- def handle_delete_event(self, wid, ev, data=None):
+- self.handle_cancel(wid)
+-
+- def handle_dont_show_toggle(self, button, data=None):
+- reroll_autostart(not button.get_active())
+-
+- def handle_cancel(self, button):
+- if self.watch:
+- gobject.source_remove(self.watch)
+- if self.download:
+- self.download.cancel()
+- gtk.main_quit()
+- self.user_cancelled = True
+-
+- def handle_ok(self, button):
+- # begin download
+- self.ok.hide()
+- self.download = DownloadState()
+- self.one_chunk = self.download.copy_data()
+- self.watch = gobject.io_add_watch(self.download.socket,
+- gobject.IO_IN |
+- gobject.IO_PRI |
+- gobject.IO_ERR |
+- gobject.IO_HUP,
+- self.handle_data_waiting)
+- self.label.hide()
+- self.dont_show_again_align.hide()
+- self.progress.show()
+-
+- def update_progress(self, text, fraction):
+- self.progress.set_text(text % int(fraction*100))
+- self.progress.set_fraction(fraction)
+- gtk_flush_events()
+-
+- def handle_data_waiting(self, fd, condition):
+- if condition == gobject.IO_HUP:
+- FatalVisibleError("Connection to server unexpectedly
closed.")
+- elif condition == gobject.IO_ERR:
+- FatalVisibleError("Unexpected error occurred with
download.")
+- try:
+- while True:
+- progress, status = self.one_chunk.next()
+- if not status:
+- break
+- self.update_progress(DOWNLOADING,
float(progress)/self.download.size)
+- except StopIteration:
+- self.update_progress(DOWNLOADING, 1.0)
+- self.unpack_dropbox()
+- return False
+- else:
+- self.update_progress(DOWNLOADING,
float(progress)/self.download.size)
+- return True
+-
+- def unpack_dropbox(self):
+- one_member = self.download.unpack()
+- try:
+- while True:
+- name, i, total = one_member.next()
+- self.update_progress(UNPACKING, float(i)/total)
+- except StopIteration:
+- self.update_progress(UNPACKING, 1.0)
+- gtk.main_quit()
+-
+- def mouse_down(self, widget, event):
+- if self.hovering:
+- self.clicked_link = True
+-
+- def mouse_up(self, widget, event):
+- if self.clicked_link:
+- webbrowser.open(LINK)
+- self.clicked_link = False
+-
+- def label_motion(self, widget, event):
+- offx, offy = self.label.get_layout_offsets()
+- layout = self.label.get_layout()
+- index = layout.xy_to_index(int((offx+event.x)*pango.SCALE),
+- int((offy+event.y)*pango.SCALE))[0]
+- link_index = layout.get_text().find(LINK)
+- if index >= link_index and index < link_index+len(LINK):
+- self.hovering = True
+- self.label_box.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.HAND2))
+- else:
+- self.hovering = False
+- self.label_box.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.ARROW))
+-
+-
+- def __init__(self):
+- super(DownloadDialog, self).__init__(parent = None,
+- title = "Dropbox
Installation")
+-
+- self.download = None
+- self.watch = None
+- self.hovering = False
+- self.clicked_link = False
+- self.user_cancelled = False
+-
+- self.ok = ok = gtk.Button(stock=gtk.STOCK_OK)
+- ok.connect('clicked', self.handle_ok)
+- self.action_area.add(ok)
+- ok.show()
+-
+- cancel = gtk.Button(stock=gtk.STOCK_CANCEL)
+- cancel.connect('clicked', self.handle_cancel)
+- self.action_area.add(cancel)
+- cancel.show()
+-
+- self.connect('delete_event', self.handle_delete_event)
+-
+- self.box_logo = gtk.image_new_from_pixbuf(box_logo_pixbuf)
+- self.box_logo.show()
+-
+- self.set_icon(window_icon)
+-
+- self.progress = gtk.ProgressBar()
+- self.progress.set_property('width-request', 300)
+-
+- self.label = gtk.Label()
+- GPG_WARNING_MSG = (u"\n\n" + GPG_WARNING) if not gpgme else
u""
+- self.label.set_markup('%s <span foreground="#000099"
underline="single" weight="bold">%s</span>\n\n%s%s' %
(INFO, LINK, WARNING, GPG_WARNING_MSG))
+- self.label.set_line_wrap(True)
+- self.label.set_property('width-request', 300)
+- self.label.show()
+-
+- self.label_box = gtk.EventBox()
+- self.label_box.add(self.label)
+- self.label_box.connect("button-release-event", self.mouse_up)
+- self.label_box.connect("button-press-event", self.mouse_down)
+- self.label_box.connect("motion-notify-event",
self.label_motion)
+-
+- self.label_box.show()
+- def on_realize(widget):
+- self.label_box.add_events(gtk.gdk.POINTER_MOTION_MASK)
+- self.label_box.connect("realize", on_realize)
+-
+- self.hbox = gtk.HBox(spacing=10)
+- self.hbox.set_property('border-width',10)
+- self.hbox.pack_start(self.box_logo, False, False)
+- self.hbox.pack_start(self.label_box, False, False)
+- self.hbox.pack_start(self.progress, False, False)
+- self.hbox.show()
+-
+- self.vbox.add(self.hbox)
+-
+- try:
+- if can_reroll_autostart():
+- dont_show_again = gtk.CheckButton("_Don't show this
again")
+- dont_show_again.connect('toggled',
self.handle_dont_show_toggle)
+- dont_show_again.show()
+-
+- self.dont_show_again_align = gtk.Alignment(xalign=1.0,
yalign=0.0, xscale=0.0, yscale=0.0)
+- self.dont_show_again_align.add(dont_show_again)
+- self.dont_show_again_align.show()
+-
+- hbox = gtk.HBox()
+- hbox.set_property('border-width', 10)
+- hbox.pack_start(self.dont_show_again_align, True, True)
+- hbox.show()
+-
+- self.vbox.add(hbox)
+-
+- self.set_resizable(False)
+- except:
+- import traceback
+- traceback.print_exc()
+-
+- self.ok.grab_focus()
+-
+- dialog = DownloadDialog()
+- dialog.show()
+- gtk.main()
+- if dialog.user_cancelled:
+- raise Exception("user cancelled download!!!")
+-else:
+- def download():
+- global FatalVisibleError
+- def FatalVisibleError(s):
+- console_print(u"\nError: %s" % s, f=sys.stderr)
+- sys.exit(-1)
+-
+-
+- ESC = "\x1b"
+- save = ESC+"7"
+- unsave = ESC+"8"
+- clear = ESC+"[2J"
+- erase_to_start = ESC+"[1K"
+- write = sys.stdout.write
+- flush = sys.stdout.flush
+-
+- last_progress = [None, None]
+- def setprogress(text, frac):
+- if last_progress == [text, frac]:
+- return
+- if sys.stdout.isatty():
+- write(erase_to_start)
+- write(unsave)
+- console_print(text % int(100*frac), linebreak=not sys.stdout.isatty())
+- if sys.stdout.isatty():
+- flush()
+- last_progress[0], last_progress[1] = text, frac
+-
+- console_print()
+- if sys.stdout.isatty():
+- write(save)
+- flush()
+- console_print(u"%s %s\n" % (INFO, LINK))
+- GPG_WARNING_MSG = (u"\n%s" % GPG_WARNING) if not gpgme else
u""
+-
+- download = DownloadState()
+- one_chunk = download.copy_data()
+-
+- try:
+- while True:
+- progress = one_chunk.next()[0]
+- setprogress(DOWNLOADING, float(progress)/download.size)
+- except StopIteration:
+- setprogress(DOWNLOADING, 1.0)
+- console_print()
+- write(save)
+-
+- one_member = download.unpack()
+-
+- try:
+- while True:
+- name, i, total = one_member.next()
+- setprogress(UNPACKING, float(i)/total)
+- except StopIteration:
+- setprogress(UNPACKING, 1.0)
+-
+- console_print()
+-
+-class CommandTicker(threading.Thread):
+- def __init__(self):
+- threading.Thread.__init__(self)
+- self.stop_event = threading.Event()
+-
+- def stop(self):
+- self.stop_event.set()
+-
+- def run(self):
+- ticks = ['[. ]', '[.. ]', '[...]', '[ ..]',
'[ .]', '[ ]']
+- i = 0
+- first = True
+- while True:
+- self.stop_event.wait(0.25)
+- if self.stop_event.isSet(): break
+- if i == len(ticks):
+- first = False
+- i = 0
+- if not first:
+- sys.stderr.write("\r%s\r" % ticks[i])
+- sys.stderr.flush()
+- i += 1
+- sys.stderr.flush()
+-
+-
+-class DropboxCommand(object):
+- class CouldntConnectError(Exception): pass
+- class BadConnectionError(Exception): pass
+- class EOFError(Exception): pass
+- class CommandError(Exception): pass
+-
+- def __init__(self, timeout=5):
+- self.s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+- self.s.settimeout(timeout)
+- try:
+- self.s.connect(os.path.expanduser(u'~/.dropbox/command_socket'))
+- except socket.error, e:
+- raise DropboxCommand.CouldntConnectError()
+- self.f = self.s.makefile("r+", 4096)
+-
+- def close(self):
+- self.f.close()
+- self.s.close()
+-
+- def __readline(self):
+- try:
+- toret = self.f.readline().decode('utf8').rstrip(u"\n")
+- except socket.error, e:
+- raise DropboxCommand.BadConnectionError()
+- if toret == '':
+- raise DropboxCommand.EOFError()
+- else:
+- return toret
+-
+- # atttribute doesn't exist, i know what you want
+- def send_command(self, name, args):
+- self.f.write(name.encode('utf8'))
+- self.f.write(u"\n".encode('utf8'))
+- self.f.writelines((u"\t".join([k] + (list(v)
+- if hasattr(v, '__iter__') else
+- [v])) +
u"\n").encode('utf8')
+- for k,v in args.iteritems())
+- self.f.write(u"done\n".encode('utf8'))
+-
+- self.f.flush()
+-
+- # Start a ticker
+- ticker_thread = CommandTicker()
+- ticker_thread.start()
+-
+- # This is the potentially long-running call.
+- try:
+- ok = self.__readline() == u"ok"
+- except KeyboardInterrupt:
+- raise DropboxCommand.BadConnectionError("Keyboard interruption
detected")
+- finally:
+- # Tell the ticker to stop.
+- ticker_thread.stop()
+- ticker_thread.join()
+-
+- if ok:
+- toret = {}
+- for i in range(21):
+- if i == 20:
+- raise Exception(u"close this connection!")
+-
+- line = self.__readline()
+- if line == u"done":
+- break
+-
+- argval = line.split(u"\t")
+- toret[argval[0]] = argval[1:]
+-
+- return toret
+- else:
+- problems = []
+- for i in range(21):
+- if i == 20:
+- raise Exception(u"close this connection!")
+-
+- line = self.__readline()
+- if line == u"done":
+- break
+-
+- problems.append(line)
+-
+- raise DropboxCommand.CommandError(u"\n".join(problems))
+-
+- # this is the hotness, auto marshalling
+- def __getattr__(self, name):
+- try:
+- return super(DropboxCommand, self).__getattr__(name)
+- except:
+- def __spec_command(**kw):
+- return self.send_command(unicode(name), kw)
+- self.__setattr__(name, __spec_command)
+- return __spec_command
+-
+-commands = {}
+-aliases = {}
+-
+-def command(meth):
+- global commands, aliases
+- assert meth.__doc__, "All commands need properly formatted docstrings (even
%r!!)" % meth
+- if hasattr(meth, 'im_func'): # bound method, if we ever have one
+- meth = meth.im_func
+- commands[meth.func_name] = meth
+- meth_aliases = [unicode(alias) for alias in aliases.iterkeys() if
aliases[alias].func_name == meth.func_name]
+- if meth_aliases:
+- meth.__doc__ += u"\nAliases: %s" % ",".join(meth_aliases)
+- return meth
+-
+-def alias(name):
+- def decorator(meth):
+- global commands, aliases
+- assert name not in commands, "This alias is the name of a command."
+- aliases[name] = meth
+- return meth
+- return decorator
+-
+-def requires_dropbox_running(meth):
+- def newmeth(*n, **kw):
+- if is_dropbox_running():
+- return meth(*n, **kw)
+- else:
+- console_print(u"Dropbox isn't running!")
+- newmeth.func_name = meth.func_name
+- newmeth.__doc__ = meth.__doc__
+- return newmeth
+-
+-def start_dropbox():
+- db_path = os.path.expanduser(DROPBOXD_PATH).encode(sys.getfilesystemencoding())
+- if os.access(db_path, os.X_OK):
+- f = open("/dev/null", "w")
+- # we don't reap the child because we're gonna die anyway, let init do
it
+- a = subprocess.Popen([db_path], preexec_fn=os.setsid,
cwd=os.path.expanduser("~"),
+- stderr=sys.stderr, stdout=f, close_fds=True)
+-
+- # in seconds
+- interval = 0.5
+- wait_for = 60
+- for i in xrange(int(wait_for / interval)):
+- if is_dropbox_running():
+- return True
+- # back off from connect for a while
+- time.sleep(interval)
+-
+- return False
+- else:
+- return False
+-
+-# Extracted and modified from os.cmd.Cmd
+-def columnize(list, display_list=None, display_width=None):
+- if not list:
+- console_print(u"<empty>")
+- return
+-
+- non_unicode = [i for i in range(len(list)) if not (isinstance(list[i], unicode))]
+- if non_unicode:
+- raise TypeError, ("list[i] not a string for i in %s" %
+- ", ".join(map(unicode, non_unicode)))
+-
+- if not display_width:
+- d = os.popen('stty size', 'r').read().split()
+- if d:
+- display_width = int(d[1])
+- else:
+- for item in list:
+- console_print(item)
+- return
+-
+- if not display_list:
+- display_list = list
+-
+- size = len(list)
+- if size == 1:
+- console_print(display_list[0])
+- return
+-
+- for nrows in range(1, len(list)):
+- ncols = (size+nrows-1) // nrows
+- colwidths = []
+- totwidth = -2
+- for col in range(ncols):
+- colwidth = 0
+- for row in range(nrows):
+- i = row + nrows*col
+- if i >= size:
+- break
+- x = list[i]
+- colwidth = max(colwidth, len(x))
+- colwidths.append(colwidth)
+- totwidth += colwidth + 2
+- if totwidth > display_width:
+- break
+- if totwidth <= display_width:
+- break
+- else:
+- nrows = len(list)
+- ncols = 1
+- colwidths = [0]
+- lines = []
+- for row in range(nrows):
+- texts = []
+- display_texts = []
+- for col in range(ncols):
+- i = row + nrows*col
+- if i >= size:
+- x = ""
+- y = ""
+- else:
+- x = list[i]
+- y = display_list[i]
+- texts.append(x)
+- display_texts.append(y)
+- while texts and not texts[-1]:
+- del texts[-1]
+- original_texts = texts[:]
+- for col in range(len(texts)):
+- texts[col] = texts[col].ljust(colwidths[col])
+- line = u"%s" % " ".join(texts)
+- for i, text in enumerate(original_texts):
+- line = line.replace(text, display_texts[i])
+- lines.append(line)
+- for line in lines:
+- console_print(line)
+-
+-@command
+-def update(args):
+- u"""download latest version of dropbox
+-dropbox update
+-
+-Downloads the latest version of dropbox.
+-"""
+- download()
+-
+-@command
+-@requires_dropbox_running
+-@alias('stat')
+-def filestatus(args):
+- u"""get current sync status of one or more files
+-dropbox filestatus [-l] [-a] [FILE]...
+-
+-Prints the current status of each FILE.
+-
+-options:
+- -l --list prints out information in a format similar to ls. works best when your
console supports color :)
+- -a --all do not ignore entries starting with .
+-"""
+- global enc
+-
+- oparser = optparse.OptionParser()
+- oparser.add_option("-l", "--list",
action="store_true", dest="list")
+- oparser.add_option("-a", "--all", action="store_true",
dest="all")
+- (options, args) = oparser.parse_args(args)
+-
+- try:
+- with closing(DropboxCommand()) as dc:
+- if options.list:
+- # Listing.
+-
+- # Separate directories from files.
+- if len(args) == 0:
+- dirs, nondirs = [u"."], []
+- else:
+- dirs, nondirs = [], []
+-
+- for a in args:
+- try:
+- (dirs if os.path.isdir(a) else
nondirs).append(a.decode(enc))
+- except UnicodeDecodeError:
+- continue
+-
+- if len(dirs) == 0 and len(nondirs) == 0:
+- #TODO: why?
+- exit(1)
+-
+- dirs.sort(key=methodcaller('lower'))
+- nondirs.sort(key=methodcaller('lower'))
+-
+- # Gets a string representation for a path.
+- def path_to_string(file_path):
+- if not os.path.exists(file_path):
+- path = u"%s (File doesn't exist!)" %
os.path.basename(file_path)
+- return (path, path)
+- try:
+- status =
dc.icon_overlay_file_status(path=file_path).get(u'status', [None])[0]
+- except DropboxCommand.CommandError, e:
+- path = u"%s (%s)" % (os.path.basename(file_path), e)
+- return (path, path)
+-
+- env_term = os.environ.get('TERM','')
+- supports_color = (sys.stderr.isatty() and (
+- env_term.startswith('vt') or
+- env_term.startswith('linux') or
+- 'xterm' in env_term or
+- 'color' in env_term
+- )
+- )
+-
+- # TODO: Test when you don't support color.
+- if not supports_color:
+- path = os.path.basename(file_path)
+- return (path, path)
+-
+- if status == u"up to date":
+- init, cleanup = "\x1b[32;1m", "\x1b[0m"
+- elif status == u"syncing":
+- init, cleanup = "\x1b[36;1m", "\x1b[0m"
+- elif status == u"unsyncable":
+- init, cleanup = "\x1b[41;1m", "\x1b[0m"
+- elif status == u"selsync":
+- init, cleanup = "\x1b[37;1m", "\x1b[0m"
+- else:
+- init, cleanup = '', ''
+-
+- path = os.path.basename(file_path)
+- return (path, u"%s%s%s" % (init, path, cleanup))
+-
+- # Prints a directory.
+- def print_directory(name):
+- clean_paths = []
+- formatted_paths = []
+- for subname in sorted(os.listdir(name),
key=methodcaller('lower')):
+- if type(subname) != unicode:
+- continue
+-
+- if not options.all and subname[0] == u'.':
+- continue
+-
+- try:
+- clean, formatted =
path_to_string(unicode_abspath(os.path.join(name, subname)))
+- clean_paths.append(clean)
+- formatted_paths.append(formatted)
+- except (UnicodeEncodeError, UnicodeDecodeError), e:
+- continue
+-
+- columnize(clean_paths, formatted_paths)
+-
+- try:
+- if len(dirs) == 1 and len(nondirs) == 0:
+- print_directory(dirs[0])
+- else:
+- nondir_formatted_paths = []
+- nondir_clean_paths = []
+- for name in nondirs:
+- try:
+- clean, formatted =
path_to_string(unicode_abspath(name))
+- nondir_clean_paths.append(clean)
+- nondir_formatted_paths.append(formatted)
+- except (UnicodeEncodeError, UnicodeDecodeError), e:
+- continue
+-
+- if nondir_clean_paths:
+- columnize(nondir_clean_paths, nondir_formatted_paths)
+-
+- if len(nondirs) == 0:
+- console_print(dirs[0] + u":")
+- print_directory(dirs[0])
+- dirs = dirs[1:]
+-
+- for name in dirs:
+- console_print()
+- console_print(name + u":")
+- print_directory(name)
+-
+- except DropboxCommand.EOFError:
+- console_print(u"Dropbox daemon stopped.")
+- except DropboxCommand.BadConnectionError, e:
+- console_print(u"Dropbox isn't responding!")
+- else:
+- if len(args) == 0:
+- args = [name for name in sorted(os.listdir(u"."),
key=methodcaller('lower')) if type(name) == unicode]
+- indent = max(len(st)+1 for st in args)
+- for file in args:
+-
+- try:
+- if type(file) is not unicode:
+- file = file.decode(enc)
+- fp = unicode_abspath(file)
+- except (UnicodeEncodeError, UnicodeDecodeError), e:
+- continue
+- if not os.path.exists(fp):
+- console_print(u"%-*s %s" % \
+- (indent, file+':', "File doesn't
exist"))
+- continue
+-
+- try:
+- status =
dc.icon_overlay_file_status(path=fp).get(u'status', [u'unknown'])[0]
+- console_print(u"%-*s %s" % (indent, file+':',
status))
+- except DropboxCommand.CommandError, e:
+- console_print(u"%-*s %s" % (indent, file+':',
e))
+- except DropboxCommand.CouldntConnectError, e:
+- console_print(u"Dropbox isn't running!")
+-
+-@command
+-@requires_dropbox_running
+-def ls(args):
+- u"""list directory contents with current sync status
+-dropbox ls [FILE]...
+-
+-This is an alias for filestatus -l
+-"""
+- return filestatus(["-l"] + args)
+-
+-@command
+-@requires_dropbox_running
+-def puburl(args):
+- u"""get public url of a file in your dropbox
+-dropbox puburl FILE
+-
+-Prints out a public url for FILE.
+-"""
+- if len(args) != 1:
+- console_print(puburl.__doc__,linebreak=False)
+- return
+-
+- try:
+- with closing(DropboxCommand()) as dc:
+- try:
+-
console_print(dc.get_public_link(path=unicode_abspath(args[0].decode(sys.getfilesystemencoding()))).get(u'link',
[u'No Link'])[0])
+- except DropboxCommand.CommandError, e:
+- console_print(u"Couldn't get public url: " + str(e))
+- except DropboxCommand.BadConnectionError, e:
+- console_print(u"Dropbox isn't responding!")
+- except DropboxCommand.EOFError:
+- console_print(u"Dropbox daemon stopped.")
+- except DropboxCommand.CouldntConnectError, e:
+- console_print(u"Dropbox isn't running!")
+-
+-@command
+-@requires_dropbox_running
+-def status(args):
+- u"""get current status of the dropboxd
+-dropbox status
+-
+-Prints out the current status of the Dropbox daemon.
+-"""
+- if len(args) != 0:
+- console_print(status.__doc__,linebreak=False)
+- return
+-
+- try:
+- with closing(DropboxCommand()) as dc:
+- try:
+- lines = dc.get_dropbox_status()[u'status']
+- if len(lines) == 0:
+- console_print(u'Idle')
+- else:
+- for line in lines:
+- console_print(line)
+- except KeyError:
+- console_print(u"Couldn't get status: daemon isn't
responding")
+- except DropboxCommand.CommandError, e:
+- console_print(u"Couldn't get status: " + str(e))
+- except DropboxCommand.BadConnectionError, e:
+- console_print(u"Dropbox isn't responding!")
+- except DropboxCommand.EOFError:
+- console_print(u"Dropbox daemon stopped.")
+- except DropboxCommand.CouldntConnectError, e:
+- console_print(u"Dropbox isn't running!")
+-
+-@command
+-def running(argv):
+- u"""return whether dropbox is running
+-dropbox running
+-
+-Returns 1 if running 0 if not running.
+-"""
+- return int(is_dropbox_running())
+-
+-@command
+-@requires_dropbox_running
+-def stop(args):
+- u"""stop dropboxd
+-dropbox stop
+-
+-Stops the dropbox daemon.
+-"""
+- try:
+- with closing(DropboxCommand()) as dc:
+- try:
+- dc.tray_action_hard_exit()
+- except DropboxCommand.BadConnectionError, e:
+- console_print(u"Dropbox isn't responding!")
+- except DropboxCommand.EOFError:
+- console_print(u"Dropbox daemon stopped.")
+- except DropboxCommand.CouldntConnectError, e:
+- console_print(u"Dropbox isn't running!")
+-
+-#returns true if link is necessary
+-def grab_link_url_if_necessary():
+- try:
+- with closing(DropboxCommand()) as dc:
+- try:
+- link_url = dc.needs_link().get(u"link_url", None)
+- if link_url is not None:
+- console_print(u"To link this computer to a dropbox account,
visit the following url:\n%s" % link_url[0])
+- return True
+- else:
+- return False
+- except DropboxCommand.CommandError, e:
+- pass
+- except DropboxCommand.BadConnectionError, e:
+- console_print(u"Dropbox isn't responding!")
+- except DropboxCommand.EOFError:
+- console_print(u"Dropbox daemon stopped.")
+- except DropboxCommand.CouldntConnectError, e:
+- console_print(u"Dropbox isn't running!")
+-
+-@command
+-@requires_dropbox_running
+-def lansync(argv):
+- u"""enables or disables LAN sync
+-dropbox lansync [y/n]
+-
+-options:
+- y dropbox will use LAN sync (default)
+- n dropbox will not use LAN sync
+-"""
+- if len(argv) != 1:
+- console_print(lansync.__doc__, linebreak=False)
+- return
+-
+- s = argv[0].lower()
+- if s.startswith('y') or s.startswith('-y'):
+- should_lansync = True
+- elif s.startswith('n') or s.startswith('-n'):
+- should_lansync = False
+- else:
+- should_lansync = None
+-
+- if should_lansync is None:
+- console_print(lansync.__doc__,linebreak=False)
+- else:
+- with closing(DropboxCommand()) as dc:
+- dc.set_lan_sync(lansync='enabled' if should_lansync else
'disabled')
+-
+-
+-@command
+-@requires_dropbox_running
+-def exclude(args):
+- u"""ignores/excludes a directory from syncing
+-dropbox exclude [list]
+-dropbox exclude add [DIRECTORY] [DIRECTORY] ...
+-dropbox exclude remove [DIRECTORY] [DIRECTORY] ...
+-
+-"list" prints a list of directories currently excluded from syncing.
+-"add" adds one or more directories to the exclusion list, then resynchronizes
Dropbox.
+-"remove" removes one or more directories from the exclusion list, then
resynchronizes Dropbox.
+-With no arguments, executes "list".
+-Any specified path must be within Dropbox.
+-"""
+- if len(args) == 0:
+- try:
+- with closing(DropboxCommand()) as dc:
+- try:
+- lines = [relpath(path) for path in
dc.get_ignore_set()[u'ignore_set']]
+- lines.sort()
+- if len(lines) == 0:
+- console_print(u'No directories are being ignored.')
+- else:
+- console_print(u'Excluded: ')
+- for line in lines:
+- console_print(unicode(line))
+- except KeyError:
+- console_print(u"Couldn't get ignore set: daemon isn't
responding")
+- except DropboxCommand.CommandError, e:
+- if e.args[0].startswith(u"No command exists by that
name"):
+- console_print(u"This version of the client does not support
this command.")
+- else:
+- console_print(u"Couldn't get ignore set: " +
str(e))
+- except DropboxCommand.BadConnectionError, e:
+- console_print(u"Dropbox isn't responding!")
+- except DropboxCommand.EOFError:
+- console_print(u"Dropbox daemon stopped.")
+- except DropboxCommand.CouldntConnectError, e:
+- console_print(u"Dropbox isn't running!")
+- elif len(args) == 1 and args[0] == u"list":
+- exclude([])
+- elif len(args) >= 2:
+- sub_command = args[0]
+- paths = args[1:]
+- absolute_paths = [unicode_abspath(path.decode(sys.getfilesystemencoding())) for
path in paths]
+- if sub_command == u"add":
+- try:
+- with closing(DropboxCommand(timeout=None)) as dc:
+- try:
+- result = dc.ignore_set_add(paths=absolute_paths)
+- if result[u"ignored"]:
+- console_print(u"Excluded: ")
+- lines = [relpath(path) for path in
result[u"ignored"]]
+- for line in lines:
+- console_print(unicode(line))
+- except KeyError:
+- console_print(u"Couldn't add ignore path: daemon
isn't responding")
+- except DropboxCommand.CommandError, e:
+- if e.args[0].startswith(u"No command exists by that
name"):
+- console_print(u"This version of the client does not
support this command.")
+- else:
+- console_print(u"Couldn't get ignore set: " +
str(e))
+- except DropboxCommand.BadConnectionError, e:
+- console_print(u"Dropbox isn't responding! [%s]" %
e)
+- except DropboxCommand.EOFError:
+- console_print(u"Dropbox daemon stopped.")
+- except DropboxCommand.CouldntConnectError, e:
+- console_print(u"Dropbox isn't running!")
+- elif sub_command == u"remove":
+- try:
+- with closing(DropboxCommand(timeout=None)) as dc:
+- try:
+- result = dc.ignore_set_remove(paths=absolute_paths)
+- if result[u"removed"]:
+- console_print(u"No longer excluded: ")
+- lines = [relpath(path) for path in
result[u"removed"]]
+- for line in lines:
+- console_print(unicode(line))
+- except KeyError:
+- console_print(u"Couldn't remove ignore path: daemon
isn't responding")
+- except DropboxCommand.CommandError, e:
+- if e.args[0].startswith(u"No command exists by that
name"):
+- console_print(u"This version of the client does not
support this command.")
+- else:
+- console_print(u"Couldn't get ignore set: " +
str(e))
+- except DropboxCommand.BadConnectionError, e:
+- console_print(u"Dropbox isn't responding! [%s]" %
e)
+- except DropboxCommand.EOFError:
+- console_print(u"Dropbox daemon stopped.")
+- except DropboxCommand.CouldntConnectError, e:
+- console_print(u"Dropbox isn't running!")
+- else:
+- console_print(exclude.__doc__, linebreak=False)
+- return
+- else:
+- console_print(exclude.__doc__, linebreak=False)
+- return
+-
+-@command
+-def start(argv):
+- u"""start dropboxd
+-dropbox start [-i]
+-
+-Starts the dropbox daemon, dropboxd. If dropboxd is already running, this will do
nothing.
+-
+-options:
+- -i --install auto install dropboxd if not available on the system
+-"""
+-
+- should_install = "-i" in argv or "--install" in argv
+-
+- # first check if dropbox is already running
+- if is_dropbox_running():
+- if not grab_link_url_if_necessary():
+- console_print(u"Dropbox is already running!")
+- return
+-
+- console_print(u"Starting Dropbox...", linebreak=False)
+- console_flush()
+- if not start_dropbox():
+- if not should_install:
+- console_print()
+- console_print(u"The Dropbox daemon is not installed!")
+- console_print(u"Run \"dropbox start -i\" to install the
daemon")
+- return
+-
+- # install dropbox!!!
+- try:
+- args = [ "pkexec", "dropbox" ]
+- if "http_proxy" in os.environ:
+- args.extend(["--http-proxy",
os.environ["http_proxy"]])
+- if "https_proxy" in os.environ:
+- args.extend(["--https-proxy",
os.environ["https_proxy"]])
+- args.append("update")
+- status = os.spawnvp(os.P_WAIT, "pkexec", args)
+- if status != 0:
+- console_print(u"The installation of Dropbox failed.")
+- return
+- except:
+- pass
+- else:
+- if GUI_AVAILABLE:
+- start_dropbox()
+- console_print(u"Done!")
+- else:
+- if start_dropbox():
+- if not grab_link_url_if_necessary():
+- console_print(u"Done!")
+- else:
+- if not grab_link_url_if_necessary():
+- console_print(u"Done!")
+-
+-
+-def can_reroll_autostart():
+- return u".config" in os.listdir(os.path.expanduser(u'~'))
+-
+-def reroll_autostart(should_autostart):
+- home_dir = os.path.expanduser(u'~')
+- contents = os.listdir(home_dir)
+-
+- # UBUNTU
+- if u".config" in contents:
+- autostart_dir = os.path.join(home_dir, u".config",
u"autostart")
+- autostart_link = os.path.join(autostart_dir, u"dropbox.desktop")
+- if should_autostart:
+- if os.path.exists(DESKTOP_FILE):
+- if not os.path.exists(autostart_dir):
+- os.makedirs(autostart_dir)
+- shutil.copyfile(DESKTOP_FILE, autostart_link)
+- elif os.path.exists(autostart_link):
+- os.remove(autostart_link)
+-
+-
+-
+-@command
+-def autostart(argv):
+- u"""automatically start dropbox at login
+-dropbox autostart [y/n]
+-
+-options:
+- n dropbox will not start automatically at login
+- y dropbox will start automatically at login (default)
+-
+-Note: May only work on current Ubuntu distributions.
+-"""
+- if len(argv) != 1:
+- console_print(''.join(autostart.__doc__.split('\n',
1)[1:]).decode('ascii'))
+- return
+-
+- s = argv[0].lower()
+- if s.startswith('y') or s.startswith('-y'):
+- should_autostart = True
+- elif s.startswith('n') or s.startswith('-n'):
+- should_autostart = False
+- else:
+- should_autostart = None
+-
+- if should_autostart is None:
+- console_print(autostart.__doc__,linebreak=False)
+- else:
+- reroll_autostart(should_autostart)
+-
+-@command
+-def help(argv):
+- u"""provide help
+-dropbox help [COMMAND]
+-
+-With no arguments, print a list of commands and a short description of each. With a
command, print descriptive help on how to use the command.
+-"""
+- if not argv:
+- return usage(argv)
+- for command in commands:
+- if command == argv[0]:
+- console_print(commands[command].__doc__.split('\n',
1)[1].decode('ascii'))
+- return
+- for alias in aliases:
+- if alias == argv[0]:
+- console_print(aliases[alias].__doc__.split('\n',
1)[1].decode('ascii'))
+- return
+- console_print(u"unknown command '%s'" % argv[0], f=sys.stderr)
+-
+-def usage(argv):
+- console_print(u"Dropbox command-line interface\n")
+- console_print(u"commands:\n")
+- console_print(u"Note: use dropbox help <command> to view usage for a
specific command.\n")
+- out = []
+- for command in commands:
+- out.append((command, commands[command].__doc__.splitlines()[0]))
+- spacing = max(len(o[0])+3 for o in out)
+- for o in out:
+- console_print(" %-*s%s" % (spacing, o[0], o[1]))
+- console_print()
+-
+-def main(argv):
+- global commands
+-
+- # now we need to find out if one of the commands are in the
+- # argv list, and if so split the list at the point to
+- # separate the argv list at that point
+- cut = None
+- for i in range(len(argv)):
+- if argv[i] in commands or argv[i] in aliases:
+- cut = i
+- break
+-
+- if cut == None:
+- usage(argv)
+- os._exit(0)
+- return
+-
+- # option parsing code
+- def set_http_proxy(option, opt_str, value, parser):
+- os.environ["http_proxy"] = value
+- # Need to reset _urlopener to take the new value into account
+- urllib._urlopener = DropboxURLopener()
+-
+- def set_https_proxy(option, opt_str, value, parser):
+- os.environ["https_proxy"] = value
+- # Need to reset _urlopener to take the new value into account
+- urllib._urlopener = DropboxURLopener()
+-
+- globaloptionparser = optparse.OptionParser()
+- globaloptionparser.add_option("--http-proxy",
action="callback",
+- type="string", callback=set_http_proxy)
+- globaloptionparser.add_option("--https-proxy",
action="callback",
+- type="string", callback=set_https_proxy)
+- globaloptionparser.parse_args(argv[0:i])
+-
+- # now dispatch and run
+- result = None
+- if argv[i] in commands:
+- result = commands[argv[i]](argv[i+1:])
+- elif argv[i] in aliases:
+- result = aliases[argv[i]](argv[i+1:])
+-
+- # flush, in case output is rerouted to a file.
+- console_flush()
+-
+- # done
+- return result
+-
+-if __name__ == "__main__":
+- ret = main(sys.argv)
+- if ret is not None:
+- sys.exit(ret)
+diff --git a/nemo-dropbox/dropbox.txt.in b/nemo-dropbox/dropbox.txt.in
+deleted file mode 100644
+index 638fdc1c..00000000
+--- a/nemo-dropbox/dropbox.txt.in
++++ /dev/null
+@@ -1,51 +0,0 @@
+-=========
+- dropbox
+-=========
+-
+------------------------------------------------
+-A command line interface to the Dropbox service
+------------------------------------------------
+-
+-:Date: @DATE@
+-:Copyright: Copyright (C) 2008-2011 Evenflow, Inc. Free use of this software is granted
under the terms of the GNU General Public License (GPL).
+-:Version: @PACKAGE_VERSION@
+-:Manual section: 1
+-
+-SYNOPSIS
+-========
+-
+-@SYNOPSIS@
+-
+-DESCRIPTION
+-===========
+-
+-The *dropbox* command provides a command line interface to the Dropbox, the easiest
online file storage,
+-synchronization and sharing service.
+-
+-COMMANDS
+-========
+-
+-@COMMANDS@
+-
+-BUGS
+-====
+-
+-There are currently no known bugs. If you think you have found a bug or have a feature
request, post in
+-the forums, or send us an e-mail (see the **RESOURCES** section below).
+-
+-In the future there will be full command line support including linking accounts and
setting preferences
+-from the *dropbox* command.
+-
+-RESOURCES
+-=========
+-
+-* Dropbox <
http://www.dropbox.com/>
+-* Dropbox Forums <
http://forums.dropbox.com/>
+-* Dropbox Support E-mail <support(a)dropbox.com>
+-
+-AUTHORS
+-=======
+-
+-@AUTHORS@
+-
+-
+diff --git a/nemo-dropbox/rst2man.py b/nemo-dropbox/rst2man.py
+deleted file mode 100644
+index be255dde..00000000
+--- a/nemo-dropbox/rst2man.py
++++ /dev/null
+@@ -1,1091 +0,0 @@
+-#!/usr/bin/python2
+-
+-# Author:
+-# Contact: grubert(a)users.sf.net
+-# Copyright: This module has been placed in the public domain.
+-
+-"""
+-man.py
+-======
+-
+-This module provides a simple command line interface that uses the
+-man page writer to output from ReStructuredText source.
+-"""
+-
+-import locale
+-try:
+- locale.setlocale(locale.LC_ALL, '')
+-except:
+- pass
+-
+-from docutils.core import publish_cmdline, default_description
+-
+-# $Id: manpage.py 5645 2008-09-21 08:25:13Z grubert $
+-# Author: Engelbert Gruber <grubert(a)users.sourceforge.net>
+-# Copyright: This module is put into the public domain.
+-
+-"""
+-Simple man page writer for reStructuredText.
+-
+-Man pages (short for "manual pages") contain system documentation on
unix-like
+-systems. The pages are grouped in numbered sections:
+-
+- 1 executable programs and shell commands
+- 2 system calls
+- 3 library functions
+- 4 special files
+- 5 file formats
+- 6 games
+- 7 miscellaneous
+- 8 system administration
+-
+-Man pages are written *troff*, a text file formatting system.
+-
+-See
http://www.tldp.org/HOWTO/Man-Page for a start.
+-
+-Man pages have no subsection only parts.
+-Standard parts
+-
+- NAME ,
+- SYNOPSIS ,
+- DESCRIPTION ,
+- OPTIONS ,
+- FILES ,
+- SEE ALSO ,
+- BUGS ,
+-
+-and
+-
+- AUTHOR .
+-
+-A unix-like system keeps an index of the DESCRIPTIONs, which is accesable
+-by the command whatis or apropos.
+-
+-"""
+-
+-# NOTE: the macros only work when at line start, so try the rule
+-# start new lines in visit_ functions.
+-
+-__docformat__ = 'reStructuredText'
+-
+-import sys
+-import os
+-import time
+-import re
+-from types import ListType
+-
+-import docutils
+-from docutils import nodes, utils, writers, languages
+-
+-FIELD_LIST_INDENT = 7
+-DEFINITION_LIST_INDENT = 7
+-OPTION_LIST_INDENT = 7
+-BLOCKQOUTE_INDENT = 3.5
+-
+-# Define two macros so man/roff can calculate the
+-# indent/unindent margins by itself
+-MACRO_DEF = (r"""
+-.nr rst2man-indent-level 0
+-.
+-.de1 rstReportMargin
+-\\$1 \\n[an-margin]
+-level \\n[rst2man-indent-level]
+-level magin: \\n[rst2man-indent\\n[rst2man-indent-level]]
+--
+-\\n[rst2man-indent0]
+-\\n[rst2man-indent1]
+-\\n[rst2man-indent2]
+-..
+-.de1 INDENT
+-.\" .rstReportMargin pre:
+-. RS \\$1
+-. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
+-. nr rst2man-indent-level +1
+-.\" .rstReportMargin post:
+-..
+-.de UNINDENT
+-. RE
+-.\" indent \\n[an-margin]
+-.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
+-.nr rst2man-indent-level -1
+-.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
+-.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
+-..
+-""")
+-
+-class Writer(writers.Writer):
+-
+- supported = ('manpage')
+- """Formats this writer supports."""
+-
+- output = None
+- """Final translated form of `document`."""
+-
+- def __init__(self):
+- writers.Writer.__init__(self)
+- self.translator_class = Translator
+-
+- def translate(self):
+- visitor = self.translator_class(self.document)
+- self.document.walkabout(visitor)
+- self.output = visitor.astext()
+-
+-
+-class Table:
+- def __init__(self):
+- self._rows = []
+- self._options = ['center', ]
+- self._tab_char = '\t'
+- self._coldefs = []
+- def new_row(self):
+- self._rows.append([])
+- def append_cell(self, cell_lines):
+- """cell_lines is an array of lines"""
+- self._rows[-1].append(cell_lines)
+- if len(self._coldefs) < len(self._rows[-1]):
+- self._coldefs.append('l')
+- def astext(self):
+- text = '.TS\n'
+- text += ' '.join(self._options) + ';\n'
+- text += '|%s|.\n' % ('|'.join(self._coldefs))
+- for row in self._rows:
+- # row = array of cells. cell = array of lines.
+- # line above
+- text += '_\n'
+- max_lns_in_cell = 0
+- for cell in row:
+- max_lns_in_cell = max(len(cell), max_lns_in_cell)
+- for ln_cnt in range(max_lns_in_cell):
+- line = []
+- for cell in row:
+- if len(cell) > ln_cnt:
+- line.append(cell[ln_cnt])
+- else:
+- line.append(" ")
+- text += self._tab_char.join(line) + '\n'
+- text += '_\n'
+- text += '.TE\n'
+- return text
+-
+-class Translator(nodes.NodeVisitor):
+- """"""
+-
+- words_and_spaces = re.compile(r'\S+| +|\n')
+- document_start = """Man page generated from
reStructeredText."""
+-
+- def __init__(self, document):
+- nodes.NodeVisitor.__init__(self, document)
+- self.settings = settings = document.settings
+- lcode = settings.language_code
+- self.language = languages.get_language(lcode)
+- self.head = []
+- self.body = []
+- self.foot = []
+- self.section_level = 0
+- self.context = []
+- self.topic_class = ''
+- self.colspecs = []
+- self.compact_p = 1
+- self.compact_simple = None
+- # the list style "*" bullet or "#" numbered
+- self._list_char = []
+- # writing the header .TH and .SH NAME is postboned after
+- # docinfo.
+- self._docinfo = {
+- "title" : "", "subtitle" : "",
+- "manual_section" : "", "manual_group" :
"",
+- "author" : "",
+- "date" : "",
+- "copyright" : "",
+- "version" : "",
+- }
+- self._in_docinfo = None
+- self._active_table = None
+- self._in_entry = None
+- self.header_written = 0
+- self.authors = []
+- self.section_level = 0
+- self._indent = [0]
+- # central definition of simple processing rules
+- # what to output on : visit, depart
+- self.defs = {
+- 'indent' : ('.INDENT %.1f\n', '.UNINDENT\n'),
+- 'definition' : ('', ''),
+- 'definition_list' : ('', '.TP 0\n'),
+- 'definition_list_item' : ('\n.TP', ''),
+- #field_list
+- #field
+- 'field_name' : ('\n.TP\n.B ', '\n'),
+- 'field_body' : ('', '.RE\n', ),
+- 'literal' : ('\\fB', '\\fP'),
+- 'literal_block' : ('\n.nf\n', '\n.fi\n'),
+-
+- #option_list
+- 'option_list_item' : ('\n.TP', ''),
+- #option_group, option
+- 'description' : ('\n', ''),
+-
+- 'reference' : (r'\fI\%', r'\fP'),
+- #'target' : (r'\fI\%', r'\fP'),
+- 'emphasis': ('\\fI', '\\fP'),
+- 'strong' : ('\\fB', '\\fP'),
+- 'term' : ('\n.B ', '\n'),
+- 'title_reference' : ('\\fI', '\\fP'),
+-
+- 'problematic' : ('\n.nf\n', '\n.fi\n'),
+- }
+- # TODO dont specify the newline before a dot-command, but ensure
+- # check it is there.
+-
+- def comment_begin(self, text):
+- """Return commented version of the passed text WITHOUT end of
line/comment."""
+- prefix = '\n.\\" '
+- return prefix+prefix.join(text.split('\n'))
+-
+- def comment(self, text):
+- """Return commented version of the passed
text."""
+- return self.comment_begin(text)+'\n'
+-
+- def astext(self):
+- """Return the final formatted document as a
string."""
+- if not self.header_written:
+- # ensure we get a ".TH" as viewers require it.
+- self.head.append(self.header())
+- return ''.join(self.head + self.body + self.foot)
+-
+- def visit_Text(self, node):
+- text = node.astext().replace('-','\-')
+- text = text.replace("'","\\'")
+- self.body.append(text)
+-
+- def depart_Text(self, node):
+- pass
+-
+- def list_start(self, node):
+- class enum_char:
+- enum_style = {
+- 'arabic' : (3,1),
+- 'loweralpha' : (3,'a'),
+- 'upperalpha' : (3,'A'),
+- 'lowerroman' : (5,'i'),
+- 'upperroman' : (5,'I'),
+- 'bullet' : (2,'\\(bu'),
+- 'emdash' : (2,'\\(em'),
+- }
+- def __init__(self, style):
+- if style == 'arabic':
+- if node.has_key('start'):
+- start = node['start']
+- else:
+- start = 1
+- self._style = (
+- len(str(len(node.children)))+2,
+- start )
+- # BUG: fix start for alpha
+- else:
+- self._style = self.enum_style[style]
+- self._cnt = -1
+- def next(self):
+- self._cnt += 1
+- # BUG add prefix postfix
+- try:
+- return "%d." % (self._style[1] + self._cnt)
+- except:
+- if self._style[1][0] == '\\':
+- return self._style[1]
+- # BUG romans dont work
+- # BUG alpha only a...z
+- return "%c." % (ord(self._style[1])+self._cnt)
+- def get_width(self):
+- return self._style[0]
+- def __repr__(self):
+- return 'enum_style%r' % list(self._style)
+-
+- if node.has_key('enumtype'):
+- self._list_char.append(enum_char(node['enumtype']))
+- else:
+- self._list_char.append(enum_char('bullet'))
+- if len(self._list_char) > 1:
+- # indent nested lists
+- # BUG indentation depends on indentation of parent list.
+- self.indent(self._list_char[-2].get_width())
+- else:
+- self.indent(self._list_char[-1].get_width())
+-
+- def list_end(self):
+- self.dedent()
+- self._list_char.pop()
+-
+- def header(self):
+- tmpl = (".TH %(title)s %(manual_section)s"
+- " \"%(date)s\" \"%(version)s\"
\"%(manual_group)s\"\n"
+- ".SH NAME\n"
+- "%(title)s \- %(subtitle)s\n")
+- return tmpl % self._docinfo
+-
+- def append_header(self):
+- """append header with .TH and .SH NAME"""
+- # TODO before everything
+- # .TH title section date source manual
+- if self.header_written:
+- return
+- self.body.append(self.header())
+- self.body.append(MACRO_DEF)
+- self.header_written = 1
+-
+- def visit_address(self, node):
+- raise NotImplementedError, node.astext()
+- self.visit_docinfo_item(node, 'address', meta=None)
+-
+- def depart_address(self, node):
+- self.depart_docinfo_item()
+-
+- def visit_admonition(self, node, name):
+- raise NotImplementedError, node.astext()
+- self.body.append(self.starttag(node, 'div', CLASS=name))
+- self.body.append('<p class="admonition-title">'
+- + self.language.labels[name] + '</p>\n')
+-
+- def depart_admonition(self):
+- raise NotImplementedError, node.astext()
+- self.body.append('</div>\n')
+-
+- def visit_attention(self, node):
+- self.visit_admonition(node, 'attention')
+-
+- def depart_attention(self, node):
+- self.depart_admonition()
+-
+- def visit_author(self, node):
+- self._docinfo['author'] = node.astext()
+- raise nodes.SkipNode
+-
+- def depart_author(self, node):
+- pass
+-
+- def visit_authors(self, node):
+- self.body.append(self.comment('visit_authors'))
+-
+- def depart_authors(self, node):
+- self.body.append(self.comment('depart_authors'))
+-
+- def visit_block_quote(self, node):
+- #self.body.append(self.comment('visit_block_quote'))
+- # BUG/HACK: indent alway uses the _last_ indention,
+- # thus we need two of them.
+- self.indent(BLOCKQOUTE_INDENT)
+- self.indent(0)
+-
+- def depart_block_quote(self, node):
+- #self.body.append(self.comment('depart_block_quote'))
+- self.dedent()
+- self.dedent()
+-
+- def visit_bullet_list(self, node):
+- self.list_start(node)
+-
+- def depart_bullet_list(self, node):
+- self.list_end()
+-
+- def visit_caption(self, node):
+- raise NotImplementedError, node.astext()
+- self.body.append(self.starttag(node, 'p', '',
CLASS='caption'))
+-
+- def depart_caption(self, node):
+- raise NotImplementedError, node.astext()
+- self.body.append('</p>\n')
+-
+- def visit_caution(self, node):
+- self.visit_admonition(node, 'caution')
+-
+- def depart_caution(self, node):
+- self.depart_admonition()
+-
+- def visit_citation(self, node):
+- raise NotImplementedError, node.astext()
+- self.body.append(self.starttag(node, 'table', CLASS='citation',
+- frame="void", rules="none"))
+- self.body.append('<colgroup><col class="label"
/><col /></colgroup>\n'
+- '<col />\n'
+- '<tbody valign="top">\n'
+- '<tr>')
+- self.footnote_backrefs(node)
+-
+- def depart_citation(self, node):
+- raise NotImplementedError, node.astext()
+- self.body.append('</td></tr>\n'
+- '</tbody>\n</table>\n')
+-
+- def visit_citation_reference(self, node):
+- raise NotImplementedError, node.astext()
+- href = ''
+- if node.has_key('refid'):
+- href = '#' + node['refid']
+- elif node.has_key('refname'):
+- href = '#' + self.document.nameids[node['refname']]
+- self.body.append(self.starttag(node, 'a', '[', href=href,
+- CLASS='citation-reference'))
+-
+- def depart_citation_reference(self, node):
+- raise NotImplementedError, node.astext()
+- self.body.append(']</a>')
+-
+- def visit_classifier(self, node):
+- raise NotImplementedError, node.astext()
+- self.body.append(' <span
class="classifier-delimiter">:</span> ')
+- self.body.append(self.starttag(node, 'span', '',
CLASS='classifier'))
+-
+- def depart_classifier(self, node):
+- raise NotImplementedError, node.astext()
+- self.body.append('</span>')
+-
+- def visit_colspec(self, node):
+- self.colspecs.append(node)
+-
+- def depart_colspec(self, node):
+- pass
+-
+- def write_colspecs(self):
+- self.body.append("%s.\n" % ('L '*len(self.colspecs)))
+-
+- def visit_comment(self, node,
+- sub=re.compile('-(?=-)').sub):
+- self.body.append(self.comment(node.astext()))
+- raise nodes.SkipNode
+-
+- def visit_contact(self, node):
+- self.visit_docinfo_item(node, 'contact')
+-
+- def depart_contact(self, node):
+- self.depart_docinfo_item()
+-
+- def visit_copyright(self, node):
+- self._docinfo['copyright'] = node.astext()
+- raise nodes.SkipNode
+-
+- def visit_danger(self, node):
+- self.visit_admonition(node, 'danger')
+-
+- def depart_danger(self, node):
+- self.depart_admonition()
+-
+- def visit_date(self, node):
+- self._docinfo['date'] = node.astext()
+- raise nodes.SkipNode
+-
+- def visit_decoration(self, node):
+- pass
+-
+- def depart_decoration(self, node):
+- pass
+-
+- def visit_definition(self, node):
+- self.body.append(self.defs['definition'][0])
+-
+- def depart_definition(self, node):
+- self.body.append(self.defs['definition'][1])
+-
+- def visit_definition_list(self, node):
+- self.indent(DEFINITION_LIST_INDENT)
+-
+- def depart_definition_list(self, node):
+- self.dedent()
+-
+- def visit_definition_list_item(self, node):
+- self.body.append(self.defs['definition_list_item'][0])
+-
+- def depart_definition_list_item(self, node):
+- self.body.append(self.defs['definition_list_item'][1])
+-
+- def visit_description(self, node):
+- self.body.append(self.defs['description'][0])
+-
+- def depart_description(self, node):
+- self.body.append(self.defs['description'][1])
+-
+- def visit_docinfo(self, node):
+- self._in_docinfo = 1
+-
+- def depart_docinfo(self, node):
+- self._in_docinfo = None
+- # TODO nothing should be written before this
+- self.append_header()
+-
+- def visit_docinfo_item(self, node, name):
+- self.body.append(self.comment('%s: ' % self.language.labels[name]))
+- if len(node):
+- return
+- if isinstance(node[0], nodes.Element):
+- node[0].set_class('first')
+- if isinstance(node[0], nodes.Element):
+- node[-1].set_class('last')
+-
+- def depart_docinfo_item(self):
+- pass
+-
+- def visit_doctest_block(self, node):
+- raise NotImplementedError, node.astext()
+- self.body.append(self.starttag(node, 'pre',
CLASS='doctest-block'))
+-
+- def depart_doctest_block(self, node):
+- raise NotImplementedError, node.astext()
+- self.body.append('\n</pre>\n')
+-
+- def visit_document(self, node):
+- self.body.append(self.comment(self.document_start).lstrip())
+- # writing header is postboned
+- self.header_written = 0
+-
+- def depart_document(self, node):
+- if self._docinfo['author']:
+- self.body.append('\n.SH AUTHOR\n%s\n'
+- % self._docinfo['author'])
+- if self._docinfo['copyright']:
+- self.body.append('\n.SH COPYRIGHT\n%s\n'
+- % self._docinfo['copyright'])
+- self.body.append(
+- self.comment(
+- 'Generated by docutils manpage writer on %s.\n'
+- % (time.strftime('%Y-%m-%d %H:%M')) ) )
+-
+- def visit_emphasis(self, node):
+- self.body.append(self.defs['emphasis'][0])
+-
+- def depart_emphasis(self, node):
+- self.body.append(self.defs['emphasis'][1])
+-
+- def visit_entry(self, node):
+- # BUG entries have to be on one line separated by tab force it.
+- self.context.append(len(self.body))
+- self._in_entry = 1
+-
+- def depart_entry(self, node):
+- start = self.context.pop()
+- self._active_table.append_cell(self.body[start:])
+- del self.body[start:]
+- self._in_entry = 0
+-
+- def visit_enumerated_list(self, node):
+- self.list_start(node)
+-
+- def depart_enumerated_list(self, node):
+- self.list_end()
+-
+- def visit_error(self, node):
+- self.visit_admonition(node, 'error')
+-
+- def depart_error(self, node):
+- self.depart_admonition()
+-
+- def visit_field(self, node):
+- #self.body.append(self.comment('visit_field'))
+- pass
+-
+- def depart_field(self, node):
+- #self.body.append(self.comment('depart_field'))
+- pass
+-
+- def visit_field_body(self, node):
+- #self.body.append(self.comment('visit_field_body'))
+- if self._in_docinfo:
+- self._docinfo[
+- self._field_name.lower().replace(" ","_")] =
node.astext()
+- raise nodes.SkipNode
+-
+- def depart_field_body(self, node):
+- pass
+-
+- def visit_field_list(self, node):
+- self.indent(FIELD_LIST_INDENT)
+-
+- def depart_field_list(self, node):
+- self.dedent('depart_field_list')
+-
+-
+- def visit_field_name(self, node):
+- if self._in_docinfo:
+- self._field_name = node.astext()
+- raise nodes.SkipNode
+- else:
+- self.body.append(self.defs['field_name'][0])
+-
+- def depart_field_name(self, node):
+- self.body.append(self.defs['field_name'][1])
+-
+- def visit_figure(self, node):
+- raise NotImplementedError, node.astext()
+-
+- def depart_figure(self, node):
+- raise NotImplementedError, node.astext()
+-
+- def visit_footer(self, node):
+- raise NotImplementedError, node.astext()
+-
+- def depart_footer(self, node):
+- raise NotImplementedError, node.astext()
+- start = self.context.pop()
+- footer = (['<hr class="footer"/>\n',
+- self.starttag(node, 'div', CLASS='footer')]
+- + self.body[start:] + ['</div>\n'])
+- self.body_suffix[:0] = footer
+- del self.body[start:]
+-
+- def visit_footnote(self, node):
+- raise NotImplementedError, node.astext()
+- self.body.append(self.starttag(node, 'table', CLASS='footnote',
+- frame="void", rules="none"))
+- self.body.append('<colgroup><col class="label"
/><col /></colgroup>\n'
+- '<tbody valign="top">\n'
+- '<tr>')
+- self.footnote_backrefs(node)
+-
+- def footnote_backrefs(self, node):
+- raise NotImplementedError, node.astext()
+- if self.settings.footnote_backlinks and node.hasattr('backrefs'):
+- backrefs = node['backrefs']
+- if len(backrefs) == 1:
+- self.context.append('')
+- self.context.append('<a class="fn-backref"
href="#%s" '
+- 'name="%s">' % (backrefs[0],
node['id']))
+- else:
+- i = 1
+- backlinks = []
+- for backref in backrefs:
+- backlinks.append('<a class="fn-backref"
href="#%s">%s</a>'
+- % (backref, i))
+- i += 1
+- self.context.append('<em>(%s)</em> ' % ',
'.join(backlinks))
+- self.context.append('<a name="%s">' %
node['id'])
+- else:
+- self.context.append('')
+- self.context.append('<a name="%s">' %
node['id'])
+-
+- def depart_footnote(self, node):
+- raise NotImplementedError, node.astext()
+- self.body.append('</td></tr>\n'
+- '</tbody>\n</table>\n')
+-
+- def visit_footnote_reference(self, node):
+- raise NotImplementedError, node.astext()
+- href = ''
+- if node.has_key('refid'):
+- href = '#' + node['refid']
+- elif node.has_key('refname'):
+- href = '#' + self.document.nameids[node['refname']]
+- format = self.settings.footnote_references
+- if format == 'brackets':
+- suffix = '['
+- self.context.append(']')
+- elif format == 'superscript':
+- suffix = '<sup>'
+- self.context.append('</sup>')
+- else: # shouldn't happen
+- suffix = '???'
+- self.content.append('???')
+- self.body.append(self.starttag(node, 'a', suffix, href=href,
+- CLASS='footnote-reference'))
+-
+- def depart_footnote_reference(self, node):
+- raise NotImplementedError, node.astext()
+- self.body.append(self.context.pop() + '</a>')
+-
+- def visit_generated(self, node):
+- pass
+-
+- def depart_generated(self, node):
+- pass
+-
+- def visit_header(self, node):
+- raise NotImplementedError, node.astext()
+- self.context.append(len(self.body))
+-
+- def depart_header(self, node):
+- raise NotImplementedError, node.astext()
+- start = self.context.pop()
+- self.body_prefix.append(self.starttag(node, 'div',
CLASS='header'))
+- self.body_prefix.extend(self.body[start:])
+- self.body_prefix.append('<hr />\n</div>\n')
+- del self.body[start:]
+-
+- def visit_hint(self, node):
+- self.visit_admonition(node, 'hint')
+-
+- def depart_hint(self, node):
+- self.depart_admonition()
+-
+- def visit_image(self, node):
+- raise NotImplementedError, node.astext()
+- atts = node.attributes.copy()
+- atts['src'] = atts['uri']
+- del atts['uri']
+- if not atts.has_key('alt'):
+- atts['alt'] = atts['src']
+- if isinstance(node.parent, nodes.TextElement):
+- self.context.append('')
+- else:
+- self.body.append('<p>')
+- self.context.append('</p>\n')
+- self.body.append(self.emptytag(node, 'img', '', **atts))
+-
+- def depart_image(self, node):
+- raise NotImplementedError, node.astext()
+- self.body.append(self.context.pop())
+-
+- def visit_important(self, node):
+- self.visit_admonition(node, 'important')
+-
+- def depart_important(self, node):
+- self.depart_admonition()
+-
+- def visit_label(self, node):
+- raise NotImplementedError, node.astext()
+- self.body.append(self.starttag(node, 'td', '%s[' %
self.context.pop(),
+- CLASS='label'))
+-
+- def depart_label(self, node):
+- raise NotImplementedError, node.astext()
+- self.body.append(']</a></td><td>%s' %
self.context.pop())
+-
+- def visit_legend(self, node):
+- raise NotImplementedError, node.astext()
+- self.body.append(self.starttag(node, 'div', CLASS='legend'))
+-
+- def depart_legend(self, node):
+- raise NotImplementedError, node.astext()
+- self.body.append('</div>\n')
+-
+- def visit_line_block(self, node):
+- self.body.append('\n')
+-
+- def depart_line_block(self, node):
+- self.body.append('\n')
+-
+- def visit_line(self, node):
+- pass
+-
+- def depart_line(self, node):
+- self.body.append('\n.br\n')
+-
+- def visit_list_item(self, node):
+- # man 7 man argues to use ".IP" instead of ".TP"
+- self.body.append('\n.IP %s %d\n' % (
+- self._list_char[-1].next(),
+- self._list_char[-1].get_width(),) )
+-
+- def depart_list_item(self, node):
+- pass
+-
+- def visit_literal(self, node):
+- self.body.append(self.defs['literal'][0])
+-
+- def depart_literal(self, node):
+- self.body.append(self.defs['literal'][1])
+-
+- def visit_literal_block(self, node):
+- self.body.append(self.defs['literal_block'][0])
+-
+- def depart_literal_block(self, node):
+- self.body.append(self.defs['literal_block'][1])
+-
+- def visit_meta(self, node):
+- raise NotImplementedError, node.astext()
+- self.head.append(self.emptytag(node, 'meta', **node.attributes))
+-
+- def depart_meta(self, node):
+- pass
+-
+- def visit_note(self, node):
+- self.visit_admonition(node, 'note')
+-
+- def depart_note(self, node):
+- self.depart_admonition()
+-
+- def indent(self, by=0.5):
+- # if we are in a section ".SH" there already is a .RS
+- #self.body.append('\n[[debug: listchar: %r]]\n' % map(repr,
self._list_char))
+- #self.body.append('\n[[debug: indent %r]]\n' % self._indent)
+- step = self._indent[-1]
+- self._indent.append(by)
+- self.body.append(self.defs['indent'][0] % step)
+-
+- def dedent(self, name=''):
+- #self.body.append('\n[[debug: dedent %s %r]]\n' % (name, self._indent))
+- self._indent.pop()
+- self.body.append(self.defs['indent'][1])
+-
+- def visit_option_list(self, node):
+- self.indent(OPTION_LIST_INDENT)
+-
+- def depart_option_list(self, node):
+- self.dedent()
+-
+- def visit_option_list_item(self, node):
+- # one item of the list
+- self.body.append(self.defs['option_list_item'][0])
+-
+- def depart_option_list_item(self, node):
+- self.body.append(self.defs['option_list_item'][1])
+-
+- def visit_option_group(self, node):
+- # as one option could have several forms it is a group
+- # options without parameter bold only, .B, -v
+- # options with parameter bold italic, .BI, -f file
+-
+- # we do not know if .B or .BI
+- self.context.append('.B') # blind guess
+- self.context.append(len(self.body)) # to be able to insert later
+- self.context.append(0) # option counter
+-
+- def depart_option_group(self, node):
+- self.context.pop() # the counter
+- start_position = self.context.pop()
+- text = self.body[start_position:]
+- del self.body[start_position:]
+- self.body.append('\n%s%s' % (self.context.pop(),
''.join(text)))
+-
+- def visit_option(self, node):
+- # each form of the option will be presented separately
+- if self.context[-1]>0:
+- self.body.append(' ,')
+- if self.context[-3] == '.BI':
+- self.body.append('\\')
+- self.body.append(' ')
+-
+- def depart_option(self, node):
+- self.context[-1] += 1
+-
+- def visit_option_string(self, node):
+- # do not know if .B or .BI
+- pass
+-
+- def depart_option_string(self, node):
+- pass
+-
+- def visit_option_argument(self, node):
+- self.context[-3] = '.BI' # bold/italic alternate
+- if node['delimiter'] != ' ':
+- self.body.append('\\fn%s ' % node['delimiter'] )
+- elif self.body[len(self.body)-1].endswith('='):
+- # a blank only means no blank in output, just changing font
+- self.body.append(' ')
+- else:
+- # backslash blank blank
+- self.body.append('\\ ')
+-
+- def depart_option_argument(self, node):
+- pass
+-
+- def visit_organization(self, node):
+- raise NotImplementedError, node.astext()
+- self.visit_docinfo_item(node, 'organization')
+-
+- def depart_organization(self, node):
+- raise NotImplementedError, node.astext()
+- self.depart_docinfo_item()
+-
+- def visit_paragraph(self, node):
+- # BUG every but the first paragraph in a list must be intended
+- # TODO .PP or new line
+- return
+-
+- def depart_paragraph(self, node):
+- # TODO .PP or an empty line
+- if not self._in_entry:
+- self.body.append('\n\n')
+-
+- def visit_problematic(self, node):
+- self.body.append(self.defs['problematic'][0])
+-
+- def depart_problematic(self, node):
+- self.body.append(self.defs['problematic'][1])
+-
+- def visit_raw(self, node):
+- if node.get('format') == 'manpage':
+- self.body.append(node.astext())
+- # Keep non-manpage raw text out of output:
+- raise nodes.SkipNode
+-
+- def visit_reference(self, node):
+- """E.g. link or email address."""
+- self.body.append(self.defs['reference'][0])
+-
+- def depart_reference(self, node):
+- self.body.append(self.defs['reference'][1])
+-
+- def visit_revision(self, node):
+- self.visit_docinfo_item(node, 'revision')
+-
+- def depart_revision(self, node):
+- self.depart_docinfo_item()
+-
+- def visit_row(self, node):
+- self._active_table.new_row()
+-
+- def depart_row(self, node):
+- pass
+-
+- def visit_section(self, node):
+- self.section_level += 1
+-
+- def depart_section(self, node):
+- self.section_level -= 1
+-
+- def visit_status(self, node):
+- raise NotImplementedError, node.astext()
+- self.visit_docinfo_item(node, 'status', meta=None)
+-
+- def depart_status(self, node):
+- self.depart_docinfo_item()
+-
+- def visit_strong(self, node):
+- self.body.append(self.defs['strong'][1])
+-
+- def depart_strong(self, node):
+- self.body.append(self.defs['strong'][1])
+-
+- def visit_substitution_definition(self, node):
+- """Internal only."""
+- raise nodes.SkipNode
+-
+- def visit_substitution_reference(self, node):
+- self.unimplemented_visit(node)
+-
+- def visit_subtitle(self, node):
+- self._docinfo["subtitle"] = node.astext()
+- raise nodes.SkipNode
+-
+- def visit_system_message(self, node):
+- # TODO add report_level
+- #if node['level'] <
self.document.reporter['writer'].report_level:
+- # Level is too low to display:
+- # raise nodes.SkipNode
+- self.body.append('\.SH system-message\n')
+- attr = {}
+- backref_text = ''
+- if node.hasattr('id'):
+- attr['name'] = node['id']
+- if node.hasattr('line'):
+- line = ', line %s' % node['line']
+- else:
+- line = ''
+- self.body.append('System Message: %s/%s (%s:%s)\n'
+- % (node['type'], node['level'],
node['source'], line))
+-
+- def depart_system_message(self, node):
+- self.body.append('\n')
+-
+- def visit_table(self, node):
+- self._active_table = Table()
+-
+- def depart_table(self, node):
+- self.body.append(self._active_table.astext())
+- self._active_table = None
+-
+- def visit_target(self, node):
+- self.body.append(self.comment('visit_target'))
+- #self.body.append(self.defs['target'][0])
+- #self.body.append(node['refuri'])
+-
+- def depart_target(self, node):
+- self.body.append(self.comment('depart_target'))
+- #self.body.append(self.defs['target'][1])
+-
+- def visit_tbody(self, node):
+- pass
+-
+- def depart_tbody(self, node):
+- pass
+-
+- def visit_term(self, node):
+- self.body.append(self.defs['term'][0])
+-
+- def depart_term(self, node):
+- self.body.append(self.defs['term'][1])
+-
+- def visit_tgroup(self, node):
+- pass
+-
+- def depart_tgroup(self, node):
+- pass
+-
+- def visit_thead(self, node):
+- raise NotImplementedError, node.astext()
+- self.write_colspecs()
+- self.body.append(self.context.pop()) # '</colgroup>\n'
+- # There may or may not be a <thead>; this is for <tbody> to use:
+- self.context.append('')
+- self.body.append(self.starttag(node, 'thead', valign='bottom'))
+-
+- def depart_thead(self, node):
+- raise NotImplementedError, node.astext()
+- self.body.append('</thead>\n')
+-
+- def visit_tip(self, node):
+- self.visit_admonition(node, 'tip')
+-
+- def depart_tip(self, node):
+- self.depart_admonition()
+-
+- def visit_title(self, node):
+- if isinstance(node.parent, nodes.topic):
+- self.body.append(self.comment('topic-title'))
+- elif isinstance(node.parent, nodes.sidebar):
+- self.body.append(self.comment('sidebar-title'))
+- elif isinstance(node.parent, nodes.admonition):
+- self.body.append(self.comment('admonition-title'))
+- elif self.section_level == 0:
+- # document title for .TH
+- self._docinfo['title'] = node.astext()
+- raise nodes.SkipNode
+- elif self.section_level == 1:
+- self.body.append('\n.SH ')
+- else:
+- self.body.append('\n.SS ')
+-
+- def depart_title(self, node):
+- self.body.append('\n')
+-
+- def visit_title_reference(self, node):
+- """inline citation reference"""
+- self.body.append(self.defs['title_reference'][0])
+-
+- def depart_title_reference(self, node):
+- self.body.append(self.defs['title_reference'][1])
+-
+- def visit_topic(self, node):
+- self.body.append(self.comment('topic: '+node.astext()))
+- raise nodes.SkipNode
+- ##self.topic_class = node.get('class')
+-
+- def depart_topic(self, node):
+- ##self.topic_class = ''
+- pass
+-
+- def visit_transition(self, node):
+- # .PP Begin a new paragraph and reset prevailing indent.
+- # .sp N leaves N lines of blank space.
+- # .ce centers the next line
+- self.body.append('\n.sp\n.ce\n----\n')
+-
+- def depart_transition(self, node):
+- self.body.append('\n.ce 0\n.sp\n')
+-
+- def visit_version(self, node):
+- self._docinfo["version"] = node.astext()
+- raise nodes.SkipNode
+-
+- def visit_warning(self, node):
+- self.visit_admonition(node, 'warning')
+-
+- def depart_warning(self, node):
+- self.depart_admonition()
+-
+- def unimplemented_visit(self, node):
+- raise NotImplementedError('visiting unimplemented node type: %s'
+- % node.__class__.__name__)
+-
+-# vim: set et ts=4 ai :
+-
+-description = ("Generates plain man. " + default_description)
+-
+-publish_cmdline(writer=Writer(), description=description)
+diff --git a/nemo-dropbox/serializeimages.py b/nemo-dropbox/serializeimages.py
+deleted file mode 100644
+index 26427d4f..00000000
+--- a/nemo-dropbox/serializeimages.py
++++ /dev/null
+@@ -1,27 +0,0 @@
+-import sys
+-import gtk
+-
+-import re
+-
+-def replace_many(src2dest, buf):
+- src_re = re.compile('|'.join(re.escape(word) for word in src2dest))
+-
+- def replace_repl(mo):
+- return src2dest[mo.group()]
+- return src_re.sub(replace_repl, buf)
+-
+-if __name__ == '__main__':
+- pixbuf64 =
gtk.gdk.pixbuf_new_from_file("data/icons/hicolor/64x64/apps/dropbox.png")
+- pixbuf16 =
gtk.gdk.pixbuf_new_from_file("data/icons/hicolor/16x16/apps/dropbox.png")
+- src2dest = {'@PACKAGE_VERSION@': sys.argv[1],
+- '@DESKTOP_FILE_DIR@': sys.argv[2],
+- '@IMAGEDATA64@': ("gtk.gdk.pixbuf_new_from_data(%r,
gtk.gdk.COLORSPACE_RGB, %r, %r, %r, %r, %r)" %
+- (pixbuf64.get_pixels(), pixbuf64.get_has_alpha(),
pixbuf64.get_bits_per_sample(),
+- pixbuf64.get_width(), pixbuf64.get_height(),
pixbuf64.get_rowstride())),
+- '@IMAGEDATA16@': ("gtk.gdk.pixbuf_new_from_data(%r,
gtk.gdk.COLORSPACE_RGB, %r, %r, %r, %r, %r)" %
+- (pixbuf16.get_pixels(), pixbuf16.get_has_alpha(),
pixbuf16.get_bits_per_sample(),
+- pixbuf16.get_width(), pixbuf16.get_height(),
pixbuf16.get_rowstride())),
+- }
+-
+- buf = sys.stdin.read()
+- sys.stdout.write(replace_many(src2dest, buf))
diff --git a/sources b/sources
index 4478b2c..a86a668 100644
--- a/sources
+++ b/sources
@@ -1 +1 @@
-7adfc83cc8ea942f0707429543a71d52 nemo-extensions-4.2.0.tar.gz
+SHA512 (nemo-extensions-4.4.0.tar.gz) =
5ca2cf57950986fdc0f3ed5962fa8ee914ac34bf46c702a0fe0323169060ae8f300406bcb5194fbf88b50aa41e6deb8fa2ee32fdf14eedb7fd9553b10240a2c9