actionmailer 0.3.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of actionmailer might be problematic. Click here for more details.

Files changed (34) hide show
  1. data/CHANGELOG +3 -0
  2. data/MIT-LICENSE +21 -0
  3. data/README +102 -0
  4. data/install.rb +61 -0
  5. data/lib/action_mailer.rb +44 -0
  6. data/lib/action_mailer/base.rb +111 -0
  7. data/lib/action_mailer/mail_helper.rb +17 -0
  8. data/lib/action_mailer/vendor/text/format.rb +1447 -0
  9. data/lib/action_mailer/vendor/tmail.rb +4 -0
  10. data/lib/action_mailer/vendor/tmail/address.rb +223 -0
  11. data/lib/action_mailer/vendor/tmail/base64.rb +52 -0
  12. data/lib/action_mailer/vendor/tmail/config.rb +50 -0
  13. data/lib/action_mailer/vendor/tmail/encode.rb +447 -0
  14. data/lib/action_mailer/vendor/tmail/facade.rb +531 -0
  15. data/lib/action_mailer/vendor/tmail/header.rb +893 -0
  16. data/lib/action_mailer/vendor/tmail/info.rb +16 -0
  17. data/lib/action_mailer/vendor/tmail/loader.rb +1 -0
  18. data/lib/action_mailer/vendor/tmail/mail.rb +420 -0
  19. data/lib/action_mailer/vendor/tmail/mailbox.rb +414 -0
  20. data/lib/action_mailer/vendor/tmail/mbox.rb +1 -0
  21. data/lib/action_mailer/vendor/tmail/net.rb +261 -0
  22. data/lib/action_mailer/vendor/tmail/obsolete.rb +116 -0
  23. data/lib/action_mailer/vendor/tmail/parser.rb +1503 -0
  24. data/lib/action_mailer/vendor/tmail/port.rb +358 -0
  25. data/lib/action_mailer/vendor/tmail/scanner.rb +22 -0
  26. data/lib/action_mailer/vendor/tmail/scanner_r.rb +244 -0
  27. data/lib/action_mailer/vendor/tmail/stringio.rb +260 -0
  28. data/lib/action_mailer/vendor/tmail/tmail.rb +1 -0
  29. data/lib/action_mailer/vendor/tmail/utils.rb +215 -0
  30. data/rakefile +99 -0
  31. data/test/fixtures/templates/signed_up.rhtml +3 -0
  32. data/test/fixtures/test_mailer/signed_up.rhtml +3 -0
  33. data/test/mail_service_test.rb +53 -0
  34. metadata +86 -0
@@ -0,0 +1,893 @@
1
+ #
2
+ # header.rb
3
+ #
4
+ # Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
5
+ #
6
+ # This program is free software.
7
+ # You can distribute/modify this program under the terms of
8
+ # the GNU Lesser General Public License version 2 or later.
9
+ #
10
+
11
+ require 'tmail/encode'
12
+ require 'tmail/address'
13
+ require 'tmail/parser'
14
+ require 'tmail/config'
15
+ require 'tmail/utils'
16
+
17
+
18
+ module TMail
19
+
20
+ class HeaderField
21
+
22
+ include TextUtils
23
+
24
+ class << self
25
+
26
+ alias newobj new
27
+
28
+ def new( name, body, conf = DEFAULT_CONFIG )
29
+ klass = FNAME_TO_CLASS[name.downcase] || UnstructuredHeader
30
+ klass.newobj body, conf
31
+ end
32
+
33
+ def new_from_port( port, name, conf = DEFAULT_CONFIG )
34
+ re = Regep.new('\A(' + Regexp.quote(name) + '):', 'i')
35
+ str = nil
36
+ port.ropen {|f|
37
+ f.each do |line|
38
+ if m = re.match(line) then str = m.post_match.strip
39
+ elsif str and /\A[\t ]/ === line then str << ' ' << line.strip
40
+ elsif /\A-*\s*\z/ === line then break
41
+ elsif str then break
42
+ end
43
+ end
44
+ }
45
+ new(name, str, Config.to_config(conf))
46
+ end
47
+
48
+ def internal_new( name, conf )
49
+ FNAME_TO_CLASS[name].newobj('', conf, true)
50
+ end
51
+
52
+ end # class << self
53
+
54
+ def initialize( body, conf, intern = false )
55
+ @body = body
56
+ @config = conf
57
+
58
+ @illegal = false
59
+ @parsed = false
60
+ if intern
61
+ @parsed = true
62
+ parse_init
63
+ end
64
+ end
65
+
66
+ def inspect
67
+ "#<#{self.class} #{@body.inspect}>"
68
+ end
69
+
70
+ def illegal?
71
+ @illegal
72
+ end
73
+
74
+ def empty?
75
+ ensure_parsed
76
+ return true if @illegal
77
+ isempty?
78
+ end
79
+
80
+ private
81
+
82
+ def ensure_parsed
83
+ return if @parsed
84
+ @parsed = true
85
+ parse
86
+ end
87
+
88
+ # defabstract parse
89
+ # end
90
+
91
+ def clear_parse_status
92
+ @parsed = false
93
+ @illegal = false
94
+ end
95
+
96
+ public
97
+
98
+ def body
99
+ ensure_parsed
100
+ v = Decoder.new(s = '')
101
+ do_accept v
102
+ v.terminate
103
+ s
104
+ end
105
+
106
+ def body=( str )
107
+ @body = str
108
+ clear_parse_status
109
+ end
110
+
111
+ include StrategyInterface
112
+
113
+ def accept( strategy, dummy1 = nil, dummy2 = nil )
114
+ ensure_parsed
115
+ do_accept strategy
116
+ strategy.terminate
117
+ end
118
+
119
+ # abstract do_accept
120
+
121
+ end
122
+
123
+
124
+ class UnstructuredHeader < HeaderField
125
+
126
+ def body
127
+ ensure_parsed
128
+ @body
129
+ end
130
+
131
+ def body=( arg )
132
+ ensure_parsed
133
+ @body = arg
134
+ end
135
+
136
+ private
137
+
138
+ def parse_init
139
+ end
140
+
141
+ def parse
142
+ @body = Decoder.decode(@body.gsub(/\n|\r\n|\r/, ''))
143
+ end
144
+
145
+ def isempty?
146
+ not @body
147
+ end
148
+
149
+ def do_accept( strategy )
150
+ strategy.text @body
151
+ end
152
+
153
+ end
154
+
155
+
156
+ class StructuredHeader < HeaderField
157
+
158
+ def comments
159
+ ensure_parsed
160
+ @comments
161
+ end
162
+
163
+ private
164
+
165
+ def parse
166
+ save = nil
167
+
168
+ begin
169
+ parse_init
170
+ do_parse
171
+ rescue SyntaxError
172
+ if not save and mime_encoded? @body
173
+ save = @body
174
+ @body = Decoder.decode(save)
175
+ retry
176
+ elsif save
177
+ @body = save
178
+ end
179
+
180
+ @illegal = true
181
+ raise if @config.strict_parse?
182
+ end
183
+ end
184
+
185
+ def parse_init
186
+ @comments = []
187
+ init
188
+ end
189
+
190
+ def do_parse
191
+ obj = Parser.parse(self.class::PARSE_TYPE, @body, @comments)
192
+ set obj if obj
193
+ end
194
+
195
+ end
196
+
197
+
198
+ class DateTimeHeader < StructuredHeader
199
+
200
+ PARSE_TYPE = :DATETIME
201
+
202
+ def date
203
+ ensure_parsed
204
+ @date
205
+ end
206
+
207
+ def date=( arg )
208
+ ensure_parsed
209
+ @date = arg
210
+ end
211
+
212
+ private
213
+
214
+ def init
215
+ @date = nil
216
+ end
217
+
218
+ def set( t )
219
+ @date = t
220
+ end
221
+
222
+ def isempty?
223
+ not @date
224
+ end
225
+
226
+ def do_accept( strategy )
227
+ strategy.meta time2str(@date)
228
+ end
229
+
230
+ end
231
+
232
+
233
+ class AddressHeader < StructuredHeader
234
+
235
+ PARSE_TYPE = :MADDRESS
236
+
237
+ def addrs
238
+ ensure_parsed
239
+ @addrs
240
+ end
241
+
242
+ private
243
+
244
+ def init
245
+ @addrs = []
246
+ end
247
+
248
+ def set( a )
249
+ @addrs = a
250
+ end
251
+
252
+ def isempty?
253
+ @addrs.empty?
254
+ end
255
+
256
+ def do_accept( strategy )
257
+ first = true
258
+ @addrs.each do |a|
259
+ if first
260
+ first = false
261
+ else
262
+ strategy.meta ','
263
+ strategy.space
264
+ end
265
+ a.accept strategy
266
+ end
267
+
268
+ @comments.each do |c|
269
+ strategy.space
270
+ strategy.meta '('
271
+ strategy.text c
272
+ strategy.meta ')'
273
+ end
274
+ end
275
+
276
+ end
277
+
278
+
279
+ class ReturnPathHeader < AddressHeader
280
+
281
+ PARSE_TYPE = :RETPATH
282
+
283
+ def addr
284
+ addrs()[0]
285
+ end
286
+
287
+ def spec
288
+ a = addr() or return nil
289
+ a.spec
290
+ end
291
+
292
+ def routes
293
+ a = addr() or return nil
294
+ a.routes
295
+ end
296
+
297
+ private
298
+
299
+ def do_accept( strategy )
300
+ a = addr()
301
+
302
+ strategy.meta '<'
303
+ unless a.routes.empty?
304
+ strategy.meta a.routes.map {|i| '@' + i }.join(',')
305
+ strategy.meta ':'
306
+ end
307
+ spec = a.spec
308
+ strategy.meta spec if spec
309
+ strategy.meta '>'
310
+ end
311
+
312
+ end
313
+
314
+
315
+ class SingleAddressHeader < AddressHeader
316
+
317
+ def addr
318
+ addrs()[0]
319
+ end
320
+
321
+ private
322
+
323
+ def do_accept( strategy )
324
+ a = addr()
325
+ a.accept strategy
326
+ @comments.each do |c|
327
+ strategy.space
328
+ strategy.meta '('
329
+ strategy.text c
330
+ strategy.meta ')'
331
+ end
332
+ end
333
+
334
+ end
335
+
336
+
337
+ class MessageIdHeader < StructuredHeader
338
+
339
+ def id
340
+ ensure_parsed
341
+ @id
342
+ end
343
+
344
+ def id=( arg )
345
+ ensure_parsed
346
+ @id = arg
347
+ end
348
+
349
+ private
350
+
351
+ def init
352
+ @id = nil
353
+ end
354
+
355
+ def isempty?
356
+ not @id
357
+ end
358
+
359
+ def do_parse
360
+ @id = @body.slice(MESSAGE_ID) or
361
+ raise SyntaxError, "wrong Message-ID format: #{@body}"
362
+ end
363
+
364
+ def do_accept( strategy )
365
+ strategy.meta @id
366
+ end
367
+
368
+ end
369
+
370
+
371
+ class ReferencesHeader < StructuredHeader
372
+
373
+ def refs
374
+ ensure_parsed
375
+ @refs
376
+ end
377
+
378
+ def each_id
379
+ self.refs.each do |i|
380
+ yield i if MESSAGE_ID === i
381
+ end
382
+ end
383
+
384
+ def ids
385
+ ensure_parsed
386
+ @ids
387
+ end
388
+
389
+ def each_phrase
390
+ self.refs.each do |i|
391
+ yield i unless MESSAGE_ID === i
392
+ end
393
+ end
394
+
395
+ def phrases
396
+ ret = []
397
+ each_phrase {|i| ret.push i }
398
+ ret
399
+ end
400
+
401
+ private
402
+
403
+ def init
404
+ @refs = []
405
+ @ids = []
406
+ end
407
+
408
+ def isempty?
409
+ @ids.empty?
410
+ end
411
+
412
+ def do_parse
413
+ str = @body
414
+ while m = MESSAGE_ID.match(str)
415
+ pre = m.pre_match.strip
416
+ @refs.push pre unless pre.empty?
417
+ @refs.push s = m[0]
418
+ @ids.push s
419
+ str = m.post_match
420
+ end
421
+ str = str.strip
422
+ @refs.push str unless str.empty?
423
+ end
424
+
425
+ def do_accept( strategy )
426
+ first = true
427
+ @ids.each do |i|
428
+ if first
429
+ first = false
430
+ else
431
+ strategy.space
432
+ end
433
+ strategy.meta i
434
+ end
435
+ end
436
+
437
+ end
438
+
439
+
440
+ class ReceivedHeader < StructuredHeader
441
+
442
+ PARSE_TYPE = :RECEIVED
443
+
444
+ def from
445
+ ensure_parsed
446
+ @from
447
+ end
448
+
449
+ def from=( arg )
450
+ ensure_parsed
451
+ @from = arg
452
+ end
453
+
454
+ def by
455
+ ensure_parsed
456
+ @by
457
+ end
458
+
459
+ def by=( arg )
460
+ ensure_parsed
461
+ @by = arg
462
+ end
463
+
464
+ def via
465
+ ensure_parsed
466
+ @via
467
+ end
468
+
469
+ def via=( arg )
470
+ ensure_parsed
471
+ @via = arg
472
+ end
473
+
474
+ def with
475
+ ensure_parsed
476
+ @with
477
+ end
478
+
479
+ def id
480
+ ensure_parsed
481
+ @id
482
+ end
483
+
484
+ def id=( arg )
485
+ ensure_parsed
486
+ @id = arg
487
+ end
488
+
489
+ def _for
490
+ ensure_parsed
491
+ @_for
492
+ end
493
+
494
+ def _for=( arg )
495
+ ensure_parsed
496
+ @_for = arg
497
+ end
498
+
499
+ def date
500
+ ensure_parsed
501
+ @date
502
+ end
503
+
504
+ def date=( arg )
505
+ ensure_parsed
506
+ @date = arg
507
+ end
508
+
509
+ private
510
+
511
+ def init
512
+ @from = @by = @via = @with = @id = @_for = nil
513
+ @with = []
514
+ @date = nil
515
+ end
516
+
517
+ def set( args )
518
+ @from, @by, @via, @with, @id, @_for, @date = *args
519
+ end
520
+
521
+ def isempty?
522
+ @with.empty? and not (@from or @by or @via or @id or @_for or @date)
523
+ end
524
+
525
+ def do_accept( strategy )
526
+ list = []
527
+ list.push 'from ' + @from if @from
528
+ list.push 'by ' + @by if @by
529
+ list.push 'via ' + @via if @via
530
+ @with.each do |i|
531
+ list.push 'with ' + i
532
+ end
533
+ list.push 'id ' + @id if @id
534
+ list.push 'for <' + @_for + '>' if @_for
535
+
536
+ first = true
537
+ list.each do |i|
538
+ strategy.space unless first
539
+ strategy.meta i
540
+ first = false
541
+ end
542
+ if @date
543
+ strategy.meta ';'
544
+ strategy.space
545
+ strategy.meta time2str(@date)
546
+ end
547
+ end
548
+
549
+ end
550
+
551
+
552
+ class KeywordsHeader < StructuredHeader
553
+
554
+ PARSE_TYPE = :KEYWORDS
555
+
556
+ def keys
557
+ ensure_parsed
558
+ @keys
559
+ end
560
+
561
+ private
562
+
563
+ def init
564
+ @keys = []
565
+ end
566
+
567
+ def set( a )
568
+ @keys = a
569
+ end
570
+
571
+ def isempty?
572
+ @keys.empty?
573
+ end
574
+
575
+ def do_accept( strategy )
576
+ first = true
577
+ @keys.each do |i|
578
+ if first
579
+ first = false
580
+ else
581
+ strategy.meta ','
582
+ end
583
+ strategy.meta i
584
+ end
585
+ end
586
+
587
+ end
588
+
589
+
590
+ class EncryptedHeader < StructuredHeader
591
+
592
+ PARSE_TYPE = :ENCRYPTED
593
+
594
+ def encrypter
595
+ ensure_parsed
596
+ @encrypter
597
+ end
598
+
599
+ def encrypter=( arg )
600
+ ensure_parsed
601
+ @encrypter = arg
602
+ end
603
+
604
+ def keyword
605
+ ensure_parsed
606
+ @keyword
607
+ end
608
+
609
+ def keyword=( arg )
610
+ ensure_parsed
611
+ @keyword = arg
612
+ end
613
+
614
+ private
615
+
616
+ def init
617
+ @encrypter = nil
618
+ @keyword = nil
619
+ end
620
+
621
+ def set( args )
622
+ @encrypter, @keyword = args
623
+ end
624
+
625
+ def isempty?
626
+ not (@encrypter or @keyword)
627
+ end
628
+
629
+ def do_accept( strategy )
630
+ if @key
631
+ strategy.meta @encrypter + ','
632
+ strategy.space
633
+ strategy.meta @keyword
634
+ else
635
+ strategy.meta @encrypter
636
+ end
637
+ end
638
+
639
+ end
640
+
641
+
642
+ class MimeVersionHeader < StructuredHeader
643
+
644
+ PARSE_TYPE = :MIMEVERSION
645
+
646
+ def major
647
+ ensure_parsed
648
+ @major
649
+ end
650
+
651
+ def major=( arg )
652
+ ensure_parsed
653
+ @major = arg
654
+ end
655
+
656
+ def minor
657
+ ensure_parsed
658
+ @minor
659
+ end
660
+
661
+ def minor=( arg )
662
+ ensure_parsed
663
+ @minor = arg
664
+ end
665
+
666
+ def version
667
+ sprintf('%d.%d', major, minor)
668
+ end
669
+
670
+ private
671
+
672
+ def init
673
+ @major = nil
674
+ @minor = nil
675
+ end
676
+
677
+ def set( args )
678
+ @major, @minor = *args
679
+ end
680
+
681
+ def isempty?
682
+ not (@major or @minor)
683
+ end
684
+
685
+ def do_accept( strategy )
686
+ strategy.meta sprintf('%d.%d', @major, @minor)
687
+ end
688
+
689
+ end
690
+
691
+
692
+ class ContentTypeHeader < StructuredHeader
693
+
694
+ PARSE_TYPE = :CTYPE
695
+
696
+ def main_type
697
+ ensure_parsed
698
+ @main
699
+ end
700
+
701
+ def main_type=( arg )
702
+ ensure_parsed
703
+ @main = arg.downcase
704
+ end
705
+
706
+ def sub_type
707
+ ensure_parsed
708
+ @sub
709
+ end
710
+
711
+ def sub_type=( arg )
712
+ ensure_parsed
713
+ @sub = arg.downcase
714
+ end
715
+
716
+ def content_type
717
+ ensure_parsed
718
+ @sub ? sprintf('%s/%s', @main, @sub) : @main
719
+ end
720
+
721
+ def params
722
+ ensure_parsed
723
+ @params
724
+ end
725
+
726
+ def []( key )
727
+ ensure_parsed
728
+ @params and @params[key]
729
+ end
730
+
731
+ def []=( key, val )
732
+ ensure_parsed
733
+ (@params ||= {})[key] = val
734
+ end
735
+
736
+ private
737
+
738
+ def init
739
+ @main = @sub = @params = nil
740
+ end
741
+
742
+ def set( args )
743
+ @main, @sub, @params = *args
744
+ end
745
+
746
+ def isempty?
747
+ not (@main or @sub)
748
+ end
749
+
750
+ def do_accept( strategy )
751
+ if @sub
752
+ strategy.meta sprintf('%s/%s', @main, @sub)
753
+ else
754
+ strategy.meta @main
755
+ end
756
+ @params.each do |k,v|
757
+ strategy.meta ';'
758
+ strategy.space
759
+ strategy.kv_pair k, v
760
+ end
761
+ end
762
+
763
+ end
764
+
765
+
766
+ class ContentTransferEncodingHeader < StructuredHeader
767
+
768
+ PARSE_TYPE = :CENCODING
769
+
770
+ def encoding
771
+ ensure_parsed
772
+ @encoding
773
+ end
774
+
775
+ def encoding=( arg )
776
+ ensure_parsed
777
+ @encoding = arg
778
+ end
779
+
780
+ private
781
+
782
+ def init
783
+ @encoding = nil
784
+ end
785
+
786
+ def set( s )
787
+ @encoding = s
788
+ end
789
+
790
+ def isempty?
791
+ not @encoding
792
+ end
793
+
794
+ def do_accept( strategy )
795
+ strategy.meta @encoding.capitalize
796
+ end
797
+
798
+ end
799
+
800
+
801
+ class ContentDispositionHeader < StructuredHeader
802
+
803
+ PARSE_TYPE = :CDISPOSITION
804
+
805
+ def disposition
806
+ ensure_parsed
807
+ @disposition
808
+ end
809
+
810
+ def disposition=( str )
811
+ ensure_parsed
812
+ @disposition = str.downcase
813
+ end
814
+
815
+ def params
816
+ ensure_parsed
817
+ @params
818
+ end
819
+
820
+ def []( key )
821
+ ensure_parsed
822
+ @params and @params[key]
823
+ end
824
+
825
+ def []=( key, val )
826
+ ensure_parsed
827
+ (@params ||= {})[key] = val
828
+ end
829
+
830
+ private
831
+
832
+ def init
833
+ @disposition = @params = nil
834
+ end
835
+
836
+ def set( args )
837
+ @disposition, @params = *args
838
+ end
839
+
840
+ def isempty?
841
+ not @disposition and (not @params or @params.empty?)
842
+ end
843
+
844
+ def do_accept( strategy )
845
+ strategy.meta @disposition
846
+ @params.each do |k,v|
847
+ strategy.meta ';'
848
+ strategy.space
849
+ strategy.kv_pair k, v
850
+ end
851
+ end
852
+
853
+ end
854
+
855
+
856
+ class HeaderField # redefine
857
+
858
+ FNAME_TO_CLASS = {
859
+ 'date' => DateTimeHeader,
860
+ 'resent-date' => DateTimeHeader,
861
+ 'to' => AddressHeader,
862
+ 'cc' => AddressHeader,
863
+ 'bcc' => AddressHeader,
864
+ 'from' => AddressHeader,
865
+ 'reply-to' => AddressHeader,
866
+ 'resent-to' => AddressHeader,
867
+ 'resent-cc' => AddressHeader,
868
+ 'resent-bcc' => AddressHeader,
869
+ 'resent-from' => AddressHeader,
870
+ 'resent-reply-to' => AddressHeader,
871
+ 'sender' => SingleAddressHeader,
872
+ 'resent-sender' => SingleAddressHeader,
873
+ 'return-path' => ReturnPathHeader,
874
+ 'message-id' => MessageIdHeader,
875
+ 'resent-message-id' => MessageIdHeader,
876
+ 'in-reply-to' => ReferencesHeader,
877
+ 'received' => ReceivedHeader,
878
+ 'references' => ReferencesHeader,
879
+ 'keywords' => KeywordsHeader,
880
+ 'encrypted' => EncryptedHeader,
881
+ 'mime-version' => MimeVersionHeader,
882
+ 'content-type' => ContentTypeHeader,
883
+ 'content-transfer-encoding' => ContentTransferEncodingHeader,
884
+ 'content-disposition' => ContentDispositionHeader,
885
+ 'content-id' => MessageIdHeader,
886
+ 'subject' => UnstructuredHeader,
887
+ 'comments' => UnstructuredHeader,
888
+ 'content-description' => UnstructuredHeader
889
+ }
890
+
891
+ end
892
+
893
+ end # module TMail