mailparser 0.5.0.beta1 → 0.5.0.beta2
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/README.md +30 -0
- data/lib/mailparser.rb +78 -73
- data/lib/mailparser/loose.rb +68 -21
- data/lib/mailparser/rfc2822/scanner.rb +11 -8
- data/test/test_mailparser.rb +17 -5
- metadata +3 -3
- data/README.txt +0 -497
data/README.md
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
MailParser
|
2
|
+
==========
|
3
|
+
|
4
|
+
MailParser is a parser for mail message.
|
5
|
+
|
6
|
+
Installation
|
7
|
+
------------
|
8
|
+
|
9
|
+
% gem install --pre mailparser
|
10
|
+
|
11
|
+
Usage
|
12
|
+
-----
|
13
|
+
|
14
|
+
require 'mailparser'
|
15
|
+
f = File.open('hoge.eml')
|
16
|
+
m = MailParser::Message.new(f, :output_charset=>'utf-8')
|
17
|
+
m.subject #=> String
|
18
|
+
m.body #=> String
|
19
|
+
m.part #=> Array of Mailparser::Message
|
20
|
+
|
21
|
+
License
|
22
|
+
-------
|
23
|
+
|
24
|
+
Ruby's
|
25
|
+
|
26
|
+
|
27
|
+
Copyright
|
28
|
+
---------
|
29
|
+
|
30
|
+
Copyright (C) 2009 TOMITA Masahiro <tommy@tmtm.org>
|
data/lib/mailparser.rb
CHANGED
@@ -15,14 +15,6 @@ require "mmapscanner"
|
|
15
15
|
require "stringio"
|
16
16
|
require "tempfile"
|
17
17
|
|
18
|
-
# メールをパースする。
|
19
|
-
#
|
20
|
-
# m = MailParser.new
|
21
|
-
# m.parse(src)
|
22
|
-
# m.header => #<MailParser::Header>
|
23
|
-
# m.body => パースされた本文文字列
|
24
|
-
# m.part => [#<Mailparser>, ...]
|
25
|
-
#
|
26
18
|
module MailParser
|
27
19
|
include RFC2045, RFC2183, RFC2822
|
28
20
|
|
@@ -57,14 +49,14 @@ module MailParser
|
|
57
49
|
"content-disposition" => RFC2183,
|
58
50
|
}
|
59
51
|
|
60
|
-
#
|
52
|
+
# Header field
|
61
53
|
class HeaderItem
|
62
|
-
# name
|
63
|
-
# raw
|
64
|
-
# opt
|
65
|
-
#
|
66
|
-
#
|
67
|
-
#
|
54
|
+
# @param [String] name header field name
|
55
|
+
# @param [String] raw header field value
|
56
|
+
# @param [Hash] opt options
|
57
|
+
# @option opt [Boolean] :decode_mime_header (false) decode MIME header
|
58
|
+
# @option opt [String] :output_charset (nil) output encoding name
|
59
|
+
# @option opt [Boolean] :strict (false) raise ParseError exception when message is invalid
|
68
60
|
def initialize(name, raw, opt={})
|
69
61
|
@name = name
|
70
62
|
@raw = raw
|
@@ -72,9 +64,10 @@ module MailParser
|
|
72
64
|
@opt = opt
|
73
65
|
end
|
74
66
|
|
67
|
+
# @return [String] raw field value
|
75
68
|
attr_reader :raw
|
76
69
|
|
77
|
-
#
|
70
|
+
# @return [header field value] parsed header object
|
78
71
|
def parse()
|
79
72
|
return @parsed if @parsed
|
80
73
|
if HEADER_PARSER.key? @name then
|
@@ -107,8 +100,12 @@ module MailParser
|
|
107
100
|
end
|
108
101
|
end
|
109
102
|
|
110
|
-
#
|
103
|
+
# Header part
|
111
104
|
class Header
|
105
|
+
# @param [Hash] opt options
|
106
|
+
# @option opt [Boolean] :decode_mime_header decode MIME header
|
107
|
+
# @option opt [String] :output_charset output encoding name
|
108
|
+
# @option opt [Boolean] :strict raise ParseError exception when message is invalid
|
112
109
|
def initialize(opt={})
|
113
110
|
@hash = {}
|
114
111
|
@parsed = {}
|
@@ -116,17 +113,19 @@ module MailParser
|
|
116
113
|
@opt = opt
|
117
114
|
end
|
118
115
|
|
119
|
-
#
|
120
|
-
# name
|
121
|
-
# body
|
116
|
+
# add header field
|
117
|
+
# @param [String] name header field name
|
118
|
+
# @param [String] body header field value
|
119
|
+
# @return [void]
|
122
120
|
def add(name, body)
|
123
121
|
name = name.downcase
|
124
122
|
@hash[name] = [] unless @hash.key? name
|
125
123
|
@hash[name] << HeaderItem.new(name, body, @opt)
|
126
124
|
end
|
127
125
|
|
128
|
-
#
|
129
|
-
# name
|
126
|
+
# header field value
|
127
|
+
# @param [String] name header field name
|
128
|
+
# @return [Array<header field value>]
|
130
129
|
def [](name)
|
131
130
|
return nil unless @hash.key? name
|
132
131
|
return @parsed[name] if @parsed.key? name
|
@@ -134,8 +133,8 @@ module MailParser
|
|
134
133
|
return @parsed[name]
|
135
134
|
end
|
136
135
|
|
137
|
-
#
|
138
|
-
#
|
136
|
+
# @param [String] name header field name
|
137
|
+
# @return [Array<String>] raw header value
|
139
138
|
def raw(name)
|
140
139
|
return nil unless @hash.key? name
|
141
140
|
return @raw[name] if @raw.key? name
|
@@ -143,18 +142,22 @@ module MailParser
|
|
143
142
|
return @raw[name]
|
144
143
|
end
|
145
144
|
|
146
|
-
#
|
145
|
+
# @return [Array<String>] header names
|
147
146
|
def keys()
|
148
147
|
return @hash.keys
|
149
148
|
end
|
150
149
|
|
151
|
-
#
|
150
|
+
# @param [String] name header field name
|
151
|
+
# @return [Boolean] true if header field exists
|
152
152
|
def key?(name)
|
153
153
|
return @hash.key?(name)
|
154
154
|
end
|
155
155
|
|
156
|
-
#
|
157
|
-
#
|
156
|
+
# repeat block for each header field
|
157
|
+
# @yield [key, value]
|
158
|
+
# @yieldparam [String] key header field name
|
159
|
+
# @yieldparam [header field value] value header field value
|
160
|
+
# @return [void]
|
158
161
|
def each()
|
159
162
|
@hash.each do |k, v|
|
160
163
|
yield k, self[k]
|
@@ -162,17 +165,22 @@ module MailParser
|
|
162
165
|
end
|
163
166
|
end
|
164
167
|
|
165
|
-
#
|
168
|
+
# Mail message
|
169
|
+
# @example
|
170
|
+
# require 'mailparser'
|
171
|
+
# f = File.open('hoge.eml')
|
172
|
+
# m = MailParser::Message.new(f, :output_charset=>'utf-8')
|
173
|
+
# m.subject #=> String
|
174
|
+
# m.body #=> String
|
175
|
+
# m.part #=> Array of Mailparser::Message
|
166
176
|
class Message
|
167
|
-
# src
|
168
|
-
#
|
169
|
-
# opt
|
170
|
-
#
|
171
|
-
#
|
172
|
-
#
|
173
|
-
#
|
174
|
-
# :strict:: RFC違反時に ParseError 例外を発生する
|
175
|
-
# :charset_converter:: 文字コード変換用 Proc または Method
|
177
|
+
# @param [String, File, MmapScanner, #read] src source object
|
178
|
+
# @param [Hash] opt options
|
179
|
+
# @option opt [Boolean] :decode_mime_header (false) decode MIME header
|
180
|
+
# @option opt [Boolean] :decode_mime_filename (false) decode MIME encoded filename
|
181
|
+
# @option opt [Boolean] :output_charset (nil) output encoding
|
182
|
+
# @option opt [Boolean] :strict (false) raise ParseError exception when message is invalid
|
183
|
+
# @option opt [Proc, Method, #call] :charset_converter (nil) charset converter. default is MailParser::ConvCharset.conv_charset
|
176
184
|
def initialize(src, opt={})
|
177
185
|
if src.is_a? String
|
178
186
|
@src = MmapScanner.new src
|
@@ -206,8 +214,12 @@ module MailParser
|
|
206
214
|
end
|
207
215
|
|
208
216
|
attr_reader :header, :part
|
217
|
+
# @!attribute [r] header
|
218
|
+
# @return [MailParser::Header]
|
219
|
+
# @!attribute [r] part
|
220
|
+
# @return [Array<MailParser::Message>]
|
209
221
|
|
210
|
-
# charset
|
222
|
+
# @return [String] message body decoded and converted charset
|
211
223
|
def body
|
212
224
|
body = body_preconv
|
213
225
|
if type == 'text' and charset and @opt[:output_charset]
|
@@ -220,9 +232,8 @@ module MailParser
|
|
220
232
|
body
|
221
233
|
end
|
222
234
|
|
223
|
-
# charset
|
235
|
+
# @return [String] message body decoded and not converted charset
|
224
236
|
def body_preconv
|
225
|
-
return '' if type == 'multipart' or type == 'message'
|
226
237
|
body = @rawbody.to_s
|
227
238
|
ret = case content_transfer_encoding
|
228
239
|
when "quoted-printable" then RFC2045.qp_decode(body)
|
@@ -236,11 +247,10 @@ module MailParser
|
|
236
247
|
ret
|
237
248
|
end
|
238
249
|
|
239
|
-
#
|
250
|
+
# @return [MailParser::Message] body type is message/*
|
251
|
+
# @return [nil] when type is not message/*
|
240
252
|
def message
|
241
|
-
|
242
|
-
return nil
|
243
|
-
end
|
253
|
+
return nil unless type == "message"
|
244
254
|
if ['7bit', '8bit'].include? content_transfer_encoding
|
245
255
|
@rawbody.pos = 0
|
246
256
|
return Message.new(@rawbody, @opt)
|
@@ -248,8 +258,8 @@ module MailParser
|
|
248
258
|
return Message.new(body_preconv, @opt)
|
249
259
|
end
|
250
260
|
|
251
|
-
#
|
252
|
-
#
|
261
|
+
# @return [MailParser::RFC2822::Mailbox] From field
|
262
|
+
# @return [nil] when From field don't exist
|
253
263
|
def from()
|
254
264
|
return @from if @from
|
255
265
|
if @header.key? "from" then
|
@@ -260,8 +270,8 @@ module MailParser
|
|
260
270
|
return @from
|
261
271
|
end
|
262
272
|
|
263
|
-
#
|
264
|
-
#
|
273
|
+
# @return [Array<MailParser::RFC2822::Mailbox>] To field
|
274
|
+
# @return [nil] when To field don't exist
|
265
275
|
def to()
|
266
276
|
return @to if @to
|
267
277
|
if @header.key? "to" then
|
@@ -272,8 +282,8 @@ module MailParser
|
|
272
282
|
return @to
|
273
283
|
end
|
274
284
|
|
275
|
-
#
|
276
|
-
#
|
285
|
+
# @return [Array<MailParser::RFC2822::Mailbox>] Cc field
|
286
|
+
# @return [nil] when Cc field don't exist
|
277
287
|
def cc()
|
278
288
|
return @cc if @cc
|
279
289
|
if @header.key? "cc" then
|
@@ -284,8 +294,7 @@ module MailParser
|
|
284
294
|
return @cc
|
285
295
|
end
|
286
296
|
|
287
|
-
# Subject
|
288
|
-
# なければ空文字
|
297
|
+
# @return [String] Subject field
|
289
298
|
def subject()
|
290
299
|
return @subject if @subject
|
291
300
|
if @header.key? "subject" then
|
@@ -296,8 +305,7 @@ module MailParser
|
|
296
305
|
return @subject
|
297
306
|
end
|
298
307
|
|
299
|
-
# Content-Type
|
300
|
-
# Content-Type がない場合は "text"
|
308
|
+
# @return [String] Content-Type main type as lower-case
|
301
309
|
def type()
|
302
310
|
return @type if @type
|
303
311
|
if @header.key? "content-type" then
|
@@ -308,8 +316,7 @@ module MailParser
|
|
308
316
|
return @type
|
309
317
|
end
|
310
318
|
|
311
|
-
# Content-Type
|
312
|
-
# Content-Type がない場合は "plain"
|
319
|
+
# @return [String] Content-Type sub type as lower-case
|
313
320
|
def subtype()
|
314
321
|
return @subtype if @subtype
|
315
322
|
if @header.key? "content-type" then
|
@@ -320,8 +327,8 @@ module MailParser
|
|
320
327
|
return @subtype
|
321
328
|
end
|
322
329
|
|
323
|
-
# Content-Type
|
324
|
-
# charset
|
330
|
+
# @return [String] Content-Type charset attribute as lower-case
|
331
|
+
# @return [nil] when charset attribute don't exist
|
325
332
|
def charset()
|
326
333
|
return @charset if @charset
|
327
334
|
if @header.key? "content-type" then
|
@@ -333,13 +340,12 @@ module MailParser
|
|
333
340
|
return @charset
|
334
341
|
end
|
335
342
|
|
336
|
-
#
|
343
|
+
# @return [Boolean] true if multipart type
|
337
344
|
def multipart?()
|
338
345
|
return type == "multipart"
|
339
346
|
end
|
340
347
|
|
341
|
-
# Content-Transfer-Encoding
|
342
|
-
# Content-Transfer-Encoding がない場合は "7bit"
|
348
|
+
# @return [String] Content-Transfer-Encoding mechanism. default is "7bit"
|
343
349
|
def content_transfer_encoding()
|
344
350
|
return @content_transfer_encoding if @content_transfer_encoding
|
345
351
|
if @header.key? "content-transfer-encoding" then
|
@@ -350,10 +356,8 @@ module MailParser
|
|
350
356
|
return @content_transfer_encoding
|
351
357
|
end
|
352
358
|
|
353
|
-
#
|
354
|
-
#
|
355
|
-
# または Content-Type の name パラメータ。
|
356
|
-
# デフォルトは nil。
|
359
|
+
# @return [String] Content-Disposition filename attribute or Content-Type name attribute
|
360
|
+
# @return [nil] when filename attribute don't exist
|
357
361
|
def filename()
|
358
362
|
return @filename if @filename
|
359
363
|
if @header.key? "content-disposition" and @header["content-disposition"][0].params.key? "filename" then
|
@@ -365,19 +369,23 @@ module MailParser
|
|
365
369
|
return @filename
|
366
370
|
end
|
367
371
|
|
368
|
-
#
|
372
|
+
# @return [String] raw message
|
369
373
|
def raw
|
370
374
|
return @src.to_s
|
371
375
|
end
|
372
376
|
|
373
|
-
#
|
377
|
+
# @return [String] raw header
|
374
378
|
def rawheader
|
375
379
|
@rawheader.to_s
|
376
380
|
end
|
377
381
|
|
382
|
+
# @return [String] raw body
|
383
|
+
def rawbody
|
384
|
+
@rawbody.to_s
|
385
|
+
end
|
386
|
+
|
378
387
|
private
|
379
388
|
|
380
|
-
# ヘッダ部をパースする
|
381
389
|
def read_header()
|
382
390
|
@rawheader = @src.scan_until(/^(?=\r?\n)|\z/)
|
383
391
|
@header = Header.new(@opt)
|
@@ -390,11 +398,10 @@ module MailParser
|
|
390
398
|
@rawheader.skip(/.*\n/) or break
|
391
399
|
end
|
392
400
|
end
|
393
|
-
@src.scan(/\r?\n/) #
|
401
|
+
@src.scan(/\r?\n/) # skip delimiter line
|
394
402
|
@rawbody = @src.rest
|
395
403
|
end
|
396
404
|
|
397
|
-
# 各パートの Message オブジェクトの配列を作成
|
398
405
|
def read_part()
|
399
406
|
return if type != "multipart" or @src.eos?
|
400
407
|
b = @header["content-type"][0].params["boundary"]
|
@@ -410,7 +417,6 @@ module MailParser
|
|
410
417
|
end
|
411
418
|
end
|
412
419
|
|
413
|
-
# uuencode のデコード
|
414
420
|
def decode_uuencode(str)
|
415
421
|
ret = ""
|
416
422
|
str.each_line do |line|
|
@@ -423,7 +429,6 @@ module MailParser
|
|
423
429
|
ret
|
424
430
|
end
|
425
431
|
|
426
|
-
# str をそのまま返す
|
427
432
|
def decode_plain(str)
|
428
433
|
str
|
429
434
|
end
|
data/lib/mailparser/loose.rb
CHANGED
@@ -40,10 +40,10 @@ module MailParser
|
|
40
40
|
}
|
41
41
|
|
42
42
|
module_function
|
43
|
-
#
|
44
|
-
#
|
45
|
-
#
|
46
|
-
#
|
43
|
+
# @param [String] hname
|
44
|
+
# @param [String] hbody
|
45
|
+
# @param [Hash] opt options
|
46
|
+
# @return [header field value]
|
47
47
|
def parse(hname, hbody, opt={})
|
48
48
|
if HEADER_PARSER.key? hname then
|
49
49
|
return method(HEADER_PARSER[hname]).call(hbody, opt)
|
@@ -57,7 +57,10 @@ module MailParser
|
|
57
57
|
end
|
58
58
|
end
|
59
59
|
|
60
|
-
# Date
|
60
|
+
# parse Date field
|
61
|
+
# @param [String] str
|
62
|
+
# @param [Hash] opt options
|
63
|
+
# @return [MailParser::RFC2822::DateTime]
|
61
64
|
def parse_date(str, opt={})
|
62
65
|
begin
|
63
66
|
t = Time.rfc2822(str) rescue Time.parse(str)
|
@@ -67,27 +70,42 @@ module MailParser
|
|
67
70
|
return RFC2822::DateTime.new(t.year, t.month, t.day, t.hour, t.min, t.sec, t.zone)
|
68
71
|
end
|
69
72
|
|
70
|
-
# From,To,Cc
|
73
|
+
# parse From, To,Cc field
|
74
|
+
# @param [String] str
|
75
|
+
# @param [Hash] opt options
|
76
|
+
# @return [Array<MailParser::RFC2822::Mailbox>]
|
71
77
|
def parse_mailbox_list(str, opt={})
|
72
78
|
mailbox_list(str, opt)
|
73
79
|
end
|
74
80
|
|
75
|
-
# Sender,Resent-Sender
|
81
|
+
# parse Sender,Resent-Sender field
|
82
|
+
# @param [String] str
|
83
|
+
# @param [Hash] opt options
|
84
|
+
# @return [MailParser::RFC2822::Mailbox]
|
76
85
|
def parse_mailbox(str, opt={})
|
77
86
|
mailbox_list(str, opt)[0]
|
78
87
|
end
|
79
88
|
|
80
|
-
# Message-Id,Resent-Message-Id
|
89
|
+
# parse Message-Id, Resent-Message-Id field
|
90
|
+
# @param [String] str
|
91
|
+
# @param [Hash] opt options
|
92
|
+
# @return [MailParser::RFC2822::MsgId]
|
81
93
|
def parse_msg_id(str, opt={})
|
82
94
|
msg_id_list(str)[0]
|
83
95
|
end
|
84
96
|
|
85
|
-
# In-Reply-To,References
|
97
|
+
# parse In-Reply-To, References field
|
98
|
+
# @param [String] str
|
99
|
+
# @param [Hash] opt options
|
100
|
+
# @return [MailParser::RFC2822::MsgIdList]
|
86
101
|
def parse_msg_id_list(str, opt={})
|
87
102
|
msg_id_list(str)
|
88
103
|
end
|
89
104
|
|
90
|
-
# Keywords
|
105
|
+
# parse Keywords field
|
106
|
+
# @param [String] str
|
107
|
+
# @param [Hash] opt options
|
108
|
+
# @return [Array<String>]
|
91
109
|
def parse_phrase_list(str, opt={})
|
92
110
|
s = split_by(Tokenizer.token(str), ",")
|
93
111
|
s.map!{|i| i.join(" ")}
|
@@ -97,12 +115,18 @@ module MailParser
|
|
97
115
|
s
|
98
116
|
end
|
99
117
|
|
100
|
-
# Return-Path
|
118
|
+
# parse Return-Path field
|
119
|
+
# @param [String] str
|
120
|
+
# @param [Hash] opt options
|
121
|
+
# @return [Array<MailParser::RFC2822::ReturnPath>]
|
101
122
|
def parse_return_path(str, opt={})
|
102
123
|
mailbox_list(str, opt)[0]
|
103
124
|
end
|
104
125
|
|
105
|
-
#
|
126
|
+
# parse Received field
|
127
|
+
# @param [String] str
|
128
|
+
# @param [Hash] opt options
|
129
|
+
# @return [MailParser::RFC2822::Received]
|
106
130
|
def parse_received(str, opt={})
|
107
131
|
a = split_by(Tokenizer.token_received(str), ";")
|
108
132
|
date = a.length > 1 ? parse_date(a.last.join(" ")) : RFC2822::DateTime.now
|
@@ -124,7 +148,10 @@ module MailParser
|
|
124
148
|
RFC2822::Received.new(name_val, date)
|
125
149
|
end
|
126
150
|
|
127
|
-
# Content-Type
|
151
|
+
# parse Content-Type field
|
152
|
+
# @param [String] str
|
153
|
+
# @param [Hash] opt options
|
154
|
+
# @return [MailParser::RFC2045::ContentType]
|
128
155
|
def parse_content_type(str, opt={})
|
129
156
|
token = split_by(Tokenizer.token(str), ";")
|
130
157
|
type, subtype = token.empty? ? nil : token.shift.join.split("/", 2)
|
@@ -140,17 +167,26 @@ module MailParser
|
|
140
167
|
RFC2045::ContentType.new(type, subtype, params)
|
141
168
|
end
|
142
169
|
|
143
|
-
# Content-Transfer-Encoding
|
170
|
+
# parse Content-Transfer-Encoding field
|
171
|
+
# @param [String] str
|
172
|
+
# @param [Hash] opt options
|
173
|
+
# @return [MailParser::RFC2045::ContentTransferEncoding]
|
144
174
|
def parse_content_transfer_encoding(str, opt={})
|
145
175
|
RFC2045::ContentTransferEncoding.new(Tokenizer.token(str).first.to_s)
|
146
176
|
end
|
147
177
|
|
148
|
-
# Mime-Version
|
178
|
+
# parse Mime-Version field
|
179
|
+
# @param [String] str
|
180
|
+
# @param [Hash] opt options
|
181
|
+
# @return [String]
|
149
182
|
def parse_mime_version(str, opt={})
|
150
183
|
Tokenizer.token(str).join
|
151
184
|
end
|
152
185
|
|
153
|
-
# Content-Disposition
|
186
|
+
# parse Content-Disposition field
|
187
|
+
# @param [String] str
|
188
|
+
# @param [Hash] opt options
|
189
|
+
# @return [MailParser::RFC2183::ContentDispositoin]
|
154
190
|
def parse_content_disposition(str, opt={})
|
155
191
|
token = split_by(Tokenizer.token(str), ";")
|
156
192
|
type = token.empty? ? '' : token.shift.join
|
@@ -162,7 +198,10 @@ module MailParser
|
|
162
198
|
RFC2183::ContentDisposition.new(type, params)
|
163
199
|
end
|
164
200
|
|
165
|
-
#
|
201
|
+
# split arry by delim
|
202
|
+
# @param [Array] array
|
203
|
+
# @param [Object] delim
|
204
|
+
# @return [Array<Array>]
|
166
205
|
def split_by(array, delim)
|
167
206
|
ret = []
|
168
207
|
a = []
|
@@ -178,7 +217,10 @@ module MailParser
|
|
178
217
|
return ret
|
179
218
|
end
|
180
219
|
|
181
|
-
# Mailbox
|
220
|
+
# parse Mailbox type field
|
221
|
+
# @param [String] str
|
222
|
+
# @param [Hash] opt options
|
223
|
+
# @return [Array<MailParser::RFC2822::Mailbox>]
|
182
224
|
def mailbox_list(str, opt)
|
183
225
|
ret = []
|
184
226
|
split_by(Tokenizer.token(str), ",").each do |m|
|
@@ -198,7 +240,9 @@ module MailParser
|
|
198
240
|
return ret
|
199
241
|
end
|
200
242
|
|
201
|
-
# MsgId
|
243
|
+
# parse MsgId type field
|
244
|
+
# @param [String] str
|
245
|
+
# @return [Array<MailParser::RFC2822::MsgId>]
|
202
246
|
def msg_id_list(str)
|
203
247
|
ret = []
|
204
248
|
flag = false
|
@@ -226,12 +270,14 @@ module MailParser
|
|
226
270
|
end
|
227
271
|
|
228
272
|
class Tokenizer < RFC2822::Scanner
|
273
|
+
# @return [String] str source string
|
229
274
|
def initialize(str)
|
230
275
|
@comments = []
|
231
276
|
@ss = StringScanner.new(str)
|
232
277
|
end
|
233
278
|
|
234
|
-
#
|
279
|
+
# tokenize
|
280
|
+
# @return [Array<String>] tokens
|
235
281
|
def token()
|
236
282
|
token = []
|
237
283
|
while @ss.rest? do
|
@@ -256,7 +302,8 @@ module MailParser
|
|
256
302
|
return token
|
257
303
|
end
|
258
304
|
|
259
|
-
# Received
|
305
|
+
# tokenize for Received field
|
306
|
+
# @return [Array<String>] tokens
|
260
307
|
def token_received()
|
261
308
|
ret = []
|
262
309
|
while @ss.rest? do
|
@@ -68,7 +68,8 @@ class MailParser::RFC2822::Scanner
|
|
68
68
|
@ss.rest
|
69
69
|
end
|
70
70
|
|
71
|
-
#
|
71
|
+
# scan from after "(" to end of comment part
|
72
|
+
# @return [String] comment
|
72
73
|
def cfws(ss)
|
73
74
|
comments = []
|
74
75
|
while true
|
@@ -81,17 +82,17 @@ class MailParser::RFC2822::Scanner
|
|
81
82
|
return comments.join
|
82
83
|
end
|
83
84
|
|
84
|
-
#
|
85
|
-
# return
|
85
|
+
# process comment part
|
86
|
+
# @return [String] comment part
|
86
87
|
def cfws_sub(ss)
|
87
88
|
ret = ""
|
88
89
|
until ss.eos? do
|
89
90
|
if ss.scan(/(\s*(\\[#{TEXT_RE}]|[#{CTEXT_RE}]))*\s*/o) then
|
90
91
|
ret << ss.matched
|
91
92
|
end
|
92
|
-
if ss.scan(/\)/) then
|
93
|
+
if ss.scan(/\)/) then # return when ")"
|
93
94
|
return ret
|
94
|
-
elsif ss.scan(/\(/) then #
|
95
|
+
elsif ss.scan(/\(/) then # recursive when "("
|
95
96
|
c = cfws_sub(ss)
|
96
97
|
break if c.nil?
|
97
98
|
ret << "(" << c << ")"
|
@@ -99,17 +100,19 @@ class MailParser::RFC2822::Scanner
|
|
99
100
|
raise MailParser::ParseError, ss.rest
|
100
101
|
end
|
101
102
|
end
|
102
|
-
#
|
103
|
+
# error when ")" don't exist
|
103
104
|
raise MailParser::ParseError, ss.rest
|
104
105
|
end
|
105
106
|
|
106
|
-
# @
|
107
|
+
# @param [Integer] s start index
|
108
|
+
# @param [Integer] e end index
|
107
109
|
def get_comment(s, e)
|
108
110
|
a = @token[s..e].select{|i| i =~ /^\s*\(/}.map{|i| i.strip}
|
109
111
|
return a
|
110
112
|
end
|
111
113
|
|
112
|
-
# @
|
114
|
+
# @param [Integer] s_id start object id
|
115
|
+
# @param [Integer] e_id end object id
|
113
116
|
def get_comment_by_id(s_id, e_id)
|
114
117
|
s = s_id ? @token_idx[s_id] : 0
|
115
118
|
e = e_id ? @token_idx[e_id] : -1
|
data/test/test_mailparser.rb
CHANGED
@@ -604,7 +604,7 @@ EOS
|
|
604
604
|
assert_equal("body2", m.part[1].body)
|
605
605
|
end
|
606
606
|
|
607
|
-
def
|
607
|
+
def test_message_type()
|
608
608
|
msg = StringIO.new(<<EOS)
|
609
609
|
From: from1@example.com
|
610
610
|
Content-Type: multipart/mixed; boundary="xxxx"
|
@@ -623,7 +623,7 @@ Content-Type: text/plain
|
|
623
623
|
body2
|
624
624
|
--xxxx--
|
625
625
|
EOS
|
626
|
-
m = MailParser::Message.new(msg
|
626
|
+
m = MailParser::Message.new(msg)
|
627
627
|
assert_equal("<from1@example.com>", m.from.to_s)
|
628
628
|
assert_equal(2, m.part.size)
|
629
629
|
assert_equal("text", m.part[0].type)
|
@@ -631,9 +631,10 @@ EOS
|
|
631
631
|
assert_equal("message", m.part[1].type)
|
632
632
|
assert_equal("<from2@example.com>", m.part[1].message.from.to_s)
|
633
633
|
assert_equal("body2", m.part[1].message.body)
|
634
|
+
assert_equal("From: from2@example.com\nContent-Type: text/plain\n\nbody2", m.part[1].body)
|
634
635
|
end
|
635
636
|
|
636
|
-
def
|
637
|
+
def test_message_type_header_only
|
637
638
|
msg = StringIO.new(<<EOS)
|
638
639
|
From: from1@example.com
|
639
640
|
Content-Type: multipart/mixed; boundary="xxxx"
|
@@ -650,7 +651,7 @@ From: from2@example.com
|
|
650
651
|
Content-Type: text/plain
|
651
652
|
--xxxx--
|
652
653
|
EOS
|
653
|
-
m = MailParser::Message.new(msg
|
654
|
+
m = MailParser::Message.new(msg)
|
654
655
|
assert_equal("<from1@example.com>", m.from.to_s)
|
655
656
|
assert_equal(2, m.part.size)
|
656
657
|
assert_equal("text", m.part[0].type)
|
@@ -710,7 +711,7 @@ hoge
|
|
710
711
|
hoge
|
711
712
|
EOS
|
712
713
|
m = MailParser::Message.new(msg)
|
713
|
-
assert_equal("", m.body)
|
714
|
+
assert_equal("hoge\nhoge\n", m.body)
|
714
715
|
assert_equal([], m.part)
|
715
716
|
end
|
716
717
|
|
@@ -858,6 +859,17 @@ EOS
|
|
858
859
|
assert_equal "From: from@example.com\r\nContent-Type: text/plain\r\n", m.rawheader
|
859
860
|
end
|
860
861
|
|
862
|
+
def test_rawbody
|
863
|
+
msg = StringIO.new(<<EOS)
|
864
|
+
From: from@example.com\r
|
865
|
+
Content-Type: text/plain\r
|
866
|
+
\r
|
867
|
+
hogehoge\r
|
868
|
+
EOS
|
869
|
+
m = MailParser::Message.new msg
|
870
|
+
assert_equal "hogehoge\r\n", m.rawbody
|
871
|
+
end
|
872
|
+
|
861
873
|
def test_raw_single_part
|
862
874
|
msg = StringIO.new(<<EOS)
|
863
875
|
From: from@example.com
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mailparser
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.0.
|
4
|
+
version: 0.5.0.beta2
|
5
5
|
prerelease: 6
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-09-
|
12
|
+
date: 2012-09-18 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: mmapscanner
|
@@ -33,7 +33,7 @@ executables: []
|
|
33
33
|
extensions: []
|
34
34
|
extra_rdoc_files: []
|
35
35
|
files:
|
36
|
-
- README.
|
36
|
+
- README.md
|
37
37
|
- lib/mailparser.rb
|
38
38
|
- lib/mailparser/rfc2183/scanner.rb
|
39
39
|
- lib/mailparser/rfc2183/parser.rb
|
data/README.txt
DELETED
@@ -1,497 +0,0 @@
|
|
1
|
-
= MailParser =
|
2
|
-
|
3
|
-
メールメッセージを解析する。
|
4
|
-
|
5
|
-
== 作者 ==
|
6
|
-
|
7
|
-
とみたまさひろ <tommy@tmtm.org>
|
8
|
-
|
9
|
-
== ライセンス ==
|
10
|
-
|
11
|
-
Ruby ライセンス http://www.ruby-lang.org/ja/LICENSE.txt と同等。
|
12
|
-
|
13
|
-
== 機能 ==
|
14
|
-
|
15
|
-
* メールファイルをパースした結果を返す。
|
16
|
-
* メール構造による例外は発生しない(例外を発生させることも可能)。
|
17
|
-
不正な構造のメッセージがあった場合は適当に処理する。
|
18
|
-
|
19
|
-
* 0.4 でイチから作りなおしたので、0.3 とは互換がない。
|
20
|
-
require "mailparser/obsolete" すれば 0.3 と同じ機能が使用可能。
|
21
|
-
|
22
|
-
== ダウンロード ==
|
23
|
-
|
24
|
-
* http://github.com/tmtm/mailparser
|
25
|
-
|
26
|
-
== インストール ==
|
27
|
-
|
28
|
-
インストールには racc が必要。
|
29
|
-
|
30
|
-
{{{
|
31
|
-
$ make
|
32
|
-
$ make test
|
33
|
-
# make install
|
34
|
-
}}}
|
35
|
-
|
36
|
-
== 使用例 ==
|
37
|
-
|
38
|
-
{{{
|
39
|
-
require "mailparser"
|
40
|
-
File.open("/tmp/hoge.eml") do |f|
|
41
|
-
m = MailParser::Message.new(f, :decode_mime_header=>true)
|
42
|
-
m.from # => From ヘッダ (MailParser::RFC2822::Mailbox)
|
43
|
-
m.to # => To ヘッダ (MailParser::RFC2822::Mailbox の配列)
|
44
|
-
m.subject # => Subject 文字列 (String)
|
45
|
-
m.body # => 本文文字列 (String)
|
46
|
-
m.charset # => 本文文字コード (String)
|
47
|
-
m.part # => 添付ファイル (MailParser::Message の配列)
|
48
|
-
m.part[0].filename # => 1つめの添付ファイルのファイル名 (String)
|
49
|
-
m.part[0].body # => 1つめの添付ファイルの本文 (String)
|
50
|
-
end
|
51
|
-
}}}
|
52
|
-
|
53
|
-
== MailParser::Message ==
|
54
|
-
|
55
|
-
=== self.new(io, [opt]) ===
|
56
|
-
|
57
|
-
io から MailParser::Message オブジェクトを生成する。
|
58
|
-
|
59
|
-
io には IO または StringIO オブジェクトを指定する。実際には1行毎の文
|
60
|
-
字列を返す gets イテレータを持ち、処理した位置を覚えていて次回実
|
61
|
-
行時に続きから実行できるオブジェクトであれば何でも良い。
|
62
|
-
String オブジェクトも指定可能。内部で StringIO が生成されて使用される。
|
63
|
-
|
64
|
-
opt は Hash オブジェクトで次の値を指定できる。
|
65
|
-
|
66
|
-
:skip_body => true
|
67
|
-
本文をスキップする。デフォルトは false。
|
68
|
-
|
69
|
-
:text_body_only => true
|
70
|
-
text/* type 以外の本文をスキップする。デフォルトは false。
|
71
|
-
|
72
|
-
:extract_message_type => true
|
73
|
-
message/* type を展開する。デフォルトは false。
|
74
|
-
|
75
|
-
:decode_mime_header => true
|
76
|
-
MIMEヘッダをデコードする。デフォルトは false。
|
77
|
-
|
78
|
-
:decode_mime_filename => true
|
79
|
-
MIME エンコードされたファイル名をデコードする。デフォルトは false。
|
80
|
-
|
81
|
-
:output_charset => charsetname
|
82
|
-
出力文字コード。デフォルトは nil で無変換。
|
83
|
-
|
84
|
-
:charset_converter => proc{|f,t,s| ...}
|
85
|
-
文字コード変換時に呼び出される Proc オブジェクト。引数は、元charset,新charset,元文字列。デフォルトは MailParser::ConvCharset.conv_charset が呼ばれる。
|
86
|
-
|
87
|
-
:strict => true
|
88
|
-
RFC違反時に ParseError 例外を発生する。デフォルトは false。
|
89
|
-
|
90
|
-
:use_file => bytes
|
91
|
-
raw が指定したサイズを超えたら、メモリではなくファイルを使う。nil 指定時は無制限にメモリを使う。デフォルトは nil。
|
92
|
-
|
93
|
-
=== from ===
|
94
|
-
|
95
|
-
From ヘッダのパース結果の最初のアドレスを MailParser::Mailbox オブジェクトで返す。
|
96
|
-
{{{
|
97
|
-
m = MailParser::Message.new(StringIO.new(<<EOS))
|
98
|
-
From: TOMITA Masahiro <tommy@tmtm.org>
|
99
|
-
EOS
|
100
|
-
m.from.display_name # => "TOMITA Masahiro"
|
101
|
-
m.from.addr_spec.to_s # => "tommy@tmtm.org"
|
102
|
-
}}}
|
103
|
-
|
104
|
-
=== to ===
|
105
|
-
|
106
|
-
To ヘッダのパース結果を MailParser::Mailbox オブジェクトの配列で返す。
|
107
|
-
{{{
|
108
|
-
m = MailParser::Message.new(StringIO.new(<<EOS))
|
109
|
-
To: TOMITA Masahiro <tommy@tmtm.org>, foo@example.com
|
110
|
-
EOS
|
111
|
-
m.to[0].to_s # => "TOMITA Masahiro <tommy@tmt.morg>"
|
112
|
-
m.to[1].to_s # => "<foo@example.com>"
|
113
|
-
}}}
|
114
|
-
|
115
|
-
=== cc ===
|
116
|
-
|
117
|
-
Cc ヘッダのパース結果を MailParser::Mailbox オブジェクトの配列で返す。
|
118
|
-
|
119
|
-
=== subject ===
|
120
|
-
|
121
|
-
Subject ヘッダの値を文字列で返す。
|
122
|
-
{{{
|
123
|
-
m = MailParser::Message.new(StringIO.new(<<EOS), :decode_mime_header=>true, :output_charset=>"utf-8")
|
124
|
-
Subject: =?iso-2022-jp?b?GyRCJEgkXyQ/JEckORsoQg==?=
|
125
|
-
EOS
|
126
|
-
m.subject # => "とみたです"
|
127
|
-
}}}
|
128
|
-
|
129
|
-
=== type ===
|
130
|
-
|
131
|
-
Content-Type ヘッダのタイプを小文字の文字列で返す。
|
132
|
-
Content-Type がない場合は "text" を返す。
|
133
|
-
{{{
|
134
|
-
m = MailParser::Message.new(StringIO.new(<<EOS), :decode_mime_header=>true, :output_charset=>"utf-8")
|
135
|
-
Content-Type: Text/Plain; charset=ISO-2022-JP
|
136
|
-
EOS
|
137
|
-
m.type # => "text"
|
138
|
-
m.subtype # => "plain"
|
139
|
-
m.charset # => "iso-2022-jp"
|
140
|
-
}}}
|
141
|
-
|
142
|
-
=== subtype ===
|
143
|
-
|
144
|
-
Content-Type ヘッダのサブタイプを小文字の文字列で返す。
|
145
|
-
Content-Type がない場合またはサブタイプがない場合は "plain" を返す。
|
146
|
-
|
147
|
-
=== charset ===
|
148
|
-
|
149
|
-
Content-Type ヘッダの charset 属性を小文字の文字列で返す。
|
150
|
-
Content-Type がない場合または charset がない場合は nil を返す。
|
151
|
-
|
152
|
-
=== multipart? ===
|
153
|
-
|
154
|
-
マルチパートメッセージの場合 true を返す。
|
155
|
-
|
156
|
-
=== filename ===
|
157
|
-
|
158
|
-
ファイル名を返す。
|
159
|
-
ファイル名は、Content-Disposition ヘッダの filename 属性または Content-Type ヘッダの name 属性から取得する(Content-Disposition ヘッダが優先)。
|
160
|
-
{{{
|
161
|
-
m = MailParser::Message.new(StringIO.new(<<EOS))
|
162
|
-
Content-Disposition: attachment; filename="hogehoge.txt"
|
163
|
-
EOS
|
164
|
-
m.filename # => "hogehoge.txt"
|
165
|
-
}}}
|
166
|
-
|
167
|
-
{{{
|
168
|
-
m = MailParser::Message.new(StringIO.new(<<EOS))
|
169
|
-
Content-Disposition: attachment; filename*=utf-8''%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E5%90%8D.txt
|
170
|
-
EOS
|
171
|
-
m.filename # => "ファイル名.txt"
|
172
|
-
}}}
|
173
|
-
|
174
|
-
RFC的には正しくない(でも一般に使われている)ダブルクォートで括られてMIMEエンコードされた文字列を MIMEデコードしたい場合は、:decode_mime_filename=>true を指定する必要がある。
|
175
|
-
|
176
|
-
{{{
|
177
|
-
m = MailParser::Message.new(StringIO.new(<<EOS), :decode_mime_filename=>true)
|
178
|
-
Content-Disposition: attachment; filename="=?utf-8?b?44OV44Kh44Kk44Or5ZCNLnR4dA==?="
|
179
|
-
EOS
|
180
|
-
m.filename # => "ファイル名"
|
181
|
-
}}}
|
182
|
-
|
183
|
-
=== header ===
|
184
|
-
|
185
|
-
ヘッダを表す MailParser::Header オブジェクトを返す。
|
186
|
-
MailParser::Header はハッシュのように使用できる。
|
187
|
-
キーは小文字のヘッダ名文字列で、値はヘッダオブジェクトの配列(複数存在する場合があるので配列)。
|
188
|
-
{{{
|
189
|
-
m = MailParser::Message.new(StringIO.new(<<EOS))
|
190
|
-
From: TOMITA Masahiro <tommy@tmtm.org>
|
191
|
-
To: foo@example.com
|
192
|
-
Subject: test subject
|
193
|
-
EOS
|
194
|
-
m.header["from"][0] # => MailParser::Mailbox の配列
|
195
|
-
m.header["to"][0] # => MailParser::Mailbox の配列
|
196
|
-
m.header["subject"][0] # => String
|
197
|
-
}}}
|
198
|
-
|
199
|
-
ヘッダとパース結果のクラスの対応は次の通り。
|
200
|
-
|
201
|
-
|| Date || MailParser::DateTime
|
202
|
-
|| From || MailParser::Mailbox の配列
|
203
|
-
|| Sender || MailParser::Mailbox
|
204
|
-
|| Reply-To || MailParser::Mailbox または MailParser::Group の配列
|
205
|
-
|| To || MailParser::Mailbox または MailParser::Group の配列
|
206
|
-
|| Cc || MailParser::Mailbox または MailParser::Group の配列
|
207
|
-
|| Bcc || MailParser::Mailbox または MailParser::Group の配列
|
208
|
-
|| Message-Id || MailParser::MsgId
|
209
|
-
|| In-Reply-To || 文字列または MailParser::MsgId の配列
|
210
|
-
|| References || 文字列または MailParser::MsgId の配列
|
211
|
-
|| Keywords || 文字列の配列
|
212
|
-
|| Resent-Date || MailParser::DateTime
|
213
|
-
|| Resent-From || MailParser::Mailbox の配列
|
214
|
-
|| Resent-To || MailParser::Mailbox または MailParser::Group の配列
|
215
|
-
|| Resent-Cc || MailParser::Mailbox または MailParser::Group の配列
|
216
|
-
|| Resent-Bcc || MailParser::Mailbox または MailParser::Group の配列
|
217
|
-
|| Resent-Message-Id || MailParser::MsgId
|
218
|
-
|| Return-Path || MailParser::Mailbox または nil (<> の場合)
|
219
|
-
|| Received || MailParser::Received
|
220
|
-
|| Content-Type || MailParser::ContentType
|
221
|
-
|| Content-Transfer-Encoding || MailParser::ContentTransferEncoding
|
222
|
-
|| Content-Id || MailParser::MsgId
|
223
|
-
|| Mime-Version || 文字列
|
224
|
-
|| Content-Disposition || MailParser::ContentDisposition
|
225
|
-
|
226
|
-
=== body ===
|
227
|
-
|
228
|
-
本文文字列を返す。
|
229
|
-
{{{
|
230
|
-
m = MailParser::Message.new(StringIO.new(<<EOS))
|
231
|
-
From: TOMITA Masahiro <tommy@tmtm.org>
|
232
|
-
Content-Type: text/plain; charset=utf-8
|
233
|
-
|
234
|
-
これは本文です。
|
235
|
-
EOS
|
236
|
-
m.body # => "これは本文です。\n"
|
237
|
-
}}}
|
238
|
-
|
239
|
-
=== body_preconv ===
|
240
|
-
|
241
|
-
本文の charset変換前文字列を返す。
|
242
|
-
{{{
|
243
|
-
m = MailParser::Message.new(<<EOS)
|
244
|
-
From: TOMITA Masahiro <tommy@tmtm.org>
|
245
|
-
Content-Type: text/plain; charset=iso-2022-jp
|
246
|
-
|
247
|
-
\e$B$3$l$OK\\J8$G$9\e(B
|
248
|
-
EOS
|
249
|
-
m.body # => "これは本文です\n"
|
250
|
-
m.body_preconv # => "\e$B$3$l$OK\\J8$G$9\e(B\n"
|
251
|
-
}}}
|
252
|
-
|
253
|
-
=== part ===
|
254
|
-
|
255
|
-
マルチパートメッセージの場合、各パートを表す MailParser::Message オブジェクトの配列を返す。
|
256
|
-
マルチパートメッセージでない場合は空配列を返す。
|
257
|
-
{{{
|
258
|
-
m = MailParser::Message.new(StringIO.new(<<EOS, :output_charset=>"utf8"))
|
259
|
-
Content-Type: multipart/mixed; boundary="abcdefg"
|
260
|
-
|
261
|
-
--abcdefg
|
262
|
-
Content-Type: text/plain
|
263
|
-
|
264
|
-
first part.
|
265
|
-
--abcdefg
|
266
|
-
Content-Type: text/plain
|
267
|
-
|
268
|
-
second part.
|
269
|
-
--abcdefg--
|
270
|
-
EOS
|
271
|
-
m.part.size # => 2
|
272
|
-
m.part[0] # => 最初のパートの MailParser::Message オブジェクト
|
273
|
-
m.part[1] # => 2番目のパートの MailParser::Message オブジェクト
|
274
|
-
}}}
|
275
|
-
|
276
|
-
=== message ===
|
277
|
-
|
278
|
-
:extract_message_type=>true で message タイプのメッセージの場合、本文が示すメッセージの Mailparser::Message オブジェクトを返す。
|
279
|
-
:extract_message_type=>false または message タイプでない場合は nil を返す。
|
280
|
-
{{{
|
281
|
-
m = MailParser::Message.new(StringIO.new(<<EOS), :extract_message_type=>true)
|
282
|
-
Content-Type: message/rfc822
|
283
|
-
|
284
|
-
Subject: message subject
|
285
|
-
|
286
|
-
message body
|
287
|
-
EOS
|
288
|
-
m.message.subject # => "message subject"
|
289
|
-
}}}
|
290
|
-
|
291
|
-
=== rawheader ===
|
292
|
-
|
293
|
-
ヘッダ部文字列をパースせずにそのまま返す。
|
294
|
-
{{{
|
295
|
-
m = MailParser::Message.new(StringIO.new(<<EOS))
|
296
|
-
From: TOMITA Masahiro <tommy@tmtm.org>
|
297
|
-
To: foo@example.com
|
298
|
-
Subject: subject
|
299
|
-
|
300
|
-
body message
|
301
|
-
EOS
|
302
|
-
m.rawheader # => "From: TOMITA Masahiro <tommy@tmtm.org>\nTo: foo@example.com\nSubject: subject\n"
|
303
|
-
}}}
|
304
|
-
|
305
|
-
=== raw ===
|
306
|
-
|
307
|
-
生メッセージ文字列を返す。
|
308
|
-
{{{
|
309
|
-
m = MailParser::Message.new(StringIO.new(<<EOS))
|
310
|
-
From: TOMITA Masahiro <tommy@tmtm.org>
|
311
|
-
To: foo@example.com
|
312
|
-
Subject: subject
|
313
|
-
|
314
|
-
body message
|
315
|
-
EOS
|
316
|
-
m.rawheader # => "From: TOMITA Masahiro <tommy@tmtm.org>\nTo: foo@example.com\nSubject: subject\n"
|
317
|
-
}}}
|
318
|
-
|
319
|
-
== MailParser::Header ==
|
320
|
-
|
321
|
-
同じ名前を持つヘッダを表すクラス。
|
322
|
-
|
323
|
-
=== add(name, body) ===
|
324
|
-
name ヘッダの値として body を追加する。
|
325
|
-
|
326
|
-
=== [](name) ===
|
327
|
-
name ヘッダの値をパースした結果オブジェクトの配列を返す。
|
328
|
-
パース結果オブジェクトは raw メソッドを持ち、パース前文字列を取り出すことができる。
|
329
|
-
|
330
|
-
=== raw(name) ===
|
331
|
-
name ヘッダの値のパース前の文字列の配列を返す。
|
332
|
-
|
333
|
-
=== keys ===
|
334
|
-
ヘッダ名文字列の一覧を返す。
|
335
|
-
|
336
|
-
=== key?(name) ===
|
337
|
-
name ヘッダがあれば真。
|
338
|
-
|
339
|
-
=== each {|n,v| } ===
|
340
|
-
各ヘッダについてブロックを繰り返す。
|
341
|
-
ブロック引数は、1番目がヘッダ名文字列、2番目がパース結果オブジェクトの配列。
|
342
|
-
|
343
|
-
== MailParser::DateTime ==
|
344
|
-
|
345
|
-
Date ヘッダを表すクラス。
|
346
|
-
|
347
|
-
=== year ===
|
348
|
-
|
349
|
-
年を表す整数。
|
350
|
-
|
351
|
-
=== month ===
|
352
|
-
|
353
|
-
月を表す整数。
|
354
|
-
|
355
|
-
=== day ===
|
356
|
-
|
357
|
-
日を表す整数。
|
358
|
-
|
359
|
-
=== hour ===
|
360
|
-
|
361
|
-
時を表す整数。
|
362
|
-
|
363
|
-
=== min ===
|
364
|
-
|
365
|
-
分を表す整数。
|
366
|
-
|
367
|
-
=== sec ===
|
368
|
-
|
369
|
-
秒を表す整数。
|
370
|
-
|
371
|
-
=== zone ===
|
372
|
-
|
373
|
-
タイムゾーンを表す文字列。「+9999」または「-9999」の形式。
|
374
|
-
|
375
|
-
=== time ===
|
376
|
-
|
377
|
-
Time オブジェクトを返す。範囲外の日付の場合は :strict が false でも ArgumentError 例外が発生するので注意。
|
378
|
-
|
379
|
-
== MailParser::Mailbox ==
|
380
|
-
|
381
|
-
メールアドレスを表すクラス。
|
382
|
-
|
383
|
-
=== addr_spec ===
|
384
|
-
|
385
|
-
メールアドレスを表す MailParser::AddrSpec を返す。
|
386
|
-
|
387
|
-
=== local_part ===
|
388
|
-
|
389
|
-
ローカルパートを表す文字列。
|
390
|
-
MailParser::Mailbox#addr_spec.local_part と同じ。
|
391
|
-
|
392
|
-
=== domain ===
|
393
|
-
|
394
|
-
ドメインを表す文字列。
|
395
|
-
addr_spec.domain と同じ。
|
396
|
-
|
397
|
-
=== display_name ===
|
398
|
-
|
399
|
-
表示名を表す文字列。
|
400
|
-
|
401
|
-
=== phrase ===
|
402
|
-
|
403
|
-
display_name と同じ。
|
404
|
-
|
405
|
-
== MailParser::Group ==
|
406
|
-
|
407
|
-
グループアドレスを表すクラス。
|
408
|
-
|
409
|
-
=== mailbox_list ===
|
410
|
-
|
411
|
-
MailParser::Mailbox の配列。
|
412
|
-
|
413
|
-
=== display_name ===
|
414
|
-
|
415
|
-
表示名を表す文字列。
|
416
|
-
|
417
|
-
=== phrase ===
|
418
|
-
|
419
|
-
display_name と同じ。
|
420
|
-
|
421
|
-
== MailParser::MsgId ==
|
422
|
-
|
423
|
-
=== msg_id ===
|
424
|
-
|
425
|
-
メッセージID文字列。先頭と末尾の < > は含まない。
|
426
|
-
|
427
|
-
== MailParser::Received ==
|
428
|
-
|
429
|
-
Received ヘッダを表すクラス。
|
430
|
-
|
431
|
-
=== name_val ===
|
432
|
-
|
433
|
-
Received ヘッダ中の名前(小文字の文字列)と値(文字列)の組を表す Hash を返す。
|
434
|
-
{{{
|
435
|
-
m = MailParser::Message.new(StringIO.new(<<EOS))
|
436
|
-
Received: from mx1.tmtm.org (localhost [127.0.0.1])
|
437
|
-
by mx2.tmtm.org (Postfix) with ESMTP id 3ED69108383
|
438
|
-
for <tommy@tmtm.org>; Tue, 16 Jan 2007 14:20:23 +0900 (JST)
|
439
|
-
EOS
|
440
|
-
r = m.header["received"][0]
|
441
|
-
r.name_val["from"] # => "mx1.tmtm.org"
|
442
|
-
r.name_val["by"] # => "mx2.tmtm.org"
|
443
|
-
r.name_val["with"] # => "ESMTP"
|
444
|
-
r.name_val["for"] # => "tommy@tmtm.org"
|
445
|
-
}}}
|
446
|
-
|
447
|
-
=== date_time ===
|
448
|
-
|
449
|
-
Received ヘッダ中の日時を表す MailParser::DateTime オブジェクトを返す。
|
450
|
-
|
451
|
-
== MailParser::ContentType ==
|
452
|
-
|
453
|
-
Content-Type ヘッダのパラメータを表すクラス。
|
454
|
-
|
455
|
-
=== type ===
|
456
|
-
|
457
|
-
Content-Type ヘッダのタイプを表す小文字の文字列を返す。
|
458
|
-
|
459
|
-
=== subtype ===
|
460
|
-
|
461
|
-
Content-Type ヘッダのサブタイプを表す小文字の文字列を返す。
|
462
|
-
|
463
|
-
=== params ===
|
464
|
-
|
465
|
-
Content-Type ヘッダのパラメータを表す Hash を返す。
|
466
|
-
Hash のキーは小文字の文字列。値は文字列。
|
467
|
-
|
468
|
-
== MailParser::ContentTransferEncoding ==
|
469
|
-
|
470
|
-
Content-Transfer-Encoding ヘッダを表すクラス。
|
471
|
-
|
472
|
-
=== mechanism ===
|
473
|
-
|
474
|
-
Content-Transfer-Encoding ヘッダの値を小文字の文字列で返す。
|
475
|
-
|
476
|
-
== MailParser::ContentDisposition ==
|
477
|
-
|
478
|
-
=== type ===
|
479
|
-
|
480
|
-
Content-Disposition ヘッダのタイプを表す小文字の文字列を返す。
|
481
|
-
|
482
|
-
=== params ===
|
483
|
-
|
484
|
-
Content-Disposition ヘッダのパラメータを表す Hash を返す。
|
485
|
-
Hash のキーは小文字の文字列。値は文字列。
|
486
|
-
|
487
|
-
== MailParser::AddrSpec ==
|
488
|
-
|
489
|
-
メールアドレスを表すクラス。
|
490
|
-
|
491
|
-
=== local_part ===
|
492
|
-
|
493
|
-
メールアドレスのローカルパート文字列を返す。
|
494
|
-
|
495
|
-
=== domain ===
|
496
|
-
|
497
|
-
メールアドレスのドメイン部文字列を返す。
|