kbaum-mail 2.1.2.1

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.
Files changed (114) hide show
  1. data/CHANGELOG.rdoc +289 -0
  2. data/README.rdoc +575 -0
  3. data/Rakefile +72 -0
  4. data/TODO.rdoc +19 -0
  5. data/lib/mail.rb +113 -0
  6. data/lib/mail/attachments_list.rb +76 -0
  7. data/lib/mail/body.rb +243 -0
  8. data/lib/mail/configuration.rb +69 -0
  9. data/lib/mail/core_extensions/nil.rb +11 -0
  10. data/lib/mail/core_extensions/string.rb +19 -0
  11. data/lib/mail/elements/address.rb +306 -0
  12. data/lib/mail/elements/address_list.rb +74 -0
  13. data/lib/mail/elements/content_disposition_element.rb +30 -0
  14. data/lib/mail/elements/content_location_element.rb +25 -0
  15. data/lib/mail/elements/content_transfer_encoding_element.rb +21 -0
  16. data/lib/mail/elements/content_type_element.rb +35 -0
  17. data/lib/mail/elements/date_time_element.rb +26 -0
  18. data/lib/mail/elements/envelope_from_element.rb +34 -0
  19. data/lib/mail/elements/message_ids_element.rb +29 -0
  20. data/lib/mail/elements/mime_version_element.rb +26 -0
  21. data/lib/mail/elements/phrase_list.rb +21 -0
  22. data/lib/mail/elements/received_element.rb +30 -0
  23. data/lib/mail/encodings/base64.rb +18 -0
  24. data/lib/mail/encodings/encodings.rb +201 -0
  25. data/lib/mail/encodings/quoted_printable.rb +26 -0
  26. data/lib/mail/envelope.rb +35 -0
  27. data/lib/mail/field.rb +219 -0
  28. data/lib/mail/field_list.rb +33 -0
  29. data/lib/mail/fields/bcc_field.rb +53 -0
  30. data/lib/mail/fields/cc_field.rb +52 -0
  31. data/lib/mail/fields/comments_field.rb +41 -0
  32. data/lib/mail/fields/common/address_container.rb +16 -0
  33. data/lib/mail/fields/common/common_address.rb +128 -0
  34. data/lib/mail/fields/common/common_date.rb +51 -0
  35. data/lib/mail/fields/common/common_field.rb +64 -0
  36. data/lib/mail/fields/common/common_message_id.rb +57 -0
  37. data/lib/mail/fields/common/parameter_hash.rb +39 -0
  38. data/lib/mail/fields/content_description_field.rb +19 -0
  39. data/lib/mail/fields/content_disposition_field.rb +60 -0
  40. data/lib/mail/fields/content_id_field.rb +63 -0
  41. data/lib/mail/fields/content_location_field.rb +42 -0
  42. data/lib/mail/fields/content_transfer_encoding_field.rb +45 -0
  43. data/lib/mail/fields/content_type_field.rb +175 -0
  44. data/lib/mail/fields/date_field.rb +53 -0
  45. data/lib/mail/fields/from_field.rb +53 -0
  46. data/lib/mail/fields/in_reply_to_field.rb +52 -0
  47. data/lib/mail/fields/keywords_field.rb +43 -0
  48. data/lib/mail/fields/message_id_field.rb +80 -0
  49. data/lib/mail/fields/mime_version_field.rb +54 -0
  50. data/lib/mail/fields/optional_field.rb +11 -0
  51. data/lib/mail/fields/received_field.rb +62 -0
  52. data/lib/mail/fields/references_field.rb +53 -0
  53. data/lib/mail/fields/reply_to_field.rb +53 -0
  54. data/lib/mail/fields/resent_bcc_field.rb +53 -0
  55. data/lib/mail/fields/resent_cc_field.rb +53 -0
  56. data/lib/mail/fields/resent_date_field.rb +33 -0
  57. data/lib/mail/fields/resent_from_field.rb +53 -0
  58. data/lib/mail/fields/resent_message_id_field.rb +32 -0
  59. data/lib/mail/fields/resent_sender_field.rb +60 -0
  60. data/lib/mail/fields/resent_to_field.rb +53 -0
  61. data/lib/mail/fields/return_path_field.rb +62 -0
  62. data/lib/mail/fields/sender_field.rb +65 -0
  63. data/lib/mail/fields/structured_field.rb +36 -0
  64. data/lib/mail/fields/subject_field.rb +15 -0
  65. data/lib/mail/fields/to_field.rb +53 -0
  66. data/lib/mail/fields/unstructured_field.rb +117 -0
  67. data/lib/mail/header.rb +235 -0
  68. data/lib/mail/mail.rb +194 -0
  69. data/lib/mail/message.rb +1780 -0
  70. data/lib/mail/network/delivery_methods/file_delivery.rb +40 -0
  71. data/lib/mail/network/delivery_methods/sendmail.rb +62 -0
  72. data/lib/mail/network/delivery_methods/smtp.rb +110 -0
  73. data/lib/mail/network/delivery_methods/test_mailer.rb +40 -0
  74. data/lib/mail/network/retriever_methods/imap.rb +31 -0
  75. data/lib/mail/network/retriever_methods/pop3.rb +149 -0
  76. data/lib/mail/parsers/address_lists.rb +61 -0
  77. data/lib/mail/parsers/address_lists.treetop +19 -0
  78. data/lib/mail/parsers/content_disposition.rb +369 -0
  79. data/lib/mail/parsers/content_disposition.treetop +46 -0
  80. data/lib/mail/parsers/content_location.rb +133 -0
  81. data/lib/mail/parsers/content_location.treetop +20 -0
  82. data/lib/mail/parsers/content_transfer_encoding.rb +179 -0
  83. data/lib/mail/parsers/content_transfer_encoding.treetop +25 -0
  84. data/lib/mail/parsers/content_type.rb +512 -0
  85. data/lib/mail/parsers/content_type.treetop +58 -0
  86. data/lib/mail/parsers/date_time.rb +111 -0
  87. data/lib/mail/parsers/date_time.treetop +11 -0
  88. data/lib/mail/parsers/envelope_from.rb +188 -0
  89. data/lib/mail/parsers/envelope_from.treetop +32 -0
  90. data/lib/mail/parsers/message_ids.rb +42 -0
  91. data/lib/mail/parsers/message_ids.treetop +15 -0
  92. data/lib/mail/parsers/mime_version.rb +141 -0
  93. data/lib/mail/parsers/mime_version.treetop +19 -0
  94. data/lib/mail/parsers/phrase_lists.rb +42 -0
  95. data/lib/mail/parsers/phrase_lists.treetop +15 -0
  96. data/lib/mail/parsers/received.rb +68 -0
  97. data/lib/mail/parsers/received.treetop +11 -0
  98. data/lib/mail/parsers/rfc2045.rb +406 -0
  99. data/lib/mail/parsers/rfc2045.treetop +35 -0
  100. data/lib/mail/parsers/rfc2822.rb +5081 -0
  101. data/lib/mail/parsers/rfc2822.treetop +410 -0
  102. data/lib/mail/parsers/rfc2822_obsolete.rb +3607 -0
  103. data/lib/mail/parsers/rfc2822_obsolete.treetop +241 -0
  104. data/lib/mail/part.rb +82 -0
  105. data/lib/mail/parts_list.rb +34 -0
  106. data/lib/mail/patterns.rb +43 -0
  107. data/lib/mail/utilities.rb +163 -0
  108. data/lib/mail/vendor/treetop.rb +4 -0
  109. data/lib/mail/version.rb +10 -0
  110. data/lib/mail/version_specific/ruby_1_8.rb +84 -0
  111. data/lib/mail/version_specific/ruby_1_9.rb +77 -0
  112. data/lib/tasks/corpus.rake +125 -0
  113. data/lib/tasks/treetop.rake +10 -0
  114. metadata +188 -0
@@ -0,0 +1,42 @@
1
+ # encoding: utf-8
2
+ #
3
+ #
4
+ #
5
+ module Mail
6
+ class ContentLocationField < StructuredField
7
+
8
+ FIELD_NAME = 'content-location'
9
+ CAPITALIZED_FIELD = 'Content-Location'
10
+
11
+ def initialize(*args)
12
+ super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, args.last))
13
+ self.parse
14
+ self
15
+
16
+ end
17
+
18
+ def parse(val = value)
19
+ unless val.blank?
20
+ @element = Mail::ContentLocationElement.new(val)
21
+ end
22
+ end
23
+
24
+ def element
25
+ @element ||= Mail::ContentLocationElement.new(value)
26
+ end
27
+
28
+ def location
29
+ element.location
30
+ end
31
+
32
+ # TODO: Fix this up
33
+ def encoded
34
+ "#{CAPITALIZED_FIELD}: #{value}\r\n"
35
+ end
36
+
37
+ def decoded
38
+ value
39
+ end
40
+
41
+ end
42
+ end
@@ -0,0 +1,45 @@
1
+ # encoding: utf-8
2
+ #
3
+ #
4
+ #
5
+ module Mail
6
+ class ContentTransferEncodingField < StructuredField
7
+
8
+ FIELD_NAME = 'content-transfer-encoding'
9
+ CAPITALIZED_FIELD = 'Content-Transfer-Encoding'
10
+
11
+ def initialize(*args)
12
+ super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, args.last.to_s.downcase))
13
+ end
14
+
15
+ def parse(val = value)
16
+ unless val.blank?
17
+ @element = Mail::ContentTransferEncodingElement.new(val)
18
+ @tree = @element.tree
19
+ end
20
+ end
21
+
22
+ def tree
23
+ @element ||= Mail::ContentTransferEncodingElement.new(value)
24
+ @tree ||= @element.tree
25
+ end
26
+
27
+ def element
28
+ @element ||= Mail::ContentTransferEncodingElement.new(value)
29
+ end
30
+
31
+ def encoding
32
+ element.encoding
33
+ end
34
+
35
+ # TODO: Fix this up
36
+ def encoded
37
+ "#{CAPITALIZED_FIELD}: #{value}\r\n"
38
+ end
39
+
40
+ def decoded
41
+ value
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,175 @@
1
+ # encoding: utf-8
2
+ #
3
+ #
4
+ #
5
+ module Mail
6
+ class ContentTypeField < StructuredField
7
+
8
+ FIELD_NAME = 'content-type'
9
+ CAPITALIZED_FIELD = 'Content-Type'
10
+
11
+ def initialize(*args)
12
+ if args.last.class == Array
13
+ @main_type = args.last[0]
14
+ @sub_type = args.last[1]
15
+ @parameters = ParameterHash.new.merge!(args.last.last)
16
+ super(CAPITALIZED_FIELD, args.last)
17
+ else
18
+ @main_type = nil
19
+ @sub_type = nil
20
+ @parameters = nil
21
+ super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, args.last))
22
+ end
23
+ self.parse
24
+ self
25
+ end
26
+
27
+ def parse(val = value)
28
+ unless val.blank?
29
+ self.value = val
30
+ @element = nil
31
+ element
32
+ end
33
+ end
34
+
35
+ def element
36
+ begin
37
+ @element ||= Mail::ContentTypeElement.new(value)
38
+ rescue
39
+ attempt_to_clean
40
+ end
41
+ end
42
+
43
+ def attempt_to_clean
44
+ # Sanitize the value, handle special cases
45
+ @element ||= Mail::ContentTypeElement.new(sanatize(value))
46
+ rescue
47
+ # All else fails, just get the mime type
48
+ @element ||= Mail::ContentTypeElement.new(get_mime_type(value))
49
+ end
50
+
51
+ def main_type
52
+ @main_type ||= element.main_type
53
+ end
54
+
55
+ def sub_type
56
+ @sub_type ||= element.sub_type
57
+ end
58
+
59
+ def string
60
+ "#{main_type}/#{sub_type}"
61
+ end
62
+
63
+ def default
64
+ decoded
65
+ end
66
+
67
+ alias :content_type :string
68
+
69
+ def parameters
70
+ unless @parameters
71
+ @parameters = ParameterHash.new
72
+ element.parameters.each { |p| @parameters.merge!(p) }
73
+ end
74
+ @parameters
75
+ end
76
+
77
+ def ContentTypeField.with_boundary(type)
78
+ new("#{type}; boundary=#{generate_boundary}")
79
+ end
80
+
81
+ def ContentTypeField.generate_boundary
82
+ "--==_mimepart_#{Mail.random_tag}"
83
+ end
84
+
85
+ def value
86
+ if @value.class == Array
87
+ "#{@main_type}/#{@sub_type}; #{stringify(parameters)}"
88
+ else
89
+ @value
90
+ end
91
+ end
92
+
93
+ def stringify(params)
94
+ params.map { |k,v| "#{k}=#{Encodings.param_encode(v)}" }.join("; ")
95
+ end
96
+
97
+ def filename
98
+ case
99
+ when parameters['filename']
100
+ @filename = parameters['filename']
101
+ when parameters['name']
102
+ @filename = parameters['name']
103
+ else
104
+ @filename = nil
105
+ end
106
+ @filename
107
+ end
108
+
109
+ # TODO: Fix this up
110
+ def encoded
111
+ "#{CAPITALIZED_FIELD}: #{content_type};\r\n\t#{parameters.encoded};\r\n"
112
+ end
113
+
114
+ def decoded
115
+ value
116
+ end
117
+
118
+ private
119
+
120
+ def method_missing(name, *args, &block)
121
+ if name.to_s =~ /([\w_]+)=/
122
+ self.parameters[$1] = args.first
123
+ @value = "#{content_type}; #{stringify(parameters)}"
124
+ else
125
+ super
126
+ end
127
+ end
128
+
129
+ # Various special cases from random emails found that I am not going to change
130
+ # the parser for
131
+ def sanatize( val )
132
+ case
133
+ when val.chomp =~ /^\s*([\w\d\-_]+)\/([\w\d\-_]+)\s*;;+(.*)$/i
134
+ # Handles 'text/plain;; format="flowed"' (double semi colon)
135
+ "#{$1}/#{$2}; #{$3}"
136
+ when val.chomp =~ /^\s*([\w\d\-_]+)\/([\w\d\-_]+)\s*;(ISO[\w\d\-_]+)$/i
137
+ # Microsoft helper:
138
+ # Handles 'mime/type;ISO-8559-1'
139
+ "#{$1}/#{$2}; charset=#{quote_atom($3)}"
140
+ when val.chomp =~ /^text;?$/i
141
+ # Handles 'text;' and 'text'
142
+ "text/plain;"
143
+ when val.chomp =~ /^(\w+);\s(.*)$/i
144
+ # Handles 'text; <parameters>'
145
+ "text/plain; #{$2}"
146
+ when val =~ /([\w\d\-_]+\/[\w\d\-_]+);\scharset="charset="(\w+)""/i
147
+ # Handles text/html; charset="charset="GB2312""
148
+ "#{$1}; charset=#{quote_atom($2)}"
149
+ when val =~ /([\w\d\-_]+\/[\w\d\-_]+);\s+(.*)/i
150
+ type = $1
151
+ # Handles misquoted param values
152
+ # e.g: application/octet-stream; name=archiveshelp1[1].htm
153
+ # and: audio/x-midi;\r\n\tname=Part .exe
154
+ params = $2.to_s.split(/\s+/)
155
+ params = params.map { |i| i.to_s.chomp.strip }
156
+ params = params.map { |i| i.split(/\s*\=\s*/) }
157
+ params = params.map { |i| "#{i[0]}=#{dquote(i[1].to_s)}" }.join('; ')
158
+ "#{type}; #{params}"
159
+ when val =~ /^\s*$/
160
+ 'text/plain'
161
+ else
162
+ ''
163
+ end
164
+ end
165
+
166
+ def get_mime_type( val )
167
+ case
168
+ when val =~ /^([\w\d\-_]+)\/([\w\d\-_]+);.+$/i
169
+ "#{$1}/#{$2}"
170
+ else
171
+ 'text/plain'
172
+ end
173
+ end
174
+ end
175
+ end
@@ -0,0 +1,53 @@
1
+ # encoding: utf-8
2
+ #
3
+ # = Date Field
4
+ #
5
+ # The Date field inherits from StructuredField and handles the Date: header
6
+ # field in the email.
7
+ #
8
+ # Sending date to a mail message will instantiate a Mail::Field object that
9
+ # has a DateField as it's field type. This includes all Mail::CommonAddress
10
+ # module instance methods.
11
+ #
12
+ # There must be excatly one Date field in an RFC2822 email.
13
+ #
14
+ # == Examples:
15
+ #
16
+ # mail = Mail.new
17
+ # mail.date = 'Mon, 24 Nov 1997 14:22:01 -0800'
18
+ # mail.date #=> #<DateTime: 211747170121/86400,-1/3,2299161>
19
+ # mail.date.to_s #=> 'Mon, 24 Nov 1997 14:22:01 -0800'
20
+ # mail[:date] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::DateField:0x180e1c4
21
+ # mail['date'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::DateField:0x180e1c4
22
+ # mail['Date'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::DateField:0x180e1c4
23
+ #
24
+ module Mail
25
+ class DateField < StructuredField
26
+
27
+ include Mail::CommonDate
28
+
29
+ FIELD_NAME = 'date'
30
+ CAPITALIZED_FIELD = "Date"
31
+
32
+ def initialize(*args)
33
+ if args.last.blank?
34
+ self.name = CAPITALIZED_FIELD
35
+ self.value = Time.now.strftime('%a, %d %b %Y %H:%M:%S %z')
36
+ self
37
+ else
38
+ value = strip_field(FIELD_NAME, args.last)
39
+ value = ::DateTime.parse(value.to_s).strftime('%a, %d %b %Y %H:%M:%S %z')
40
+ super(CAPITALIZED_FIELD, value)
41
+ end
42
+ end
43
+
44
+ def encoded
45
+ do_encode(CAPITALIZED_FIELD)
46
+ end
47
+
48
+ def decoded
49
+ do_decode
50
+ end
51
+
52
+ end
53
+ end
@@ -0,0 +1,53 @@
1
+ # encoding: utf-8
2
+ #
3
+ # = From Field
4
+ #
5
+ # The From field inherits from StructuredField and handles the From: header
6
+ # field in the email.
7
+ #
8
+ # Sending from to a mail message will instantiate a Mail::Field object that
9
+ # has a FromField as it's field type. This includes all Mail::CommonAddress
10
+ # module instance metods.
11
+ #
12
+ # Only one From field can appear in a header, though it can have multiple
13
+ # addresses and groups of addresses.
14
+ #
15
+ # == Examples:
16
+ #
17
+ # mail = Mail.new
18
+ # mail.from = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
19
+ # mail.from #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
20
+ # mail[:from] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::FromField:0x180e1c4
21
+ # mail['from'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::FromField:0x180e1c4
22
+ # mail['From'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::FromField:0x180e1c4
23
+ #
24
+ # mail[:from].encoded #=> 'from: Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net\r\n'
25
+ # mail[:from].decoded #=> 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
26
+ # mail[:from].addresses #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
27
+ # mail[:from].formatted #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
28
+ #
29
+ module Mail
30
+ class FromField < StructuredField
31
+
32
+ include Mail::CommonAddress
33
+
34
+ FIELD_NAME = 'from'
35
+ CAPITALIZED_FIELD = 'From'
36
+
37
+ def initialize(*args)
38
+ super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, args.last))
39
+ self.parse
40
+ self
41
+
42
+ end
43
+
44
+ def encoded
45
+ do_encode(CAPITALIZED_FIELD)
46
+ end
47
+
48
+ def decoded
49
+ do_decode
50
+ end
51
+
52
+ end
53
+ end
@@ -0,0 +1,52 @@
1
+ # encoding: utf-8
2
+ #
3
+ # = In-Reply-To Field
4
+ #
5
+ # The In-Reply-To field inherits from StructuredField and handles the
6
+ # In-Reply-To: header field in the email.
7
+ #
8
+ # Sending in_reply_to to a mail message will instantiate a Mail::Field object that
9
+ # has a InReplyToField as it's field type. This includes all Mail::CommonMessageId
10
+ # module instance metods.
11
+ #
12
+ # Note that, the #message_ids method will return an array of message IDs without the
13
+ # enclosing angle brackets which per RFC are not syntactically part of the message id.
14
+ #
15
+ # Only one InReplyTo field can appear in a header, though it can have multiple
16
+ # Message IDs.
17
+ #
18
+ # == Examples:
19
+ #
20
+ # mail = Mail.new
21
+ # mail.in_reply_to = '<F6E2D0B4-CC35-4A91-BA4C-C7C712B10C13@test.me.dom>'
22
+ # mail.in_reply_to #=> '<F6E2D0B4-CC35-4A91-BA4C-C7C712B10C13@test.me.dom>'
23
+ # mail[:in_reply_to] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::InReplyToField:0x180e1c4
24
+ # mail['in_reply_to'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::InReplyToField:0x180e1c4
25
+ # mail['In-Reply-To'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::InReplyToField:0x180e1c4
26
+ #
27
+ # mail[:in_reply_to].message_ids #=> ['F6E2D0B4-CC35-4A91-BA4C-C7C712B10C13@test.me.dom']
28
+ #
29
+ module Mail
30
+ class InReplyToField < StructuredField
31
+
32
+ include Mail::CommonMessageId
33
+
34
+ FIELD_NAME = 'in-reply-to'
35
+ CAPITALIZED_FIELD = 'In-Reply-To'
36
+
37
+ def initialize(*args)
38
+ super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, args.last))
39
+ self.parse
40
+ self
41
+ end
42
+
43
+ def encoded
44
+ do_encode(CAPITALIZED_FIELD)
45
+ end
46
+
47
+ def decoded
48
+ do_decode
49
+ end
50
+
51
+ end
52
+ end
@@ -0,0 +1,43 @@
1
+ # encoding: utf-8
2
+ #
3
+ # keywords = "Keywords:" phrase *("," phrase) CRLF
4
+ module Mail
5
+ class KeywordsField < StructuredField
6
+
7
+ FIELD_NAME = 'keywords'
8
+ CAPITALIZED_FIELD = 'Keywords'
9
+
10
+ def initialize(*args)
11
+ super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, args.last))
12
+ self.parse
13
+ self
14
+ end
15
+
16
+ def parse(val = value)
17
+ unless val.blank?
18
+ @phrase_list ||= PhraseList.new(value)
19
+ end
20
+ end
21
+
22
+ def phrase_list
23
+ @phrase_list ||= PhraseList.new(value)
24
+ end
25
+
26
+ def keywords
27
+ phrase_list.phrases
28
+ end
29
+
30
+ def encoded
31
+ "#{CAPITALIZED_FIELD}: #{keywords.join(', ')}\r\n"
32
+ end
33
+
34
+ def decoded
35
+ keywords.join(', ')
36
+ end
37
+
38
+ def default
39
+ keywords
40
+ end
41
+
42
+ end
43
+ end