mailparser 0.5.0.beta1 → 0.5.0.beta2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
メールアドレスのドメイン部文字列を返す。
|