gurgitate-mail 1.10.3 → 1.10.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,282 +0,0 @@
1
- #!/opt/bin/ruby -w
2
-
3
- require "gurgitate/header"
4
-
5
- module Gurgitate
6
- class IllegalHeader < RuntimeError ; end
7
-
8
- # ========================================================================
9
-
10
- class HeaderBag < Array
11
- def =~(regex)
12
- inject(false) do |y,x|
13
- y or ( ( x =~ regex ) != nil )
14
- end
15
- end
16
-
17
- def sub!(regex, replacement)
18
- each do |header|
19
- header.contents = header.contents.sub regex, replacement
20
- end
21
- end
22
-
23
- def sub(regex, replacement)
24
- ::Gurgitate::HeaderBag.new(
25
- clone.map do |header|
26
- ::Gurgitate::Header.new(
27
- "#{header.name}: " + header.contents.sub(regex,
28
- replacement)
29
- )
30
- end
31
- )
32
- end
33
-
34
- def to_s
35
- map do |member|
36
- member.to_s
37
- end.join ""
38
- end
39
-
40
- end
41
-
42
- # A slightly bigger class for all of a message's headers
43
- class Headers
44
-
45
- private
46
-
47
- # Figures out whether the first line of a mail message is an
48
- # mbox-style "From " line (say, if you get this from sendmail),
49
- # or whether it's just a normal header.
50
- # --
51
- # If you run "fetchmail" with the -m option to feed the
52
- # mail message straight to gurgitate, skipping the "local
53
- # MTA" step, then it doesn't have a "From " line. So I
54
- # have to deal with that by hand. First, check to see if
55
- # there's a "From " line present in the first place.
56
- def figure_out_from_line(headertext)
57
- (unix_from,normal_headers) = headertext.split(/\n/,2)
58
-
59
- if unix_from =~ /^From / then
60
- headertext=normal_headers
61
- unix_from=unix_from
62
- else
63
- # If there isn't, then deal with it after we've
64
- # worried about the rest of the headers, 'cos we'll
65
- # have to make our own.
66
- unix_from=nil
67
- end
68
- return unix_from, headertext
69
- end
70
-
71
- def parse_headers
72
- @headertext.each_line do |h|
73
- h.chomp!
74
- if(h=~/^\s+/) then
75
- @lastheader << h
76
- else
77
- header=Header.new(h)
78
- @headers[header.name] ||= HeaderBag.new
79
- @headers[header.name].push(header)
80
- @lastheader=header
81
- end
82
- end
83
-
84
- @headers_changed=false
85
- end
86
-
87
- # Get the envelope From information. This comes with a
88
- # whole category of rants: this information is absurdly hard
89
- # to get your hands on. The best you can manage is a sort
90
- # of educated guess. Thus, this horrible glob of hackiness.
91
- # I don't recommend looking too closely at this code if you
92
- # can avoid it, and further I recommend making sure to
93
- # configure your MTA so that it sends proper sender and
94
- # recipient information to gurgitate so that this code never
95
- # has to be run at all.
96
- def guess_sender
97
- # Start by worrying about the "From foo@bar" line. If it's
98
- # not there, then make one up from the Return-Path: header.
99
- # If there isn't a "Return-Path:" header (then I suspect we
100
- # have bigger problems, but still) then use From: as a wild
101
- # guess. If I hope that this entire lot of code doesn't get
102
- # used, then I _particularly_ hope that things never get so
103
- # bad that poor gurgitate has to use the From: header as a
104
- # source of authoritative information on anything.
105
- #
106
- # And then after all that fuss, if we're delivering to a
107
- # Maildir, I have to get rid of it. And sometimes the MTA
108
- # gives me a mbox-style From line and sometimes it doesn't.
109
- # It's annoying, but I have no choice but to Just Deal With
110
- # It.
111
- if @unix_from then
112
- # If it is there, then grab the email address in it and
113
- # use that as our official "from".
114
- fromregex=/^From ([^ ]+@[^ ]+) /
115
- fromregex.match(@unix_from)
116
- @from=$+
117
-
118
- # or maybe it's local
119
- if @from == nil then
120
- @unix_from =~ /^From (\S+) /
121
- @from=$+
122
- end
123
- else
124
- fromregex=/([^ ]+@[^ ]+) \(.*\)|[^<]*[<](.*@.*)[>]|([^ ]+@[^ ]+
125
- )/
126
- if self["Return-Path"] != nil then
127
- fromregex.match(self["Return-Path"][0].contents)
128
- else
129
- if self["From"] != nil then
130
- fromregex.match(self["From"][0].contents)
131
- end
132
- end
133
- address_candidate=$+
134
-
135
- # If there STILL isn't a match, then it's probably safe to
136
- # assume that it's local mail, and doesn't have an @ in its
137
- # address.
138
- unless address_candidate
139
- if self["Return-Path"] then
140
- self["Return-Path"][0].contents =~ /(\S+)/
141
- address_candidate=$+
142
- else
143
- if self["From"] then
144
- self["From"][0].contents =~ /(\S+)/
145
- address_candidate=$+
146
- end
147
- end
148
- end
149
-
150
- @from=address_candidate
151
-
152
- @unix_from="From "+self.from+" "+Time.new.to_s
153
- end
154
- end
155
-
156
- public
157
-
158
- # Creates a Headers object.
159
- # headertext::
160
- # The text of the message headers.
161
- def initialize(headertext=nil, sender=nil, recipient=nil)
162
- @from = sender
163
- @to = recipient
164
- @headers = Hash.new(nil)
165
-
166
- if Hash === headertext
167
- @headers_changed = true
168
- headertext.each_key do |key|
169
-
170
- headername = key.to_s.gsub("_","-")
171
-
172
- header=Header.new(headername, headertext[key])
173
- @headers[header.name] ||= HeaderBag.new
174
- @headers[header.name].push(header)
175
- end
176
- else
177
- if headertext
178
- @unix_from, @headertext = figure_out_from_line headertext
179
- parse_headers if @headertext
180
-
181
- if sender # then don't believe the mbox separator
182
- @from = sender
183
- @unix_from="From "+self.from+" "+Time.new.to_s
184
- else
185
- guess_sender
186
- end
187
- end
188
- end
189
- end
190
-
191
- # Grab the headers with names +names+
192
- # names:: The names of the header.
193
- def [](*names)
194
- if names.inject(false) do |accum,name|
195
- accum or @headers.has_key? name
196
- end then
197
- return HeaderBag.new(names.collect { |name|
198
- @headers[name]
199
- }.flatten.delete_if { |e| e == nil } )
200
- else
201
- return nil
202
- end
203
- end
204
-
205
- # Set the header named +name+ to +value+
206
- # name:: The name of the header.
207
- # value:: The new value of the header.
208
- def []=(name,value)
209
- @headers_changed = true
210
- @headers[name]=HeaderBag.new([Header.new(name,value)])
211
- end
212
-
213
- # Who the message is to (the envelope to)
214
- #
215
- # Yet another bucket of rants. Unix mail sucks.
216
- def to
217
- return @to || @headers["X-Original-To"] || nil
218
- end
219
-
220
- # Who the message is from (the envelope from)
221
- def from
222
- return @from || ""
223
- end
224
-
225
- # Change the envelope from line to whatever you want. This might
226
- # not be particularly neighborly, but oh well.
227
- # newfrom:: An email address
228
- def from=(newfrom)
229
- @from=newfrom
230
- @unix_from="From "+self.from+" "+Time.new.to_s
231
- end
232
-
233
- # Match header +name+ against +regex+
234
- # name::
235
- # A string containing the name of the header to match (for example,
236
- # "From")
237
- # regex:: The regex to match it against (for example, /@aol.com/)
238
- def match(name,regex)
239
- ret=false
240
- if(@headers[name]) then
241
- @headers[name].each do |h|
242
- ret |= h.matches(regex)
243
- end
244
- end
245
- return ret
246
- end
247
-
248
- # Return true if headers +names+ match +regex+
249
- # names:: An array of header names (for example, %w{From Reply-To})
250
- # regex:: The regex to match the headers against.
251
- def matches(names,regex)
252
- ret=false
253
-
254
- if names.class == String then
255
- names=[names]
256
- end
257
-
258
- names.each do |n|
259
- ret |= match(n,regex)
260
- end
261
- return ret
262
- end
263
-
264
- # Returns the headers properly formatted for an email
265
- # message.
266
- def to_mbox
267
- return @unix_from+"\n"+to_s
268
- end
269
-
270
- # Returns the headers formatted for an email message (without
271
- # the "From " line
272
- def to_s
273
- if @headers_changed then
274
- return @headers.map do |name,hdr|
275
- hdr.map do |hdr_content| hdr_content.to_s end.join("\n")
276
- end.join("\n")
277
- else
278
- return @headertext
279
- end
280
- end
281
- end
282
- end
@@ -1,166 +0,0 @@
1
- #!/opt/bin/ruby -w
2
-
3
- require "gurgitate/headers"
4
-
5
- module Gurgitate
6
- class IllegalHeader < RuntimeError ; end
7
-
8
- # ========================================================================
9
-
10
- # A slightly bigger class for all of a message's headers
11
- class MailHeaders < Headers
12
-
13
- private
14
-
15
- # Figures out whether the first line of a mail message is an
16
- # mbox-style "From " line (say, if you get this from sendmail),
17
- # or whether it's just a normal header.
18
- # --
19
- # If you run "fetchmail" with the -m option to feed the
20
- # mail message straight to gurgitate, skipping the "local
21
- # MTA" step, then it doesn't have a "From " line. So I
22
- # have to deal with that by hand. First, check to see if
23
- # there's a "From " line present in the first place.
24
- def figure_out_from_line(headertext)
25
- (unix_from,normal_headers) = headertext.split(/\n/,2)
26
-
27
- if unix_from =~ /^From / then
28
- headertext=normal_headers
29
- unix_from=unix_from
30
- else
31
- # If there isn't, then deal with it after we've
32
- # worried about the rest of the headers, 'cos we'll
33
- # have to make our own.
34
- unix_from=nil
35
- end
36
- return unix_from, headertext
37
- end
38
-
39
- # Get the envelope From information. This comes with a
40
- # whole category of rants: this information is absurdly hard
41
- # to get your hands on. The best you can manage is a sort
42
- # of educated guess. Thus, this horrible glob of hackiness.
43
- # I don't recommend looking too closely at this code if you
44
- # can avoid it, and further I recommend making sure to
45
- # configure your MTA so that it sends proper sender and
46
- # recipient information to gurgitate so that this code never
47
- # has to be run at all.
48
- def guess_sender
49
- # Start by worrying about the "From foo@bar" line. If it's
50
- # not there, then make one up from the Return-Path: header.
51
- # If there isn't a "Return-Path:" header (then I suspect we
52
- # have bigger problems, but still) then use From: as a wild
53
- # guess. If I hope that this entire lot of code doesn't get
54
- # used, then I _particularly_ hope that things never get so
55
- # bad that poor gurgitate has to use the From: header as a
56
- # source of authoritative information on anything.
57
- #
58
- # And then after all that fuss, if we're delivering to a
59
- # Maildir, I have to get rid of it. And sometimes the MTA
60
- # gives me a mbox-style From line and sometimes it doesn't.
61
- # It's annoying, but I have no choice but to Just Deal With
62
- # It.
63
- if @unix_from then
64
- # If it is there, then grab the email address in it and
65
- # use that as our official "from".
66
- fromregex=/^From ([^ ]+@[^ ]+) /
67
- fromregex.match(@unix_from)
68
- @from=$+
69
-
70
- # or maybe it's local
71
- if @from == nil then
72
- @unix_from =~ /^From (\S+) /
73
- @from=$+
74
- end
75
- else
76
- fromregex=/([^ ]+@[^ ]+) \(.*\)|[^<]*[<](.*@.*)[>]|([^ ]+@[^ ]+)/
77
- if self["Return-Path"] != nil then
78
- fromregex.match(self["Return-Path"][0].contents)
79
- else
80
- if self["From"] != nil then
81
- fromregex.match(self["From"][0].contents)
82
- end
83
- end
84
- address_candidate=$+
85
-
86
- # If there STILL isn't a match, then it's probably safe to
87
- # assume that it's local mail, and doesn't have an @ in its
88
- # address.
89
- unless address_candidate
90
- if self["Return-Path"] != nil then
91
- self["Return-Path"][0].contents =~ /(\S+)/
92
- address_candidate=$+
93
- else
94
- self["From"][0].contents =~ /(\S+)/
95
- address_candidate=$+
96
- end
97
- end
98
-
99
- @from=address_candidate
100
-
101
- @unix_from="From "+self.from+" "+Time.new.to_s
102
- end
103
- end
104
-
105
- public
106
-
107
- # Creates a MailHeaders object.
108
- # headertext::
109
- # The text of the message headers.
110
- def initialize(headertext=nil, sender=nil, recipient=nil)
111
- @from = sender
112
- @to = recipient
113
- @headers = Hash.new(nil)
114
-
115
- if Hash === headertext
116
- @headers_changed = true
117
- headertext.each_key do |key|
118
-
119
- headername = key.to_s.gsub("_","-")
120
-
121
- header=Header.new(headername, headertext[key])
122
- @headers[header.name] ||= HeaderBag.new
123
- @headers[header.name].push(header)
124
- end
125
- else
126
- if headertext
127
- @unix_from, @headertext = figure_out_from_line headertext
128
- parse_headers if @headertext
129
-
130
- if sender # then don't believe the mbox separator
131
- @from = sender
132
- @unix_from="From "+self.from+" "+Time.new.to_s
133
- else
134
- guess_sender
135
- end
136
- end
137
- end
138
- end
139
-
140
- # Who the message is to (the envelope to)
141
- #
142
- # Yet another bucket of rants. Unix mail sucks.
143
- def to
144
- return @to || @headers["X-Original-To"] || nil
145
- end
146
-
147
- # Who the message is from (the envelope from)
148
- def from
149
- return @from || ""
150
- end
151
-
152
- # Change the envelope from line to whatever you want. This might
153
- # not be particularly neighborly, but oh well.
154
- # newfrom:: An email address
155
- def from=(newfrom)
156
- @from=newfrom
157
- @unix_from="From "+self.from+" "+Time.new.to_s
158
- end
159
-
160
- # Returns the headers properly formatted for an mbox-format
161
- # email message.
162
- def to_mbox
163
- return @unix_from+"\n"+to_s
164
- end
165
- end
166
- end
@@ -1,124 +0,0 @@
1
- # Contains the class Gurgitate::Mailmessage, used to handle the parsing
2
- # of existing messages and the creation of new messages.
3
-
4
- require 'gurgitate/headers'
5
- require 'gurgitate/message'
6
-
7
- module Gurgitate
8
-
9
- # A complete mail message. This is the base class for
10
- # gurgitate-mail itself: if you want to use gurgitate-mail to create
11
- # new messages, this is what you want to use.
12
- class Mailmessage < Message
13
-
14
- Fromregex=/([^ ]+@[^ ]+) \(.*\)|[^<][<](.*@.*)[>]|([^ ]+@[^ ]+)/;
15
-
16
- # The envelope sender and recipient, if anyone thought to
17
- # mention them to us.
18
- attr_accessor :sender
19
- attr_accessor :recipient
20
-
21
- # Creates a new mail message from the options hash, and the body of the
22
- # message in a string.
23
- #
24
- # This can actually be invoked in several ways:
25
- #
26
- # Gurgitate::Mailmessage.create "This is the message body",
27
- # :from => "from_address@example.com",
28
- # :to => "to_address@example.com",
29
- # :subject => "This is the message subject"
30
- #
31
- # This results in an email message that, when rendered via to_s, will
32
- # look like this:
33
- #
34
- # From: from_address@example.com
35
- # To: to_address@example.com
36
- # Subject: This is the message subject
37
- #
38
- # This is the message body
39
- #
40
- # If you prefer to do things entirely by options hashes, as some do,
41
- # you can substitute a :body key for the first argument:
42
- #
43
- # Gurgitate::Mailmessage.create(
44
- # :body => "This is the message body",
45
- # :from => "from_address@example.com",
46
- # :to => "to_address@example.com",
47
- # :subject => "This is the message subject"
48
- # )
49
- #
50
- # There are two other special options you can use: :sender and
51
- # :recipient. These are used to specify the sender and recipient of
52
- # email messages, when the message is sent via SMTP.
53
- #
54
- def self.create(*args)
55
- options = body = nil
56
-
57
- if String === args[0]
58
- options = args[1]
59
- body = args[0]
60
- elsif Hash === args[0]
61
- options = args[0]
62
- else
63
- options = {}
64
- end
65
-
66
- message = self.new
67
-
68
- message.instance_eval do
69
- if body
70
- @body=body
71
- end
72
-
73
- %w/sender recipient body/.each do |key|
74
- if options.has_key? key.to_sym
75
- instance_variable_set("@#{key}", options[key.to_sym])
76
- options.delete key.to_sym
77
- end
78
- end
79
-
80
- @headers = MailHeaders.new(options)
81
- end
82
-
83
- message
84
- end
85
-
86
- def initialize(text=nil, recipient=nil, sender=nil)
87
-
88
- @recipient = recipient
89
- @sender = sender
90
-
91
- if text
92
- (@headertext,@body)=text.split(/\n\n/,2)
93
- @headers=MailHeaders.new(@headertext);
94
- Fromregex.match(@headers["From"][0].contents);
95
- @from=$+
96
- else
97
- super(text)
98
- end
99
- end
100
-
101
- # custom accessors
102
-
103
- # Returns the message's sender
104
- def from; @sender || @headers.from; end
105
-
106
- # Returns all the candidates for a recipient
107
- def to
108
- if @recipient
109
- then @recipient
110
- elsif @headers["To"]
111
- then @headers["To"][0].contents
112
- elsif @headers["Cc"]
113
- then @headers["Cc"][0].contents
114
- elsif @headers["X-Original-To"]
115
- then @headers["X-Original-To"][0].contents
116
- else
117
- ""
118
- end
119
- end
120
-
121
- # Returns the mail message formatted for mbox
122
- def to_mbox; @headers.to_mbox + "\n\n" + @body; end
123
- end
124
- end