ruby-gmail 0.0.8 → 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- 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
|