26
from win32api import GetUserNameEx
27
from win32con import NameSamCompatible
28
from win32pipe import CallNamedPipe
29
28
from twisted.internet import defer, reactor
29
from _winreg import OpenKey, HKEY_LOCAL_MACHINE, QueryValueEx
31
31
from ubuntuone.syncdaemon.config import get_user_config
32
from ubuntuone.platform.windows.ipc import NAMED_PIPE_URL
33
32
from ubuntuone.platform.windows.ipc_client import UbuntuOneClient
34
U1_REG_PATH = r'Software\\Ubuntu One'
35
SD_INSTALL_PATH = 'SyncDaemonInstallPath'
36
37
def is_running(bus=None):
37
"""Check if there is a syncdaemon instance running.
39
Running means the name is registered in the given bus.
42
pipe = NAMED_PIPE_URL % GetUserNameEx(NameSamCompatible)
44
CallNamedPipe(pipe, '', 512, 0)
38
"""Check if there is a syncdaemon instance running."""
39
#TODO: Do not start two instances of this process
40
# https://launchpad.net/bugs/803672
41
#NOTE: Changed to True so SD can be restarted while this bug is fixed
50
45
class SyncDaemonTool(object):
51
46
"""Various utility methods to test/play with the SyncDaemon."""
53
def __init__(self, named_pipe=None):
54
self.named_pipe = named_pipe
55
self.client = UbuntuOneClient()
48
# WARNING: most of the methods of this class are "pre-processed" by
49
# __getattribute__, to call _call_after_connection before the method
50
# is called, so they should either be decorated with inlineCallbacks
51
# or return a deferred.
53
# All methods and instance variables that should not be handled that way
54
# should be put in the list below (or start with _):
56
_DONT_VERIFY_CONNECTED = [
58
"client", "last_event", "delayed_call", "log", "connected",
61
def _should_wrap(self, attr_name):
62
"""Check if this attribute should be wrapped."""
63
return not (attr_name in SyncDaemonTool._DONT_VERIFY_CONNECTED
64
or attr_name.startswith("_"))
66
def __getattribute__(self, attr_name):
67
"""If the attribute is not special, verify the ipc connection."""
68
attr = super(SyncDaemonTool, self).__getattribute__(attr_name)
69
if SyncDaemonTool._should_wrap(self, attr_name):
70
return self._call_after_connection(attr)
75
"""Initialize this instance."""
76
self.client = UbuntuOneClient()
56
77
self.last_event = 0
57
78
self.delayed_call = None
58
79
self.log = logging.getLogger('ubuntuone.SyncDaemon.SDTool')
80
self.connected = self.client.connect()
82
def _call_after_connection(self, method):
83
"""Make sure Perspective Broker is connected before calling."""
85
@defer.inlineCallbacks
86
def call_after_connection_inner(*args, **kwargs):
87
"""Call the given method after the connection to pb is made."""
89
retval = yield method(*args, **kwargs)
90
defer.returnValue(retval)
92
return call_after_connection_inner
61
94
def _get_dict(self, a_dict):
62
95
"""Converts a dict returned by the IPC to a dict of strings."""
84
117
reactor.callLater(.5, check_connection_status)
120
@defer.inlineCallbacks
87
121
def get_current_downloads(self):
88
122
"""Return a deferred that will be fired with the current downloads."""
91
def reply_handler(downloads, d):
92
"""Current downloads callback."""
94
for download in downloads:
95
downloads_str.append(self._get_dict(download))
96
d.callback(downloads_str)
98
downloads_d = self.client.status.current_downloads()
99
downloads_d.addCallback(lambda downloads: reply_handler(downloads, d))
123
downloads = yield self.client.status.current_downloads()
125
for download in downloads:
126
downloads_str.append(self._get_dict(download))
127
defer.returnValue(downloads_str)
102
129
def wait_all_downloads(self, verbose=False):
103
130
"""Wait until there is no more pending downloads."""
126
153
d.addCallback(reply_handler)
156
@defer.inlineCallbacks
129
157
def get_current_uploads(self):
130
158
"""Return a deferred that will be called with the current uploads."""
133
def reply_handler(uploads, d):
136
for upload in uploads:
137
uploads_str.append(self._get_dict(upload))
138
d.callback(uploads_str)
140
uploads_d = self.client.status.current_uploads()
141
uploads_d.addCallback(lambda uploads: reply_handler(uploads, d))
159
uploads = yield self.client.status.current_uploads()
161
for upload in uploads:
162
uploads_str.append(self._get_dict(upload))
163
defer.returnValue(uploads_str)
144
165
def wait_all_uploads(self, verbose=False):
145
166
"""Wait until there is no more pending uploads."""
259
280
self.log.debug('unsubscribe_share: %r', share_id)
260
281
return self.client.shares.unsubscribe(share_id)
283
@defer.inlineCallbacks
262
284
def get_shares(self):
263
285
"""Get the list of shares (accepted or not)."""
264
286
self.log.debug('get_shares')
267
def reply_handler(results):
268
"""Get_shares reply handler."""
270
for result in results:
271
shares.append(self._get_dict(result))
272
self.log.debug('shares: %r', shares)
275
shares_d = self.client.shares.get_shares()
276
shares_d.addCallback(reply_handler)
287
shares = yield self.client.shares.get_shares()
290
shares_str.append(self._get_dict(share))
291
defer.returnValue(shares_str)
279
293
def refresh_shares(self):
280
294
"""Call refresh_shares method via DBus.
292
306
return self.client.shares.create_share(path, username, name,
309
@defer.inlineCallbacks
295
310
def list_shared(self):
296
311
"""Get the list of the shares "shared"/created/offered."""
297
312
self.log.debug('list_shared')
300
def reply_handler(results):
301
"""Get_shares reply handler."""
303
for result in results:
304
shares.append(self._get_dict(result))
305
self.log.debug('shared: %r', shares)
308
shared_d = self.client.shares.get_shared()
309
shared_d.addCallback(reply_handler)
313
shared = yield self.client.shares.get_shared()
316
shares_str.append(self._get_dict(share))
317
defer.returnValue(shares_str)
312
319
def wait_for_signals(self, signal_ok, signal_error,
313
320
dbus_iface=None):
339
346
self.log.debug('unsubscribe_folder')
340
347
return self.client.folders.unsubscribe(folder_id)
349
@defer.inlineCallbacks
342
350
def get_folders(self):
343
351
"""Return the list of folders (a list of dicts)."""
344
352
self.log.debug('get_folders')
347
def reply_handler(results):
348
"""Get_folders reply handler."""
350
for result in results:
351
folders.append(self._get_dict(result))
352
self.log.debug('folders: %r', folders)
355
folders_d = self.client.folders.get_folders()
356
folders_d.addCallback(reply_handler)
353
folders = yield self.client.folders.get_folders()
355
for folder in folders:
356
folders_str.append(self._get_dict(folder))
357
defer.returnValue(folders_str)
359
359
def get_folder_info(self, path):
360
360
"""Call the get_info method for a UDF path."""
412
412
"""Disconnect syncdaemon."""
413
413
return self.client.sync_daemon.disconnect()
415
@defer.inlineCallbacks
415
416
def get_status(self):
416
417
"""Get the current_status dict."""
419
def reply_handler(status):
420
"""The reply handler"""
421
state_dict = self._get_dict(status)
422
state_dict['is_connected'] = bool(state_dict['is_connected'])
423
state_dict['is_online'] = bool(state_dict['is_online'])
424
state_dict['is_error'] = bool(state_dict['is_error'])
425
d.callback(state_dict)
426
status_d = self.client.status.current_status()
427
status_d.addCallback(reply_handler)
428
status_d.addErrback(d.errback)
418
status = yield self.client.status.current_status()
419
state_dict = self._get_dict(status)
420
state_dict['is_connected'] = bool(state_dict['is_connected'])
421
state_dict['is_online'] = bool(state_dict['is_online'])
422
state_dict['is_error'] = bool(state_dict['is_error'])
423
defer.returnValue(state_dict)
431
425
def waiting(self):
432
426
"""Return a description of the waiting queue elements."""
444
438
"""Start syncdaemon if it's not running."""
445
if not is_running(self.bus):
446
raise Exception('Not implemented yet!')
440
# look in the reg to find the path of the .exe to be executed
441
# to launch the sd on windows
442
key = OpenKey(HKEY_LOCAL_MACHINE, U1_REG_PATH)
443
path = QueryValueEx(key, SD_INSTALL_PATH)[0]
444
p = subprocess.Popen([path,])
445
return defer.succeed(p)
448
447
return defer.succeed(None)