gam7 7.3.4__py3-none-any.whl

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.

Potentially problematic release.


This version of gam7 might be problematic. Click here for more details.

Files changed (72) hide show
  1. gam/__init__.py +77555 -0
  2. gam/__main__.py +40 -0
  3. gam/atom/__init__.py +1460 -0
  4. gam/atom/auth.py +41 -0
  5. gam/atom/client.py +214 -0
  6. gam/atom/core.py +535 -0
  7. gam/atom/data.py +327 -0
  8. gam/atom/http.py +354 -0
  9. gam/atom/http_core.py +599 -0
  10. gam/atom/http_interface.py +144 -0
  11. gam/atom/mock_http.py +123 -0
  12. gam/atom/mock_http_core.py +313 -0
  13. gam/atom/mock_service.py +235 -0
  14. gam/atom/service.py +723 -0
  15. gam/atom/token_store.py +105 -0
  16. gam/atom/url.py +130 -0
  17. gam/cacerts.pem +1130 -0
  18. gam/cbcm-v1.1beta1.json +593 -0
  19. gam/contactdelegation-v1.json +249 -0
  20. gam/datastudio-v1.json +486 -0
  21. gam/gamlib/__init__.py +17 -0
  22. gam/gamlib/glaction.py +308 -0
  23. gam/gamlib/glapi.py +837 -0
  24. gam/gamlib/glcfg.py +616 -0
  25. gam/gamlib/glclargs.py +1184 -0
  26. gam/gamlib/glentity.py +831 -0
  27. gam/gamlib/glgapi.py +817 -0
  28. gam/gamlib/glgdata.py +98 -0
  29. gam/gamlib/glglobals.py +307 -0
  30. gam/gamlib/glindent.py +46 -0
  31. gam/gamlib/glmsgs.py +547 -0
  32. gam/gamlib/glskus.py +246 -0
  33. gam/gamlib/gluprop.py +279 -0
  34. gam/gamlib/glverlibs.py +33 -0
  35. gam/gamlib/yubikey.py +202 -0
  36. gam/gdata/__init__.py +825 -0
  37. gam/gdata/alt/__init__.py +20 -0
  38. gam/gdata/alt/app_engine.py +101 -0
  39. gam/gdata/alt/appengine.py +321 -0
  40. gam/gdata/apps/__init__.py +526 -0
  41. gam/gdata/apps/audit/__init__.py +1 -0
  42. gam/gdata/apps/audit/service.py +278 -0
  43. gam/gdata/apps/contacts/__init__.py +874 -0
  44. gam/gdata/apps/contacts/service.py +355 -0
  45. gam/gdata/apps/service.py +544 -0
  46. gam/gdata/apps/sites/__init__.py +283 -0
  47. gam/gdata/apps/sites/service.py +246 -0
  48. gam/gdata/service.py +1714 -0
  49. gam/gdata/urlfetch.py +247 -0
  50. gam/googleapiclient/__init__.py +27 -0
  51. gam/googleapiclient/_auth.py +167 -0
  52. gam/googleapiclient/_helpers.py +207 -0
  53. gam/googleapiclient/channel.py +315 -0
  54. gam/googleapiclient/discovery.py +1662 -0
  55. gam/googleapiclient/discovery_cache/__init__.py +78 -0
  56. gam/googleapiclient/discovery_cache/appengine_memcache.py +55 -0
  57. gam/googleapiclient/discovery_cache/base.py +46 -0
  58. gam/googleapiclient/discovery_cache/file_cache.py +145 -0
  59. gam/googleapiclient/errors.py +197 -0
  60. gam/googleapiclient/http.py +1962 -0
  61. gam/googleapiclient/mimeparse.py +183 -0
  62. gam/googleapiclient/model.py +429 -0
  63. gam/googleapiclient/schema.py +317 -0
  64. gam/googleapiclient/version.py +15 -0
  65. gam/iso8601/__init__.py +28 -0
  66. gam/iso8601/iso8601.py +160 -0
  67. gam/serviceaccountlookup-v1.json +141 -0
  68. gam/six.py +982 -0
  69. gam7-7.3.4.dist-info/METADATA +69 -0
  70. gam7-7.3.4.dist-info/RECORD +72 -0
  71. gam7-7.3.4.dist-info/WHEEL +4 -0
  72. gam7-7.3.4.dist-info/licenses/LICENSE +201 -0
gam/atom/data.py ADDED
@@ -0,0 +1,327 @@
1
+ #!/usr/bin/env python
2
+ #
3
+ # Copyright (C) 2009 Google Inc.
4
+ #
5
+ # Licensed under the Apache License 2.0;
6
+
7
+
8
+
9
+ # This module is used for version 2 of the Google Data APIs.
10
+
11
+
12
+ # __author__ = 'j.s@google.com (Jeff Scudder)'
13
+
14
+ import atom.core
15
+
16
+ XML_TEMPLATE = '{http://www.w3.org/XML/1998/namespace}%s'
17
+ ATOM_TEMPLATE = '{http://www.w3.org/2005/Atom}%s'
18
+ APP_TEMPLATE_V1 = '{http://purl.org/atom/app#}%s'
19
+ APP_TEMPLATE_V2 = '{http://www.w3.org/2007/app}%s'
20
+
21
+
22
+ class Name(atom.core.XmlElement):
23
+ """The atom:name element."""
24
+ _qname = ATOM_TEMPLATE % 'name'
25
+
26
+
27
+ class Email(atom.core.XmlElement):
28
+ """The atom:email element."""
29
+ _qname = ATOM_TEMPLATE % 'email'
30
+
31
+
32
+ class Uri(atom.core.XmlElement):
33
+ """The atom:uri element."""
34
+ _qname = ATOM_TEMPLATE % 'uri'
35
+
36
+
37
+ class Person(atom.core.XmlElement):
38
+ """A foundation class which atom:author and atom:contributor extend.
39
+
40
+ A person contains information like name, email address, and web page URI for
41
+ an author or contributor to an Atom feed.
42
+ """
43
+ name = Name
44
+ email = Email
45
+ uri = Uri
46
+
47
+
48
+ class Author(Person):
49
+ """The atom:author element.
50
+
51
+ An author is a required element in Feed unless each Entry contains an Author.
52
+ """
53
+ _qname = ATOM_TEMPLATE % 'author'
54
+
55
+
56
+ class Contributor(Person):
57
+ """The atom:contributor element."""
58
+ _qname = ATOM_TEMPLATE % 'contributor'
59
+
60
+
61
+ class Link(atom.core.XmlElement):
62
+ """The atom:link element."""
63
+ _qname = ATOM_TEMPLATE % 'link'
64
+ href = 'href'
65
+ rel = 'rel'
66
+ type = 'type'
67
+ hreflang = 'hreflang'
68
+ title = 'title'
69
+ length = 'length'
70
+
71
+
72
+ class Generator(atom.core.XmlElement):
73
+ """The atom:generator element."""
74
+ _qname = ATOM_TEMPLATE % 'generator'
75
+ uri = 'uri'
76
+ version = 'version'
77
+
78
+
79
+ class Text(atom.core.XmlElement):
80
+ """A foundation class from which atom:title, summary, etc. extend.
81
+
82
+ This class should never be instantiated.
83
+ """
84
+ type = 'type'
85
+
86
+
87
+ class Title(Text):
88
+ """The atom:title element."""
89
+ _qname = ATOM_TEMPLATE % 'title'
90
+
91
+
92
+ class Subtitle(Text):
93
+ """The atom:subtitle element."""
94
+ _qname = ATOM_TEMPLATE % 'subtitle'
95
+
96
+
97
+ class Rights(Text):
98
+ """The atom:rights element."""
99
+ _qname = ATOM_TEMPLATE % 'rights'
100
+
101
+
102
+ class Summary(Text):
103
+ """The atom:summary element."""
104
+ _qname = ATOM_TEMPLATE % 'summary'
105
+
106
+
107
+ class Content(Text):
108
+ """The atom:content element."""
109
+ _qname = ATOM_TEMPLATE % 'content'
110
+ src = 'src'
111
+
112
+
113
+ class Category(atom.core.XmlElement):
114
+ """The atom:category element."""
115
+ _qname = ATOM_TEMPLATE % 'category'
116
+ term = 'term'
117
+ scheme = 'scheme'
118
+ label = 'label'
119
+
120
+
121
+ class Id(atom.core.XmlElement):
122
+ """The atom:id element."""
123
+ _qname = ATOM_TEMPLATE % 'id'
124
+
125
+
126
+ class Icon(atom.core.XmlElement):
127
+ """The atom:icon element."""
128
+ _qname = ATOM_TEMPLATE % 'icon'
129
+
130
+
131
+ class Logo(atom.core.XmlElement):
132
+ """The atom:logo element."""
133
+ _qname = ATOM_TEMPLATE % 'logo'
134
+
135
+
136
+ class Draft(atom.core.XmlElement):
137
+ """The app:draft element which indicates if this entry should be public."""
138
+ _qname = (APP_TEMPLATE_V1 % 'draft', APP_TEMPLATE_V2 % 'draft')
139
+
140
+
141
+ class Control(atom.core.XmlElement):
142
+ """The app:control element indicating restrictions on publication.
143
+
144
+ The APP control element may contain a draft element indicating whether or
145
+ not this entry should be publicly available.
146
+ """
147
+ _qname = (APP_TEMPLATE_V1 % 'control', APP_TEMPLATE_V2 % 'control')
148
+ draft = Draft
149
+
150
+
151
+ class Date(atom.core.XmlElement):
152
+ """A parent class for atom:updated, published, etc."""
153
+
154
+
155
+ class Updated(Date):
156
+ """The atom:updated element."""
157
+ _qname = ATOM_TEMPLATE % 'updated'
158
+
159
+
160
+ class Published(Date):
161
+ """The atom:published element."""
162
+ _qname = ATOM_TEMPLATE % 'published'
163
+
164
+
165
+ class LinkFinder(object):
166
+ """An "interface" providing methods to find link elements
167
+
168
+ Entry elements often contain multiple links which differ in the rel
169
+ attribute or content type. Often, developers are interested in a specific
170
+ type of link so this class provides methods to find specific classes of
171
+ links.
172
+
173
+ This class is used as a mixin in Atom entries and feeds.
174
+ """
175
+
176
+ def find_url(self, rel):
177
+ """Returns the URL (as a string) in a link with the desired rel value."""
178
+ for link in self.link:
179
+ if link.rel == rel and link.href:
180
+ return link.href
181
+ return None
182
+
183
+ FindUrl = find_url
184
+
185
+ def get_link(self, rel):
186
+ """Returns a link object which has the desired rel value.
187
+
188
+ If you are interested in the URL instead of the link object,
189
+ consider using find_url instead.
190
+ """
191
+ for link in self.link:
192
+ if link.rel == rel and link.href:
193
+ return link
194
+ return None
195
+
196
+ GetLink = get_link
197
+
198
+ def find_self_link(self):
199
+ """Find the first link with rel set to 'self'
200
+
201
+ Returns:
202
+ A str containing the link's href or None if none of the links had rel
203
+ equal to 'self'
204
+ """
205
+ return self.find_url('self')
206
+
207
+ FindSelfLink = find_self_link
208
+
209
+ def get_self_link(self):
210
+ return self.get_link('self')
211
+
212
+ GetSelfLink = get_self_link
213
+
214
+ def find_edit_link(self):
215
+ return self.find_url('edit')
216
+
217
+ FindEditLink = find_edit_link
218
+
219
+ def get_edit_link(self):
220
+ return self.get_link('edit')
221
+
222
+ GetEditLink = get_edit_link
223
+
224
+ def find_edit_media_link(self):
225
+ link = self.find_url('edit-media')
226
+ # Search for media-edit as well since Picasa API used media-edit instead.
227
+ if link is None:
228
+ return self.find_url('media-edit')
229
+ return link
230
+
231
+ FindEditMediaLink = find_edit_media_link
232
+
233
+ def get_edit_media_link(self):
234
+ link = self.get_link('edit-media')
235
+ if link is None:
236
+ return self.get_link('media-edit')
237
+ return link
238
+
239
+ GetEditMediaLink = get_edit_media_link
240
+
241
+ def find_next_link(self):
242
+ return self.find_url('next')
243
+
244
+ FindNextLink = find_next_link
245
+
246
+ def get_next_link(self):
247
+ return self.get_link('next')
248
+
249
+ GetNextLink = get_next_link
250
+
251
+ def find_license_link(self):
252
+ return self.find_url('license')
253
+
254
+ FindLicenseLink = find_license_link
255
+
256
+ def get_license_link(self):
257
+ return self.get_link('license')
258
+
259
+ GetLicenseLink = get_license_link
260
+
261
+ def find_alternate_link(self):
262
+ return self.find_url('alternate')
263
+
264
+ FindAlternateLink = find_alternate_link
265
+
266
+ def get_alternate_link(self):
267
+ return self.get_link('alternate')
268
+
269
+ GetAlternateLink = get_alternate_link
270
+
271
+
272
+ class FeedEntryParent(atom.core.XmlElement, LinkFinder):
273
+ """A super class for atom:feed and entry, contains shared attributes"""
274
+ author = [Author]
275
+ category = [Category]
276
+ contributor = [Contributor]
277
+ id = Id
278
+ link = [Link]
279
+ rights = Rights
280
+ title = Title
281
+ updated = Updated
282
+
283
+ def __init__(self, atom_id=None, text=None, *args, **kwargs):
284
+ if atom_id is not None:
285
+ self.id = atom_id
286
+ atom.core.XmlElement.__init__(self, text=text, *args, **kwargs)
287
+
288
+
289
+ class Source(FeedEntryParent):
290
+ """The atom:source element."""
291
+ _qname = ATOM_TEMPLATE % 'source'
292
+ generator = Generator
293
+ icon = Icon
294
+ logo = Logo
295
+ subtitle = Subtitle
296
+
297
+
298
+ class Entry(FeedEntryParent):
299
+ """The atom:entry element."""
300
+ _qname = ATOM_TEMPLATE % 'entry'
301
+ content = Content
302
+ published = Published
303
+ source = Source
304
+ summary = Summary
305
+ control = Control
306
+
307
+
308
+ class Feed(Source):
309
+ """The atom:feed element which contains entries."""
310
+ _qname = ATOM_TEMPLATE % 'feed'
311
+ entry = [Entry]
312
+
313
+
314
+ class ExtensionElement(atom.core.XmlElement):
315
+ """Provided for backwards compatibility to the v1 atom.ExtensionElement."""
316
+
317
+ def __init__(self, tag=None, namespace=None, attributes=None,
318
+ children=None, text=None, *args, **kwargs):
319
+ if namespace:
320
+ self._qname = '{%s}%s' % (namespace, tag)
321
+ else:
322
+ self._qname = tag
323
+ self.children = children or []
324
+ self.attributes = attributes or {}
325
+ self.text = text
326
+
327
+ _BecomeChildElement = atom.core.XmlElement._become_child
gam/atom/http.py ADDED
@@ -0,0 +1,354 @@
1
+ #
2
+ # Copyright (C) 2008 Google Inc.
3
+ #
4
+ # Licensed under the Apache License 2.0;
5
+
6
+
7
+
8
+ """HttpClients in this module use httplib to make HTTP requests.
9
+
10
+ This module make HTTP requests based on httplib, but there are environments
11
+ in which an httplib based approach will not work (if running in Google App
12
+ Engine for example). In those cases, higher level classes (like AtomService
13
+ and GDataService) can swap out the HttpClient to transparently use a
14
+ different mechanism for making HTTP requests.
15
+
16
+ HttpClient: Contains a request method which performs an HTTP call to the
17
+ server.
18
+
19
+ ProxiedHttpClient: Contains a request method which connects to a proxy using
20
+ settings stored in operating system environment variables then
21
+ performs an HTTP call to the endpoint server.
22
+ """
23
+
24
+ # __author__ = 'api.jscudder (Jeff Scudder)'
25
+
26
+ import base64
27
+ import http.client
28
+ import os
29
+ import socket
30
+
31
+ import atom.http_core
32
+ import atom.http_interface
33
+ import atom.url
34
+
35
+ ssl_imported = False
36
+ ssl = None
37
+ try:
38
+ import ssl
39
+
40
+ ssl_imported = True
41
+ except ImportError:
42
+ pass
43
+
44
+
45
+ class ProxyError(atom.http_interface.Error):
46
+ pass
47
+
48
+
49
+ class TestConfigurationError(Exception):
50
+ pass
51
+
52
+
53
+ DEFAULT_CONTENT_TYPE = 'application/atom+xml'
54
+
55
+
56
+ class HttpClient(atom.http_interface.GenericHttpClient):
57
+ # Added to allow old v1 HttpClient objects to use the new
58
+ # http_code.HttpClient. Used in unit tests to inject a mock client.
59
+ v2_http_client = None
60
+
61
+ def __init__(self, headers=None):
62
+ self.debug = False
63
+ self.headers = headers or {}
64
+
65
+ def request(self, operation, url, data=None, headers=None):
66
+ """Performs an HTTP call to the server, supports GET, POST, PUT, and
67
+ DELETE.
68
+
69
+ Usage example, perform and HTTP GET on http://www.google.com/:
70
+ import atom.http
71
+ client = atom.http.HttpClient()
72
+ http_response = client.request('GET', 'http://www.google.com/')
73
+
74
+ Args:
75
+ operation: str The HTTP operation to be performed. This is usually one
76
+ of 'GET', 'POST', 'PUT', or 'DELETE'
77
+ data: filestream, list of parts, or other object which can be converted
78
+ to a string. Should be set to None when performing a GET or DELETE.
79
+ If data is a file-like object which can be read, this method will
80
+ read a chunk of 100K bytes at a time and send them.
81
+ If the data is a list of parts to be sent, each part will be
82
+ evaluated and sent.
83
+ url: The full URL to which the request should be sent. Can be a string
84
+ or atom.url.Url.
85
+ headers: dict of strings. HTTP headers which should be sent
86
+ in the request.
87
+ """
88
+ all_headers = self.headers.copy()
89
+ if headers:
90
+ all_headers.update(headers)
91
+
92
+ # If the list of headers does not include a Content-Length, attempt to
93
+ # calculate it based on the data object.
94
+ if data and 'Content-Length' not in all_headers:
95
+ if isinstance(data, (str,)):
96
+ all_headers['Content-Length'] = str(len(data))
97
+ else:
98
+ raise atom.http_interface.ContentLengthRequired('Unable to calculate '
99
+ 'the length of the data parameter. Specify a value for '
100
+ 'Content-Length')
101
+
102
+ # Set the content type to the default value if none was set.
103
+ if 'Content-Type' not in all_headers:
104
+ all_headers['Content-Type'] = DEFAULT_CONTENT_TYPE
105
+
106
+ if self.v2_http_client is not None:
107
+ http_request = atom.http_core.HttpRequest(method=operation)
108
+ atom.http_core.Uri.parse_uri(str(url)).modify_request(http_request)
109
+ http_request.headers = all_headers
110
+ if data:
111
+ http_request._body_parts.append(data)
112
+ return self.v2_http_client.request(http_request=http_request)
113
+
114
+ if not isinstance(url, atom.url.Url):
115
+ if isinstance(url, str):
116
+ url = atom.url.parse_url(url)
117
+ else:
118
+ raise atom.http_interface.UnparsableUrlObject('Unable to parse url parameter because it was not a string or atom.url.Url')
119
+
120
+ connection = self._prepare_connection(url, all_headers)
121
+
122
+ if self.debug:
123
+ connection.debuglevel = 1
124
+
125
+ connection.putrequest(operation, self._get_access_url(url), skip_host=True)
126
+
127
+ if url.port is not None:
128
+ connection.putheader('Host', '%s:%s' % (url.host, url.port))
129
+ else:
130
+ connection.putheader('Host', url.host)
131
+
132
+ # Overcome a bug in Python 2.4 and 2.5
133
+ # httplib.HTTPConnection.putrequest adding
134
+ # HTTP request header 'Host: www.google.com:443' instead of
135
+ # 'Host: www.google.com', and thus resulting the error message
136
+ # 'Token invalid - AuthSub token has wrong scope' in the HTTP response.
137
+ if (url.protocol == 'https' and int(url.port or 443) == 443 and
138
+ hasattr(connection, '_buffer') and
139
+ isinstance(connection._buffer, list)):
140
+ header_line = 'Host: %s:443' % url.host
141
+ replacement_header_line = 'Host: %s' % url.host
142
+ try:
143
+ connection._buffer[connection._buffer.index(header_line)] = (
144
+ replacement_header_line)
145
+ except ValueError: # header_line missing from connection._buffer
146
+ pass
147
+
148
+ # Send the HTTP headers.
149
+ for header_name in all_headers:
150
+ connection.putheader(header_name, all_headers[header_name])
151
+ connection.endheaders()
152
+
153
+ # If there is data, send it in the request.
154
+ if data:
155
+ if isinstance(data, list):
156
+ for data_part in data:
157
+ _send_data_part(data_part, connection)
158
+ else:
159
+ _send_data_part(data, connection)
160
+
161
+ # Return the HTTP Response from the server.
162
+ return connection.getresponse()
163
+
164
+ def _prepare_connection(self, url, headers):
165
+ if not isinstance(url, atom.url.Url):
166
+ if isinstance(url, (str,)):
167
+ url = atom.url.parse_url(url)
168
+ else:
169
+ raise atom.http_interface.UnparsableUrlObject('Unable to parse url '
170
+ 'parameter because it was not a string or atom.url.Url')
171
+ if url.protocol == 'https':
172
+ if not url.port:
173
+ return http.client.HTTPSConnection(url.host)
174
+ return http.client.HTTPSConnection(url.host, int(url.port))
175
+ else:
176
+ if not url.port:
177
+ return http.client.HTTPConnection(url.host)
178
+ return http.client.HTTPConnection(url.host, int(url.port))
179
+
180
+ def _get_access_url(self, url):
181
+ return url.to_string()
182
+
183
+
184
+ class ProxiedHttpClient(HttpClient):
185
+ """Performs an HTTP request through a proxy.
186
+
187
+ The proxy settings are obtained from enviroment variables. The URL of the
188
+ proxy server is assumed to be stored in the environment variables
189
+ 'https_proxy' and 'http_proxy' respectively. If the proxy server requires
190
+ a Basic Auth authorization header, the username and password are expected to
191
+ be in the 'proxy-username' or 'proxy_username' variable and the
192
+ 'proxy-password' or 'proxy_password' variable, or in 'http_proxy' or
193
+ 'https_proxy' as "protocol://[username:password@]host:port".
194
+
195
+ After connecting to the proxy server, the request is completed as in
196
+ HttpClient.request.
197
+ """
198
+
199
+ def _prepare_connection(self, url, headers):
200
+ proxy_settings = os.environ.get('%s_proxy' % url.protocol)
201
+ if not proxy_settings:
202
+ # The request was HTTP or HTTPS, but there was no appropriate proxy set.
203
+ return HttpClient._prepare_connection(self, url, headers)
204
+ else:
205
+ proxy_auth = _get_proxy_auth(proxy_settings)
206
+ proxy_netloc = _get_proxy_net_location(proxy_settings)
207
+ if url.protocol == 'https':
208
+ # Set any proxy auth headers
209
+ if proxy_auth:
210
+ proxy_auth = 'Proxy-Authorization: %s' % proxy_auth
211
+
212
+ # Construct the proxy connect command.
213
+ port = url.port
214
+ if not port:
215
+ port = '443'
216
+ proxy_connect = 'CONNECT %s:%s HTTP/1.0\r\n' % (url.host, port)
217
+
218
+ # Set the user agent to send to the proxy
219
+ if headers and 'User-Agent' in headers:
220
+ user_agent = 'User-Agent: %s\r\n' % (headers['User-Agent'])
221
+ else:
222
+ user_agent = 'User-Agent: python\r\n'
223
+
224
+ proxy_pieces = '%s%s%s\r\n' % (proxy_connect, proxy_auth, user_agent)
225
+
226
+ # Find the proxy host and port.
227
+ proxy_url = atom.url.parse_url(proxy_netloc)
228
+ if not proxy_url.port:
229
+ proxy_url.port = '80'
230
+
231
+ # Connect to the proxy server, very simple recv and error checking
232
+ p_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
233
+ p_sock.connect((proxy_url.host, int(proxy_url.port)))
234
+ #p_sock.sendall(proxy_pieces)
235
+ p_sock.sendall(proxy_pieces.encode('utf-8'))
236
+ response = ''
237
+
238
+ # Wait for the full response.
239
+ while response.find("\r\n\r\n") == -1:
240
+ #response += p_sock.recv(8192)
241
+ response += p_sock.recv(8192).decode('utf-8')
242
+
243
+ p_status = response.split()[1]
244
+ if p_status != str(200):
245
+ raise ProxyError('Error status=%s' % str(p_status))
246
+
247
+ # Trivial setup for ssl socket.
248
+ sslobj = None
249
+ if ssl_imported:
250
+ context = ssl.SSLContext(ssl.PROTOCOL_TLS)
251
+ context.minimum_version = ssl.TLSVersion.TLSv1_2
252
+ sslobj = context.wrap_socket(p_sock, server_hostname=url.host)
253
+ else:
254
+ sock_ssl = socket.ssl(p_sock, None, None)
255
+ sslobj = http.client.FakeSocket(p_sock, sock_ssl)
256
+
257
+ # Initalize httplib and replace with the proxy socket.
258
+ connection = http.client.HTTPConnection(proxy_url.host)
259
+ connection.sock = sslobj
260
+ return connection
261
+ else:
262
+ # If protocol was not https.
263
+ # Find the proxy host and port.
264
+ proxy_url = atom.url.parse_url(proxy_netloc)
265
+ if not proxy_url.port:
266
+ proxy_url.port = '80'
267
+
268
+ if proxy_auth:
269
+ headers['Proxy-Authorization'] = proxy_auth.strip()
270
+
271
+ return http.client.HTTPConnection(proxy_url.host, int(proxy_url.port))
272
+
273
+ def _get_access_url(self, url):
274
+ return url.to_string()
275
+
276
+
277
+ def _get_proxy_auth(proxy_settings):
278
+ """Returns proxy authentication string for header.
279
+
280
+ Will check environment variables for proxy authentication info, starting with
281
+ proxy(_/-)username and proxy(_/-)password before checking the given
282
+ proxy_settings for a [protocol://]username:password@host[:port] string.
283
+
284
+ Args:
285
+ proxy_settings: String from http_proxy or https_proxy environment variable.
286
+
287
+ Returns:
288
+ Authentication string for proxy, or empty string if no proxy username was
289
+ found.
290
+ """
291
+ proxy_username = None
292
+ proxy_password = None
293
+
294
+ proxy_username = os.environ.get('proxy-username')
295
+ if not proxy_username:
296
+ proxy_username = os.environ.get('proxy_username')
297
+ proxy_password = os.environ.get('proxy-password')
298
+ if not proxy_password:
299
+ proxy_password = os.environ.get('proxy_password')
300
+
301
+ if not proxy_username:
302
+ if '@' in proxy_settings:
303
+ protocol_and_proxy_auth = proxy_settings.split('@')[0].split(':')
304
+ if len(protocol_and_proxy_auth) == 3:
305
+ # 3 elements means we have [<protocol>, //<user>, <password>]
306
+ proxy_username = protocol_and_proxy_auth[1].lstrip('/')
307
+ proxy_password = protocol_and_proxy_auth[2]
308
+ elif len(protocol_and_proxy_auth) == 2:
309
+ # 2 elements means we have [<user>, <password>]
310
+ proxy_username = protocol_and_proxy_auth[0]
311
+ proxy_password = protocol_and_proxy_auth[1]
312
+ if proxy_username:
313
+ user_auth = base64.b64encode(('%s:%s' % (proxy_username, proxy_password)).encode('utf-8'))
314
+ return 'Basic %s\r\n' % (user_auth.strip().decode('utf-8'))
315
+ else:
316
+ return ''
317
+
318
+
319
+ def _get_proxy_net_location(proxy_settings):
320
+ """Returns proxy host and port.
321
+
322
+ Args:
323
+ proxy_settings: String from http_proxy or https_proxy environment variable.
324
+ Must be in the form of protocol://[username:password@]host:port
325
+
326
+ Returns:
327
+ String in the form of protocol://host:port
328
+ """
329
+ if '@' in proxy_settings:
330
+ protocol = proxy_settings.split(':')[0]
331
+ netloc = proxy_settings.split('@')[1]
332
+ return '%s://%s' % (protocol, netloc)
333
+ else:
334
+ return proxy_settings
335
+
336
+
337
+ def _send_data_part(data, connection):
338
+ if isinstance(data, (str,)):
339
+ connection.send(data)
340
+ return
341
+ # Check to see if data is a file-like object that has a read method.
342
+ elif hasattr(data, 'read'):
343
+ # Read the file and send it a chunk at a time.
344
+ while 1:
345
+ binarydata = data.read(100000)
346
+ if binarydata == b'': break
347
+ connection.send(binarydata)
348
+ return
349
+ else:
350
+ # The data object was not a file.
351
+ # Try to convert to a string and send the data.
352
+ #connection.send(str(data))
353
+ connection.send(str(data).encode('utf-8'))
354
+ return