postfix-xforward 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+