mailparser 0.4.22a

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.
@@ -0,0 +1,370 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright (C) 2006-2010 TOMITA Masahiro
3
+ # mailto:tommy@tmtm.org
4
+
5
+ require "timeout"
6
+ require "mailparser/rfc2822"
7
+
8
+ unless ARGV.empty?
9
+ ARGV.each do |fname|
10
+ File.open(fname) do |f|
11
+ header = []
12
+ f.each do |line|
13
+ break if line.chomp.empty?
14
+ if line =~ /^\s/ then
15
+ header[-1] << line
16
+ else
17
+ header << line
18
+ end
19
+ end
20
+ header.each do |h|
21
+ begin
22
+ MailParser::RFC2822.parse(*h.split(/\s*:\s*/, 2))
23
+ rescue MailParser::ParseError => e
24
+ puts fname
25
+ p e
26
+ p h
27
+ end
28
+ end
29
+ end
30
+ end
31
+ exit
32
+ end
33
+
34
+ require "test/unit"
35
+
36
+ class TC_RFC2822Parser < Test::Unit::TestCase
37
+ def setup()
38
+ @p = MailParser::RFC2822::Parser.new()
39
+ end
40
+ def teardown()
41
+ end
42
+
43
+ def test_mailbox_list()
44
+ m = @p.parse(:MAILBOX_LIST, "a@b.c")
45
+ assert_equal(1, m.size)
46
+ assert_kind_of(MailParser::RFC2822::Mailbox, m[0])
47
+ assert_equal("<a@b.c>", m[0].to_s)
48
+ m = @p.parse(:MAILBOX_LIST, "hoge <a@b.c>")
49
+ assert_equal(1, m.size)
50
+ assert_kind_of(MailParser::RFC2822::Mailbox, m[0])
51
+ assert_equal("hoge <a@b.c>", m[0].to_s)
52
+ m = @p.parse(:MAILBOX_LIST, "hoge <a@b.c>, d@e.f")
53
+ assert_equal(2, m.size)
54
+ assert_kind_of(MailParser::RFC2822::Mailbox, m[0])
55
+ assert_kind_of(MailParser::RFC2822::Mailbox, m[1])
56
+ assert_equal("hoge <a@b.c>", m[0].to_s)
57
+ assert_equal("<d@e.f>", m[1].to_s)
58
+ m = @p.parse(:MAILBOX_LIST, "hoge <a@b.c> (xx), (yy) d@e.f (zz)")
59
+ assert_equal(2, m.size)
60
+ assert_kind_of(MailParser::RFC2822::Mailbox, m[0])
61
+ assert_kind_of(MailParser::RFC2822::Mailbox, m[1])
62
+ assert_equal("hoge <a@b.c>", m[0].to_s)
63
+ assert_equal("<d@e.f>", m[1].to_s)
64
+ assert_equal(["(xx)", "(yy)", "(zz)"], m.comments)
65
+ assert_equal(["(xx)"], m[0].comments)
66
+ assert_equal(["(yy)", "(zz)"], m[1].comments)
67
+ end
68
+
69
+ def test_mailbox()
70
+ m = @p.parse(:MAILBOX, "a@b.c")
71
+ assert_equal("", m.display_name.to_s)
72
+ assert_equal("a@b.c", m.addr_spec.to_s)
73
+ m = @p.parse(:MAILBOX, "hoge <a@b.c>")
74
+ assert_equal("hoge", m.display_name.to_s)
75
+ assert_equal("a@b.c", m.addr_spec.to_s)
76
+ m = @p.parse(:MAILBOX, "hoge fuga <a@b.c>")
77
+ assert_equal("hoge fuga", m.display_name)
78
+ assert_equal("a@b.c", m.addr_spec.to_s)
79
+ m = @p.parse(:MAILBOX, "a@b.c (xxx)")
80
+ assert_equal("", m.display_name.to_s)
81
+ assert_equal("a@b.c", m.addr_spec.to_s)
82
+ assert_equal(["(xxx)"], m.comments)
83
+ m = @p.parse(:MAILBOX, "hoge (yyy) fuga <a@b.c>")
84
+ assert_equal("hoge fuga", m.display_name.to_s)
85
+ assert_equal("a@b.c", m.addr_spec.to_s)
86
+ assert_equal(["(yyy)"], m.comments)
87
+ m = @p.parse(:MAILBOX, "=?us-ascii?q?abc?= <a@b.c>")
88
+ assert_equal("=?us-ascii?q?abc?=", m.display_name.to_s)
89
+ assert_equal("a@b.c", m.addr_spec.to_s)
90
+
91
+ m = MailParser::RFC2822::Parser.new(:decode_mime_header=>true).parse(:MAILBOX, "=?euc-jp?q?=A4=A2=A4=A4?= <a@b.c>")
92
+ assert_equal("\xA4\xA2\xA4\xA4", m.display_name.to_s)
93
+ assert_equal("a@b.c", m.addr_spec.to_s)
94
+
95
+ m = MailParser::RFC2822::Parser.new(:decode_mime_header=>true, :output_charset=>"utf-8").parse(:MAILBOX, "=?euc-jp?q?=A4=A2=A4=A4?= <a@b.c>")
96
+ assert_equal("あい", m.display_name.to_s)
97
+ assert_equal("a@b.c", m.addr_spec.to_s)
98
+
99
+ m = MailParser::RFC2822::Parser.new(:decode_mime_header=>true, :output_charset=>"utf-8", :charset_converter=>proc{"abc"}).parse(:MAILBOX, "=?euc-jp?q?=A4=A2=A4=A4?= <a@b.c>")
100
+ assert_equal("abc", m.display_name.to_s)
101
+ assert_equal("a@b.c", m.addr_spec.to_s)
102
+ end
103
+
104
+ def test_address_list()
105
+ a = @p.parse(:ADDRESS_LIST, "a@b.c")
106
+ assert_equal(1, a.size)
107
+ assert_kind_of(MailParser::RFC2822::Mailbox, a[0])
108
+ assert_equal("<a@b.c>", a[0].to_s)
109
+ a = @p.parse(:ADDRESS_LIST, "group:;, hoge <a@b.c>")
110
+ assert_equal(2, a.size)
111
+ assert_kind_of(MailParser::RFC2822::Group, a[0])
112
+ assert_kind_of(MailParser::RFC2822::Mailbox, a[1])
113
+ assert_equal("group:;", a[0].to_s)
114
+ assert_equal("hoge <a@b.c>", a[1].to_s)
115
+ a = @p.parse(:ADDRESS_LIST, "group: a@b.c, hoge <d@e.f>;")
116
+ assert_equal(1, a.size)
117
+ assert_kind_of(MailParser::RFC2822::Group, a[0])
118
+ assert_kind_of(Array, a[0].mailbox_list)
119
+ assert_equal(2, a[0].mailbox_list.size)
120
+ assert_kind_of(MailParser::RFC2822::Mailbox, a[0].mailbox_list[0])
121
+ assert_kind_of(MailParser::RFC2822::Mailbox, a[0].mailbox_list[1])
122
+ assert_equal("group:<a@b.c>,hoge <d@e.f>;", a[0].to_s)
123
+ a = @p.parse(:ADDRESS_LIST, "hoge <a@b.c> (xx), (yy) d@e.f (zz)")
124
+ assert_equal(2, a.size)
125
+ assert_kind_of(MailParser::RFC2822::Mailbox, a[0])
126
+ assert_kind_of(MailParser::RFC2822::Mailbox, a[1])
127
+ assert_equal("hoge <a@b.c>", a[0].to_s)
128
+ assert_equal("<d@e.f>", a[1].to_s)
129
+ assert_equal(["(xx)", "(yy)", "(zz)"], a.comments)
130
+ assert_equal(["(xx)"], a[0].comments)
131
+ assert_equal(["(yy)", "(zz)"], a[1].comments)
132
+ a = @p.parse(:ADDRESS_LIST, "(aa) group: (bb) a@b.c(cc), hoge <d@e.f>;, g@h.i (dd)")
133
+ assert_equal(2, a.size)
134
+ assert_kind_of(MailParser::RFC2822::Group, a[0])
135
+ assert_kind_of(Array, a[0].mailbox_list)
136
+ assert_equal(2, a[0].mailbox_list.size)
137
+ assert_kind_of(MailParser::RFC2822::Mailbox, a[0].mailbox_list[0])
138
+ assert_kind_of(MailParser::RFC2822::Mailbox, a[0].mailbox_list[1])
139
+ assert_equal("group:<a@b.c>,hoge <d@e.f>;", a[0].to_s)
140
+ assert_kind_of(MailParser::RFC2822::Mailbox, a[1])
141
+ assert_equal(["(aa)", "(bb)", "(cc)", "(dd)"], a.comments)
142
+ assert_equal(["(aa)", "(bb)", "(cc)"], a[0].mailbox_list[0].comments)
143
+ assert_equal([], a[0].mailbox_list[1].comments)
144
+ assert_equal(["(dd)"], a[1].comments)
145
+ a = @p.parse(:ADDRESS_LIST, "group:,<a@b.c>;")
146
+ assert_equal(1, a.size)
147
+ assert_kind_of(MailParser::RFC2822::Group, a[0])
148
+ assert_equal(1, a[0].mailbox_list.size)
149
+ assert_kind_of(MailParser::RFC2822::Mailbox, a[0].mailbox_list[0])
150
+ end
151
+
152
+ def test_address_list_bcc()
153
+ a = @p.parse(:ADDRESS_LIST_BCC, "")
154
+ assert_equal(0, a.size)
155
+ a = @p.parse(:ADDRESS_LIST_BCC, "a@b.c")
156
+ assert_equal(1, a.size)
157
+ assert_kind_of(MailParser::RFC2822::Mailbox, a[0])
158
+ assert_equal("<a@b.c>", a[0].to_s)
159
+ a = @p.parse(:ADDRESS_LIST_BCC, "group:;, hoge <a@b.c>")
160
+ assert_equal(2, a.size)
161
+ assert_kind_of(MailParser::RFC2822::Group, a[0])
162
+ assert_kind_of(MailParser::RFC2822::Mailbox, a[1])
163
+ assert_equal("group:;", a[0].to_s)
164
+ assert_equal("hoge <a@b.c>", a[1].to_s)
165
+ a = @p.parse(:ADDRESS_LIST_BCC, "group: a@b.c, hoge <d@e.f>;")
166
+ assert_equal(1, a.size)
167
+ assert_kind_of(MailParser::RFC2822::Group, a[0])
168
+ assert_kind_of(Array, a[0].mailbox_list)
169
+ assert_equal(2, a[0].mailbox_list.size)
170
+ assert_kind_of(MailParser::RFC2822::Mailbox, a[0].mailbox_list[0])
171
+ assert_kind_of(MailParser::RFC2822::Mailbox, a[0].mailbox_list[1])
172
+ assert_equal("group:<a@b.c>,hoge <d@e.f>;", a[0].to_s)
173
+ end
174
+
175
+ def test_many_address_list()
176
+ Timeout.timeout 5 do
177
+ @p.parse(:ADDRESS_LIST, (["foo bar <hoge@example.com>"]*1000).join(","))
178
+ end
179
+ end
180
+
181
+ def test_msg_id()
182
+ m = @p.parse(:MSG_ID, "<a@b.c>")
183
+ assert_kind_of(MailParser::RFC2822::MsgId, m)
184
+ assert_equal("<a@b.c>", m.to_s)
185
+ end
186
+
187
+ def test_msg_id_list()
188
+ m = @p.parse(:MSG_ID_LIST, "<a@b.c>")
189
+ assert_equal(1, m.size)
190
+ assert_kind_of(MailParser::RFC2822::MsgId, m[0])
191
+ assert_equal("<a@b.c>", m.to_s)
192
+ m = @p.parse(:MSG_ID_LIST, "<a@b.c> <d@e.f>")
193
+ assert_kind_of(Array, m)
194
+ assert_equal(2, m.size)
195
+ assert_equal("a@b.c", m[0].msg_id)
196
+ assert_equal("d@e.f", m[1].msg_id)
197
+ assert_equal("<a@b.c> <d@e.f>", m.to_s)
198
+ end
199
+
200
+ def test_phrase_msg_id_list()
201
+ m = @p.parse(:PHRASE_MSG_ID_LIST, "<a@b.c>")
202
+ assert_equal(1, m.size)
203
+ assert_kind_of(MailParser::RFC2822::MsgId, m[0])
204
+ assert_equal("<a@b.c>", m.to_s)
205
+ m = @p.parse(:PHRASE_MSG_ID_LIST, "<a@b.c> <d@e.f>")
206
+ assert_kind_of(Array, m)
207
+ assert_equal(2, m.size)
208
+ assert_equal("a@b.c", m[0].msg_id)
209
+ assert_equal("d@e.f", m[1].msg_id)
210
+ assert_equal("<a@b.c> <d@e.f>", m.to_s)
211
+ end
212
+
213
+ def test_phrase_list()
214
+ p = @p.parse(:PHRASE_LIST, "hoge")
215
+ assert_kind_of(Array, p)
216
+ assert_equal(["hoge"], p)
217
+ p = @p.parse(:PHRASE_LIST, "hoge,fuga hage")
218
+ assert_equal(2, p.size)
219
+ assert_equal("hoge", p[0])
220
+ assert_equal("fuga hage", p[1])
221
+ end
222
+
223
+ def test_date_time_jst()
224
+ d = @p.parse(:DATE_TIME, "25 Sep 2006 01:23:56 +0900")
225
+ assert_equal(2006, d.year)
226
+ assert_equal(9, d.month)
227
+ assert_equal(25, d.day)
228
+ assert_equal(1, d.hour)
229
+ assert_equal(23, d.min)
230
+ assert_equal(56, d.sec)
231
+ assert_equal("+0900", d.zone)
232
+ assert_equal(Time.utc(2006,9,24,16,23,56), d.time)
233
+ end
234
+
235
+ def test_date_time_edt()
236
+ d = @p.parse(:DATE_TIME, "25 Sep 2006 01:23:56 -0400")
237
+ assert_equal(2006, d.year)
238
+ assert_equal(9, d.month)
239
+ assert_equal(25, d.day)
240
+ assert_equal(1, d.hour)
241
+ assert_equal(23, d.min)
242
+ assert_equal(56, d.sec)
243
+ assert_equal("-0400", d.zone)
244
+ assert_equal(Time.utc(2006,9,25,5,23,56), d.time)
245
+ end
246
+
247
+ def test_date_time_invalid()
248
+ assert_raises(MailParser::ParseError){@p.parse(:DATE_TIME, "0 Jan 2007 08:19:08 +0900")}
249
+ assert_raises(MailParser::ParseError){@p.parse(:DATE_TIME, "29 Xxx 2007 08:19:08 +0900")}
250
+ assert_raises(MailParser::ParseError){@p.parse(:DATE_TIME, "29 Jan 10000 08:19:08 +0900")}
251
+ assert_raises(MailParser::ParseError){@p.parse(:DATE_TIME, "29 Jan 2007 25:19:08 +0900")}
252
+ assert_raises(MailParser::ParseError){@p.parse(:DATE_TIME, "29 Jan 2007 08:60:08 +0900")}
253
+ assert_raises(MailParser::ParseError){@p.parse(:DATE_TIME, "29 Jan 2007 08:19:61 +0900")}
254
+ assert_raises(MailParser::ParseError){@p.parse(:DATE_TIME, "29 Jan 2007 08:19:08 +0980")}
255
+ end
256
+
257
+ end
258
+
259
+ class TC_AddrSpec < Test::Unit::TestCase
260
+ def test_new()
261
+ a = MailParser::RFC2822::AddrSpec.new("local", "domain")
262
+ assert_equal("local", a.local_part)
263
+ assert_equal("domain", a.domain)
264
+ assert_equal("local@domain", a.to_s)
265
+ end
266
+ end
267
+
268
+ class TC_Mailbox < Test::Unit::TestCase
269
+ def test_new()
270
+ m = MailParser::RFC2822::Mailbox.new("local@domain", "phrase")
271
+ assert_equal("local@domain", m.addr_spec)
272
+ assert_equal("phrase", m.display_name)
273
+ assert_equal("phrase", m.phrase)
274
+ assert_equal("phrase <local@domain>", m.to_s)
275
+ end
276
+
277
+ def test_new_no_phrase()
278
+ m = MailParser::RFC2822::Mailbox.new("local@domain")
279
+ assert_equal("local@domain", m.addr_spec)
280
+ assert_equal("", m.display_name)
281
+ assert_equal("", m.phrase)
282
+ assert_equal("<local@domain>", m.to_s)
283
+ end
284
+ end
285
+
286
+ class TC_DateTime < Test::Unit::TestCase
287
+ def test_new_int()
288
+ d = MailParser::RFC2822::DateTime.new(2006, 9, 25, 23, 56, 10, "+0900")
289
+ assert_equal(2006, d.year)
290
+ assert_equal(9, d.month)
291
+ assert_equal(25, d.day)
292
+ assert_equal(23, d.hour)
293
+ assert_equal(56, d.min)
294
+ assert_equal(10, d.sec)
295
+ assert_equal("+0900", d.zone)
296
+ assert_equal(Time.utc(2006,9,25,14,56,10), d.time)
297
+ end
298
+
299
+ def test_new_str()
300
+ d = MailParser::RFC2822::DateTime.new("2006", "9", "25", "23", "56", "10", "+0900")
301
+ assert_equal(2006, d.year)
302
+ assert_equal(9, d.month)
303
+ assert_equal(25, d.day)
304
+ assert_equal(23, d.hour)
305
+ assert_equal(56, d.min)
306
+ assert_equal(10, d.sec)
307
+ assert_equal("+0900", d.zone)
308
+ assert_equal(Time.utc(2006,9,25,14,56,10), d.time)
309
+ end
310
+
311
+ def test_new_obsolete_zone()
312
+ d = MailParser::RFC2822::DateTime.new("2006", "9", "25", "23", "56", "10", "GMT")
313
+ assert_equal(2006, d.year)
314
+ assert_equal(9, d.month)
315
+ assert_equal(25, d.day)
316
+ assert_equal(23, d.hour)
317
+ assert_equal(56, d.min)
318
+ assert_equal(10, d.sec)
319
+ assert_equal("+0000", d.zone)
320
+ assert_equal(Time.utc(2006,9,25,23,56,10), d.time)
321
+ end
322
+
323
+ def test_new_unknown_zone()
324
+ d = MailParser::RFC2822::DateTime.new("2006", "9", "25", "23", "56", "10", "xxx")
325
+ assert_equal(2006, d.year)
326
+ assert_equal(9, d.month)
327
+ assert_equal(25, d.day)
328
+ assert_equal(23, d.hour)
329
+ assert_equal(56, d.min)
330
+ assert_equal(10, d.sec)
331
+ assert_equal("-0000", d.zone)
332
+ assert_equal(Time.utc(2006,9,25,23,56,10), d.time)
333
+ end
334
+
335
+ def test_new_month0()
336
+ assert_raises(ArgumentError){MailParser::RFC2822::DateTime.new(2008, 0, 1, 11, 22, 33, "+0900")}
337
+ end
338
+
339
+ def test_new_month13()
340
+ assert_raises(ArgumentError){MailParser::RFC2822::DateTime.new(2008, 13, 1, 11, 22, 33, "+0900")}
341
+ end
342
+
343
+ def test_new_day0()
344
+ assert_raises(ArgumentError){MailParser::RFC2822::DateTime.new(2008, 1, 0, 11, 22, 33, "+0900")}
345
+ end
346
+
347
+ def test_new_day31()
348
+ assert_raises(ArgumentError){MailParser::RFC2822::DateTime.new(2008, 2, 31, 11, 22, 33, "+0900")}
349
+ assert_raises(ArgumentError){MailParser::RFC2822::DateTime.new(2008, 4, 31, 11, 22, 33, "+0900")}
350
+ assert_raises(ArgumentError){MailParser::RFC2822::DateTime.new(2008, 6, 31, 11, 22, 33, "+0900")}
351
+ assert_raises(ArgumentError){MailParser::RFC2822::DateTime.new(2008, 9, 31, 11, 22, 33, "+0900")}
352
+ assert_raises(ArgumentError){MailParser::RFC2822::DateTime.new(2008, 11, 31, 11, 22, 33, "+0900")}
353
+ end
354
+
355
+ def test_new_day32()
356
+ 1.upto(12) do |m|
357
+ assert_raises(ArgumentError){MailParser::RFC2822::DateTime.new(2008, m, 32, 11, 22, 33, "+0900")}
358
+ end
359
+ end
360
+
361
+ def test_new_leap()
362
+ assert_raises(ArgumentError){MailParser::RFC2822::DateTime.new(1999, 2, 29, 0, 0, 0, "+0900")}
363
+ assert_nothing_raised(){MailParser::RFC2822::DateTime.new(2000, 2, 29, 0, 0, 0, "+0900")}
364
+ assert_nothing_raised(){MailParser::RFC2822::DateTime.new(2004, 2, 29, 0, 0, 0, "+0900")}
365
+ assert_raises(ArgumentError){MailParser::RFC2822::DateTime.new(2100, 2, 29, 0, 0, 0, "+0900")}
366
+ assert_nothing_raised(){MailParser::RFC2822::DateTime.new(2008, 1, 15, 23, 59, 60, "+0900")}
367
+ assert_nothing_raised(){MailParser::RFC2822::DateTime.new(2009, 1, 1, 8, 59, 60, "+0900")}
368
+ assert_raises(ArgumentError){MailParser::RFC2822::DateTime.new(2009, 1, 1, 8, 59, 61, "+0900")}
369
+ end
370
+ end
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mailparser
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.22a
5
+ prerelease: 6
6
+ platform: ruby
7
+ authors:
8
+ - TOMITA Masahiro
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-12-27 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description:
15
+ email: tommy@tmtm.org
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - README.txt
21
+ - HISTORY
22
+ - lib/mailparser.rb
23
+ - lib/mailparser/rfc2183/scanner.rb
24
+ - lib/mailparser/rfc2183/parser.rb
25
+ - lib/mailparser/error.rb
26
+ - lib/mailparser/rfc2045/scanner.rb
27
+ - lib/mailparser/rfc2045/parser.rb
28
+ - lib/mailparser/rfc2047.rb
29
+ - lib/mailparser/conv_charset.rb
30
+ - lib/mailparser/loose.rb
31
+ - lib/mailparser/rfc2183.rb
32
+ - lib/mailparser/rfc2045.rb
33
+ - lib/mailparser/rfc2822.rb
34
+ - lib/mailparser/rfc2822/scanner.rb
35
+ - lib/mailparser/rfc2822/parser.rb
36
+ - lib/mailparser/obsolete.rb
37
+ - lib/mailparser/rfc2231.rb
38
+ - test.rb
39
+ - test/test_loose.rb
40
+ - test/test_rfc2231.rb
41
+ - test/test_rfc2183.rb
42
+ - test/test_rfc2047.rb
43
+ - test/test_rfc2822.rb
44
+ - test/test_obsolete.rb
45
+ - test/test_mailparser.rb
46
+ - test/test_rfc2045.rb
47
+ homepage: http://github.com/tmtm/mailparser
48
+ licenses:
49
+ - Ruby's
50
+ post_install_message:
51
+ rdoc_options: []
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ! '>='
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ required_rubygems_version: !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>'
64
+ - !ruby/object:Gem::Version
65
+ version: 1.3.1
66
+ requirements: []
67
+ rubyforge_project:
68
+ rubygems_version: 1.8.11
69
+ signing_key:
70
+ specification_version: 3
71
+ summary: Mail Parser
72
+ test_files:
73
+ - test.rb
74
+ - test/test_loose.rb
75
+ - test/test_rfc2231.rb
76
+ - test/test_rfc2183.rb
77
+ - test/test_rfc2047.rb
78
+ - test/test_rfc2822.rb
79
+ - test/test_obsolete.rb
80
+ - test/test_mailparser.rb
81
+ - test/test_rfc2045.rb