22
22
from __future__ import with_statement
38
from contextlib import closing
46
from contextlib import closing, contextmanager
39
47
from posixpath import curdir, sep, pardir, join, abspath, commonprefix
41
49
INFO = u"Dropbox is the easiest way to share and store your files online. Want to learn more? Head to"
42
50
LINK = u"http://www.dropbox.com/"
43
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."
45
54
DOWNLOADING = u"Downloading Dropbox... %d%%"
46
55
UNPACKING = u"Unpacking Dropbox... %d%%"
48
PARENT_DIR = os.path.expanduser("~")
57
PARENT_DIR = os.path.expanduser("/var/lib/dropbox")
49
58
DROPBOXD_PATH = "%s/.dropbox-dist/dropboxd" % PARENT_DIR
51
60
enc = locale.getpreferredencoding()
62
# Available from http://linux.dropbox.com/fedora/rpm-public-key.asc
63
DROPBOX_PUBLIC_KEY = """
64
-----BEGIN PGP PUBLIC KEY BLOCK-----
67
mQENBEt0ibEBCACv4hZRPqwtpU6z8+BB5YZU1a3yjEvg2W68+a6hEwxtCa2U++4dzQ+7EqaU
68
q5ybQnwtbDdpFpsOi9x31J+PCpufPUfIG694/0rlEpmzl2GWzY8NqfdBFGGm/SPSSwvKbeNc
69
FMRLu5neo7W9kwvfMbGjHmvUbzBUVpCVKD0OEEf1q/Ii0Qcekx9CMoLvWq7ZwNHEbNnij7ec
70
nvwNlE2MxNsOSJj+hwZGK+tM19kuYGSKw4b5mR8IyThlgiSLIfpSBh1n2KX+TDdk9GR+57TY
71
vlRu6nTPu98P05IlrrCP+KF0hYZYOaMvQs9Rmc09tc/eoQlN0kkaBWw9Rv/dvLVc0aUXABEB
72
AAG0MURyb3Bib3ggQXV0b21hdGljIFNpZ25pbmcgS2V5IDxsaW51eEBkcm9wYm94LmNvbT6J
73
ATYEEwECACAFAkt0ibECGwMGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAKCRD8kYszUESRLi/z
74
B/wMscEa15rS+0mIpsORknD7kawKwyda+LHdtZc0hD/73QGFINR2P23UTol/R4nyAFEuYNsF
75
0C4IAD6y4pL49eZ72IktPrr4H27Q9eXhNZfJhD7BvQMBx75L0F5gSQwuC7GdYNlwSlCD0AAh
76
Qbi70VBwzeIgITBkMQcJIhLvllYo/AKD7Gv9huy4RLaIoSeofp+2Q0zUHNPl/7zymOqu+5Ox
77
e1ltuJT/kd/8hU+N5WNxJTSaOK0sF1/wWFM6rWd6XQUP03VyNosAevX5tBo++iD1WY2/lFVU
78
JkvAvge2WFk3c6tAwZT/tKxspFy4M/tNbDKeyvr685XKJw9ei6GcOGHD
80
-----END PGP PUBLIC KEY BLOCK-----
55
85
def methodcaller(name, *args, **kwargs):
137
167
def unicode_abspath(path):
139
169
assert type(path) is unicode
140
# shouldn't pass unicode to this craphead, it appends with os.getcwd() which is always a str
170
# shouldn't pass unicode to this craphead, it appends with os.getcwd() which is always a str
141
171
return os.path.abspath(path.encode(sys.getfilesystemencoding())).decode(sys.getfilesystemencoding())
174
def gpgme_context(keys):
175
gpg_conf_contents = ''
176
_gpghome = tempfile.mkdtemp(prefix='tmp.gpghome')
179
os.environ['GNUPGHOME'] = _gpghome
180
fp = open(os.path.join(_gpghome, 'gpg.conf'), 'wb')
181
fp.write(gpg_conf_contents)
183
ctx = gpgme.Context()
186
for key_file in keys:
187
result = ctx.import_(key_file)
188
key = ctx.get_key(result.imports[0][0])
195
del os.environ['GNUPGHOME']
196
shutil.rmtree(_gpghome, ignore_errors=True)
198
def verify_signature(key_file, sig_file, plain_file):
199
with gpgme_context([key_file]) as ctx:
200
sigs = ctx.verify(sig_file, plain_file, None)
201
return sigs[0].status == None
203
def download_file_chunk(socket, buf, size):
205
with closing(socket) as f:
208
chunk = os.read(f.fileno(), 4096)
209
progress += len(chunk)
211
yield (progress, True)
215
if hasattr(e, 'errno') and e.errno == errno.EAGAIN:
216
# nothing left to read
217
yield (progress, False)
221
def download_uri_to_buffer(uri):
223
socket = urllib.urlopen(uri)
225
FatalVisibleError("Trouble connecting to Dropbox servers. Maybe your internet connection is down, or you need to set your http_proxy environment variable.")
227
fcntl.fcntl(socket, fcntl.F_SETFL, os.O_NONBLOCK)
228
size = int(socket.info()['content-length'])
230
buf = StringIO.StringIO()
231
download_chunk = download_file_chunk(socket, buf, size)
233
for _ in download_chunk:
143
239
# This sets a custom User-Agent
144
240
class DropboxURLopener(urllib.FancyURLopener):
145
241
version = "DropboxLinuxDownloader/@PACKAGE_VERSION@"
148
244
class DownloadState(object):
149
245
def __init__(self):
151
self.file = urllib.urlopen("http://www.dropbox.com/download?plat=%s" % plat())
247
self.socket = urllib.urlopen("http://www.dropbox.com/download?plat=%s" % plat())
153
FatalVisibleError("Trouble connecting to Dropbox servers. Maybe your internet connection is down, or you need to set your http_proxy environment variable.")
155
fcntl.fcntl(self.file, fcntl.F_SETFL, os.O_NONBLOCK)
157
self.size = int(self.file.info()['content-length'])
160
self.local_path = "%s/dropbox.tar.gz" % PARENT_DIR
161
self.local_file = open(self.local_path, 'wb')
249
FatalVisibleError("Trouble connecting to Dropbox servers. Maybe your internet connection is down, or you need to set your http_proxy environment variable")
251
fcntl.fcntl(self.socket, fcntl.F_SETFL, os.O_NONBLOCK)
252
self.size = int(self.socket.info()['content-length'])
254
self.local_file = StringIO.StringIO()
255
self.download_chunk = download_file_chunk(self.socket, self.local_file, self.size)
163
257
def copy_data(self):
166
chunk = os.read(self.file.fileno(), 4096)
167
self.progress += len(chunk)
168
self.local_file.write(chunk)
170
if self.progress == self.size:
173
if hasattr(e, 'errno') and e.errno == errno.EAGAIN:
174
# nothing left to read
258
return self.download_chunk
181
260
def unpack(self):
182
self.local_file.close()
183
archive = tarfile.open(self.local_path, 'r:gz')
262
signature = download_uri_to_buffer("http://www.dropbox.com/download?plat=%s&signature=1" % plat())
264
self.local_file.seek(0)
266
if not verify_signature(StringIO.StringIO(DROPBOX_PUBLIC_KEY), signature, self.local_file):
267
FatalVisibleError("Downloaded binary does not match Dropbox signature, aborting install.")
269
self.local_file.seek(0)
270
archive = tarfile.open(fileobj=self.local_file, mode='r:gz')
184
271
total_members = len(archive.getmembers())
185
272
for i, member in enumerate(archive.getmembers()):
273
filename = os.path.join(PARENT_DIR, member.name)
274
if os.path.exists(filename) and not os.path.isdir(filename):
186
276
archive.extract(member, PARENT_DIR)
187
277
yield member.name, i, total_members
189
os.remove(self.local_path)
191
280
def cancel(self):
192
281
if not self.local_file.closed:
193
282
self.local_file.close()
194
if os.path.exists(self.local_path):
195
os.remove(self.local_path)
197
284
def load_serialized_images():
198
285
global box_logo_pixbuf, window_icon
270
357
elif condition == gobject.IO_ERR:
271
358
FatalVisibleError("Unexpected error occurred with download.")
273
while self.one_chunk.next():
274
self.update_progress(DOWNLOADING, float(self.download.progress)/self.download.size)
361
progress, status = self.one_chunk.next()
364
self.update_progress(DOWNLOADING, float(progress)/self.download.size)
275
365
except StopIteration:
276
366
self.update_progress(DOWNLOADING, 1.0)
277
367
self.unpack_dropbox()
280
self.update_progress(DOWNLOADING, float(self.download.progress)/self.download.size)
370
self.update_progress(DOWNLOADING, float(progress)/self.download.size)
283
373
def unpack_dropbox(self):
344
434
self.progress.set_property('width-request', 300)
346
436
self.label = gtk.Label()
347
self.label.set_markup('%s <span foreground="#000099" underline="single" weight="bold">%s</span>\n\n%s' % (INFO, LINK, WARNING))
437
GPG_WARNING_MSG = (u"\n\n" + GPG_WARNING) if not gpgme else u""
438
self.label.set_markup('%s <span foreground="#000099" underline="single" weight="bold">%s</span>\n\n%s%s' % (INFO, LINK, WARNING, GPG_WARNING_MSG))
348
439
self.label.set_line_wrap(True)
349
440
self.label.set_property('width-request', 300)
350
441
self.label.show()
433
524
console_print(u"%s %s\n" % (INFO, LINK))
525
GPG_WARNING_MSG = (u"\n%s" % GPG_WARNING) if not gpgme else u""
435
if not yes_no_question(WARNING):
527
if not yes_no_question("%s%s" % (WARNING, GPG_WARNING_MSG)):
438
530
download = DownloadState()
439
531
one_chunk = download.copy_data()
444
setprogress(DOWNLOADING, float(download.progress)/download.size)
535
progress = one_chunk.next()[0]
536
setprogress(DOWNLOADING, float(progress)/download.size)
445
537
except StopIteration:
446
538
setprogress(DOWNLOADING, 1.0)
521
613
[v])) + u"\n").encode('utf8')
522
614
for k,v in args.iteritems())
523
615
self.f.write(u"done\n".encode('utf8'))
528
620
ticker_thread = CommandTicker()
529
621
ticker_thread.start()
531
# This is the potentially long-running call.
623
# This is the potentially long-running call.
533
625
ok = self.__readline() == u"ok"
534
626
except KeyboardInterrupt:
535
627
raise DropboxCommand.BadConnectionError("Keyboard interruption detected")
537
# Tell the ticker to stop.
629
# Tell the ticker to stop.
538
630
ticker_thread.stop()
539
631
ticker_thread.join()
543
635
for i in range(21):
603
695
if is_dropbox_running():
604
696
return meth(*n, **kw)
606
console_print(u"Dropbox isn't running!")
698
console_print(u"Dropbox isn't running!")
607
699
newmeth.func_name = meth.func_name
608
700
newmeth.__doc__ = meth.__doc__
611
703
def start_dropbox():
612
db_path = os.path.expanduser(u"~/.dropbox-dist/dropboxd").encode(sys.getfilesystemencoding())
704
db_path = os.path.expanduser(DROPBOXD_PATH).encode(sys.getfilesystemencoding())
613
705
if os.access(db_path, os.X_OK):
614
706
f = open("/dev/null", "w")
615
707
# we don't reap the child because we're gonna die anyway, let init do it
616
708
a = subprocess.Popen([db_path], preexec_fn=os.setsid, cwd=os.path.expanduser("~"),
617
709
stderr=sys.stderr, stdout=f, close_fds=True)
745
837
if len(dirs) == 0 and len(nondirs) == 0:
749
841
dirs.sort(key=methodcaller('lower'))
750
842
nondirs.sort(key=methodcaller('lower'))
752
# Gets a string representation for a path.
844
# Gets a string representation for a path.
753
845
def path_to_string(file_path):
754
846
if not os.path.exists(file_path):
755
847
path = u"%s (File doesn't exist!)" % os.path.basename(file_path)
758
850
status = dc.icon_overlay_file_status(path=file_path).get(u'status', [None])[0]
759
851
except DropboxCommand.CommandError, e:
760
path = u"%s (%s)" % (os.path.basename(file_path), e)
852
path = u"%s (%s)" % (os.path.basename(file_path), e)
761
853
return (path, path)
763
855
env_term = os.environ.get('TERM','')
764
856
supports_color = (sys.stderr.isatty() and (
765
env_term.startswith('vt') or
766
env_term.startswith('linux') or
767
'xterm' in env_term or
857
env_term.startswith('vt') or
858
env_term.startswith('linux') or
859
'xterm' in env_term or
768
860
'color' in env_term
772
# TODO: Test when you don't support color.
864
# TODO: Test when you don't support color.
773
865
if not supports_color:
774
866
path = os.path.basename(file_path)
775
867
return (path, path)
788
880
path = os.path.basename(file_path)
789
881
return (path, u"%s%s%s" % (init, path, cleanup))
791
# Prints a directory.
883
# Prints a directory.
792
884
def print_directory(name):
794
886
formatted_paths = []
795
887
for subname in sorted(os.listdir(name), key=methodcaller('lower')):
796
888
if type(subname) != unicode:
799
891
if not options.all and subname[0] == u'.':
855
947
console_print(u"%-*s %s" % \
856
948
(indent, file+':', "File doesn't exist"))
860
952
status = dc.icon_overlay_file_status(path=fp).get(u'status', [u'unknown'])[0]
861
953
console_print(u"%-*s %s" % (indent, file+':', status))
893
985
except DropboxCommand.CommandError, e:
894
986
console_print(u"Couldn't get public url: " + str(e))
895
987
except DropboxCommand.BadConnectionError, e:
896
console_print(u"Dropbox isn't responding!")
988
console_print(u"Dropbox isn't responding!")
897
989
except DropboxCommand.EOFError:
898
990
console_print(u"Dropbox daemon stopped.")
899
991
except DropboxCommand.CouldntConnectError, e:
954
1046
dc.tray_action_hard_exit()
955
1047
except DropboxCommand.BadConnectionError, e:
956
console_print(u"Dropbox isn't responding!")
1048
console_print(u"Dropbox isn't responding!")
957
1049
except DropboxCommand.EOFError:
958
1050
console_print(u"Dropbox daemon stopped.")
959
1051
except DropboxCommand.CouldntConnectError, e:
973
1065
except DropboxCommand.CommandError, e:
975
1067
except DropboxCommand.BadConnectionError, e:
976
console_print(u"Dropbox isn't responding!")
1068
console_print(u"Dropbox isn't responding!")
977
1069
except DropboxCommand.EOFError:
978
1070
console_print(u"Dropbox daemon stopped.")
979
1071
except DropboxCommand.CouldntConnectError, e:
983
1075
@requires_dropbox_running
1077
u"""enables or disables LAN sync
1078
dropbox lansync [y/n]
1081
y dropbox will use LAN sync (default)
1082
n dropbox will not use LAN sync
1085
console_print(lansync.__doc__, linebreak=False)
1089
if s.startswith('y') or s.startswith('-y'):
1090
should_lansync = True
1091
elif s.startswith('n') or s.startswith('-n'):
1092
should_lansync = False
1094
should_lansync = None
1096
if should_lansync is None:
1097
console_print(lansync.__doc__,linebreak=False)
1099
with closing(DropboxCommand()) as dc:
1100
dc.set_lan_sync(lansync='enabled' if should_lansync else 'disabled')
1104
@requires_dropbox_running
984
1105
def exclude(args):
985
1106
u"""ignores/excludes a directory from syncing
986
1107
dropbox exclude [list]
987
1108
dropbox exclude add [DIRECTORY] [DIRECTORY] ...
988
1109
dropbox exclude remove [DIRECTORY] [DIRECTORY] ...
990
"list" prints a list of directories currently excluded from syncing.
991
"add" adds one or more directories to the exclusion list, then resynchronizes Dropbox.
1111
"list" prints a list of directories currently excluded from syncing.
1112
"add" adds one or more directories to the exclusion list, then resynchronizes Dropbox.
992
1113
"remove" removes one or more directories from the exclusion list, then resynchronizes Dropbox.
993
With no arguments, executes "list".
1114
With no arguments, executes "list".
994
1115
Any specified path must be within Dropbox.
996
1117
if len(args) == 0:
1024
1145
sub_command = args[0]
1025
1146
paths = args[1:]
1026
1147
absolute_paths = [unicode_abspath(path.decode(sys.getfilesystemencoding())) for path in paths]
1027
if sub_command == u"add":
1148
if sub_command == u"add":
1029
1150
with closing(DropboxCommand(timeout=None)) as dc:
1031
1152
result = dc.ignore_set_add(paths=absolute_paths)
1032
1153
if result[u"ignored"]:
1033
console_print(u"Excluded: ")
1154
console_print(u"Excluded: ")
1034
1155
lines = [relpath(path) for path in result[u"ignored"]]
1035
1156
for line in lines:
1036
1157
console_print(unicode(line))
1134
1255
if u".config" in contents:
1135
1256
autostart_dir = os.path.join(home_dir, u".config", u"autostart")
1136
1257
autostart_link = os.path.join(autostart_dir, u"%s.desktop" % "dropbox") #BUILD_KEY.lower()
1137
desktop_file = u"/usr/local/share/applications/%s.desktop" % "dropbox" #BUILD_KEY.lower()
1258
desktop_file = u"@DESKTOP_FILE_DIR@/%s.desktop" % "dropbox" #BUILD_KEY.lower()
1138
1259
if should_autostart:
1139
1260
if os.path.exists(desktop_file):
1140
1261
if not os.path.exists(autostart_dir):