diff --git a/addon.xml b/addon.xml
index d5e07d6..a58bd05 100644
--- a/addon.xml
+++ b/addon.xml
@@ -6,11 +6,8 @@
-
-
-
-
-
+
+
diff --git a/resources/lib/pydrive/LICENSE.txt b/resources/lib/pydrive/LICENSE.txt
deleted file mode 100644
index 5c87881..0000000
--- a/resources/lib/pydrive/LICENSE.txt
+++ /dev/null
@@ -1,185 +0,0 @@
-Copyright 2013 Google Inc. All Rights Reserved.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
-TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
-2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
-3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
-4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
-5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
-6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
-7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
-8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
-9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
\ No newline at end of file
diff --git a/resources/lib/pydrive/__init__.py b/resources/lib/pydrive/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/resources/lib/pydrive/apiattr.py b/resources/lib/pydrive/apiattr.py
deleted file mode 100644
index fe99700..0000000
--- a/resources/lib/pydrive/apiattr.py
+++ /dev/null
@@ -1,174 +0,0 @@
-class ApiAttribute(object):
- """A data descriptor that sets and returns values."""
-
- def __init__(self, name):
- """Create an instance of ApiAttribute.
-
- :param name: name of this attribute.
- :type name: str.
- """
- self.name = name
-
- def __get__(self, obj, type=None):
- """Accesses value of this attribute."""
- return obj.attr.get(self.name)
-
- def __set__(self, obj, value):
- """Write value of this attribute."""
- obj.attr[self.name] = value
- if obj.dirty.get(self.name) is not None:
- obj.dirty[self.name] = True
-
- def __del__(self, obj=None):
- """Delete value of this attribute."""
- if(obj != None):
- del obj.attr[self.name]
- if obj.dirty.get(self.name) is not None:
- del obj.dirty[self.name]
-
-
-class ApiAttributeMixin(object):
- """Mixin to initialize required global variables to use ApiAttribute."""
-
- def __init__(self):
- self.attr = {}
- self.dirty = {}
-
-
-class ApiResource(dict):
- """Super class of all api resources.
-
- Inherits and behaves as a python dictionary to handle api resources.
- Save clean copy of metadata in self.metadata as a dictionary.
- Provides changed metadata elements to efficiently update api resources.
- """
- auth = ApiAttribute('auth')
-
- def __init__(self, *args, **kwargs):
- """Create an instance of ApiResource."""
- self.update(*args, **kwargs)
-
- def __getitem__(self, key):
- """Overwritten method of dictionary.
-
- :param key: key of the query.
- :type key: str.
- :returns: value of the query.
- """
- return dict.__getitem__(self, key)
-
- def __setitem__(self, key, val):
- """Overwritten method of dictionary.
-
- :param key: key of the query.
- :type key: str.
- :param val: value of the query.
- """
- dict.__setitem__(self, key, val)
-
- def __repr__(self):
- """Overwritten method of dictionary."""
- dictrepr = dict.__repr__(self)
- return '%s(%s)' % (type(self).__name__, dictrepr)
-
- def update(self, *args, **kwargs):
- """Overwritten method of dictionary."""
- for k, v in dict(*args, **kwargs).iteritems():
- self[k] = v
-
- def UpdateMetadata(self, metadata=None):
- """Update metadata and mark all of them to be clean."""
- if metadata:
- self.update(metadata)
- self.metadata = dict(self)
-
- def GetChanges(self):
- """Returns changed metadata elements to update api resources efficiently.
-
- :returns: dict -- changed metadata elements.
- """
- dirty = {}
- for key in self:
- if self.metadata.get(key) is None:
- dirty[key] = self[key]
- elif self.metadata[key] != self[key]:
- dirty[key] = self[key]
- return dirty
-
-
-class ApiResourceList(ApiAttributeMixin, ApiResource):
- """Abstract class of all api list resources.
-
- Inherits ApiResource and builds iterator to list any API resource.
- """
- metadata = ApiAttribute('metadata')
-
- def __init__(self, auth=None, metadata=None):
- """Create an instance of ApiResourceList.
-
- :param auth: authorized GoogleAuth instance.
- :type auth: GoogleAuth.
- :param metadata: parameter to send to list command.
- :type metadata: dict.
- """
- ApiAttributeMixin.__init__(self)
- ApiResource.__init__(self)
- self.auth = auth
- self.UpdateMetadata()
- if metadata:
- self.update(metadata)
-
- def __iter__(self):
- """Returns iterator object.
-
- :returns: ApiResourceList -- self
- """
- return self
-
- def next(self):
- """Make API call to list resources and return them.
-
- Auto updates 'pageToken' everytime it makes API call and
- raises StopIteration when it reached the end of iteration.
-
- :returns: list -- list of API resources.
- :raises: StopIteration
- """
- if 'pageToken' in self and self['pageToken'] is None:
- raise StopIteration
- result = self._GetList()
- self['pageToken'] = self.metadata.get('nextPageToken')
- return result
-
- def GetList(self):
- """Get list of API resources.
-
- If 'maxResults' is not specified, it will automatically iterate through
- every resources available. Otherwise, it will make API call once and
- update 'pageToken'.
-
- :returns: list -- list of API resources.
- """
- if self.get('maxResults') is None:
- self['maxResults'] = 1000
- result = []
- for x in self:
- result.extend(x)
- del self['maxResults']
- return result
- else:
- return self.next()
-
- def _GetList(self):
- """Helper function which actually makes API call.
-
- Should be overwritten.
-
- :raises: NotImplementedError
- """
- raise NotImplementedError
-
- def Reset(self):
- """Resets current iteration"""
- if 'pageToken' in self:
- del self['pageToken']
diff --git a/resources/lib/pydrive/auth.py b/resources/lib/pydrive/auth.py
deleted file mode 100644
index aeace4b..0000000
--- a/resources/lib/pydrive/auth.py
+++ /dev/null
@@ -1,415 +0,0 @@
-import socket
-import webbrowser
-import httplib2
-import oauth2client.clientsecrets as clientsecrets
-
-from googleapiclient.discovery import build
-from functools import wraps
-from oauth2client.client import FlowExchangeError
-from oauth2client.client import AccessTokenRefreshError
-from oauth2client.client import OAuth2WebServerFlow
-from oauth2client.client import OOB_CALLBACK_URN
-from oauth2client.file import Storage
-from oauth2client.tools import ClientRedirectHandler
-from oauth2client.tools import ClientRedirectServer
-from oauth2client._helpers import scopes_to_string
-from .apiattr import ApiAttribute
-from .apiattr import ApiAttributeMixin
-from .settings import LoadSettingsFile
-from .settings import ValidateSettings
-from .settings import SettingsError
-from .settings import InvalidConfigError
-
-
-class AuthError(Exception):
- """Base error for authentication/authorization errors."""
-
-
-class InvalidCredentialsError(IOError):
- """Error trying to read credentials file."""
-
-
-class AuthenticationRejected(AuthError):
- """User rejected authentication."""
-
-
-class AuthenticationError(AuthError):
- """General authentication error."""
-
-
-class RefreshError(AuthError):
- """Access token refresh error."""
-
-def LoadAuth(decoratee):
- """Decorator to check if the auth is valid and loads auth if not."""
- @wraps(decoratee)
- def _decorated(self, *args, **kwargs):
- if self.auth is None: # Initialize auth if needed.
- self.auth = GoogleAuth()
- if self.auth.access_token_expired:
- self.auth.LocalWebserverAuth()
- if self.auth.service is None: # Check if drive api is built.
- self.auth.Authorize()
- return decoratee(self, *args, **kwargs)
- return _decorated
-
-def CheckAuth(decoratee):
- """Decorator to check if it requires OAuth2 flow request."""
- @wraps(decoratee)
- def _decorated(self, *args, **kwargs):
- dirty = False
- code = None
- save_credentials = self.settings.get('save_credentials')
- if self.credentials is None and save_credentials:
- self.LoadCredentials()
- if self.flow is None:
- self.GetFlow()
- if self.credentials is None:
- code = decoratee(self, *args, **kwargs)
- dirty = True
- else:
- if self.access_token_expired:
- if self.credentials.refresh_token is not None:
- self.Refresh()
- else:
- code = decoratee(self, *args, **kwargs)
- dirty = True
- if code is not None:
- self.Auth(code)
- if dirty and save_credentials:
- self.SaveCredentials()
- return _decorated
-
-
-class GoogleAuth(ApiAttributeMixin, object):
- """Wrapper class for oauth2client library in google-api-python-client.
-
- Loads all settings and credentials from one 'settings.yaml' file
- and performs common OAuth2.0 related functionality such as authentication
- and authorization.
- """
- DEFAULT_SETTINGS = {
- 'client_config_backend': 'file',
- 'client_config_file': 'client_secrets.json',
- 'save_credentials': False,
- 'oauth_scope': ['https://www.googleapis.com/auth/drive']
- }
- CLIENT_CONFIGS_LIST = ['client_id', 'client_secret', 'auth_uri',
- 'token_uri', 'revoke_uri', 'redirect_uri']
- settings = ApiAttribute('settings')
- client_config = ApiAttribute('client_config')
- flow = ApiAttribute('flow')
- credentials = ApiAttribute('credentials')
- http = ApiAttribute('http')
- service = ApiAttribute('service')
-
- def __init__(self, settings_file='settings.yaml'):
- """Create an instance of GoogleAuth.
-
- This constructor just sets the path of settings file.
- It does not actually read the file.
-
- :param settings_file: path of settings file. 'settings.yaml' by default.
- :type settings_file: str.
- """
- ApiAttributeMixin.__init__(self)
- self.client_config = {}
- try:
- self.settings = LoadSettingsFile(settings_file)
- except SettingsError:
- self.settings = self.DEFAULT_SETTINGS
- else:
- if self.settings is None:
- self.settings = self.DEFAULT_SETTINGS
- else:
- ValidateSettings(self.settings)
-
- @property
- def access_token_expired(self):
- """Checks if access token doesn't exist or is expired.
-
- :returns: bool -- True if access token doesn't exist or is expired.
- """
- if self.credentials is None:
- return True
- return self.credentials.access_token_expired
-
- @CheckAuth
- def LocalWebserverAuth(self, host_name='localhost',
- port_numbers=[8080, 8090]):
- """Authenticate and authorize from user by creating local webserver and
- retrieving authentication code.
-
- This function is not for webserver application. It creates local webserver
- for user from standalone application.
-
- :param host_name: host name of the local webserver.
- :type host_name: str.
- :param port_numbers: list of port numbers to be tried to used.
- :type port_numbers: list.
- :returns: str -- code returned from local webserver
- :raises: AuthenticationRejected, AuthenticationError
- """
- success = False
- port_number = 0
- for port in port_numbers:
- port_number = port
- try:
- httpd = ClientRedirectServer((host_name, port), ClientRedirectHandler)
- except socket.error as e:
- pass
- else:
- success = True
- break
- if success:
- oauth_callback = 'http://%s:%s/' % (host_name, port_number)
- else:
- raise AuthenticationError()
- self.flow.redirect_uri = oauth_callback
- authorize_url = self.GetAuthUrl()
- webbrowser.open(authorize_url, new=1, autoraise=True)
- httpd.handle_request()
- if 'error' in httpd.query_params:
- raise AuthenticationRejected('User rejected authentication')
- if 'code' in httpd.query_params:
- return httpd.query_params['code']
- else:
- raise AuthenticationError('No code found in redirect')
-
- @CheckAuth
- def CommandLineAuth(self):
- """Authenticate and authorize from user by printing authentication url
- retrieving authentication code from command-line.
-
- :returns: str -- code returned from commandline.
- """
- self.flow.redirect_uri = OOB_CALLBACK_URN
- authorize_url = self.GetAuthUrl()
- return raw_input('Enter verification code: ').strip()
-
- def LoadCredentials(self, backend=None):
- """Loads credentials or create empty credentials if it doesn't exist.
-
- :param backend: target backend to save credential to.
- :type backend: str.
- :raises: InvalidConfigError
- """
- if backend is None:
- backend = self.settings.get('save_credentials_backend')
- if backend is None:
- raise InvalidConfigError('Please specify credential backend')
- if backend == 'file':
- self.LoadCredentialsFile()
- else:
- raise InvalidConfigError('Unknown save_credentials_backend')
-
- def LoadCredentialsFile(self, credentials_file=None):
- """Loads credentials or create empty credentials if it doesn't exist.
-
- Loads credentials file from path in settings if not specified.
-
- :param credentials_file: path of credentials file to read.
- :type credentials_file: str.
- :raises: InvalidConfigError, InvalidCredentialsError
- """
- if credentials_file is None:
- credentials_file = self.settings.get('save_credentials_file')
- if credentials_file is None:
- raise InvalidConfigError('Please specify credentials file to read')
- try:
- storage = Storage(credentials_file)
- self.credentials = storage.get()
- except IOError:
- raise InvalidCredentialsError('Credentials file cannot be symbolic link')
-
- def SaveCredentials(self, backend=None):
- """Saves credentials according to specified backend.
-
- If you have any specific credentials backend in mind, don't use this
- function and use the corresponding function you want.
-
- :param backend: backend to save credentials.
- :type backend: str.
- :raises: InvalidConfigError
- """
- if backend is None:
- backend = self.settings.get('save_credentials_backend')
- if backend is None:
- raise InvalidConfigError('Please specify credential backend')
- if backend == 'file':
- self.SaveCredentialsFile()
- else:
- raise InvalidConfigError('Unknown save_credentials_backend')
-
- def SaveCredentialsFile(self, credentials_file=None):
- """Saves credentials to the file in JSON format.
-
- :param credentials_file: destination to save file to.
- :type credentials_file: str.
- :raises: InvalidConfigError, InvalidCredentialsError
- """
- if self.credentials is None:
- raise InvalidCredentialsError('No credentials to save')
- if credentials_file is None:
- credentials_file = self.settings.get('save_credentials_file')
- if credentials_file is None:
- raise InvalidConfigError('Please specify credentials file to read')
- try:
- storage = Storage(credentials_file)
- storage.put(self.credentials)
- self.credentials.set_store(storage)
- except CredentialsFileSymbolicLinkError:
- raise InvalidCredentialsError('Credentials file cannot be symbolic link')
-
- def LoadClientConfig(self, backend=None):
- """Loads client configuration according to specified backend.
-
- If you have any specific backend to load client configuration from in mind,
- don't use this function and use the corresponding function you want.
-
- :param backend: backend to load client configuration from.
- :type backend: str.
- :raises: InvalidConfigError
- """
- if backend is None:
- backend = self.settings.get('client_config_backend')
- if backend is None:
- raise InvalidConfigError('Please specify client config backend')
- if backend == 'file':
- self.LoadClientConfigFile()
- elif backend == 'settings':
- self.LoadClientConfigSettings()
- else:
- raise InvalidConfigError('Unknown client_config_backend')
-
- def LoadClientConfigFile(self, client_config_file=None):
- """Loads client configuration file downloaded from APIs console.
-
- Loads client config file from path in settings if not specified.
-
- :param client_config_file: path of client config file to read.
- :type client_config_file: str.
- :raises: InvalidConfigError
- """
- if client_config_file is None:
- client_config_file = self.settings['client_config_file']
- try:
- client_type, client_info = clientsecrets.loadfile(client_config_file)
- except clientsecrets.InvalidClientSecretsError as error:
- raise InvalidConfigError('Invalid client secrets file %s' % error)
- if not client_type in (clientsecrets.TYPE_WEB,
- clientsecrets.TYPE_INSTALLED):
- raise InvalidConfigError('Unknown client_type of client config file')
- try:
- config_index = ['client_id', 'client_secret', 'auth_uri', 'token_uri']
- for config in config_index:
- self.client_config[config] = client_info[config]
- self.client_config['revoke_uri'] = client_info.get('revoke_uri')
- self.client_config['redirect_uri'] = client_info['redirect_uris'][0]
- except KeyError:
- raise InvalidConfigError('Insufficient client config in file')
-
- def LoadClientConfigSettings(self):
- """Loads client configuration from settings file.
-
- :raises: InvalidConfigError
- """
-
- for config in self.CLIENT_CONFIGS_LIST:
- try:
- self.client_config[config] = self.settings['client_config'][config]
-
- except KeyError:
- raise InvalidConfigError('Insufficient client config in settings')
-
- def GetFlow(self):
- """Gets Flow object from client configuration.
-
- :raises: InvalidConfigError
- """
- if not all(config in self.client_config \
- for config in self.CLIENT_CONFIGS_LIST):
- self.LoadClientConfig()
- constructor_kwargs = {
- 'redirect_uri': self.client_config['redirect_uri'],
- 'auth_uri': self.client_config['auth_uri'],
- 'token_uri': self.client_config['token_uri'],
- }
- if self.client_config['revoke_uri'] is not None:
- constructor_kwargs['revoke_uri'] = self.client_config['revoke_uri']
- self.flow = OAuth2WebServerFlow(
- self.client_config['client_id'],
- self.client_config['client_secret'],
- scopes_to_string(self.settings['oauth_scope']),
- **constructor_kwargs)
- if self.settings.get('get_refresh_token'):
- self.flow.params.update({'access_type': 'offline'})
-
- def Refresh(self):
- """Refreshes the access_token.
-
- :raises: RefreshError
- """
- if self.credentials is None:
- raise RefreshError('No credential to refresh.')
- if self.credentials.refresh_token is None:
- raise RefreshError('No refresh_token found.'
- 'Please set access_type of OAuth to offline.')
- if self.http is None:
- self.http = httplib2.Http()
- try:
- self.credentials.refresh(self.http)
- except AccessTokenRefreshError as error:
- raise RefreshError('Access token refresh failed: %s' % error)
-
- def GetAuthUrl(self, keys = None):
- """Creates authentication url where user visits to grant access.
-
- :returns: str -- Authentication url.
- """
-
- if(keys != None):
- #update some of the settings in the client_config dict
- self.client_config['client_id'] = keys['client_id']
- self.client_config['client_secret'] = keys['client_secret']
-
- if self.flow is None:
- self.GetFlow()
-
- return self.flow.step1_get_authorize_url()
-
- def Auth(self, code):
- """Authenticate, authorize, and build service.
-
- :param code: Code for authentication.
- :type code: str.
- :raises: AuthenticationError
- """
- self.Authenticate(code)
- self.Authorize()
-
- def Authenticate(self, code):
- """Authenticates given authentication code back from user.
-
- :param code: Code for authentication.
- :type code: str.
- :raises: AuthenticationError
- """
- if self.flow is None:
- self.GetFlow()
- try:
- self.credentials = self.flow.step2_exchange(code)
- except FlowExchangeError as e:
- raise AuthenticationError('OAuth2 code exchange failed: %s' % e)
-
- def Authorize(self):
- """Authorizes and builds service.
-
- :raises: AuthenticationError
- """
- if self.http is None:
- self.http = httplib2.Http()
- if self.access_token_expired:
- raise AuthenticationError('No valid credentials provided to authorize')
- self.http = self.credentials.authorize(self.http)
- self.service = build('drive', 'v2', http=self.http)
diff --git a/resources/lib/pydrive/drive.py b/resources/lib/pydrive/drive.py
deleted file mode 100644
index 94233e4..0000000
--- a/resources/lib/pydrive/drive.py
+++ /dev/null
@@ -1,38 +0,0 @@
-from .apiattr import ApiAttributeMixin
-from .files import GoogleDriveFile
-from .files import GoogleDriveFileList
-
-
-class GoogleDrive(ApiAttributeMixin, object):
- """Main Google Drive class."""
-
- def __init__(self, auth=None):
- """Create an instance of GoogleDrive.
-
- :param auth: authorized GoogleAuth instance.
- :type auth: pydrive.auth.GoogleAuth.
- """
- ApiAttributeMixin.__init__(self)
- self.auth = auth
-
- def CreateFile(self, metadata=None):
- """Create an instance of GoogleDriveFile with auth of this instance.
-
- This method would not upload a file to GoogleDrive.
-
- :param metadata: file resource to initialize GoogleDriveFile with.
- :type metadata: dict.
- :returns: pydrive.files.GoogleDriveFile -- initialized with auth of this instance.
- """
- return GoogleDriveFile(auth=self.auth, metadata=metadata)
-
- def ListFile(self, param=None):
- """Create an instance of GoogleDriveFileList with auth of this instance.
-
- This method will not fetch from Files.List().
-
- :param param: parameter to be sent to Files.List().
- :type param: dict.
- :returns: pydrive.files.GoogleDriveFileList -- initialized with auth of this instance.
- """
- return GoogleDriveFileList(auth=self.auth, param=param)
diff --git a/resources/lib/pydrive/files.py b/resources/lib/pydrive/files.py
deleted file mode 100644
index a7dcab9..0000000
--- a/resources/lib/pydrive/files.py
+++ /dev/null
@@ -1,322 +0,0 @@
-import io
-import mimetypes
-
-from googleapiclient import errors
-from googleapiclient.http import MediaIoBaseUpload
-from functools import wraps
-
-from .apiattr import ApiAttribute
-from .apiattr import ApiAttributeMixin
-from .apiattr import ApiResource
-from .apiattr import ApiResourceList
-from .auth import LoadAuth
-
-
-class FileNotUploadedError(RuntimeError):
- """Error trying to access metadata of file that is not uploaded."""
-
-
-class ApiRequestError(IOError):
- """Error while making any API requests."""
-
-
-class FileNotDownloadableError(RuntimeError):
- """Error trying to download file that is not downloadable."""
-
-
-def LoadMetadata(decoratee):
- """Decorator to check if the file has metadata and fetches it if not.
-
- :raises: ApiRequestError, FileNotUploadedError
- """
- @wraps(decoratee)
- def _decorated(self, *args, **kwargs):
- if not self.uploaded:
- self.FetchMetadata()
- return decoratee(self, *args, **kwargs)
- return _decorated
-
-
-class GoogleDriveFileList(ApiResourceList):
- """Google Drive FileList instance.
-
- Equivalent to Files.list() in Drive APIs.
- """
-
- def __init__(self, auth=None, param=None):
- """Create an instance of GoogleDriveFileList."""
- super(GoogleDriveFileList, self).__init__(auth=auth, metadata=param)
-
- @LoadAuth
- def _GetList(self):
- """Overwritten method which actually makes API call to list files.
-
- :returns: list -- list of pydrive.files.GoogleDriveFile.
- """
- self.metadata = self.auth.service.files().list(**dict(self)).execute()
- result = []
- for file_metadata in self.metadata['items']:
- tmp_file = GoogleDriveFile(
- auth=self.auth,
- metadata=file_metadata,
- uploaded=True)
- result.append(tmp_file)
- return result
-
-
-class GoogleDriveFile(ApiAttributeMixin, ApiResource):
- """Google Drive File instance.
-
- Inherits ApiResource which inherits dict.
- Can access and modify metadata like dictionary.
- """
- content = ApiAttribute('content')
- uploaded = ApiAttribute('uploaded')
- metadata = ApiAttribute('metadata')
-
- def __init__(self, auth=None, metadata=None, uploaded=False):
- """Create an instance of GoogleDriveFile.
-
- :param auth: authorized GoogleAuth instance.
- :type auth: pydrive.auth.GoogleAuth
- :param metadata: file resource to initialize GoogleDirveFile with.
- :type metadata: dict.
- :param uploaded: True if this file is confirmed to be uploaded.
- :type uploaded: bool.
- """
- ApiAttributeMixin.__init__(self)
- ApiResource.__init__(self)
- self.metadata = {}
- self.dirty = {'content': False}
- self.auth = auth
- self.uploaded = uploaded
- if uploaded:
- self.UpdateMetadata(metadata)
- elif metadata:
- self.update(metadata)
-
- def __getitem__(self, key):
- """Overwrites manner of accessing Files resource.
-
- If this file instance is not uploaded and id is specified,
- it will try to look for metadata with Files.get().
-
- :param key: key of dictionary query.
- :type key: str.
- :returns: value of Files resource
- :raises: KeyError, FileNotUploadedError
- """
- try:
- return dict.__getitem__(self, key)
- except KeyError as e:
- if self.uploaded:
- raise KeyError(e)
- if self.get('id'):
- self.FetchMetadata()
- return dict.__getitem__(self, key)
- else:
- raise FileNotUploadedError()
-
- def SetContentString(self, content):
- """Set content of this file to be a string.
-
- Creates io.BytesIO instance of utf-8 encoded string.
- Sets mimeType to be 'text/plain' if not specified.
-
- :param content: content of the file in string.
- :type content: str.
- """
- self.content = io.BytesIO(content.encode('utf-8'))
- if self.get('mimeType') is None:
- self['mimeType'] = 'text/plain'
-
- def SetContentFile(self, filename):
- """Set content of this file from a file.
-
- Opens the file specified by this method.
- Will be read, uploaded, and closed by Upload() method.
- Sets metadata 'title' and 'mimeType' automatically if not specified.
-
- :param filename: name of the file to be uploaded.
- :type filename: str.
- """
- self.content = open(filename, 'rb')
-
- if self.get('title') is None:
- self['title'] = filename
- if self.get('mimeType') is None:
- self['mimeType'] = mimetypes.guess_type(filename)[0]
-
- def GetContentString(self):
- """Get content of this file as a string.
-
- :returns: str -- utf-8 decoded content of the file
- :raises: ApiRequestError, FileNotUploadedError, FileNotDownloadableError
- """
- if self.content is None or type(self.content) is not io.BytesIO:
- self.FetchContent()
- return self.content.getvalue().decode('utf-8')
-
- def GetContentFile(self, filename, mimetype=None):
- """Save content of this file as a local file.
-
- :param filename: name of the file to write to.
- :type filename: str.
- :raises: ApiRequestError, FileNotUploadedError, FileNotDownloadableError
- """
- if self.content is None or type(self.content) is not io.BytesIO:
- self.FetchContent(mimetype)
- f = open(filename, 'wb')
- f.write(self.content.getvalue())
- f.close()
-
- @LoadAuth
- def FetchMetadata(self):
- """Download file's metadata from id using Files.get().
-
- :raises: ApiRequestError, FileNotUploadedError
- """
- file_id = self.metadata.get('id') or self.get('id')
- if file_id:
- try:
- metadata = self.auth.service.files().get(fileId=file_id).execute()
- except errors.HttpError as error:
- raise ApiRequestError(error)
- else:
- self.uploaded = True
- self.UpdateMetadata(metadata)
- else:
- raise FileNotUploadedError()
-
- @LoadMetadata
- def FetchContent(self, mimetype=None):
- """Download file's content from download_url.
-
- :raises: ApiRequestError, FileNotUploadedError, FileNotDownloadableError
- """
- download_url = self.metadata.get('downloadUrl')
- if download_url:
- self.content = io.BytesIO(self._DownloadFromUrl(download_url))
- self.dirty['content'] = False
- return
-
- export_links = self.metadata.get('exportLinks')
- if export_links and export_links.get(mimetype):
- self.content = io.BytesIO(
- self._DownloadFromUrl(export_links.get(mimetype)))
- self.dirty['content'] = False
- return
-
- raise FileNotDownloadableError(
- 'No downloadLink/exportLinks for mimetype found in metadata')
-
- def Upload(self, param=None):
- """Upload/update file by choosing the most efficient method.
-
- :param param: additional parameter to upload file.
- :type param: dict.
- :raises: ApiRequestError
- """
- if self.uploaded or self.get('id') is not None:
- if self.dirty['content']:
- self._FilesUpdate(param=param)
- else:
- self._FilesPatch(param=param)
- else:
- self._FilesInsert(param=param)
-
- def Delete(self):
- if self.get('id') is not None:
- self.auth.service.files().delete(fileId=self.get('id')).execute()
-
- @LoadAuth
- def _FilesInsert(self, param=None):
- """Upload a new file using Files.insert().
-
- :param param: additional parameter to upload file.
- :type param: dict.
- :raises: ApiRequestError
- """
- if param is None:
- param = {}
- param['body'] = self.GetChanges()
- try:
- if self.dirty['content']:
- param['media_body'] = self._BuildMediaBody()
- metadata = self.auth.service.files().insert(**param).execute()
- except errors.HttpError as error:
- raise ApiRequestError(error)
- else:
- self.uploaded = True
- self.dirty['content'] = False
- self.UpdateMetadata(metadata)
-
- @LoadAuth
- @LoadMetadata
- def _FilesUpdate(self, param=None):
- """Update metadata and/or content using Files.Update().
-
- :param param: additional parameter to upload file.
- :type param: dict.
- :raises: ApiRequestError, FileNotUploadedError
- """
- if param is None:
- param = {}
- param['body'] = self.GetChanges()
- param['fileId'] = self.metadata.get('id')
- try:
- if self.dirty['content']:
- param['media_body'] = self._BuildMediaBody()
- metadata = self.auth.service.files().update(**param).execute()
- except errors.HttpError as error:
- raise ApiRequestError(error)
- else:
- self.uploaded = True
- self.dirty['content'] = False
- self.UpdateMetadata(metadata)
-
- @LoadAuth
- @LoadMetadata
- def _FilesPatch(self, param=None):
- """Update metadata using Files.Patch().
-
- :param param: additional parameter to upload file.
- :type param: dict.
- :raises: ApiRequestError, FileNotUploadedError
- """
- if param is None:
- param = {}
- param['body'] = self.GetChanges()
- param['fileId'] = self.metadata.get('id')
- try:
- metadata = self.auth.service.files().patch(**param).execute()
- except errors.HttpError as error:
- raise ApiRequestError(error)
- else:
- self.UpdateMetadata(metadata)
-
- def _BuildMediaBody(self):
- """Build MediaIoBaseUpload to get prepared to upload content of the file.
-
- Sets mimeType as 'application/octet-stream' if not specified.
-
- :returns: MediaIoBaseUpload -- instance that will be used to upload content.
- """
- if self.get('mimeType') is None:
- self['mimeType'] = 'application/octet-stream'
-
- return MediaIoBaseUpload(self.content, self['mimeType'])
-
- @LoadAuth
- def _DownloadFromUrl(self, url):
- """Download file from url using provided credential.
-
- :param url: link of the file to download.
- :type url: str.
- :returns: str -- content of downloaded file in string.
- :raises: ApiRequestError
- """
- resp, content = self.auth.service._http.request(url)
- if resp.status != 200:
- raise ApiRequestError('Cannot download file: %s' % resp)
- return content
diff --git a/resources/lib/pydrive/settings.py b/resources/lib/pydrive/settings.py
deleted file mode 100644
index 8940f5f..0000000
--- a/resources/lib/pydrive/settings.py
+++ /dev/null
@@ -1,192 +0,0 @@
-from yaml import load
-from yaml import YAMLError
-try:
- from yaml import CLoader as Loader
-except ImportError:
- from yaml import Loader
-
-SETTINGS_FILE = 'settings.yaml'
-SETTINGS_STRUCT = {
- 'client_config_backend': {
- 'type': str,
- 'required': True,
- 'default': 'file',
- 'dependency': [
- {
- 'value': 'file',
- 'attribute': ['client_config_file']
- },
- {
- 'value': 'settings',
- 'attribute': ['client_config']
- }
- ]
- },
- 'save_credentials': {
- 'type': bool,
- 'required': True,
- 'default': False,
- 'dependency': [
- {
- 'value': True,
- 'attribute': ['save_credentials_backend']
- }
- ]
- },
- 'get_refresh_token': {
- 'type': bool,
- 'required': False,
- 'default': False
- },
- 'client_config_file': {
- 'type': str,
- 'required': False,
- 'default': 'client_secrets.json'
- },
- 'save_credentials_backend': {
- 'type': str,
- 'required': False,
- 'dependency': [
- {
- 'value': 'file',
- 'attribute': ['save_credentials_file']
- }
- ]
- },
- 'client_config': {
- 'type': dict,
- 'required': False,
- 'struct': {
- 'client_id': {
- 'type': str,
- 'required': True,
- 'default':'blank'
- },
- 'client_secret': {
- 'type': str,
- 'required': True,
- 'default':'blank'
- },
- 'auth_uri': {
- 'type': str,
- 'required': True,
- 'default': 'https://accounts.google.com/o/oauth2/auth'
- },
- 'token_uri': {
- 'type': str,
- 'required': True,
- 'default': 'https://accounts.google.com/o/oauth2/token'
- },
- 'redirect_uri': {
- 'type': str,
- 'required': True,
- 'default': 'urn:ietf:wg:oauth:2.0:oob'
- },
- 'revoke_uri': {
- 'type': str,
- 'required': True,
- 'default': None
- }
- }
- },
- 'oauth_scope': {
- 'type': list,
- 'required': True,
- 'struct': str,
- 'default': ['https://www.googleapis.com/auth/drive']
- },
- 'save_credentials_file': {
- 'type': str,
- 'required': False,
- }
-}
-
-
-class SettingsError(IOError):
- """Error while loading/saving settings"""
-
-
-class InvalidConfigError(IOError):
- """Error trying to read client configuration."""
-
-
-def LoadSettingsFile(filename=SETTINGS_FILE):
- """Loads settings file in yaml format given file name.
-
- :param filename: path for settings file. 'settings.yaml' by default.
- :type filename: str.
- :raises: SettingsError
- """
- try:
- stream = file(filename, 'r')
- data = load(stream, Loader=Loader)
- except (YAMLError, IOError) as e:
- raise SettingsError(e)
- return data
-
-
-def ValidateSettings(data):
- """Validates if current settings is valid.
-
- :param data: dictionary containing all settings.
- :type data: dict.
- :raises: InvalidConfigError
- """
- _ValidateSettingsStruct(data, SETTINGS_STRUCT)
-
-
-def _ValidateSettingsStruct(data, struct):
- """Validates if provided data fits provided structure.
-
- :param data: dictionary containing settings.
- :type data: dict.
- :param struct: dictionary containing structure information of settings.
- :type struct: dict.
- :raises: InvalidConfigError
- """
- # Validate required elements of the setting.
- for key in struct:
- if struct[key]['required']:
- _ValidateSettingsElement(data, struct, key)
-
-
-def _ValidateSettingsElement(data, struct, key):
- """Validates if provided element of settings data fits provided structure.
-
- :param data: dictionary containing settings.
- :type data: dict.
- :param struct: dictionary containing structure information of settings.
- :type struct: dict.
- :param key: key of the settings element to validate.
- :type key: str.
- :raises: InvalidConfigError
- """
- # Check if data exists. If not, check if default value exists.
- value = data.get(key)
- data_type = struct[key]['type']
- if value is None:
- try:
- default = struct[key]['default']
- except KeyError:
- raise InvalidConfigError('Missing required setting %s' % key)
- else:
- data[key] = default
- # If data exists, Check type of the data
- elif type(value) is not data_type:
- raise InvalidConfigError('Setting %s should be type %s' % (key, data_type))
- # If type of this data is dict, check if structure of the data is valid.
- if data_type is dict:
- _ValidateSettingsStruct(data[key], struct[key]['struct'])
- # If type of this data is list, check if all values in the list is valid.
- elif data_type is list:
- for element in data[key]:
- if type(element) is not struct[key]['struct']:
- raise InvalidConfigError('Setting %s should be list of %s' %
- (key, struct[key]['struct']))
- # Check dependency of this attribute.
- dependencies = struct[key].get('dependency')
- if dependencies:
- for dependency in dependencies:
- if value == dependency['value']:
- for reqkey in dependency['attribute']:
- _ValidateSettingsElement(data, struct, reqkey)
diff --git a/resources/lib/pydrive/settings.yaml b/resources/lib/pydrive/settings.yaml
deleted file mode 100644
index 1b64f1b..0000000
--- a/resources/lib/pydrive/settings.yaml
+++ /dev/null
@@ -1,7 +0,0 @@
-client_config_backend: 'settings'
-client_config:
- client_id: "blank"
- client_secret: "blank"
-get_refresh_token: True
-oauth_scope:
- - "https://www.googleapis.com/auth/drive.file"
\ No newline at end of file