sup 0.19.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (123) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.travis.yml +12 -0
  4. data/CONTRIBUTORS +84 -0
  5. data/Gemfile +3 -0
  6. data/HACKING +42 -0
  7. data/History.txt +361 -0
  8. data/LICENSE +280 -0
  9. data/README.md +70 -0
  10. data/Rakefile +12 -0
  11. data/ReleaseNotes +231 -0
  12. data/bin/sup +434 -0
  13. data/bin/sup-add +118 -0
  14. data/bin/sup-config +243 -0
  15. data/bin/sup-dump +43 -0
  16. data/bin/sup-import-dump +101 -0
  17. data/bin/sup-psych-ify-config-files +21 -0
  18. data/bin/sup-recover-sources +87 -0
  19. data/bin/sup-sync +210 -0
  20. data/bin/sup-sync-back-maildir +127 -0
  21. data/bin/sup-tweak-labels +140 -0
  22. data/contrib/colorpicker.rb +100 -0
  23. data/contrib/completion/_sup.zsh +114 -0
  24. data/devel/console.sh +3 -0
  25. data/devel/count-loc.sh +3 -0
  26. data/devel/load-index.rb +9 -0
  27. data/devel/profile.rb +12 -0
  28. data/devel/start-console.rb +5 -0
  29. data/doc/FAQ.txt +119 -0
  30. data/doc/Hooks.txt +79 -0
  31. data/doc/Philosophy.txt +69 -0
  32. data/lib/sup.rb +467 -0
  33. data/lib/sup/account.rb +90 -0
  34. data/lib/sup/buffer.rb +768 -0
  35. data/lib/sup/colormap.rb +239 -0
  36. data/lib/sup/contact.rb +67 -0
  37. data/lib/sup/crypto.rb +461 -0
  38. data/lib/sup/draft.rb +119 -0
  39. data/lib/sup/hook.rb +159 -0
  40. data/lib/sup/horizontal_selector.rb +59 -0
  41. data/lib/sup/idle.rb +42 -0
  42. data/lib/sup/index.rb +882 -0
  43. data/lib/sup/interactive_lock.rb +89 -0
  44. data/lib/sup/keymap.rb +140 -0
  45. data/lib/sup/label.rb +87 -0
  46. data/lib/sup/logger.rb +77 -0
  47. data/lib/sup/logger/singleton.rb +10 -0
  48. data/lib/sup/maildir.rb +257 -0
  49. data/lib/sup/mbox.rb +187 -0
  50. data/lib/sup/message.rb +803 -0
  51. data/lib/sup/message_chunks.rb +328 -0
  52. data/lib/sup/mode.rb +140 -0
  53. data/lib/sup/modes/buffer_list_mode.rb +50 -0
  54. data/lib/sup/modes/completion_mode.rb +55 -0
  55. data/lib/sup/modes/compose_mode.rb +38 -0
  56. data/lib/sup/modes/console_mode.rb +125 -0
  57. data/lib/sup/modes/contact_list_mode.rb +148 -0
  58. data/lib/sup/modes/edit_message_async_mode.rb +110 -0
  59. data/lib/sup/modes/edit_message_mode.rb +728 -0
  60. data/lib/sup/modes/file_browser_mode.rb +109 -0
  61. data/lib/sup/modes/forward_mode.rb +82 -0
  62. data/lib/sup/modes/help_mode.rb +19 -0
  63. data/lib/sup/modes/inbox_mode.rb +85 -0
  64. data/lib/sup/modes/label_list_mode.rb +138 -0
  65. data/lib/sup/modes/label_search_results_mode.rb +38 -0
  66. data/lib/sup/modes/line_cursor_mode.rb +203 -0
  67. data/lib/sup/modes/log_mode.rb +57 -0
  68. data/lib/sup/modes/person_search_results_mode.rb +12 -0
  69. data/lib/sup/modes/poll_mode.rb +19 -0
  70. data/lib/sup/modes/reply_mode.rb +228 -0
  71. data/lib/sup/modes/resume_mode.rb +52 -0
  72. data/lib/sup/modes/scroll_mode.rb +252 -0
  73. data/lib/sup/modes/search_list_mode.rb +204 -0
  74. data/lib/sup/modes/search_results_mode.rb +59 -0
  75. data/lib/sup/modes/text_mode.rb +76 -0
  76. data/lib/sup/modes/thread_index_mode.rb +1033 -0
  77. data/lib/sup/modes/thread_view_mode.rb +941 -0
  78. data/lib/sup/person.rb +134 -0
  79. data/lib/sup/poll.rb +272 -0
  80. data/lib/sup/rfc2047.rb +56 -0
  81. data/lib/sup/search.rb +110 -0
  82. data/lib/sup/sent.rb +58 -0
  83. data/lib/sup/service/label_service.rb +45 -0
  84. data/lib/sup/source.rb +244 -0
  85. data/lib/sup/tagger.rb +50 -0
  86. data/lib/sup/textfield.rb +253 -0
  87. data/lib/sup/thread.rb +452 -0
  88. data/lib/sup/time.rb +93 -0
  89. data/lib/sup/undo.rb +38 -0
  90. data/lib/sup/update.rb +30 -0
  91. data/lib/sup/util.rb +747 -0
  92. data/lib/sup/util/ncurses.rb +274 -0
  93. data/lib/sup/util/path.rb +9 -0
  94. data/lib/sup/util/query.rb +17 -0
  95. data/lib/sup/util/uri.rb +15 -0
  96. data/lib/sup/version.rb +3 -0
  97. data/sup.gemspec +53 -0
  98. data/test/dummy_source.rb +61 -0
  99. data/test/gnupg_test_home/gpg.conf +1 -0
  100. data/test/gnupg_test_home/pubring.gpg +0 -0
  101. data/test/gnupg_test_home/receiver_pubring.gpg +0 -0
  102. data/test/gnupg_test_home/receiver_secring.gpg +0 -0
  103. data/test/gnupg_test_home/receiver_trustdb.gpg +0 -0
  104. data/test/gnupg_test_home/secring.gpg +0 -0
  105. data/test/gnupg_test_home/sup-test-2@foo.bar.asc +20 -0
  106. data/test/gnupg_test_home/trustdb.gpg +0 -0
  107. data/test/integration/test_label_service.rb +18 -0
  108. data/test/messages/bad-content-transfer-encoding-1.eml +8 -0
  109. data/test/messages/binary-content-transfer-encoding-2.eml +21 -0
  110. data/test/messages/missing-line.eml +9 -0
  111. data/test/test_crypto.rb +109 -0
  112. data/test/test_header_parsing.rb +168 -0
  113. data/test/test_helper.rb +7 -0
  114. data/test/test_message.rb +532 -0
  115. data/test/test_messages_dir.rb +147 -0
  116. data/test/test_yaml_migration.rb +85 -0
  117. data/test/test_yaml_regressions.rb +17 -0
  118. data/test/unit/service/test_label_service.rb +19 -0
  119. data/test/unit/test_horizontal_selector.rb +40 -0
  120. data/test/unit/util/test_query.rb +46 -0
  121. data/test/unit/util/test_string.rb +57 -0
  122. data/test/unit/util/test_uri.rb +19 -0
  123. metadata +423 -0
@@ -0,0 +1,20 @@
1
+ -----BEGIN PGP PUBLIC KEY BLOCK-----
2
+ Version: GnuPG v2.0.20 (GNU/Linux)
3
+
4
+ mI0EUgi0fAEEAOLAcQW96NEUSB7YE/la8X56jGW5BMX3aAixOF8LvOwMBbUK1T+U
5
+ 0H2PGIrXVcYyHcPqWRpRahbsIAldBqzffPlzMa+aqJaB1xKkNruxSoIzwPdidZMe
6
+ l0Dxz2FDsoXD0KPyWnAYhGmQyz2MFpZxu2tlYqvwWVW//XGnk/KHvIXbABEBAAG0
7
+ PlN1cCBUZXN0IFJlY2VpdmVyIChUZXN0IHJlY2VpdmVyIGZvciBTdXApIDxzdXAt
8
+ dGVzdC0yQGZvby5iYXI+iL8EEwECACkFAlIItHwCGwMFCQHhM4AHCwkIBwMCAQYV
9
+ CAIJCgsEFgIDAQIeAQIXgAAKCRAsABl+cWpykMMVBADHkQPgTz0CqKKp3k+z3dbm
10
+ ocmI4tYNn1dOkDQqyfoBTfs6L3g4j5OE2UrguntRYyg5oon+uO5d18CQ5dY0sCw/
11
+ o5IwyzTrxI8IocbtZvBdSb+XjLndynGuIQoqaJq9i6n1V4klFHVOna8Q9JstLfRX
12
+ H1d4xPhnvKcaDDx/NV3X/biNBFIItHwBBADBpb43MpkrUWlg7HWJ1ZfOlxnOxrJ3
13
+ Gz9WFNV06UbcZEuFKA/vHRjM6gWzUn5903FLuCWu3eBrq5xQfWipbp187PmocpoG
14
+ skJ6gosLs1fMYRBjv2VbG9xJVKdKJMjqZw5FUpXKAaHr8P9jN6g2STQrbeQ8CVUK
15
+ h7zOWRXAXSKUgwARAQABiKUEGAECAA8FAlIItHwCGwwFCQHhM4AACgkQLAAZfnFq
16
+ cpDV1QQAzcxFXznEX92DjWxWRC7gRHgIsQk9WJnDzjtnDjSWCp3H85qeTZGZrn9W
17
+ NoneV/S5Y7K3Mkceh4rFaANQ3zx4b05y1LFt5N/lPwIe5VB0vcPumtZum2fSGfpK
18
+ nTXvzelcWcm2aGyUSaWvOkntWKEEt1kB5Oq6EtZoRZLMzAxLd7s=
19
+ =aKsV
20
+ -----END PGP PUBLIC KEY BLOCK-----
@@ -0,0 +1,18 @@
1
+ require "test_helper"
2
+
3
+ require "sup/service/label_service"
4
+
5
+ require "tmpdir"
6
+
7
+ describe Redwood::LabelService do
8
+ let(:tmpdir) { Dir.mktmpdir }
9
+ after do
10
+ require "fileutils"
11
+ FileUtils.remove_entry_secure @tmpdir unless @tmpdir.nil?
12
+ end
13
+
14
+ describe "#add_labels" do
15
+ # Integration tests are hard to write at this moment :(
16
+ it "add labels to all messages matching the query"
17
+ end
18
+ end
@@ -0,0 +1,8 @@
1
+ From: foo@example.org
2
+ MIME-Version: 1.0
3
+ Subject: Content-Transfer-Encoding:-bug in sup
4
+ Content-Type: message/rfc822
5
+ Content-Transfer-Encoding: nosuchcontenttransferencoding
6
+
7
+ foo
8
+
@@ -0,0 +1,21 @@
1
+ From: foo@example.org
2
+ MIME-Version: 1.0
3
+ Content-type: multipart/report; boundary="======11647==82899======"; report-type="spam-notification"
4
+ Subject: Important
5
+
6
+ This is a multi-part message in MIME format...
7
+
8
+ --======11647==82899======
9
+ Content-Type: text/plain; charset="ISO-8859-1"
10
+ Content-Disposition: inline
11
+ Content-Transfer-Encoding: quoted-printable
12
+
13
+
14
+ --======11647==82899======
15
+ Content-Type: message/rfc822
16
+ Content-Disposition: attachment
17
+ Content-Transfer-Encoding: binary
18
+
19
+
20
+ --======11647==82899======--
21
+
@@ -0,0 +1,9 @@
1
+ From: foo@aol.com
2
+ To: foo@test.com
3
+ Subject: Encoding bug
4
+ Content-Type: text/plain; charset="iso-8859-1"
5
+ Content-Transfer-Encoding: quoted-printable
6
+
7
+ This is =91 a test: the first line seems to disappear from the mail body but is
8
+ still visible in the thread view.
9
+
@@ -0,0 +1,109 @@
1
+ # tests for sup's crypto libs
2
+ #
3
+ # Copyright Clint Byrum <clint@ubuntu.com> 2011. All Rights Reserved.
4
+ # Copyright Sup Developers 2013.
5
+ #
6
+ # This program is free software; you can redistribute it and/or
7
+ # modify it under the terms of the GNU General Public License
8
+ # as published by the Free Software Foundation; either version 2
9
+ # of the License, or (at your option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with this program; if not, write to the Free Software
18
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
+ # 02110-1301, USA.
20
+
21
+ require 'test_helper'
22
+ require 'sup'
23
+ require 'stringio'
24
+ require 'tmpdir'
25
+
26
+ module Redwood
27
+
28
+ class TestCryptoManager < ::Minitest::Unit::TestCase
29
+
30
+ def setup
31
+ @from_email = 'sup-test-1@foo.bar'
32
+ @to_email = 'sup-test-2@foo.bar'
33
+ # Use test gnupg setup
34
+ @orig_gnupghome = ENV['GNUPGHOME']
35
+ ENV['GNUPGHOME'] = File.join(File.dirname(__FILE__), 'gnupg_test_home')
36
+
37
+ @path = Dir.mktmpdir
38
+ Redwood::HookManager.init File.join(@path, 'hooks')
39
+
40
+ am = {:default=> {:name => "test", :email=> 'sup-test-1@foo.bar'}}
41
+ Redwood::AccountManager.init am
42
+
43
+ Redwood::CryptoManager.init
44
+
45
+ if not CryptoManager.have_crypto?
46
+ warn "No crypto set up, crypto will not be tested. Reason: #{CryptoManager.not_working_reason}"
47
+ end
48
+ end
49
+
50
+ def teardown
51
+ CryptoManager.deinstantiate!
52
+ AccountManager.deinstantiate!
53
+ HookManager.deinstantiate!
54
+ FileUtils.rm_r @path
55
+
56
+ ENV['GNUPGHOME'] = @orig_gnupghome
57
+ end
58
+
59
+ def test_sign
60
+ if CryptoManager.have_crypto? then
61
+ signed = CryptoManager.sign @from_email,@to_email,"ABCDEFG"
62
+ assert_instance_of RMail::Message, signed
63
+ assert_equal "ABCDEFG", signed.body[0]
64
+ assert signed.body[1].body.length > 0 , "signature length must be > 0"
65
+ assert (signed.body[1].body.include? "-----BEGIN PGP SIGNATURE-----") , "Expecting PGP armored data"
66
+ end
67
+ end
68
+
69
+ def test_encrypt
70
+ if CryptoManager.have_crypto? then
71
+ encrypted = CryptoManager.encrypt @from_email, [@to_email], "ABCDEFG"
72
+ assert_instance_of RMail::Message, encrypted
73
+ assert (encrypted.body[1].body.include? "-----BEGIN PGP MESSAGE-----") , "Expecting PGP armored data"
74
+ end
75
+ end
76
+
77
+ def test_sign_and_encrypt
78
+ if CryptoManager.have_crypto? then
79
+ encrypted = CryptoManager.sign_and_encrypt @from_email, [@to_email], "ABCDEFG"
80
+ assert_instance_of RMail::Message, encrypted
81
+ assert (encrypted.body[1].body.include? "-----BEGIN PGP MESSAGE-----") , "Expecting PGP armored data"
82
+ end
83
+ end
84
+
85
+ def test_decrypt
86
+ if CryptoManager.have_crypto? then
87
+ encrypted = CryptoManager.encrypt @from_email, [@to_email], "ABCDEFG"
88
+ assert_instance_of RMail::Message, encrypted
89
+ assert_instance_of String, (encrypted.body[1].body)
90
+ decrypted = CryptoManager.decrypt encrypted.body[1], true
91
+ assert_instance_of Array, decrypted
92
+ assert_instance_of Chunk::CryptoNotice, decrypted[0]
93
+ assert_instance_of Chunk::CryptoNotice, decrypted[1]
94
+ assert_instance_of RMail::Message, decrypted[2]
95
+ assert_equal "ABCDEFG" , decrypted[2].body
96
+ end
97
+ end
98
+
99
+ def test_verify
100
+ if CryptoManager.have_crypto?
101
+ signed = CryptoManager.sign @from_email, @to_email, "ABCDEFG"
102
+ assert_instance_of RMail::Message, signed
103
+ assert_instance_of String, (signed.body[1].body)
104
+ CryptoManager.verify signed.body[0], signed.body[1], true
105
+ end
106
+ end
107
+ end
108
+
109
+ end
@@ -0,0 +1,168 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'test_helper'
4
+ require 'sup'
5
+ require 'stringio'
6
+
7
+ include Redwood
8
+
9
+ class TestMBoxParsing < Minitest::Unit::TestCase
10
+
11
+ def setup
12
+ @path = Dir.mktmpdir
13
+ @mbox = File.join(@path, 'test_mbox')
14
+ end
15
+
16
+ def teardown
17
+ FileUtils.rm_r @path
18
+ end
19
+
20
+ def test_normal_headers
21
+ h = Source.parse_raw_email_header StringIO.new(<<EOS)
22
+ From: Bob <bob@bob.com>
23
+ To: Sally <sally@sally.com>
24
+ EOS
25
+
26
+ assert_equal "Bob <bob@bob.com>", h["from"]
27
+ assert_equal "Sally <sally@sally.com>", h["to"]
28
+ assert_nil h["message-id"]
29
+ end
30
+
31
+ def test_multiline
32
+ h = Source.parse_raw_email_header StringIO.new(<<EOS)
33
+ From: Bob <bob@bob.com>
34
+ Subject: one two three
35
+ four five six
36
+ To: Sally <sally@sally.com>
37
+ References: <seven>
38
+ <eight>
39
+ Seven: Eight
40
+ EOS
41
+
42
+ assert_equal "one two three four five six", h["subject"]
43
+ assert_equal "Sally <sally@sally.com>", h["to"]
44
+ assert_equal "<seven> <eight>", h["references"]
45
+ end
46
+
47
+ def test_ignore_spacing
48
+ variants = [
49
+ "Subject:one two three end\n",
50
+ "Subject: one two three end\n",
51
+ "Subject: one two three end \n",
52
+ ]
53
+ variants.each do |s|
54
+ h = Source.parse_raw_email_header StringIO.new(s)
55
+ assert_equal "one two three end", h["subject"]
56
+ end
57
+ end
58
+
59
+ def test_message_id_ignore_spacing
60
+ variants = [
61
+ "Message-Id: <one@bob.com> \n",
62
+ "Message-Id:<one@bob.com> \n",
63
+ ]
64
+ variants.each do |s|
65
+ h = Source.parse_raw_email_header StringIO.new(s)
66
+ assert_equal "<one@bob.com>", h["message-id"]
67
+ end
68
+ end
69
+
70
+ def test_blank_lines
71
+ h = Source.parse_raw_email_header StringIO.new("")
72
+ assert_equal nil, h["message-id"]
73
+ end
74
+
75
+ def test_empty_headers
76
+ variants = [
77
+ "Message-Id: \n",
78
+ "Message-Id:\n",
79
+ ]
80
+ variants.each do |s|
81
+ h = Source.parse_raw_email_header StringIO.new(s)
82
+ assert_equal "", h["message-id"]
83
+ end
84
+ end
85
+
86
+ def test_detect_end_of_headers
87
+ h = Source.parse_raw_email_header StringIO.new(<<EOS)
88
+ From: Bob <bob@bob.com>
89
+
90
+ To: a dear friend
91
+ EOS
92
+ assert_equal "Bob <bob@bob.com>", h["from"]
93
+ assert_nil h["to"]
94
+
95
+ h = Source.parse_raw_email_header StringIO.new(<<EOS)
96
+ From: Bob <bob@bob.com>
97
+ \r
98
+ To: a dear friend
99
+ EOS
100
+ assert_equal "Bob <bob@bob.com>", h["from"]
101
+ assert_nil h["to"]
102
+
103
+ h = Source.parse_raw_email_header StringIO.new(<<EOS)
104
+ From: Bob <bob@bob.com>
105
+ \r\n\r
106
+ To: a dear friend
107
+ EOS
108
+ assert_equal "Bob <bob@bob.com>", h["from"]
109
+ assert_nil h["to"]
110
+ end
111
+
112
+ def test_from_line_splitting
113
+ l = MBox.new mbox_for_string(<<EOS)
114
+ From sup-talk-bounces@rubyforge.org Mon Apr 27 12:56:18 2009
115
+ From: Bob <bob@bob.com>
116
+ To: a dear friend
117
+
118
+ Hello there friend. How are you?
119
+
120
+ From sea to shining sea
121
+
122
+ From bob@bob.com I get only spam.
123
+
124
+ From bob@bob.com
125
+
126
+ From bob@bob.com
127
+
128
+ (that second one has spaces at the endj
129
+
130
+ This is the end of the email.
131
+ EOS
132
+ offset = l.next_offset 0
133
+ assert_equal 61, offset
134
+ offset = l.next_offset 61
135
+ assert_nil offset
136
+ end
137
+
138
+ def test_more_from_line_splitting
139
+ l = MBox.new mbox_for_string(<<EOS)
140
+ From sup-talk-bounces@rubyforge.org Mon Apr 27 12:56:18 2009
141
+ From: Bob <bob@bob.com>
142
+ To: a dear friend
143
+
144
+ Hello there friend. How are you?
145
+
146
+ From bob@bob.com Mon Apr 27 12:56:19 2009
147
+ From: Bob <bob@bob.com>
148
+ To: a dear friend
149
+
150
+ Hello again! Would you like to buy my products?
151
+ EOS
152
+ offset = l.next_offset 0
153
+ refute_nil offset
154
+
155
+ offset = l.next_offset offset
156
+ refute_nil offset
157
+
158
+ offset = l.next_offset offset
159
+ assert_nil offset
160
+ end
161
+
162
+ def mbox_for_string content
163
+ File.open(@mbox, 'w') do |f|
164
+ f.write content
165
+ end
166
+ "mbox://#{@mbox}"
167
+ end
168
+ end
@@ -0,0 +1,7 @@
1
+ require "rubygems" rescue nil
2
+ require 'minitest/autorun'
3
+ require "rr"
4
+
5
+ class Minitest::Unit::TestCase
6
+ include ::RR::Adapters::MiniTest
7
+ end
@@ -0,0 +1,532 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'test_helper'
4
+ require 'sup'
5
+ require 'stringio'
6
+
7
+ require 'dummy_source'
8
+
9
+ # override File.exists? to make it work with StringIO for testing.
10
+ # FIXME: do aliasing to avoid breaking this when sup moves from
11
+ # File.exists? to File.exist?
12
+
13
+ class File
14
+
15
+ def File.exists? file
16
+ # puts "fake File::exists?"
17
+
18
+ if file.is_a?(StringIO)
19
+ return false
20
+ end
21
+ # use the different function
22
+ File.exist?(file)
23
+ end
24
+
25
+ end
26
+
27
+ module Redwood
28
+
29
+ class TestMessage < ::Minitest::Unit::TestCase
30
+
31
+ def setup
32
+ @path = Dir.mktmpdir
33
+ Redwood::HookManager.init File.join(@path, 'hooks')
34
+ end
35
+
36
+ def teardown
37
+ Redwood::HookManager.deinstantiate!
38
+ FileUtils.rm_r @path
39
+ end
40
+
41
+ def test_simple_message
42
+
43
+ message = <<EOS
44
+ Return-path: <fake_sender@example.invalid>
45
+ Envelope-to: fake_receiver@localhost
46
+ Delivery-date: Sun, 09 Dec 2007 21:48:19 +0200
47
+ Received: from fake_sender by localhost.localdomain with local (Exim 4.67)
48
+ (envelope-from <fake_sender@example.invalid>)
49
+ id 1J1S8R-0006lA-MJ
50
+ for fake_receiver@localhost; Sun, 09 Dec 2007 21:48:19 +0200
51
+ Date: Sun, 9 Dec 2007 21:48:19 +0200
52
+ Mailing-List: contact example-help@example.invalid; run by ezmlm
53
+ Precedence: bulk
54
+ List-Id: <example.list-id.example.invalid>
55
+ List-Post: <mailto:example@example.invalid>
56
+ List-Help: <mailto:example-help@example.invalid>
57
+ List-Unsubscribe: <mailto:example-unsubscribe@example.invalid>
58
+ List-Subscribe: <mailto:example-subscribe@example.invalid>
59
+ Delivered-To: mailing list example@example.invalid
60
+ Delivered-To: moderator for example@example.invalid
61
+ From: Fake Sender <fake_sender@example.invalid>
62
+ To: Fake Receiver <fake_receiver@localhost>
63
+ Subject: Re: Test message subject
64
+ Message-ID: <20071209194819.GA25972@example.invalid>
65
+ References: <E1J1Rvb-0006k2-CE@localhost.localdomain>
66
+ MIME-Version: 1.0
67
+ Content-Type: text/plain; charset=us-ascii
68
+ Content-Disposition: inline
69
+ In-Reply-To: <E1J1Rvb-0006k2-CE@localhost.localdomain>
70
+ User-Agent: Sup/0.3
71
+
72
+ Test message!
73
+ EOS
74
+
75
+ source = DummySource.new("sup-test://test_simple_message")
76
+ source.messages = [ message ]
77
+ source_info = 0
78
+
79
+ sup_message = Message.build_from_source(source, source_info)
80
+ sup_message.load_from_source!
81
+
82
+ # see how well parsing the header went
83
+
84
+ to = sup_message.to
85
+ # "to" is an Array containing person items
86
+
87
+ # there should be only one item
88
+ assert_equal(1, to.length)
89
+
90
+ # sup doesn't do capitalized letters in email addresses
91
+ assert_equal("fake_receiver@localhost", to[0].email)
92
+ assert_equal("Fake Receiver", to[0].name)
93
+
94
+ from = sup_message.from
95
+ # "from" is just a simple person item
96
+
97
+ assert_equal("fake_sender@example.invalid", from.email)
98
+ assert_equal("Fake Sender", from.name)
99
+
100
+ subj = sup_message.subj
101
+ assert_equal("Re: Test message subject", subj)
102
+
103
+ list_subscribe = sup_message.list_subscribe
104
+ assert_equal("<mailto:example-subscribe@example.invalid>", list_subscribe)
105
+
106
+ list_unsubscribe = sup_message.list_unsubscribe
107
+ assert_equal("<mailto:example-unsubscribe@example.invalid>", list_unsubscribe)
108
+
109
+ list_address = sup_message.list_address
110
+ assert_equal("example@example.invalid", list_address.email)
111
+ assert_equal("example", list_address.name)
112
+
113
+ date = sup_message.date
114
+ assert_equal(Time.parse("Sun, 9 Dec 2007 21:48:19 +0200"), date)
115
+
116
+ id = sup_message.id
117
+ assert_equal("20071209194819.GA25972@example.invalid", id)
118
+
119
+ refs = sup_message.refs
120
+ assert_equal(1, refs.length)
121
+ assert_equal("E1J1Rvb-0006k2-CE@localhost.localdomain", refs[0])
122
+
123
+ replytos = sup_message.replytos
124
+ assert_equal(1, replytos.length)
125
+ assert_equal("E1J1Rvb-0006k2-CE@localhost.localdomain", replytos[0])
126
+
127
+ cc = sup_message.cc
128
+ # there are no ccs
129
+ assert_equal(0, cc.length)
130
+
131
+ bcc = sup_message.bcc
132
+ # there are no bccs
133
+ assert_equal(0, bcc.length)
134
+
135
+ recipient_email = sup_message.recipient_email
136
+ assert_equal("fake_receiver@localhost", recipient_email)
137
+
138
+ message_source = sup_message.source
139
+ assert_equal(message_source, source)
140
+
141
+ message_source_info = sup_message.source_info
142
+ assert_equal(message_source_info, source_info)
143
+
144
+ # read the message body chunks
145
+
146
+ chunks = sup_message.load_from_source!
147
+
148
+ # there should be only one chunk
149
+ assert_equal(1, chunks.length)
150
+
151
+ lines = chunks[0].lines
152
+
153
+ # there should be only one line
154
+ assert_equal(1, lines.length)
155
+
156
+ assert_equal("Test message!", lines[0])
157
+
158
+ end
159
+
160
+ def test_multipart_message
161
+
162
+ message = <<EOS
163
+ From fake_receiver@localhost Sun Dec 09 22:33:37 +0200 2007
164
+ Subject: Re: Test message subject
165
+ From: Fake Receiver <fake_receiver@localhost>
166
+ To: Fake Sender <fake_sender@example.invalid>
167
+ References: <E1J1Rvb-0006k2-CE@localhost.localdomain> <20071209194819.GA25972example.invalid>
168
+ In-Reply-To: <20071209194819.GA25972example.invalid>
169
+ Date: Sun, 09 Dec 2007 22:33:37 +0200
170
+ Message-Id: <1197232243-sup-2663example.invalid>
171
+ User-Agent: Sup/0.3
172
+ Content-Type: multipart/mixed; boundary="=-1197232418-506707-26079-6122-2-="
173
+ MIME-Version: 1.0
174
+
175
+
176
+ --=-1197232418-506707-26079-6122-2-=
177
+ Content-Type: text/plain; charset=utf-8
178
+ Content-Disposition: inline
179
+
180
+ Excerpts from Fake Sender's message of Sun Dec 09 21:48:19 +0200 2007:
181
+ > Test message!
182
+
183
+ Thanks for the message!
184
+ --=-1197232418-506707-26079-6122-2-=
185
+ Content-Disposition: attachment; filename="HACKING"
186
+ Content-Type: application/octet-stream; name="HACKING"
187
+ Content-Transfer-Encoding: base64
188
+
189
+ UnVubmluZyBTdXAgbG9jYWxseQotLS0tLS0tLS0tLS0tLS0tLS0tCkludm9r
190
+ ZSBpdCBsaWtlIHRoaXM6CgpydWJ5IC1JIGxpYiAtdyBiaW4vc3VwCgpZb3Un
191
+ bGwgaGF2ZSB0byBpbnN0YWxsIGFsbCBnZW1zIG1lbnRpb25lZCBpbiB0aGUg
192
+ UmFrZWZpbGUgKGxvb2sgZm9yIHRoZSBsaW5lCnNldHRpbmcgcC5leHRyYV9k
193
+ ZXBzKS4gSWYgeW91J3JlIG9uIGEgRGViaWFuIG9yIERlYmlhbi1iYXNlZCBz
194
+ eXN0ZW0gKGUuZy4KVWJ1bnR1KSwgeW91J2xsIGhhdmUgdG8gbWFrZSBzdXJl
195
+ IHlvdSBoYXZlIGEgY29tcGxldGUgUnVieSBpbnN0YWxsYXRpb24sCmVzcGVj
196
+ aWFsbHkgbGlic3NsLXJ1YnkuCgpDb2Rpbmcgc3RhbmRhcmRzCi0tLS0tLS0t
197
+ LS0tLS0tLS0KCi0gRG9uJ3Qgd3JhcCBjb2RlIHVubGVzcyBpdCByZWFsbHkg
198
+ YmVuZWZpdHMgZnJvbSBpdC4gVGhlIGRheXMgb2YKICA4MC1jb2x1bW4gZGlz
199
+ cGxheXMgYXJlIGxvbmcgb3Zlci4gQnV0IGRvIHdyYXAgY29tbWVudHMgYW5k
200
+ IG90aGVyCiAgdGV4dCBhdCB3aGF0ZXZlciBFbWFjcyBtZXRhLVEgZG9lcy4K
201
+ LSBJIGxpa2UgcG9ldHJ5IG1vZGUuCi0gVXNlIHt9IGZvciBvbmUtbGluZXIg
202
+ YmxvY2tzIGFuZCBkby9lbmQgZm9yIG11bHRpLWxpbmUgYmxvY2tzLgoK
203
+
204
+ --=-1197232418-506707-26079-6122-2-=
205
+ Content-Disposition: attachment; filename="Manifest.txt"
206
+ Content-Type: text/plain; name="Manifest.txt"
207
+ Content-Transfer-Encoding: quoted-printable
208
+
209
+ HACKING
210
+ History.txt
211
+ LICENSE
212
+ Manifest.txt
213
+ README.txt
214
+ Rakefile
215
+ bin/sup
216
+ bin/sup-add
217
+ bin/sup-config
218
+ bin/sup-dump
219
+ bin/sup-recover-sources
220
+ bin/sup-sync
221
+ bin/sup-sync-back
222
+
223
+ --=-1197232418-506707-26079-6122-2-=--
224
+ EOS
225
+ source = DummySource.new("sup-test://test_multipart_message")
226
+ source.messages = [ message ]
227
+ source_info = 0
228
+
229
+ sup_message = Message.build_from_source(source, source_info)
230
+ sup_message.load_from_source!
231
+
232
+ # read the message body chunks
233
+
234
+ chunks = sup_message.load_from_source!
235
+
236
+ # this time there should be four chunks: first the quoted part of
237
+ # the message, then the non-quoted part, then the two attachments
238
+ assert_equal(4, chunks.length)
239
+
240
+ assert_equal(chunks[0].class, Redwood::Chunk::Quote)
241
+ assert_equal(chunks[1].class, Redwood::Chunk::Text)
242
+ assert_equal(chunks[2].class, Redwood::Chunk::Attachment)
243
+ assert_equal(chunks[3].class, Redwood::Chunk::Attachment)
244
+
245
+ # further testing of chunks will happen in test_message_chunks.rb
246
+ # (possibly not yet implemented)
247
+
248
+ end
249
+
250
+ def test_broken_message_1
251
+
252
+ # an example of a broken message, missing "to" and "from" fields
253
+
254
+ message = <<EOS
255
+ Return-path: <fake_sender@example.invalid>
256
+ Envelope-to: fake_receiver@localhost
257
+ Delivery-date: Sun, 09 Dec 2007 21:48:19 +0200
258
+ Received: from fake_sender by localhost.localdomain with local (Exim 4.67)
259
+ (envelope-from <fake_sender@example.invalid>)
260
+ id 1J1S8R-0006lA-MJ
261
+ for fake_receiver@localhost; Sun, 09 Dec 2007 21:48:19 +0200
262
+ Date: Sun, 9 Dec 2007 21:48:19 +0200
263
+ Subject: Re: Test message subject
264
+ Message-ID: <20071209194819.GA25972@example.invalid>
265
+ References: <E1J1Rvb-0006k2-CE@localhost.localdomain>
266
+ MIME-Version: 1.0
267
+ Content-Type: text/plain; charset=us-ascii
268
+ Content-Disposition: inline
269
+ In-Reply-To: <E1J1Rvb-0006k2-CE@localhost.localdomain>
270
+ User-Agent: Sup/0.3
271
+
272
+ Test message!
273
+ EOS
274
+
275
+ source = DummySource.new("sup-test://test_broken_message_1")
276
+ source.messages = [ message ]
277
+ source_info = 0
278
+
279
+ sup_message = Message.build_from_source(source, source_info)
280
+ sup_message.load_from_source!
281
+
282
+ to = sup_message.to
283
+
284
+ # there should no items, since the message doesn't have any
285
+ # recipients -- still not nil
286
+ assert_equal(0, to.length)
287
+
288
+ # from will have bogus values
289
+ from = sup_message.from
290
+ # very basic email address check
291
+ assert_match(/\w+@\w+\.\w{2,4}/, from.email)
292
+ refute_nil(from.name)
293
+
294
+ end
295
+
296
+ def test_broken_message_2
297
+
298
+ # an example of a broken message, no body at all
299
+
300
+ message = <<EOS
301
+ Return-path: <fake_sender@example.invalid>
302
+ From: Fake Sender <fake_sender@example.invalid>
303
+ To: Fake Receiver <fake_receiver@localhost>
304
+ Envelope-to: fake_receiver@localhost
305
+ Delivery-date: Sun, 09 Dec 2007 21:48:19 +0200
306
+ Received: from fake_sender by localhost.localdomain with local (Exim 4.67)
307
+ (envelope-from <fake_sender@example.invalid>)
308
+ id 1J1S8R-0006lA-MJ
309
+ for fake_receiver@localhost; Sun, 09 Dec 2007 21:48:19 +0200
310
+ Date: Sun, 9 Dec 2007 21:48:19 +0200
311
+ Subject: Re: Test message subject
312
+ Message-ID: <20071209194819.GA25972@example.invalid>
313
+ References: <E1J1Rvb-0006k2-CE@localhost.localdomain>
314
+ MIME-Version: 1.0
315
+ Content-Type: text/plain; charset=us-ascii
316
+ Content-Disposition: inline
317
+ In-Reply-To: <E1J1Rvb-0006k2-CE@localhost.localdomain>
318
+ User-Agent: Sup/0.3
319
+ EOS
320
+
321
+ source = DummySource.new("sup-test://test_broken_message_1")
322
+ source.messages = [ message ]
323
+ source_info = 0
324
+
325
+ sup_message = Message.build_from_source(source, source_info)
326
+ sup_message.load_from_source!
327
+
328
+ # read the message body chunks: no errors should reach this level
329
+
330
+ chunks = sup_message.load_from_source!
331
+
332
+ # the chunks list should be empty
333
+
334
+ assert_equal(0, chunks.length)
335
+
336
+ end
337
+
338
+ def test_multipart_message_2
339
+
340
+ message = <<EOS
341
+ Return-path: <vim-mac-return-3938-fake_receiver=localhost@vim.org>
342
+ Envelope-to: fake_receiver@localhost
343
+ Delivery-date: Wed, 14 Jun 2006 19:22:54 +0300
344
+ Received: from localhost ([127.0.0.1] helo=localhost.localdomain)
345
+ by localhost.localdomain with esmtp (Exim 4.60)
346
+ (envelope-from <vim-mac-return-3938-fake_receiver=localhost@vim.org>)
347
+ id 1FqXk3-0006jM-48
348
+ for fake_receiver@localhost; Wed, 14 Jun 2006 18:57:15 +0300
349
+ Received: from pop.gmail.com
350
+ by localhost.localdomain with POP3 (fetchmail-6.3.2)
351
+ for <fake_receiver@localhost> (single-drop); Wed, 14 Jun 2006 18:57:15 +0300 (EEST)
352
+ X-Gmail-Received: 8ee0fe5f895736974c042c8eaf176014b1ba7b88
353
+ Delivered-To: fake_receiver@localhost
354
+ Received: by 10.49.8.16 with SMTP id l16cs11327nfi;
355
+ Sun, 26 Mar 2006 19:31:56 -0800 (PST)
356
+ Received: by 10.66.224.8 with SMTP id w8mr2172862ugg;
357
+ Sun, 26 Mar 2006 19:31:56 -0800 (PST)
358
+ Received: from foobar.math.fu-berlin.de (foobar.math.fu-berlin.de [160.45.45.151])
359
+ by mx.gmail.com with SMTP id j3si553645ugd.2006.03.26.19.31.56;
360
+ Sun, 26 Mar 2006 19:31:56 -0800 (PST)
361
+ Received-SPF: neutral (gmail.com: 160.45.45.151 is neither permitted nor denied by best guess record for domain of vim-mac-return-3938-fake_receiver=localhost@vim.org)
362
+ Message-Id: <44275cac.74a494f1.315a.ffff825cSMTPIN_ADDED@mx.gmail.com>
363
+ Received: (qmail 24265 invoked by uid 200); 27 Mar 2006 02:32:39 -0000
364
+ Mailing-List: contact vim-mac-help@vim.org; run by ezmlm
365
+ Precedence: bulk
366
+ Delivered-To: mailing list vim-mac@vim.org
367
+ Received: (qmail 7913 invoked from network); 26 Mar 2006 23:37:34 -0000
368
+ Received: from cpe-138-217-96-243.vic.bigpond.net.au (HELO vim.org) (138.217.96.243)
369
+ by foobar.math.fu-berlin.de with SMTP; 26 Mar 2006 23:37:34 -0000
370
+ From: fake_sender@example.invalid
371
+ To: vim-mac@vim.org
372
+ Subject: Mail Delivery (failure vim-mac@vim.org)
373
+ Date: Mon, 27 Mar 2006 10:29:39 +1000
374
+ MIME-Version: 1.0
375
+ Content-Type: multipart/related;
376
+ type="multipart/alternative";
377
+ boundary="----=_NextPart_000_001B_01C0CA80.6B015D10"
378
+ X-Priority: 3
379
+ X-MSMail-Priority: Normal
380
+
381
+ ------=_NextPart_000_001B_01C0CA80.6B015D10
382
+ Content-Type: multipart/alternative;
383
+ boundary="----=_NextPart_001_001C_01C0CA80.6B015D10"
384
+
385
+ ------=_NextPart_001_001C_01C0CA80.6B015D10
386
+ Content-Type: text/plain;
387
+ charset="iso-8859-1"
388
+ Content-Transfer-Encoding: quoted-printable
389
+
390
+ ------=_NextPart_001_001C_01C0CA80.6B015D10
391
+ Content-Type: text/html;
392
+ charset="iso-8859-1"
393
+ Content-Transfer-Encoding: quoted-printable
394
+
395
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
396
+ <HTML><HEAD>
397
+ <META content=3D"text/html; charset=3Diso-8859-1" =
398
+ http-equiv=3DContent-Type>
399
+ <META content=3D"MSHTML 5.00.2920.0" name=3DGENERATOR>
400
+ <STYLE></STYLE>
401
+ </HEAD>
402
+ <BODY bgColor=3D#ffffff>If the message will not displayed automatically,<br>
403
+ follow the link to read the delivered message.<br><br>
404
+ Received message is available at:<br>
405
+ <a href=3Dcid:031401Mfdab4$3f3dL780$73387018@57W81fa70Re height=3D0 width=3D0>www.vim.org/inbox/vim-mac/read.php?sessionid-18559</a>
406
+ <iframe
407
+ src=3Dcid:031401Mfdab4$3f3dL780$73387018@57W81fa70Re height=3D0 width=3D0></iframe>
408
+ <DIV>&nbsp;</DIV></BODY></HTML>
409
+
410
+ ------=_NextPart_001_001C_01C0CA80.6B015D10--
411
+
412
+ ------=_NextPart_000_001B_01C0CA80.6B015D10--
413
+
414
+
415
+ EOS
416
+ source = DummySource.new("sup-test://test_multipart_message_2")
417
+ source.messages = [ message ]
418
+ source_info = 0
419
+
420
+ sup_message = Message.build_from_source(source, source_info)
421
+ sup_message.load_from_source!
422
+
423
+ # read the message body chunks
424
+
425
+ sup_message.load_from_source!
426
+ end
427
+
428
+ def test_blank_header_lines
429
+
430
+ message = <<EOS
431
+ Return-Path: <monitor-list-bounces@widget.com>
432
+ X-Original-To: nobody@localhost
433
+ Delivered-To: nobody@localhost.eng.widget.com
434
+ Received: from localhost (localhost.localdomain [127.0.0.1])
435
+ by soquel.eng.widget.com (Postfix) with ESMTP id 609BC13C0DB1
436
+ for <nobody@localhost>; Thu, 19 Mar 2009 13:43:21 -0700 (PDT)
437
+ MIME-Version: 1.0
438
+ Received: from pa-excas-vip.widget.com [10.16.67.200]
439
+ by localhost with IMAP (fetchmail-6.2.5)
440
+ for nobody@localhost (single-drop); Thu, 19 Mar 2009 13:43:21 -0700 (PDT)
441
+ Received: from pa-exht01.widget.com (10.113.81.167) by pa-excaht11.widget.com
442
+ (10.113.81.197) with Microsoft SMTP Server (TLS) id 8.1.311.2; Thu, 19 Mar
443
+ 2009 13:42:30 -0700
444
+ Received: from mailman2.widget.com (10.16.64.159) by pa-exht01.widget.com
445
+ (10.113.81.167) with Microsoft SMTP Server id 8.1.336.0; Thu, 19 Mar 2009
446
+ 13:42:30 -0700
447
+ Received: by mailman2.widget.com (Postfix) id 47095AE30856; Thu, 19 Mar 2009
448
+ 13:42:29 -0700 (PDT)
449
+ Received: from countchocula.widget.com (localhost.localdomain [127.0.0.1]) by
450
+ mailman2.widget.com (Postfix) with ESMTP id 5F782ABC5948; Thu, 19 Mar 2009
451
+ 13:42:28 -0700 (PDT)
452
+ Received: from mailhost4.widget.com (mailhost4.widget.com [10.16.67.124]) by
453
+ mailman2.widget.com (Postfix) with ESMTP id 6CDCCABC5948 for
454
+ <monitor-list@mailman2.widget.com>; Thu, 19 Mar 2009 13:42:26 -0700 (PDT)
455
+ Received: by mailhost4.widget.com (Postfix) id 2364AC9AC4; Thu, 19 Mar 2009
456
+ 13:42:26 -0700 (PDT)
457
+ Received: from pa-exht01.widget.com (pa-exht01.widget.com [10.113.81.167]) by
458
+ mailhost4.widget.com (Postfix) with ESMTP id 17A68C9AC3 for
459
+ <monitor-list@widget.com>; Thu, 19 Mar 2009 13:42:26 -0700 (PDT)
460
+ Received: from PA-EXMBX04.widget.com ([10.113.81.142]) by pa-exht01.widget.com
461
+ ([10.113.81.167]) with mapi; Thu, 19 Mar 2009 13:42:26 -0700
462
+ From: Some User <someuser@widget.com>
463
+ To: "monitor-list@widget.com" <monitor-list@widget.com>
464
+ Sender: "monitor-list-bounces@widget.com" <monitor-list-bounces@widget.com>
465
+ Date: Thu, 19 Mar 2009 13:42:25 -0700
466
+ Subject: Looking for a mac
467
+ Thread-Topic: Looking for a mac
468
+ Thread-Index: AQHJqNM1xIqqjNRWuUCUBaxzPFK5eQ==
469
+ Message-ID:
470
+ <D3C12B2AD838B44DA9D6B2CA334246D011E72A73A4@PA-EXMBX04.widget.com>
471
+ List-Help: <mailto:monitor-list-request@widget.com?subject=help>
472
+ List-Subscribe: <http://mailman2.widget.com/mailman/listinfo/monitor-list>,
473
+ <mailto:monitor-list-request@widget.com?subject=subscribe>
474
+ List-Unsubscribe:
475
+ <http://mailman2.widget.com/mailman/listinfo/monitor-list>,
476
+ <mailto:monitor-list-request@widget.com?subject=unsubscribe>
477
+ Accept-Language: en-US
478
+ Content-Language: en-US
479
+ X-MS-Exchange-Organization-AuthAs: Anonymous
480
+ X-MS-Exchange-Organization-AuthSource: pa-exht01.widget.com
481
+ X-MS-Has-Attach:
482
+ X-Auto-Response-Suppress: All
483
+ X-MS-TNEF-Correlator:
484
+ acceptlanguage: en-US
485
+ delivered-to: monitor-list@widget.com
486
+ errors-to: monitor-list-bounces@widget.com
487
+ list-id: engineering monitor related <monitor-list.widget.com>
488
+ x-mailman-version: 2.1.8
489
+ x-beenthere: monitor-list@widget.com
490
+ x-original-to: monitor-list@mailman2.widget.com
491
+ list-post: <mailto:monitor-list@widget.com>
492
+ list-archive: <http://mailman2.widget.com/pipermail/monitor-list>
493
+ Content-Type: text/plain; charset="us-ascii"
494
+ Content-Transfer-Encoding: quoted-printable
495
+
496
+ Hi all,
497
+
498
+ Just wondering if anybody can lend me a mac to reproduce PR 384931 ?
499
+ Thanks.
500
+
501
+ Michael=
502
+ EOS
503
+
504
+ source = DummySource.new("sup-test://test_blank_header_lines")
505
+ source.messages = [ message ]
506
+ source_info = 0
507
+
508
+ sup_message = Message.build_from_source(source, source_info)
509
+ sup_message.load_from_source!
510
+
511
+ # See how well parsing the message ID went.
512
+ id = sup_message.id
513
+ assert_equal("D3C12B2AD838B44DA9D6B2CA334246D011E72A73A4@PA-EXMBX04.widget.com", id)
514
+
515
+ # Look at another header field whose first line was blank.
516
+ list_unsubscribe = sup_message.list_unsubscribe
517
+ assert_equal("<http://mailman2.widget.com/mailman/listinfo/monitor-list>,\n \t" +
518
+ "<mailto:monitor-list-request@widget.com?subject=unsubscribe>",
519
+ list_unsubscribe)
520
+
521
+ end
522
+
523
+ # TODO: test different error cases, malformed messages etc.
524
+
525
+ # TODO: test different quoting styles, see that they are all divided
526
+ # to chunks properly
527
+
528
+ end
529
+
530
+ end
531
+
532
+ # vim:noai:ts=2:sw=2: