ruby-qmail 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/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Allen Fair
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,66 @@
1
+ = ruby-qmail
2
+
3
+ The RubyQmail Plugin provides support for the Qmail MTA and helpers for creating data-aware applications
4
+ While initially intended to operate in a large-scale Qmail environment, some of these features may be useful for
5
+ applications on other platforms. In fact, this plugin is not required if you are using Qmail because
6
+ ActionMailer's sendmail and smtp configurations will work for simple Qmail installations.
7
+
8
+ NOTE: This is still in an early stage of development, but has been tested to insert email into the qmail queue
9
+ via the qmail-queue and QMQP protocols.
10
+
11
+ == Using ruby-qmail
12
+
13
+ Ruby Qmail takes a message in 3 parts
14
+ * The envelope return path. This is not necessarily the same as the From: header, and is the email address to which
15
+ bounces will be sent. This should most likely be a special address used to identify the mailing being sent,
16
+ containing the message number or other identifier, and perhaps the source of the message. For example:
17
+ bounces-messageid@example.com
18
+ Ruby-Qmail used VERP (Variable Envelope Return Path) by default, so bounces will be returned in the form
19
+ bounces-messageid-recipientmailbox=recipientdomain@example.com
20
+ for individual returns. Qmail will also send a report to the return path (without the recipient address encoded)
21
+ bounces-messageid-@example.com
22
+ with addresses and error messages for all undeliverable addresses.
23
+ * Recipient List. This is either a String containing an email address (only the user@domain part), an array of
24
+ email addresses, or a file of email addresses (one per line), or any other object implementing Enumerable.
25
+ * Message Data. This is a String or Filename of a raw RFC822 message, with full message headers, body parts and
26
+ attachments, ususally composed via Tmail or Rmail.
27
+
28
+ Call the insert command
29
+ RubyQmail::Queue.insert "bounces-123@example.com", ['recipient@email.com',...], message_file
30
+ The call returns true if the message was inserted. If you want more control and information, you can run it as:
31
+ rqq = RubyQmail::Queue.new
32
+ sucess? = rqq.qmail_queue "bounces-123@example.com", ['recipient@email.com',...], message_file
33
+ puts rqq.response #=> Response message
34
+
35
+ You can also specify a set of options on the invocation line as :name=>value parameters after the message file.
36
+ RubyQmail::Queue.insert "bounces-123@example.com", recipient_file, message_file, :method=>:qmqp
37
+ Options can be
38
+ * :config_file - Location of the YAML file with these config settings for the application/system.
39
+ * :qmail_root - Location of the qmail install, usually /var/qmail
40
+ * :qmqp_port - Where any QMQP daemon process is listening, usually 628
41
+ * :logger - Object used to log any messages, such as RAILS_DEFAULT_LOGGER
42
+ * :qmail_queue - The name of the qmail-queue binary (qmail-queue or qmail-qmqpc or other).
43
+ * :qmqp_servers - The location of the time used to specify the IP address of a QMQPD process
44
+ * :ip - The IP address of a QMQP server
45
+ * :method - either :qmqp or :queue, used on the #insert method
46
+ * :noverp - Do not use VERP encoding on the return path
47
+ * :delimiter - Character to separate the email address and VERP encoding (Qmail prefers '-', others user '+')
48
+
49
+ == Feature Path
50
+ * Queue management, moving, reporting
51
+ * Bounce handling
52
+
53
+ == Note on Patches/Pull Requests
54
+
55
+ * Fork the project.
56
+ * Make your feature addition or bug fix.
57
+ * Add tests for it. This is important so I don't break it in a
58
+ future version unintentionally.
59
+ * Commit, do not mess with rakefile, version, or history.
60
+ (if you want to have your own version, that is fine but
61
+ bump version in a commit by itself I can ignore when I pull)
62
+ * Send me a pull request. Bonus points for topic branches.
63
+
64
+ == Copyright
65
+
66
+ Copyright (c) 2009 Allen Fair. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,45 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "ruby-qmail"
8
+ gem.summary = %Q{Ruby interfaces for Qmail}
9
+ gem.description = %Q{Provides methods to interact with Qmail to send/queue messages, manage bounces, and manage the queue.}
10
+ gem.email = "allen.fair@gmail.com"
11
+ gem.homepage = "http://github.com/afair/ruby-qmail"
12
+ gem.authors = ["Allen Fair"]
13
+ gem.add_development_dependency "rspec", ">= 1.2.9"
14
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
+ end
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
19
+ end
20
+
21
+ require 'spec/rake/spectask'
22
+ Spec::Rake::SpecTask.new(:spec) do |spec|
23
+ spec.libs << 'lib' << 'spec'
24
+ spec.spec_files = FileList['spec/**/*_spec.rb']
25
+ end
26
+
27
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
28
+ spec.libs << 'lib' << 'spec'
29
+ spec.pattern = 'spec/**/*_spec.rb'
30
+ spec.rcov = true
31
+ end
32
+
33
+ task :spec => :check_dependencies
34
+
35
+ task :default => :spec
36
+
37
+ require 'rake/rdoctask'
38
+ Rake::RDocTask.new do |rdoc|
39
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
40
+
41
+ rdoc.rdoc_dir = 'rdoc'
42
+ rdoc.title = "ruby-qmail #{version}"
43
+ rdoc.rdoc_files.include('README*')
44
+ rdoc.rdoc_files.include('lib/**/*.rb')
45
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
data/lib/bounce.rb ADDED
@@ -0,0 +1,44 @@
1
+ module RubyQmail
2
+ # Bounce Handler for Qmail Bounce messages. There are typically two types of bounces
3
+ # * Remote Bounces - When email is accepted by the remote email server, then it cannot be delivered, it
4
+ # sends the message with its own error message to the Return-Path or sender address (not necessarily the
5
+ # same one in the From: header--although some non-RFC-compliant servers still will do this). For this
6
+ # processing, it is useful to enable VERP (RubyQmail does this). VERP (Variable Envelope Return Path) in
7
+ # Qmail appends the recipient's email address to each unique delivery, replacing the @ by the = symbol
8
+ # (returnpath-recipient=recipdomain@example.com).
9
+ # * Undeliverable Bounces - An email is sent back to the reuturn path with a formatted report off all undeliverable
10
+ # addresses (where the remote server could not be reached or accept delivery) and the error message
11
+ # generated by Qmail or returned by the Remote server during the SMTP session.
12
+ class BounceHandler
13
+
14
+
15
+ def initialize(bounce_io)
16
+ @bounce_io = bounce_io
17
+ end
18
+
19
+ # Parses a Qmail bounce message as IO object, then calls block with |address, message| for each bounced address
20
+ def parse(&block)
21
+ address=nil
22
+ mesage=nil
23
+ bounces=0
24
+ @bounce_io.each do |line|
25
+ break if line =~ /^--- Below this line is a copy of the message\./
26
+ if line =~ /^<(\S+)>:/
27
+ address = $1
28
+ message=''
29
+ bounces += 1
30
+ elsif bounces==0
31
+ next
32
+ #elsif line ~! /\S/ # empty
33
+ # yield address, message
34
+ elsif line =~ /Remote host said: (.+)/
35
+ yield address, $1
36
+ #message += line
37
+ end
38
+ end
39
+ bounces
40
+ end
41
+
42
+ end
43
+
44
+ end
data/lib/config.rb ADDED
@@ -0,0 +1,31 @@
1
+ module RubyQmail
2
+ require 'logger'
3
+
4
+ # Configuration for the Qmail system. Loads a configuration YAML file, and accepts a Hash of run-time overrides.
5
+ class Config
6
+ attr_reader :options
7
+ DEFAULTS = {
8
+ :qmqp_port => 628,
9
+ :qmail_root => '/var/qmail',
10
+ :delimiter => '-',
11
+ :logger => Logger.new("#{ENV['HOME']}/logs/ruby-qmail.log")
12
+ }
13
+ QMQP_SERVERS = '/control/qmqpservers'
14
+ QMAIL_QUEUE = '/bin/qmail-queue'
15
+
16
+ def self.load_file(config_file, options={})
17
+ @options = DEFAULTS.merge(options)
18
+ if config_file && File.exists?(config_file)
19
+ @options = YAML.load_file(config_file).merge(@options)
20
+ end
21
+ @options[:qmail_queue] ||= @options[:qmail_root] + QMAIL_QUEUE
22
+ @options[:qmqp_servers] ||= @options[:qmail_root] + QMQP_SERVERS
23
+ @options
24
+ end
25
+
26
+ def method_missing(method)
27
+ @options[method.to_sym]
28
+ end
29
+
30
+ end
31
+ end
data/lib/netstring.rb ADDED
@@ -0,0 +1,13 @@
1
+ module RubyQmail
2
+ module Netstring
3
+
4
+ # Converts the string to a netstring: "length:value,"
5
+ def to_netstring()
6
+ "#{self.size}:#{self},"
7
+ end
8
+ end
9
+ end
10
+
11
+ class String#:nodoc:
12
+ include RubyQmail::Netstring
13
+ end
data/lib/queue.rb ADDED
@@ -0,0 +1,248 @@
1
+ module RubyQmail
2
+
3
+ class Queue
4
+ include Process
5
+ attr_accessor( :return_path, :message, :recipients, :options, :response, :success )
6
+ QMAIL_QUEUE_SUCCESS = 0
7
+ QMAIL_ERRORS = {
8
+ -1 => "Unknown Error",
9
+ 0 => "Success",
10
+ 11 => "Address too long",
11
+ 31 => "Mail server permanently refuses to send the message to any recipients.",
12
+ 51 => "Out of memory.",
13
+ 52 => "Timeout.",
14
+ 53 => "Write error; e.g., disk full.",
15
+ 54 => "Unable to read the message or envelope.",
16
+ 55 => "Unable to read a configuration file.",
17
+ 56 => "Problem making a network connection from this host.",
18
+ 61 => "Problem with the qmail home directory.",
19
+ 62 => "Problem with the queue directory.",
20
+ 63 => "Problem with queue/pid.",
21
+ 64 => "Problem with queue/mess.",
22
+ 65 => "Problem with queue/intd.",
23
+ 66 => "Problem with queue/todo.",
24
+ 71 => "Mail server temporarily refuses to send the message to any recipients.",
25
+ 72 => "Connection to mail server timed out.",
26
+ 73 => "Connection to mail server rejected. ",
27
+ 74 => "Connection to mail server succeeded, but communication failed.",
28
+ 81 => "Internal bug; e.g., segmentation fault.",
29
+ 91 => "Envelope format error"
30
+ }
31
+
32
+ # Class Method to place the message into the Qmail queue.
33
+ def self.insert(return_path, recipients, message, *options)
34
+ q = Queue.new(return_path, recipients, message, *options)
35
+ if q.options.has_key?[:ip] || q.options[:method]==:qmqp
36
+ q.qmqp
37
+ else
38
+ q.qmail_queue
39
+ end
40
+ end
41
+
42
+ # Recipients can be a filename, array or other object that responds to :each, or #to_s resolves to an email address
43
+ # Message can be a filename, string, or other object that responds to :each
44
+ def initialize(return_path=nil, recipients=nil, message=nil, *options)
45
+ parameters(return_path, recipients, message, options)
46
+ end
47
+
48
+ def parameters(return_path, recipients, message, options) #:nodoc:
49
+ @return_path = return_path if return_path
50
+ @options = RubyQmail::Config.load_file( nil, options.last || {})
51
+ @recipients = recipients if recipients
52
+ @recipients = File.new(@recipients) if @recipients.is_a?(String) && File.exists?(@recipients)
53
+ @recipients = [ @recipients.to_s ] unless @recipients.respond_to?(:each)
54
+ @message = message if message
55
+ @message = File.new(@message) if @message.is_a?(String) && File.exists?(@message)
56
+ @message = @message.split(/\n/) if @message.is_a?(String)
57
+
58
+ # Edits the return path for VERP. bounces@example.com => bounces-@example.com-@[]
59
+ if return_path && !@options.has_key?(:noverp)
60
+ rp1, rp2 = return_path.split(/@/)
61
+ @return_path = "#{rp1}#{@options[:delimiter]}@#{rp2}" if (rp1.match(/(.)$/)[1] != @options[:delimiter])
62
+ @return_path += '-@[]' unless @return_path =~ /-@\[\]$/
63
+ end
64
+ end
65
+
66
+ # This calls the Qmail-Queue program, so requires qmail to be installed (does not require it to be currently running).
67
+ def queue(return_path=nil, recipients=nil, message=nil, *options)
68
+ parameters(return_path, recipients, message, options)
69
+ @success = run_qmail_queue() do |msg, env|
70
+ # Send the Message
71
+ @message.each { |m| msg.puts(m) }
72
+ msg.close
73
+
74
+ env.write('F' + @return_path + "\0")
75
+ @recipients.each { |r| env.write('T' + r + "\0") }
76
+ env.write("\0") # End of "file"
77
+ end
78
+ @options[:logger].info("RubyQmail Queue exited:#{@success} #{Queue.qmail_queue_error_message(@success)}")
79
+ return true if @success == QMAIL_QUEUE_SUCCESS
80
+ raise Queue.qmail_queue_error_message(@success)
81
+ end
82
+
83
+ # Maps the qmail-queue exit code to the error message
84
+ def self.qmail_queue_error_message(code) #:nodoc:
85
+ "RubyQmail::Queue Error #{code}:" + QMAIL_ERRORS.has_key?(code) ? QMAIL_ERRORS[code]:QMAIL_ERRORS[-1]
86
+ end
87
+
88
+ # Builds the QMQP request, and opens a connection to the QMQP Server and sends
89
+ # This implemtents the QMQP protocol, so does not need Qmail installed on the host system.
90
+ # System defaults will be used if no ip or port given.
91
+ # Returns true on success, false on failure (see @response), or nul on deferral
92
+ def qmqp(return_path=nil, recipients=nil, message=nil, *options)
93
+ parameters(return_path, recipients, message, options)
94
+
95
+ begin
96
+ ip = @options[:ip] || File.readlines(QMQP_SERVERS).first.chomp
97
+ #puts "CONNECT #{:ip}, #{@options[:qmqp_port]}"
98
+ socket = TCPSocket.new(ip, @options[:qmqp_port])
99
+ raise "QMQP can not connect to #{ip}:#{@options[:qmqp_port]}" unless socket
100
+
101
+ # Build netstring of messagebody+returnpath+recipient...
102
+ nstr = (@message.map.join("\n")+"\n").to_netstring # { |m| m }.join("\t").to_netstring
103
+ nstr += @return_path.to_netstring
104
+ nstr += @recipients.map { |r| r.to_netstring }.join
105
+ socket.send( nstr.to_netstring, 0 )
106
+
107
+ @response = socket.recv(1000) # "23:Kok 1182362995 qp 21894," (its a netstring)
108
+ @success = case @response.match(/^\d+:([KZD])(.+),$/)[1]
109
+ when 'K' : true # success
110
+ when 'Z' : nil # deferral
111
+ when 'D' : false # failure
112
+ else false
113
+ end
114
+ logmsg = "RubyQmail QMQP [#{ip}:#{@options[:qmqp_port]}]: #{@response} return:#{@success}"
115
+ @options[:logger].info(logmsg)
116
+ puts logmsg
117
+ @success
118
+ rescue Exception => e
119
+ @options[:logger].error( "QMQP can not connect to #{@opt[:qmqp_ip]}:#{@options[:qmqp_port]} #{e}" )
120
+ raise e
121
+ ensure
122
+ socket.close if socket
123
+ end
124
+ end
125
+
126
+ # # Like #qmail_queue, but writes directly to the queue, not via the qmail-queue program
127
+ # # Is this a good idea? It expects a unique PID per message.
128
+ # def qmail_queue_direct(return_path=nil, recipients=nil, message=nil, *options)
129
+ # parameters(return_path, recipients, message, options)
130
+ # end
131
+
132
+ # def sendmail(return_path=nil, recipients=nil, message=nil, *options)
133
+ # parameters(return_path, recipients, message, options)
134
+ # end
135
+
136
+ # Sends email directly via qmail-remote. It does not store in the queue, It will halt the process
137
+ # and wait for the network event to complete. If multiple recipients are passed, it will run
138
+ # qmail-remote delivery for each at a time to honor VERP return paths.
139
+ def qmail_remote(return_path=nil, recipients=nil, message=nil, *options)
140
+ parameters(return_path, recipients, message, options)
141
+ rp1, rp2 = @return_path.split(/@/,2)
142
+ rp = @return_path
143
+ @recipients.each do |recip|
144
+ unless @options[:noverp]
145
+ mailbox, host = recip.split(/@/)
146
+ rp = "#{rp1}#{mailbox}=#{host}@#{rp2}"
147
+ end
148
+
149
+ @message.rewind if @message.respond_to?(:rewind)
150
+ cmd = "#{@options[:qmail_root]}+/bin/qmail-remote #{host} #{rp} #{recip}"
151
+ @success = self.spawn_command(cmd) do |send, recv|
152
+ @message.each { |m| send.puts m }
153
+ send.close
154
+ @response = recv.readpartial(1000)
155
+ end
156
+
157
+ @options[:logger].info("RubyQmail Remote #{recip} exited:#{@success} responded:#{@response}")
158
+ end
159
+ return [ @success, @response ] # Last one
160
+ end
161
+
162
+ # Forks, sets up stdin and stdout pipes, and starts the command.
163
+ # IF a block is passed, yeilds to it with [sendpipe, receivepipe],
164
+ # returing the exit code, otherwise returns {:send=>, :recieve=>, :pid=>}
165
+ # qmail-queue does not work with this as it reads from both pipes.
166
+ def spawn_command(command, &block)
167
+ child_read, parent_write = IO.pipe # From parent to child(stdin)
168
+ parent_read, child_write = IO.pipe # From child(stdout) to parent
169
+ @child = fork
170
+
171
+ # Child process
172
+ unless @child #
173
+ $stdin.close # closes FD==0
174
+ child_read.dup # copies to FD==0
175
+ child_read.close
176
+
177
+ $stdout.close # closes FD==1
178
+ child_write.dup # copies to FD==1
179
+ child_write.close
180
+
181
+ Dir.chdir(@options[:qmail_root]) unless @options[:nochdir]
182
+ exec(command)
183
+ raise "Exec spawn_command #{command} failed"
184
+ end
185
+
186
+ # Parent Process with block
187
+ if block_given?
188
+ yield(parent_write, parent_read)
189
+ parent_write.close
190
+ parent_read.close
191
+ wait(@child)
192
+ @success = $? >> 8
193
+ return @sucess
194
+ end
195
+
196
+ # Parent process, no block
197
+ {:send=>parent_write, :receive=>parent_read, :pid=>@child}
198
+ end
199
+
200
+ # Forks, sets up stdin and stdout pipes, and starts qmail-queue.
201
+ # IF a block is passed, yields to it with [sendpipe, receivepipe],
202
+ # and returns the exist cod, otherwise returns {:msg=>pipe, :env=>pipe, :pid=>@child}
203
+ # It exits 0 on success or another code on failure.
204
+ # Qmail-queue Protocol: Reads mail message from File Descriptor 0, then reads Envelope from FD 1
205
+ # Envelope Stream: 'F' + sender_email + "\0" + ("T" + recipient_email + "\0") ... + "\0"
206
+ def run_qmail_queue(command=nil, &block)
207
+ # Set up pipes and qmail-queue child process
208
+ msg_read, msg_write = IO.pipe
209
+ env_read, env_write = IO.pipe
210
+ @child=fork # child? nil : childs_process_id
211
+
212
+ unless @child
213
+ ## Set child's stdin(0) to read from msg
214
+ $stdin.close # FD=0
215
+ msg_read.dup
216
+ msg_read.close
217
+ msg_write.close
218
+
219
+ ## Set child's stdout(1) to read from env
220
+ $stdout.close # FD=1
221
+ env_read.dup
222
+ env_read.close
223
+ env_write.close
224
+
225
+ # Change directory and load command
226
+ Dir.chdir(@options[:qmail_root])
227
+ exec( command || @options[:qmail_queue] )
228
+ raise "Exec qmail-queue failed"
229
+ end
230
+
231
+ # Parent Process with block
232
+ if block_given?
233
+ yield(msg_write, env_write)
234
+ # msg_write.close
235
+ env_write.close
236
+ wait(@child)
237
+ @success = $? >> 8
238
+ # puts "#{$$} parent waited for #{@child} s=#{@success} #{$?.inspect}"
239
+ return @sucess
240
+ end
241
+
242
+ # Parent process, no block
243
+ {:msg=>msg_write, :env=>env_write, :pid=>@child}
244
+ end
245
+
246
+ end
247
+
248
+ end
data/lib/ruby-qmail.rb ADDED
@@ -0,0 +1,6 @@
1
+ require File.dirname(__FILE__) + "/netstring"
2
+ require File.dirname(__FILE__) + "/config"
3
+ require File.dirname(__FILE__) + "/queue"
4
+ # require File.dirname(__FILE__) + "/"
5
+ # require File.dirname(__FILE__) + "/"
6
+ # require File.dirname(__FILE__) + "/"
@@ -0,0 +1,33 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+ require 'rubygems'
3
+ require 'ruby-debug'
4
+
5
+ describe "RubyQmail" do
6
+
7
+ before(:all) do
8
+ @rqq = RubyQmail::Queue.new()
9
+ @rpath = 'allen-whitelist@biglist.com'
10
+ @recip = %w( allen-1@biglist.com allen-2@biglist.com )
11
+ @msg = %q(To: allen-admin@biglist.com
12
+ From: allen-listmaster@biglist.com
13
+ Subject: ruby-qmail
14
+
15
+ testing 1 2 3.
16
+ later!
17
+ )
18
+ end
19
+
20
+ it "should add a #to_netstring method to the string class" do
21
+ "qmail".to_netstring.should == "5:qmail,"
22
+ end
23
+
24
+ it "should submit a message by qmail-queue" do
25
+ @rqq.queue( @rpath, @recip, @msg ).should_equal true
26
+ end
27
+
28
+ it "should submit a message by QMQP" do
29
+ @rqq.qmqp( @rpath, @recip, @msg, :ip=>'173.161.130.227', :qmqp_port=>631 ).should_equal true
30
+ end
31
+
32
+
33
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,9 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'ruby-qmail'
4
+ require 'spec'
5
+ require 'spec/autorun'
6
+
7
+ Spec::Runner.configure do |config|
8
+
9
+ end
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ruby-qmail
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Allen Fair
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-28 00:00:00 -04:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rspec
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.2.9
24
+ version:
25
+ description: Provides methods to interact with Qmail to send/queue messages, manage bounces, and manage the queue.
26
+ email: allen.fair@gmail.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - LICENSE
33
+ - README.rdoc
34
+ files:
35
+ - .document
36
+ - .gitignore
37
+ - LICENSE
38
+ - README.rdoc
39
+ - Rakefile
40
+ - VERSION
41
+ - lib/bounce.rb
42
+ - lib/config.rb
43
+ - lib/netstring.rb
44
+ - lib/queue.rb
45
+ - lib/ruby-qmail.rb
46
+ - spec/ruby-qmail_spec.rb
47
+ - spec/spec.opts
48
+ - spec/spec_helper.rb
49
+ has_rdoc: true
50
+ homepage: http://github.com/afair/ruby-qmail
51
+ licenses: []
52
+
53
+ post_install_message:
54
+ rdoc_options:
55
+ - --charset=UTF-8
56
+ require_paths:
57
+ - lib
58
+ required_ruby_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: "0"
63
+ version:
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: "0"
69
+ version:
70
+ requirements: []
71
+
72
+ rubyforge_project:
73
+ rubygems_version: 1.3.5
74
+ signing_key:
75
+ specification_version: 3
76
+ summary: Ruby interfaces for Qmail
77
+ test_files:
78
+ - spec/ruby-qmail_spec.rb
79
+ - spec/spec_helper.rb