Skip to content
Permalink
Browse files
implemented google drive as a provider
  • Loading branch information
0xPoly committed Dec 12, 2014
1 parent 81311a0 commit 1eda9c6ca26623867bc9e49b4218b1b5e4108713
Showing with 392 additions and 2 deletions.
  1. +21 −0 UPLOAD-GOOGLE-DRIVE
  2. +335 −0 bundles2drive.py
  3. +9 −0 drive.cfg
  4. +25 −0 drive.links
  5. +2 −2 providers.txt
@@ -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.
@@ -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)
@@ -0,0 +1,9 @@
[general]
upload_dir = upload
tbb_key = tbb-key.asc

[app]
client-id =
secret =
refresh_token =

@@ -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

0 comments on commit 1eda9c6

Please sign in to comment.