1''' 2 3seaf-cli is command line interface for seafile client. 4 5Subcommands: 6 7 init: create config files for seafile client 8 start: start and run seafile client as daemon 9 stop: stop seafile client 10 list: list local liraries 11 status: show syncing status 12 download: download a library from seafile server 13 sync: synchronize an existing folder with a library in 14 seafile server 15 desync: desynchronize a library with seafile server 16 create: create a new library 17 18 19Detail 20====== 21 22Seafile client stores all its configure information in a config dir. The default location is `~/.ccnet`. All the commands below accept an option `-c <config-dir>`. 23 24init 25---- 26Initialize seafile client. This command initializes the config dir. It also creates sub-directories `seafile-data` and `seafile` under `parent-dir`. `seafile-data` is used to store internal data, while `seafile` is used as the default location put downloaded libraries. 27 28 seaf-cli init [-c <config-dir>] -d <parent-dir> 29 30start 31----- 32Start seafile client. This command start `ccnet` and `seaf-daemon`, `ccnet` is the network part of seafile client, `seaf-daemon` manages the files. 33 34 seaf-cli start [-c <config-dir>] 35 36stop 37---- 38Stop seafile client. 39 40 seaf-cli stop [-c <config-dir>] 41 42 43Download 44-------- 45Download a library from seafile server 46 47 seaf-cli download -l <library-id> -s <seahub-server-url> -d <parent-directory> -u <username> -p <password> 48 49 50sync 51---- 52Synchronize a library with an existing folder. 53 54 seaf-cli sync -l <library-id> -s <seahub-server-url> -d <existing-folder> -u <username> -p <password> 55 56desync 57------ 58Desynchronize a library from seafile server 59 60 seaf-cli desync -d <existing-folder> 61 62create 63------ 64Create a new library 65 66 seaf-cli create -s <seahub-server-url> -n <library-name> -u <username> -p <password> -t <description> [-e <library-password>] 67 68''' 69 70import os 71import json 72import subprocess 73import sys 74import time 75import urllib 76import urllib2 77import httplib 78from urlparse import urlparse 79 80import ccnet 81import seafile 82 83def _check_seafile(): 84 ''' Check ccnet and seafile have been installed ''' 85 86 sep = ':' if os.name != 'nt' else ';' 87 dirs = os.environ['PATH'].split(sep) 88 def exist_in_path(prog): 89 ''' Check whether 'prog' exists in system path ''' 90 for d in dirs: 91 if d == '': 92 continue 93 path = os.path.join(d, prog) 94 if os.path.exists(path): 95 return True 96 97 progs = [ 'ccnet', 'seaf-daemon' ] 98 99 for prog in progs: 100 if os.name == 'nt': 101 prog += '.exe' 102 if not exist_in_path(prog): 103 print "%s not found in PATH. Have you installed seafile?" % prog 104 sys.exit(1) 105 106def run_argv(argv, cwd=None, env=None, suppress_stdout=False, suppress_stderr=False, wait=True): 107 '''Run a program and wait it to finish, and return its exit code. The 108 standard output of this program is supressed. 109 110 ''' 111 with open(os.devnull, 'w') as devnull: 112 if suppress_stdout: 113 stdout = devnull 114 else: 115 stdout = sys.stdout 116 117 if suppress_stderr: 118 stderr = devnull 119 else: 120 stderr = sys.stderr 121 122 proc = subprocess.Popen(argv, 123 cwd=cwd, 124 stdout=stdout, 125 stderr=stderr, 126 env=env) 127 if wait: 128 return proc.wait() 129 return 0 130 131def get_env(): 132 env = dict(os.environ) 133 ld_library_path = os.environ.get('SEAFILE_LD_LIBRARY_PATH', '') 134 if ld_library_path: 135 env['LD_LIBRARY_PATH'] = ld_library_path 136 137 return env 138 139class NoRedirect(urllib2.HTTPRedirectHandler): 140 def redirect_request(self, req, fp, code, msg, hdrs, newurl): 141 pass 142 143def urlopen(url, data=None, headers=None, follow_redirect=True): 144 if data: 145 data = urllib.urlencode(data) 146 headers = headers or {} 147 req = urllib2.Request(url, data=data, headers=headers) 148 if follow_redirect: 149 resp = urllib2.urlopen(req) 150 else: 151 try: 152 opener = urllib2.build_opener(NoRedirect()) 153 resp = opener.open(req) 154 except urllib2.HTTPError: 155 return None 156 return resp.read() 157 158def get_token(url, username, password): 159 data = { 160 'username': username, 161 'password': password, 162 } 163 token_json = urlopen("%s/api2/auth-token/" % url, data=data) 164 tmp = json.loads(token_json) 165 token = tmp['token'] 166 return token 167 168def get_repo_downlod_info(url, token): 169 headers = { 'Authorization': 'Token %s' % token } 170 repo_info = urlopen(url, headers=headers) 171 return json.loads(repo_info) 172 173def seaf_init(conf_dir): 174 ''' initialize config directorys''' 175 176 _check_seafile() 177 178 seafile_ini = os.path.join(conf_dir, "seafile.ini") 179 seafile_data = os.path.join(conf_dir, "seafile-data") 180 fp = open(seafile_ini, 'w') 181 fp.write(seafile_data) 182 fp.close() 183 184 print 'Init ccnet config success.' 185 186def seaf_start_all(conf_dir): 187 ''' start ccnet and seafile daemon ''' 188 189 seaf_start_ccnet(conf_dir) 190 # wait ccnet process 191 time.sleep(1) 192 seaf_start_seafile(conf_dir) 193 194 print 'Start ccnet, seafile daemon success.' 195 196def seaf_start_ccnet(conf_dir): 197 ''' start ccnet daemon ''' 198 199 cmd = [ "ccnet", "--daemon", "-c", conf_dir ] 200 wait = False if os.name == 'nt' else True 201 if run_argv(cmd, env=get_env(), suppress_stdout=True, wait=wait) != 0: 202 print 'Failed to start ccnet daemon.' 203 sys.exit(1) 204 205def seaf_start_seafile(conf_dir): 206 ''' start seafile daemon ''' 207 208 cmd = [ "seaf-daemon", "--daemon", "-c", conf_dir, 209 "-d", os.path.join(conf_dir, 'seafile-data'), 210 "-w", os.path.join(conf_dir, 'seafile') ] 211 wait = False if os.name == 'nt' else True 212 if run_argv(cmd, env=get_env(), suppress_stdout=True, wait=wait) != 0: 213 print 'Failed to start seafile daemon' 214 sys.exit(1) 215 216def seaf_stop(conf_dir): 217 '''stop seafile daemon ''' 218 219 pool = ccnet.ClientPool(conf_dir) 220 client = pool.get_client() 221 try: 222 client.send_cmd("shutdown") 223 except: 224 # ignore NetworkError("Failed to read from socket") 225 pass 226 227 print 'Stop ccnet, seafile daemon success.' 228 229def get_base_url(url): 230 parse_result = urlparse(url) 231 scheme = parse_result.scheme 232 netloc = parse_result.netloc 233 234 if scheme and netloc: 235 return '%s://%s' % (scheme, netloc) 236 237 return None 238 239def get_netloc(url): 240 parse_result = urlparse(url) 241 return parse_result.netloc 242 243def seaf_sync(conf_dir, server_url, repo_id, worktree, username, passwd): 244 ''' synchronize a library from seafile server ''' 245 246 pool = ccnet.ClientPool(conf_dir) 247 seafile_rpc = seafile.RpcClient(pool, req_pool=False) 248 249 token = get_token(server_url, username, passwd) 250 tmp = get_repo_downlod_info("%s/api2/repos/%s/download-info/" % (server_url, repo_id), token) 251 252 encrypted = tmp['encrypted'] 253 magic = tmp.get('magic', None) 254 enc_version = tmp.get('enc_version', None) 255 random_key = tmp.get('random_key', None) 256 257 clone_token = tmp['token'] 258 relay_id = tmp['relay_id'] 259 relay_addr = tmp['relay_addr'] 260 relay_port = str(tmp['relay_port']) 261 email = tmp['email'] 262 repo_name = tmp['repo_name'] 263 version = tmp.get('repo_version', 0) 264 265 more_info = None 266 base_url = get_base_url(server_url) 267 if base_url: 268 more_info = json.dumps({'server_url': base_url}) 269 270 if encrypted == 1: 271 repo_passwd = 's123' 272 else: 273 repo_passwd = None 274 275 seafile_rpc.clone(repo_id, 276 version, 277 relay_id, 278 repo_name.encode('utf-8'), 279 worktree, 280 clone_token, 281 repo_passwd, magic, 282 relay_addr, 283 relay_port, 284 email, random_key, enc_version, more_info) 285 286 print 'Synchronize repo test success.' 287 288def seaf_desync(conf_dir, repo_path): 289 '''Desynchronize a library from seafile server''' 290 291 pool = ccnet.ClientPool(conf_dir) 292 seafile_rpc = seafile.RpcClient(pool, req_pool=False) 293 294 repos = seafile_rpc.get_repo_list(-1, -1) 295 repo = None 296 for r in repos: 297 if r.worktree.replace('/', '\\') == repo_path.decode('utf-8').replace('/', '\\'): 298 repo = r 299 break 300 301 if repo: 302 print "Desynchronize repo test success." 303 seafile_rpc.remove_repo(repo.id) 304 else: 305 print "%s is not a library worktree" % repo_path 306 307def seaf_create(conf_dir, server_url, username, passwd, enc_repo): 308 '''Create a library''' 309 310 # curl -d 'username=<USERNAME>&password=<PASSWORD>' http://127.0.0.1:8000/api2/auth-token 311 token = get_token(server_url, username, passwd) 312 313 headers = { 'Authorization': 'Token %s' % token } 314 data = { 315 'name': 'test', 316 'desc': 'test', 317 } 318 if enc_repo: 319 data['passwd'] = 's123' 320 321 repo_info_json = urlopen("%s/api2/repos/" % server_url, data=data, headers=headers) 322 repo_info = json.loads(repo_info_json) 323 324 if enc_repo: 325 print 'Create encrypted repo test success.' 326 else: 327 print 'Create non encrypted repo test success.' 328 329 return repo_info['repo_id'] 330 331def seaf_delete(conf_dir, server_url, username, passwd, repo_id): 332 '''Delete a library''' 333 334 token = get_token(server_url, username, passwd) 335 headers = { 'Authorization': 'Token %s' % token } 336 337 conn = httplib.HTTPConnection(get_netloc(server_url)) 338 conn.request('DELETE', '/api2/repos/%s/' % repo_id, None, headers) 339 resp = conn.getresponse() 340 if resp.status == 200: 341 print 'Delete repo test success.' 342 else: 343 print 'Delete repo test failed: %s.' % resp.reason 344 345def seaf_get_repo(conf_dir, repo_id): 346 pool = ccnet.ClientPool(conf_dir) 347 seafile_rpc = seafile.RpcClient(pool, req_pool=False) 348 return seafile_rpc.seafile_get_repo(repo_id) 349