smailr 0.6.2 → 0.8.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 137f8c287a816f8de1cc332745f1b7a7e7c6248d
4
- data.tar.gz: 20e72c156dc8fdc41c17778c0436eadbf8187d0b
2
+ SHA256:
3
+ metadata.gz: 6effb269294c6ec1f81fc25e2e05ee1e3f4ae6d98eef3d44a0fa64e939d8a8bc
4
+ data.tar.gz: 8a2e12d33594449fdacf111a06a4813e65454b45141af4bd884de25edb5c3c06
5
5
  SHA512:
6
- metadata.gz: ce459c937af2283a0411329fc04f5369d2894557fb288b3a501d2c20d10ffebf9fbbd382a4d12fd06163f34e25daa395f5789c4b101a4c26b9dd1b8be84ae56e
7
- data.tar.gz: 3166aba8a9629e697c316c64e72a938c3c784b150911b784fb2ec39ab28dbf6fcad23ac8979befddd2f48cf1022567d9bdc0b7b3ef71e10a0fcbffd89e495c81
6
+ metadata.gz: 13a0e5bb2fb7d5d3e0e92c87a774a44750015d6daf8feb579ed1391490893f866ce45ef38d692b8473fc41b024977b476c0fc02c84cf3183b23b33c6107341e8
7
+ data.tar.gz: 847ef00e13a7d0e5624ad2f5b3609fcdbb94a3170cda4245eb7c4cfc7e23d77460ff8d368f6b4372123af23309f0c867ad7e1ba960932b49b97461db8a78f54e
data/README.md CHANGED
@@ -157,9 +157,9 @@ automatically. Open mutt for the specified mailbox:
157
157
  ### Verify
158
158
 
159
159
  Smailr generates a report via the Port25 SMTP Verifier. It generates a test,
160
- sends it to check-auth-user=eaxmple.comt@verifier.port25.com, which will in
161
- return generate a echo message with a report about a results from many SMTP
162
- combonents: SPF, SenderID, DomainKeys, DKIM and Spamassassin.
160
+ sends it to check-auth-user=eaxmple.comt@verifier.port25.com, which will in
161
+ return generate a echo message with a report about results of many SMTP
162
+ components: SPF, SenderID, DomainKeys, DKIM and Spamassassin.
163
163
 
164
164
  To generate a message, sent from user@example.com and return the report to the
165
165
  same address simply call the following command:
data/bin/smailr CHANGED
@@ -1,241 +1,15 @@
1
1
  #!/usr/bin/env ruby
2
- $: << File.expand_path('../../lib', __FILE__)
3
-
4
- require 'smailr'
5
-
2
+ #$: << File.expand_path('../../lib', __FILE__)
6
3
  #
7
- # CLI Helpers
4
+ #require 'pathname'
5
+ #ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
6
+ # Pathname.new(__FILE__).realpath)
8
7
  #
9
- def determine_object(string)
10
- return :domain if string =~ /^[^@][A-Z0-9.-]+\.[A-Z]{2,6}$/i
11
- return :address if string =~ /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,6}$/i
12
- end
13
-
14
- def ask_password
15
- min_password_length = Smailr.config["password_policy"]["length"]
16
-
17
- password = ask("Password: ") { |q| q.echo = "*" }
18
- confirm = ask("Confirm: ") { |q| q.echo = "*" }
19
-
20
- if password != confirm
21
- say("Mismatch; try again.")
22
- ask_password
23
- end
24
-
25
- if password.length < min_password_length.to_i
26
- say("Too short; try again.")
27
- ask_password
28
- end
29
-
30
- password
31
- end
32
-
33
- program :version, Smailr::VERSION
34
- program :description, 'Simple MAIL manageR - Virtual mail hosting management from the CLI'
35
-
36
-
37
- #
38
- # Commands
39
- #
40
- command :add do |c|
41
- c.syntax = 'smailr add domain | mailbox | alias [options]'
42
- c.summary = 'Add a new domain, mailbox or alias to the mail system.'
43
- c.example 'Add a domain', 'smailr add example.com'
44
- c.example 'Add a mailbox', 'smailr add user@example.com'
45
- c.example 'Add an alias', 'smailr add alias@localdom.com --alias user@example.com,user1@example.com'
46
- c.example 'Setup DKIM for a domain', 'smailr add ono.at --dkim'
47
- c.option '--alias DESTINATION', String, 'Specify the alias destination.'
48
- c.option '--password PASSWORD', String, 'The password for a new mailbox. If you omit this option, it prompts for one.'
49
- c.option '--dkim SELECTOR', String, 'Add a DKIM Key with the specified selector for domain.'
50
- c.action do |args, options|
51
- address = args[0]
52
- type = determine_object(address)
53
-
54
- case type
55
- when :domain
56
- if options.dkim
57
- selector = options.dkim
58
- key = Smailr::Dkim.add(address, selector)
59
-
60
- puts "public-key " + key.to_a[1..-2].join.gsub(/\n/, '')
61
- else
62
- Smailr::Domain.add(address)
63
- end
64
-
65
- when :address
66
- if options.alias
67
- source = args[0]
68
- destinations = options.alias.split(',')
69
- Smailr::Alias.add(source, destinations)
70
- else
71
- options.password ||= ask_password
72
- Smailr::Mailbox.add(address, options.password)
73
- end
74
-
75
- end
76
- end
77
- end
78
-
79
- command :ls do |c|
80
- c.syntax = 'smailr ls [domain]'
81
- c.summary = 'List domains or mailboxes of a specific domain.'
82
- c.action do |args, options|
83
- case args[0]
84
- when /^[^@][A-Z0-9.-]+\.[A-Z]{2,6}$/i then
85
- domain = Smailr::Model::Domain[:fqdn => args[0]]
86
- domain.mailboxes.each do |mbox|
87
- puts "m: #{mbox.localpart}@#{args[0]}"
88
- end
89
- domain.aliases.each do |aliass|
90
- puts "a: #{aliass.localpart}@#{args[0]} > #{aliass.dstlocalpart}@#{aliass.dstdomain}"
91
- end
92
- when nil
93
- domains = Smailr::DB[:domains]
94
- domains.all.each do |d|
95
- domain = Smailr::Model::Domain[:fqdn => d[:fqdn]]
96
- puts d[:fqdn]
97
- end
98
- else
99
- error "You can either list a domains or a domains addresses."
100
- exit 1
101
- end
102
- end
103
- end
104
-
105
- command :rm do |c|
106
- c.syntax = 'smailr rm domain | mailbox [options]'
107
- c.summary = 'Remove a domain, mailbox or alias known to the mail system.'
108
- c.example 'Remove a domain', 'smailr rm example.com'
109
- c.option '--force', 'Force the operation, do not ask for confirmation.'
110
- c.option '--dkim SELECTOR', String, 'Remove a dkim key.'
111
- c.option '--alias DESTINATION', String, 'Specify the destination you want to remove from the alias.'
112
- c.action do |args, options|
113
- address = args[0]
114
- type = determine_object(address)
115
- case type
116
- when :domain
117
- if options.dkim
118
- selecotr = options.dkim
119
- Smailr::Dkim.rm(address, selector)
120
- else
121
- Smailr::Domain.rm(address, options.force)
122
- end
123
-
124
- when :address
125
- if options.alias
126
- source = args[0]
127
- destinations = options.alias.split(',')
8
+ #require 'rubygems'
9
+ #require 'bundler/setup'
128
10
 
129
- Smailr::Alias.rm(source, destinations)
130
- else
131
- Smailr::Mailbox.rm(address, options)
132
- end
133
- end
134
- end
135
- end
136
-
137
- command :passwd do |c|
138
- c.syntax = 'smailr passwd mailbox'
139
- c.summary = 'Update a users password.'
140
- c.action do |args,options|
141
- address = args[0]
142
- password = ask_password
143
- Smailr::Mailbox.update_password(address, password)
144
- end
145
- end
146
-
147
-
148
- command :setup do |c|
149
- c.syntax = 'smailr setup'
150
- c.summary = 'Install all required components on a mailserver'
151
- c.action do |args,options|
152
- Smailr::Setup.run
153
- end
154
- end
155
-
156
-
157
- command :migrate do |c|
158
- c.syntax = 'smailr migrate [options]'
159
- c.summary = 'Create database and run migrations'
160
- c.option '--to VERSION', String, 'Migrate the database to a specifict version.'
161
- c.action do |args,options|
162
- require 'sequel/extensions/migration'
163
- raise "Database not configured" unless Smailr::DB
164
-
165
- if options.version.nil?
166
- Sequel::Migrator.apply(Smailr::DB, Smailr.migrations_directory)
167
- else
168
- Sequel::Migrator.apply(Smailr::DB, Smailr.migrations_directory, :target => options.version.to_i)
169
- end
170
- end
171
- end
172
-
173
-
174
- command :mutt do |c|
175
- base = Smailr.config["mail_spool_path"]
176
-
177
- c.syntax = "smailr mutt address"
178
- c.summary = "View the mailbox of the specified address in mutt."
179
- c.description = "Open the mailbox of the specified address in mutt.\n\n " +
180
- "Requires that mutt is installed and tries to find a suitable maildir in: " + base
181
- c.example 'Open test@example.com', 'smailr mutt test@example.com'
182
- c.action do |args,options|
183
- localpart, fqdn = args[0].split('@')
184
-
185
- mutt = `command -v mutt || { echo "Please install mutt first. Aborting." >&2; exit 1; }`
186
- if $?
187
-
188
- possibilities = [
189
- "#{base}/#{fqdn}/#{localpart}/Maildir",
190
- "#{base}/users/#{fqdn}/#{localpart}/Maildir",
191
- "#{base}/users/#{fqdn}/#{localpart}/.maildir",
192
- "#{base}/users/#{fqdn}/#{localpart}"
193
- ]
194
-
195
- possibilities.each do |path|
196
- if File.readable?(path)
197
- puts "Opening maildir #{path} with mutt."
198
- exec "MAIL=#{path} MAILDIR=#{path} #{mutt} -mMaildir"
199
- end
200
- end
201
- end
202
- end
203
- end
204
-
205
- command :verify do |c|
206
- c.syntax = "smailr verify address"
207
- c.summary = "Send out a test message to verify a domains configuration via verifier.port25.com"
208
- c.description = "A reply email will be sent back to you with an analysis of the message’s authentication" +
209
- "status. The report will perform the following checks: SPF, SenderID, DomainKeys, DKIM " +
210
- "and Spamassassin.\n\n"
211
- c.example 'Verify test@example.com (report will be sent to test@example.com)', 'smailr verify test@example.com'
212
- c.example 'Verify test@example.com, send report to root@example.com', 'smailr verify test@example.com --report-to root@example.com'
213
-
214
- c.option '-r DESTINATION', '--report-to DESTINATION', String, 'Send the report to the specified address instead.'
215
-
216
- c.action do |args,options|
217
- from = args[0]
218
- options.default :report_to => from
219
-
220
- dstlocalpart, dstfqdn = options.report_to.split('@')
221
-
222
- require 'socket'
223
- require 'date'
224
- require 'net/smtp'
225
- Net::SMTP.start('localhost', 25) do |smtp|
226
- to = "check-auth-#{dstlocalpart}=#{dstfqdn}@verifier.port25.com"
227
-
228
- message = [
229
- "From: #{from}",
230
- "To: #{to}",
231
- "Subject: Port25 Mail Verification Test",
232
- "Date: #{DateTime.now.strftime("%a, %d %b %Y %H:%M:%S %z")}",
233
- "",
234
- "This is a test message for the port25 mail verification test, it was",
235
- "sent from the following server: #{Socket.gethostname}."
236
- ].join("\r\n")
11
+ require 'smailr'
12
+ require 'smailr/cli'
13
+ require 'smailr/setup'
237
14
 
238
- smtp.send_message(message, from, to)
239
- end
240
- end
241
- end
15
+ Smailr::Cli.new.run
data/lib/smailr/alias.rb CHANGED
@@ -1,40 +1,39 @@
1
1
  module Smailr
2
- module Alias
3
- def self.add(source, destinations)
4
- srclocalpart, srcdomain = source.split('@')
2
+ class Alias
3
+ def self.add(source, destinations)
4
+ srclocalpart, srcdomain = source.split('@')
5
5
 
6
- # We don't want aliases for non-local domains, since the
7
- # exim router won't accept it.
8
- if not Model::Domain[:fqdn => srcdomain].exists?
9
- say_error "You are trying to add an alias for a non-local domain: #{source}"
10
- exit 1
11
- end
6
+ # We don't want aliases for non-local domains, since the
7
+ # exim router won't accept it.
8
+ if not Model::Domain[:fqdn => srcdomain].exists?
9
+ raise MissingDomain, "You are trying to add an alias for a non existing domain: #{source}"
10
+ end
12
11
 
13
- destinations.each do |dst|
14
- dstlocalpart, dstdomain = dst.split('@')
12
+ destinations.each do |dst|
13
+ dstlocalpart, dstdomain = dst.split('@')
15
14
 
16
- puts "Adding alias: #{source} -> #{dst}"
15
+ Smailr::logger.warn("Adding alias: #{source} -> #{dst}")
17
16
 
18
- Model::Alias.find_or_create(:domain => Model::Domain[:fqdn => srcdomain],
19
- :localpart => srclocalpart,
20
- :dstdomain => dstdomain,
21
- :dstlocalpart => dstlocalpart)
22
- end
23
- end
17
+ Model::Alias.find_or_create(:domain => Model::Domain[:fqdn => srcdomain],
18
+ :localpart => srclocalpart,
19
+ :dstdomain => dstdomain,
20
+ :dstlocalpart => dstlocalpart)
21
+ end
22
+ end
24
23
 
25
- def self.rm(source, destinations)
26
- srclocalpart, srcdomain = source.split('@')
24
+ def self.rm(source, destinations)
25
+ srclocalpart, srcdomain = source.split('@')
27
26
 
28
- destinations.each do |dst|
29
- puts "Removing alias: #{source} -> #{dst}"
27
+ destinations.each do |dst|
28
+ Smailr::logger.warn("Removing alias: #{source} -> #{dst}")
30
29
 
31
- dstlocalpart, dstdomain = dst.split('@')
30
+ dstlocalpart, dstdomain = dst.split('@')
32
31
 
33
- Model::Alias.filter(:domain => Model::Domain[:fqdn => srcdomain],
34
- :localpart => srclocalpart,
35
- :dstdomain => dstdomain,
36
- :dstlocalpart => dstlocalpart).delete
37
- end
38
- end
32
+ Model::Alias.filter(:domain => Model::Domain[:fqdn => srcdomain],
33
+ :localpart => srclocalpart,
34
+ :dstdomain => dstdomain,
35
+ :dstlocalpart => dstlocalpart).delete
36
+ end
39
37
  end
38
+ end
40
39
  end
data/lib/smailr/cli.rb ADDED
@@ -0,0 +1,276 @@
1
+ require 'commander'
2
+
3
+ class String
4
+ def unindent
5
+ gsub(/^#{scan(/^\s*/).min_by{|l|l.length}}/, "")
6
+ end
7
+ end
8
+
9
+ module Smailr
10
+ class Cli
11
+
12
+ include Commander::Methods
13
+
14
+ ## Our own CLI Helpers
15
+
16
+ # Determine whether we the passed string is a domain or a mail
17
+ # address.
18
+ #
19
+ # Returns either :domain or :address
20
+ def determine_object(string)
21
+ return :domain if string =~ /^[^@][A-Z0-9.-]+\.[A-Z]{2,6}$/i
22
+ return :address if string =~ /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,6}$/i
23
+ end
24
+
25
+ # Run an interactive cli dialog to enter and confirm a password.
26
+ #
27
+ # Returns a string containing the entered password if password and
28
+ # confirmation match.
29
+ def ask_password
30
+ min_password_length = Smailr.config["password_policy"]["length"]
31
+
32
+ password = ask("Password: ") { |q| q.echo = "*" }
33
+ confirm = ask("Confirm: ") { |q| q.echo = "*" }
34
+
35
+ if password != confirm
36
+ say("Mismatch; try again.")
37
+ ask_password
38
+ end
39
+
40
+ if password.length < min_password_length.to_i
41
+ say("Too short; try again.")
42
+ ask_password
43
+ end
44
+
45
+ password
46
+ end
47
+
48
+ # Initialize the Cli
49
+ def run
50
+ program :description, 'smailr - Virtual Mail Hosting Management CLI'
51
+ program :version, Smailr::VERSION
52
+
53
+ ### Commands
54
+
55
+ command :add do |c|
56
+ c.syntax = 'smailr add domain | mailbox | alias [options]'
57
+ c.summary = 'Add a new domain, mailbox or alias to the mail system.'
58
+ c.example 'Add a domain', 'smailr add example.com'
59
+ c.example 'Add a mailbox', 'smailr add user@example.com'
60
+ c.example 'Add an alias', 'smailr add alias@localdom.com --alias user@example.com,user1@example.com'
61
+ c.example 'Setup DKIM for a domain', 'smailr add ono.at --dkim mail'
62
+ c.option '--alias DESTINATION', String, 'Specify the alias destination.'
63
+ c.option '--password PASSWORD', String, 'The password for a new mailbox. If you omit this option, it prompts for one.'
64
+ c.option '--dkim SELECTOR', String, 'Add a DKIM Key with the specified selector for domain.'
65
+ c.action do |args, options|
66
+ address = args[0]
67
+ type = determine_object(address)
68
+
69
+ case type
70
+ when :domain
71
+ if options.dkim
72
+ selector = options.dkim
73
+ key = Smailr::Dkim.add(address, selector)
74
+ key_fmt = key.split("\n").slice(1..-2).join
75
+
76
+ puts <<-EOM.unindent
77
+ .
78
+ DKIM is active now, please setup domainkey records in zone #{address}:
79
+
80
+ _domainkey IN TXT "t=y\; o=~\;"
81
+ #{selector}._domainkey IN TXT "v=DKIM1\; t=y\; k=rsa\; p=#{key_fmt}
82
+ .
83
+ EOM
84
+ else
85
+ Smailr::Domain.add(address)
86
+ end
87
+
88
+ when :address
89
+ if options.alias
90
+ source = args[0]
91
+ destinations = options.alias.split(',')
92
+ Smailr::Alias.add(source, destinations)
93
+ else
94
+ options.password ||= ask_password
95
+ Smailr::Mailbox.add(address, options.password)
96
+ end
97
+
98
+ end
99
+ end
100
+ end
101
+
102
+ command :ls do |c|
103
+ c.syntax = 'smailr ls [domain]'
104
+ c.summary = 'List domains or mailboxes and aliases of a specific domain.'
105
+ c.action do |args, options|
106
+ case args[0]
107
+ when /^[^@][A-Z0-9.-]+\.[A-Z]{2,6}$/i then
108
+ domain = Smailr::Model::Domain[:fqdn => args[0]]
109
+ domain.mailboxes.each do |mbox|
110
+ puts "m: #{mbox.localpart}@#{args[0]}"
111
+ end
112
+ domain.aliases.each do |aliass|
113
+ puts "a: #{aliass.localpart}@#{args[0]} > #{aliass.dstlocalpart}@#{aliass.dstdomain}"
114
+ end
115
+ when nil
116
+ domains = Smailr::DB[:domains]
117
+ domains.all.each do |d|
118
+ domain = Smailr::Model::Domain[:fqdn => d[:fqdn]]
119
+ puts d[:fqdn]
120
+ end
121
+ else
122
+ error "You can either list a domains or a domains addresses."
123
+ exit 1
124
+ end
125
+ end
126
+ end
127
+
128
+ command :rm do |c|
129
+ c.syntax = 'smailr rm domain | mailbox [options]'
130
+ c.summary = 'Remove a domain, mailbox or alias known to the mail system.'
131
+ c.example 'Remove a domain', 'smailr rm example.com'
132
+ c.option '--force', 'Force the operation, do not ask for confirmation.'
133
+ c.option '--dkim SELECTOR', String, 'Remove a dkim key.'
134
+ c.option '--alias DESTINATION', String, 'Specify the destination you want to remove from the alias.'
135
+ c.action do |args, options|
136
+ address = args[0]
137
+ type = determine_object(address)
138
+ case type
139
+ when :domain
140
+ if options.dkim
141
+ selector = options.dkim
142
+ Smailr::Dkim.rm(address, selector)
143
+ else
144
+ Smailr::Domain.rm(address, options.force)
145
+ end
146
+
147
+ when :address
148
+ if options.alias
149
+ source = args[0]
150
+ destinations = options.alias.split(',')
151
+
152
+ Smailr::Alias.rm(source, destinations)
153
+ else
154
+ Smailr::Mailbox.rm(address, options)
155
+ end
156
+ end
157
+ end
158
+ end
159
+
160
+ command :passwd do |c|
161
+ c.syntax = 'smailr passwd mailbox'
162
+ c.summary = 'Update a users password.'
163
+ c.action do |args,options|
164
+ address = args[0]
165
+ password = ask_password
166
+ Smailr::Mailbox.update_password(address, password)
167
+ end
168
+ end
169
+
170
+
171
+ command :setup do |c|
172
+ c.syntax = 'smailr setup'
173
+ c.summary = 'Install all required components on a mailserver'
174
+ c.action do |args,options|
175
+ Smailr::Setup.new.run
176
+ end
177
+ end
178
+
179
+
180
+ command :migrate do |c|
181
+ c.syntax = 'smailr migrate [options]'
182
+ c.summary = 'Create database and run migrations'
183
+ c.option '--to VERSION', String, 'Migrate the database to a specifict version.'
184
+ c.action do |args,options|
185
+ require 'sequel/extensions/migration'
186
+ raise "Database not configured" unless Smailr::DB
187
+
188
+ if options.to.nil?
189
+ if Sequel::Migrator.is_current?(Smailr::DB, Smailr.migrations_directory)
190
+ puts "Database schema already up to date. Exiting"
191
+ exit 0
192
+ end
193
+
194
+ puts "Running database migrations to latest version."
195
+ Sequel::Migrator.apply(Smailr::DB, Smailr.migrations_directory)
196
+ else
197
+ puts "Running database migrations to version: #{options.to}"
198
+ Sequel::Migrator.apply(Smailr::DB, Smailr.migrations_directory, options.to.to_i)
199
+ end
200
+ end
201
+ end
202
+
203
+
204
+ command :mutt do |c|
205
+ base = Smailr.config["mail_spool_path"]
206
+
207
+ c.syntax = "smailr mutt address"
208
+ c.summary = "View the mailbox of the specified address in mutt."
209
+ c.description = "Open the mailbox of the specified address in mutt.\n\n " +
210
+ "Requires that mutt is installed and tries to find a suitable maildir in: " + base
211
+ c.example 'Open test@example.com', 'smailr mutt test@example.com'
212
+ c.action do |args,options|
213
+ localpart, fqdn = args[0].split('@')
214
+
215
+ mutt = `command -v mutt || { echo "Please install mutt first. Aborting." >&2; exit 1; }`
216
+ if $?
217
+
218
+ possibilities = [
219
+ "#{base}/#{fqdn}/#{localpart}/Maildir",
220
+ "#{base}/users/#{fqdn}/#{localpart}/Maildir",
221
+ "#{base}/users/#{fqdn}/#{localpart}/.maildir",
222
+ "#{base}/users/#{fqdn}/#{localpart}"
223
+ ]
224
+
225
+ possibilities.each do |path|
226
+ if File.readable?(path)
227
+ puts "Opening maildir #{path} with mutt."
228
+ exec "MAIL=#{path} MAILDIR=#{path} #{mutt} -mMaildir"
229
+ end
230
+ end
231
+ end
232
+ end
233
+ end
234
+
235
+ command :verify do |c|
236
+ c.syntax = "smailr verify address"
237
+ c.summary = "Send out a test message to verify a domains configuration via verifier.port25.com"
238
+ c.description = "A reply email will be sent back to you with an analysis of the message’s authentication" +
239
+ "status. The report will perform the following checks: SPF, SenderID, DomainKeys, DKIM " +
240
+ "and Spamassassin.\n\n"
241
+ c.example 'Verify test@example.com (report will be sent to test@example.com)', 'smailr verify test@example.com'
242
+ c.example 'Verify test@example.com, send report to root@example.com', 'smailr verify test@example.com --report-to root@example.com'
243
+
244
+ c.option '-r DESTINATION', '--report-to DESTINATION', String, 'Send the report to the specified address instead.'
245
+
246
+ c.action do |args,options|
247
+ from = args[0]
248
+ options.default :report_to => from
249
+
250
+ dstlocalpart, dstfqdn = options.report_to.split('@')
251
+
252
+ require 'socket'
253
+ require 'date'
254
+ require 'net/smtp'
255
+ Net::SMTP.start('localhost', 25) do |smtp|
256
+ to = "check-auth-#{dstlocalpart}=#{dstfqdn}@verifier.port25.com"
257
+
258
+ message = [
259
+ "From: #{from}",
260
+ "To: #{to}",
261
+ "Subject: Port25 Mail Verification Test",
262
+ "Date: #{DateTime.now.strftime("%a, %d %b %Y %H:%M:%S %z")}",
263
+ "",
264
+ "This is a test message for the port25 mail verification test, it was",
265
+ "sent from the following server: #{Socket.gethostname}."
266
+ ].join("\r\n")
267
+
268
+ smtp.send_message(message, from, to)
269
+ end
270
+ end
271
+ end
272
+
273
+ run!
274
+ end
275
+ end
276
+ end
data/lib/smailr/dkim.rb CHANGED
@@ -1,37 +1,33 @@
1
- require 'date'
2
- require 'openssl'
3
-
4
1
  module Smailr
5
- module Dkim
6
- def self.add(fqdn, selector)
7
- if not Model::Domain[:fqdn => fqdn]
8
- say_error "You trying to add a DKIM key for a non existing domain: #{fqdn}"
9
- exit 1
10
- end
2
+ class Dkim
3
+ def self.add(fqdn, selector)
4
+ unless Model::Domain[:fqdn => fqdn]
5
+ raise MissingDomain, "You trying to add a DKIM key for a non existing domain: #{fqdn}"
6
+ end
11
7
 
12
- private_key, public_key = generate_rsa_key
8
+ private_key, public_key = generate_rsa_key
13
9
 
14
- dkim = Model::Dkim.for_domain!(fqdn, selector)
15
- dkim.private_key = private_key
16
- dkim.public_key = public_key
17
- dkim.selector = selector
18
- dkim.save
10
+ dkim = Model::Dkim.for_domain!(fqdn, selector)
11
+ dkim.private_key = private_key
12
+ dkim.public_key = public_key
13
+ dkim.selector = selector
14
+ dkim.save
19
15
 
20
- # Return the key so it can be used for automation
21
- dkim.public_key
22
- end
16
+ # Return the key so it can be used for automation
17
+ dkim.public_key
18
+ end
23
19
 
24
- def self.rm(fqdn, selector)
25
- dkim = Model::Dkim.for_domain(fqdn, selector)
26
- dkim.destroy
27
- end
20
+ def self.rm(fqdn, selector)
21
+ dkim = Model::Dkim.for_domain(fqdn, selector)
22
+ dkim.destroy
23
+ end
28
24
 
29
- private
25
+ private
30
26
 
31
- def self.generate_rsa_key(length = 1024)
32
- rsa_key = OpenSSL::PKey::RSA.new(length)
33
- [ rsa_key.to_pem,
34
- rsa_key.public_key.to_pem ]
35
- end
27
+ def self.generate_rsa_key(length = 1024)
28
+ rsa_key = OpenSSL::PKey::RSA.new(length)
29
+ [ rsa_key.to_pem,
30
+ rsa_key.public_key.to_pem ]
36
31
  end
32
+ end
37
33
  end
data/lib/smailr/domain.rb CHANGED
@@ -1,18 +1,17 @@
1
1
  module Smailr
2
- module Domain
3
- def self.add(fqdn)
4
- puts "Adding domain: #{fqdn}"
5
- Model::Domain.create(:fqdn => fqdn)
6
- end
7
-
8
- def self.rm(fqdn, force = false)
9
- if force or
10
- agree("Do you want to remove the domain #{fqdn} and all related items? (yes/no) ")
2
+ class Domain
3
+ def self.add(fqdn)
4
+ Smailr::logger.warn("Adding domain: #{fqdn}")
5
+ Model::Domain.create(:fqdn => fqdn)
6
+ end
11
7
 
12
- domain = Model::Domain[:fqdn => fqdn]
13
- domain.rm_related
14
- domain.destroy
15
- end
16
- end
8
+ def self.rm(fqdn, force = false)
9
+ # TODO - only require force, if related entries exist
10
+ if force
11
+ domain = Model::Domain[:fqdn => fqdn]
12
+ domain.rm_related
13
+ domain.destroy
14
+ end
17
15
  end
16
+ end
18
17
  end
@@ -1,32 +1,31 @@
1
1
  module Smailr
2
- module Mailbox
3
- def self.add(address, password)
4
- puts "Adding mailbox: #{address}"
2
+ class Mailbox
3
+ def self.add(address, password)
4
+ Smailr::logger.warn("Adding mailbox: #{address}")
5
5
 
6
- fqdn = address.split('@')[1]
6
+ fqdn = address.split('@')[1]
7
7
 
8
- if not Model::Domain[:fqdn => fqdn]
9
- say_error "Trying to add a mailbox for a non existing domain: #{fqdn}"
10
- exit 1
11
- end
8
+ if not Model::Domain[:fqdn => fqdn]
9
+ raise MissingDomain, "Trying to add a mailbox for a non existing domain: #{fqdn}"
10
+ end
12
11
 
13
- mbox = Model::Mailbox.for_address!(address)
14
- mbox.password = password
15
- mbox.save
16
- end
12
+ mbox = Model::Mailbox.for_address!(address)
13
+ mbox.password = password
14
+ mbox.save
15
+ end
17
16
 
18
- def self.update_password(address, password)
19
- mbox = Model::Mailbox.for_address(address)
20
- mbox.password = password
21
- mbox.save
22
- end
17
+ def self.update_password(address, password)
18
+ mbox = Model::Mailbox.for_address(address)
19
+ mbox.password = password
20
+ mbox.save
21
+ end
23
22
 
24
- def self.rm(address, options)
25
- puts "Removing mailbox (from database): #{address}"
23
+ def self.rm(address, options)
24
+ Smailr::logger.warn("Removing mailbox (from database): #{address}")
26
25
 
27
- mbox = Model::Mailbox.for_address(address)
28
- mbox.rm_related
29
- mbox.destroy
30
- end
26
+ mbox = Model::Mailbox.for_address(address)
27
+ mbox.rm_related
28
+ mbox.destroy
31
29
  end
30
+ end
32
31
  end
data/lib/smailr/model.rb CHANGED
@@ -1,68 +1,74 @@
1
- require 'digest/sha1'
1
+ require 'bcrypt'
2
2
 
3
3
  module Smailr
4
- module Model
5
- class Domain < Sequel::Model
6
- one_to_many :mailboxes
7
- one_to_many :aliases
8
- one_to_many :dkims
4
+ module Model
5
+ class Domain < Sequel::Model
6
+ one_to_many :mailboxes
7
+ one_to_many :aliases
8
+ one_to_many :dkims
9
9
 
10
- def rm_related
11
- self.remove_all_mailboxes
12
- self.remove_all_aliases
13
- self.remove_all_dkims
14
- end
15
- end
10
+ def rm_related
11
+ self.remove_all_mailboxes
12
+ self.remove_all_aliases
13
+ self.remove_all_dkims
14
+ end
15
+ end
16
+
17
+ class Dkim < Sequel::Model
18
+ many_to_one :domain
16
19
 
17
- class Dkim < Sequel::Model
18
- many_to_one :domain
20
+ def self.for_domain(fqdn, selector)
21
+ self[:domain => Domain[:fqdn => fqdn], :selector => selector]
22
+ end
19
23
 
20
- def self.for_domain(fqdn, selector)
21
- self[:domain => Domain[:fqdn => fqdn], :selector => selector]
22
- end
24
+ def self.for_domain!(fqdn, selector)
25
+ find_or_create(:domain => Domain[:fqdn => fqdn], :selector => selector)
26
+ end
27
+ end
23
28
 
24
- def self.for_domain!(fqdn, selector)
25
- find_or_create(:domain => Domain[:fqdn => fqdn], :selector => selector)
26
- end
27
- end
29
+ class Mailbox < Sequel::Model
30
+ many_to_one :domain
28
31
 
29
- class Mailbox < Sequel::Model
30
- many_to_one :domain
32
+ def password=(clear)
33
+ crypted = BCrypt::Password.create(
34
+ clear,
35
+ cost: BCrypt::Engine::DEFAULT_COST,
36
+ salt: BCrypt::Engine.generate_salt
37
+ )
31
38
 
32
- def password=(clear)
33
- self[:password] = Digest::SHA1.hexdigest(clear)
34
- self[:password_scheme] = "{SHA}"
35
- end
39
+ self[:password_scheme] = '{BLF-CRYPT}'
40
+ self[:password] = crypted
41
+ end
36
42
 
37
- def rm_related
38
- self.aliases.destroy
39
- end
43
+ def rm_related
44
+ self.aliases.destroy
45
+ end
40
46
 
41
- def aliases
42
- Model::Alias.where(
43
- :dstlocalpart => self.localpart,
44
- :dstdomain => self.domain.fqdn
45
- )
46
- end
47
+ def aliases
48
+ Model::Alias.where(
49
+ :dstlocalpart => self.localpart,
50
+ :dstdomain => self.domain.fqdn
51
+ )
52
+ end
47
53
 
48
- def self.domain(fqdn)
49
- Domain[:fqdn => fqdn]
50
- end
54
+ def self.domain(fqdn)
55
+ Domain[:fqdn => fqdn]
56
+ end
51
57
 
52
- def self.for_address(address)
53
- localpart, fqdn = address.split('@')
54
- self[:localpart => localpart, :domain => domain(fqdn)]
55
- end
58
+ def self.for_address(address)
59
+ localpart, fqdn = address.split('@')
60
+ self[:localpart => localpart, :domain => domain(fqdn)]
61
+ end
56
62
 
57
- def self.for_address!(address)
58
- localpart, fqdn = address.split('@')
59
- find_or_create(:localpart => localpart, :domain => domain(fqdn))
60
- end
63
+ def self.for_address!(address)
64
+ localpart, fqdn = address.split('@')
65
+ find_or_create(:localpart => localpart, :domain => domain(fqdn))
66
+ end
61
67
 
62
- end
68
+ end
63
69
 
64
- class Alias < Sequel::Model
65
- many_to_one :domain
66
- end
70
+ class Alias < Sequel::Model
71
+ many_to_one :domain
67
72
  end
73
+ end
68
74
  end
data/lib/smailr/setup.rb CHANGED
@@ -1,6 +1,10 @@
1
+ require 'commander'
2
+
1
3
  module Smailr
2
- module Setup
3
- def self.run
4
+ class Setup
5
+ include Commander::Methods
6
+
7
+ def run
4
8
 
5
9
  if Process.euid != 0
6
10
  say "ERROR: YOU ARE NOT RUNNING THIS SCRIPT WITH ROOT PRIVILEGES, EXITING."
@@ -0,0 +1,3 @@
1
+ module Smailr
2
+ VERSION = '0.8.1'
3
+ end
data/lib/smailr.rb CHANGED
@@ -1,47 +1,72 @@
1
1
  require 'rubygems'
2
- require 'yaml'
3
- require 'sqlite3'
4
- require 'sequel'
5
- require 'commander/import'
2
+ require 'smailr/version'
3
+
6
4
  require 'fileutils'
5
+ require 'logger'
6
+ require 'sequel'
7
+ require 'sqlite3'
8
+ require 'yaml'
9
+
10
+ # dkim
11
+ require 'date'
12
+ require 'openssl'
13
+
14
+ require 'smailr/alias'
15
+ require 'smailr/dkim'
16
+ require 'smailr/domain'
17
+ require 'smailr/mailbox'
7
18
 
8
19
  module Smailr
9
- VERSION = '0.6.2'
10
-
11
- autoload :Model, 'smailr/model'
12
- autoload :Domain, 'smailr/domain'
13
- autoload :Mailbox, 'smailr/mailbox'
14
- autoload :Alias, 'smailr/alias'
15
- autoload :Dkim, 'smailr/dkim'
16
- autoload :Setup, 'smailr/setup'
17
-
18
- class << self;
19
- attr_accessor :config
20
- attr_accessor :config_files
21
- attr_accessor :load_config
22
- attr_accessor :contrib_directory
23
- attr_accessor :migrations_directory
24
- end
25
20
 
26
- def self.load_config
27
- config = {}
28
- config_files.each do |f|
29
- if File.readable?(f)
30
- config.merge!(YAML.load_file(f))
31
- end
32
- end
33
- self.config = config
34
- end
21
+ # Exception Classes
22
+ class MissingDomain < StandardError ; end
35
23
 
36
- def self.db_connect
37
- Sequel.connect(self.config['database'])
24
+ class << self;
25
+ attr_accessor :config
26
+ attr_accessor :config_files
27
+ attr_accessor :load_config
28
+ attr_accessor :contrib_directory
29
+ attr_accessor :migrations_directory
30
+ end
31
+
32
+ def self.load_config
33
+ config = {}
34
+ config_files.each do |f|
35
+ if File.readable?(f)
36
+ config.merge!(YAML.load_file(f))
37
+ end
38
38
  end
39
+ self.config = config
40
+ end
41
+
42
+ def self.db_connect
43
+ Sequel.connect(self.config['database'])
44
+ end
39
45
 
46
+ def self.logger
47
+ unless @logger
48
+ @logger = Logger.new(STDOUT)
49
+ @logger.level = Logger::Severity::DEBUG
50
+ @logger.formatter = proc do |severity, datetime, progname, msg|
51
+ if severity == "ERROR"
52
+ "ERROR: #{msg}\n"
53
+ else
54
+ "#{msg}\n"
55
+ end
56
+ end
57
+ end
58
+ @logger
59
+ end
40
60
 
61
+ def self.logger=(logger)
62
+ @logger = logger
63
+ end
41
64
  end
42
65
 
43
- Smailr.contrib_directory ||= File.expand_path('../../contrib', __FILE__)
44
- Smailr.migrations_directory ||= File.expand_path('../../migrations', __FILE__)
45
- Smailr.config_files ||= [ File.expand_path('../../smailr.yml', __FILE__), '/etc/smailr.yml']
66
+ Smailr.contrib_directory ||= File.expand_path('../../contrib', __FILE__)
67
+ Smailr.migrations_directory ||= File.expand_path('../../migrations', __FILE__)
68
+ Smailr.config_files ||= [ File.expand_path('../../smailr.yml', __FILE__), '/etc/smailr.yml']
46
69
  Smailr.load_config
47
70
  Smailr::DB = Smailr::db_connect
71
+ require 'smailr/model'
72
+ Smailr::DB.sql_log_level = :debug
@@ -1,27 +1,27 @@
1
1
  Sequel.migration do
2
- up do
3
- puts <<-MESSAGE
2
+ up do
3
+ puts <<-MESSAGE
4
4
 
5
- WARNING ---------------------------------------------------------------------------------
5
+ WARNING ---------------------------------------------------------------------------------
6
6
 
7
- You need to adapt your mailserver configuration with this version of smailr, as
8
- passwords are now stored including the hash scheme.
7
+ You need to adapt your mailserver configuration with this version of smailr, as
8
+ passwords are now stored including the hash scheme.
9
9
 
10
- Select the hash from `mailboxes.password` and the scheme from `mailboxes.password_scheme`
10
+ Select the hash from `mailboxes.password` and the scheme from `mailboxes.password_scheme`
11
11
 
12
- --------------------------------------------------------------------------------- WARNING
12
+ --------------------------------------------------------------------------------- WARNING
13
13
 
14
- MESSAGE
14
+ MESSAGE
15
15
 
16
- add_column :mailboxes, :password_scheme, String
16
+ add_column :mailboxes, :password_scheme, String
17
17
 
18
- from(:mailboxes).update(password_scheme: "{SHA}")
18
+ from(:mailboxes).update(password_scheme: "{SHA}")
19
19
 
20
- alter_table(:mailboxes) do
21
- set_column_not_null(:password_scheme)
22
- end
20
+ alter_table(:mailboxes) do
21
+ set_column_not_null(:password_scheme)
23
22
  end
23
+ end
24
24
 
25
- # Downgrade is not supported, as we would drop necessary information do do effective hashing
26
- # in case this feature was already used to add various hashes to the database
25
+ # Downgrade is not supported, as we would drop necessary information do do effective hashing
26
+ # in case this feature was already used to add various hashes to the database
27
27
  end
@@ -0,0 +1,12 @@
1
+ Sequel.migration do
2
+ up do
3
+ # Remove this until we figured out whats wrong with error handling
4
+ # and we can do it for all fields
5
+ alter_table(:mailboxes) do
6
+ set_column_allow_null(:password_scheme)
7
+ end
8
+ end
9
+
10
+ # Downgrade is not supported, as we would drop necessary information do do effective hashing
11
+ # in case this feature was already used to add various hashes to the database
12
+ end
metadata CHANGED
@@ -1,17 +1,64 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smailr
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.2
4
+ version: 0.8.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stefan Schlesinger
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2016-01-19 00:00:00.000000000 Z
10
+ date: 2026-05-05 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: commander
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '4.3'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '4.3'
26
+ - !ruby/object:Gem::Dependency
27
+ name: sequel
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '5.0'
33
+ - - "<"
34
+ - !ruby/object:Gem::Version
35
+ version: '6.0'
36
+ type: :runtime
37
+ prerelease: false
38
+ version_requirements: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: '5.0'
43
+ - - "<"
44
+ - !ruby/object:Gem::Version
45
+ version: '6.0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: bcrypt
48
+ requirement: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - "~>"
51
+ - !ruby/object:Gem::Version
52
+ version: '3.1'
53
+ type: :runtime
54
+ prerelease: false
55
+ version_requirements: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: '3.1'
60
+ - !ruby/object:Gem::Dependency
61
+ name: net-smtp
15
62
  requirement: !ruby/object:Gem::Requirement
16
63
  requirements:
17
64
  - - ">="
@@ -25,13 +72,27 @@ dependencies:
25
72
  - !ruby/object:Gem::Version
26
73
  version: '0'
27
74
  - !ruby/object:Gem::Dependency
28
- name: sequel
75
+ name: minitest
29
76
  requirement: !ruby/object:Gem::Requirement
30
77
  requirements:
31
78
  - - ">="
32
79
  - !ruby/object:Gem::Version
33
80
  version: '0'
34
- type: :runtime
81
+ type: :development
82
+ prerelease: false
83
+ version_requirements: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ - !ruby/object:Gem::Dependency
89
+ name: minitest-mock
90
+ requirement: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ type: :development
35
96
  prerelease: false
36
97
  version_requirements: !ruby/object:Gem::Requirement
37
98
  requirements:
@@ -48,27 +109,29 @@ extensions: []
48
109
  extra_rdoc_files: []
49
110
  files:
50
111
  - README.md
51
- - bin/commander
52
- - bin/sequel
53
112
  - bin/smailr
54
113
  - contrib/dovecot-sql.conf
55
114
  - contrib/dovecot.conf
56
115
  - contrib/exim4.conf
57
116
  - lib/smailr.rb
58
117
  - lib/smailr/alias.rb
118
+ - lib/smailr/cli.rb
59
119
  - lib/smailr/dkim.rb
60
120
  - lib/smailr/domain.rb
61
121
  - lib/smailr/mailbox.rb
62
122
  - lib/smailr/model.rb
63
123
  - lib/smailr/setup.rb
124
+ - lib/smailr/version.rb
64
125
  - migrations/001_domains.rb
65
126
  - migrations/002_mailboxes.rb
66
127
  - migrations/003_aliases.rb
67
128
  - migrations/004_dkims.rb
68
129
  - migrations/005_mailboxes_add_digest_identifier_to_passwords.rb
130
+ - migrations/006_mailboxes_password_schema_allow_null.rb
69
131
  - smailr.yml
70
132
  homepage: http://github.com/sts/smailr
71
- licenses: []
133
+ licenses:
134
+ - Apache-2.0
72
135
  metadata: {}
73
136
  post_install_message: |2+
74
137
 
@@ -81,7 +144,7 @@ post_install_message: |2+
81
144
 
82
145
  * Install Dovecot with SQlite support
83
146
 
84
- * run export PATH="/var/lib/gems/1.8/bin:${PATH}"
147
+ * run ln -s `gem contents smailr|grep bin/smailr` /usr/local/sbin
85
148
 
86
149
  * run "smailr setup" to create exim, dovecot and smailr configuration (you
87
150
  can edit the configuration in an editor window before everyting is
@@ -107,10 +170,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
107
170
  requirements:
108
171
  - Exim
109
172
  - Dovecot
110
- - Debian
111
- rubyforge_project:
112
- rubygems_version: 2.4.6
113
- signing_key:
173
+ rubygems_version: 4.0.6
114
174
  specification_version: 4
115
175
  summary: Simple MAIL manageR - Virtual mail hosting management from the CLI
116
176
  test_files: []
177
+ ...
data/bin/commander DELETED
@@ -1,16 +0,0 @@
1
- #!/usr/bin/env ruby
2
- #
3
- # This file was generated by Bundler.
4
- #
5
- # The application 'commander' is installed as part of a gem, and
6
- # this file is here to facilitate running it.
7
- #
8
-
9
- require 'pathname'
10
- ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
- Pathname.new(__FILE__).realpath)
12
-
13
- require 'rubygems'
14
- require 'bundler/setup'
15
-
16
- load Gem.bin_path('commander', 'commander')
data/bin/sequel DELETED
@@ -1,16 +0,0 @@
1
- #!/usr/bin/env ruby
2
- #
3
- # This file was generated by Bundler.
4
- #
5
- # The application 'sequel' is installed as part of a gem, and
6
- # this file is here to facilitate running it.
7
- #
8
-
9
- require 'pathname'
10
- ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
- Pathname.new(__FILE__).realpath)
12
-
13
- require 'rubygems'
14
- require 'bundler/setup'
15
-
16
- load Gem.bin_path('sequel', 'sequel')