Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
with
392 additions
and 2 deletions.
- +21 −0 UPLOAD-GOOGLE-DRIVE
- +335 −0 bundles2drive.py
- +9 −0 drive.cfg
- +25 −0 drive.links
- +2 −2 providers.txt
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
| @@ -0,0 +1,21 @@ | ||
| 1) Clone into the latest version of gettor: | ||
|
|
||
| $ git clone https://github.com/ileiva/gettor.git | ||
|
|
||
| 2) Get the PGP key that signs the Tor Browser Bundle | ||
|
|
||
| 2) Visit https://console.developers.google.com//start/api?id=drive&credential=client_key and follow OAUTH2 process to get client ID and client secret for 'other' desktop application. | ||
|
|
||
| 3) Edit drive.cfg to with new client-id and secret. Leave refresh_token empty. | ||
|
|
||
| 4) Install the google drive API python client: | ||
|
|
||
| $ pip install --upgrade google-api-python-client | ||
|
|
||
| 5) Run the script: | ||
|
|
||
| $ python bundles2drive.py | ||
|
|
||
| The first time the script is run, you will have to authorize it through a web browser. You will be prompted with a URL. Once that is done, a refresh token will be stored locally so that re-authorzing is unnesaccary. | ||
|
|
||
| The script will then look for files in upload_dir (as specified in drive.cfg) and upload them to google drive. If no errors occur, it will then add formatted links and hash information to the drive.links file. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
| @@ -0,0 +1,335 @@ | ||
| # -*- coding: utf-8 -*- | ||
| # | ||
| # This file is part of GetTor, a Tor Browser distribution system. | ||
| # | ||
| # :authors: poly <poly@darkdepths.net> | ||
| # Israel Leiva <ilv@riseup.net> | ||
| # see also AUTHORS file | ||
| # | ||
| # :copyright: (c) 2008-2014, The Tor Project, Inc. | ||
| # (c) 2014, Poly | ||
| # (c) 2014, Israel Leiva | ||
| # | ||
| # :license: This is Free Software. See LICENSE for license information. | ||
|
|
||
| import re | ||
| import os | ||
| import gnupg | ||
| import hashlib | ||
| import ConfigParser | ||
| import gettor.core | ||
|
|
||
| #import google drive libs | ||
| import httplib2 | ||
| from apiclient.discovery import build | ||
| from apiclient.http import MediaFileUpload | ||
| from oauth2client.client import OAuth2WebServerFlow | ||
| from oauth2client.client import Credentials | ||
|
|
||
|
|
||
| def valid_format(file, osys): | ||
| """Check for valid bundle format | ||
| Check if the given file has a valid bundle format | ||
| (e.g. tor-browser-linux32-3.6.2_es-ES.tar.xz) | ||
| :param: file (string) the name of the file. | ||
| :param: osys (string) the OS. | ||
| :return: (boolean) true if the bundle format is valid, false otherwise. | ||
| """ | ||
| if(osys == 'windows'): | ||
| m = re.search( | ||
| 'torbrowser-install-\d\.\d\.\d_\w\w(-\w\w)?\.exe', | ||
| file) | ||
| elif(osys == 'linux'): | ||
| m = re.search( | ||
| 'tor-browser-linux\d\d-\d\.\d\.\d_(\w\w)(-\w\w)?\.tar\.xz', | ||
| file) | ||
| elif(osys == 'osx'): | ||
| m = re.search( | ||
| 'TorBrowser-\d\.\d\.\d-osx\d\d_(\w\w)(-\w\w)?\.dmg', | ||
| file) | ||
| if m: | ||
| return True | ||
| else: | ||
| return False | ||
|
|
||
|
|
||
| def get_bundle_info(file, osys): | ||
| """Get the os, arch and lc from a bundle string. | ||
| :param: file (string) the name of the file. | ||
| :param: osys (string) the OS. | ||
| :raise: ValueError if the bundle doesn't have a valid bundle format. | ||
| :return: (list) the os, arch and lc. | ||
| """ | ||
| if(osys == 'windows'): | ||
| m = re.search( | ||
| 'torbrowser-install-\d\.\d\.\d_(\w\w)(-\w\w)?\.exe', | ||
| file) | ||
| if m: | ||
| lc = m.group(1) | ||
| return 'windows', '32/64', lc | ||
| else: | ||
| raise ValueError("Invalid bundle format %s" % file) | ||
| elif(osys == 'linux'): | ||
| m = re.search( | ||
| 'tor-browser-linux(\d\d)-\d\.\d\.\d_(\w\w)(-\w\w)?\.tar\.xz', | ||
| file) | ||
| if m: | ||
| arch = m.group(1) | ||
| lc = m.group(2) | ||
| return 'linux', arch, lc | ||
| else: | ||
| raise ValueError("Invalid bundle format %s" % file) | ||
| elif(osys == 'osx'): | ||
| m = re.search( | ||
| 'TorBrowser-\d\.\d\.\d-osx(\d\d)_(\w\w)(-\w\w)?\.dmg', | ||
| file) | ||
| if m: | ||
| os = 'osx' | ||
| arch = m.group(1) | ||
| lc = m.group(2) | ||
| return 'osx', arch, lc | ||
| else: | ||
| raise ValueError("Invalid bundle format %s" % file) | ||
|
|
||
|
|
||
| def get_file_sha256(file): | ||
| """Get the sha256 of a file. | ||
| :param: file (string) the path of the file. | ||
| :return: (string) the sha256 hash. | ||
| """ | ||
| # as seen on the internetz | ||
| BLOCKSIZE = 65536 | ||
| hasher = hashlib.sha256() | ||
| with open(file, 'rb') as afile: | ||
| buf = afile.read(BLOCKSIZE) | ||
| while len(buf) > 0: | ||
| hasher.update(buf) | ||
| buf = afile.read(BLOCKSIZE) | ||
| return hasher.hexdigest() | ||
|
|
||
|
|
||
| def upload_files(client, basedir): | ||
| """Upload files to Google Drive. | ||
| Looks for tor browser files inside basedir. | ||
| :param: basedir (string) path of the folder with the files to be | ||
| uploaded. | ||
| :param: client (object) Google Drive object. | ||
| :raise: UploadError if something goes wrong while uploading the | ||
| files to Google Drive. All files are uploaded to '/'. | ||
| :return: (dict) the names of the uploaded files as the keys, | ||
| and file id as the value | ||
| """ | ||
| files = [] | ||
|
|
||
| p = re.compile('.*\.tar.xz$') | ||
| for name in os.listdir(basedir): | ||
| path = os.path.abspath(os.path.join(basedir, name)) | ||
| if os.path.isfile(path) and p.match(path)\ | ||
| and valid_format(name, 'linux'): | ||
| files.append(name) | ||
|
|
||
| p = re.compile('.*\.exe$') | ||
| for name in os.listdir(basedir): | ||
| path = os.path.abspath(os.path.join(basedir, name)) | ||
| if os.path.isfile(path) and p.match(path)\ | ||
| and valid_format(name, 'windows'): | ||
| files.append(name) | ||
|
|
||
| p = re.compile('.*\.dmg$') | ||
| for name in os.listdir(basedir): | ||
| path = os.path.abspath(os.path.join(basedir, name)) | ||
| if os.path.isfile(path) and p.match(path)\ | ||
| and valid_format(name, 'osx'): | ||
| files.append(name) | ||
|
|
||
| # dictionary to store file names and IDs | ||
| files_dict = dict() | ||
|
|
||
| for file in files: | ||
| asc = "%s.asc" % file | ||
| abs_file = os.path.abspath(os.path.join(basedir, file)) | ||
| abs_asc = os.path.abspath(os.path.join(basedir, asc)) | ||
|
|
||
| if not os.path.isfile(abs_asc): | ||
| # there are some .mar files that don't have .asc, don't upload it | ||
| continue | ||
|
|
||
| # upload tor browser installer | ||
| file_body = MediaFileUpload(abs_file, resumable=True) | ||
| body = { | ||
| 'title': file | ||
| } | ||
| print "Uploading '%s'..." % file | ||
| try: | ||
| file_data = drive_service.files().insert(body=body, media_body=file_body).execute() | ||
| except: | ||
| raise UploadError | ||
|
|
||
| # upload signature | ||
| asc_body = MediaFileUpload(abs_asc, resumable=True) | ||
| asc_head = { | ||
| 'title': "%s.asc" % file | ||
| } | ||
| print "Uploading '%s'..." % asc | ||
| try: | ||
| asc_data = drive_service.files().insert(body=asc_head, media_body=asc_body).execute() | ||
| except: | ||
| raise UploadError | ||
|
|
||
| # add filenames and file id to dict | ||
| files_dict[file] = file_data['id'] | ||
| files_dict[asc] = asc_data['id'] | ||
|
|
||
| return files_dict | ||
|
|
||
| def share_file(service, file_id): | ||
| """Make files public | ||
| For a given file-id, sets role 'reader' to 'anyone'. Returns public | ||
| link to file. | ||
| :param: file_id (string) | ||
| :return: (string) url to shared file | ||
| """ | ||
| permission = { | ||
| 'type': "anyone", | ||
| 'role': "reader", | ||
| 'withLink': True | ||
| } | ||
|
|
||
| try: | ||
| service.permissions().insert( | ||
| fileId=file_id, body=permission).execute() | ||
| except errors.HttpError, error: | ||
| print('An error occured while sharing: %s' % file_id) | ||
|
|
||
| try: | ||
| file = service.files().get(fileId=file_id).execute() | ||
| except errors.HttpError, error: | ||
| print('Error occured while fetch public link for file: %s' % file_id) | ||
|
|
||
| print("Uploaded to %s" % file['webContentLink']) | ||
| return file['webContentLink'] | ||
|
|
||
|
|
||
| if __name__ == '__main__': | ||
| config = ConfigParser.ConfigParser() | ||
| config.read('drive.cfg') | ||
|
|
||
| client_id = config.get('app', 'client-id') | ||
| app_secret = config.get('app', 'secret') | ||
| refresh_token = config.get('app', 'refresh_token') | ||
| upload_dir = config.get('general', 'upload_dir') | ||
|
|
||
| # important: this key must be the one that signed the packages | ||
| tbb_key = config.get('general', 'tbb_key') | ||
|
|
||
| # requests full access to drive account | ||
| OAUTH_SCOPE = 'https://www.googleapis.com/auth/drive' | ||
| REDIRECT_URI = 'urn:ietf:wg:oauth:2.0:oob' | ||
|
|
||
| print "Authenticating..." | ||
|
|
||
| flow = OAuth2WebServerFlow(client_id, app_secret, OAUTH_SCOPE, | ||
| redirect_uri=REDIRECT_URI) | ||
|
|
||
| # If no valid token found, need to prompt user. | ||
| # this should only occur once | ||
| if not refresh_token: | ||
| flow.params['access_type'] = 'offline' | ||
| flow.params['approval_prompt'] = 'force' | ||
| authorize_url = flow.step1_get_authorize_url() | ||
| print 'Go to the following link in your browser: ' + authorize_url | ||
| code = raw_input('Enter verification code: ').strip() | ||
| credentials = flow.step2_exchange(code) | ||
|
|
||
| # oauth2 credentials instance must be stored as json string | ||
| config.set('app', 'refresh_token', credentials.to_json()) | ||
| with open('drive.cfg', 'wb') as configfile: | ||
| config.write(configfile) | ||
| else: | ||
| # we already have a valid token | ||
| credentials = Credentials.new_from_json(refresh_token) | ||
|
|
||
| # authenticate with oauth2 | ||
| http = httplib2.Http() | ||
| http = credentials.authorize(http) | ||
|
|
||
| # initialize drive instance | ||
| drive_service = build('drive', 'v2', http=http) | ||
|
|
||
|
|
||
| # import key fingerprint | ||
| gpg = gnupg.GPG() | ||
| key_data = open(tbb_key).read() | ||
| import_result = gpg.import_keys(key_data) | ||
| fp = import_result.results[0]['fingerprint'] | ||
|
|
||
| # make groups of four characters to make fingerprint more readable | ||
| # e.g. 123A 456B 789C 012D 345E 678F 901G 234H 567I 890J | ||
| readable = ' '.join(fp[i:i+4] for i in xrange(0, len(fp), 4)) | ||
|
|
||
| try: | ||
| uploaded_files = upload_files(drive_service, upload_dir) | ||
| # use default config | ||
| core = gettor.core.Core('/home/gettor/core.cfg') | ||
|
|
||
| # erase old links | ||
| core.create_links_file('Drive', readable) | ||
|
|
||
| # recognize file OS by its extension | ||
| p1 = re.compile('.*\.tar.xz$') | ||
| p2 = re.compile('.*\.exe$') | ||
| p3 = re.compile('.*\.dmg$') | ||
| p4 = re.compile('.*\.asc$') | ||
|
|
||
| for file in uploaded_files.keys(): | ||
| # only run for tor browser installers | ||
| if p4.match(file): | ||
| continue | ||
| asc = "%s.asc" % file | ||
| abs_file = os.path.abspath(os.path.join(upload_dir, file)) | ||
| abs_asc = os.path.abspath(os.path.join(upload_dir, asc)) | ||
|
|
||
| sha_file = get_file_sha256(abs_file) | ||
|
|
||
| # build links | ||
| link_file = share_file(drive_service, | ||
| uploaded_files[file]) | ||
| link_asc = share_file(drive_service, | ||
| uploaded_files["%s.asc" % file]) | ||
|
|
||
| if p1.match(file): | ||
| osys, arch, lc = get_bundle_info(file, 'linux') | ||
| elif p2.match(file): | ||
| osys, arch, lc = get_bundle_info(file, 'windows') | ||
| elif p3.match(file): | ||
| osys, arch, lc = get_bundle_info(file, 'osx') | ||
|
|
||
| link = "Package (%s-bit): %s\nASC signature (%s-bit): %s\n"\ | ||
| "Package SHA256 checksum (%s-bit): %s\n" %\ | ||
| (arch, link_file, arch, link_asc, | ||
| arch, sha_file) | ||
|
|
||
| # note that you should only upload bundles for supported locales | ||
| core.add_link('Drive', osys, lc, link) | ||
| except (ValueError, RuntimeError) as e: | ||
| print str(e) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
| @@ -0,0 +1,9 @@ | ||
| [general] | ||
| upload_dir = upload | ||
| tbb_key = tbb-key.asc | ||
|
|
||
| [app] | ||
| client-id = | ||
| secret = | ||
| refresh_token = | ||
|
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
| @@ -0,0 +1,25 @@ | ||
| [provider] | ||
| name = Drive | ||
|
|
||
| [key] | ||
| fingerprint = A3C4 F0F9 79CA A22C DBA8 F512 EE8C BC9E 886D DD89 | ||
|
|
||
| [linux] | ||
| en = Package (32-bit): https://docs.google.com/uc?id=0B7humfJYX-LjNTJPanBRekI5Slk&export=download | ||
| ASC signature (32-bit): https://docs.google.com/uc?id=0B7humfJYX-LjTXVONkhvZ1VRY2c&export=download | ||
| Package SHA256 checksum (32-bit): 03a4ecab9ffb4b579c9d03a0f800d9bcd4446b49eff21eb3e1db255fa9d9b930, | ||
| Package (64-bit): https://docs.google.com/uc?id=0B7humfJYX-LjbXp2MUh5RmxTa28&export=download | ||
| ASC signature (64-bit): https://docs.google.com/uc?id=0B7humfJYX-LjNTVZVnJUTnNCOEk&export=download | ||
| Package SHA256 checksum (64-bit): 03a4ecab9ffb4b579c9d03a0f800d9bcd4446b49eff21eb3e1db255fa9d9b930 | ||
|
|
||
|
|
||
| [windows] | ||
| en = Package (32/64-bit): https://docs.google.com/uc?id=0B7humfJYX-LjakM4a21iRlQzQVE&export=download | ||
| ASC signature (32/64-bit): https://docs.google.com/uc?id=0B7humfJYX-LjZy1FLVpTLW9INFU&export=download | ||
| Package SHA256 checksum (32/64-bit): 21e54d964366e8a67f379ed402007f59cb3aebfddbc3a30dacc3ac64171145a6 | ||
|
|
||
| [osx] | ||
| en = Package (32-bit): https://docs.google.com/uc?id=0B7humfJYX-LjenRobzJtcWw0UnM&export=download | ||
| ASC signature (32-bit): https://docs.google.com/uc?id=0B7humfJYX-LjZ0R4Z0I1ZlBRUHc&export=download | ||
| Package SHA256 checksum (32-bit): dd86727270ba236d85673194782b4cc9860bd886e4abdf2dfc6d716dabddb68f | ||
|
|
Oops, something went wrong.