ufload3 2.0__tar.gz → 2.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ufload3
3
- Version: 2.0
3
+ Version: 2.2
4
4
  Summary: UniField python3 loader
5
5
  Author: MSF
6
6
  Project-URL: Homepage, https://github.com/Unifield/ufload3
@@ -1,7 +1,7 @@
1
1
  from . import cloud; assert cloud
2
2
  from . import db; assert db
3
3
 
4
- __version__ = '2.0'
4
+ __version__ = '2.2'
5
5
 
6
6
  # null progress, can be overridden by importers
7
7
  def _progress(p):
@@ -233,9 +233,6 @@ def _dirRestore(args):
233
233
  return 2, None
234
234
 
235
235
  def _multiRestore(args):
236
- if not _required(args, [ 'user', 'pw' ]):
237
- ufload3.progress('With no -file or -dir argument, cloud credentials are mandatory.')
238
- return 2, None
239
236
 
240
237
  if args.i is None:
241
238
  if not _required(args, [ 'oc' ]):
@@ -308,7 +305,7 @@ def _multiRestore(args):
308
305
 
309
306
  ufload3.progress("Instances to be restored: %s" % ", ".join(list(instances.keys())))
310
307
  dbs=[]
311
- pattern = re.compile('.*-[A-Z]{1}[a-z]{2}\.zip$')
308
+ pattern = re.compile(r'.*-[A-Z]{1}[a-z]{2}\.zip$')
312
309
 
313
310
  for i in instances:
314
311
 
@@ -491,8 +488,14 @@ def _syncLink(args, dbs, sdb):
491
488
 
492
489
 
493
490
  def _cmdLs(args):
494
- if not _required(args, [ 'user', 'pw', 'oc' ]):
491
+ if not _required(args, [ 'tenant', 'client_id', 'oc' ]):
492
+ return 2
493
+ if not args.cert_path and not args.cert_content:
494
+ ufload3.progress('Argument --cert-content or --cert-path is required for this sub-command.')
495
495
  return 2
496
+
497
+
498
+
496
499
  if args.subdir is None:
497
500
  args.subdir = ''
498
501
 
@@ -563,14 +566,14 @@ def _cmdUpgrade(args):
563
566
  return 1
564
567
 
565
568
  #Download the patch
566
- patches.sort(key=lambda s: list(map(int, re.split('\.|-|p',re.search('uf(.+?)\.patch\.zip', s[1], re.I).group(1)))))
569
+ patches.sort(key=lambda s: list(map(int, re.split(r'\.|-|p',re.search(r'uf(.+?)\.patch\.zip', s[1], re.I).group(1)))))
567
570
  i = 0
568
571
  for j in patches:
569
572
  filename = dav.download(j[2], j[1])
570
573
 
571
574
  #Set patch and version args
572
575
  args.patch = filename
573
- m = re.search('(.+?)\.patch\.zip', filename)
576
+ m = re.search(r'(.+?)\.patch\.zip', filename)
574
577
  if m:
575
578
  args.version = m.group(1)
576
579
 
@@ -727,7 +730,7 @@ def _cmdUpgrade(args):
727
730
  if len(patches) == 0:
728
731
  ufload3.progress("No User Rights found.")
729
732
  return 1
730
- patches.sort(key=lambda s: list(map(int, re.split('\.|-|p',re.search('User Rights v(.+?).zip', s[1], re.I).group(1)))))
733
+ patches.sort(key=lambda s: list(map(int, re.split(r'\.|-|p',re.search('User Rights v(.+?).zip', s[1], re.I).group(1)))))
731
734
 
732
735
  urfilename = None
733
736
  for j in patches:
@@ -788,8 +791,9 @@ spinner = spinning_cursor()
788
791
  def parse():
789
792
  parser = argparse.ArgumentParser(prog='ufload3')
790
793
 
791
- parser.add_argument("-user", help="Cloud username")
792
- parser.add_argument("-pw", help="Cloud password")
794
+ parser.add_argument("-tenant", help="Cloud Tenant")
795
+ parser.add_argument("-client-id", help="Cloud App-Id")
796
+ parser.add_argument("-cert-path", help="Cloud CertPath")
793
797
  parser.add_argument("-oc", help="OC (OCG, OCA and OCB accepted) - optional for the restore command (if not provided, ufload will try and deduce the right OC(s) from the name of the requested instances)")
794
798
 
795
799
  parser.add_argument("-syncuser", help="username to access the sync server backup")
@@ -49,9 +49,6 @@ def instance_to_dir(instance):
49
49
 
50
50
  def get_cloud_info(args, sub_dir=''):
51
51
 
52
- #Cloud password is encrypted
53
- pword = _decrypt(args.pw)
54
-
55
52
  #Cloud path depends on the OC
56
53
  if args.oc:
57
54
  dir = '/personal/UF_' + args.oc.upper() + '_msf_geneva_msf_org/'
@@ -67,17 +64,18 @@ def get_cloud_info(args, sub_dir=''):
67
64
  except:
68
65
  #The argument cloudpath is not defined, forget about it (this is not the upgrade process)
69
66
  pass
67
+
68
+ if args.cert_path:
69
+ with open(args.cert_path, 'r') as c:
70
+ args.cert_content = c.read()
70
71
 
71
72
  ret = {
72
73
  'url': args.cloud_url,
73
74
  'dir': dir + sub,
74
75
  'site': dir,
75
76
  'path': args.cloud_path,
76
- 'login': args.user,
77
- 'password': pword,
78
77
  'tenant': args.tenant,
79
78
  'client_id': args.client_id,
80
- 'thumbprint': args.thumbprint,
81
79
  'cert_content': args.cert_content,
82
80
  }
83
81
 
@@ -88,10 +86,12 @@ def get_onedrive_connection(args):
88
86
  info = get_cloud_info(args)
89
87
  if not info.get('url'):
90
88
  ufload3.progress('URL is not set!')
91
- if not info.get('login'):
92
- ufload3.progress('login is not set!')
93
- if not info.get('password'):
94
- ufload3.progress('Password is not set!')
89
+ if not info.get('tenant'):
90
+ ufload3.progress('Tenant is not set!')
91
+ if not info.get('client_id'):
92
+ ufload3.progress('Client_id is not set!')
93
+ if not info.get('cert_content'):
94
+ ufload3.progress('Cert is not set!')
95
95
 
96
96
  url = urlparse(info['url'])
97
97
  if not url.netloc:
@@ -100,8 +100,7 @@ def get_onedrive_connection(args):
100
100
  path = info.get('site') + url.path
101
101
 
102
102
  try:
103
- dav = webdav.Client(url.netloc, port=url.port, protocol=url.scheme, username=info['login'],
104
- password=info['password'], tenant=info['tenant'], client_id= info['client_id'], thumbprint=info['thumbprint'], cert_content=info['cert_content'], path=path)
103
+ dav = webdav.Client(url.netloc, tenant=info['tenant'], client_id= info['client_id'], cert_content=info['cert_content'], path=path)
105
104
  return dav
106
105
  except webdav.ConnectionFailed as e:
107
106
  ufload3.progress('Unable to connect: {}'.format(e))
@@ -124,24 +123,20 @@ def _get_all_files_and_timestamp(dav, d):
124
123
  ret = []
125
124
  for f in all_zip:
126
125
  #if not f['Name'] or f['Name'][-1] == '/':
127
- if not f['Name']:
126
+ if not f.name:
128
127
  continue
129
128
 
130
- # We try to extract a timestamp to get an idea of the creation date
131
- # Format: Mon, 14 Mar 2016 03:31:40 GMT
132
- t = time.strptime(f['TimeLastModified'], '%Y-%m-%dT%H:%M:%SZ')
133
-
134
129
  # We don't take into consideration backups that are too recent.
135
130
  # Otherwise they could be half uploaded (=> corrupted)
136
- if abs(time.time() - time.mktime(t)) < 900:
131
+ if abs(time.time() - f.time_last_modified.timestamp()) < 900:
137
132
  continue
138
133
 
139
134
  # ufload3.progress('File found: %s' % f['Name'])
140
135
 
141
- if f['Name'].split(".")[-1] != "zip":
142
- logging.warn("Ignoring non-zipfile: %s" % f['Name'])
136
+ if f.name.split(".")[-1] != "zip":
137
+ logging.warn("Ignoring non-zipfile: %s" % f.name)
143
138
  continue
144
- ret.append((t, f['Name'], f['ServerRelativeUrl']))
139
+ ret.append((f.time_last_modified, f.name, f.serverRelativeUrl))
145
140
  return ret
146
141
 
147
142
  # returns True if x has instance as a substring
@@ -361,7 +361,7 @@ def instantiate(args, db):
361
361
  connection = transport.make_connection(host)
362
362
  connection.timeout = 4
363
363
  sock = xmlrpc.client.ServerProxy('http://%s:%s/xmlrpc/common' % (host, port), transport=transport)
364
- uid = sock.login(db, args.adminuser.lower(), args.adminpw)
364
+ sock.login(db, args.adminuser.lower(), args.adminpw)
365
365
  except Exception as e:
366
366
  ufload3.progress("non blocking error at first connection %s" % e)
367
367
 
@@ -498,8 +498,8 @@ def delive(args, db):
498
498
  if args.db_prefix:
499
499
  db_name = db_name.split(args.db_prefix+'_', 1)[1]
500
500
  new_pass_dict = []
501
- for pass_part in re.split( '(\[\d+\+\d+\])', args.newuserspw):
502
- m = re.search('\[(\d+)\+(\d+)\]', pass_part)
501
+ for pass_part in re.split(r'(\[\d+\+\d+\])', args.newuserspw):
502
+ m = re.search(r'\[(\d+)\+(\d+)\]', pass_part)
503
503
  if m:
504
504
  pos = int(m.group(1)) - 1
505
505
  add = int(m.group(2))
@@ -511,7 +511,17 @@ def delive(args, db):
511
511
 
512
512
  for new_user_info in args.createusers.split(';'):
513
513
  new_user_data = new_user_info.split(':')
514
- if len(new_user_data) == 3:
514
+ new_user_dpt = False
515
+ new_user_name = False
516
+ new_user_email = False
517
+ if len(new_user_data) == 6:
518
+ new_user = new_user_data[0]
519
+ new_user_name = new_user_data[1]
520
+ new_user_email = new_user_data[2]
521
+ new_user_dpt = new_user_data[3]
522
+ new_user_pass = new_user_data[4]
523
+ groups = new_user_data[5]
524
+ elif len(new_user_data) == 3:
515
525
  new_user= new_user_data[0]
516
526
  new_user_pass = new_user_data[1]
517
527
  groups = new_user_data[2]
@@ -519,12 +529,25 @@ def delive(args, db):
519
529
  new_user= new_user_data[0]
520
530
  new_user_pass = newpass
521
531
  groups = new_user_data[1]
532
+
533
+ if not new_user_name:
534
+ new_user_name = new_user
522
535
  rc, new_userid = psql(args, """ insert into res_users (name, active, login, password, context_lang, company_id, view, menu_id) values
523
536
  ('%s', 't', '%s', '%s', 'en_MF', 1, 'simple', 1) returning id;"""
524
- % (new_user, new_user.lower(), new_user_pass), db, silent=True)
537
+ % (new_user_name, new_user.lower(), new_user_pass), db, silent=True)
525
538
  if rc != 0:
526
539
  return rc
527
- for new_group in groups.split(','):
540
+ if new_user_dpt:
541
+ psql(args, """ update res_users u set context_department_id = d.id
542
+ from hr_department d
543
+ where d.name = '%s' and u.id = %s """ % (new_user_dpt, new_userid), db)
544
+
545
+ if new_user_email:
546
+ rc, address_id = psql(args, """ insert into res_partner_address (name, email) values ('%s', '%s') returning id """ % (new_user_name, new_user_email), db, silent=True)
547
+ if address_id:
548
+ psql(args,"update res_users set address_id=%s where id=%s" % (address_id, new_userid), db)
549
+
550
+ for new_group in groups.split(','):
528
551
  rc = psql(args, " insert into res_groups_users_rel (uid, gid) (select %s, id from res_groups where name='%s');" % (new_userid, new_group), db)
529
552
  if rc != 0:
530
553
  return rc
@@ -608,7 +631,7 @@ def get_hwid(args):
608
631
  import winreg
609
632
  try:
610
633
  with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE,
611
- "SYSTEM\ControlSet001\services\eventlog\Application\openerp-web-6.0",
634
+ r"SYSTEM\ControlSet001\services\eventlog\Application\openerp-web-6.0",
612
635
  0, winreg.KEY_READ) as registry_key:
613
636
  hwid, regtype = winreg.QueryValueEx(registry_key, "HardwareId")
614
637
  ufload3.progress("Hardware id from registry key: %s" % hwid)
@@ -0,0 +1,229 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ import requests
4
+ from office365.sharepoint.client_context import ClientContext
5
+ from office365.runtime.client_runtime_context import ClientRuntimeContext
6
+ from office365.runtime.client_request_exception import ClientRequestException
7
+ from office365.sharepoint.files.file import File
8
+ from office365.sharepoint.folders.folder import Folder
9
+ from office365.runtime.queries.service_operation import ServiceOperationQuery
10
+ from cryptography.x509 import load_pem_x509_certificate
11
+ from cryptography.hazmat.primitives import hashes
12
+
13
+ def move_to_newname(self, destination, newname, flag):
14
+ """Moves the file to the specified destination url.
15
+
16
+ :param str or office365.sharepoint.folders.folder.Folder destination: Specifies the existing folder or folder
17
+ site relative url
18
+ :param str new file name
19
+ :param int flag: Specifies the kind of move operation.
20
+ """
21
+
22
+ def _moveto(destination_folder):
23
+ # type: (Folder) -> None
24
+ file_url = "/".join([str(destination_folder.serverRelativeUrl), newname])
25
+
26
+ params = {"newurl": file_url, "flags": flag}
27
+ qry = ServiceOperationQuery(self, "moveto", params)
28
+ self.context.add_query(qry)
29
+
30
+ def _update_file(return_type):
31
+ self.set_property("ServerRelativeUrl", file_url)
32
+
33
+ self.context.after_query_execute(_update_file)
34
+
35
+ def _source_file_resolved():
36
+ if isinstance(destination, Folder):
37
+ destination.ensure_property("ServerRelativeUrl", _moveto, destination)
38
+ else:
39
+ self.context.web.ensure_folder_path(destination).get().after_execute(
40
+ _moveto
41
+ )
42
+
43
+ self.ensure_properties(["ServerRelativeUrl", "Name"], _source_file_resolved)
44
+ return self
45
+
46
+ File.move_to_newname = move_to_newname
47
+
48
+ import logging
49
+ import os
50
+ import posixpath
51
+ from urllib.parse import urlparse, urljoin
52
+ from time import sleep
53
+
54
+ def execute_query_retry(
55
+ self,
56
+ max_retry=5,
57
+ timeout_secs=5,
58
+ success_callback=None,
59
+ failure_callback=None,
60
+ exceptions=(ClientRequestException,),
61
+ ):
62
+ """
63
+ Executes the current set of data retrieval queries and method invocations and retries it if needed.
64
+
65
+ :param int max_retry: Number of times to retry the request
66
+ :param int timeout_secs: Seconds to wait before retrying the request.
67
+ :param (office365.runtime.client_object.ClientObject)-> None success_callback:
68
+ :param (int, requests.exceptions.RequestException)-> None failure_callback:
69
+ :param exceptions: tuple of exceptions that we retry
70
+ """
71
+ retry = 1
72
+ while True:
73
+ try:
74
+ self.execute_query()
75
+ if callable(success_callback):
76
+ success_callback(self.current_query.return_type)
77
+ break
78
+ except exceptions as e:
79
+ if retry > max_retry:
80
+ raise
81
+ retry += 1
82
+ self.add_query(self.current_query)
83
+ if callable(failure_callback):
84
+ failure_callback(retry, e)
85
+ sleep(timeout_secs)
86
+ ClientRuntimeContext.execute_query_retry=execute_query_retry
87
+
88
+
89
+ class ConnectionFailed(Exception):
90
+ pass
91
+
92
+ class PasswordFailed(Exception):
93
+ pass
94
+
95
+ class Client(object):
96
+ def __init__(self, host, tenant, client_id, cert_content=False, cert_path=False, max_retry=1, path=None):
97
+ self.tenant = tenant
98
+ self.client_id = client_id
99
+ if not cert_content and cert_path:
100
+ with open(cert_path, 'r') as c:
101
+ cert_content = c.read()
102
+ self.cert_content = cert_content
103
+ cert = load_pem_x509_certificate(bytes(self.cert_content, 'utf8'))
104
+ self.thumbprint = cert.fingerprint(hashes.SHA1()).hex()
105
+
106
+ self.path = path or ''
107
+
108
+ self.url = 'https://{0}'.format(host)
109
+
110
+ self.max_retry = max_retry
111
+ self.login()
112
+
113
+ def login(self):
114
+ full_path = urljoin(self.url, self.path)
115
+ self.request = ClientContext(full_path)
116
+
117
+ self.request.with_client_certificate(
118
+ tenant=self.tenant,
119
+ client_id=self.client_id,
120
+ private_key=self.cert_content,
121
+ thumbprint=self.thumbprint,
122
+ )
123
+ self.baseurl = self.request._get_context_web_information().WebFullUrl
124
+ self.request._auth_context.url = self.baseurl
125
+ if not self.baseurl:
126
+ raise requests.exceptions.RequestException("Full Url not found %s" % self.path)
127
+ if not self.baseurl.endswith('/'):
128
+ self.baseurl = '%s/' % self.baseurl
129
+ self.path = urlparse(full_path).path
130
+
131
+ if not self.path.startswith('/'):
132
+ self.path = '/%s' % self.path
133
+ if not self.path.endswith('/') and len(self.path) > 1:
134
+ self.path = '%s/' % (self.path, )
135
+
136
+ def create_folder(self, remote_path):
137
+ if not self.folder_exists(remote_path):
138
+ self.request.web.get_folder_by_server_relative_url(self.path).add(remote_path).execute_query_with_incremental_retry(max_retry=self.max_retry)
139
+ return True
140
+
141
+ def delete(self, remote_path):
142
+ webUri = '%s%s' % (self.path, remote_path)
143
+ return self.request.web.get_file_by_server_relative_url(webUri).delete_object().execute_query_with_incremental_retry(max_retry=self.max_retry)
144
+
145
+ def move(self, remote_path, dest, retry=True):
146
+ # Move file to folder
147
+ webUri = '%s%s' % (self.path, remote_path)
148
+
149
+ to_folder_dest = posixpath.join(self.path, dest)
150
+ self.request.web.get_file_by_server_relative_path(webUri).moveto(to_folder_dest, 1).execute_query_with_incremental_retry(max_retry=self.max_retry)
151
+ return True
152
+
153
+ def move_to_file(self, remote_path, dest):
154
+ webUri = '%s%s' % (self.path, remote_path)
155
+ full_name_dest = '/'.join([self.path, dest])
156
+
157
+ dest_file_name = full_name_dest.split('/')[-1]
158
+ dest_folder = '/'.join(full_name_dest.split('/')[0:-1])
159
+ self.request.web.get_file_by_server_relative_path(webUri).move_to_newname(dest_folder, dest_file_name, 1).execute_query_with_incremental_retry(max_retry=self.max_retry)
160
+ return True
161
+
162
+ def upload(self, fileobj, remote_path, buffer_size=None, log=False, progress_obj=False, continuation=False):
163
+ split_name = remote_path.split('/')
164
+ new_file = split_name.pop()
165
+ split_name.insert(0, self.path)
166
+ path = '/'.join(split_name)
167
+ if path[-1] != '/':
168
+ path += '/'
169
+
170
+ if buffer_size is None:
171
+ buffer_size = 10 * 1024 * 1024
172
+
173
+ if progress_obj:
174
+ log = True
175
+
176
+ report_method = None
177
+ def report_progress(uploaded, **a):
178
+ percent_txt = ''
179
+ if size:
180
+ percent = round(uploaded*100/size)
181
+ percent_txt = '%d%%' % percent
182
+ if progress_obj:
183
+ progress_obj.write({'name': percent})
184
+
185
+ if logger:
186
+ logger.info('OneDrive: %d bytes sent on %s bytes %s' % (uploaded, size or 'unknown', percent_txt))
187
+
188
+ if log:
189
+ logger = logging.getLogger('cloud.backup')
190
+ try:
191
+ size = os.path.getsize(fileobj.name)
192
+ except:
193
+ size = None
194
+ report_method = report_progress
195
+
196
+ target_folder = self.request.web.get_folder_by_server_relative_url(path)
197
+ return target_folder.files.create_upload_session(
198
+ fileobj, buffer_size, chunk_uploaded=report_method, file_name=new_file
199
+ ).execute_query_with_incremental_retry(max_retry=self.max_retry)
200
+
201
+ def list(self, remote_path):
202
+ if not remote_path.startswith(self.path):
203
+ remote_path = posixpath.join(self.path, remote_path)
204
+ return (
205
+ self.request.web.get_folder_by_server_relative_path(remote_path)
206
+ .get_files()
207
+ .expand(["TimeLastModified"])
208
+ .execute_query_retry(max_retry=self.max_retry)
209
+ )
210
+
211
+ def folder_exists(self, remote_path):
212
+ webUri = '%s%s' % (self.path, remote_path)
213
+ try:
214
+ self.request.web.get_folder_by_server_relative_url(webUri).get().execute_query_with_incremental_retry(max_retry=self.max_retry)
215
+ except ClientRequestException as e:
216
+ if e.response.status_code == 404:
217
+ return False
218
+ raise
219
+ return True
220
+
221
+ def download(self, remote_path, filename):
222
+ if not remote_path.startswith(self.path):
223
+ remote_path = posixpath.join(self.path, remote_path)
224
+
225
+ src = self.request.web.get_file_by_server_relative_path(remote_path)
226
+ with open(filename, 'wb') as file:
227
+ src.download_session(file).execute_query_with_incremental_retry(max_retry=self.max_retry)
228
+ return filename
229
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ufload3
3
- Version: 2.0
3
+ Version: 2.2
4
4
  Summary: UniField python3 loader
5
5
  Author: MSF
6
6
  Project-URL: Homepage, https://github.com/Unifield/ufload3
@@ -1,280 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- import requests
4
- from office365.sharepoint.client_context import ClientContext
5
- from office365.runtime.http.request_options import RequestOptions
6
- from office365.runtime.http.http_method import HttpMethod
7
- from office365.runtime.auth.providers.saml_token_provider import SamlTokenProvider
8
- import uuid
9
- import logging
10
- import os
11
- import posixpath
12
- import time
13
- from urllib.parse import urlparse, urljoin
14
-
15
-
16
- class ConnectionFailed(Exception):
17
- pass
18
-
19
- class PasswordFailed(Exception):
20
- pass
21
-
22
- class Client(object):
23
- def __init__(self, host, port=0, auth=None, username=None, password=None, tenant=None, client_id=None, thumbprint=None, cert_content=None, protocol='http', path=None):
24
- self.requests_timeout = 45
25
- self.session_uuid = False
26
- self.session_offset = -1
27
- self.session_nb_error = 0
28
-
29
- self.username = username
30
- self.password = password
31
-
32
- self.tenant = tenant
33
- self.client_id = client_id
34
- self.thumbprint = thumbprint
35
- self.cert_content = cert_content
36
-
37
-
38
- self.path = path or ''
39
-
40
- self.url = '{0}://{1}'.format(protocol, host)
41
-
42
- self.login()
43
-
44
- def login(self):
45
- baseurl = urljoin(self.url, self.path)
46
- self.request = ClientContext(baseurl)
47
-
48
- if not self.tenant:
49
- self.request.with_user_credentials(self.username, self.password)
50
- if not isinstance(self.request.authentication_context._provider, SamlTokenProvider) or \
51
- not self.request.authentication_context._provider.get_authentication_cookie():
52
- raise requests.exceptions.RequestException(self.request.get_last_error())
53
-
54
- # get the server_site url
55
- if not self.path.startswith('/'):
56
- self.path = '/%s' % self.path
57
- options = RequestOptions(self.url)
58
- self.request._ensure_form_digest(options)
59
- baseurl = self.request._contextWebInformation.WebFullUrl
60
- else:
61
- self.ctx = self.request.with_client_certificate(
62
- tenant=self.tenant,
63
- client_id=self.client_id,
64
- #cert_path=self.cert_path,
65
- private_key=self.cert_content,
66
- thumbprint=self.thumbprint,
67
- )
68
- self.ctx.web.get().execute_query()
69
-
70
-
71
- if not baseurl:
72
- raise requests.exceptions.RequestException("Full Url not found %s" % self.path)
73
- if not baseurl.endswith('/'):
74
- baseurl = '%s/' % baseurl
75
- parsed_base = urlparse(baseurl).path
76
- self.baseurl = '%s%s' % (self.url, parsed_base)
77
- if not self.path.startswith('/'):
78
- self.path = '/%s' % self.path
79
- if not self.path.endswith('/') and len(self.path) > 1:
80
- self.path = '%s/' % (self.path, )
81
-
82
-
83
- def format_request(self, url, method='POST', data="", session=False):
84
- assert(method in ['POST', 'DELETE'])
85
- r_meth = {
86
- 'POST': HttpMethod.Post,
87
- 'DELETE': HttpMethod.Delete,
88
- }
89
- options = RequestOptions(url)
90
- options.method = r_meth[method]
91
- options.set_header("X-HTTP-Method", method)
92
- options.set_header('Accept', 'application/json')
93
- options.set_header('Content-Type', 'application/json')
94
- self.request._authenticate_request(options)
95
- self.request._ensure_form_digest(options)
96
-
97
- if session:
98
- return session.post(url, data=data, headers=options.headers, auth=options.auth, timeout=self.requests_timeout)
99
-
100
- return requests.post(url, data=data, headers=options.headers, auth=options.auth, timeout=self.requests_timeout)
101
-
102
- def parse_error(self, result):
103
- try:
104
- if 'application/json' in result.headers.get('Content-Type'):
105
- resp_content = result.json()
106
- msg = resp_content['odata.error']['message']
107
- error = []
108
- if isinstance(msg, dict):
109
- error = [msg['value']]
110
- else:
111
- error = [msg]
112
- if resp_content['odata.error'].get('code'):
113
- error.append('Code: %s' % resp_content['odata.error']['code'])
114
- return ' '.join(error)
115
- except:
116
- pass
117
- return result.content
118
-
119
- def create_folder(self, remote_path):
120
- webUri = '%s%s' % (self.path, remote_path)
121
- request_url = "%s_api/web/GetFolderByServerRelativeUrl('%s')" % (self.baseurl, webUri)
122
- result = self.format_request(request_url, 'POST')
123
- if result.status_code not in (200, 201):
124
- result = self.format_request("%s_api/Web/Folders/add('%s')" % (self.baseurl, webUri), 'POST')
125
- if result.status_code not in (200, 201):
126
- raise Exception(self.parse_error(result))
127
- return True
128
-
129
- def delete(self, remote_path):
130
- webUri = '%s%s' % (self.path, remote_path)
131
- request_url = "%s_api/web/getfilebyserverrelativeurl('%s')" % (self.baseurl, webUri)
132
- result = self.format_request(request_url, 'DELETE')
133
- if result.status_code not in (200, 201):
134
- raise Exception(self.parse_error(result))
135
- return True
136
-
137
- def move(self, remote_path, dest, retry=True):
138
- webUri = '%s%s' % (self.path, remote_path)
139
- destUri = '%s%s' % (self.path, dest)
140
- # falgs=1 to overwrite existing file
141
- request_url = "%s_api/web/getfilebyserverrelativeurl('%s')/moveto(newurl='%s',flags=1)" % (self.baseurl, webUri, destUri)
142
- result = self.format_request(request_url, 'POST')
143
- if result.status_code not in (200, 201):
144
- error = self.parse_error(result)
145
- if retry and ('timed out' in error or '2130575252' in error):
146
- logging.getLogger('cloud.backup').info('OneDrive move: session time out')
147
- self.login()
148
- return self.move(remote_path, dest, retry=False)
149
- raise Exception(self.parse_error(result))
150
- return True
151
-
152
- def upload(self, fileobj, remote_path, buffer_size=None, log=False, progress_obj=False, continuation=False):
153
- if not self.session_uuid:
154
- self.session_uuid = uuid.uuid1()
155
-
156
- if progress_obj:
157
- log = True
158
-
159
- if log:
160
- logger = logging.getLogger('cloud.backup')
161
- try:
162
- size = os.path.getsize(fileobj.name)
163
- except:
164
- size = None
165
-
166
- if not continuation:
167
- self.session_offset = -1
168
-
169
- if self.session_offset != -1:
170
- fileobj.seek(self.session_offset)
171
-
172
- if not buffer_size:
173
- buffer_size = 10* 1024 * 1024
174
-
175
- x = ""
176
- split_name = remote_path.split('/')
177
- new_file = split_name.pop()
178
- split_name.insert(0, self.path)
179
- path = '/'.join(split_name)
180
- if path[-1] != '/':
181
- path += '/'
182
- webUri = '%s%s' % (path, new_file)
183
- s = requests.Session()
184
-
185
- while True:
186
- if self.session_offset == -1:
187
- # first loop create an empty file
188
- request_url = "%s_api/web/GetFolderByServerRelativeUrl('%s')/Files/add(url='%s',overwrite=true)" % (self.baseurl, path, new_file)
189
- self.session_offset = 0
190
- else:
191
- x = fileobj.read(buffer_size)
192
- if not x:
193
- break
194
- if not self.session_offset:
195
- # 2nd loop
196
- if len(x) == buffer_size:
197
- # split needed
198
- request_url="%s_api/web/getfilebyserverrelativeurl('%s')/startupload(uploadId=guid'%s')" % (self.baseurl, webUri, self.session_uuid)
199
- else:
200
- # file size < buffer: no need to split
201
- request_url = "%s_api/web/GetFolderByServerRelativeUrl('%s')/Files/add(url='%s',overwrite=true)" % (self.baseurl, path, new_file)
202
- elif len(x) == buffer_size:
203
- request_url = "%s_api/web/getfilebyserverrelativeurl('%s')/continueupload(uploadId=guid'%s',fileOffset=%s)" % (self.baseurl, webUri, self.session_uuid, self.session_offset)
204
- else:
205
- request_url = "%s_api/web/getfilebyserverrelativeurl('%s')/finishupload(uploadId=guid'%s',fileOffset=%s)" % (self.baseurl, webUri, self.session_uuid, self.session_offset)
206
-
207
- result = self.format_request(request_url, method='POST', data=x, session=s)
208
- if result.status_code not in (200, 201):
209
- return (False, self.parse_error(result))
210
- self.session_nb_error = 0
211
- self.session_offset += len(x)
212
-
213
- if log and self.session_offset and self.session_offset % (buffer_size*5) == 0:
214
- percent_txt = ''
215
- if size:
216
- percent = round(self.session_offset*100/size)
217
- percent_txt = '%d%%' % percent
218
- if progress_obj:
219
- progress_obj.write({'name': percent})
220
-
221
- logger.info('OneDrive: %d bytes sent on %s bytes %s' % (self.session_offset, size or 'unknown', percent_txt))
222
- self.session_offset = -1
223
- return (True, '')
224
-
225
- def list(self, remote_path):
226
- if not remote_path.startswith(self.path):
227
- remote_path = posixpath.join(self.path, remote_path)
228
- request_url = "%s_api/web/getfolderbyserverrelativeurl('%s')/files" % (self.baseurl, remote_path)
229
- options = RequestOptions(request_url)
230
- options.method = HttpMethod.Get
231
- options.set_header("X-HTTP-Method", "GET")
232
- options.set_header('accept', 'application/json;odata=verbose')
233
- self.request._authenticate_request(options)
234
- self.request._ensure_form_digest(options)
235
- result = requests.get(url=request_url, headers=options.headers, auth=options.auth)
236
- if result.status_code not in (200, 201):
237
- raise requests.exceptions.RequestException(self.parse_error(result))
238
-
239
- result = result.json()
240
- files=[]
241
- for i in range(len(result['d']['results'])):
242
- item = result['d']['results'][i]
243
- files.append(item)
244
- return files
245
-
246
-
247
- def download(self, remote_path, filename):
248
- if not remote_path.startswith(self.path):
249
- remote_path = posixpath.join(self.path, remote_path)
250
- request_url = "%s_api/web/getfilebyserverrelativeurl('%s')/$value" % (self.baseurl, remote_path)
251
- options = RequestOptions(request_url)
252
- options.method = HttpMethod.Get
253
- options.set_header("X-HTTP-Method", "GET")
254
- options.set_header('accept', 'application/json;odata=verbose')
255
- retry = 5
256
- while retry:
257
- try:
258
- self.request._authenticate_request(options)
259
- self.request._ensure_form_digest(options)
260
- with requests.get(url=request_url, headers=options.headers, auth=options.auth, stream=True, timeout=120) as r:
261
- if r.status_code not in (200, 201):
262
- error = self.parse_error(r)
263
- raise requests.exceptions.RequestException(error)
264
-
265
- with open(filename, 'wb') as file:
266
- for chunk in r.iter_content(chunk_size=8192):
267
- if chunk:
268
- file.write(chunk)
269
- except requests.exceptions.RequestException:
270
- time.sleep(3)
271
- self.login()
272
- retry -= 1
273
- if not retry:
274
- raise
275
- continue
276
-
277
- retry = 0
278
-
279
- return filename
280
-
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes