Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/extensions/web/google/google.py
blob: 2d0373e8665a33f95d689b2400a7d7243eb867d6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
#!/usr/bin/env python
#
# Copyright (c) 2012 Raul Gutierrez S. - rgs@itevenworks.net

#Permission is hereby granted, free of charge, to any person obtaining a copy
#of this software and associated documentation files (the "Software"), to deal
#in the Software without restriction, including without limitation the rights
#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
#copies of the Software, and to permit persons to whom the Software is
#furnished to do so, subject to the following conditions:

#The above copyright notice and this permission notice shall be included in
#all copies or substantial portions of the Software.

#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
#THE SOFTWARE.

import httplib
import json
import logging
import mimetypes
import time
import urllib
import urllib2
import os

from gi.repository import GObject

class GoogleAccount():
    APP_ID = "634890811982.apps.googleusercontent.com"
    SCOPE = [
            'https://www.googleapis.com/auth/drive.file',
            'https://www.googleapis.com/auth/userinfo.email'
            ]
    REDIRECT_URI = "http://localhost"

    _refresh_token = ""
    _access_token = ""

    @classmethod
    def auth_url(cls):
        url = 'https://accounts.google.com/o/oauth2/auth'
        params = [
            ('client_id', cls.APP_ID),
            ('redirect_uri', cls.REDIRECT_URI),
            ('response_type', 'code'),
            ('scope', ' '.join(cls.SCOPE)),
            ('state', 'auth_cb')
            ]

        return "%s?%s" % (url, urllib.urlencode(params))

    @classmethod
    def set_refresh_token(cls, refresh_token):
        cls._refresh_token = refresh_token

    @classmethod
    def set_access_token(cls, access_token):
        cls._access_token = access_token

    @classmethod
    def access_token(cls):
        return cls._access_token

    @classmethod
    def refresh_access_token(cls, token):
        token_url = 'https://accounts.google.com/o/oauth2/token'
        params = [
            ('client_id', cls.APP_ID),
            ('refresh_token', token),
            ('grant_type', 'refresh_token')
            ]

        return json.load(urllib2.urlopen(token_url, urllib.urlencode(params)))

class GDriveObjectNotCreatedException(Exception):
    pass

class GDriveBadCall(Exception):
    pass

class GDriveFile(GObject.GObject):

    __gsignals__ = {
        'file-created': (GObject.SignalFlags.RUN_FIRST, None, ([str])),
        'file-create-failed': (GObject.SignalFlags.RUN_FIRST, None, ([str])),
    }

    def __init__(self, gdrive_object_id=None):
        GObject.GObject.__init__(self)
        self.gdrive_object_id = gdrive_object_id

    def create(self, file_path):
        GObject.idle_add(self._create, file_path)

    def check_created(self, method_name):
        if self.gdrive_object_id is None:
            errmsg = "Need to call create before calling %s" % (method_name)
            raise GDriveObjectNotCreatedException(errmsg)

    def _create(self, file_path):
        # Upload the file
        response = self._http_call('POST', '/upload/drive/v2/files?uploadType=media', file_path) #TODO use the resumable interface
        if not self._check_success('_upload', response):
            return

        data = json.loads(response.read())
        if not 'id' in data:
            raise GDriveBadCall(data)

        self.gdrive_object_id = data['id']

        # Update metadata
        url = '/drive/v2/files'
        params = {
                'title': os.path.basename(file_path),
                'description': 'A Sugar Journal file'
                }

        response = self._http_call('PUT', '%s/%s' % (url, self.gdrive_object_id), params)
        if not self._check_success('_create', response):
            return

        self.emit('file-created', self.gdrive_object_id)

    def _http_call(self, method, url, params = None):
        if not params:
            params = {}

        headers = {
                'Authorization': "Bearer %s" % (self.access_token())
                }

        if isinstance(params, basestring):
            mimetype, encoding = mimetypes.guess_type(params, strict=False)
            if mimetype:
                headers['Content-Type'] = mimetype
            if encoding:
                headers['Content-Encoding'] = encoding
            params = open(params, 'rb')
        else:
            params = json.dumps(params)
            headers['Content-Type'] = 'application/json'

        conn = httplib.HTTPSConnection('www.googleapis.com')
        conn.request(method, url, params, headers)

        return conn.getresponse()

    def _check_success(self, operation, response):
        if response.status and response.status == 200:
            return True

        logging.debug("%s failed, HTTP resp code: %d" % (operation, response.status))
        if response.status == 401:
            failed_reason = "Expired access token."
        else:
            failed_reason = "Failed reason unknown: %s" % (str(response))
            print failed_reason

        self.emit('file-create-failed', failed_reason)
        return False

if __name__ == '__main__':
    import sys
    if len(sys.argv) != 3:
        print "Tests need access_token and a file path!"
        exit(1)

    access_token, file_path = sys.argv[1:3]
    GoogleAccount.set_access_token(access_token)


def test_create_file(loop):
    def file_created_cb(file, file_id, loop):
        print "File created: %s" % (file_id)
        loop.quit()

    file = GDriveFile()
    file.connect('file-created', file_created_cb, loop)
    file.create(file_path)


def timeout_cb(test_name, loop):
    print "%s timed out and failed" % (test_name)
    loop.quit()
    return False

if __name__ == '__main__':
    tests = [eval(t) for t in dir() if t.startswith('test_')]

    for t in tests:
        print "\n=== Starting %s (%s) ===" % (t.__name__, time.time())
        loop = GObject.MainLoop()
        tid = GObject.timeout_add(30000, timeout_cb, t.__name__, loop)
        t(loop)
        loop.run()
        GObject.source_remove(tid)
        print "=== Finished %s (%s) ===\n" % (t.__name__, time.time())