├── README.md └── sharepoint.py /README.md: -------------------------------------------------------------------------------- 1 | # Sharefouine 2 | 3 | This python script allows you to easily navigate into Sharepoint using UNIX like commands. 4 | 5 | It gives access to the following commands: 6 | 7 | - `cd`: Go to a specific Sharepoint site or OneDrive 8 | - `ls`: Display thee content of a site, OneDrive or directory 9 | - `search_all`: Search in all Sharepoint and OneDrive files 10 | - `search`: Search in the current site or OneDrive 11 | - `get`: Download a specific file 12 | 13 | # Use it 14 | In the code set you `tenant_id`, `client_id` and `client_secret` 15 | 16 | Set your sharepoint and oneedrive URL. Usually the sharepoint is https://company.sharepoint.com and the OneDrive is https://company-my.sharepoint.com 17 | 18 | Run the script and enjoy -------------------------------------------------------------------------------- /sharepoint.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import json 3 | import math 4 | 5 | tenant_id = '' 6 | client_id = '' 7 | client_secret = '' 8 | 9 | 10 | # The Sharepoint and Onedrive base URL 11 | # The sharepoint is usually https://xxxx.sharepoint.com 12 | # The onedrive is usually https://xxxx-my.sharepoint.com 13 | info = { 14 | 'onedrive': '', 15 | 'sharepoint': '' 16 | } 17 | 18 | def get_all_sites(host, access_token): 19 | row_limit = 1000 20 | url = f"{host}/_api/search/query?querytext='contentclass:STS_Site'&RowLimit={row_limit}&startrow=" 21 | headers = { 22 | 'Authorization': f'Bearer {access_token}', 23 | 'Accept': 'application/json;odata=verbose' 24 | } 25 | have_res = True 26 | i = 0 27 | while(have_res): 28 | response = requests.get(url+'{}'.format(i*row_limit), headers=headers) 29 | response_json = response.json() 30 | have_res = response_json['d']['query']['PrimaryQueryResult']['RelevantResults']['RowCount'] 31 | i += 1 32 | 33 | for result in response_json['d']['query']['PrimaryQueryResult']['RelevantResults']['Table']['Rows']['results']: 34 | title = None 35 | path = None 36 | desc = None 37 | for cell in result['Cells']['results']: 38 | if cell['Key'] == 'Title': 39 | title = cell['Value'] 40 | elif cell['Key'] == 'Path': 41 | path = cell['Value'] 42 | elif cell['Key'] == 'Description': 43 | desc = cell['Value'] 44 | try: 45 | print(f'{title} | {desc} | {path}') 46 | except UnicodeEncodeError: 47 | print(f'{path}') 48 | 49 | 50 | def get_access_tokens(tenant_id, client_id, client_secret, host): 51 | onedrive_host = "{}-my.{}".format( 52 | host.split('.')[0], 53 | '.'.join(host.split('.')[1:]) 54 | ) 55 | 56 | url = f'https://accounts.accesscontrol.windows.net/{tenant_id}/tokens/OAuth/2' 57 | onedrive_scope = f'00000003-0000-0ff1-ce00-000000000000/{onedrive_host}@{tenant_id}' 58 | sharepoint_scope = f'00000003-0000-0ff1-ce00-000000000000/{host}@{tenant_id}' 59 | 60 | data = { 61 | 'grant_type':'client_credentials', 62 | 'client_id': f'{client_id}@{tenant_id}', 63 | 'client_secret': client_secret, 64 | 'resource': sharepoint_scope 65 | } 66 | 67 | req = requests.post(url, data=data) 68 | try: 69 | sharepoint_token = req.json()['access_token'] 70 | except KeyError: 71 | raise ValueError("Failed to retrieve the sharepoint token : \n{}".format(req.text)) 72 | 73 | data['resource'] = onedrive_scope 74 | req = requests.post(url, data=data) 75 | try: 76 | onedrive_token = req.json()['access_token'] 77 | except KeyError: 78 | raise ValueError("Failed to retrieve the onedrive token : \n{}".format(req.text)) 79 | 80 | return { 81 | 'onedrive': onedrive_token, 82 | 'sharepoint': sharepoint_token 83 | } 84 | 85 | def search_site(info, folder, searchtext): 86 | if folder.startswith('/personal/'): 87 | host = info['onedrive'] 88 | access_token = info['access_token']['onedrive'] 89 | else: 90 | host = info['sharepoint'] 91 | access_token = info['access_token']['sharepoint'] 92 | 93 | headers = { 94 | 'Authorization': f'Bearer {access_token}', 95 | 'Accept': 'application/json;odata=verbose' 96 | } 97 | folder_split = folder[1:].split('/') 98 | site_type, site_name = folder_split[:2] 99 | 100 | have_res = True 101 | i = 0 102 | row_limit = 1000 103 | url = f"{host}/{site_type}/{site_name}/_api/search/query?querytext='{searchtext} AND Path:{host}/{site_type}/{site_name}'&RowLimit={row_limit}&startrow=" 104 | file_list = [] 105 | while(have_res): 106 | response = requests.get(url+'{}'.format(i*row_limit), headers=headers) 107 | try: 108 | response_json = response.json() 109 | except: 110 | have_res = False 111 | continue 112 | 113 | have_res = response_json['d']['query']['PrimaryQueryResult']['RelevantResults']['RowCount'] 114 | i += 1 115 | 116 | for result in response_json['d']['query']['PrimaryQueryResult']['RelevantResults']['Table']['Rows']['results']: 117 | description = None 118 | path = None 119 | date = None 120 | size = None 121 | for cell in result['Cells']['results']: 122 | if cell['Key'] == 'HitHighlightedSummary': 123 | description = cell['Value'] 124 | elif cell['Key'] == 'Path': 125 | path = '/' + '/'.join(cell['Value'].split('/')[3:]) 126 | elif cell['Key'] == 'LastModifiedTime': 127 | try: 128 | date = cell['Value'].split('T')[0] 129 | except: 130 | date = 'NaN' 131 | elif cell['Key'] == 'Size': 132 | size = cell['Value'] 133 | 134 | file_list.append({ 135 | 'description': description, 136 | 'path': path, 137 | 'date': date, 138 | 'size': size 139 | }) 140 | return file_list 141 | 142 | 143 | def search_site_all(info, searchtext, filter=''): 144 | host = info['sharepoint'] 145 | access_token = info['access_token']['sharepoint'] 146 | 147 | headers = { 148 | 'Authorization': f'Bearer {access_token}', 149 | 'Accept': 'application/json;odata=verbose' 150 | } 151 | 152 | have_res = True 153 | i = 0 154 | row_limit = 1000 155 | if filter != '': 156 | url_filter = f"refinementfilters='{filter}'&" 157 | else: 158 | url_filter = '' 159 | if searchtext != '': 160 | url_search = f"querytext='{searchtext}'&" 161 | else: 162 | url_search = '' 163 | 164 | url = f"{host}/_api/search/query?{url_search}{url_filter}RowLimit={row_limit}&startrow=" 165 | print(url) 166 | file_list = [] 167 | while(have_res): 168 | response = requests.get(url+'{}'.format(i*row_limit), headers=headers) 169 | try: 170 | response_json = response.json() 171 | except: 172 | have_res = False 173 | continue 174 | 175 | have_res = response_json['d']['query']['PrimaryQueryResult']['RelevantResults']['RowCount'] 176 | i += 1 177 | 178 | for result in response_json['d']['query']['PrimaryQueryResult']['RelevantResults']['Table']['Rows']['results']: 179 | description = None 180 | path = None 181 | date = None 182 | size = None 183 | for cell in result['Cells']['results']: 184 | if cell['Key'] == 'HitHighlightedSummary': 185 | description = cell['Value'] 186 | elif cell['Key'] == 'Path': 187 | path = '/' + '/'.join(cell['Value'].split('/')[3:]) 188 | elif cell['Key'] == 'LastModifiedTime': 189 | try: 190 | date = cell['Value'].split('T')[0] 191 | except: 192 | date = 'NaN' 193 | elif cell['Key'] == 'Size': 194 | size = cell['Value'] 195 | 196 | file_list.append({ 197 | 'description': description, 198 | 'path': path, 199 | 'date': date, 200 | 'size': size 201 | }) 202 | 203 | host = info['onedrive'] 204 | access_token = info['access_token']['onedrive'] 205 | have_res = True 206 | while(have_res): 207 | response = requests.get(url+'{}'.format(i*row_limit), headers=headers) 208 | try: 209 | response_json = response.json() 210 | except: 211 | have_res = False 212 | continue 213 | 214 | have_res = response_json['d']['query']['PrimaryQueryResult']['RelevantResults']['RowCount'] 215 | i += 1 216 | 217 | for result in response_json['d']['query']['PrimaryQueryResult']['RelevantResults']['Table']['Rows']['results']: 218 | description = None 219 | path = None 220 | date = None 221 | size = None 222 | for cell in result['Cells']['results']: 223 | if cell['Key'] == 'HitHighlightedSummary': 224 | description = cell['Value'] 225 | elif cell['Key'] == 'Path': 226 | path = '/' + '/'.join(cell['Value'].split('/')[3:]) 227 | elif cell['Key'] == 'LastModifiedTime': 228 | try: 229 | date = cell['Value'].split('T')[0] 230 | except: 231 | date = 'NaN' 232 | elif cell['Key'] == 'Size': 233 | size = cell['Value'] 234 | 235 | file_list.append({ 236 | 'description': description, 237 | 'path': path, 238 | 'date': date, 239 | 'size': size 240 | }) 241 | return file_list 242 | 243 | 244 | def get_folder(info, folder): 245 | if folder.startswith('/personal/'): 246 | host = info['onedrive'] 247 | access_token = info['access_token']['onedrive'] 248 | else: 249 | host = info['sharepoint'] 250 | access_token = info['access_token']['sharepoint'] 251 | 252 | headers = { 253 | 'Authorization': f'Bearer {access_token}', 254 | 'Accept': 'application/json;odata=verbose' 255 | } 256 | folder_split = folder[1:].split('/') 257 | site_type, site_name = folder_split[:2] 258 | encoded_folder = '%20'.join(folder.split(' ')) 259 | encoded_folder = '%27%27'.join(encoded_folder.split("'")) 260 | url = f'{host}/{site_type}/{site_name}/_api/Web/GetFolderByServerRelativeUrl(\'{encoded_folder}\')/Folders' 261 | req = requests.get(url, headers=headers) 262 | try: 263 | response = req.json() 264 | except: 265 | raise ValueError("Failed to get the sharepoint response for {}: \n".format(url, req.text)) 266 | 267 | folders = [] 268 | for elt in response['d']['results']: 269 | folders.append({ 270 | 'name': elt['Name'], 271 | 'link': elt['ServerRelativeUrl'], 272 | 'time': elt['TimeLastModified'].split('T')[0] 273 | }) 274 | return folders 275 | 276 | 277 | def get_files(info, folder): 278 | if folder.startswith('/personal/'): 279 | host = info['onedrive'] 280 | access_token = info['access_token']['onedrive'] 281 | else: 282 | host = info['sharepoint'] 283 | access_token = info['access_token']['sharepoint'] 284 | 285 | headers = { 286 | 'Authorization': f'Bearer {access_token}', 287 | 'Accept': 'application/json;odata=verbose' 288 | } 289 | 290 | folder_split = folder[1:].split('/') 291 | site_type, site_name = folder_split[:2] 292 | encoded_folder = '%20'.join(folder.split(' ')) 293 | encoded_folder = '%27%27'.join(encoded_folder.split("'")) 294 | url = f'{host}/{site_type}/{site_name}/_api/Web/GetFolderByServerRelativeUrl(\'{encoded_folder}\')/Files' 295 | req = requests.get(url, headers=headers) 296 | try: 297 | response = req.json() 298 | except: 299 | raise ValueError("Failed to get the sharepoint response for {}: \n".format(url, req.text)) 300 | 301 | folders = [] 302 | for elt in response['d']['results']: 303 | folders.append({ 304 | 'name': elt['Name'], 305 | 'link': elt['ServerRelativeUrl'], 306 | 'time': elt['TimeLastModified'].split('T')[0], 307 | 'size': elt['Length'] 308 | }) 309 | return folders 310 | 311 | def download_file(info, folder): 312 | if folder.startswith('/personal/'): 313 | host = info['onedrive'] 314 | access_token = info['access_token']['onedrive'] 315 | else: 316 | host = info['sharepoint'] 317 | access_token = info['access_token']['sharepoint'] 318 | 319 | headers = { 320 | 'Authorization': f'Bearer {access_token}', 321 | 'Accept': 'application/json;odata=verbose' 322 | } 323 | folder_split = folder[1:].split('/') 324 | site_type, site_name = folder_split[:2] 325 | filename = folder_split[-1] 326 | encoded_folder = '%20'.join(folder.split(' ')) 327 | encoded_folder = '%27%27'.join(encoded_folder.split("'")) 328 | url = f'{host}/{site_type}/{site_name}/_api/Web/GetFileByServerRelativePath(decodedurl=\'{encoded_folder}\')/$Value' 329 | req = requests.get(url, headers=headers) 330 | if req.status_code == 200: 331 | with open(filename, "wb") as file: 332 | file.write(req.content) 333 | else: 334 | raise ValueError(f"Failed to download the file {url}: {req.text}") 335 | 336 | 337 | 338 | info['access_token'] = get_access_tokens(tenant_id, client_id, client_secret, info['sharepoint'].split('://')[-1]) 339 | current_directory = '' 340 | while(1): 341 | try: 342 | if current_directory.endswith('/'): 343 | current_directory = current_directory[:-1] 344 | raw_cmd = input(f"{current_directory} >> ") 345 | print('\n') 346 | try: 347 | cmd = raw_cmd.split(' ')[0] 348 | content = ' '.join(raw_cmd.split(' ')[1:]) 349 | except: 350 | print(f"Unknown command {cmd}") 351 | continue 352 | if cmd == 'cd': 353 | if content == '..': 354 | current_directory = '/'.join(current_directory.split('/')[:-1]) 355 | elif content.startswith('/'): 356 | current_directory = content 357 | else: 358 | current_directory += f'/{content}' 359 | elif cmd == 'ls': 360 | if 'content' == '.': 361 | link = current_directory 362 | else: 363 | if content.startswith('/'): 364 | link = content 365 | else: 366 | link = f"{current_directory}/{content}" 367 | 368 | folders = get_folder(info, link) 369 | files = get_files(info, link) 370 | for elt in folders: 371 | print(f"{elt['time']} - (dir) {elt['name']}") 372 | 373 | for elt in files: 374 | print(f"{elt['time']} - {elt['name']} ({math.floor(int(elt['size'])/(1024*1024))}MB)") 375 | elif cmd == 'get': 376 | if content.startswith('/'): 377 | download_file(info, content) 378 | else: 379 | download_file(info, f"{current_directory}/{content}") 380 | elif cmd == 'search': 381 | result = search_site(info, '/'.join(current_directory.split('/')[:3]), content) 382 | for elt in result: 383 | print(f"{elt['date']} - {elt['path']} ({math.floor(int(elt['size'])/(1024*1024))}MB)\n Preview : {elt['description']}") 384 | print('\n') 385 | elif cmd == 'search_all': 386 | result = search_site_all(info, content) 387 | for elt in result: 388 | print(f"{elt['date']} - {elt['path']} ({math.floor(int(elt['size'])/(1024*1024))}MB)\n Preview : {elt['description']}") 389 | print('\n') 390 | elif cmd == 'exit': 391 | break 392 | else: 393 | print(f"The command {cmd} is not defined") 394 | except Exception as e: 395 | print(f"Unkown error...: {e}") 396 | print('\n') 397 | --------------------------------------------------------------------------------