smailr 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/smailr +165 -0
- data/contrib/dovecot-sql.conf +135 -0
- data/contrib/dovecot.conf +1281 -0
- data/contrib/exim4.conf +308 -0
- data/lib/smailr/alias.rb +36 -0
- data/lib/smailr/dkim.rb +35 -0
- data/lib/smailr/domain.rb +18 -0
- data/lib/smailr/mailbox.rb +22 -0
- data/lib/smailr/model.rb +68 -0
- data/lib/smailr.rb +22 -0
- data/migrations/001_domains.rb +8 -0
- data/migrations/002_mailboxes.rb +12 -0
- data/migrations/003_aliases.rb +12 -0
- data/migrations/004_dkims.rb +12 -0
- metadata +133 -0
data/bin/smailr
ADDED
@@ -0,0 +1,165 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$: << File.expand_path('../../lib', __FILE__)
|
3
|
+
|
4
|
+
require 'smailr'
|
5
|
+
|
6
|
+
DB = Sequel.connect("sqlite:///etc/exim4/smailr.sqlite")
|
7
|
+
|
8
|
+
#
|
9
|
+
# CLI Helpers
|
10
|
+
#
|
11
|
+
def determine_object(string)
|
12
|
+
return :domain if string =~ /^[^@][A-Z0-9.-]+\.[A-Z]{2,6}$/i
|
13
|
+
return :address if string =~ /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,6}$/i
|
14
|
+
end
|
15
|
+
|
16
|
+
def ask_password
|
17
|
+
min_password_length = 5
|
18
|
+
password = ask("Password: ") { |q| q.echo = "*" }
|
19
|
+
confirm = ask("Confirm: ") { |q| q.echo = "*" }
|
20
|
+
|
21
|
+
if password != confirm
|
22
|
+
say("Mismatch; try again.")
|
23
|
+
ask_password
|
24
|
+
end
|
25
|
+
|
26
|
+
if password.length < min_password_length
|
27
|
+
say("Too short; try again.")
|
28
|
+
ask_password
|
29
|
+
end
|
30
|
+
|
31
|
+
return password
|
32
|
+
end
|
33
|
+
|
34
|
+
program :version, Smailr::VERSION
|
35
|
+
program :description, 'Simple MAIL mangaR - Virtual mail hosting management from the CLI'
|
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 STRING', String, 'Specify the alias destination.'
|
48
|
+
c.option '--password STRING', String, 'The password for a new mailbox. If you omit this option, it prompts for one.'
|
49
|
+
c.option '--dkim', String, 'Add a DKIM Key for a 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
|
+
Smailr::Dkim.add(address, options)
|
58
|
+
else
|
59
|
+
Smailr::Domain.add(address)
|
60
|
+
end
|
61
|
+
|
62
|
+
when :address
|
63
|
+
if options.alias
|
64
|
+
source = args[0]
|
65
|
+
destinations = options.alias.split(',')
|
66
|
+
Smailr::Alias.add(source, destinations)
|
67
|
+
else
|
68
|
+
options.password ||= ask_password
|
69
|
+
Smailr::Mailbox.add(address, options.password)
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
command :ls do |c|
|
77
|
+
c.syntax = 'smailr ls [domain]'
|
78
|
+
c.summary = 'List domains or mailboxes of a specific domain.'
|
79
|
+
c.action do |args, options|
|
80
|
+
case args[0]
|
81
|
+
when /^[^@][A-Z0-9.-]+\.[A-Z]{2,6}$/i then
|
82
|
+
domain = Smailr::Model::Domain[:fqdn => args[0]]
|
83
|
+
domain.mailboxes.each do |mbox|
|
84
|
+
puts "m: #{mbox.localpart}@#{args[0]}"
|
85
|
+
end
|
86
|
+
domain.aliases.each do |aliass|
|
87
|
+
puts "a: #{aliass.localpart}@#{args[0]} > #{aliass.dstlocalpart}@#{aliass.dstdomain}"
|
88
|
+
end
|
89
|
+
when nil
|
90
|
+
domains = DB[:domains]
|
91
|
+
domains.all.each do |d|
|
92
|
+
domain = Smailr::Model::Domain[:fqdn => d[:fqdn]]
|
93
|
+
puts d[:fqdn]
|
94
|
+
end
|
95
|
+
else
|
96
|
+
error "You can either list a domains or a domains addresses."
|
97
|
+
exit 1
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
command :rm do |c|
|
103
|
+
c.syntax = 'smailr rm domain | mailbox [options]'
|
104
|
+
c.summary = 'Remove a domain, mailbox or alias known to the mail system.'
|
105
|
+
c.example 'Remove a domain', 'smailr rm example.com'
|
106
|
+
c.option '--force', 'Force the operation, do not ask for confirmation.'
|
107
|
+
c.option '--dkim', 'Remove a dkim key.'
|
108
|
+
c.option '--alias STRING', String, 'Specify the alias which you want to remove.'
|
109
|
+
c.action do |args, options|
|
110
|
+
address = args[0]
|
111
|
+
type = determine_object(address)
|
112
|
+
case type
|
113
|
+
when :domain
|
114
|
+
if options.dkim
|
115
|
+
Smailr::Dkim.rm(address, options)
|
116
|
+
else
|
117
|
+
Smailr::Domain.rm(address, options.force)
|
118
|
+
end
|
119
|
+
|
120
|
+
when :address
|
121
|
+
if options.alias
|
122
|
+
source = args[0]
|
123
|
+
destinations = options.alias.split(',')
|
124
|
+
|
125
|
+
Smailr::Alias.rm(source, destinations)
|
126
|
+
else
|
127
|
+
Smailr::Mailbox.rm(address, options)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
|
134
|
+
command :migrate do |c|
|
135
|
+
c.syntax = 'smailr migrate [options]'
|
136
|
+
c.summary = 'Create database and run migrations'
|
137
|
+
c.option '--to VERSION', String, 'Migrate the database to a specifict version.'
|
138
|
+
c.action do |args,options|
|
139
|
+
require 'sequel/extensions/migration'
|
140
|
+
raise "Database not configured" unless DB
|
141
|
+
|
142
|
+
if options.version.nil?
|
143
|
+
Sequel::Migrator.apply(DB, Smailr.migrations_directory)
|
144
|
+
else
|
145
|
+
Sequel::Migrator.apply(DB, Smailr.migrations_directory, :target => options.version.to_i)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
|
151
|
+
command :mutt do |c|
|
152
|
+
c.syntax = "smailr mutt address"
|
153
|
+
c.summary = "View the mailbox of the specified address in mutt."
|
154
|
+
c.description = "Open the mailbox of the specified address in mutt.\n\n " +
|
155
|
+
"Requires that mutt is installed and assumes the default mail\n " +
|
156
|
+
"storage directory structure: /srv/mail/users/<fqdn>/<localpart>"
|
157
|
+
c.example 'Open test@example.com', 'smailr mutt test@example.com'
|
158
|
+
c.action do |args,options|
|
159
|
+
localpart, fqdn = args[0].split('@')
|
160
|
+
`command -v mutt >/dev/null 2>&1 || { echo "Please install mutt first. Aborting." >&2; exit 1; }`
|
161
|
+
if not $?
|
162
|
+
exec "MAIL=/srv/mail/users/#{fqdn}/#{localpart} MAILDIR=$MAIL mutt -mMaildir]"
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
# This file is opened as root, so it should be owned by root and mode 0600.
|
2
|
+
#
|
3
|
+
# http://wiki.dovecot.org/AuthDatabase/SQL
|
4
|
+
#
|
5
|
+
# For the sql passdb module, you'll need a database with a table that
|
6
|
+
# contains fields for at least the username and password. If you want to
|
7
|
+
# use the user@domain syntax, you might want to have a separate domain
|
8
|
+
# field as well.
|
9
|
+
#
|
10
|
+
# If your users all have the same uig/gid, and have predictable home
|
11
|
+
# directories, you can use the static userdb module to generate the home
|
12
|
+
# dir based on the username and domain. In this case, you won't need fields
|
13
|
+
# for home, uid, or gid in the database.
|
14
|
+
#
|
15
|
+
# If you prefer to use the sql userdb module, you'll want to add fields
|
16
|
+
# for home, uid, and gid. Here is an example table:
|
17
|
+
#
|
18
|
+
# CREATE TABLE users (
|
19
|
+
# username VARCHAR(128) NOT NULL,
|
20
|
+
# domain VARCHAR(128) NOT NULL,
|
21
|
+
# password VARCHAR(64) NOT NULL,
|
22
|
+
# home VARCHAR(255) NOT NULL,
|
23
|
+
# uid INTEGER NOT NULL,
|
24
|
+
# gid INTEGER NOT NULL,
|
25
|
+
# active CHAR(1) DEFAULT 'Y' NOT NULL
|
26
|
+
# );
|
27
|
+
|
28
|
+
# Database driver: mysql, pgsql, sqlite
|
29
|
+
driver = sqlite
|
30
|
+
|
31
|
+
# Database connection string. This is driver-specific setting.
|
32
|
+
#
|
33
|
+
# pgsql:
|
34
|
+
# For available options, see the PostgreSQL documention for the
|
35
|
+
# PQconnectdb function of libpq.
|
36
|
+
#
|
37
|
+
# mysql:
|
38
|
+
# Basic options emulate PostgreSQL option names:
|
39
|
+
# host, port, user, password, dbname
|
40
|
+
#
|
41
|
+
# But also adds some new settings:
|
42
|
+
# client_flags - See MySQL manual
|
43
|
+
# ssl_ca, ssl_ca_path - Set either one or both to enable SSL
|
44
|
+
# ssl_cert, ssl_key - For sending client-side certificates to server
|
45
|
+
# ssl_cipher - Set minimum allowed cipher security (default: HIGH)
|
46
|
+
# option_file - Read options from the given file instead of
|
47
|
+
# the default my.cnf location
|
48
|
+
# option_group - Read options from the given group (default: client)
|
49
|
+
#
|
50
|
+
# You can connect to UNIX sockets by using host: host=/var/run/mysqld/mysqld.sock
|
51
|
+
# Note that currently you can't use spaces in parameters.
|
52
|
+
#
|
53
|
+
# MySQL supports multiple host parameters for load balancing / HA.
|
54
|
+
#
|
55
|
+
# sqlite:
|
56
|
+
# The path to the database file.
|
57
|
+
#
|
58
|
+
# Examples:
|
59
|
+
# connect = host=192.168.1.1 dbname=users
|
60
|
+
# connect = host=sql.example.com dbname=virtual user=virtual password=blarg
|
61
|
+
# connect = /etc/dovecot/authdb.sqlite
|
62
|
+
#
|
63
|
+
connect = /etc/exim4/smailr.sqlite
|
64
|
+
|
65
|
+
# Default password scheme.
|
66
|
+
#
|
67
|
+
# List of supported schemes is in
|
68
|
+
# http://wiki.dovecot.org/Authentication/PasswordSchemes
|
69
|
+
#
|
70
|
+
default_pass_scheme = MD5
|
71
|
+
|
72
|
+
# passdb query to retrieve the password. It can return fields:
|
73
|
+
# password - The user's password. This field must be returned.
|
74
|
+
# user - user@domain from the database. Needed with case-insensitive lookups.
|
75
|
+
# username and domain - An alternative way to represent the "user" field.
|
76
|
+
#
|
77
|
+
# The "user" field is often necessary with case-insensitive lookups to avoid
|
78
|
+
# e.g. "name" and "nAme" logins creating two different mail directories. If
|
79
|
+
# your user and domain names are in separate fields, you can return "username"
|
80
|
+
# and "domain" fields instead of "user".
|
81
|
+
#
|
82
|
+
# The query can also return other fields which have a special meaning, see
|
83
|
+
# http://wiki.dovecot.org/PasswordDatabase/ExtraFields
|
84
|
+
#
|
85
|
+
# Commonly used available substitutions (see http://wiki.dovecot.org/Variables
|
86
|
+
# for full list):
|
87
|
+
# %u = entire user@domain
|
88
|
+
# %n = user part of user@domain
|
89
|
+
# %d = domain part of user@domain
|
90
|
+
#
|
91
|
+
# Note that these can be used only as input to SQL query. If the query outputs
|
92
|
+
# any of these substitutions, they're not touched. Otherwise it would be
|
93
|
+
# difficult to have eg. usernames containing '%' characters.
|
94
|
+
#
|
95
|
+
# Example:
|
96
|
+
# password_query = SELECT userid AS user, pw AS password \
|
97
|
+
# FROM users WHERE userid = '%u' AND active = 'Y'
|
98
|
+
#
|
99
|
+
#password_query = \
|
100
|
+
# SELECT username, domain, password \
|
101
|
+
# FROM users WHERE username = '%n' AND domain = '%d'
|
102
|
+
|
103
|
+
# userdb query to retrieve the user information. It can return fields:
|
104
|
+
# uid - System UID (overrides mail_uid setting)
|
105
|
+
# gid - System GID (overrides mail_gid setting)
|
106
|
+
# home - Home directory
|
107
|
+
# mail - Mail location (overrides mail_location setting)
|
108
|
+
#
|
109
|
+
# None of these are strictly required. If you use a single UID and GID, and
|
110
|
+
# home or mail directory fits to a template string, you could use userdb static
|
111
|
+
# instead. For a list of all fields that can be returned, see
|
112
|
+
# http://wiki.dovecot.org/UserDatabase/ExtraFields
|
113
|
+
#
|
114
|
+
# Examples:
|
115
|
+
# user_query = SELECT home, uid, gid FROM users WHERE userid = '%u'
|
116
|
+
# user_query = SELECT dir AS home, user AS uid, group AS gid FROM users where userid = '%u'
|
117
|
+
# user_query = SELECT home, 501 AS uid, 501 AS gid FROM users WHERE userid = '%u'
|
118
|
+
#
|
119
|
+
#user_query = \
|
120
|
+
# SELECT home, uid, gid \
|
121
|
+
# FROM users WHERE username = '%n' AND domain = '%d'
|
122
|
+
|
123
|
+
# If you wish to avoid two SQL lookups (passdb + userdb), you can use
|
124
|
+
# userdb prefetch instead of userdb sql in dovecot.conf. In that case you'll
|
125
|
+
# also have to return userdb fields in password_query prefixed with "userdb_"
|
126
|
+
# string. For example:
|
127
|
+
|
128
|
+
password_query = \
|
129
|
+
SELECT mailboxes.password AS password, \
|
130
|
+
mailboxes.localpart AS username, \
|
131
|
+
domains.fqdn AS domain \
|
132
|
+
FROM mailboxes, domains \
|
133
|
+
WHERE mailboxes.localpart = '%n' \
|
134
|
+
AND domains.fqdn = '%d' \
|
135
|
+
AND domains.id = mailboxes.domain_id
|