smailr 0.3.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.
@@ -0,0 +1,308 @@
1
+ ################################################################################
2
+ # Smailr Exim Configuration
3
+
4
+
5
+ # DATABASE QUERIES
6
+
7
+ ACL_DONT_SCAN_TWIZE_SECRET = Quai2tohr7TeeGh
8
+
9
+ SQLITE_DATABASE_FILE = /etc/exim4/smailr.sqlite
10
+
11
+ VIRTUAL_DOMAINS_SQL = SELECT DISTINCT fqdn FROM domains WHERE fqdn = '${quote_sqlite:$domain}'
12
+ VIRTUAL_DOMAINS = ${lookup sqlite{SQLITE_DATABASE_FILE VIRTUAL_DOMAINS_SQL}}
13
+
14
+ R_VIRTUAL_MAILBOX_CONDITION_SQL = \
15
+ SELECT '/srv/mail/users/' || domains.fqdn || '/' || mailboxes.localpart \
16
+ FROM mailboxes, domains \
17
+ WHERE mailboxes.localpart = '${quote_sqlite:$local_part}' \
18
+ AND domains.fqdn = '${quote_sqlite:$domain}' \
19
+ AND mailboxes.domain_id = domains.id
20
+
21
+ R_VIRTUAL_MAILBOX_CONDITION = ${lookup sqlite{SQLITE_DATABASE_FILE R_VIRTUAL_MAILBOX_CONDITION_SQL}}
22
+
23
+ domainlist local_domains = @ : VIRTUAL_DOMAINS
24
+
25
+ #############################################################################
26
+ # Main Settings
27
+
28
+ smtp_banner = $primary_hostname NO UCE/NO UBE ESMTP MTA
29
+
30
+ exim_user = Debian-exim
31
+ exim_group = Debian-exim
32
+ never_users = root
33
+
34
+ daemon_smtp_ports = 25 : 465 : 587
35
+
36
+ tls_certificate = /etc/exim4/exim.crt
37
+ tls_privatekey = /etc/exim4/exim.key
38
+ tls_advertise_hosts = *
39
+
40
+ split_spool_directory = true
41
+
42
+ smtp_return_error_details = true
43
+
44
+ log_selector = +subject \
45
+ +address_rewrite \
46
+ +connection_reject \
47
+ +delay_delivery \
48
+ +delivery_size \
49
+ +dnslist_defer \
50
+ +lost_incoming_connection \
51
+ +queue_run \
52
+ +received_recipients \
53
+ +sender_on_delivery \
54
+ +size_reject \
55
+ +smtp_confirmation \
56
+ +smtp_protocol_error \
57
+ +smtp_syntax_error \
58
+ +tls_cipher \
59
+ +tls_peerdn
60
+
61
+ # Maximum message size
62
+ message_size_limit = 20M
63
+
64
+ # Number of unknown SMTP commands we accept before dropping the connection
65
+ smtp_max_unknown_commands = 10
66
+
67
+ bounce_return_size_limit = 10K
68
+
69
+ # These protections need to take into account MailScanners need to do
70
+ # MIME explosion.
71
+ check_spool_inodes = 1000
72
+ check_spool_space = 100M
73
+
74
+ # do a reverse DNS lookup on every connection
75
+ host_lookup = *
76
+
77
+ # No RFC 1413 (ident)-lookups
78
+ rfc1413_hosts = !*
79
+
80
+ # Make ESMTP PIPELINING available in all cases
81
+ pipelining_advertise_hosts = *
82
+
83
+ # A bit of good cop / bad cop with helo
84
+ helo_allow_chars = "_"
85
+ helo_verify_hosts = !*
86
+ helo_try_verify_hosts = !*
87
+
88
+ # Reverse DNS information is useful
89
+ helo_lookup_domains = *
90
+
91
+ # Send a notification about forzen messages at these intervals
92
+ delay_warning = 1h:2h:8h:24h:48h:72h
93
+
94
+ # Don't send a notification for messages with Precedence:bulk|list|junk
95
+ delay_warning_condition = "${if match{$h_precedence:}{(?i)bulk|list|junk}{no}{yes}}"
96
+
97
+ # Accept 8-bit MIME in Helo and Body.
98
+ accept_8bitmime
99
+
100
+ # Allow to manually specify a envelope-from when submitting local mail
101
+ local_from_check = false
102
+ local_sender_retain = true
103
+ untrusted_set_sender = *
104
+
105
+ # Clamav socket
106
+ av_scanner = clamd:/var/run/clamav/clamd.ctl
107
+
108
+
109
+ #############################################################################
110
+ # ACL Configuration
111
+
112
+ # We use the following ACLs:
113
+ acl_smtp_connect = accept
114
+ acl_smtp_helo = accept
115
+ acl_smtp_starttls = accept
116
+ acl_smtp_mail = accept
117
+ acl_smtp_rcpt = acl_check_rcpt
118
+ acl_smtp_data = acl_check_data
119
+
120
+ # We dont allow VRFY/EXPN
121
+ acl_smtp_vrfy = deny
122
+ acl_smtp_expn = deny
123
+
124
+ begin acl
125
+
126
+ acl_check_rcpt:
127
+ # TODO: Put up propper spam detection
128
+
129
+ # Accept if source is local SMTP (not over TCP). We do this by testing
130
+ # for an empty sending host field.
131
+ accept hosts = :
132
+
133
+ # Accept everything from localhost
134
+ accept hosts = 127.0.0.1/8 : ::::1
135
+
136
+ # Deny if the local part contains @ or % or / or | or !. These are rarely
137
+ # found in genuine local parts, but are often tried by people looking to
138
+ # circumvent relaying restrictions.
139
+ deny local_parts = ^.*[@%!/|] : ^\\.
140
+
141
+ # Don't scan messages with our cryptographic header.
142
+ # This is needed for messages, which run the ACL twize, (eg. redirects)
143
+ # to save processing time.
144
+ warn message = X-SA-Do-Not-Run: Yes
145
+ condition = ${if eq{\
146
+ ${hmac{md5}{ACL_DONT_SCAN_TWIZE_SECRET}{$body_linecount}}}\
147
+ {$h_X-Scan-Signature:}\
148
+ {1}{0}}
149
+
150
+ # Accept authenticated messages.
151
+ accept authenticated = *
152
+
153
+ # Deny relaying on port 587 if not authenticated.
154
+ deny !authenticated = *
155
+ condition = ${if eq {$interface_port}{587} {yes}{no}}
156
+ message = Relaying denied. Proper authentication required on port 587.
157
+
158
+ # Teergrube any borken reverse DNS entries.
159
+ warn message = X-Broken-Reverse-DNS: no host name for IP address $sender_host_address
160
+ !verify = reverse_host_lookup
161
+ delay = TEERGRUBE
162
+
163
+ # Accept if the address is in a local domain, but only if the recipient can
164
+ # be verified. Otherwise deny. The "endpass" line is the border between
165
+ # passing on to the next ACL statement (if tests above it fail) or denying
166
+ # access (if tests below it fail).
167
+ accept domains = +local_domains
168
+ endpass
169
+ verify = recipient
170
+
171
+ # Accept if the address is in a domain for which we are relaying, but again,
172
+ # only if the recipient can be verified (this saves your secondary
173
+ # MXes from accepting mail that they then can't send to your primary
174
+ # MX)
175
+ accept domains = +relay_to_domains
176
+ endpass
177
+ message = unrouteable address
178
+ verify = recipient/callout=30s/callout_defer_ok
179
+
180
+ accept hosts = +relay_from_hosts
181
+
182
+ # Reaching the end of the ACL causes a "deny".
183
+ deny message = <$local_part@$domain>: Relaying denied. Proper authentication required.
184
+ delay = TEERGRUBE
185
+
186
+
187
+ acl_check_data:
188
+
189
+ # Accept if source is local SMTP (i.e. not over TCP/IP). We do this by
190
+ # testing for an empty sending host field.
191
+ accept hosts = :
192
+
193
+ # Run clamav against the message and reject if it contains malware. This
194
+ # acl condition will not deny if there is a problem with clamav.
195
+ deny message = This message contains malware ($malware_name)
196
+ malware = */defer_ok
197
+
198
+ accept
199
+
200
+
201
+
202
+ #############################################################################
203
+ # Router Configuration
204
+
205
+ begin routers
206
+
207
+ # Aliases for local mailboxes
208
+ virtual_alias:
209
+ debug_print = "R: virtual_alias for $local_part@$domain"
210
+ driver = redirect
211
+ domains = +local_domains
212
+ allow_fail
213
+ allow_defer
214
+ # Lookup the mailbox which we route to
215
+ data = ${lookup sqlite {SQLITE_DATABASE_FILE \
216
+ SELECT mailboxes.localpart || '@' || domains.fqdn \
217
+ FROM mailboxes, domains, aliases \
218
+ WHERE aliases.address = '${quote_sqlite:$local_part}@${quote_sqlite:$domain}' \
219
+ AND domains.fqdn = '${quote_sqlite:$domain}' \
220
+ AND mailboxes.domain_id = domains.id \
221
+ AND aliases.mailbox_id = mailboxes.id }{$value}fail}
222
+
223
+ virtual_mailbox:
224
+ debug_print = "R: virtual_mailbox for $local_part@$domain"
225
+ driver = accept
226
+ domains = +local_domains
227
+ transport = dovecot_virtual_delivery
228
+ condition = R_VIRTUAL_MAILBOX_CONDITION
229
+
230
+ # This router routes to remote hosts over SMTP using a DNS lookup with
231
+ # default options.
232
+ dnslookup:
233
+ debug_print = "R: dnslookup for $local_part@$domain"
234
+ driver = dnslookup
235
+ domains = ! +local_domains
236
+ ignore_target_hosts = 0.0.0.0 : 127.0.0.0/8
237
+ cannot_route_message = Unkown user $local_part in domain $domain
238
+ # Optimization since the dnslookup router is independent of the local part
239
+ same_domain_copy_routing = yes
240
+ transport = remote_smtp
241
+ no_more
242
+
243
+
244
+
245
+ #############################################################################
246
+ # Transport Configuration
247
+
248
+ begin transports
249
+
250
+ remote_smtp:
251
+ debug_print = "T: remote_smtp for $local_part@$domain"
252
+ driver = smtp
253
+ hosts_nopass_tls = *
254
+ hosts_avoid_tls = +hosts_avoid_tls
255
+ hosts_try_auth = +hosts_try_auth
256
+ headers_remove = "X-SA-Do-Not-Run:X-SA-Exim-Scanned:X-SA-Exim-Rcpt-From:X-SA-Exim-Rcpt-To:X-SA-Exim-Version"
257
+
258
+
259
+ dovecot_virtual_delivery:
260
+ debug_print = "T: dovecot_virtual_delivery for $local_part@$domain"
261
+ driver = pipe
262
+ command = /usr/lib/dovecot/deliver -d $local_part@$domain -f $sender_address -a $original_local_part@$original_domain
263
+ message_prefix =
264
+ message_suffix =
265
+ delivery_date_add
266
+ envelope_to_add
267
+ return_path_add
268
+ log_output
269
+ user = vmail
270
+ temp_errors = 64 : 69 : 70: 71 : 72 : 73 : 74 : 75 : 78
271
+ # Remove the X-SA_Exim-Rcpt-To header again, otherwise we would expose BCCs
272
+ headers_remove = "X-SA-Exim-Rcpt-To"
273
+
274
+
275
+ #############################################################################
276
+ # Rewrites/Retries/Authenticatos
277
+
278
+ begin rewrite
279
+
280
+
281
+ begin retry
282
+
283
+ # Retry.. every 10 mins for 2 hours
284
+ # Then.. every hour for 24 hours
285
+ # Finaly.. every 6 hours for 4 days
286
+
287
+ # Domain Error Retry.. Then.. Finaly..
288
+ * * F,2h,10m; F,24h,1h; F,4d,6h
289
+
290
+ begin authenticators
291
+
292
+ plain:
293
+ driver = plaintext
294
+ public_name = PLAIN
295
+ server_set_id = $2
296
+ server_debug_print = yes
297
+ server_prompts = :
298
+ server_condition = \
299
+ "${if and { \
300
+ {!eq{$2}{}} \
301
+ {!eq{$3}{}} \
302
+ {crypteq{$3}{\\{sha1\\}${lookup sqlite{SQLITE_DATABASE_FILE \
303
+ SELECT mailboxes.password \
304
+ FROM mailboxes, domains \
305
+ WHERE mailboxes.localpart = '${quote_sqlite:${local_part:$auth2}}' \
306
+ AND domains.fqdn = '${quote_sqlite:${domain:$auth2}}' \
307
+ AND domains.id = mailboxes.domain_id} \
308
+ {$value}fail}} }} {yes}{no}}"
@@ -0,0 +1,36 @@
1
+ module Smailr
2
+ module Alias
3
+ def self.add(source, destinations)
4
+ srclocalpart, srcdomain = source.split('@')
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
12
+
13
+ destinations.each do |dst|
14
+ dstlocalpart, dstdomain = dst.split('@')
15
+
16
+ Model::Alias.find_or_create(:domain => Model::Domain[:fqdn => srcdomain],
17
+ :localpart => srclocalpart,
18
+ :dstdomain => dstdomain,
19
+ :dstlocalpart => dstlocalpart)
20
+ end
21
+ end
22
+
23
+ def self.rm(source, destinations)
24
+ srclocalpart, srcdomain = source.split('@')
25
+
26
+ destinations.each do |dst|
27
+ dstlocalpart, dstdomain = dst.split('@')
28
+
29
+ Model::Alias.filter(:domain => Model::Domain[:fqdn => srcdomain],
30
+ :localpart => srclocalpart,
31
+ :dstdomain => dstdomain,
32
+ :dstlocalpart => dstlocalpart).delete
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,35 @@
1
+ require 'date'
2
+ require 'openssl'
3
+
4
+ module Smailr
5
+ module Dkim
6
+ def self.add(fqdn, options)
7
+ options.testing ||= true
8
+
9
+ if not Model::Domain[:fqdn => fqdn]
10
+ say_error "You trying to add a DKIM key for a non existing domain: #{fqdn}"
11
+ exit 1
12
+ end
13
+
14
+ private_key, public_key = generate_rsa_key
15
+ dkim = Model::Dkim.for_domain!(fqdn)
16
+ dkim.set(:private_key => private_key,
17
+ :public_key => public_key,
18
+ :testing => options.testing)
19
+ dkim.save
20
+ end
21
+
22
+ def self.rm(fqdn, options)
23
+ dkim = Model::Dkim.for_domain(fqdn)
24
+ dkim.destroy
25
+ end
26
+
27
+ private
28
+
29
+ def self.generate_rsa_key(length = 1024)
30
+ rsa_key = OpenSSL::PKey::RSA.new(length)
31
+ [ rsa_key.to_pem,
32
+ rsa_key.public_key.to_pem ]
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,18 @@
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) ")
11
+
12
+ domain = Model::Domain[:fqdn => fqdn]
13
+ domain.rm_related
14
+ domain.destroy
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,22 @@
1
+ module Smailr
2
+ module Mailbox
3
+ def self.add(address, password)
4
+ fqdn = address.split('@')[1]
5
+
6
+ if not Model::Domain[:fqdn => fqdn]
7
+ say_error "Trying to add a mailbox for a non existing domain: #{fqdn}"
8
+ exit 1
9
+ end
10
+
11
+ mbox = Model::Mailbox.for_address!(address)
12
+ mbox.password = password
13
+ mbox.save
14
+ end
15
+
16
+ def self.rm(address, options)
17
+ mbox = Model::Mailbox.for_address(address)
18
+ mbox.rm_related
19
+ mbox.destroy
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,68 @@
1
+ require 'digest/sha1'
2
+
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
9
+
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
19
+
20
+ def self.for_domain(fqdn)
21
+ self[:domain => Domain[:fqdn => fqdn]]
22
+ end
23
+
24
+ def self.for_domain!(fqdn)
25
+ find_or_create(:domain => Domain[:fqdn => fqdn])
26
+ end
27
+ one_to_many :aliases
28
+ end
29
+
30
+ class Mailbox < Sequel::Model
31
+ many_to_one :domain
32
+
33
+ def password=(clear)
34
+ self[:password] = Digest::SHA1.hexdigest(clear)
35
+ end
36
+
37
+ def rm_related
38
+ self.aliases.destroy
39
+ end
40
+
41
+ def aliases
42
+ Model::Alias.where(
43
+ :dstlocalpart => self.localpart,
44
+ :dstdomain => self.domain.fqdn
45
+ )
46
+ end
47
+
48
+ def self.domain(fqdn)
49
+ Domain[:fqdn => fqdn]
50
+ end
51
+
52
+ def self.for_address(address)
53
+ localpart, fqdn = address.split('@')
54
+ self[:localpart => localpart, :domain => domain(fqdn)]
55
+ end
56
+
57
+ def self.for_address!(address)
58
+ localpart, fqdn = address.split('@')
59
+ find_or_create(:localpart => localpart, :domain => domain(fqdn))
60
+ end
61
+
62
+ end
63
+
64
+ class Alias < Sequel::Model
65
+ many_to_one :domain
66
+ end
67
+ end
68
+ end
data/lib/smailr.rb ADDED
@@ -0,0 +1,22 @@
1
+ require 'rubygems'
2
+ require 'sqlite3'
3
+ require 'sequel'
4
+ require 'commander/import'
5
+
6
+ module Smailr
7
+ autoload :Model, 'smailr/model'
8
+ autoload :Domain, 'smailr/domain'
9
+ autoload :Mailbox, 'smailr/mailbox'
10
+ autoload :Alias, 'smailr/alias'
11
+ autoload :Dkim, 'smailr/dkim'
12
+
13
+ class << self;
14
+ attr_accessor :contrib_directory
15
+ attr_accessor :migrations_directory
16
+ end
17
+
18
+ VERSION = '0.3.0'
19
+ end
20
+
21
+ Smailr.contrib_directory ||= File.expand_path('../../contrib', __FILE__)
22
+ Smailr.migrations_directory ||= File.expand_path('../../migrations', __FILE__)
@@ -0,0 +1,8 @@
1
+ Sequel.migration do
2
+ change do
3
+ create_table :domains do
4
+ primary_key :id
5
+ String :fqdn, :unique => true
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,12 @@
1
+ Sequel.migration do
2
+ change do
3
+ create_table :mailboxes do
4
+ primary_key :id
5
+ foreign_key :domain_id
6
+ String :localpart, :required => true
7
+ String :password, :required => true
8
+
9
+ index [:domain_id, :localpart], :unique => true
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ Sequel.migration do
2
+ change do
3
+ create_table :aliases do
4
+ primary_key :id
5
+ foreign_key :domain_id
6
+ String :localpart
7
+ String :dstlocalpart
8
+ String :dstdomain
9
+ index [:domain_id, :localpart, :dstlocalpart, :dstdomain], :unique => true
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ Sequel.migration do
2
+ change do
3
+ create_table :dkims do
4
+ primary_key :id
5
+ foreign_key :domain_id
6
+ String :private_key, :required => true
7
+ String :public_key, :required => true
8
+ String :mode, :required => true
9
+ Boolean :testing, :required => true
10
+ end
11
+ end
12
+ end
metadata ADDED
@@ -0,0 +1,133 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: smailr
3
+ version: !ruby/object:Gem::Version
4
+ hash: 19
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 3
9
+ - 0
10
+ version: 0.3.0
11
+ platform: ruby
12
+ authors:
13
+ - Stefan Schlesinger
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-04-26 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: commander
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 3
29
+ segments:
30
+ - 0
31
+ version: "0"
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: sqlite3
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ hash: 3
43
+ segments:
44
+ - 0
45
+ version: "0"
46
+ type: :runtime
47
+ version_requirements: *id002
48
+ - !ruby/object:Gem::Dependency
49
+ name: sequel
50
+ prerelease: false
51
+ requirement: &id003 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ hash: 3
57
+ segments:
58
+ - 0
59
+ version: "0"
60
+ type: :runtime
61
+ version_requirements: *id003
62
+ description: |-
63
+ Smailr is a CLI tool which lets you manage your Exim/Dovecot setup
64
+ from the shell. It currently uses SQLite as a backend.
65
+ email: sts@ono.at
66
+ executables:
67
+ - smailr
68
+ extensions: []
69
+
70
+ extra_rdoc_files: []
71
+
72
+ files:
73
+ - bin/smailr
74
+ - lib/smailr/alias.rb
75
+ - lib/smailr/dkim.rb
76
+ - lib/smailr/domain.rb
77
+ - lib/smailr/mailbox.rb
78
+ - lib/smailr/model.rb
79
+ - lib/smailr.rb
80
+ - contrib/dovecot-sql.conf
81
+ - contrib/dovecot.conf
82
+ - contrib/exim4.conf
83
+ - migrations/001_domains.rb
84
+ - migrations/002_mailboxes.rb
85
+ - migrations/003_aliases.rb
86
+ - migrations/004_dkims.rb
87
+ homepage: http://github.com/sts/smailr
88
+ licenses: []
89
+
90
+ post_install_message: |+
91
+
92
+
93
+ SMAILR /////////////////////////////////////////////////////////////////
94
+
95
+ To finish the installation copy the example Exim and Dovecot
96
+ configuration files from the contrib directory and run
97
+ 'smailr migrate' to initialize the database.
98
+
99
+ //////////////////////////////////////////////////////////////// ///////
100
+
101
+ rdoc_options: []
102
+
103
+ require_paths:
104
+ - lib
105
+ required_ruby_version: !ruby/object:Gem::Requirement
106
+ none: false
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ hash: 3
111
+ segments:
112
+ - 0
113
+ version: "0"
114
+ required_rubygems_version: !ruby/object:Gem::Requirement
115
+ none: false
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ hash: 3
120
+ segments:
121
+ - 0
122
+ version: "0"
123
+ requirements:
124
+ - Exim
125
+ - Dovecot
126
+ - Debian
127
+ rubyforge_project:
128
+ rubygems_version: 1.8.21
129
+ signing_key:
130
+ specification_version: 3
131
+ summary: Simple MAIL manageR - Virtual mail hosting management from the CLI
132
+ test_files: []
133
+