postfix-xforward 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.rdoc ADDED
@@ -0,0 +1,4 @@
1
+ = 0.1.0, release 2010-08-19
2
+
3
+ * Initial Release
4
+
data/LICENSE ADDED
@@ -0,0 +1,18 @@
1
+ Copyright (c) 2010 Kendall Gifford
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7
+ the Software, and to permit persons to whom the Software is furnished to do so,
8
+ subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
16
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,71 @@
1
+ = Postfix XFORWARD Extension for Net::SMTP
2
+
3
+ The postfix-xforward extension adds methods to Ruby's Net::SMTP library to support the
4
+ {XFORWARD}[http://www.postfix.org/XFORWARD_README.html] SMTP extension, as defined and
5
+ supported by the {Postfix MTA}[http://www.postfix.org/].
6
+
7
+ Code that currently uses
8
+ {Net::SMTP}[http://ruby-doc.org/stdlib/libdoc/net/smtp/rdoc/index.html] as follows:
9
+
10
+ require 'net/smtp'
11
+ to = 'some@address.tld'
12
+ from = 'other@domain.tld'
13
+ msg = <<EMAIL
14
+ To: #{to}
15
+ From: #{from}
16
+ Subject: Test Message
17
+
18
+ This is a test message.
19
+ EMAIL
20
+
21
+ Net::SMTP.start('mail.localdomain', 25, 'my-hostname') do |smtp|
22
+ smtp.send_message(msg, from, to)
23
+ end
24
+
25
+ ... can now use it this way in order to include XFORWARD attributes:
26
+
27
+ require 'postfix-xforward'
28
+ attr = { 'NAME' => 'actual.domain.tld', 'ADDR' => '123.234.012.123' }
29
+ to = 'some@address.tld'
30
+ from = 'other@domain.tld'
31
+ msg = <<EMAIL
32
+ To: #{to}
33
+ From: #{from}
34
+ Subject: Test Message
35
+
36
+ This is a test message.
37
+ EMAIL
38
+
39
+ Net::SMTP.start('mail.localdomain', 25, 'my-hostname', nil, nil, nil, attr) do |smtp|
40
+ smtp.send_message(msg, from, to)
41
+ end
42
+
43
+ == Details
44
+
45
+ The XFORWARD extension, supported by the Postfix MTA is very simple really. It allows the
46
+ SMTP client to include various attributes before the MAIL TO command. These attributes
47
+ tell the server where the email message *really* came from.
48
+
49
+ This is almost always used on special, private instances of an SMTP server where only
50
+ trusted (usually local) clients can connect. This is usually used in configurations such
51
+ as the following:
52
+
53
+ client -> smtp-server-1 -> filter-script -> smtp-server-2 -> nexthop
54
+
55
+ In these setups, smtp-server-1 and 2 both run under the same Postfix instance in order
56
+ to provide {after-queue content filtering}[http://www.postfix.org/FILTER_README.html].
57
+ The smtp-server-1 instance has access to the actual information about the client that
58
+ is sending the message. It can pass this on, if configured correctly to an external
59
+ filter-script.
60
+
61
+ This library makes it possible, using Net::SMTP to write filter-script in Ruby and to
62
+ be able to pass original client information on when speaking SMTP to smtp-server-2
63
+ using XFORWARD.
64
+
65
+ This has just been thrown together so any comments, fixes, improvemnts, or other contributions
66
+ are appreciated.
67
+
68
+ == Authors and Credits
69
+
70
+ Authors:: Kendall Gifford
71
+
@@ -0,0 +1,12 @@
1
+ require 'net/smtp'
2
+ module PostfixXForward
3
+ class << self
4
+ # hooks PostfixXForward::SMTP into Net::SMTP
5
+ def enable
6
+ return if Net::SMTP.respond_to? :xstart
7
+ require 'postfix-xforward/smtp'
8
+ Net::SMTP.send :include, SMTP
9
+ end
10
+ end
11
+ end
12
+ PostfixXForward.enable
@@ -0,0 +1,265 @@
1
+ require 'postfix-xforward/version'
2
+ require 'postfix-xforward/utils'
3
+
4
+ module PostfixXForward
5
+ module SMTP
6
+
7
+ # RFC 822: maximum SMTP command line length is 512 (including <CRLF>)
8
+ # This is set to the maximum length, NOT including the <CRLF>
9
+ MAXLEN = 510
10
+
11
+ # Official XFORWARD command extension name
12
+ XCMD = 'XFORWARD'
13
+
14
+ # Extend Net::SMTP to support XFORWARD
15
+ def self.included(base)
16
+ base.class_eval do
17
+ extend ClassMethods
18
+ alias_method :initialize_without_xforward, :initialize
19
+ alias_method :initialize, :initialize_with_xforward
20
+ alias_method :do_start_without_xforward, :do_start
21
+ alias_method :do_start, :do_start_with_xforward
22
+ alias_method :do_finish_without_xforward, :do_finish
23
+ alias_method :do_finish, :do_finish_with_xforward
24
+ end
25
+ class << base
26
+ alias_method :start_without_xforward, :start
27
+ alias_method :start, :start_with_xforward
28
+ end
29
+ end
30
+
31
+ module ClassMethods
32
+ # - Aliased as #start
33
+ # - Original #start available as #start_without_xforward
34
+ #
35
+ # This version adds an optional xforward_attrs parameter to the end of the
36
+ # list. You may provide a hash for this parameter to specify any XFORWARD
37
+ # attribute values you wish to provide the SMTP server on the other end.
38
+ #
39
+ # If the server doesn't support XFORWARD, then XFORWARD won't be
40
+ # attempted. Each of the provided XFORWARD attributes aren't supported by
41
+ # the server won't be transmitted either. So, for an attribute to be
42
+ # successfully transmitted via XFORWARD, the server must support the
43
+ # XFORWARD attribute specifically (and XFORWARD generally).
44
+ #
45
+ # Currently, Postfix documents the following XFORWARD attributes
46
+ # (from http://www.postfix.com/XFORWARD_README.html):
47
+ #
48
+ # NAME:: The up-stream hostname
49
+ # ADDR:: The up-stream network address
50
+ # PORT:: The up-stream client TCP port number
51
+ # PROTO:: Protocol used for receiving mail from the up-stream host
52
+ # HELO:: Hostname that the up-stream announced itself with
53
+ # IDENT:: Local message identifier on the up-stream host
54
+ # SOURCE:: Either "LOCAL" or "REMOTE" : where the up-stream host received
55
+ # the message from
56
+ def start_with_xforward(address,
57
+ port = nil,
58
+ helo = 'localhost.localdomain',
59
+ user = nil,
60
+ secret = nil,
61
+ authtype = nil,
62
+ xforward_attrs = nil,
63
+ &block
64
+ )
65
+ new(address, port, xforward_attrs).start(helo, user, secret, authtype, &block)
66
+ end
67
+
68
+ # This method accomplishes the same thing as our overridden version of
69
+ # the #start class method above (#start_with_xforward) but re-orders the
70
+ # parameters, giving priority (and making required) the xforward_attrs
71
+ # hash. Also the server address and port are now combined in one string
72
+ # that should use 'domain.tld:10025' syntax to explicitely define a port.
73
+ def xstart(address_and_port, xforward_attrs,
74
+ helo = 'localhost.localdomain',
75
+ user = nil,
76
+ secret = nil,
77
+ authtype = nil,
78
+ &block
79
+ )
80
+ address = address_and_port
81
+ port = 25
82
+ if address_and_port =~ /^(.+):(\d+)$/
83
+ address = $1
84
+ port = $2.to_i
85
+ end
86
+ new(address, port, xforward_attrs).start(helo, user, secret, authtype, &block)
87
+ end
88
+ end
89
+
90
+ # - Aliased as #initialize
91
+ # - Original #initialize available as #initialize_without_xforward
92
+ # This extended version of #initialize allows us to receive an optional
93
+ # Hash of XFORWARD attributes as the final parameter.
94
+ def initialize_with_xforward(address, port = nil, xforward_attrs = nil)
95
+ initialize_without_xforward(address, port)
96
+ @xforward = false
97
+ @xforward_attrs = {}
98
+ if xforward_attrs.is_a?(Hash)
99
+ @xforward = true
100
+ # Normalize all provided XFORWARD attribute names to upper case
101
+ # TODO: explicit validation of attributes
102
+ xforward_attrs.each do |k, v|
103
+ @xforward_attrs[k.upcase] = v
104
+ end
105
+ end
106
+ end
107
+
108
+ # Query whether to try to use XFORWARD or not.
109
+ def xforward ; @xforward ; end
110
+ # Set whether to try to use XFORWARD or not. This should be done before
111
+ # calling #start. Note that if #start is called in XFORWARD mode but the
112
+ # server doesn't support XFORWARD then it won't by tried anyway.
113
+ def xforward=(bool)
114
+ if @started
115
+ logging('ignoring request to enable/disable use of XFORWARD: SMTP session already started')
116
+ return
117
+ end
118
+ @xforward = bool
119
+ end
120
+
121
+ # The following attribute accessors allow you to set or query the various
122
+ # XFORWARD attributes that will be sent when the SMTP session is started,
123
+ # so long as the attribute (and XFORWARD in general) is supported by the
124
+ # server. Note that setting an attribute to a non-nil value will
125
+ # automatically set #xforward to true (requesting the use of XFORWARD).
126
+ def xforward_name ; @xforward_attrs['NAME'] ; end
127
+ def xforward_name=(name)
128
+ @xforward = true
129
+ @xforward_attrs['NAME'] = name
130
+ end
131
+ def xforward_addr ; @xforward_attrs['ADDR'] ; end
132
+ def xforward_addr=(addr)
133
+ @xforward = true
134
+ @xforward_attrs['ADDR'] = addr
135
+ end
136
+ def xforward_port ; @xforward_attrs['PORT'] ; end
137
+ def xforward_port=(port)
138
+ @xforward = true
139
+ @xforward_attrs['PORT'] = port
140
+ end
141
+ def xforward_proto ; @xforward_attrs['PROTO'] ; end
142
+ def xforward_proto=(proto)
143
+ @xforward = true
144
+ @xforward_attrs['PROTO'] = proto
145
+ end
146
+ def xforward_helo ; @xforward_attrs['HELO'] ; end
147
+ def xforward_helo=(helo)
148
+ @xforward = true
149
+ @xforward_attrs['HELO'] = helo
150
+ end
151
+ def xforward_ident ; @xforward_attrs['IDENT'] ; end
152
+ def xforward_ident=(ident)
153
+ @xforward = true
154
+ @xforward_attrs['IDENT'] = ident
155
+ end
156
+ def xforward_source ; @xforward_attrs['SOURCE'] ; end
157
+ def xforward_source=(source)
158
+ @xforward = true
159
+ @xforward_attrs['SOURCE'] = source
160
+ end
161
+
162
+ # true if server advertises XFORWARD.
163
+ # You cannot get a valid value before opening an SMTP session.
164
+ def capable_xforward?
165
+ capapble?(XCMD)
166
+ end
167
+
168
+ # true if server advertises XFORWARD attribute NAME.
169
+ # You cannot get a valid value before opening an SMTP session.
170
+ def capable_xforward_name?
171
+ xforward_capable?('NAME')
172
+ end
173
+
174
+ # true if server advertises XFORWARD attribute ADDR.
175
+ # You cannot get a valid value before opening an SMTP session.
176
+ def capable_xforward_addr?
177
+ xforward_capable?('ADDR')
178
+ end
179
+
180
+ # true if server advertises XFORWARD attribute PORT.
181
+ # You cannot get a valid value before opening an SMTP session.
182
+ def capable_xforward_port?
183
+ xforward_capable?('PORT')
184
+ end
185
+
186
+ # true if server advertises XFORWARD attribute PROTO.
187
+ # You cannot get a valid value before opening an SMTP session.
188
+ def capable_xforward_proto?
189
+ xforward_capable?('PROTO')
190
+ end
191
+
192
+ # true if server advertises XFORWARD attribute HELO.
193
+ # You cannot get a valid value before opening an SMTP session.
194
+ def capable_xforward_helo?
195
+ xforward_capable?('HELO')
196
+ end
197
+
198
+ # true if server advertises XFORWARD attribute IDENT.
199
+ # You cannot get a valid value before opening an SMTP session.
200
+ def capable_xforward_ident?
201
+ xforward_capable?('IDENT')
202
+ end
203
+
204
+ # true if server advertises XFORWARD attribute SOURCE.
205
+ # You cannot get a valid value before opening an SMTP session.
206
+ def capable_xforward_source?
207
+ xforward_capable?('SOURCE')
208
+ end
209
+
210
+ # Returns supported XFORWARD attributes on the SMTP server.
211
+ # You cannot get valid values before opening SMTP session.
212
+ def capable_xforward_attrs
213
+ return [] unless @capabilities
214
+ return [] unless @capabilities[XCMD]
215
+ @capabilities[XCMD]
216
+ end
217
+
218
+ private
219
+
220
+ def xforward_capapble?(attr)
221
+ return nil unless @capabilities
222
+ return false unless @capabilities[XCMD]
223
+ @capabilities[XCMD].include?(attr)
224
+ end
225
+
226
+ def do_start_with_xforward(helo_domain, user, secret, authtype)
227
+ do_start_without_xforward(helo_domain, user, secret, authtype)
228
+ if @started and @xforward
229
+ if capable?(XCMD)
230
+ do_xforward
231
+ else
232
+ logging("ignoring request to do XFORWARD: server isn't capable")
233
+ end
234
+ end
235
+ end
236
+
237
+ def do_finish_with_xforward
238
+ do_finish_without_xforward
239
+ @xforward = false
240
+ @xforward_attrs = {}
241
+ end
242
+
243
+ # Issues the XFORWARD smtp command, sending each of the supplied
244
+ # XFORWARD attributes (as long as they are supported). Unsupported
245
+ # XFORWARD attributes are ignored.
246
+ def do_xforward
247
+ cmd = XCMD.dup
248
+ cap = capable_xforward_attrs
249
+ @xforward_attrs.each do |attr, value|
250
+ if cap.include?(attr)
251
+ tmp = " #{attr}=#{Utils.xtext(value)}"
252
+ if cmd.length + tmp.length > MAXLEN
253
+ getok(cmd)
254
+ cmd = XCMD.dup
255
+ end
256
+ cmd << tmp
257
+ else
258
+ logging("ignoring XFORWARD attribute '#{attr}': unsupported by server")
259
+ end
260
+ end
261
+ getok(cmd) if cmd != XCMD
262
+ end
263
+
264
+ end
265
+ end
@@ -0,0 +1,10 @@
1
+ module PostfixXForward
2
+ class Utils
3
+ # Convert the txt string to "xtext" as defined in RFC 1891
4
+ def Utils.xtext(txt)
5
+ txt.gsub(/[+=\x00-\x20\x7f-\xff]/) do |c|
6
+ "+#{c.unpack('H2').first.upcase}"
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,8 @@
1
+ module PostfixXForward
2
+ module Version
3
+ MAJOR = 0
4
+ MINOR = 1
5
+ TINY = 0
6
+ STRING = [MAJOR, MINOR, TINY].join('.')
7
+ end
8
+ end
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: postfix-xforward
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Kendall Gifford
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-08-20 00:00:00 -06:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: |-
23
+ This extends ruby's Net::SMTP library to support the Postfix MTA's
24
+ XFORWARD SMTP extension.
25
+ email:
26
+ - zettabyte@gmail.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files: []
32
+
33
+ files:
34
+ - lib/postfix-xforward/smtp.rb
35
+ - lib/postfix-xforward/utils.rb
36
+ - lib/postfix-xforward/version.rb
37
+ - lib/postfix-xforward.rb
38
+ - LICENSE
39
+ - README.rdoc
40
+ - CHANGELOG.rdoc
41
+ has_rdoc: true
42
+ homepage: http://github.com/zettabyte/postfix-xforward
43
+ licenses: []
44
+
45
+ post_install_message:
46
+ rdoc_options: []
47
+
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ hash: 3
56
+ segments:
57
+ - 0
58
+ version: "0"
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ none: false
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ hash: 23
65
+ segments:
66
+ - 1
67
+ - 3
68
+ - 6
69
+ version: 1.3.6
70
+ requirements: []
71
+
72
+ rubyforge_project:
73
+ rubygems_version: 1.3.7
74
+ signing_key:
75
+ specification_version: 3
76
+ summary: Net::SMTP extension to support Postfix MTA's XFORWARD SMTP extension.
77
+ test_files: []
78
+