3
# Copyright 2008 Evenflow, Inc.
6
# Dropbox frontend script
7
# This file is part of nautilus-dropbox @PACKAGE_VERSION@.
9
# nautilus-dropbox is free software: you can redistribute it and/or modify
10
# it under the terms of the GNU General Public License as published by
11
# the Free Software Foundation, either version 3 of the License, or
12
# (at your option) any later version.
14
# nautilus-dropbox is distributed in the hope that it will be useful,
15
# but WITHOUT ANY WARRANTY; without even the implied warranty of
16
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
# GNU General Public License for more details.
19
# You should have received a copy of the GNU General Public License
20
# along with nautilus-dropbox. If not, see <http://www.gnu.org/licenses/>.
22
from __future__ import with_statement
46
from contextlib import closing, contextmanager
47
from posixpath import curdir, sep, pardir, join, abspath, commonprefix
49
INFO = u"Dropbox is the easiest way to share and store your files online. Want to learn more? Head to"
50
LINK = u"http://www.dropbox.com/"
51
WARNING = u"In order to use Dropbox, you must download the proprietary daemon."
52
GPG_WARNING = u"Note: python-gpgme is not installed, we will not be able to verify binary signatures."
54
DOWNLOADING = u"Downloading Dropbox... %d%%"
55
UNPACKING = u"Unpacking Dropbox... %d%%"
57
PARENT_DIR = os.path.expanduser("~")
58
DROPBOXD_PATH = "%s/.dropbox-dist/dropboxd" % PARENT_DIR
59
DESKTOP_FILE = u"@DESKTOP_FILE_DIR@/dropbox.desktop"
61
enc = locale.getpreferredencoding()
63
# Available from http://linux.dropbox.com/fedora/rpm-public-key.asc
64
DROPBOX_PUBLIC_KEY = """
65
-----BEGIN PGP PUBLIC KEY BLOCK-----
68
mQENBEt0ibEBCACv4hZRPqwtpU6z8+BB5YZU1a3yjEvg2W68+a6hEwxtCa2U++4dzQ+7EqaU
69
q5ybQnwtbDdpFpsOi9x31J+PCpufPUfIG694/0rlEpmzl2GWzY8NqfdBFGGm/SPSSwvKbeNc
70
FMRLu5neo7W9kwvfMbGjHmvUbzBUVpCVKD0OEEf1q/Ii0Qcekx9CMoLvWq7ZwNHEbNnij7ec
71
nvwNlE2MxNsOSJj+hwZGK+tM19kuYGSKw4b5mR8IyThlgiSLIfpSBh1n2KX+TDdk9GR+57TY
72
vlRu6nTPu98P05IlrrCP+KF0hYZYOaMvQs9Rmc09tc/eoQlN0kkaBWw9Rv/dvLVc0aUXABEB
73
AAG0MURyb3Bib3ggQXV0b21hdGljIFNpZ25pbmcgS2V5IDxsaW51eEBkcm9wYm94LmNvbT6J
74
ATYEEwECACAFAkt0ibECGwMGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAKCRD8kYszUESRLi/z
75
B/wMscEa15rS+0mIpsORknD7kawKwyda+LHdtZc0hD/73QGFINR2P23UTol/R4nyAFEuYNsF
76
0C4IAD6y4pL49eZ72IktPrr4H27Q9eXhNZfJhD7BvQMBx75L0F5gSQwuC7GdYNlwSlCD0AAh
77
Qbi70VBwzeIgITBkMQcJIhLvllYo/AKD7Gv9huy4RLaIoSeofp+2Q0zUHNPl/7zymOqu+5Ox
78
e1ltuJT/kd/8hU+N5WNxJTSaOK0sF1/wWFM6rWd6XQUP03VyNosAevX5tBo++iD1WY2/lFVU
79
JkvAvge2WFk3c6tAwZT/tKxspFy4M/tNbDKeyvr685XKJw9ei6GcOGHD
81
-----END PGP PUBLIC KEY BLOCK-----
86
def methodcaller(name, *args, **kwargs):
88
return getattr(obj, name)(*args, **kwargs)
91
def relpath(path, start=curdir):
92
"""Return a relative version of a path"""
95
raise ValueError("no path specified")
97
if type(start) is unicode:
98
start_list = unicode_abspath(start).split(sep)
100
start_list = abspath(start).split(sep)
102
if type(path) is unicode:
103
path_list = unicode_abspath(path).split(sep)
105
path_list = abspath(path).split(sep)
107
# Work out how much of the filepath is shared by start and path.
108
i = len(commonprefix([start_list, path_list]))
110
rel_list = [pardir] * (len(start_list)-i) + path_list[i:]
113
return join(*rel_list)
118
def console_print(st=u"", f=sys.stdout, linebreak=True):
120
assert type(st) is unicode
121
f.write(st.encode(enc))
122
if linebreak: f.write(os.linesep)
124
def console_flush(f=sys.stdout):
127
def yes_no_question(question):
129
console_print(question, linebreak=False)
130
console_print(u" [y/n] ", linebreak=False)
133
if text.lower().startswith("y"):
135
elif text.lower().startswith("n"):
138
console_print(u"Sorry, I didn't understand that. Please type yes or no.")
141
if sys.platform.lower().startswith('linux'):
142
arch = platform.machine()
143
if (arch[0] == 'i' and
144
arch[1].isdigit() and
147
elif arch == 'x86_64':
150
FatalVisibleError("Platform not supported")
151
return "lnx.%s" % plat
153
FatalVisibleError("Platform not supported")
155
def is_dropbox_running():
156
pidfile = os.path.expanduser("~/.dropbox/dropbox.pid")
159
with open(pidfile, "r") as f:
161
with open("/proc/%d/cmdline" % pid, "r") as f:
162
cmdline = f.read().lower()
166
return "dropbox" in cmdline
168
def unicode_abspath(path):
170
assert type(path) is unicode
171
# shouldn't pass unicode to this craphead, it appends with os.getcwd() which is always a str
172
return os.path.abspath(path.encode(sys.getfilesystemencoding())).decode(sys.getfilesystemencoding())
175
def gpgme_context(keys):
176
gpg_conf_contents = ''
177
_gpghome = tempfile.mkdtemp(prefix='tmp.gpghome')
180
os.environ['GNUPGHOME'] = _gpghome
181
fp = open(os.path.join(_gpghome, 'gpg.conf'), 'wb')
182
fp.write(gpg_conf_contents)
184
ctx = gpgme.Context()
187
for key_file in keys:
188
result = ctx.import_(key_file)
189
key = ctx.get_key(result.imports[0][0])
196
del os.environ['GNUPGHOME']
197
shutil.rmtree(_gpghome, ignore_errors=True)
199
def verify_signature(key_file, sig_file, plain_file):
200
with gpgme_context([key_file]) as ctx:
201
sigs = ctx.verify(sig_file, plain_file, None)
202
return sigs[0].status == None
204
def download_file_chunk(socket, buf, size):
206
with closing(socket) as f:
209
chunk = os.read(f.fileno(), 4096)
210
progress += len(chunk)
212
yield (progress, True)
216
if hasattr(e, 'errno') and e.errno == errno.EAGAIN:
217
# nothing left to read
218
yield (progress, False)
222
def download_uri_to_buffer(uri):
224
socket = urllib.urlopen(uri)
226
FatalVisibleError("Trouble connecting to Dropbox servers. Maybe your internet connection is down, or you need to set your http_proxy environment variable.")
228
fcntl.fcntl(socket, fcntl.F_SETFL, os.O_NONBLOCK)
229
size = int(socket.info()['content-length'])
231
buf = StringIO.StringIO()
232
download_chunk = download_file_chunk(socket, buf, size)
234
for _ in download_chunk:
240
# This sets a custom User-Agent
241
class DropboxURLopener(urllib.FancyURLopener):
242
version = "DropboxLinuxDownloader/@PACKAGE_VERSION@"
243
urllib._urlopener = DropboxURLopener()
245
class DownloadState(object):
248
self.socket = urllib.urlopen("http://www.dropbox.com/download?plat=%s" % plat())
250
FatalVisibleError("Trouble connecting to Dropbox servers. Maybe your internet connection is down, or you need to set your http_proxy environment variable")
252
fcntl.fcntl(self.socket, fcntl.F_SETFL, os.O_NONBLOCK)
253
self.size = int(self.socket.info()['content-length'])
255
self.local_file = StringIO.StringIO()
256
self.download_chunk = download_file_chunk(self.socket, self.local_file, self.size)
259
return self.download_chunk
263
signature = download_uri_to_buffer("http://www.dropbox.com/download?plat=%s&signature=1" % plat())
265
self.local_file.seek(0)
267
if not verify_signature(StringIO.StringIO(DROPBOX_PUBLIC_KEY), signature, self.local_file):
268
FatalVisibleError("Downloaded binary does not match Dropbox signature, aborting install.")
270
self.local_file.seek(0)
271
archive = tarfile.open(fileobj=self.local_file, mode='r:gz')
272
total_members = len(archive.getmembers())
273
for i, member in enumerate(archive.getmembers()):
274
filename = os.path.join(PARENT_DIR, member.name)
275
if os.path.exists(filename) and not os.path.isdir(filename):
277
archive.extract(member, PARENT_DIR)
278
yield member.name, i, total_members
282
if not self.local_file.closed:
283
self.local_file.close()
285
def load_serialized_images():
286
global box_logo_pixbuf, window_icon
288
box_logo_pixbuf = @IMAGEDATA64@
289
window_icon = @IMAGEDATA16@
291
GUI_AVAILABLE = os.environ.get("DISPLAY", '')
302
load_serialized_images()
304
global FatalVisibleError
305
def FatalVisibleError(s):
306
error = gtk.MessageDialog(parent = None,
307
flags = gtk.DIALOG_MODAL,
308
type = gtk.MESSAGE_ERROR,
309
buttons = gtk.BUTTONS_OK,
311
error.set_title("Error")
316
def gtk_flush_events():
317
while gtk.events_pending():
320
class DownloadDialog(gtk.Dialog):
321
def handle_delete_event(self, wid, ev, data=None):
322
self.handle_cancel(wid)
324
def handle_dont_show_toggle(self, button, data=None):
325
reroll_autostart(not button.get_active())
327
def handle_cancel(self, button):
329
gobject.source_remove(self.watch)
331
self.download.cancel()
333
self.user_cancelled = True
335
def handle_ok(self, button):
338
self.download = DownloadState()
339
self.one_chunk = self.download.copy_data()
340
self.watch = gobject.io_add_watch(self.download.socket,
345
self.handle_data_waiting)
347
self.dont_show_again_align.hide()
350
def update_progress(self, text, fraction):
351
self.progress.set_text(text % int(fraction*100))
352
self.progress.set_fraction(fraction)
355
def handle_data_waiting(self, fd, condition):
356
if condition == gobject.IO_HUP:
357
FatalVisibleError("Connection to server unexpectedly closed.")
358
elif condition == gobject.IO_ERR:
359
FatalVisibleError("Unexpected error occurred with download.")
362
progress, status = self.one_chunk.next()
365
self.update_progress(DOWNLOADING, float(progress)/self.download.size)
366
except StopIteration:
367
self.update_progress(DOWNLOADING, 1.0)
368
self.unpack_dropbox()
371
self.update_progress(DOWNLOADING, float(progress)/self.download.size)
374
def unpack_dropbox(self):
375
one_member = self.download.unpack()
378
name, i, total = one_member.next()
379
self.update_progress(UNPACKING, float(i)/total)
380
except StopIteration:
381
self.update_progress(UNPACKING, 1.0)
384
def mouse_down(self, widget, event):
386
self.clicked_link = True
388
def mouse_up(self, widget, event):
389
if self.clicked_link:
390
webbrowser.open(LINK)
391
self.clicked_link = False
393
def label_motion(self, widget, event):
394
offx, offy = self.label.get_layout_offsets()
395
layout = self.label.get_layout()
396
index = layout.xy_to_index(int((offx+event.x)*pango.SCALE),
397
int((offy+event.y)*pango.SCALE))[0]
398
link_index = layout.get_text().find(LINK)
399
if index >= link_index and index < link_index+len(LINK):
401
self.label_box.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.HAND2))
403
self.hovering = False
404
self.label_box.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.ARROW))
408
super(DownloadDialog, self).__init__(parent = None,
409
title = "Dropbox Installation")
413
self.hovering = False
414
self.clicked_link = False
415
self.user_cancelled = False
417
self.ok = ok = gtk.Button(stock=gtk.STOCK_OK)
418
ok.connect('clicked', self.handle_ok)
419
self.action_area.add(ok)
422
cancel = gtk.Button(stock=gtk.STOCK_CANCEL)
423
cancel.connect('clicked', self.handle_cancel)
424
self.action_area.add(cancel)
427
self.connect('delete_event', self.handle_delete_event)
429
self.box_logo = gtk.image_new_from_pixbuf(box_logo_pixbuf)
432
self.set_icon(window_icon)
434
self.progress = gtk.ProgressBar()
435
self.progress.set_property('width-request', 300)
437
self.label = gtk.Label()
438
GPG_WARNING_MSG = (u"\n\n" + GPG_WARNING) if not gpgme else u""
439
self.label.set_markup('%s <span foreground="#000099" underline="single" weight="bold">%s</span>\n\n%s%s' % (INFO, LINK, WARNING, GPG_WARNING_MSG))
440
self.label.set_line_wrap(True)
441
self.label.set_property('width-request', 300)
444
self.label_box = gtk.EventBox()
445
self.label_box.add(self.label)
446
self.label_box.connect("button-release-event", self.mouse_up)
447
self.label_box.connect("button-press-event", self.mouse_down)
448
self.label_box.connect("motion-notify-event", self.label_motion)
450
self.label_box.show()
451
def on_realize(widget):
452
self.label_box.add_events(gtk.gdk.POINTER_MOTION_MASK)
453
self.label_box.connect("realize", on_realize)
455
self.hbox = gtk.HBox(spacing=10)
456
self.hbox.set_property('border-width',10)
457
self.hbox.pack_start(self.box_logo, False, False)
458
self.hbox.pack_start(self.label_box, False, False)
459
self.hbox.pack_start(self.progress, False, False)
462
self.vbox.add(self.hbox)
465
if can_reroll_autostart():
466
dont_show_again = gtk.CheckButton("_Don't show this again")
467
dont_show_again.connect('toggled', self.handle_dont_show_toggle)
468
dont_show_again.show()
470
self.dont_show_again_align = gtk.Alignment(xalign=1.0, yalign=0.0, xscale=0.0, yscale=0.0)
471
self.dont_show_again_align.add(dont_show_again)
472
self.dont_show_again_align.show()
475
hbox.set_property('border-width', 10)
476
hbox.pack_start(self.dont_show_again_align, True, True)
481
self.set_resizable(False)
484
traceback.print_exc()
488
dialog = DownloadDialog()
491
if dialog.user_cancelled:
492
raise Exception("user cancelled download!!!")
495
global FatalVisibleError
496
def FatalVisibleError(s):
497
console_print(u"\nError: %s" % s, f=sys.stderr)
505
erase_to_start = ESC+"[1K"
506
write = sys.stdout.write
507
flush = sys.stdout.flush
509
last_progress = [None, None]
510
def setprogress(text, frac):
511
if last_progress == [text, frac]:
513
if sys.stdout.isatty():
514
write(erase_to_start)
516
console_print(text % int(100*frac), linebreak=not sys.stdout.isatty())
517
if sys.stdout.isatty():
519
last_progress[0], last_progress[1] = text, frac
522
if sys.stdout.isatty():
525
console_print(u"%s %s\n" % (INFO, LINK))
526
GPG_WARNING_MSG = (u"\n%s" % GPG_WARNING) if not gpgme else u""
528
if not yes_no_question("%s%s" % (WARNING, GPG_WARNING_MSG)):
531
download = DownloadState()
532
one_chunk = download.copy_data()
536
progress = one_chunk.next()[0]
537
setprogress(DOWNLOADING, float(progress)/download.size)
538
except StopIteration:
539
setprogress(DOWNLOADING, 1.0)
543
one_member = download.unpack()
547
name, i, total = one_member.next()
548
setprogress(UNPACKING, float(i)/total)
549
except StopIteration:
550
setprogress(UNPACKING, 1.0)
554
class CommandTicker(threading.Thread):
556
threading.Thread.__init__(self)
557
self.stop_event = threading.Event()
560
self.stop_event.set()
563
ticks = ['[. ]', '[.. ]', '[...]', '[ ..]', '[ .]', '[ ]']
567
self.stop_event.wait(0.25)
568
if self.stop_event.isSet(): break
573
sys.stderr.write("\r%s\r" % ticks[i])
579
class DropboxCommand(object):
580
class CouldntConnectError(Exception): pass
581
class BadConnectionError(Exception): pass
582
class EOFError(Exception): pass
583
class CommandError(Exception): pass
585
def __init__(self, timeout=5):
586
self.s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
587
self.s.settimeout(timeout)
589
self.s.connect(os.path.expanduser(u'~/.dropbox/command_socket'))
590
except socket.error, e:
591
raise DropboxCommand.CouldntConnectError()
592
self.f = self.s.makefile("r+", 4096)
598
def __readline(self):
600
toret = self.f.readline().decode('utf8').rstrip(u"\n")
601
except socket.error, e:
602
raise DropboxCommand.BadConnectionError()
604
raise DropboxCommand.EOFError()
608
# atttribute doesn't exist, i know what you want
609
def send_command(self, name, args):
610
self.f.write(name.encode('utf8'))
611
self.f.write(u"\n".encode('utf8'))
612
self.f.writelines((u"\t".join([k] + (list(v)
613
if hasattr(v, '__iter__') else
614
[v])) + u"\n").encode('utf8')
615
for k,v in args.iteritems())
616
self.f.write(u"done\n".encode('utf8'))
621
ticker_thread = CommandTicker()
622
ticker_thread.start()
624
# This is the potentially long-running call.
626
ok = self.__readline() == u"ok"
627
except KeyboardInterrupt:
628
raise DropboxCommand.BadConnectionError("Keyboard interruption detected")
630
# Tell the ticker to stop.
638
raise Exception(u"close this connection!")
640
line = self.__readline()
644
argval = line.split(u"\t")
645
toret[argval[0]] = argval[1:]
652
raise Exception(u"close this connection!")
654
line = self.__readline()
658
problems.append(line)
660
raise DropboxCommand.CommandError(u"\n".join(problems))
662
# this is the hotness, auto marshalling
663
def __getattr__(self, name):
665
return super(DropboxCommand, self).__getattr__(name)
667
def __spec_command(**kw):
668
return self.send_command(unicode(name), kw)
669
self.__setattr__(name, __spec_command)
670
return __spec_command
676
global commands, aliases
677
assert meth.__doc__, "All commands need properly formatted docstrings (even %r!!)" % meth
678
if hasattr(meth, 'im_func'): # bound method, if we ever have one
680
commands[meth.func_name] = meth
681
meth_aliases = [unicode(alias) for alias in aliases.iterkeys() if aliases[alias].func_name == meth.func_name]
683
meth.__doc__ += u"\nAliases: %s" % ",".join(meth_aliases)
688
global commands, aliases
689
assert name not in commands, "This alias is the name of a command."
694
def requires_dropbox_running(meth):
695
def newmeth(*n, **kw):
696
if is_dropbox_running():
697
return meth(*n, **kw)
699
console_print(u"Dropbox isn't running!")
700
newmeth.func_name = meth.func_name
701
newmeth.__doc__ = meth.__doc__
705
db_path = os.path.expanduser(DROPBOXD_PATH).encode(sys.getfilesystemencoding())
706
if os.access(db_path, os.X_OK):
707
f = open("/dev/null", "w")
708
# we don't reap the child because we're gonna die anyway, let init do it
709
a = subprocess.Popen([db_path], preexec_fn=os.setsid, cwd=os.path.expanduser("~"),
710
stderr=sys.stderr, stdout=f, close_fds=True)
715
for i in xrange(int(wait_for / interval)):
716
if is_dropbox_running():
718
# back off from connect for a while
725
# Extracted and modified from os.cmd.Cmd
726
def columnize(list, display_list=None, display_width=None):
728
console_print(u"<empty>")
731
non_unicode = [i for i in range(len(list)) if not (isinstance(list[i], unicode))]
733
raise TypeError, ("list[i] not a string for i in %s" %
734
", ".join(map(unicode, non_unicode)))
736
if not display_width:
737
d = os.popen('stty size', 'r').read().split()
739
display_width = int(d[1])
750
console_print(display_list[0])
753
for nrows in range(1, len(list)):
754
ncols = (size+nrows-1) // nrows
757
for col in range(ncols):
759
for row in range(nrows):
764
colwidth = max(colwidth, len(x))
765
colwidths.append(colwidth)
766
totwidth += colwidth + 2
767
if totwidth > display_width:
769
if totwidth <= display_width:
776
for row in range(nrows):
779
for col in range(ncols):
788
display_texts.append(y)
789
while texts and not texts[-1]:
791
original_texts = texts[:]
792
for col in range(len(texts)):
793
texts[col] = texts[col].ljust(colwidths[col])
794
line = u"%s" % " ".join(texts)
795
for i, text in enumerate(original_texts):
796
line = line.replace(text, display_texts[i])
803
u"""download latest version of dropbox
806
Downloads the latest version of dropbox.
811
@requires_dropbox_running
813
def filestatus(args):
814
u"""get current sync status of one or more files
815
dropbox filestatus [-l] [-a] [FILE]...
817
Prints the current status of each FILE.
820
-l --list prints out information in a format similar to ls. works best when your console supports color :)
821
-a --all do not ignore entries starting with .
825
oparser = optparse.OptionParser()
826
oparser.add_option("-l", "--list", action="store_true", dest="list")
827
oparser.add_option("-a", "--all", action="store_true", dest="all")
828
(options, args) = oparser.parse_args(args)
831
with closing(DropboxCommand()) as dc:
835
# Separate directories from files.
837
dirs, nondirs = [u"."], []
839
dirs, nondirs = [], []
843
(dirs if os.path.isdir(a) else nondirs).append(a.decode(enc))
844
except UnicodeDecodeError:
847
if len(dirs) == 0 and len(nondirs) == 0:
851
dirs.sort(key=methodcaller('lower'))
852
nondirs.sort(key=methodcaller('lower'))
854
# Gets a string representation for a path.
855
def path_to_string(file_path):
856
if not os.path.exists(file_path):
857
path = u"%s (File doesn't exist!)" % os.path.basename(file_path)
860
status = dc.icon_overlay_file_status(path=file_path).get(u'status', [None])[0]
861
except DropboxCommand.CommandError, e:
862
path = u"%s (%s)" % (os.path.basename(file_path), e)
865
env_term = os.environ.get('TERM','')
866
supports_color = (sys.stderr.isatty() and (
867
env_term.startswith('vt') or
868
env_term.startswith('linux') or
869
'xterm' in env_term or
874
# TODO: Test when you don't support color.
875
if not supports_color:
876
path = os.path.basename(file_path)
879
if status == u"up to date":
880
init, cleanup = "\x1b[32;1m", "\x1b[0m"
881
elif status == u"syncing":
882
init, cleanup = "\x1b[36;1m", "\x1b[0m"
883
elif status == u"unsyncable":
884
init, cleanup = "\x1b[41;1m", "\x1b[0m"
885
elif status == u"selsync":
886
init, cleanup = "\x1b[37;1m", "\x1b[0m"
888
init, cleanup = '', ''
890
path = os.path.basename(file_path)
891
return (path, u"%s%s%s" % (init, path, cleanup))
893
# Prints a directory.
894
def print_directory(name):
897
for subname in sorted(os.listdir(name), key=methodcaller('lower')):
898
if type(subname) != unicode:
901
if not options.all and subname[0] == u'.':
905
clean, formatted = path_to_string(unicode_abspath(os.path.join(name, subname)))
906
clean_paths.append(clean)
907
formatted_paths.append(formatted)
908
except (UnicodeEncodeError, UnicodeDecodeError), e:
911
columnize(clean_paths, formatted_paths)
914
if len(dirs) == 1 and len(nondirs) == 0:
915
print_directory(dirs[0])
917
nondir_formatted_paths = []
918
nondir_clean_paths = []
921
clean, formatted = path_to_string(unicode_abspath(name))
922
nondir_clean_paths.append(clean)
923
nondir_formatted_paths.append(formatted)
924
except (UnicodeEncodeError, UnicodeDecodeError), e:
927
if nondir_clean_paths:
928
columnize(nondir_clean_paths, nondir_formatted_paths)
930
if len(nondirs) == 0:
931
console_print(dirs[0] + u":")
932
print_directory(dirs[0])
937
console_print(name + u":")
938
print_directory(name)
940
except DropboxCommand.EOFError:
941
console_print(u"Dropbox daemon stopped.")
942
except DropboxCommand.BadConnectionError, e:
943
console_print(u"Dropbox isn't responding!")
946
args = [name for name in sorted(os.listdir(u"."), key=methodcaller('lower')) if type(name) == unicode]
947
indent = max(len(st)+1 for st in args)
951
if type(file) is not unicode:
952
file = file.decode(enc)
953
fp = unicode_abspath(file)
954
except (UnicodeEncodeError, UnicodeDecodeError), e:
956
if not os.path.exists(fp):
957
console_print(u"%-*s %s" % \
958
(indent, file+':', "File doesn't exist"))
962
status = dc.icon_overlay_file_status(path=fp).get(u'status', [u'unknown'])[0]
963
console_print(u"%-*s %s" % (indent, file+':', status))
964
except DropboxCommand.CommandError, e:
965
console_print(u"%-*s %s" % (indent, file+':', e))
966
except DropboxCommand.CouldntConnectError, e:
967
console_print(u"Dropbox isn't running!")
970
@requires_dropbox_running
972
u"""list directory contents with current sync status
975
This is an alias for filestatus -l
977
return filestatus(["-l"] + args)
980
@requires_dropbox_running
982
u"""get public url of a file in your dropbox
985
Prints out a public url for FILE.
988
console_print(puburl.__doc__,linebreak=False)
992
with closing(DropboxCommand()) as dc:
994
console_print(dc.get_public_link(path=unicode_abspath(args[0].decode(sys.getfilesystemencoding()))).get(u'link', [u'No Link'])[0])
995
except DropboxCommand.CommandError, e:
996
console_print(u"Couldn't get public url: " + str(e))
997
except DropboxCommand.BadConnectionError, e:
998
console_print(u"Dropbox isn't responding!")
999
except DropboxCommand.EOFError:
1000
console_print(u"Dropbox daemon stopped.")
1001
except DropboxCommand.CouldntConnectError, e:
1002
console_print(u"Dropbox isn't running!")
1005
@requires_dropbox_running
1007
u"""get current status of the dropboxd
1010
Prints out the current status of the Dropbox daemon.
1013
console_print(status.__doc__,linebreak=False)
1017
with closing(DropboxCommand()) as dc:
1019
lines = dc.get_dropbox_status()[u'status']
1021
console_print(u'Idle')
1026
console_print(u"Couldn't get status: daemon isn't responding")
1027
except DropboxCommand.CommandError, e:
1028
console_print(u"Couldn't get status: " + str(e))
1029
except DropboxCommand.BadConnectionError, e:
1030
console_print(u"Dropbox isn't responding!")
1031
except DropboxCommand.EOFError:
1032
console_print(u"Dropbox daemon stopped.")
1033
except DropboxCommand.CouldntConnectError, e:
1034
console_print(u"Dropbox isn't running!")
1038
u"""return whether dropbox is running
1041
Returns 1 if running 0 if not running.
1043
return int(is_dropbox_running())
1046
@requires_dropbox_running
1051
Stops the dropbox daemon.
1054
with closing(DropboxCommand()) as dc:
1056
dc.tray_action_hard_exit()
1057
except DropboxCommand.BadConnectionError, e:
1058
console_print(u"Dropbox isn't responding!")
1059
except DropboxCommand.EOFError:
1060
console_print(u"Dropbox daemon stopped.")
1061
except DropboxCommand.CouldntConnectError, e:
1062
console_print(u"Dropbox isn't running!")
1064
#returns true if link is necessary
1065
def grab_link_url_if_necessary():
1067
with closing(DropboxCommand()) as dc:
1069
link_url = dc.needs_link().get(u"link_url", None)
1070
if link_url is not None:
1071
console_print(u"To link this computer to a dropbox account, visit the following url:\n%s" % link_url[0])
1075
except DropboxCommand.CommandError, e:
1077
except DropboxCommand.BadConnectionError, e:
1078
console_print(u"Dropbox isn't responding!")
1079
except DropboxCommand.EOFError:
1080
console_print(u"Dropbox daemon stopped.")
1081
except DropboxCommand.CouldntConnectError, e:
1082
console_print(u"Dropbox isn't running!")
1085
@requires_dropbox_running
1087
u"""enables or disables LAN sync
1088
dropbox lansync [y/n]
1091
y dropbox will use LAN sync (default)
1092
n dropbox will not use LAN sync
1095
console_print(lansync.__doc__, linebreak=False)
1099
if s.startswith('y') or s.startswith('-y'):
1100
should_lansync = True
1101
elif s.startswith('n') or s.startswith('-n'):
1102
should_lansync = False
1104
should_lansync = None
1106
if should_lansync is None:
1107
console_print(lansync.__doc__,linebreak=False)
1109
with closing(DropboxCommand()) as dc:
1110
dc.set_lan_sync(lansync='enabled' if should_lansync else 'disabled')
1114
@requires_dropbox_running
1116
u"""ignores/excludes a directory from syncing
1117
dropbox exclude [list]
1118
dropbox exclude add [DIRECTORY] [DIRECTORY] ...
1119
dropbox exclude remove [DIRECTORY] [DIRECTORY] ...
1121
"list" prints a list of directories currently excluded from syncing.
1122
"add" adds one or more directories to the exclusion list, then resynchronizes Dropbox.
1123
"remove" removes one or more directories from the exclusion list, then resynchronizes Dropbox.
1124
With no arguments, executes "list".
1125
Any specified path must be within Dropbox.
1129
with closing(DropboxCommand()) as dc:
1131
lines = [relpath(path) for path in dc.get_ignore_set()[u'ignore_set']]
1134
console_print(u'No directories are being ignored.')
1136
console_print(u'Excluded: ')
1138
console_print(unicode(line))
1140
console_print(u"Couldn't get ignore set: daemon isn't responding")
1141
except DropboxCommand.CommandError, e:
1142
if e.args[0].startswith(u"No command exists by that name"):
1143
console_print(u"This version of the client does not support this command.")
1145
console_print(u"Couldn't get ignore set: " + str(e))
1146
except DropboxCommand.BadConnectionError, e:
1147
console_print(u"Dropbox isn't responding!")
1148
except DropboxCommand.EOFError:
1149
console_print(u"Dropbox daemon stopped.")
1150
except DropboxCommand.CouldntConnectError, e:
1151
console_print(u"Dropbox isn't running!")
1152
elif len(args) == 1 and args[0] == u"list":
1154
elif len(args) >= 2:
1155
sub_command = args[0]
1157
absolute_paths = [unicode_abspath(path.decode(sys.getfilesystemencoding())) for path in paths]
1158
if sub_command == u"add":
1160
with closing(DropboxCommand(timeout=None)) as dc:
1162
result = dc.ignore_set_add(paths=absolute_paths)
1163
if result[u"ignored"]:
1164
console_print(u"Excluded: ")
1165
lines = [relpath(path) for path in result[u"ignored"]]
1167
console_print(unicode(line))
1169
console_print(u"Couldn't add ignore path: daemon isn't responding")
1170
except DropboxCommand.CommandError, e:
1171
if e.args[0].startswith(u"No command exists by that name"):
1172
console_print(u"This version of the client does not support this command.")
1174
console_print(u"Couldn't get ignore set: " + str(e))
1175
except DropboxCommand.BadConnectionError, e:
1176
console_print(u"Dropbox isn't responding! [%s]" % e)
1177
except DropboxCommand.EOFError:
1178
console_print(u"Dropbox daemon stopped.")
1179
except DropboxCommand.CouldntConnectError, e:
1180
console_print(u"Dropbox isn't running!")
1181
elif sub_command == u"remove":
1183
with closing(DropboxCommand(timeout=None)) as dc:
1185
result = dc.ignore_set_remove(paths=absolute_paths)
1186
if result[u"removed"]:
1187
console_print(u"No longer excluded: ")
1188
lines = [relpath(path) for path in result[u"removed"]]
1190
console_print(unicode(line))
1192
console_print(u"Couldn't remove ignore path: daemon isn't responding")
1193
except DropboxCommand.CommandError, e:
1194
if e.args[0].startswith(u"No command exists by that name"):
1195
console_print(u"This version of the client does not support this command.")
1197
console_print(u"Couldn't get ignore set: " + str(e))
1198
except DropboxCommand.BadConnectionError, e:
1199
console_print(u"Dropbox isn't responding! [%s]" % e)
1200
except DropboxCommand.EOFError:
1201
console_print(u"Dropbox daemon stopped.")
1202
except DropboxCommand.CouldntConnectError, e:
1203
console_print(u"Dropbox isn't running!")
1205
console_print(exclude.__doc__, linebreak=False)
1208
console_print(exclude.__doc__, linebreak=False)
1216
Starts the dropbox daemon, dropboxd. If dropboxd is already running, this will do nothing.
1219
-i --install auto install dropboxd if not available on the system
1222
should_install = "-i" in argv or "--install" in argv
1224
# first check if dropbox is already running
1225
if is_dropbox_running():
1226
if not grab_link_url_if_necessary():
1227
console_print(u"Dropbox is already running!")
1230
console_print(u"Starting Dropbox...", linebreak=False)
1232
if not start_dropbox():
1233
if not should_install:
1235
console_print(u"The Dropbox daemon is not installed!")
1236
console_print(u"Run \"dropbox start -i\" to install the daemon")
1239
# install dropbox!!!
1247
console_print(u"Done!")
1250
if not grab_link_url_if_necessary():
1251
console_print(u"Done!")
1253
if not grab_link_url_if_necessary():
1254
console_print(u"Done!")
1257
def can_reroll_autostart():
1258
return u".config" in os.listdir(os.path.expanduser(u'~'))
1260
def reroll_autostart(should_autostart):
1261
home_dir = os.path.expanduser(u'~')
1262
contents = os.listdir(home_dir)
1265
if u".config" in contents:
1266
autostart_dir = os.path.join(home_dir, u".config", u"autostart")
1267
autostart_link = os.path.join(autostart_dir, u"dropbox.desktop")
1268
if should_autostart:
1269
if os.path.exists(DESKTOP_FILE):
1270
if not os.path.exists(autostart_dir):
1271
os.makedirs(autostart_dir)
1272
shutil.copyfile(DESKTOP_FILE, autostart_link)
1273
elif os.path.exists(autostart_link):
1274
os.remove(autostart_link)
1279
def autostart(argv):
1280
u"""automatically start dropbox at login
1281
dropbox autostart [y/n]
1284
n dropbox will not start automatically at login
1285
y dropbox will start automatically at login (default)
1287
Note: May only work on current Ubuntu distributions.
1290
console_print(''.join(autostart.__doc__.split('\n', 1)[1:]).decode('ascii'))
1294
if s.startswith('y') or s.startswith('-y'):
1295
should_autostart = True
1296
elif s.startswith('n') or s.startswith('-n'):
1297
should_autostart = False
1299
should_autostart = None
1301
if should_autostart is None:
1302
console_print(autostart.__doc__,linebreak=False)
1304
reroll_autostart(should_autostart)
1309
dropbox help [COMMAND]
1311
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.
1315
for command in commands:
1316
if command == argv[0]:
1317
console_print(commands[command].__doc__.split('\n', 1)[1].decode('ascii'))
1319
for alias in aliases:
1320
if alias == argv[0]:
1321
console_print(aliases[alias].__doc__.split('\n', 1)[1].decode('ascii'))
1323
console_print(u"unknown command '%s'" % argv[0], f=sys.stderr)
1326
console_print(u"Dropbox command-line interface\n")
1327
console_print(u"commands:\n")
1328
console_print(u"Note: use dropbox help <command> to view usage for a specific command.\n")
1330
for command in commands:
1331
out.append((command, commands[command].__doc__.splitlines()[0]))
1332
spacing = max(len(o[0])+3 for o in out)
1334
console_print(" %-*s%s" % (spacing, o[0], o[1]))
1340
# now we need to find out if one of the commands are in the
1341
# argv list, and if so split the list at the point to
1342
# separate the argv list at that point
1344
for i in range(len(argv)):
1345
if argv[i] in commands or argv[i] in aliases:
1354
# lol no options for now
1355
globaloptionparser = optparse.OptionParser()
1356
globaloptionparser.parse_args(argv[0:i])
1358
# now dispatch and run
1360
if argv[i] in commands:
1361
result = commands[argv[i]](argv[i+1:])
1362
elif argv[i] in aliases:
1363
result = aliases[argv[i]](argv[i+1:])
1365
# flush, in case output is rerouted to a file.
1371
if __name__ == "__main__":
1372
ret = main(sys.argv)