google_apps 0.3.0
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.
- data/lib/google_apps.rb +8 -0
- data/lib/google_apps/atom/atom.rb +10 -0
- data/lib/google_apps/atom/export.rb +52 -0
- data/lib/google_apps/atom/group.rb +48 -0
- data/lib/google_apps/atom/message.rb +21 -0
- data/lib/google_apps/atom/message_attributes.rb +56 -0
- data/lib/google_apps/atom/public_key.rb +31 -0
- data/lib/google_apps/atom/user.rb +70 -0
- data/lib/google_apps/transport.rb +235 -0
- metadata +62 -0
data/lib/google_apps.rb
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
require 'google_apps/transport'
|
2
|
+
require 'google_apps/atom/atom'
|
3
|
+
require 'google_apps/atom/user'
|
4
|
+
require 'google_apps/atom/group'
|
5
|
+
require 'google_apps/atom/public_key'
|
6
|
+
require 'google_apps/atom/export'
|
7
|
+
require 'google_apps/atom/message'
|
8
|
+
require 'google_apps/atom/message_attributes'
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module GoogleApps
|
2
|
+
module Atom
|
3
|
+
class Export
|
4
|
+
def initialize
|
5
|
+
@document = Atom::XML::Document.new
|
6
|
+
set_header
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_s
|
10
|
+
@document.to_s
|
11
|
+
end
|
12
|
+
|
13
|
+
def start_date(date)
|
14
|
+
add_prop('beginDate', date)
|
15
|
+
end
|
16
|
+
|
17
|
+
def end_date(date)
|
18
|
+
add_prop('endDate', date)
|
19
|
+
end
|
20
|
+
|
21
|
+
def include_deleted
|
22
|
+
add_prop('includeDeleted', 'true')
|
23
|
+
end
|
24
|
+
|
25
|
+
def query(query_string)
|
26
|
+
add_prop('searchQuery', query_string)
|
27
|
+
end
|
28
|
+
|
29
|
+
def content(type)
|
30
|
+
add_prop('packageContent', type)
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def set_header
|
37
|
+
@document.root = Atom::XML::Node.new 'atom:entry'
|
38
|
+
|
39
|
+
Atom::XML::Namespace.new(@document.root, 'atom', 'http://www.w3.org/2005/Atom')
|
40
|
+
Atom::XML::Namespace.new(@document.root, 'apps', 'http://schemas.google.com/apps/2006')
|
41
|
+
end
|
42
|
+
|
43
|
+
def add_prop(name, value)
|
44
|
+
prop = Atom::XML::Node.new('apps:property')
|
45
|
+
prop['name'] = name
|
46
|
+
prop['value'] = value
|
47
|
+
|
48
|
+
@document.root << prop
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module GoogleApps
|
2
|
+
module Atom
|
3
|
+
class Group
|
4
|
+
def initialize
|
5
|
+
@document = Atom::XML::Document.new
|
6
|
+
add_header
|
7
|
+
end
|
8
|
+
|
9
|
+
def new_group(group_data)
|
10
|
+
group_data.keys.each do |key|
|
11
|
+
prop = Atom::XML::Node.new('apps:property')
|
12
|
+
prop_name(prop, key)
|
13
|
+
prop.attributes['value'] = group_data[key]
|
14
|
+
@document.root << prop
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
@document.to_s
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def add_header
|
25
|
+
@document.root = Atom::XML::Node.new('atom:entry')
|
26
|
+
|
27
|
+
Atom::XML::Namespace.new(@document.root, 'atom', 'http://www.w3.org/2005/Atom')
|
28
|
+
Atom::XML::Namespace.new(@document.root, 'apps', 'http://schemas.google.com/apps/2006')
|
29
|
+
Atom::XML::Namespace.new(@document.root, 'gd', 'http://schemas.google.com/g/2005')
|
30
|
+
end
|
31
|
+
|
32
|
+
def prop_name(property, key)
|
33
|
+
case key
|
34
|
+
when :id
|
35
|
+
property.attributes['name'] = 'groupId'
|
36
|
+
when :name
|
37
|
+
property.attributes['name'] = 'groupName'
|
38
|
+
when :description
|
39
|
+
property.attributes['name'] = 'description'
|
40
|
+
when :perms
|
41
|
+
property.attributes['name'] = 'emailPermission'
|
42
|
+
end
|
43
|
+
|
44
|
+
property
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module GoogleApps
|
2
|
+
module Atom
|
3
|
+
class Message
|
4
|
+
def initialize
|
5
|
+
@document = Atom::XML::Document.new
|
6
|
+
end
|
7
|
+
|
8
|
+
def from(filename)
|
9
|
+
message = File.read(filename)
|
10
|
+
@document.root = Atom::XML::Node.new('apps:rfc822Msg', message)
|
11
|
+
Atom::XML::Namespace.new(@document.root, 'apps', 'http://schemas.google.com/apps/2006')
|
12
|
+
|
13
|
+
@document
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_s
|
17
|
+
@document.to_s
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module GoogleApps
|
2
|
+
module Atom
|
3
|
+
class MessageAttributes
|
4
|
+
def initialize
|
5
|
+
@document = Atom::XML::Document.new
|
6
|
+
set_header
|
7
|
+
end
|
8
|
+
|
9
|
+
def add_property(prop)
|
10
|
+
property = Atom::XML::Node.new 'apps:mailItemProperty'
|
11
|
+
property['value'] = prop
|
12
|
+
|
13
|
+
@document.root << property
|
14
|
+
end
|
15
|
+
|
16
|
+
def add_label(name)
|
17
|
+
label = Atom::XML::Node.new 'apps:label'
|
18
|
+
label['labelName'] = name
|
19
|
+
|
20
|
+
@document.root << label
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_s
|
24
|
+
@document.to_s
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def set_header
|
30
|
+
@document.root = Atom::XML::Node.new 'atom:entry' # API Docs show just entry here
|
31
|
+
|
32
|
+
Atom::XML::Namespace.new(@document.root, 'atom', 'http://www.w3.org/2005/Atom') # API Docs show this as just xmlns
|
33
|
+
Atom::XML::Namespace.new(@document.root, 'apps', 'http://schemas.google.com/apps/2006')
|
34
|
+
|
35
|
+
@document.root << category
|
36
|
+
@document.root << content
|
37
|
+
end
|
38
|
+
|
39
|
+
def category
|
40
|
+
header = Atom::XML::Node.new 'category'
|
41
|
+
header['scheme'] = 'http://schemas.google.com/g/2005#kind'
|
42
|
+
header['term'] = 'http://schemas.google.com/apps/2006#mailItem'
|
43
|
+
|
44
|
+
header
|
45
|
+
end
|
46
|
+
|
47
|
+
def content
|
48
|
+
header = Atom::XML::Node.new 'atom:content'
|
49
|
+
Atom::XML::Namespace.new(header, 'atom', 'http://www.w3.org/2005/Atom')
|
50
|
+
header['type'] = 'message/rfc822'
|
51
|
+
|
52
|
+
header
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module GoogleApps
|
2
|
+
module Atom
|
3
|
+
class PublicKey
|
4
|
+
def initialize
|
5
|
+
@document = Atom::XML::Document.new
|
6
|
+
add_header
|
7
|
+
end
|
8
|
+
|
9
|
+
def new_key(key)
|
10
|
+
property = Atom::XML::Node.new('apps:property')
|
11
|
+
property['name'] = 'publicKey'
|
12
|
+
property['value'] = Base64.encode64 key
|
13
|
+
|
14
|
+
@document.root << property
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_s
|
18
|
+
@document.to_s
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def add_header
|
24
|
+
@document.root = Atom::XML::Node.new('atom:entry')
|
25
|
+
|
26
|
+
Atom::XML::Namespace.new(@document.root, 'atom', 'http://www.w3.org/2005/Atom')
|
27
|
+
Atom::XML::Namespace.new(@document.root, 'apps', 'http://schemas.google.com/apps/2006')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module GoogleApps
|
2
|
+
module Atom
|
3
|
+
class User
|
4
|
+
attr_reader :document
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@document = Atom::XML::Document.new
|
8
|
+
add_header
|
9
|
+
end
|
10
|
+
|
11
|
+
def new_user(user_name, first, last, password, quota)
|
12
|
+
new_doc
|
13
|
+
add_header
|
14
|
+
@document.root << login_node(user_name, password)
|
15
|
+
@document.root << quota_node(quota)
|
16
|
+
@document.root << name_node(first, last)
|
17
|
+
|
18
|
+
@document
|
19
|
+
end
|
20
|
+
|
21
|
+
def new_doc
|
22
|
+
@document = Atom::XML::Document.new
|
23
|
+
end
|
24
|
+
|
25
|
+
def login_node(user_name, password)
|
26
|
+
login = Atom::XML::Node.new('apps:login')
|
27
|
+
login['userName'] = user_name
|
28
|
+
login['password'] = Digest::SHA1.hexdigest password
|
29
|
+
login['hashFunctionName'] = Atom::HASH_FUNCTION
|
30
|
+
login['suspended'] = "false"
|
31
|
+
|
32
|
+
login
|
33
|
+
end
|
34
|
+
|
35
|
+
def quota_node(limit)
|
36
|
+
quota = Atom::XML::Node.new('apps:quota')
|
37
|
+
quota['limit'] = limit.to_s
|
38
|
+
|
39
|
+
quota
|
40
|
+
end
|
41
|
+
|
42
|
+
def name_node(first, last)
|
43
|
+
name = Atom::XML::Node.new('apps:name')
|
44
|
+
name['familyName'] = last
|
45
|
+
name['givenName'] = first
|
46
|
+
|
47
|
+
name
|
48
|
+
end
|
49
|
+
|
50
|
+
def to_s
|
51
|
+
@document.to_s
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def add_header
|
57
|
+
@document.root = Atom::XML::Node.new('atom:entry')
|
58
|
+
|
59
|
+
Atom::XML::Namespace.new(@document.root, 'atom', 'http://www.w3.org/2005/Atom')
|
60
|
+
Atom::XML::Namespace.new(@document.root, 'apps', 'http://schemas.google.com/apps/2006')
|
61
|
+
|
62
|
+
category = Atom::XML::Node.new('atom:category')
|
63
|
+
category.attributes['scheme'] = 'http://schemas.google.com/g/2005#kind'
|
64
|
+
category.attributes['term'] = 'http://schemas.google.com/apps/2006#user'
|
65
|
+
|
66
|
+
@document.root << category
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,235 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'cgi'
|
3
|
+
require 'openssl'
|
4
|
+
require 'rexml/document'
|
5
|
+
|
6
|
+
module GoogleApps
|
7
|
+
class Transport
|
8
|
+
attr_reader :request, :response
|
9
|
+
attr_accessor :auth, :user, :group, :nickname
|
10
|
+
|
11
|
+
BOUNDARY = "=AaB03xDFHT8xgg"
|
12
|
+
|
13
|
+
def initialize(domain, targets = {})
|
14
|
+
@auth = targets[:auth] || "https://www.google.com/accounts/ClientLogin"
|
15
|
+
@user = targets[:user] || "https://apps-apis.google.com/a/feeds/#{domain}/user/2.0"
|
16
|
+
@pubkey = targets[:pubkey] || "https://apps-apis.google.com/a/feeds/compliance/audit/publickey/#{domain}"
|
17
|
+
@migration = targets[:migration] || "https://apps-apis.google.com/a/feeds/migration/2.0/#{domain}"
|
18
|
+
@group = targets[:group]
|
19
|
+
@nickname = targets[:nickname]
|
20
|
+
@export = targets[:export] || "https://apps-apis.google.com/a/feeds/compliance/audit/mail/export/#{domain}"
|
21
|
+
@token = nil
|
22
|
+
@response = nil
|
23
|
+
@request = nil
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
# authenticate will take the provided account and
|
28
|
+
# password and attempt to authenticate them with
|
29
|
+
# Google
|
30
|
+
#
|
31
|
+
# authenticate 'username@domain', 'password'
|
32
|
+
#
|
33
|
+
# authenticate returns the HTTP response received
|
34
|
+
# from Google
|
35
|
+
def authenticate(account, pass)
|
36
|
+
uri = URI(@auth)
|
37
|
+
@request = Net::HTTP::Post.new(uri.path)
|
38
|
+
@request.body = auth_body(account, pass)
|
39
|
+
set_headers :auth
|
40
|
+
|
41
|
+
@response = request(uri)
|
42
|
+
@response.body.split("\n").grep(/auth=(.*)/i)
|
43
|
+
|
44
|
+
@token = $1
|
45
|
+
|
46
|
+
@response
|
47
|
+
end
|
48
|
+
|
49
|
+
# request_export performs the GoogleApps API call to
|
50
|
+
# generate a mailbox export. It takes the username
|
51
|
+
# and an GoogleApps::Atom::Export instance as
|
52
|
+
# arguments
|
53
|
+
#
|
54
|
+
# request_export 'username', document
|
55
|
+
#
|
56
|
+
# request_export returns the HTTP response received
|
57
|
+
# from Google.
|
58
|
+
def request_export(username, document)
|
59
|
+
uri = URI(@export + "/#{username}")
|
60
|
+
@request = Net::HTTP::Post.new uri.path
|
61
|
+
@request.body = document
|
62
|
+
set_headers :user
|
63
|
+
|
64
|
+
@response = request(uri)
|
65
|
+
end
|
66
|
+
|
67
|
+
# export_status checks the status of a mailbox export
|
68
|
+
# request. It takes the username and the request_id
|
69
|
+
# as arguments
|
70
|
+
#
|
71
|
+
# export_status 'username', 847576
|
72
|
+
#
|
73
|
+
# export_status will return the body of the HTTP
|
74
|
+
# response from Google
|
75
|
+
def export_status(username, req_id)
|
76
|
+
uri = URI(@export + "/#{username}/#{req_id}")
|
77
|
+
@request = Net::HTTP::Get.new uri.path
|
78
|
+
set_headers :user
|
79
|
+
|
80
|
+
# TODO: Return actual status not whole body.
|
81
|
+
(@response = request(uri)).body
|
82
|
+
end
|
83
|
+
|
84
|
+
def fetch_export(flename) # :nodoc:
|
85
|
+
# TODO: Shouldn't rely on export_status being run first. Self, this is lazy and stupid.
|
86
|
+
doc = REXML::Document.new(@response.body)
|
87
|
+
urls = []
|
88
|
+
doc.elements.each('entry/apps:property') do |property|
|
89
|
+
urls << property.attributes['value'] if property.attributes['name'].match 'fileUrl'
|
90
|
+
end
|
91
|
+
|
92
|
+
urls.each do |url|
|
93
|
+
download(url, filename)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# download makes a get request of the provided url
|
98
|
+
# and writes the body to the provided filename.
|
99
|
+
#
|
100
|
+
# download 'url', 'save_file'
|
101
|
+
def download(url, filename)
|
102
|
+
uri = URI(url)
|
103
|
+
@request = Net::HTTP::Get.new uri.path
|
104
|
+
set_headers :user
|
105
|
+
|
106
|
+
File.new(filename, "w") do |file|
|
107
|
+
file.puts request(uri).body
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# add is a generic target for method_missing. It is
|
112
|
+
# intended to handle the general case of adding
|
113
|
+
# to the GoogleApps Domain. It takes an API endpoint
|
114
|
+
# and a GoogleApps::Atom document as arguments.
|
115
|
+
#
|
116
|
+
# add 'endpoint', document
|
117
|
+
#
|
118
|
+
# add returns the HTTP response received from Google.
|
119
|
+
def add(endpoint, document)
|
120
|
+
uri = URI(instance_variable_get("@#{endpoint.to_s}"))
|
121
|
+
@request = Net::HTTP::Post.new(uri.path)
|
122
|
+
@request.body = document
|
123
|
+
set_headers :user
|
124
|
+
|
125
|
+
@response = request(uri)
|
126
|
+
end
|
127
|
+
|
128
|
+
# update is a generic target for method_missing. It is
|
129
|
+
# intended to handle the general case of updating an
|
130
|
+
# item that already exists in your GoogleApps Domain.
|
131
|
+
# It takes an API endpoint and a GoogleApps::Atom document
|
132
|
+
# as arguments.
|
133
|
+
#
|
134
|
+
# update 'endpoint', document
|
135
|
+
#
|
136
|
+
# update returns the HTTP response received from Google
|
137
|
+
def update(endpoint, document)
|
138
|
+
# TODO: Username needs to come from somewhere for uri
|
139
|
+
uri = URI(instance_variable_get("@#{endpoint.to_s}") + "/")
|
140
|
+
end
|
141
|
+
|
142
|
+
# delete is a generic target for method_missing. It is
|
143
|
+
# intended to handle the general case of deleting an
|
144
|
+
# item from your GoogleApps Domain. delete takes an
|
145
|
+
# API endpoint and an item identifier as argumets.
|
146
|
+
#
|
147
|
+
# delete 'endpoint', 'id'
|
148
|
+
#
|
149
|
+
# delete returns the HTTP response received from Google.
|
150
|
+
def delete(endpoint, id)
|
151
|
+
uri = URI(instance_variable_get("@#{endpoint.to_s}") + "/#{id}")
|
152
|
+
@request = Net::HTTP::Delete.new(uri.path)
|
153
|
+
set_headers :user
|
154
|
+
|
155
|
+
@response = request(uri)
|
156
|
+
end
|
157
|
+
|
158
|
+
# migration performs mail migration from a local
|
159
|
+
# mail environment to GoogleApps. migrate takes a
|
160
|
+
# username a GoogleApps::Atom::Properties dcoument
|
161
|
+
# and the message as plain text (String) as arguments.
|
162
|
+
#
|
163
|
+
# migrate 'user', properties, message
|
164
|
+
#
|
165
|
+
# migrate returns the HTTP response received from Google.
|
166
|
+
def migrate(username, properties, message)
|
167
|
+
uri = URI(@migration + "/#{username}/mail")
|
168
|
+
@request = Net::HTTP::Post.new(uri.path)
|
169
|
+
@request.body = multi_part(properties, message)
|
170
|
+
set_headers :migrate
|
171
|
+
|
172
|
+
@response = request(uri)
|
173
|
+
end
|
174
|
+
|
175
|
+
def method_missing(name, *args)
|
176
|
+
super unless name.match /([a-z]*)_([a-z]*)/
|
177
|
+
|
178
|
+
case $1
|
179
|
+
when "new"
|
180
|
+
self.send(:add, $2, *args)
|
181
|
+
when "delete"
|
182
|
+
self.send(:delete, $2, *args)
|
183
|
+
else
|
184
|
+
super
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
|
189
|
+
private
|
190
|
+
|
191
|
+
# auth_body generates the body for the authentication
|
192
|
+
# request made by authenticate.
|
193
|
+
#
|
194
|
+
# auth_body 'username@domain', 'password'
|
195
|
+
#
|
196
|
+
# auth_body returns a string in the form of HTTP
|
197
|
+
# query parameters.
|
198
|
+
def auth_body(account, pass)
|
199
|
+
"&Email=#{CGI::escape(account)}&Passwd=#{CGI::escape(pass)}&accountType=HOSTED&service=apps"
|
200
|
+
end
|
201
|
+
|
202
|
+
def request(uri)
|
203
|
+
# TODO: Clashes with @request reader
|
204
|
+
Net::HTTP.start(uri.hostname, uri.port, :use_ssl => uri.scheme == 'https') do |http|
|
205
|
+
http.request(@request)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
def set_headers(request_type)
|
210
|
+
case request_type
|
211
|
+
when :auth
|
212
|
+
@request['content-type'] = "application/x-www-form-urlencoded"
|
213
|
+
when :migrate
|
214
|
+
@request['content-type'] = "multipart/related; boundary=\"#{BOUNDARY}\""
|
215
|
+
@request['authorization'] = "GoogleLogin auth=#{@token}"
|
216
|
+
else
|
217
|
+
@request['content-type'] = "application/atom+xml"
|
218
|
+
@request['authorization'] = "GoogleLogin auth=#{@token}"
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
def multi_part(properties, message)
|
223
|
+
post_body = []
|
224
|
+
post_body << "--#{BOUNDARY}\n"
|
225
|
+
post_body << "Content-Type: application/atom+xml\n\n"
|
226
|
+
post_body << properties.to_s
|
227
|
+
post_body << "\n--#{BOUNDARY}\n"
|
228
|
+
post_body << "Content-Type: message/rfc822\n\n"
|
229
|
+
post_body << message.to_s
|
230
|
+
post_body << "--#{BOUNDARY}--}"
|
231
|
+
|
232
|
+
post_body.join
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
metadata
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: google_apps
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Glen Holcomb
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-03-05 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: Library for interfacing with Google Apps' Domain and Application APIs
|
15
|
+
email:
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- !binary |-
|
21
|
+
bGliL2dvb2dsZV9hcHBzL2F0b20vYXRvbS5yYg==
|
22
|
+
- !binary |-
|
23
|
+
bGliL2dvb2dsZV9hcHBzL2F0b20vZXhwb3J0LnJi
|
24
|
+
- !binary |-
|
25
|
+
bGliL2dvb2dsZV9hcHBzL2F0b20vZ3JvdXAucmI=
|
26
|
+
- !binary |-
|
27
|
+
bGliL2dvb2dsZV9hcHBzL2F0b20vbWVzc2FnZS5yYg==
|
28
|
+
- !binary |-
|
29
|
+
bGliL2dvb2dsZV9hcHBzL2F0b20vbWVzc2FnZV9hdHRyaWJ1dGVzLnJi
|
30
|
+
- !binary |-
|
31
|
+
bGliL2dvb2dsZV9hcHBzL2F0b20vcHVibGljX2tleS5yYg==
|
32
|
+
- !binary |-
|
33
|
+
bGliL2dvb2dsZV9hcHBzL2F0b20vdXNlci5yYg==
|
34
|
+
- !binary |-
|
35
|
+
bGliL2dvb2dsZV9hcHBzL3RyYW5zcG9ydC5yYg==
|
36
|
+
- !binary |-
|
37
|
+
bGliL2dvb2dsZV9hcHBzLnJi
|
38
|
+
homepage: https://github.com/LeakyBucket/google_apps
|
39
|
+
licenses: []
|
40
|
+
post_install_message:
|
41
|
+
rdoc_options: []
|
42
|
+
require_paths:
|
43
|
+
- lib
|
44
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
45
|
+
none: false
|
46
|
+
requirements:
|
47
|
+
- - ! '>='
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '0'
|
50
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ! '>='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
requirements: []
|
57
|
+
rubyforge_project:
|
58
|
+
rubygems_version: 1.8.10
|
59
|
+
signing_key:
|
60
|
+
specification_version: 3
|
61
|
+
summary: Google Apps APIs
|
62
|
+
test_files: []
|