~ubuntu-branches/ubuntu/saucy/nautilus-dropbox/saucy

« back to all changes in this revision

Viewing changes to .pc/add-http-proxy-option.patch/dropbox.in

  • Committer: Package Import Robot
  • Author(s): Raphaël Hertzog
  • Date: 2011-12-28 11:35:56 UTC
  • mfrom: (7.1.1 sid)
  • Revision ID: package-import@ubuntu.com-20111228113556-d2xamk9cqa1v6psb
Tags: 0.7.1-2
* Update watch file to cope with changes on the dropbox website.
* Update copyright file to fix 2 syntax errors and to add a disclaimer
  concerning the fact that the package is in non-free. Thanks
  to Charles Plessy for the patch. Closes: #650735
* Split upstreamable changes from use-var-lib-dropbox.patch into
  do-not-hardcode-dropboxd-path.patch.
* Split non-upstream changes from dropbox-update.patch into
  non-interactive-update.patch.
* Update use-var-lib-dropbox.patch to ensure that the extracted files are
  owned by root.
* Stop dropbox in the prerm and not in the postrm since the program
  is no longer available at that point.
* New patch add-http-proxy-option.patch to force the http_proxy environment
  variable.
* Update use-pkexec-to-get-root-rights.patch to forward the http_proxy
  environment variable across pkexec. Closes: #651065

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/python
 
2
#
 
3
# Copyright 2008 Evenflow, Inc.
 
4
#
 
5
# dropbox
 
6
# Dropbox frontend script
 
7
# This file is part of nautilus-dropbox @PACKAGE_VERSION@.
 
8
#
 
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.
 
13
#
 
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.
 
18
#
 
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/>.
 
21
#
 
22
from __future__ import with_statement
 
23
 
 
24
import errno
 
25
import fcntl
 
26
import locale
 
27
import optparse
 
28
import os
 
29
import platform
 
30
import shutil
 
31
import socket
 
32
import StringIO
 
33
import subprocess
 
34
import sys
 
35
import tarfile
 
36
import tempfile
 
37
import threading
 
38
import time
 
39
import urllib
 
40
 
 
41
try:
 
42
    import gpgme
 
43
except ImportError:
 
44
    gpgme = None
 
45
 
 
46
from contextlib import closing, contextmanager
 
47
from posixpath import curdir, sep, pardir, join, abspath, commonprefix
 
48
 
 
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."
 
53
 
 
54
DOWNLOADING = u"Downloading Dropbox... %d%%"
 
55
UNPACKING = u"Unpacking Dropbox... %d%%"
 
56
 
 
57
PARENT_DIR = os.path.expanduser("~")
 
58
DROPBOXD_PATH = "%s/.dropbox-dist/dropboxd" % PARENT_DIR
 
59
DESKTOP_FILE = u"@DESKTOP_FILE_DIR@/dropbox.desktop"
 
60
 
 
61
enc = locale.getpreferredencoding()
 
62
 
 
63
# Available from http://linux.dropbox.com/fedora/rpm-public-key.asc
 
64
DROPBOX_PUBLIC_KEY = """
 
65
-----BEGIN PGP PUBLIC KEY BLOCK-----
 
66
Version: SKS 1.1.0
 
67
 
 
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
 
80
=5rWG
 
81
-----END PGP PUBLIC KEY BLOCK-----
 
82
"""
 
83
 
 
84
# Futures
 
85
 
 
86
def methodcaller(name, *args, **kwargs):
 
87
    def caller(obj):
 
88
        return getattr(obj, name)(*args, **kwargs)
 
89
    return caller
 
90
 
 
91
def relpath(path, start=curdir):
 
92
    """Return a relative version of a path"""
 
93
 
 
94
    if not path:
 
95
        raise ValueError("no path specified")
 
96
 
 
97
    if type(start) is unicode:
 
98
        start_list = unicode_abspath(start).split(sep)
 
99
    else:
 
100
        start_list = abspath(start).split(sep)
 
101
 
 
102
    if type(path) is unicode:
 
103
        path_list = unicode_abspath(path).split(sep)
 
104
    else:
 
105
        path_list = abspath(path).split(sep)
 
106
 
 
107
    # Work out how much of the filepath is shared by start and path.
 
108
    i = len(commonprefix([start_list, path_list]))
 
109
 
 
110
    rel_list = [pardir] * (len(start_list)-i) + path_list[i:]
 
111
    if not rel_list:
 
112
        return curdir
 
113
    return join(*rel_list)
 
114
 
 
115
# End Futures
 
116
 
 
117
 
 
118
def console_print(st=u"", f=sys.stdout, linebreak=True):
 
119
    global enc
 
120
    assert type(st) is unicode
 
121
    f.write(st.encode(enc))
 
122
    if linebreak: f.write(os.linesep)
 
123
 
 
124
def console_flush(f=sys.stdout):
 
125
    f.flush()
 
126
 
 
127
def yes_no_question(question):
 
128
    while True:
 
129
        console_print(question, linebreak=False)
 
130
        console_print(u" [y/n] ", linebreak=False)
 
131
        console_flush()
 
132
        text = raw_input()
 
133
        if text.lower().startswith("y"):
 
134
            return True
 
135
        elif text.lower().startswith("n"):
 
136
            return False
 
137
        else:
 
138
            console_print(u"Sorry, I didn't understand that. Please type yes or no.")
 
139
 
 
140
def plat():
 
141
    if sys.platform.lower().startswith('linux'):
 
142
        arch = platform.machine()
 
143
        if (arch[0] == 'i' and
 
144
            arch[1].isdigit() and
 
145
            arch[2:4] == '86'):
 
146
            plat = "x86"
 
147
        elif arch == 'x86_64':
 
148
            plat = arch
 
149
        else:
 
150
            FatalVisibleError("Platform not supported")
 
151
        return "lnx.%s" % plat
 
152
    else:
 
153
        FatalVisibleError("Platform not supported")
 
154
 
 
155
def is_dropbox_running():
 
156
    pidfile = os.path.expanduser("~/.dropbox/dropbox.pid")
 
157
 
 
158
    try:
 
159
        with open(pidfile, "r") as f:
 
160
            pid = int(f.read())
 
161
        with open("/proc/%d/cmdline" % pid, "r") as f:
 
162
            cmdline = f.read().lower()
 
163
    except:
 
164
        cmdline = ""
 
165
 
 
166
    return "dropbox" in cmdline
 
167
 
 
168
def unicode_abspath(path):
 
169
    global enc
 
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())
 
173
 
 
174
@contextmanager
 
175
def gpgme_context(keys):
 
176
    gpg_conf_contents = ''
 
177
    _gpghome = tempfile.mkdtemp(prefix='tmp.gpghome')
 
178
 
 
179
    try:
 
180
        os.environ['GNUPGHOME'] = _gpghome
 
181
        fp = open(os.path.join(_gpghome, 'gpg.conf'), 'wb')
 
182
        fp.write(gpg_conf_contents)
 
183
        fp.close()
 
184
        ctx = gpgme.Context()
 
185
 
 
186
        loaded = []
 
187
        for key_file in keys:
 
188
            result = ctx.import_(key_file)
 
189
            key = ctx.get_key(result.imports[0][0])
 
190
            loaded.append(key)
 
191
 
 
192
        ctx.signers = loaded
 
193
 
 
194
        yield ctx
 
195
    finally:
 
196
        del os.environ['GNUPGHOME']
 
197
        shutil.rmtree(_gpghome, ignore_errors=True)
 
198
 
 
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
 
203
 
 
204
def download_file_chunk(socket, buf, size):
 
205
    progress = 0
 
206
    with closing(socket) as f:
 
207
        while True:
 
208
            try:
 
209
                chunk = os.read(f.fileno(), 4096)
 
210
                progress += len(chunk)
 
211
                buf.write(chunk)
 
212
                yield (progress, True)
 
213
                if progress == size:
 
214
                    break
 
215
            except OSError, e:
 
216
                if hasattr(e, 'errno') and e.errno == errno.EAGAIN:
 
217
                    # nothing left to read
 
218
                    yield (progress, False)
 
219
                else:
 
220
                    raise
 
221
 
 
222
def download_uri_to_buffer(uri):
 
223
    try:
 
224
        socket = urllib.urlopen(uri)
 
225
    except IOError:
 
226
        FatalVisibleError("Trouble connecting to Dropbox servers. Maybe your internet connection is down, or you need to set your http_proxy environment variable.")
 
227
 
 
228
    fcntl.fcntl(socket, fcntl.F_SETFL, os.O_NONBLOCK)
 
229
    size = int(socket.info()['content-length'])
 
230
 
 
231
    buf = StringIO.StringIO()
 
232
    download_chunk = download_file_chunk(socket, buf, size)
 
233
 
 
234
    for _ in download_chunk:
 
235
        pass
 
236
 
 
237
    buf.seek(0)
 
238
    return buf
 
239
 
 
240
# This sets a custom User-Agent
 
241
class DropboxURLopener(urllib.FancyURLopener):
 
242
    version = "DropboxLinuxDownloader/@PACKAGE_VERSION@"
 
243
urllib._urlopener = DropboxURLopener()
 
244
 
 
245
class DownloadState(object):
 
246
    def __init__(self):
 
247
        try:
 
248
            self.socket = urllib.urlopen("http://www.dropbox.com/download?plat=%s" % plat())
 
249
        except IOError:
 
250
            FatalVisibleError("Trouble connecting to Dropbox servers. Maybe your internet connection is down, or you need to set your http_proxy environment variable")
 
251
 
 
252
        fcntl.fcntl(self.socket, fcntl.F_SETFL, os.O_NONBLOCK)
 
253
        self.size = int(self.socket.info()['content-length'])
 
254
 
 
255
        self.local_file = StringIO.StringIO()
 
256
        self.download_chunk = download_file_chunk(self.socket, self.local_file, self.size)
 
257
 
 
258
    def copy_data(self):
 
259
        return self.download_chunk
 
260
 
 
261
    def unpack(self):
 
262
        # download signature
 
263
        signature = download_uri_to_buffer("http://www.dropbox.com/download?plat=%s&signature=1" % plat())
 
264
 
 
265
        self.local_file.seek(0)
 
266
        if gpgme:
 
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.")
 
269
 
 
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):
 
276
                os.unlink(filename)
 
277
            archive.extract(member, PARENT_DIR)
 
278
            yield member.name, i, total_members
 
279
        archive.close()
 
280
 
 
281
    def cancel(self):
 
282
        if not self.local_file.closed:
 
283
            self.local_file.close()
 
284
 
 
285
def load_serialized_images():
 
286
    global box_logo_pixbuf, window_icon
 
287
    import gtk
 
288
    box_logo_pixbuf = @IMAGEDATA64@
 
289
    window_icon = @IMAGEDATA16@
 
290
 
 
291
GUI_AVAILABLE = os.environ.get("DISPLAY", '')
 
292
 
 
293
if GUI_AVAILABLE:
 
294
    def download():
 
295
        import pygtk
 
296
        pygtk.require("2.0")
 
297
        import gtk
 
298
        import gobject
 
299
        import pango
 
300
        import webbrowser
 
301
 
 
302
        load_serialized_images()
 
303
 
 
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,
 
310
                                      message_format = s)
 
311
            error.set_title("Error")
 
312
            error.run()
 
313
            gtk.main_quit()
 
314
            sys.exit(-1)
 
315
 
 
316
        def gtk_flush_events():
 
317
            while gtk.events_pending():
 
318
                gtk.main_iteration()
 
319
 
 
320
        class DownloadDialog(gtk.Dialog):
 
321
            def handle_delete_event(self, wid, ev, data=None):
 
322
                self.handle_cancel(wid)
 
323
 
 
324
            def handle_dont_show_toggle(self, button, data=None):
 
325
                reroll_autostart(not button.get_active())
 
326
 
 
327
            def handle_cancel(self, button):
 
328
                if self.watch:
 
329
                    gobject.source_remove(self.watch)
 
330
                if self.download:
 
331
                    self.download.cancel()
 
332
                gtk.main_quit()
 
333
                self.user_cancelled = True
 
334
 
 
335
            def handle_ok(self, button):
 
336
                # begin download
 
337
                self.ok.hide()
 
338
                self.download = DownloadState()
 
339
                self.one_chunk = self.download.copy_data()
 
340
                self.watch = gobject.io_add_watch(self.download.socket,
 
341
                                                  gobject.IO_IN |
 
342
                                                  gobject.IO_PRI |
 
343
                                                  gobject.IO_ERR |
 
344
                                                  gobject.IO_HUP,
 
345
                                                  self.handle_data_waiting)
 
346
                self.label.hide()
 
347
                self.dont_show_again_align.hide()
 
348
                self.progress.show()
 
349
 
 
350
            def update_progress(self, text, fraction):
 
351
                self.progress.set_text(text % int(fraction*100))
 
352
                self.progress.set_fraction(fraction)
 
353
                gtk_flush_events()
 
354
 
 
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.")
 
360
                try:
 
361
                    while True:
 
362
                        progress, status = self.one_chunk.next()
 
363
                        if not status:
 
364
                            break
 
365
                        self.update_progress(DOWNLOADING, float(progress)/self.download.size)
 
366
                except StopIteration:
 
367
                    self.update_progress(DOWNLOADING, 1.0)
 
368
                    self.unpack_dropbox()
 
369
                    return False
 
370
                else:
 
371
                    self.update_progress(DOWNLOADING, float(progress)/self.download.size)
 
372
                    return True
 
373
 
 
374
            def unpack_dropbox(self):
 
375
                one_member = self.download.unpack()
 
376
                try:
 
377
                    while True:
 
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)
 
382
                    gtk.main_quit()
 
383
 
 
384
            def mouse_down(self, widget, event):
 
385
                if self.hovering:
 
386
                    self.clicked_link = True
 
387
 
 
388
            def mouse_up(self, widget, event):
 
389
                if self.clicked_link:
 
390
                    webbrowser.open(LINK)
 
391
                    self.clicked_link = False
 
392
 
 
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):
 
400
                    self.hovering = True
 
401
                    self.label_box.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.HAND2))
 
402
                else:
 
403
                    self.hovering = False
 
404
                    self.label_box.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.ARROW))
 
405
 
 
406
 
 
407
            def __init__(self):
 
408
                super(DownloadDialog, self).__init__(parent = None,
 
409
                                                     title = "Dropbox Installation")
 
410
 
 
411
                self.download = None
 
412
                self.watch = None
 
413
                self.hovering = False
 
414
                self.clicked_link = False
 
415
                self.user_cancelled = False
 
416
 
 
417
                self.ok = ok = gtk.Button(stock=gtk.STOCK_OK)
 
418
                ok.connect('clicked', self.handle_ok)
 
419
                self.action_area.add(ok)
 
420
                ok.show()
 
421
 
 
422
                cancel = gtk.Button(stock=gtk.STOCK_CANCEL)
 
423
                cancel.connect('clicked', self.handle_cancel)
 
424
                self.action_area.add(cancel)
 
425
                cancel.show()
 
426
 
 
427
                self.connect('delete_event', self.handle_delete_event)
 
428
 
 
429
                self.box_logo = gtk.image_new_from_pixbuf(box_logo_pixbuf)
 
430
                self.box_logo.show()
 
431
 
 
432
                self.set_icon(window_icon)
 
433
 
 
434
                self.progress = gtk.ProgressBar()
 
435
                self.progress.set_property('width-request', 300)
 
436
 
 
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)
 
442
                self.label.show()
 
443
 
 
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)
 
449
 
 
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)
 
454
 
 
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)
 
460
                self.hbox.show()
 
461
 
 
462
                self.vbox.add(self.hbox)
 
463
 
 
464
                try:
 
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()
 
469
 
 
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()
 
473
 
 
474
                        hbox = gtk.HBox()
 
475
                        hbox.set_property('border-width', 10)
 
476
                        hbox.pack_start(self.dont_show_again_align, True, True)
 
477
                        hbox.show()
 
478
 
 
479
                        self.vbox.add(hbox)
 
480
 
 
481
                    self.set_resizable(False)
 
482
                except:
 
483
                    import traceback
 
484
                    traceback.print_exc()
 
485
 
 
486
                self.ok.grab_focus()
 
487
 
 
488
        dialog = DownloadDialog()
 
489
        dialog.show()
 
490
        gtk.main()
 
491
        if dialog.user_cancelled:
 
492
            raise Exception("user cancelled download!!!")
 
493
else:
 
494
    def download():
 
495
        global FatalVisibleError
 
496
        def FatalVisibleError(s):
 
497
            console_print(u"\nError: %s" % s, f=sys.stderr)
 
498
            sys.exit(-1)
 
499
 
 
500
 
 
501
        ESC = "\x1b"
 
502
        save = ESC+"7"
 
503
        unsave = ESC+"8"
 
504
        clear = ESC+"[2J"
 
505
        erase_to_start = ESC+"[1K"
 
506
        write = sys.stdout.write
 
507
        flush = sys.stdout.flush
 
508
 
 
509
        last_progress = [None, None]
 
510
        def setprogress(text, frac):
 
511
            if last_progress == [text, frac]:
 
512
                return
 
513
            if sys.stdout.isatty():
 
514
                write(erase_to_start)
 
515
                write(unsave)
 
516
            console_print(text % int(100*frac), linebreak=not sys.stdout.isatty())
 
517
            if sys.stdout.isatty():
 
518
                flush()
 
519
            last_progress[0], last_progress[1] = text, frac
 
520
 
 
521
        console_print()
 
522
        if sys.stdout.isatty():
 
523
            write(save)
 
524
            flush()
 
525
        console_print(u"%s %s\n" % (INFO, LINK))
 
526
        GPG_WARNING_MSG = (u"\n%s" % GPG_WARNING) if not gpgme else u""
 
527
 
 
528
        if not yes_no_question("%s%s" % (WARNING, GPG_WARNING_MSG)):
 
529
            return
 
530
 
 
531
        download = DownloadState()
 
532
        one_chunk = download.copy_data()
 
533
 
 
534
        try:
 
535
            while True:
 
536
                progress = one_chunk.next()[0]
 
537
                setprogress(DOWNLOADING, float(progress)/download.size)
 
538
        except StopIteration:
 
539
            setprogress(DOWNLOADING, 1.0)
 
540
            console_print()
 
541
            write(save)
 
542
 
 
543
        one_member = download.unpack()
 
544
 
 
545
        try:
 
546
            while True:
 
547
                name, i, total = one_member.next()
 
548
                setprogress(UNPACKING, float(i)/total)
 
549
        except StopIteration:
 
550
            setprogress(UNPACKING, 1.0)
 
551
 
 
552
        console_print()
 
553
 
 
554
class CommandTicker(threading.Thread):
 
555
    def __init__(self):
 
556
        threading.Thread.__init__(self)
 
557
        self.stop_event = threading.Event()
 
558
 
 
559
    def stop(self):
 
560
        self.stop_event.set()
 
561
 
 
562
    def run(self):
 
563
        ticks = ['[.  ]', '[.. ]', '[...]', '[ ..]', '[  .]', '[   ]']
 
564
        i = 0
 
565
        first = True
 
566
        while True:
 
567
            self.stop_event.wait(0.25)
 
568
            if self.stop_event.isSet(): break
 
569
            if i == len(ticks):
 
570
                first = False
 
571
                i = 0
 
572
            if not first:
 
573
                sys.stderr.write("\r%s\r" % ticks[i])
 
574
                sys.stderr.flush()
 
575
            i += 1
 
576
        sys.stderr.flush()
 
577
 
 
578
 
 
579
class DropboxCommand(object):
 
580
    class CouldntConnectError(Exception): pass
 
581
    class BadConnectionError(Exception): pass
 
582
    class EOFError(Exception): pass
 
583
    class CommandError(Exception): pass
 
584
 
 
585
    def __init__(self, timeout=5):
 
586
        self.s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
 
587
        self.s.settimeout(timeout)
 
588
        try:
 
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)
 
593
 
 
594
    def close(self):
 
595
        self.f.close()
 
596
        self.s.close()
 
597
 
 
598
    def __readline(self):
 
599
        try:
 
600
            toret = self.f.readline().decode('utf8').rstrip(u"\n")
 
601
        except socket.error, e:
 
602
            raise DropboxCommand.BadConnectionError()
 
603
        if toret == '':
 
604
            raise DropboxCommand.EOFError()
 
605
        else:
 
606
            return toret
 
607
 
 
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'))
 
617
 
 
618
        self.f.flush()
 
619
 
 
620
        # Start a ticker
 
621
        ticker_thread = CommandTicker()
 
622
        ticker_thread.start()
 
623
 
 
624
        # This is the potentially long-running call.
 
625
        try:
 
626
            ok = self.__readline() == u"ok"
 
627
        except KeyboardInterrupt:
 
628
            raise DropboxCommand.BadConnectionError("Keyboard interruption detected")
 
629
        finally:
 
630
            # Tell the ticker to stop.
 
631
            ticker_thread.stop()
 
632
            ticker_thread.join()
 
633
 
 
634
        if ok:
 
635
            toret = {}
 
636
            for i in range(21):
 
637
                if i == 20:
 
638
                    raise Exception(u"close this connection!")
 
639
 
 
640
                line = self.__readline()
 
641
                if line == u"done":
 
642
                    break
 
643
 
 
644
                argval = line.split(u"\t")
 
645
                toret[argval[0]] = argval[1:]
 
646
 
 
647
            return toret
 
648
        else:
 
649
            problems = []
 
650
            for i in range(21):
 
651
                if i == 20:
 
652
                    raise Exception(u"close this connection!")
 
653
 
 
654
                line = self.__readline()
 
655
                if line == u"done":
 
656
                    break
 
657
 
 
658
                problems.append(line)
 
659
 
 
660
            raise DropboxCommand.CommandError(u"\n".join(problems))
 
661
 
 
662
    # this is the hotness, auto marshalling
 
663
    def __getattr__(self, name):
 
664
        try:
 
665
            return super(DropboxCommand, self).__getattr__(name)
 
666
        except:
 
667
            def __spec_command(**kw):
 
668
                return self.send_command(unicode(name), kw)
 
669
            self.__setattr__(name, __spec_command)
 
670
            return __spec_command
 
671
 
 
672
commands = {}
 
673
aliases = {}
 
674
 
 
675
def command(meth):
 
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
 
679
        meth = meth.im_func
 
680
    commands[meth.func_name] = meth
 
681
    meth_aliases = [unicode(alias) for alias in aliases.iterkeys() if aliases[alias].func_name == meth.func_name]
 
682
    if meth_aliases:
 
683
        meth.__doc__ += u"\nAliases: %s" % ",".join(meth_aliases)
 
684
    return meth
 
685
 
 
686
def alias(name):
 
687
    def decorator(meth):
 
688
        global commands, aliases
 
689
        assert name not in commands, "This alias is the name of a command."
 
690
        aliases[name] = meth
 
691
        return meth
 
692
    return decorator
 
693
 
 
694
def requires_dropbox_running(meth):
 
695
    def newmeth(*n, **kw):
 
696
        if is_dropbox_running():
 
697
            return meth(*n, **kw)
 
698
        else:
 
699
            console_print(u"Dropbox isn't running!")
 
700
    newmeth.func_name = meth.func_name
 
701
    newmeth.__doc__ = meth.__doc__
 
702
    return newmeth
 
703
 
 
704
def start_dropbox():
 
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)
 
711
 
 
712
        # in seconds
 
713
        interval = 0.5
 
714
        wait_for = 60
 
715
        for i in xrange(int(wait_for / interval)):
 
716
            if is_dropbox_running():
 
717
                return True
 
718
            # back off from connect for a while
 
719
            time.sleep(interval)
 
720
 
 
721
        return False
 
722
    else:
 
723
        return False
 
724
 
 
725
# Extracted and modified from os.cmd.Cmd
 
726
def columnize(list, display_list=None, display_width=None):
 
727
    if not list:
 
728
        console_print(u"<empty>")
 
729
        return
 
730
 
 
731
    non_unicode = [i for i in range(len(list)) if not (isinstance(list[i], unicode))]
 
732
    if non_unicode:
 
733
        raise TypeError, ("list[i] not a string for i in %s" %
 
734
                          ", ".join(map(unicode, non_unicode)))
 
735
 
 
736
    if not display_width:
 
737
        d = os.popen('stty size', 'r').read().split()
 
738
        if d:
 
739
            display_width = int(d[1])
 
740
        else:
 
741
            for item in list:
 
742
                console_print(item)
 
743
            return
 
744
 
 
745
    if not display_list:
 
746
        display_list = list
 
747
 
 
748
    size = len(list)
 
749
    if size == 1:
 
750
        console_print(display_list[0])
 
751
        return
 
752
 
 
753
    for nrows in range(1, len(list)):
 
754
        ncols = (size+nrows-1) // nrows
 
755
        colwidths = []
 
756
        totwidth = -2
 
757
        for col in range(ncols):
 
758
            colwidth = 0
 
759
            for row in range(nrows):
 
760
                i = row + nrows*col
 
761
                if i >= size:
 
762
                    break
 
763
                x = list[i]
 
764
                colwidth = max(colwidth, len(x))
 
765
            colwidths.append(colwidth)
 
766
            totwidth += colwidth + 2
 
767
            if totwidth > display_width:
 
768
                break
 
769
        if totwidth <= display_width:
 
770
            break
 
771
    else:
 
772
        nrows = len(list)
 
773
        ncols = 1
 
774
        colwidths = [0]
 
775
    lines = []
 
776
    for row in range(nrows):
 
777
        texts = []
 
778
        display_texts = []
 
779
        for col in range(ncols):
 
780
            i = row + nrows*col
 
781
            if i >= size:
 
782
                x = ""
 
783
                y = ""
 
784
            else:
 
785
                x = list[i]
 
786
                y = display_list[i]
 
787
            texts.append(x)
 
788
            display_texts.append(y)
 
789
        while texts and not texts[-1]:
 
790
            del 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])
 
797
        lines.append(line)
 
798
    for line in lines:
 
799
        console_print(line)
 
800
 
 
801
@command
 
802
def update(args):
 
803
    u"""download latest version of dropbox
 
804
dropbox update
 
805
 
 
806
Downloads the latest version of dropbox.
 
807
"""
 
808
    download()
 
809
 
 
810
@command
 
811
@requires_dropbox_running
 
812
@alias('stat')
 
813
def filestatus(args):
 
814
    u"""get current sync status of one or more files
 
815
dropbox filestatus [-l] [-a] [FILE]...
 
816
 
 
817
Prints the current status of each FILE.
 
818
 
 
819
options:
 
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 .
 
822
"""
 
823
    global enc
 
824
 
 
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)
 
829
 
 
830
    try:
 
831
        with closing(DropboxCommand()) as dc:
 
832
            if options.list:
 
833
                # Listing.
 
834
 
 
835
                # Separate directories from files.
 
836
                if len(args) == 0:
 
837
                    dirs, nondirs = [u"."], []
 
838
                else:
 
839
                    dirs, nondirs = [], []
 
840
 
 
841
                    for a in args:
 
842
                        try:
 
843
                            (dirs if os.path.isdir(a) else nondirs).append(a.decode(enc))
 
844
                        except UnicodeDecodeError:
 
845
                            continue
 
846
 
 
847
                    if len(dirs) == 0 and len(nondirs) == 0:
 
848
                        #TODO: why?
 
849
                        exit(1)
 
850
 
 
851
                dirs.sort(key=methodcaller('lower'))
 
852
                nondirs.sort(key=methodcaller('lower'))
 
853
 
 
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)
 
858
                        return (path, path)
 
859
                    try:
 
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)
 
863
                        return (path, path)
 
864
 
 
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
 
870
                                        'color' in env_term
 
871
                                        )
 
872
                                     )
 
873
 
 
874
                    # TODO: Test when you don't support color.
 
875
                    if not supports_color:
 
876
                        path = os.path.basename(file_path)
 
877
                        return (path, path)
 
878
 
 
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"
 
887
                    else:
 
888
                        init, cleanup = '', ''
 
889
 
 
890
                    path = os.path.basename(file_path)
 
891
                    return (path, u"%s%s%s" % (init, path, cleanup))
 
892
 
 
893
                # Prints a directory.
 
894
                def print_directory(name):
 
895
                    clean_paths = []
 
896
                    formatted_paths = []
 
897
                    for subname in sorted(os.listdir(name), key=methodcaller('lower')):
 
898
                        if type(subname) != unicode:
 
899
                            continue
 
900
 
 
901
                        if not options.all and subname[0] == u'.':
 
902
                            continue
 
903
 
 
904
                        try:
 
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:
 
909
                            continue
 
910
 
 
911
                    columnize(clean_paths, formatted_paths)
 
912
 
 
913
                try:
 
914
                    if len(dirs) == 1 and len(nondirs) == 0:
 
915
                        print_directory(dirs[0])
 
916
                    else:
 
917
                        nondir_formatted_paths = []
 
918
                        nondir_clean_paths = []
 
919
                        for name in nondirs:
 
920
                            try:
 
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:
 
925
                                continue
 
926
 
 
927
                        if nondir_clean_paths:
 
928
                            columnize(nondir_clean_paths, nondir_formatted_paths)
 
929
 
 
930
                        if len(nondirs) == 0:
 
931
                            console_print(dirs[0] + u":")
 
932
                            print_directory(dirs[0])
 
933
                            dirs = dirs[1:]
 
934
 
 
935
                        for name in dirs:
 
936
                            console_print()
 
937
                            console_print(name + u":")
 
938
                            print_directory(name)
 
939
 
 
940
                except DropboxCommand.EOFError:
 
941
                    console_print(u"Dropbox daemon stopped.")
 
942
                except DropboxCommand.BadConnectionError, e:
 
943
                    console_print(u"Dropbox isn't responding!")
 
944
            else:
 
945
                if len(args) == 0:
 
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)
 
948
                for file in args:
 
949
 
 
950
                    try:
 
951
                        if type(file) is not unicode:
 
952
                            file = file.decode(enc)
 
953
                        fp = unicode_abspath(file)
 
954
                    except (UnicodeEncodeError, UnicodeDecodeError), e:
 
955
                        continue
 
956
                    if not os.path.exists(fp):
 
957
                        console_print(u"%-*s %s" % \
 
958
                                          (indent, file+':', "File doesn't exist"))
 
959
                        continue
 
960
 
 
961
                    try:
 
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!")
 
968
 
 
969
@command
 
970
@requires_dropbox_running
 
971
def ls(args):
 
972
    u"""list directory contents with current sync status
 
973
dropbox ls [FILE]...
 
974
 
 
975
This is an alias for filestatus -l
 
976
"""
 
977
    return filestatus(["-l"] + args)
 
978
 
 
979
@command
 
980
@requires_dropbox_running
 
981
def puburl(args):
 
982
    u"""get public url of a file in your dropbox
 
983
dropbox puburl FILE
 
984
 
 
985
Prints out a public url for FILE.
 
986
"""
 
987
    if len(args) != 1:
 
988
        console_print(puburl.__doc__,linebreak=False)
 
989
        return
 
990
 
 
991
    try:
 
992
        with closing(DropboxCommand()) as dc:
 
993
            try:
 
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!")
 
1003
 
 
1004
@command
 
1005
@requires_dropbox_running
 
1006
def status(args):
 
1007
    u"""get current status of the dropboxd
 
1008
dropbox status
 
1009
 
 
1010
Prints out the current status of the Dropbox daemon.
 
1011
"""
 
1012
    if len(args) != 0:
 
1013
        console_print(status.__doc__,linebreak=False)
 
1014
        return
 
1015
 
 
1016
    try:
 
1017
        with closing(DropboxCommand()) as dc:
 
1018
            try:
 
1019
                lines = dc.get_dropbox_status()[u'status']
 
1020
                if len(lines) == 0:
 
1021
                    console_print(u'Idle')
 
1022
                else:
 
1023
                    for line in lines:
 
1024
                        console_print(line)
 
1025
            except KeyError:
 
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!")
 
1035
 
 
1036
@command
 
1037
def running(argv):
 
1038
    u"""return whether dropbox is running
 
1039
dropbox running
 
1040
 
 
1041
Returns 1 if running 0 if not running.
 
1042
"""
 
1043
    return int(is_dropbox_running())
 
1044
 
 
1045
@command
 
1046
@requires_dropbox_running
 
1047
def stop(args):
 
1048
    u"""stop dropboxd
 
1049
dropbox stop
 
1050
 
 
1051
Stops the dropbox daemon.
 
1052
"""
 
1053
    try:
 
1054
        with closing(DropboxCommand()) as dc:
 
1055
            try:
 
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!")
 
1063
 
 
1064
#returns true if link is necessary
 
1065
def grab_link_url_if_necessary():
 
1066
    try:
 
1067
        with closing(DropboxCommand()) as dc:
 
1068
            try:
 
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])
 
1072
                    return True
 
1073
                else:
 
1074
                    return False
 
1075
            except DropboxCommand.CommandError, e:
 
1076
                pass
 
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!")
 
1083
 
 
1084
@command
 
1085
@requires_dropbox_running
 
1086
def lansync(argv):
 
1087
    u"""enables or disables LAN sync
 
1088
dropbox lansync [y/n]
 
1089
 
 
1090
options:
 
1091
  y  dropbox will use LAN sync (default)
 
1092
  n  dropbox will not use LAN sync
 
1093
"""
 
1094
    if len(argv) != 1:
 
1095
        console_print(lansync.__doc__, linebreak=False)
 
1096
        return
 
1097
 
 
1098
    s = argv[0].lower()
 
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
 
1103
    else:
 
1104
        should_lansync = None
 
1105
 
 
1106
    if should_lansync is None:
 
1107
        console_print(lansync.__doc__,linebreak=False)
 
1108
    else:
 
1109
        with closing(DropboxCommand()) as dc:
 
1110
            dc.set_lan_sync(lansync='enabled' if should_lansync else 'disabled')
 
1111
 
 
1112
 
 
1113
@command
 
1114
@requires_dropbox_running
 
1115
def exclude(args):
 
1116
    u"""ignores/excludes a directory from syncing
 
1117
dropbox exclude [list]
 
1118
dropbox exclude add [DIRECTORY] [DIRECTORY] ...
 
1119
dropbox exclude remove [DIRECTORY] [DIRECTORY] ...
 
1120
 
 
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.
 
1126
"""
 
1127
    if len(args) == 0:
 
1128
        try:
 
1129
            with closing(DropboxCommand()) as dc:
 
1130
                try:
 
1131
                    lines = [relpath(path) for path in dc.get_ignore_set()[u'ignore_set']]
 
1132
                    lines.sort()
 
1133
                    if len(lines) == 0:
 
1134
                        console_print(u'No directories are being ignored.')
 
1135
                    else:
 
1136
                        console_print(u'Excluded: ')
 
1137
                        for line in lines:
 
1138
                            console_print(unicode(line))
 
1139
                except KeyError:
 
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.")
 
1144
                    else:
 
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":
 
1153
        exclude([])
 
1154
    elif len(args) >= 2:
 
1155
        sub_command = args[0]
 
1156
        paths = args[1:]
 
1157
        absolute_paths = [unicode_abspath(path.decode(sys.getfilesystemencoding())) for path in paths]
 
1158
        if sub_command == u"add":
 
1159
            try:
 
1160
                with closing(DropboxCommand(timeout=None)) as dc:
 
1161
                    try:
 
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"]]
 
1166
                            for line in lines:
 
1167
                                console_print(unicode(line))
 
1168
                    except KeyError:
 
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.")
 
1173
                        else:
 
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":
 
1182
            try:
 
1183
                with closing(DropboxCommand(timeout=None)) as dc:
 
1184
                    try:
 
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"]]
 
1189
                            for line in lines:
 
1190
                                console_print(unicode(line))
 
1191
                    except KeyError:
 
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.")
 
1196
                        else:
 
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!")
 
1204
        else:
 
1205
            console_print(exclude.__doc__, linebreak=False)
 
1206
            return
 
1207
    else:
 
1208
        console_print(exclude.__doc__, linebreak=False)
 
1209
        return
 
1210
 
 
1211
@command
 
1212
def start(argv):
 
1213
    u"""start dropboxd
 
1214
dropbox start [-i]
 
1215
 
 
1216
Starts the dropbox daemon, dropboxd. If dropboxd is already running, this will do nothing.
 
1217
 
 
1218
options:
 
1219
  -i --install  auto install dropboxd if not available on the system
 
1220
"""
 
1221
 
 
1222
    should_install = "-i" in argv or "--install" in argv
 
1223
 
 
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!")
 
1228
        return
 
1229
 
 
1230
    console_print(u"Starting Dropbox...", linebreak=False)
 
1231
    console_flush()
 
1232
    if not start_dropbox():
 
1233
        if not should_install:
 
1234
            console_print()
 
1235
            console_print(u"The Dropbox daemon is not installed!")
 
1236
            console_print(u"Run \"dropbox start -i\" to install the daemon")
 
1237
            return
 
1238
 
 
1239
        # install dropbox!!!
 
1240
        try:
 
1241
            download()
 
1242
        except:
 
1243
            pass
 
1244
        else:
 
1245
            if GUI_AVAILABLE:
 
1246
                start_dropbox()
 
1247
                console_print(u"Done!")
 
1248
            else:
 
1249
                if start_dropbox():
 
1250
                    if not grab_link_url_if_necessary():
 
1251
                        console_print(u"Done!")
 
1252
    else:
 
1253
        if not grab_link_url_if_necessary():
 
1254
            console_print(u"Done!")
 
1255
 
 
1256
 
 
1257
def can_reroll_autostart():
 
1258
    return u".config" in os.listdir(os.path.expanduser(u'~'))
 
1259
 
 
1260
def reroll_autostart(should_autostart):
 
1261
    home_dir = os.path.expanduser(u'~')
 
1262
    contents = os.listdir(home_dir)
 
1263
 
 
1264
    # UBUNTU
 
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)
 
1275
 
 
1276
 
 
1277
 
 
1278
@command
 
1279
def autostart(argv):
 
1280
    u"""automatically start dropbox at login
 
1281
dropbox autostart [y/n]
 
1282
 
 
1283
options:
 
1284
  n  dropbox will not start automatically at login
 
1285
  y  dropbox will start automatically at login (default)
 
1286
 
 
1287
Note: May only work on current Ubuntu distributions.
 
1288
"""
 
1289
    if len(argv) != 1:
 
1290
        console_print(''.join(autostart.__doc__.split('\n', 1)[1:]).decode('ascii'))
 
1291
        return
 
1292
 
 
1293
    s = argv[0].lower()
 
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
 
1298
    else:
 
1299
        should_autostart = None
 
1300
 
 
1301
    if should_autostart is None:
 
1302
        console_print(autostart.__doc__,linebreak=False)
 
1303
    else:
 
1304
        reroll_autostart(should_autostart)
 
1305
 
 
1306
@command
 
1307
def help(argv):
 
1308
    u"""provide help
 
1309
dropbox help [COMMAND]
 
1310
 
 
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.
 
1312
"""
 
1313
    if not argv:
 
1314
        return usage(argv)
 
1315
    for command in commands:
 
1316
        if command == argv[0]:
 
1317
            console_print(commands[command].__doc__.split('\n', 1)[1].decode('ascii'))
 
1318
            return
 
1319
    for alias in aliases:
 
1320
        if alias == argv[0]:
 
1321
            console_print(aliases[alias].__doc__.split('\n', 1)[1].decode('ascii'))
 
1322
            return
 
1323
    console_print(u"unknown command '%s'" % argv[0], f=sys.stderr)
 
1324
 
 
1325
def usage(argv):
 
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")
 
1329
    out = []
 
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)
 
1333
    for o in out:
 
1334
        console_print(" %-*s%s" % (spacing, o[0], o[1]))
 
1335
    console_print()
 
1336
 
 
1337
def main(argv):
 
1338
    global commands
 
1339
 
 
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
 
1343
    cut = None
 
1344
    for i in range(len(argv)):
 
1345
        if argv[i] in commands or argv[i] in aliases:
 
1346
            cut = i
 
1347
            break
 
1348
 
 
1349
    if cut == None:
 
1350
        usage(argv)
 
1351
        os._exit(0)
 
1352
        return
 
1353
 
 
1354
    # lol no options for now
 
1355
    globaloptionparser = optparse.OptionParser()
 
1356
    globaloptionparser.parse_args(argv[0:i])
 
1357
 
 
1358
    # now dispatch and run
 
1359
    result = None
 
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:])
 
1364
 
 
1365
    # flush, in case output is rerouted to a file.
 
1366
    console_flush()
 
1367
 
 
1368
    # done
 
1369
    return result
 
1370
 
 
1371
if __name__ == "__main__":
 
1372
    ret = main(sys.argv)
 
1373
    if ret is not None:
 
1374
        sys.exit(ret)