redsnapper-mail 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +3 -0
- data/README +63 -0
- data/Rakefile +54 -0
- data/lib/mail.rb +134 -0
- data/lib/mail/attachment.rb +29 -0
- data/lib/mail/content_disposition.rb +57 -0
- data/lib/mail/content_type.rb +59 -0
- data/lib/mail/header.rb +101 -0
- data/lib/mail/part.rb +52 -0
- data/lib/plugins/json.rb +12 -0
- metadata +65 -0
data/LICENSE
ADDED
data/README
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
Mail Lib
|
2
|
+
|
3
|
+
An mail parsing lib.
|
4
|
+
|
5
|
+
== Features
|
6
|
+
|
7
|
+
* clean API
|
8
|
+
|
9
|
+
== Usage
|
10
|
+
|
11
|
+
Create new Mail
|
12
|
+
|
13
|
+
mail = Mail::Mail.new
|
14
|
+
mail.header.to = "test_to@test.de"
|
15
|
+
mail.header.from = "test_from@test.de"
|
16
|
+
mail.header.subject = "Test Mail"
|
17
|
+
mail.header.content_transfer_encoding = "quoted-printable"
|
18
|
+
mail.header.content_type.charset = "UTF-8"
|
19
|
+
mail.content = {:text_plain => "Hallo World !!!"}
|
20
|
+
|
21
|
+
Create new Mail with Text and HTML part
|
22
|
+
|
23
|
+
mail = Mail::Mail.new
|
24
|
+
mail.header.to = "test_to@test.de"
|
25
|
+
mail.header.from = "test_from@test.de"
|
26
|
+
mail.header.subject = "Test Mail"
|
27
|
+
mail.header.content_transfer_encoding = "quoted-printable"
|
28
|
+
mail.header.content_type.charset = "UTF-8"
|
29
|
+
mail.content = {
|
30
|
+
:text_plain => "Hallo World !!!",
|
31
|
+
:text_html => "<strong>Hallo World !!!</strong>"
|
32
|
+
}
|
33
|
+
|
34
|
+
Create new Mail Attachment
|
35
|
+
|
36
|
+
mail = Mail::Mail.new
|
37
|
+
mail.header.to = "test_to@test.de"
|
38
|
+
mail.header.from = "test_from@test.de"
|
39
|
+
mail.header.subject = "Test Mail"
|
40
|
+
mail.header.content_transfer_encoding = "quoted-printable"
|
41
|
+
mail.header.content_type.charset = "UTF-8"
|
42
|
+
mail.content = {
|
43
|
+
:text_plain => "Hallo World !!!",
|
44
|
+
:image_jpeg => File.read("path/to/image")
|
45
|
+
}
|
46
|
+
|
47
|
+
Load an existing Mail
|
48
|
+
|
49
|
+
mail = Mail::Mail.new(File.read("path/to/mail"))
|
50
|
+
|
51
|
+
puts mail.header.to => ["test_to@test.de"]
|
52
|
+
|
53
|
+
Get attachments from a Mail
|
54
|
+
|
55
|
+
mail = Mail::Mail.new(File.read("path/to/mail"))
|
56
|
+
|
57
|
+
if mail.header.attachments?
|
58
|
+
mail.attachments do |attachment|
|
59
|
+
puts attachment.header.content_type.type => "image/tiff"
|
60
|
+
puts attachment.header.content_disposition.filename => "fax.tif"
|
61
|
+
puts attachment.size => 408850
|
62
|
+
end
|
63
|
+
end
|
data/Rakefile
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/clean'
|
4
|
+
require 'rake/gempackagetask'
|
5
|
+
require 'rake/rdoctask'
|
6
|
+
require 'rake/testtask'
|
7
|
+
require File.join(File.expand_path(File.dirname(__FILE__)),'lib','mail')
|
8
|
+
|
9
|
+
spec = Gem::Specification.new do |s|
|
10
|
+
s.name = 'mail'
|
11
|
+
s.version = Mail::VERSION
|
12
|
+
s.has_rdoc = true
|
13
|
+
s.extra_rdoc_files = ['README', 'LICENSE']
|
14
|
+
s.summary = 'Mail Parsing lib'
|
15
|
+
s.description = s.summary
|
16
|
+
s.author = 'Alexander Klaiber'
|
17
|
+
s.email = 'aklaiber@mybrief.de'
|
18
|
+
s.files = %w(LICENSE README Rakefile) + Dir.glob("{bin,lib,spec}/**/*")
|
19
|
+
s.require_path = "lib"
|
20
|
+
end
|
21
|
+
|
22
|
+
Rake::GemPackageTask.new(spec) do |p|
|
23
|
+
p.gem_spec = spec
|
24
|
+
p.need_tar = true
|
25
|
+
p.need_zip = true
|
26
|
+
end
|
27
|
+
|
28
|
+
Rake::RDocTask.new do |rdoc|
|
29
|
+
files =['README', 'LICENSE', 'lib/**/*.rb']
|
30
|
+
rdoc.rdoc_files.add(files)
|
31
|
+
rdoc.main = "README"
|
32
|
+
rdoc.title = "mail Docs"
|
33
|
+
rdoc.rdoc_dir = 'doc/rdoc'
|
34
|
+
rdoc.options << '--line-numbers'
|
35
|
+
end
|
36
|
+
|
37
|
+
Rake::TestTask.new do |t|
|
38
|
+
t.test_files = FileList['test/**/*.rb']
|
39
|
+
end
|
40
|
+
|
41
|
+
desc "Create .gemspec file (useful for github)"
|
42
|
+
task :gemspec do
|
43
|
+
filename = "#{spec.name}.gemspec"
|
44
|
+
File.open(filename, "w") do |f|
|
45
|
+
f.puts spec.to_ruby
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
desc "Install the gem locally"
|
50
|
+
task :install => [:package] do
|
51
|
+
sh %{sudo gem install pkg/couchrest-#{CouchRest::VERSION}}
|
52
|
+
end
|
53
|
+
|
54
|
+
|
data/lib/mail.rb
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
# Author:: Alexander Klaiber (mailto:aklaiber@mybrief.de)
|
2
|
+
# Copyright:: Copyright (c) 2009 Alexander Klaiber
|
3
|
+
# License::
|
4
|
+
|
5
|
+
require "#{File.dirname(__FILE__)}/mail/header"
|
6
|
+
require "#{File.dirname(__FILE__)}/mail/content_type"
|
7
|
+
require "#{File.dirname(__FILE__)}/mail/content_disposition"
|
8
|
+
require "#{File.dirname(__FILE__)}/mail/part"
|
9
|
+
require "#{File.dirname(__FILE__)}/mail/attachment"
|
10
|
+
require "#{File.dirname(__FILE__)}/plugins/json"
|
11
|
+
|
12
|
+
module Mail
|
13
|
+
|
14
|
+
VERSION = '0.0.2' unless self.const_defined?("VERSION")
|
15
|
+
|
16
|
+
class Mail < Part
|
17
|
+
include Json
|
18
|
+
|
19
|
+
attr_reader :header, :body
|
20
|
+
|
21
|
+
def initialize(mail_str = nil)
|
22
|
+
super(mail_str)
|
23
|
+
@content_typs = Array.new
|
24
|
+
@parts = Hash.new
|
25
|
+
if mail_str
|
26
|
+
set_parts(mail_str)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_s
|
31
|
+
mail = @header.to_s
|
32
|
+
mail << "\n\n"
|
33
|
+
@parts.each do |type, part|
|
34
|
+
if @header.content_type.boundary
|
35
|
+
mail << "--#{@header.content_type.boundary}\n"
|
36
|
+
mail << part.header.content_type.to_s
|
37
|
+
mail << "\n\n"
|
38
|
+
end
|
39
|
+
mail << part.to_s
|
40
|
+
mail << "\n\n" if @header.content_type.boundary
|
41
|
+
end
|
42
|
+
mail << "--#{@header.content_type.boundary}--" if @header.content_type.boundary
|
43
|
+
return mail
|
44
|
+
end
|
45
|
+
|
46
|
+
def content=(options)
|
47
|
+
options.each do |type, content|
|
48
|
+
content_type = type.to_s.sub(/_/, '/')
|
49
|
+
if content_type
|
50
|
+
@parts[content_type] = Part.new(:content_type => content_type, :content => content)
|
51
|
+
@content_typs.push(content_type)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
set_content_type
|
55
|
+
end
|
56
|
+
|
57
|
+
def content(type)
|
58
|
+
return parts[type].to_s
|
59
|
+
end
|
60
|
+
|
61
|
+
def parts
|
62
|
+
return @parts
|
63
|
+
end
|
64
|
+
|
65
|
+
def text_parts
|
66
|
+
parts.each do |content_type, part|
|
67
|
+
if content_type.eql?('text/plain') || content_type.eql?('text/html')
|
68
|
+
yield part
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def attachments
|
74
|
+
parts.each do |content_type, part|
|
75
|
+
if !content_type.eql?('text/plain') && !content_type.eql?('text/html')
|
76
|
+
yield Attachment.new(part.to_s)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def set_content_type
|
84
|
+
if @content_typs.size == 1
|
85
|
+
@header.content_type.type = @content_typs.first
|
86
|
+
elsif @content_typs.include?('text/plain') && @content_typs.include?('text/html')
|
87
|
+
@header.content_type.type = "multipart/alternative"
|
88
|
+
@header.content_type.boundary="#{Time.now.to_i}"
|
89
|
+
elsif @content_typs.size == 2
|
90
|
+
@header.content_type.type = "multipart/mixed"
|
91
|
+
@header.content_type.boundary="#{Time.now.to_i}"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def set_parts(str)
|
96
|
+
buffer = String.new
|
97
|
+
body = split_body(str)
|
98
|
+
if @header.content_type && @header.content_type.boundary
|
99
|
+
part_found = false
|
100
|
+
body.each do |line|
|
101
|
+
if line.strip.eql?("--#{@header.content_type.boundary}") || line.strip.eql?("--#{@header.content_type.boundary}--")
|
102
|
+
part_found = true
|
103
|
+
unless buffer.empty?
|
104
|
+
part = Part.new(buffer)
|
105
|
+
@parts[part.header.content_type.type] = part
|
106
|
+
buffer = String.new
|
107
|
+
end
|
108
|
+
elsif part_found
|
109
|
+
buffer << line
|
110
|
+
end
|
111
|
+
end
|
112
|
+
else
|
113
|
+
body.each do |line|
|
114
|
+
buffer << line
|
115
|
+
end
|
116
|
+
@parts[@header.content_type.type] = Part.new(buffer)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def split_body(str)
|
121
|
+
buffer = String.new
|
122
|
+
body_found = false
|
123
|
+
str.each_line do |line|
|
124
|
+
if body_found
|
125
|
+
buffer << line
|
126
|
+
end
|
127
|
+
body_found = true if !body_found && line.strip.empty?
|
128
|
+
end
|
129
|
+
return buffer
|
130
|
+
return nil
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require "base64"
|
2
|
+
|
3
|
+
module Mail
|
4
|
+
class Attachment < Part
|
5
|
+
def initialize(str_part)
|
6
|
+
super(str_part)
|
7
|
+
@binary = nil
|
8
|
+
end
|
9
|
+
|
10
|
+
def size
|
11
|
+
content.size
|
12
|
+
end
|
13
|
+
|
14
|
+
def content
|
15
|
+
unless @binary
|
16
|
+
buffer = String.new
|
17
|
+
body_found = false
|
18
|
+
@part_str.each_line do |line|
|
19
|
+
if body_found
|
20
|
+
buffer << line
|
21
|
+
end
|
22
|
+
body_found = true if !body_found && line.strip.empty?
|
23
|
+
end
|
24
|
+
@binary = Base64.decode64(buffer)
|
25
|
+
end
|
26
|
+
return @binary
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Mail
|
2
|
+
class ContentDisposition
|
3
|
+
def initialize(options = nil)
|
4
|
+
@params = Hash.new
|
5
|
+
if options
|
6
|
+
if options.kind_of?(Hash)
|
7
|
+
@type = options[:type]
|
8
|
+
elsif options.kind_of?(String)
|
9
|
+
set_params(options)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
@content_type_str = String.new
|
16
|
+
@params.each do |key, value|
|
17
|
+
case key
|
18
|
+
when :type
|
19
|
+
@content_type_str << "Content-Type: #{type};"
|
20
|
+
else
|
21
|
+
@content_type_str << " #{key}=#{value}\n"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
return @content_type_str
|
25
|
+
end
|
26
|
+
|
27
|
+
def type=(value)
|
28
|
+
@params[:type] = value
|
29
|
+
end
|
30
|
+
|
31
|
+
def type
|
32
|
+
return @params[:type]
|
33
|
+
end
|
34
|
+
|
35
|
+
def method_missing(name, *args, &block)
|
36
|
+
if name.to_s.include?("=")
|
37
|
+
@params[name.to_s.delete("=").to_sym] = args.to_s
|
38
|
+
else
|
39
|
+
return @params[name]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def set_params(str)
|
46
|
+
@pattern = Hash.new
|
47
|
+
@pattern[:type] = "^(.*);"
|
48
|
+
@pattern[:filename] = "filename=(.*)$"
|
49
|
+
@pattern.each do |param_name, pattern|
|
50
|
+
match = str.match(pattern)
|
51
|
+
if match
|
52
|
+
@params[param_name] = match[1].strip
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Mail
|
2
|
+
class ContentType
|
3
|
+
def initialize(options = nil)
|
4
|
+
@params = Hash.new
|
5
|
+
if options
|
6
|
+
if options.kind_of?(Hash)
|
7
|
+
@params[:type] = options[:type]
|
8
|
+
elsif options.kind_of?(String)
|
9
|
+
set_params(options)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
@content_type_str = String.new
|
16
|
+
@params.each do |key, value|
|
17
|
+
case key
|
18
|
+
when :type
|
19
|
+
@content_type_str << "Content-Type: #{type};"
|
20
|
+
else
|
21
|
+
@content_type_str << " #{key}=#{value}\n"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
return @content_type_str
|
25
|
+
end
|
26
|
+
|
27
|
+
def type=(value)
|
28
|
+
@params[:type] = value
|
29
|
+
end
|
30
|
+
|
31
|
+
def type
|
32
|
+
return @params[:type]
|
33
|
+
end
|
34
|
+
|
35
|
+
def method_missing(name, *args, &block)
|
36
|
+
if name.to_s.include?("=")
|
37
|
+
@params[name.to_s.delete("=").to_sym] = args.to_s
|
38
|
+
else
|
39
|
+
return @params[name]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def set_params(str)
|
46
|
+
@pattern = Hash.new
|
47
|
+
@pattern[:type] = "^(.*);"
|
48
|
+
@pattern[:boundary] = "boundary=\"(.*)\""
|
49
|
+
@pattern[:name] = "name=(.*)$"
|
50
|
+
@pattern[:charset] = "charset=(.*)$"
|
51
|
+
@pattern.each do |param_name, pattern|
|
52
|
+
match = str.match(pattern)
|
53
|
+
if match
|
54
|
+
@params[param_name] = match[1].strip
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/mail/header.rb
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Mail
|
5
|
+
class Header
|
6
|
+
|
7
|
+
def initialize(options = nil)
|
8
|
+
@params = Hash.new
|
9
|
+
if options
|
10
|
+
if options.kind_of?(Hash) && options.key?(:content_type)
|
11
|
+
@params[:content_type] = ContentType.new(:type => options[:content_type])
|
12
|
+
elsif options.kind_of?(String)
|
13
|
+
set_params(options)
|
14
|
+
end
|
15
|
+
else
|
16
|
+
@params[:content_type] = ContentType.new
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_s
|
21
|
+
str_header = String.new
|
22
|
+
@params.each do |key, value|
|
23
|
+
case key
|
24
|
+
when :content_type
|
25
|
+
str_header << value.to_s
|
26
|
+
else
|
27
|
+
str_header << "#{format_key(key)}: #{value}\n"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
str_header << @content_type.to_s
|
31
|
+
return str_header
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_json
|
35
|
+
@params.to_json
|
36
|
+
end
|
37
|
+
|
38
|
+
def multipart?
|
39
|
+
self.content_type.type.include?("multipart")
|
40
|
+
end
|
41
|
+
|
42
|
+
def attachments?
|
43
|
+
if self.multipart?
|
44
|
+
return self.content_type.type.eql?("multipart/mixed")
|
45
|
+
end
|
46
|
+
return false
|
47
|
+
end
|
48
|
+
|
49
|
+
def method_missing(name, *args, &block)
|
50
|
+
if name.to_s.include?("=")
|
51
|
+
@params[name.to_s.delete("=").to_sym] = args.to_s
|
52
|
+
else
|
53
|
+
return @params[name]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def set_params(str)
|
60
|
+
str.each_line do |line|
|
61
|
+
match = line.split(":")
|
62
|
+
if match && !match.empty?
|
63
|
+
key = match[0].downcase.gsub('-', '_').to_sym
|
64
|
+
value = match[1].strip if match[1]
|
65
|
+
case key
|
66
|
+
when :to
|
67
|
+
@params[key] = value.split(",").collect! { |x| x.strip }
|
68
|
+
when :from
|
69
|
+
@params[key] = value.split(",").collect! { |x| x.strip }
|
70
|
+
when :date
|
71
|
+
@params[key] = DateTime.parse(value)
|
72
|
+
when :content_type
|
73
|
+
@params[key] = ContentType.new(value)
|
74
|
+
when :content_disposition
|
75
|
+
@params[key] = ContentDisposition.new(value)
|
76
|
+
else
|
77
|
+
@params[key] = value
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
@params[:content_type] = ContentType.new(:type => "text/plain") unless @params.key?(:content_type)
|
82
|
+
end
|
83
|
+
|
84
|
+
def format_key(key)
|
85
|
+
if key.to_s.include?("_")
|
86
|
+
buffer = String.new
|
87
|
+
key.to_s.split("_").each do |part|
|
88
|
+
if buffer.empty?
|
89
|
+
buffer << part.capitalize
|
90
|
+
else
|
91
|
+
buffer << "-#{part.capitalize}"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
return buffer
|
95
|
+
else
|
96
|
+
return key.to_s.capitalize
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
end
|
data/lib/mail/part.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
module Mail
|
2
|
+
class Part
|
3
|
+
attr_reader :header
|
4
|
+
|
5
|
+
def initialize(options)
|
6
|
+
if options.kind_of?(String)
|
7
|
+
@part_str = options
|
8
|
+
set_header(options)
|
9
|
+
elsif options.kind_of?(Hash)
|
10
|
+
@part_str = options[:content]
|
11
|
+
@header = Header.new(:content_type => options[:content_type])
|
12
|
+
else
|
13
|
+
@header = Header.new
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_s
|
18
|
+
@part_str
|
19
|
+
end
|
20
|
+
|
21
|
+
def empty?
|
22
|
+
@part_str.empty?
|
23
|
+
end
|
24
|
+
|
25
|
+
def content
|
26
|
+
buffer = String.new
|
27
|
+
body_found = false
|
28
|
+
@part_str.each_line do |line|
|
29
|
+
if body_found
|
30
|
+
buffer << line
|
31
|
+
end
|
32
|
+
body_found = true if !body_found && line.strip.empty?
|
33
|
+
end
|
34
|
+
return buffer
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def set_header(str)
|
40
|
+
buffer = String.new
|
41
|
+
header_found = true
|
42
|
+
str.each_line do |line|
|
43
|
+
if header_found
|
44
|
+
buffer << line
|
45
|
+
end
|
46
|
+
header_found = false if header_found && line.strip.empty?
|
47
|
+
end
|
48
|
+
@header = Header.new(buffer)
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
data/lib/plugins/json.rb
ADDED
metadata
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: redsnapper-mail
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Alexander Klaiber
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-07-15 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Mail Parsing lib
|
17
|
+
email: aklaiber@mybrief.de
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README
|
24
|
+
- LICENSE
|
25
|
+
files:
|
26
|
+
- LICENSE
|
27
|
+
- README
|
28
|
+
- Rakefile
|
29
|
+
- lib/mail.rb
|
30
|
+
- lib/mail
|
31
|
+
- lib/mail/attachment.rb
|
32
|
+
- lib/mail/content_type.rb
|
33
|
+
- lib/mail/header.rb
|
34
|
+
- lib/mail/content_disposition.rb
|
35
|
+
- lib/mail/part.rb
|
36
|
+
- lib/plugins
|
37
|
+
- lib/plugins/json.rb
|
38
|
+
has_rdoc: false
|
39
|
+
homepage:
|
40
|
+
post_install_message:
|
41
|
+
rdoc_options: []
|
42
|
+
|
43
|
+
require_paths:
|
44
|
+
- lib
|
45
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: "0"
|
50
|
+
version:
|
51
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: "0"
|
56
|
+
version:
|
57
|
+
requirements: []
|
58
|
+
|
59
|
+
rubyforge_project:
|
60
|
+
rubygems_version: 1.2.0
|
61
|
+
signing_key:
|
62
|
+
specification_version: 3
|
63
|
+
summary: Mail Parsing lib
|
64
|
+
test_files: []
|
65
|
+
|