]>
Commit | Line | Data |
---|---|---|
8e30d909 ER |
1 | --- cvsspam-0.2.11/cvsspam.rb 2005-02-21 21:52:45.000000000 +0200 |
2 | +++ cvsspam-0.2.11.patched/cvsspam.rb 2005-02-21 21:52:18.000000000 +0200 | |
0bfd8df2 ER |
3 | @@ -122,24 +122,49 @@ |
4 | # long values with header continuation lines as needed | |
5 | def rfc2047_encode_quoted(io, start, rest) | |
6 | raise "no charset" if @charset.nil? | |
7 | - code_begin = "=?#{@charset}?#{@encoding}?" | |
8 | + code_begin = marker_start_quoted | |
9 | start << code_begin | |
10 | - rest.each_byte do |b| | |
11 | - code = if b>126 || b==UNDERSCORE || b==TAB | |
12 | - sprintf("=%02x", b) | |
13 | - elsif b == SPACE | |
14 | - "_" | |
15 | - else | |
16 | - b.chr | |
17 | - end | |
18 | - | |
19 | + each_char_encoded(rest) do |code| | |
20 | if start.length+code.length+2 > @right_margin | |
21 | - io.puts(start + "?=") | |
22 | + io.puts(start + marker_end_quoted) | |
23 | start = " " + code_begin | |
24 | end | |
25 | start << code | |
26 | end | |
27 | - io.puts(start + "?=") | |
28 | + io.puts(start + marker_end_quoted) | |
29 | + end | |
30 | + | |
31 | + # return a string representing the given character-code in quoted-printable | |
32 | + # format | |
33 | + def quoted_encode_char(b) | |
34 | + if b>126 || b==UNDERSCORE || b==TAB | |
35 | + sprintf("=%02x", b) | |
36 | + elsif b == SPACE | |
37 | + "_" | |
38 | + else | |
39 | + b.chr | |
40 | + end | |
41 | + end | |
42 | + | |
43 | + public | |
44 | + | |
45 | + # yields a quoted-printable version of each byte in the given string | |
46 | + def each_char_encoded(text) | |
47 | + text.each_byte do |b| | |
48 | + yield quoted_encode_char(b) | |
49 | + end | |
50 | + end | |
51 | + | |
52 | + # gives the string "?=",which is used to mark the end of a quoted-printable | |
53 | + # characte rsequence | |
54 | + def marker_end_quoted | |
55 | + "?=" | |
56 | + end | |
57 | + | |
58 | + # gives a string starting "=?", and including a charset specification, that | |
59 | + # marks the start of a quoted-printable character sequence | |
60 | + def marker_start_quoted | |
61 | + "=?#{@charset}?#{@encoding}?" | |
62 | end | |
63 | ||
64 | # test to see of the given string contains non-ASCII characters | |
65 | @@ -1145,6 +1170,60 @@ | |
66 | end | |
67 | end | |
68 | ||
69 | +# an RFC 822 email address | |
70 | +class EmailAddress | |
71 | + def initialize(text) | |
72 | + if text =~ /^\s*([^<]+?)\s*<\s*([^>]+?)\s*>\s*$/ | |
73 | + @personal_name = $1 | |
74 | + @address = $2 | |
75 | + else | |
76 | + @personal_name = nil | |
77 | + @address = text | |
78 | + end | |
79 | + end | |
80 | + | |
81 | + attr_accessor :personal_name, :address | |
82 | + | |
83 | + def has_personal_name? | |
84 | + return !@personal_name.nil? | |
85 | + end | |
86 | + | |
87 | + def encoded | |
88 | + if has_personal_name? | |
89 | + "#{encoded_personal_name} <#{address}>" | |
90 | + else | |
91 | + @address | |
92 | + end | |
93 | + end | |
94 | + | |
95 | + def to_s | |
96 | + if has_personal_name? | |
97 | + "#{personal_name} <#{address}>" | |
98 | + else | |
99 | + @address | |
100 | + end | |
101 | + end | |
102 | + | |
103 | + private | |
104 | + | |
105 | + def encoded_personal_name | |
106 | + personal_name.split(" ").map{|word| encode_word(word)}.join(" ") | |
107 | + end | |
108 | + | |
109 | + # rfc2047 encode the word, if it contains non-ASCII characters | |
110 | + def encode_word(word) | |
111 | + if $encoder.requires_rfc2047?(word) | |
112 | + encoded = $encoder.marker_start_quoted | |
113 | + $encoder.each_char_encoded(word) do |code| | |
114 | + encoded << code | |
115 | + end | |
116 | + encoded << $encoder.marker_end_quoted | |
117 | + return encoded | |
118 | + end | |
119 | + word | |
120 | + end | |
121 | +end | |
122 | + | |
123 | ||
124 | cvsroot_dir = "#{ENV['CVSROOT']}/CVSROOT" | |
125 | $config = "#{cvsroot_dir}/cvsspam.conf" | |
126 | @@ -1183,10 +1262,10 @@ | |
127 | ) | |
128 | ||
129 | opts.each do |opt, arg| | |
130 | - $recipients << arg if opt=="--to" | |
131 | + $recipients << EmailAddress.new(arg) if opt=="--to" | |
132 | $config = arg if opt=="--config" | |
133 | $debug = true if opt=="--debug" | |
134 | - $from_address = arg if opt=="--from" | |
135 | + $from_address = EmailAddress.new(arg) if opt=="--from" | |
136 | # must use different variable as the config is readed later. | |
137 | $arg_charset = arg if opt == "--charset" | |
138 | end | |
139 | @@ -1218,7 +1297,7 @@ | |
140 | end | |
141 | # helper function called from the 'config file' | |
142 | def addRecipient(email) | |
143 | - $recipients << email | |
144 | + $recipients << EmailAddress.new(email) | |
145 | end | |
146 | # 'constant' used from the 'config file' | |
147 | class GUESS | |
148 | @@ -1333,6 +1412,7 @@ | |
149 | end | |
150 | ||
151 | $encoder = HeaderEncoder.new | |
152 | +# TODO: maybe we should use the system-default value instead of ISO Latin 1? | |
153 | $encoder.charset = $charset.nil? ? "ISO-8859-1" : $charset | |
154 | ||
155 | ||
156 | @@ -1572,19 +1652,19 @@ | |
157 | # Tries to look up an 'alias' email address for the given string in the | |
158 | # CVSROOT/users file, if the file exists. The argument is returned unchanged | |
159 | # if no alias is found. | |
160 | -def sender_alias(address) | |
161 | +def sender_alias(email) | |
162 | if File.exists?($users_file) | |
163 | File.open($users_file) do |io| | |
164 | io.each_line do |line| | |
165 | if line =~ /^([^:]+)\s*:\s*(['"]?)([^\n\r]+)(\2)/ | |
166 | - if address == $1 | |
167 | - return $3 | |
168 | + if email.address == $1 | |
169 | + return EmailAddress.new($3) | |
170 | end | |
171 | end | |
172 | end | |
173 | end | |
174 | end | |
175 | - address | |
176 | ||
177 | end | |
178 | ||
179 | # A handle for code that needs to add headers and a body to an email being | |
8e30d909 | 180 | @@ -1628,8 +1708,8 @@ |
0bfd8df2 ER |
181 | blah("invoking '#{cmd}'") |
182 | IO.popen(cmd, "w") do |mail| | |
183 | ctx = MailContext.new(mail) | |
184 | - ctx.header("To", recipients.join(',')) | |
0bfd8df2 | 185 | - ctx.header("From", from) if from |
8e30d909 | 186 | + ctx.header("To", recipients.map{|addr| addr.encoded}.join(',')) |
0bfd8df2 ER |
187 | + ctx.header("From", from.encoded) if from |
188 | yield ctx | |
189 | end | |
190 | end | |
8e30d909 | 191 | @@ -1657,18 +1737,18 @@ |
0bfd8df2 ER |
192 | |
193 | def send(from, recipients) | |
194 | if from == nil | |
195 | - from = ENV['USER'] || ENV['USERNAME'] || 'cvsspam' | |
196 | + from = EmailAddress.new(ENV['USER'] || ENV['USERNAME'] || 'cvsspam') | |
197 | end | |
198 | - unless from =~ /@/ | |
199 | - from = "#{from}@#{ENV['HOSTNAME']||'localhost'}" | |
200 | + unless from.address =~ /@/ | |
201 | + from.address = "#{from.address}@#{ENV['HOSTNAME']||'localhost'}" | |
202 | end | |
203 | smtp = Net::SMTP.new(@smtp_host) | |
204 | blah("connecting to '#{@smtp_host}'") | |
205 | smtp.start() | |
206 | - smtp.ready(from, recipients) do |mail| | |
207 | + smtp.ready(from.address, recipients.map{|addr| addr.address}) do |mail| | |
208 | ctx = MailContext.new(IOAdapter.new(mail)) | |
209 | - ctx.header("To", recipients.join(',')) | |
0bfd8df2 | 210 | - ctx.header("From", from) if from |
8e30d909 | 211 | + ctx.header("To", recipients.map{|addr| addr.encoded}.join(',')) |
0bfd8df2 ER |
212 | + ctx.header("From", from.encoded) if from |
213 | ctx.header("Date", Time.now.utc.strftime(DATE_HEADER_FORMAT)) | |
214 | yield ctx | |
215 | end |