ruby-gmail 0.0.8 → 0.0.9
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/History.txt +7 -1
- data/Manifest.txt +1 -0
- data/README.markdown +4 -3
- data/Rakefile +18 -0
- data/VERSION +1 -0
- data/lib/gmail.rb +10 -2
- data/lib/ietf/rfc2045.rb +1 -1
- data/lib/mime/entity.rb +3 -3
- data/lib/mime/entity_tmail.rb +217 -0
- data/lib/mime/message.rb +5 -5
- metadata +33 -54
- data.tar.gz.sig +0 -2
- metadata.gz.sig +0 -0
data/History.txt
CHANGED
data/Manifest.txt
CHANGED
data/README.markdown
CHANGED
@@ -12,7 +12,8 @@ Extra thanks for specific feature contributions from:
|
|
12
12
|
|
13
13
|
* [Justin Perkins](http://github.com/justinperkins)
|
14
14
|
* [Mikkel Malmberg](http://github.com/mikker)
|
15
|
-
|
15
|
+
* [Julien Blanchard](http://github.com/julienXX)
|
16
|
+
* [Federico Galassi](http://github.com/fgalassi)
|
16
17
|
|
17
18
|
## Description
|
18
19
|
|
@@ -74,11 +75,11 @@ A Rubyesque interface to Gmail, with all the tools you'll need. Search, read and
|
|
74
75
|
* ruby
|
75
76
|
* net/smtp
|
76
77
|
* net/imap
|
77
|
-
* shared-mime-info rubygem
|
78
|
+
* shared-mime-info rubygem (for MIME-detection when attaching files)
|
78
79
|
|
79
80
|
## Install
|
80
81
|
|
81
|
-
|
82
|
+
gem install ruby-gmail
|
82
83
|
|
83
84
|
## License
|
84
85
|
|
data/Rakefile
CHANGED
@@ -10,3 +10,21 @@ Hoe.spec 'ruby-gmail' do
|
|
10
10
|
self.url = "http://dcparker.github.com/ruby-gmail"
|
11
11
|
self.post_install_message = "\n\033[34mIf ruby-gmail saves you TWO hours of work, want to compensate me for, like, a half-hour?\nSupport me in making new and better gems:\033[0m \033[31;4mhttp://pledgie.com/campaigns/7087\033[0m\n\n"
|
12
12
|
end
|
13
|
+
|
14
|
+
begin
|
15
|
+
require 'jeweler'
|
16
|
+
Jeweler::Tasks.new do |gem|
|
17
|
+
gem.name = "ruby-gmail"
|
18
|
+
gem.summary = %Q{A Rubyesque interface to Gmail, with all the tools you'll need.}
|
19
|
+
gem.description = %Q{A Rubyesque interface to Gmail, with all the tools you'll need. Search, read and send multipart emails; archive, mark as read/unread, delete emails; and manage labels.}
|
20
|
+
gem.email = "gems@behindlogic.com"
|
21
|
+
gem.homepage = "http://dcparker.github.com/ruby-gmail"
|
22
|
+
gem.authors = ["BehindLogic"]
|
23
|
+
gem.post_install_message = "\n\033[34mIf ruby-gmail saves you TWO hours of work, want to compensate me for, like, a half-hour?\nSupport me in making new and better gems:\033[0m \033[31;4mhttp://pledgie.com/campaigns/7087\033[0m\n\n"
|
24
|
+
gem.add_dependency('shared-mime-info', '>= 0')
|
25
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
26
|
+
end
|
27
|
+
Jeweler::GemcutterTasks.new
|
28
|
+
rescue LoadError
|
29
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
30
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.9
|
data/lib/gmail.rb
CHANGED
@@ -3,7 +3,7 @@ require 'net/smtp'
|
|
3
3
|
require 'smtp_tls'
|
4
4
|
|
5
5
|
class Gmail
|
6
|
-
VERSION = '0.0.
|
6
|
+
VERSION = '0.0.9'
|
7
7
|
|
8
8
|
class NoLabel < RuntimeError; end
|
9
9
|
|
@@ -53,6 +53,14 @@ class Gmail
|
|
53
53
|
imap.create(name)
|
54
54
|
end
|
55
55
|
|
56
|
+
# List the available labels
|
57
|
+
def labels
|
58
|
+
(@imap.list("", "%") + @imap.list("[Gmail]/", "%")).inject([]) do |lables,label|
|
59
|
+
label[:name].each_line { |l| labels << l }
|
60
|
+
labels
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
56
64
|
def in_mailbox(mailbox, &block)
|
57
65
|
raise ArgumentError, "Must provide a code block" unless block_given?
|
58
66
|
mailbox_stack << mailbox
|
@@ -80,7 +88,7 @@ class Gmail
|
|
80
88
|
block.call(lambda {|to, body|
|
81
89
|
from = meta.username
|
82
90
|
puts "Sending from #{from} to #{to}:\n#{body}"
|
83
|
-
smtp.send_message(body, from, to)
|
91
|
+
smtp.send_message(body.to_s, from, to)
|
84
92
|
})
|
85
93
|
puts "SMTP closing."
|
86
94
|
end
|
data/lib/ietf/rfc2045.rb
CHANGED
@@ -4,7 +4,7 @@ module IETF
|
|
4
4
|
def self.parse_rfc2045_from(raw)
|
5
5
|
headers, raw = IETF::RFC822.parse_rfc822_from(raw)
|
6
6
|
|
7
|
-
if headers['content-type'] =~ /multipart\/(\w+); boundary=([\"\']
|
7
|
+
if headers['content-type'] =~ /multipart\/(\w+); boundary=([\"\'])(.*)\2/ || headers['content-type'] =~ /multipart\/(\w+); boundary(=)(.*)$/
|
8
8
|
content = {}
|
9
9
|
content[:type] = $1
|
10
10
|
content[:boundary] = $3
|
data/lib/mime/entity.rb
CHANGED
@@ -11,11 +11,11 @@ module MIME
|
|
11
11
|
end
|
12
12
|
class Entity
|
13
13
|
def initialize(one=nil,two=nil)
|
14
|
-
if one.is_a?(Hash) || two.is_a?(Hash)
|
14
|
+
if one.is_a?(Hash) || two.is_a?(Hash) # Intent is to generate a message from parameters
|
15
15
|
@headers = one.is_a?(Hash) ? one : two
|
16
16
|
set_content one if one.is_a?(String)
|
17
17
|
@encoding = 'quoted-printable' unless encoding
|
18
|
-
elsif one.is_a?(String)
|
18
|
+
elsif one.is_a?(String) # Intent is to parse a message body
|
19
19
|
@raw = one.gsub(/\r/,'').gsub(/\n/, "\r\n") # normalizes end-of-line characters
|
20
20
|
from_parsed(IETF::RFC2045.parse_rfc2045_from(@raw))
|
21
21
|
end
|
@@ -150,7 +150,7 @@ module MIME
|
|
150
150
|
# Renders this data structure into a string, encoded
|
151
151
|
def to_s
|
152
152
|
multipart_boundary # initialize the boundary if necessary
|
153
|
-
headers.inject('') {|a,(k,v)| a << "#{MIME.capitalize_header(k)}: #{v}\r\n"} + "\r\n" + if content.is_a?(Array)
|
153
|
+
{'charset' => 'utf-8', 'content-transfer-encoding' => @encoding}.merge(headers).inject('') {|a,(k,v)| a << "#{MIME.capitalize_header(k)}: #{v}\r\n"} + "\r\n" + if content.is_a?(Array)
|
154
154
|
"\r\n--#{multipart_boundary}\r\n" + content.collect {|part| part.to_s }.join("\r\n--#{multipart_boundary}\r\n") + "\r\n--#{multipart_boundary}--\r\n"
|
155
155
|
else
|
156
156
|
content.to_s
|
@@ -0,0 +1,217 @@
|
|
1
|
+
require 'tmail'
|
2
|
+
require 'ietf/rfc2045'
|
3
|
+
String::ALPHANUMERIC_CHARACTERS = ('a'..'z').to_a + ('A'..'Z').to_a unless defined? String::ALPHANUMERIC_CHARACTERS
|
4
|
+
def String.random(size)
|
5
|
+
length = String::ALPHANUMERIC_CHARACTERS.length
|
6
|
+
(0...size).collect { String::ALPHANUMERIC_CHARACTERS[Kernel.rand(length)] }.join
|
7
|
+
end
|
8
|
+
|
9
|
+
module MIME
|
10
|
+
def self.capitalize_header(header_name)
|
11
|
+
header_name.gsub(/^(\w)/) {|m| m.capitalize}.gsub(/-(\w)/) {|n| '-' + n[1].chr.capitalize}
|
12
|
+
end
|
13
|
+
class Entity
|
14
|
+
def initialize(one=nil,two=nil)
|
15
|
+
if one.is_a?(Hash) || two.is_a?(Hash) # Intent is to generate a message from parameters
|
16
|
+
@headers = one.is_a?(Hash) ? one : two
|
17
|
+
set_content one if one.is_a?(String)
|
18
|
+
@encoding = 'quoted-printable' unless encoding
|
19
|
+
elsif one.is_a?(String) # Intent is to parse a message body
|
20
|
+
@raw = one.gsub(/\r/,'').gsub(/\n/, "\r\n") # normalizes end-of-line characters
|
21
|
+
@tmail = TMail::Mail.parse(@raw)
|
22
|
+
from_tmail(@tmail)
|
23
|
+
elsif one.is_a?(TMail::Mail)
|
24
|
+
@tmail = one
|
25
|
+
from_tmail(@tmail)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def inspect
|
30
|
+
"<#{self.class.name}##{object_id} Headers:{#{headers.collect {|k,v| "#{k}=#{v}"}.join(' ')}} content:#{multipart? ? 'multipart' : 'flat'}>"
|
31
|
+
end
|
32
|
+
|
33
|
+
# This means we have a structure from IETF::RFC2045.
|
34
|
+
# Entity is: [headers, content], while content may be an array of Entities.
|
35
|
+
# Or, {:type, :boundary, :content}
|
36
|
+
def from_parsed(parsed)
|
37
|
+
case parsed
|
38
|
+
when Array
|
39
|
+
if parsed[0].is_a?(Hash) && (parsed[1].is_a?(Hash) || parsed[1].is_a?(String))
|
40
|
+
@headers = parsed[0]
|
41
|
+
@content = parsed[1].is_a?(Hash) ? parsed[1][:content].collect {|p| Entity.new.from_parsed(p)} : parsed[1]
|
42
|
+
if parsed[1].is_a?(Hash)
|
43
|
+
@multipart_type = parsed[1][:type]
|
44
|
+
@multipart_boundary = parsed[1][:boundary]
|
45
|
+
raise "IETF PARSING FAIL! (empty boundary)" if @multipart_boundary == ''
|
46
|
+
end
|
47
|
+
else
|
48
|
+
raise "IETF PARSING FAIL! ('A' structure)"
|
49
|
+
end
|
50
|
+
return self
|
51
|
+
when Hash
|
52
|
+
if parsed.has_key?(:type) && parsed.has_key?(:boundary) && parsed.has_key?(:content)
|
53
|
+
@content = parsed[:content].is_a?(Array) ? parsed[:content].collect {|p| Entity.new.from_parsed(p)} : parsed[:content]
|
54
|
+
else
|
55
|
+
raise "IETF PARSING FAIL! ('H' structure)"
|
56
|
+
end
|
57
|
+
return self
|
58
|
+
end
|
59
|
+
raise ArgumentError, "Must pass in either: [an array with two elements: headers(hash) and content(string or array)] OR [a hash containing :type, :boundary, and :content(being the former or a string)]"
|
60
|
+
end
|
61
|
+
def from_tmail(tmail)
|
62
|
+
raise ArgumentError, "Expecting a TMail::Mail object." unless tmail.is_a?(TMail::Mail)
|
63
|
+
@headers ||= Hash.new {|h,k| @tmail.header[k].to_s }
|
64
|
+
if multipart?
|
65
|
+
@content = @tmail.parts.collect { |tpart| Entity.new.from_tmail(tpart) }
|
66
|
+
else
|
67
|
+
set_content @tmail.body # TMail has already decoded it, but we need it still encoded
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
##############
|
72
|
+
# ATTRIBUTES #
|
73
|
+
|
74
|
+
# An Entity has Headers.
|
75
|
+
def headers
|
76
|
+
@headers ||= {}
|
77
|
+
end
|
78
|
+
# An Entity has Content.
|
79
|
+
# IF the Content-Type is a multipart type,
|
80
|
+
# the content will be one or more Entities.
|
81
|
+
attr_reader :content, :multipart_type
|
82
|
+
|
83
|
+
#################
|
84
|
+
# Macro Methods #
|
85
|
+
|
86
|
+
def multipart?
|
87
|
+
return @tmail.multipart? if @tmail
|
88
|
+
!!(headers['content-type'] =~ /multipart\//) if headers['content-type']
|
89
|
+
end
|
90
|
+
def multipart_type
|
91
|
+
if headers['content-type'] =~ /multipart\/(\w+)/
|
92
|
+
$1
|
93
|
+
end if headers['content-type']
|
94
|
+
end
|
95
|
+
# Auto-generates a boundary if one doesn't yet exist.
|
96
|
+
def multipart_boundary
|
97
|
+
return nil unless multipart?
|
98
|
+
unless @multipart_boundary ||= (@tmail && @tmail.header['content-type'] ? @tmail.header['content-type'].params['boundary'] : nil)
|
99
|
+
# Content-Type: multipart/mixed; boundary=000e0cd28d1282f4ba04788017e5
|
100
|
+
@multipart_boundary = String.random(25)
|
101
|
+
headers['content-type'] = "multipart/#{multipart_type}; boundary=#{@multipart_boundary}"
|
102
|
+
@multipart_boundary
|
103
|
+
end
|
104
|
+
@multipart_boundary
|
105
|
+
end
|
106
|
+
def attachment?
|
107
|
+
@tmail.disposition_is_attachment? || headers['content-disposition'] =~ /^form-data;.* filename=[\"\']?[^\"\']+[\"\']?/ if headers['content-disposition']
|
108
|
+
end
|
109
|
+
alias :file? :attachment?
|
110
|
+
def part_filename
|
111
|
+
# Content-Disposition: attachment; filename="summary.txt"
|
112
|
+
if headers['content-disposition'] =~ /; filename=[\"\']?([^\"\']+)/
|
113
|
+
$1
|
114
|
+
end if headers['content-disposition']
|
115
|
+
end
|
116
|
+
|
117
|
+
def encoding
|
118
|
+
@encoding ||= headers['content-transfer-encoding'] || nil
|
119
|
+
end
|
120
|
+
# Re-encodes content if necessary when a new encoding is set
|
121
|
+
def encoding=(new_encoding)
|
122
|
+
raw_content = decoded_content # nil if multipart
|
123
|
+
@encoding = new_encoding
|
124
|
+
set_content(raw_content) if raw_content # skips if multipart
|
125
|
+
@encoding
|
126
|
+
end
|
127
|
+
alias :set_encoding :encoding=
|
128
|
+
|
129
|
+
def find_part(options)
|
130
|
+
find_parts(options,true).first
|
131
|
+
end
|
132
|
+
def find_parts(options,find_only_one=false)
|
133
|
+
parts = []
|
134
|
+
return nil unless (options[:content_type] && headers['content-type']) || (options[:content_disposition] && headers['content-disposition'])
|
135
|
+
# Do I match your search?
|
136
|
+
iam = true
|
137
|
+
iam = false if options[:content_type] && headers['content-type'] !~ /^#{options[:content_type]}(?=;|$)/
|
138
|
+
iam = false if options[:content_disposition] && headers['content-disposition'] !~ /^#{options[:content_disposition]}(?=;|$)/
|
139
|
+
parts << self if iam
|
140
|
+
return parts unless parts.empty?
|
141
|
+
# Do any of my children match your search?
|
142
|
+
content.each do |part|
|
143
|
+
parts.concat part.find_parts(options,find_only_one)
|
144
|
+
return parts if !parts.empty? && find_only_one
|
145
|
+
end if multipart?
|
146
|
+
return parts
|
147
|
+
end
|
148
|
+
|
149
|
+
def save_to_file(path=nil)
|
150
|
+
filename = path if path && !File.exists?(path) # If path doesn't exist, assume it's a filename
|
151
|
+
filename ||= path + '/' + part_filename if path && attachment? # If path does exist, and we're saving an attachment, use the attachment filename
|
152
|
+
filename ||= (attachment? ? part_filename : path) # If there is no path and we're saving an attachment, use the attachment filename; otherwise use path (whether it is present or not)
|
153
|
+
filename ||= '.' # No path supplied, and not saving an attachment. We'll just save it in the current directory.
|
154
|
+
if File.directory?(filename)
|
155
|
+
i = 0
|
156
|
+
begin
|
157
|
+
i += 1
|
158
|
+
filename = filename + "/attachment-#{i}"
|
159
|
+
end until !File.exists(filename)
|
160
|
+
end
|
161
|
+
# After all that trouble to get a filename to save to...
|
162
|
+
File.open(filename, 'w') do |file|
|
163
|
+
file << decoded_content
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
##########################
|
168
|
+
# CONVERTING / RENDERING #
|
169
|
+
|
170
|
+
# Renders this data structure into a string, encoded
|
171
|
+
def to_s
|
172
|
+
multipart_boundary # initialize the boundary if necessary, so it will be included in the headers
|
173
|
+
headers.inject('') {|a,(k,v)| a << "#{MIME.capitalize_header(k)}: #{v}\r\n"} + "\r\n" + if content.is_a?(Array)
|
174
|
+
"\r\n--#{multipart_boundary}\r\n" + content.collect {|part| part.to_s }.join("\r\n--#{multipart_boundary}\r\n") + "\r\n--#{multipart_boundary}--\r\n"
|
175
|
+
else
|
176
|
+
content.to_s
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
# Converts this data structure into a string, but decoded if necessary
|
181
|
+
def decoded_content
|
182
|
+
return nil if @content.is_a?(Array)
|
183
|
+
case encoding.to_s.downcase
|
184
|
+
when 'quoted-printable'
|
185
|
+
@content.unpack('M')[0]
|
186
|
+
when 'base64'
|
187
|
+
@content.unpack('m')[0]
|
188
|
+
# when '7bit'
|
189
|
+
# # should get this 7bit encoding done too...
|
190
|
+
else
|
191
|
+
@content
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
# You can set new content, and it will be saved in encoded form.
|
196
|
+
def set_content(raw)
|
197
|
+
@content = raw.is_a?(Array) ? raw :
|
198
|
+
case encoding.to_s.downcase
|
199
|
+
when 'quoted-printable'
|
200
|
+
[raw].pack('M')
|
201
|
+
when 'base64'
|
202
|
+
[raw].pack('m')
|
203
|
+
else
|
204
|
+
raw
|
205
|
+
end
|
206
|
+
end
|
207
|
+
alias :content= :set_content
|
208
|
+
|
209
|
+
private
|
210
|
+
def transfer_to(other)
|
211
|
+
other.instance_variable_set(:@content, @content.dup) if @content
|
212
|
+
other.headers.clear
|
213
|
+
other.headers.merge!(Hash[*headers.dup.select {|k,v| k =~ /content/}.flatten])
|
214
|
+
end
|
215
|
+
|
216
|
+
end
|
217
|
+
end
|
data/lib/mime/message.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
|
-
require 'mime/
|
2
|
-
require 'shared-mime-info'
|
1
|
+
require 'mime/entity_tmail'
|
3
2
|
module MIME
|
4
3
|
# A Message is really a MIME::Entity,
|
5
4
|
# but should be used for the outermost Entity, because
|
@@ -52,6 +51,7 @@ module MIME
|
|
52
51
|
short_filename = filename.match(/([^\\\/]+)$/)[1]
|
53
52
|
|
54
53
|
# Generate the attachment piece
|
54
|
+
require 'shared-mime-info'
|
55
55
|
attachment = Entity.new(
|
56
56
|
'content-type' => MIME.check(filename).type + "; \r\n name=\"#{short_filename}\"",
|
57
57
|
'content-disposition' => "attachment; \r\n filename=\"#{short_filename}\"",
|
@@ -61,10 +61,10 @@ module MIME
|
|
61
61
|
|
62
62
|
# Enclose in a top-level multipart/mixed
|
63
63
|
if multipart? && multipart_type == 'mixed'
|
64
|
-
# If already
|
64
|
+
# If we're already dealing with a mixed multipart, all we have to do is add the attachment part
|
65
65
|
(@content ||= []) << attachment
|
66
66
|
else
|
67
|
-
# If there is no content, add a little message about the attachment(s).
|
67
|
+
# If there is no content yet, add a little message about the attachment(s).
|
68
68
|
set_content 'See attachment(s)' if @content.nil?
|
69
69
|
# Generate the new top-level multipart, transferring what is here already into a child object
|
70
70
|
new_content = Entity.new
|
@@ -72,7 +72,7 @@ module MIME
|
|
72
72
|
transfer_to(new_content)
|
73
73
|
headers.reject! {|k,v| k =~ /content/}
|
74
74
|
headers['content-type'] = 'multipart/mixed'
|
75
|
-
headers.delete 'content-transfer-encoding' # because
|
75
|
+
headers.delete 'content-transfer-encoding' # because we don't need a transfer-encoding on a multipart package.
|
76
76
|
@content = [new_content, attachment]
|
77
77
|
end
|
78
78
|
|
metadata
CHANGED
@@ -1,82 +1,60 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-gmail
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 9
|
9
|
+
version: 0.0.9
|
5
10
|
platform: ruby
|
6
11
|
authors:
|
7
|
-
-
|
12
|
+
- BehindLogic
|
8
13
|
autorequire:
|
9
14
|
bindir: bin
|
10
|
-
cert_chain:
|
11
|
-
- |
|
12
|
-
-----BEGIN CERTIFICATE-----
|
13
|
-
MIIDNjCCAh6gAwIBAgIBADANBgkqhkiG9w0BAQUFADBBMQ0wCwYDVQQDDARnZW1z
|
14
|
-
MRswGQYKCZImiZPyLGQBGRYLYmVoaW5kbG9naWMxEzARBgoJkiaJk/IsZAEZFgNj
|
15
|
-
b20wHhcNMDkxMTE5MDQ0NzIzWhcNMTAxMTE5MDQ0NzIzWjBBMQ0wCwYDVQQDDARn
|
16
|
-
ZW1zMRswGQYKCZImiZPyLGQBGRYLYmVoaW5kbG9naWMxEzARBgoJkiaJk/IsZAEZ
|
17
|
-
FgNjb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDs+2PvSoBC5mCz
|
18
|
-
Nm95RXKOb/u+CmPA70DoG9cF0zipCie003Vm0DYL3Rtobcr32eMvHxkWoJ6xoz0I
|
19
|
-
h74yNKtHjTTCxj86HWBPsE6xVMxVCftClndjCyKsiMiqvvp1wDNO0FFK+6LijmL3
|
20
|
-
2Xkp4brWq1JO92y9vYct34R7o2X//+nwZs+sss+EYhNdvdUJfWy7tA5dghGdLvRn
|
21
|
-
UhJJSAtTefkBCwO7bufLEt+n7wIRbiJJ5dDCwE3NIX4wUSrNeYwXGXA/Ybki+BUl
|
22
|
-
3KJF9IC0XR9fY9DGF0FXBKrkfDlZRrnQOem2aIxeuln0KQLJXXJuDTQPHO+mK3EG
|
23
|
-
UPhR7IAHAgMBAAGjOTA3MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQW
|
24
|
-
BBQn32ZStKmqwFqs2vuglYzkvDzBZjANBgkqhkiG9w0BAQUFAAOCAQEAe3Z+iMmy
|
25
|
-
IX9ChQW4hNNb1HOpgCMc2RL+vUwku9WE95dZ+BE4A6mOYTj5JXdYf4R4Z2vavr+d
|
26
|
-
nwJWtXPeIBWxireb8gUU0DwqodYTpsmkj5LD1zIaZ59rXlwDA9O0V4fwE1iRG5MD
|
27
|
-
mB7m8fT8WNOeg4AfjH4aSiHI1+HX1RQkc7KFdLotKnCevzYU6Jza5VUbXyJ+yCEH
|
28
|
-
DFARN3mkfGI+18MRDEi39nK2O/bBd6Wf0cYPEKsGQjNNAIBtv9belepSMd1KKfQ2
|
29
|
-
L7j8CnNDCrsHDe7/251D85wSvTH4Q/41NE5ahdCkkHwzDJeyhXpmNuUSswdn7woz
|
30
|
-
teST6sOe8lUhZQ==
|
31
|
-
-----END CERTIFICATE-----
|
15
|
+
cert_chain: []
|
32
16
|
|
33
|
-
date:
|
17
|
+
date: 2010-04-18 00:00:00 -04:00
|
34
18
|
default_executable:
|
35
19
|
dependencies:
|
36
20
|
- !ruby/object:Gem::Dependency
|
37
21
|
name: shared-mime-info
|
38
|
-
|
39
|
-
|
40
|
-
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
41
24
|
requirements:
|
42
25
|
- - ">="
|
43
26
|
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
44
29
|
version: "0"
|
45
|
-
|
46
|
-
|
47
|
-
name: hoe
|
48
|
-
type: :development
|
49
|
-
version_requirement:
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - ">="
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: 2.3.3
|
55
|
-
version:
|
30
|
+
type: :runtime
|
31
|
+
version_requirements: *id001
|
56
32
|
description: A Rubyesque interface to Gmail, with all the tools you'll need. Search, read and send multipart emails; archive, mark as read/unread, delete emails; and manage labels.
|
57
|
-
email:
|
58
|
-
- gems@behindlogic.com
|
33
|
+
email: gems@behindlogic.com
|
59
34
|
executables: []
|
60
35
|
|
61
36
|
extensions: []
|
62
37
|
|
63
38
|
extra_rdoc_files:
|
64
|
-
-
|
65
|
-
- Manifest.txt
|
39
|
+
- README.markdown
|
66
40
|
files:
|
67
41
|
- .autotest
|
68
42
|
- History.txt
|
43
|
+
- Manifest.txt
|
44
|
+
- README.markdown
|
45
|
+
- Rakefile
|
46
|
+
- VERSION
|
47
|
+
- lib/gmail.rb
|
69
48
|
- lib/gmail/mailbox.rb
|
70
49
|
- lib/gmail/message.rb
|
71
|
-
- lib/gmail.rb
|
72
50
|
- lib/ietf/rfc2045.rb
|
73
51
|
- lib/ietf/rfc822.rb
|
74
52
|
- lib/mime/entity.rb
|
53
|
+
- lib/mime/entity_tmail.rb
|
75
54
|
- lib/mime/message.rb
|
76
55
|
- lib/smtp_tls.rb
|
77
|
-
-
|
78
|
-
-
|
79
|
-
- README.markdown
|
56
|
+
- test/test_gmail.rb
|
57
|
+
- test/test_helper.rb
|
80
58
|
has_rdoc: true
|
81
59
|
homepage: http://dcparker.github.com/ruby-gmail
|
82
60
|
licenses: []
|
@@ -85,29 +63,30 @@ post_install_message: "\n\
|
|
85
63
|
\e[34mIf ruby-gmail saves you TWO hours of work, want to compensate me for, like, a half-hour?\n\
|
86
64
|
Support me in making new and better gems:\e[0m \e[31;4mhttp://pledgie.com/campaigns/7087\e[0m\n\n"
|
87
65
|
rdoc_options:
|
88
|
-
- --
|
89
|
-
- README.markdown
|
66
|
+
- --charset=UTF-8
|
90
67
|
require_paths:
|
91
68
|
- lib
|
92
69
|
required_ruby_version: !ruby/object:Gem::Requirement
|
93
70
|
requirements:
|
94
71
|
- - ">="
|
95
72
|
- !ruby/object:Gem::Version
|
73
|
+
segments:
|
74
|
+
- 0
|
96
75
|
version: "0"
|
97
|
-
version:
|
98
76
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
99
77
|
requirements:
|
100
78
|
- - ">="
|
101
79
|
- !ruby/object:Gem::Version
|
80
|
+
segments:
|
81
|
+
- 0
|
102
82
|
version: "0"
|
103
|
-
version:
|
104
83
|
requirements: []
|
105
84
|
|
106
|
-
rubyforge_project:
|
107
|
-
rubygems_version: 1.3.
|
85
|
+
rubyforge_project:
|
86
|
+
rubygems_version: 1.3.6
|
108
87
|
signing_key:
|
109
88
|
specification_version: 3
|
110
|
-
summary: A Rubyesque interface to Gmail, with all the tools you'll need
|
89
|
+
summary: A Rubyesque interface to Gmail, with all the tools you'll need.
|
111
90
|
test_files:
|
112
91
|
- test/test_gmail.rb
|
113
92
|
- test/test_helper.rb
|
data.tar.gz.sig
DELETED
metadata.gz.sig
DELETED
Binary file
|