django-gmailapi-backend 0.3.3__tar.gz → 0.3.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {django_gmailapi_backend-0.3.3 → django_gmailapi_backend-0.3.4}/PKG-INFO +9 -1
- {django_gmailapi_backend-0.3.3 → django_gmailapi_backend-0.3.4}/README.md +7 -0
- {django_gmailapi_backend-0.3.3 → django_gmailapi_backend-0.3.4}/django_gmailapi_backend.egg-info/PKG-INFO +9 -1
- {django_gmailapi_backend-0.3.3 → django_gmailapi_backend-0.3.4}/django_gmailapi_backend.egg-info/requires.txt +1 -0
- django_gmailapi_backend-0.3.4/gmailapi_backend/__init__.py +1 -0
- {django_gmailapi_backend-0.3.3 → django_gmailapi_backend-0.3.4}/gmailapi_backend/bin/gmail_oauth2.py +165 -112
- {django_gmailapi_backend-0.3.3 → django_gmailapi_backend-0.3.4}/gmailapi_backend/mail.py +31 -15
- django_gmailapi_backend-0.3.4/setup.py +51 -0
- django_gmailapi_backend-0.3.3/gmailapi_backend/__init__.py +0 -1
- django_gmailapi_backend-0.3.3/setup.py +0 -49
- {django_gmailapi_backend-0.3.3 → django_gmailapi_backend-0.3.4}/LICENSE +0 -0
- {django_gmailapi_backend-0.3.3 → django_gmailapi_backend-0.3.4}/django_gmailapi_backend.egg-info/SOURCES.txt +0 -0
- {django_gmailapi_backend-0.3.3 → django_gmailapi_backend-0.3.4}/django_gmailapi_backend.egg-info/dependency_links.txt +0 -0
- {django_gmailapi_backend-0.3.3 → django_gmailapi_backend-0.3.4}/django_gmailapi_backend.egg-info/entry_points.txt +0 -0
- {django_gmailapi_backend-0.3.3 → django_gmailapi_backend-0.3.4}/django_gmailapi_backend.egg-info/top_level.txt +0 -0
- {django_gmailapi_backend-0.3.3 → django_gmailapi_backend-0.3.4}/gmailapi_backend/bin/__init__.py +0 -0
- {django_gmailapi_backend-0.3.3 → django_gmailapi_backend-0.3.4}/requirements.txt +0 -0
- {django_gmailapi_backend-0.3.3 → django_gmailapi_backend-0.3.4}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: django-gmailapi-backend
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.4
|
|
4
4
|
Summary: Email backend for Django which sends email via the Gmail API
|
|
5
5
|
Home-page: https://github.com/dolfim/django-gmailapi-backend
|
|
6
6
|
Author: Michele Dolfi
|
|
@@ -24,6 +24,7 @@ Description-Content-Type: text/markdown
|
|
|
24
24
|
License-File: LICENSE
|
|
25
25
|
Requires-Dist: google-api-python-client~=2.0
|
|
26
26
|
Requires-Dist: google-auth<3.0.0dev,>=1.16.0
|
|
27
|
+
Requires-Dist: google-auth-oauthlib>=1.4.0
|
|
27
28
|
Dynamic: author
|
|
28
29
|
Dynamic: author-email
|
|
29
30
|
Dynamic: classifier
|
|
@@ -100,3 +101,10 @@ the setup of the API credentials. The following outlines the key steps:
|
|
|
100
101
|
--scope="https://www.googleapis.com/auth/gmail.send"
|
|
101
102
|
```
|
|
102
103
|
|
|
104
|
+
> [!NOTE]
|
|
105
|
+
> OOB flow is deprecated so ```--generate_oauth2_token``` can fail. To overcome this issue try instead ```--ugo2t``` option with the following syntax
|
|
106
|
+
```sh
|
|
107
|
+
gmail_oauth2 --ugo2t \
|
|
108
|
+
--credentials_path="<credentials_relative_path>" # need to be download from gcp after creating an Oauth2 client
|
|
109
|
+
```
|
|
110
|
+
|
|
@@ -63,3 +63,10 @@ the setup of the API credentials. The following outlines the key steps:
|
|
|
63
63
|
--scope="https://www.googleapis.com/auth/gmail.send"
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
+
> [!NOTE]
|
|
67
|
+
> OOB flow is deprecated so ```--generate_oauth2_token``` can fail. To overcome this issue try instead ```--ugo2t``` option with the following syntax
|
|
68
|
+
```sh
|
|
69
|
+
gmail_oauth2 --ugo2t \
|
|
70
|
+
--credentials_path="<credentials_relative_path>" # need to be download from gcp after creating an Oauth2 client
|
|
71
|
+
```
|
|
72
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: django-gmailapi-backend
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.4
|
|
4
4
|
Summary: Email backend for Django which sends email via the Gmail API
|
|
5
5
|
Home-page: https://github.com/dolfim/django-gmailapi-backend
|
|
6
6
|
Author: Michele Dolfi
|
|
@@ -24,6 +24,7 @@ Description-Content-Type: text/markdown
|
|
|
24
24
|
License-File: LICENSE
|
|
25
25
|
Requires-Dist: google-api-python-client~=2.0
|
|
26
26
|
Requires-Dist: google-auth<3.0.0dev,>=1.16.0
|
|
27
|
+
Requires-Dist: google-auth-oauthlib>=1.4.0
|
|
27
28
|
Dynamic: author
|
|
28
29
|
Dynamic: author-email
|
|
29
30
|
Dynamic: classifier
|
|
@@ -100,3 +101,10 @@ the setup of the API credentials. The following outlines the key steps:
|
|
|
100
101
|
--scope="https://www.googleapis.com/auth/gmail.send"
|
|
101
102
|
```
|
|
102
103
|
|
|
104
|
+
> [!NOTE]
|
|
105
|
+
> OOB flow is deprecated so ```--generate_oauth2_token``` can fail. To overcome this issue try instead ```--ugo2t``` option with the following syntax
|
|
106
|
+
```sh
|
|
107
|
+
gmail_oauth2 --ugo2t \
|
|
108
|
+
--credentials_path="<credentials_relative_path>" # need to be download from gcp after creating an Oauth2 client
|
|
109
|
+
```
|
|
110
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.3.4"
|
{django_gmailapi_backend-0.3.3 → django_gmailapi_backend-0.3.4}/gmailapi_backend/bin/gmail_oauth2.py
RENAMED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
# you may not use this file except in compliance with the License.
|
|
7
7
|
# You may obtain a copy of the License at
|
|
8
8
|
#
|
|
9
|
-
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
10
|
#
|
|
11
11
|
# Unless required by applicable law or agreed to in writing, software
|
|
12
12
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
@@ -65,10 +65,9 @@ from __future__ import print_function
|
|
|
65
65
|
import base64
|
|
66
66
|
import imaplib
|
|
67
67
|
import json
|
|
68
|
-
from optparse import OptionParser
|
|
69
68
|
import smtplib
|
|
70
69
|
import sys
|
|
71
|
-
import
|
|
70
|
+
from optparse import OptionParser
|
|
72
71
|
|
|
73
72
|
import six
|
|
74
73
|
|
|
@@ -76,62 +75,81 @@ import six
|
|
|
76
75
|
def SetupOptionParser():
|
|
77
76
|
# Usage message is the module's docstring.
|
|
78
77
|
parser = OptionParser(usage=__doc__)
|
|
79
|
-
parser.add_option(
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
parser.add_option(
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
parser.add_option(
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
parser.add_option(
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
78
|
+
parser.add_option(
|
|
79
|
+
"--generate_oauth2_token",
|
|
80
|
+
action="store_true",
|
|
81
|
+
dest="generate_oauth2_token",
|
|
82
|
+
help="generates an OAuth2 token for testing",
|
|
83
|
+
)
|
|
84
|
+
parser.add_option(
|
|
85
|
+
"--ugo2t", # updated_generate_oauth2_token
|
|
86
|
+
action="store_true",
|
|
87
|
+
dest="updated_generate_oauth2_token",
|
|
88
|
+
help="generates a token and refresh token for a given credentials file from google cloud plataform",
|
|
89
|
+
)
|
|
90
|
+
parser.add_option(
|
|
91
|
+
"--generate_oauth2_string",
|
|
92
|
+
action="store_true",
|
|
93
|
+
dest="generate_oauth2_string",
|
|
94
|
+
help="generates an initial client response string for OAuth2",
|
|
95
|
+
)
|
|
96
|
+
parser.add_option(
|
|
97
|
+
"--client_id",
|
|
98
|
+
default=None,
|
|
99
|
+
help="Client ID of the application that is authenticating. "
|
|
100
|
+
"See OAuth2 documentation for details.",
|
|
101
|
+
)
|
|
102
|
+
parser.add_option(
|
|
103
|
+
"--client_secret",
|
|
104
|
+
default=None,
|
|
105
|
+
help="Client secret of the application that is "
|
|
106
|
+
"authenticating. See OAuth2 documentation for "
|
|
107
|
+
"details.",
|
|
108
|
+
)
|
|
109
|
+
parser.add_option("--access_token", default=None, help="OAuth2 access token")
|
|
110
|
+
parser.add_option("--refresh_token", default=None, help="OAuth2 refresh token")
|
|
111
|
+
parser.add_option(
|
|
112
|
+
"--scope",
|
|
113
|
+
default="https://mail.google.com/",
|
|
114
|
+
help="scope for the access token. Multiple scopes can be "
|
|
115
|
+
"listed separated by spaces with the whole argument "
|
|
116
|
+
"quoted.",
|
|
117
|
+
)
|
|
118
|
+
parser.add_option(
|
|
119
|
+
"--test_imap_authentication",
|
|
120
|
+
action="store_true",
|
|
121
|
+
dest="test_imap_authentication",
|
|
122
|
+
help="attempts to authenticate to IMAP",
|
|
123
|
+
)
|
|
124
|
+
parser.add_option(
|
|
125
|
+
"--test_smtp_authentication",
|
|
126
|
+
action="store_true",
|
|
127
|
+
dest="test_smtp_authentication",
|
|
128
|
+
help="attempts to authenticate to SMTP",
|
|
129
|
+
)
|
|
130
|
+
parser.add_option(
|
|
131
|
+
"--user",
|
|
132
|
+
default=None,
|
|
133
|
+
help="email address of user whose account is being accessed",
|
|
134
|
+
)
|
|
135
|
+
parser.add_option(
|
|
136
|
+
"--quiet",
|
|
137
|
+
action="store_true",
|
|
138
|
+
default=False,
|
|
139
|
+
dest="quiet",
|
|
140
|
+
help="Omit verbose descriptions and only print machine-readable outputs.",
|
|
141
|
+
)
|
|
126
142
|
return parser
|
|
127
143
|
|
|
128
144
|
|
|
129
145
|
# The URL root for accessing Google Accounts.
|
|
130
|
-
GOOGLE_ACCOUNTS_BASE_URL =
|
|
146
|
+
GOOGLE_ACCOUNTS_BASE_URL = "https://accounts.google.com"
|
|
131
147
|
|
|
148
|
+
# Google tokens scope to restrict applications
|
|
149
|
+
SCOPES = ["https://mail.google.com/"]
|
|
132
150
|
|
|
133
151
|
# Hardcoded dummy redirect URI for non-web apps.
|
|
134
|
-
REDIRECT_URI =
|
|
152
|
+
REDIRECT_URI = "urn:ietf:wg:oauth:2.0:oob"
|
|
135
153
|
|
|
136
154
|
|
|
137
155
|
def AccountsUrl(command):
|
|
@@ -143,12 +161,12 @@ def AccountsUrl(command):
|
|
|
143
161
|
Returns:
|
|
144
162
|
A URL for the given command.
|
|
145
163
|
"""
|
|
146
|
-
return
|
|
164
|
+
return "%s/%s" % (GOOGLE_ACCOUNTS_BASE_URL, command)
|
|
147
165
|
|
|
148
166
|
|
|
149
167
|
def UrlEscape(text):
|
|
150
168
|
# See OAUTH 5.1 for a definition of which characters need to be escaped.
|
|
151
|
-
return six.moves.urllib.parse.quote(text, safe=
|
|
169
|
+
return six.moves.urllib.parse.quote(text, safe="~-._")
|
|
152
170
|
|
|
153
171
|
|
|
154
172
|
def UrlUnescape(text):
|
|
@@ -167,11 +185,11 @@ def FormatUrlParams(params):
|
|
|
167
185
|
"""
|
|
168
186
|
param_fragments = []
|
|
169
187
|
for param in sorted(params.items(), key=lambda x: x[0]):
|
|
170
|
-
param_fragments.append(
|
|
171
|
-
return
|
|
188
|
+
param_fragments.append("%s=%s" % (param[0], UrlEscape(param[1])))
|
|
189
|
+
return "&".join(param_fragments)
|
|
172
190
|
|
|
173
191
|
|
|
174
|
-
def GeneratePermissionUrl(client_id, scope=
|
|
192
|
+
def GeneratePermissionUrl(client_id, scope="https://mail.google.com/"):
|
|
175
193
|
"""Generates the URL for authorizing access.
|
|
176
194
|
|
|
177
195
|
This uses the "OAuth2 for Installed Applications" flow described at
|
|
@@ -184,12 +202,11 @@ def GeneratePermissionUrl(client_id, scope='https://mail.google.com/'):
|
|
|
184
202
|
A URL that the user should visit in their browser.
|
|
185
203
|
"""
|
|
186
204
|
params = {}
|
|
187
|
-
params[
|
|
188
|
-
params[
|
|
189
|
-
params[
|
|
190
|
-
params[
|
|
191
|
-
return
|
|
192
|
-
FormatUrlParams(params))
|
|
205
|
+
params["client_id"] = client_id
|
|
206
|
+
params["redirect_uri"] = REDIRECT_URI
|
|
207
|
+
params["scope"] = scope
|
|
208
|
+
params["response_type"] = "code"
|
|
209
|
+
return "%s?%s" % (AccountsUrl("o/oauth2/auth"), FormatUrlParams(params))
|
|
193
210
|
|
|
194
211
|
|
|
195
212
|
def AuthorizeTokens(client_id, client_secret, authorization_code):
|
|
@@ -208,15 +225,16 @@ def AuthorizeTokens(client_id, client_secret, authorization_code):
|
|
|
208
225
|
fields include 'access_token', 'expires_in', and 'refresh_token'.
|
|
209
226
|
"""
|
|
210
227
|
params = {}
|
|
211
|
-
params[
|
|
212
|
-
params[
|
|
213
|
-
params[
|
|
214
|
-
params[
|
|
215
|
-
params[
|
|
216
|
-
request_url = AccountsUrl(
|
|
228
|
+
params["client_id"] = client_id
|
|
229
|
+
params["client_secret"] = client_secret
|
|
230
|
+
params["code"] = authorization_code
|
|
231
|
+
params["redirect_uri"] = REDIRECT_URI
|
|
232
|
+
params["grant_type"] = "authorization_code"
|
|
233
|
+
request_url = AccountsUrl("o/oauth2/token")
|
|
217
234
|
|
|
218
235
|
response = six.moves.urllib.request.urlopen(
|
|
219
|
-
request_url, six.moves.urllib.parse.urlencode(params).encode(
|
|
236
|
+
request_url, six.moves.urllib.parse.urlencode(params).encode("utf-8")
|
|
237
|
+
).read()
|
|
220
238
|
return json.loads(response)
|
|
221
239
|
|
|
222
240
|
|
|
@@ -234,14 +252,15 @@ def RefreshToken(client_id, client_secret, refresh_token):
|
|
|
234
252
|
fields include 'access_token', 'expires_in', and 'refresh_token'.
|
|
235
253
|
"""
|
|
236
254
|
params = {}
|
|
237
|
-
params[
|
|
238
|
-
params[
|
|
239
|
-
params[
|
|
240
|
-
params[
|
|
241
|
-
request_url = AccountsUrl(
|
|
255
|
+
params["client_id"] = client_id
|
|
256
|
+
params["client_secret"] = client_secret
|
|
257
|
+
params["refresh_token"] = refresh_token
|
|
258
|
+
params["grant_type"] = "refresh_token"
|
|
259
|
+
request_url = AccountsUrl("o/oauth2/token")
|
|
242
260
|
|
|
243
261
|
response = six.moves.urllib.request.urlopen(
|
|
244
|
-
request_url, six.moves.urllib.parse.urlencode(params).encode(
|
|
262
|
+
request_url, six.moves.urllib.parse.urlencode(params).encode("utf-8")
|
|
263
|
+
).read()
|
|
245
264
|
return json.loads(response)
|
|
246
265
|
|
|
247
266
|
|
|
@@ -258,7 +277,7 @@ def GenerateOAuth2String(username, access_token, base64_encode=True):
|
|
|
258
277
|
Returns:
|
|
259
278
|
The SASL argument for the OAuth2 mechanism.
|
|
260
279
|
"""
|
|
261
|
-
auth_string =
|
|
280
|
+
auth_string = "user=%s\1auth=Bearer %s\1\1" % (username, access_token)
|
|
262
281
|
if base64_encode:
|
|
263
282
|
auth_string = base64.b64encode(auth_string)
|
|
264
283
|
return auth_string
|
|
@@ -275,10 +294,10 @@ def TestImapAuthentication(user, auth_string):
|
|
|
275
294
|
Must not be base64-encoded, since imaplib does its own base64-encoding.
|
|
276
295
|
"""
|
|
277
296
|
print()
|
|
278
|
-
imap_conn = imaplib.IMAP4_SSL(
|
|
297
|
+
imap_conn = imaplib.IMAP4_SSL("imap.gmail.com")
|
|
279
298
|
imap_conn.debug = 4
|
|
280
|
-
imap_conn.authenticate(
|
|
281
|
-
imap_conn.select(
|
|
299
|
+
imap_conn.authenticate("XOAUTH2", lambda x: auth_string)
|
|
300
|
+
imap_conn.select("INBOX")
|
|
282
301
|
|
|
283
302
|
|
|
284
303
|
def TestSmtpAuthentication(user, auth_string):
|
|
@@ -290,65 +309,99 @@ def TestSmtpAuthentication(user, auth_string):
|
|
|
290
309
|
GenerateOAuth2String.
|
|
291
310
|
"""
|
|
292
311
|
print()
|
|
293
|
-
smtp_conn = smtplib.SMTP(
|
|
312
|
+
smtp_conn = smtplib.SMTP("smtp.gmail.com", 587)
|
|
294
313
|
smtp_conn.set_debuglevel(True)
|
|
295
|
-
smtp_conn.ehlo(
|
|
314
|
+
smtp_conn.ehlo("test")
|
|
296
315
|
smtp_conn.starttls()
|
|
297
|
-
smtp_conn.docmd(
|
|
316
|
+
smtp_conn.docmd("AUTH", "XOAUTH2 " + base64.b64encode(auth_string))
|
|
298
317
|
|
|
299
318
|
|
|
300
319
|
def RequireOptions(options, *args):
|
|
301
320
|
missing = [arg for arg in args if getattr(options, arg) is None]
|
|
302
321
|
if missing:
|
|
303
|
-
print(
|
|
322
|
+
print("Missing options: %s" % " ".join(missing))
|
|
304
323
|
sys.exit(-1)
|
|
305
324
|
|
|
306
325
|
|
|
326
|
+
def UpdatedRefreshTokenGenerator(credentials_path):
|
|
327
|
+
global SCOPES
|
|
328
|
+
|
|
329
|
+
from google_auth_oauthlib.flow import InstalledAppFlow
|
|
330
|
+
|
|
331
|
+
try:
|
|
332
|
+
flow = InstalledAppFlow.from_client_secrets_file(
|
|
333
|
+
credentials_path, # your credentials file from google, download it from gcp
|
|
334
|
+
SCOPES,
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
creds = flow.run_local_server(port=8088)
|
|
338
|
+
return creds
|
|
339
|
+
except OSError as e:
|
|
340
|
+
print(f"[ERROR] PORT ALREADY IN USE: {e}")
|
|
341
|
+
|
|
342
|
+
|
|
307
343
|
def main():
|
|
308
344
|
argv = sys.argv
|
|
309
345
|
options_parser = SetupOptionParser()
|
|
310
346
|
(options, args) = options_parser.parse_args()
|
|
311
347
|
if options.refresh_token:
|
|
312
|
-
RequireOptions(options,
|
|
313
|
-
response = RefreshToken(
|
|
314
|
-
|
|
348
|
+
RequireOptions(options, "client_id", "client_secret")
|
|
349
|
+
response = RefreshToken(
|
|
350
|
+
options.client_id, options.client_secret, options.refresh_token
|
|
351
|
+
)
|
|
315
352
|
if options.quiet:
|
|
316
|
-
print(response[
|
|
353
|
+
print(response["access_token"])
|
|
317
354
|
else:
|
|
318
|
-
print(
|
|
319
|
-
print(
|
|
355
|
+
print("Access Token: %s" % response["access_token"])
|
|
356
|
+
print("Access Token Expiration Seconds: %s" % response["expires_in"])
|
|
320
357
|
elif options.generate_oauth2_string:
|
|
321
|
-
RequireOptions(options,
|
|
358
|
+
RequireOptions(options, "user", "access_token")
|
|
322
359
|
oauth2_string = GenerateOAuth2String(options.user, options.access_token)
|
|
323
360
|
if options.quiet:
|
|
324
361
|
print(oauth2_string)
|
|
325
362
|
else:
|
|
326
|
-
print(
|
|
363
|
+
print("OAuth2 argument:\n" + oauth2_string)
|
|
327
364
|
elif options.generate_oauth2_token:
|
|
328
|
-
RequireOptions(options,
|
|
329
|
-
print(
|
|
330
|
-
print(
|
|
331
|
-
authorization_code = six.moves.input(
|
|
332
|
-
response = AuthorizeTokens(
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
print(
|
|
336
|
-
print(
|
|
365
|
+
RequireOptions(options, "client_id", "client_secret")
|
|
366
|
+
print("To authorize token, visit this url and follow the directions:")
|
|
367
|
+
print(" %s" % GeneratePermissionUrl(options.client_id, options.scope))
|
|
368
|
+
authorization_code = six.moves.input("Enter verification code: ")
|
|
369
|
+
response = AuthorizeTokens(
|
|
370
|
+
options.client_id, options.client_secret, authorization_code
|
|
371
|
+
)
|
|
372
|
+
print("Refresh Token: %s" % response["refresh_token"])
|
|
373
|
+
print("Access Token: %s" % response["access_token"])
|
|
374
|
+
print("Access Token Expiration Seconds: %s" % response["expires_in"])
|
|
375
|
+
elif options.updated_generate_oauth2_token:
|
|
376
|
+
RequireOptions(options, "credentials_path")
|
|
377
|
+
print("LogIn to check the authenticity of the requested action")
|
|
378
|
+
creds = UpdatedRefreshTokenGenerator(options.credentials_path)
|
|
379
|
+
if creds:
|
|
380
|
+
print("Token: %s" % creds.token)
|
|
381
|
+
print("Refresh Token: %s" % creds.refresh_token)
|
|
382
|
+
else:
|
|
383
|
+
print("An error occurs, check your gcp project is verified and retry later")
|
|
337
384
|
elif options.test_imap_authentication:
|
|
338
|
-
RequireOptions(options,
|
|
339
|
-
TestImapAuthentication(
|
|
340
|
-
|
|
341
|
-
|
|
385
|
+
RequireOptions(options, "user", "access_token")
|
|
386
|
+
TestImapAuthentication(
|
|
387
|
+
options.user,
|
|
388
|
+
GenerateOAuth2String(
|
|
389
|
+
options.user, options.access_token, base64_encode=False
|
|
390
|
+
),
|
|
391
|
+
)
|
|
342
392
|
elif options.test_smtp_authentication:
|
|
343
|
-
RequireOptions(options,
|
|
344
|
-
TestSmtpAuthentication(
|
|
345
|
-
|
|
346
|
-
|
|
393
|
+
RequireOptions(options, "user", "access_token")
|
|
394
|
+
TestSmtpAuthentication(
|
|
395
|
+
options.user,
|
|
396
|
+
GenerateOAuth2String(
|
|
397
|
+
options.user, options.access_token, base64_encode=False
|
|
398
|
+
),
|
|
399
|
+
)
|
|
347
400
|
else:
|
|
348
401
|
options_parser.print_help()
|
|
349
|
-
print(
|
|
402
|
+
print("Nothing to do, exiting.")
|
|
350
403
|
return
|
|
351
404
|
|
|
352
405
|
|
|
353
|
-
if __name__ ==
|
|
354
|
-
main()
|
|
406
|
+
if __name__ == "__main__":
|
|
407
|
+
main()
|
|
@@ -1,47 +1,60 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Email backend that uses the GMail API via OAuth2 authentication.
|
|
3
3
|
"""
|
|
4
|
+
|
|
4
5
|
import base64
|
|
5
6
|
import logging
|
|
6
7
|
|
|
7
|
-
from django.conf import settings
|
|
8
|
-
from django.core.mail.backends.base import BaseEmailBackend
|
|
9
|
-
|
|
10
8
|
import google.oauth2.credentials
|
|
11
9
|
import googleapiclient.discovery
|
|
10
|
+
from django.conf import settings
|
|
11
|
+
from django.core.mail.backends.base import BaseEmailBackend
|
|
12
12
|
|
|
13
13
|
logger = logging.getLogger(__name__)
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
class GmailBackend(BaseEmailBackend):
|
|
17
|
-
def __init__(
|
|
18
|
-
|
|
17
|
+
def __init__(
|
|
18
|
+
self,
|
|
19
|
+
client_id=None,
|
|
20
|
+
client_secret=None,
|
|
21
|
+
refresh_token=None,
|
|
22
|
+
user_id=None,
|
|
23
|
+
fail_silently=False,
|
|
24
|
+
**kwargs,
|
|
25
|
+
):
|
|
19
26
|
super().__init__(fail_silently=fail_silently, **kwargs)
|
|
20
27
|
self.client_id = client_id or settings.GMAIL_API_CLIENT_ID
|
|
21
28
|
self.client_secret = client_secret or settings.GMAIL_API_CLIENT_SECRET
|
|
22
29
|
self.refresh_token = refresh_token or settings.GMAIL_API_REFRESH_TOKEN
|
|
23
|
-
if hasattr(settings,
|
|
30
|
+
if hasattr(settings, "GMAIL_API_USER_ID"):
|
|
24
31
|
self.user_id = user_id or settings.GMAIL_API_USER_ID
|
|
25
32
|
else:
|
|
26
|
-
self.user_id = user_id or
|
|
33
|
+
self.user_id = user_id or "me"
|
|
27
34
|
|
|
28
35
|
credentials = google.oauth2.credentials.Credentials(
|
|
29
|
-
|
|
36
|
+
"token",
|
|
30
37
|
refresh_token=self.refresh_token,
|
|
31
|
-
token_uri=
|
|
38
|
+
token_uri="https://oauth2.googleapis.com/token",
|
|
32
39
|
client_id=self.client_id,
|
|
33
|
-
client_secret=self.client_secret
|
|
40
|
+
client_secret=self.client_secret,
|
|
41
|
+
)
|
|
42
|
+
self.service = googleapiclient.discovery.build(
|
|
43
|
+
"gmail", "v1", credentials=credentials, cache_discovery=False
|
|
34
44
|
)
|
|
35
|
-
self.service = googleapiclient.discovery.build('gmail', 'v1', credentials=credentials, cache_discovery=False)
|
|
36
45
|
|
|
37
46
|
def send_message(self, email_message):
|
|
38
47
|
if not email_message.recipients():
|
|
39
48
|
return False
|
|
40
49
|
message = email_message.message()
|
|
41
50
|
if email_message.bcc:
|
|
42
|
-
email_message._set_list_header_if_not_empty(
|
|
43
|
-
|
|
44
|
-
|
|
51
|
+
email_message._set_list_header_if_not_empty(
|
|
52
|
+
message, "Bcc", email_message.bcc
|
|
53
|
+
)
|
|
54
|
+
raw_message = {"raw": base64.urlsafe_b64encode(message.as_bytes()).decode()}
|
|
55
|
+
return (
|
|
56
|
+
self.service.users().messages().send(userId=self.user_id, body=raw_message)
|
|
57
|
+
)
|
|
45
58
|
|
|
46
59
|
def send_messages(self, email_messages):
|
|
47
60
|
"""Send all messages using BatchHttpRequest"""
|
|
@@ -53,7 +66,10 @@ class GmailBackend(BaseEmailBackend):
|
|
|
53
66
|
def send_callback(r_id, response, exception):
|
|
54
67
|
nonlocal msg_count, last_exception
|
|
55
68
|
if exception is not None:
|
|
56
|
-
logger.exception(
|
|
69
|
+
logger.exception(
|
|
70
|
+
"An error occurred sending the message via GMail API: %s",
|
|
71
|
+
exception,
|
|
72
|
+
)
|
|
57
73
|
last_exception = exception
|
|
58
74
|
else:
|
|
59
75
|
msg_count += 1
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
from setuptools import find_packages, setup
|
|
5
|
+
|
|
6
|
+
if sys.version_info < (3, 5):
|
|
7
|
+
raise "must use Python version 3.5 or higher"
|
|
8
|
+
|
|
9
|
+
with open("./gmailapi_backend/__init__.py", "r") as f:
|
|
10
|
+
MATCH_EXPR = "__version__[^'\"]+(['\"])([^'\"]+)"
|
|
11
|
+
VERSION = re.search(MATCH_EXPR, f.read()).group(2).strip()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
setup(
|
|
15
|
+
name="django-gmailapi-backend",
|
|
16
|
+
version=VERSION,
|
|
17
|
+
packages=find_packages(),
|
|
18
|
+
author="Michele Dolfi",
|
|
19
|
+
author_email="michele.dolfi@gmail.com",
|
|
20
|
+
license="Apache License 2.0",
|
|
21
|
+
entry_points={
|
|
22
|
+
"console_scripts": [
|
|
23
|
+
"gmail_oauth2 = gmailapi_backend.bin.gmail_oauth2:main",
|
|
24
|
+
]
|
|
25
|
+
},
|
|
26
|
+
install_requires=[
|
|
27
|
+
"google-api-python-client~=2.0",
|
|
28
|
+
"google-auth>=1.16.0,<3.0.0dev",
|
|
29
|
+
"google-auth-oauthlib>=1.4.0",
|
|
30
|
+
],
|
|
31
|
+
url="https://github.com/dolfim/django-gmailapi-backend",
|
|
32
|
+
long_description_content_type="text/markdown",
|
|
33
|
+
long_description=open("README.md").read(),
|
|
34
|
+
description="Email backend for Django which sends email via the Gmail API",
|
|
35
|
+
classifiers=[
|
|
36
|
+
"Intended Audience :: Developers",
|
|
37
|
+
"License :: OSI Approved :: Apache Software License",
|
|
38
|
+
"Operating System :: MacOS :: MacOS X",
|
|
39
|
+
"Operating System :: Microsoft :: Windows",
|
|
40
|
+
"Operating System :: POSIX",
|
|
41
|
+
"Programming Language :: Python",
|
|
42
|
+
"Programming Language :: Python :: 3",
|
|
43
|
+
"Programming Language :: Python :: 3.5",
|
|
44
|
+
"Programming Language :: Python :: 3.6",
|
|
45
|
+
"Programming Language :: Python :: 3.7",
|
|
46
|
+
"Programming Language :: Python :: 3.8",
|
|
47
|
+
"Framework :: Django",
|
|
48
|
+
"Topic :: Communications :: Email",
|
|
49
|
+
"Development Status :: 4 - Beta",
|
|
50
|
+
],
|
|
51
|
+
)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = '0.3.3'
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import re
|
|
2
|
-
from setuptools import setup, find_packages
|
|
3
|
-
|
|
4
|
-
import sys
|
|
5
|
-
if sys.version_info < (3, 5):
|
|
6
|
-
raise 'must use Python version 3.5 or higher'
|
|
7
|
-
|
|
8
|
-
with open('./gmailapi_backend/__init__.py', 'r') as f:
|
|
9
|
-
MATCH_EXPR = "__version__[^'\"]+(['\"])([^'\"]+)"
|
|
10
|
-
VERSION = re.search(MATCH_EXPR, f.read()).group(2).strip()
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
setup(
|
|
14
|
-
name='django-gmailapi-backend',
|
|
15
|
-
version=VERSION,
|
|
16
|
-
packages=find_packages(),
|
|
17
|
-
author="Michele Dolfi",
|
|
18
|
-
author_email="michele.dolfi@gmail.com",
|
|
19
|
-
license="Apache License 2.0",
|
|
20
|
-
entry_points={
|
|
21
|
-
'console_scripts': [
|
|
22
|
-
'gmail_oauth2 = gmailapi_backend.bin.gmail_oauth2:main',
|
|
23
|
-
]
|
|
24
|
-
},
|
|
25
|
-
install_requires=[
|
|
26
|
-
'google-api-python-client~=2.0',
|
|
27
|
-
'google-auth>=1.16.0,<3.0.0dev',
|
|
28
|
-
],
|
|
29
|
-
url="https://github.com/dolfim/django-gmailapi-backend",
|
|
30
|
-
long_description_content_type='text/markdown',
|
|
31
|
-
long_description=open('README.md').read(),
|
|
32
|
-
description='Email backend for Django which sends email via the Gmail API',
|
|
33
|
-
classifiers=[
|
|
34
|
-
'Intended Audience :: Developers',
|
|
35
|
-
'License :: OSI Approved :: Apache Software License',
|
|
36
|
-
'Operating System :: MacOS :: MacOS X',
|
|
37
|
-
'Operating System :: Microsoft :: Windows',
|
|
38
|
-
'Operating System :: POSIX',
|
|
39
|
-
'Programming Language :: Python',
|
|
40
|
-
'Programming Language :: Python :: 3',
|
|
41
|
-
'Programming Language :: Python :: 3.5',
|
|
42
|
-
'Programming Language :: Python :: 3.6',
|
|
43
|
-
'Programming Language :: Python :: 3.7',
|
|
44
|
-
'Programming Language :: Python :: 3.8',
|
|
45
|
-
'Framework :: Django',
|
|
46
|
-
'Topic :: Communications :: Email',
|
|
47
|
-
'Development Status :: 4 - Beta'
|
|
48
|
-
],
|
|
49
|
-
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_gmailapi_backend-0.3.3 → django_gmailapi_backend-0.3.4}/gmailapi_backend/bin/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|