postfix_admin 0.2.0 → 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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +54 -0
- data/.gitignore +2 -0
- data/.rubocop.yml +20 -0
- data/CHANGELOG.md +19 -8
- data/Gemfile +1 -1
- data/LICENSE +1 -1
- data/README.md +26 -20
- data/Rakefile +19 -1
- data/bin/console +6 -2
- data/docker-admin/Dockerfile +6 -0
- data/docker-admin/config.local.php +24 -0
- data/docker-app/Dockerfile +21 -0
- data/docker-app/my.cnf +5 -0
- data/docker-compose.yml +37 -11
- data/docker-db/postfix.v1841.sql +383 -0
- data/docker-db/postfix.v352.sql +223 -0
- data/docker-db/postfix.v740.sql +269 -0
- data/lib/postfix_admin/admin.rb +17 -2
- data/lib/postfix_admin/alias.rb +0 -19
- data/lib/postfix_admin/base.rb +120 -94
- data/lib/postfix_admin/cli.rb +184 -127
- data/lib/postfix_admin/concerns/dovecot_cram_md5_password.rb +0 -1
- data/lib/postfix_admin/domain.rb +35 -44
- data/lib/postfix_admin/doveadm.rb +11 -10
- data/lib/postfix_admin/mailbox.rb +14 -6
- data/lib/postfix_admin/runner.rb +35 -22
- data/lib/postfix_admin/version.rb +1 -1
- data/postfix_admin.gemspec +13 -9
- metadata +55 -21
- data/Dockerfile +0 -24
- data/docker-entrypoint.sh +0 -5
data/lib/postfix_admin/cli.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
require 'yaml'
|
2
2
|
require 'postfix_admin'
|
3
3
|
require 'postfix_admin/doveadm'
|
4
|
+
require 'terminal-table'
|
4
5
|
|
5
6
|
module PostfixAdmin
|
6
7
|
class CLI
|
7
|
-
|
8
|
+
DEFAULT_CONFIG_PATH = '~/.postfix_admin.conf'
|
9
|
+
@config_file = DEFAULT_CONFIG_PATH
|
8
10
|
MIN_NUM_PASSWORD_CHARACTER = 5
|
9
11
|
|
10
12
|
def initialize
|
@@ -28,55 +30,49 @@ module PostfixAdmin
|
|
28
30
|
name = name.downcase if name
|
29
31
|
|
30
32
|
if name =~ /@/
|
33
|
+
# address like argument
|
31
34
|
if Admin.exists?(name)
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
35
|
+
# admin
|
36
|
+
show_admin_details(name, display_password: true)
|
37
|
+
puts
|
38
|
+
show_admin_domain(name)
|
39
|
+
elsif Mailbox.exists?(name)
|
40
|
+
# mailbox
|
41
|
+
show_account_details(name, display_password: true)
|
37
42
|
elsif Alias.exists?(name)
|
43
|
+
# alias
|
38
44
|
show_alias_details(name)
|
45
|
+
else
|
46
|
+
raise Error, "Could not find admin/mailbox/alias #{name}"
|
39
47
|
end
|
40
48
|
|
41
49
|
return
|
42
50
|
end
|
43
51
|
|
44
52
|
show_summary(name)
|
53
|
+
puts
|
45
54
|
|
46
55
|
if name
|
47
|
-
|
48
|
-
|
49
|
-
show_alias(name)
|
56
|
+
# domain name
|
57
|
+
show_domain_details(name)
|
50
58
|
else
|
59
|
+
# no argument: show all domains and admins
|
51
60
|
show_domain
|
61
|
+
puts
|
52
62
|
show_admin
|
53
63
|
end
|
54
64
|
end
|
55
65
|
|
56
66
|
def show_summary(domain_name = nil)
|
57
|
-
title = "Summary"
|
58
67
|
if domain_name
|
59
|
-
domain_name
|
60
|
-
|
61
|
-
|
62
|
-
end
|
63
|
-
|
64
|
-
report(title) do
|
65
|
-
if domain_name
|
66
|
-
domain = Domain.find(domain_name)
|
67
|
-
puts "Mailboxes : %4d / %4s" % [domain.rel_mailboxes.count, max_str(domain.mailboxes)]
|
68
|
-
puts "Aliases : %4d / %4s" % [domain.pure_aliases.count, max_str(domain.aliases)]
|
69
|
-
puts "Max Quota : %4d MB" % domain.maxquota
|
70
|
-
puts "Active : %3s" % domain.active_str
|
71
|
-
else
|
72
|
-
puts "Domains : %4d" % Domain.without_all.count
|
73
|
-
puts "Admins : %4d" % Admin.count
|
74
|
-
puts "Mailboxes : %4d" % Mailbox.count
|
75
|
-
puts "Aliases : %4d" % Alias.pure.count
|
76
|
-
end
|
68
|
+
show_domain_summary(domain_name)
|
69
|
+
else
|
70
|
+
show_general_summary
|
77
71
|
end
|
78
72
|
end
|
79
73
|
|
74
|
+
# Set up a domain
|
75
|
+
# Add a domain, add an admin, and grant the admin access to the domain
|
80
76
|
def setup_domain(domain_name, password)
|
81
77
|
admin = "admin@#{domain_name}"
|
82
78
|
add_domain(domain_name)
|
@@ -84,62 +80,75 @@ module PostfixAdmin
|
|
84
80
|
add_admin_domain(admin, domain_name)
|
85
81
|
end
|
86
82
|
|
87
|
-
def show_account_details(user_name)
|
83
|
+
def show_account_details(user_name, display_password: false)
|
88
84
|
account_check(user_name)
|
89
85
|
mailbox = Mailbox.find(user_name)
|
90
86
|
mail_alias = Alias.find(user_name)
|
91
87
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
88
|
+
rows = []
|
89
|
+
puts_title("Mailbox")
|
90
|
+
rows << ["Address", mailbox.username]
|
91
|
+
rows << ["Name", mailbox.name]
|
92
|
+
rows << ["Password", mailbox.password] if display_password
|
93
|
+
rows << ["Quota (MB)", mailbox.quota_mb_str]
|
94
|
+
rows << ["Go to", mail_alias.goto]
|
95
|
+
rows << ["Active", mailbox.active_str]
|
96
|
+
|
97
|
+
puts_table(rows: rows)
|
100
98
|
end
|
101
99
|
|
102
|
-
def show_admin_details(name)
|
100
|
+
def show_admin_details(name, display_password: false)
|
103
101
|
admin_check(name)
|
104
102
|
admin = Admin.find(name)
|
105
103
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
104
|
+
rows = []
|
105
|
+
puts_title("Admin")
|
106
|
+
rows << ["Name", admin.username]
|
107
|
+
rows << ["Password", admin.password] if display_password
|
108
|
+
rows << ["Domains", admin.super_admin? ? "ALL" : admin.rel_domains.count.to_s]
|
109
|
+
rows << ["Role", admin.super_admin? ? "Super Admin" : "Standard Admin"]
|
110
|
+
rows << ["Active", admin.active_str]
|
111
|
+
|
112
|
+
puts_table(rows: rows)
|
113
113
|
end
|
114
114
|
|
115
115
|
def show_alias_details(name)
|
116
116
|
alias_check(name)
|
117
117
|
mail_alias = Alias.find(name)
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
118
|
+
|
119
|
+
rows = []
|
120
|
+
puts_title("Alias")
|
121
|
+
rows << ["Address", mail_alias.address]
|
122
|
+
rows << ["Go to", mail_alias.goto]
|
123
|
+
rows << ["Active", mail_alias.active_str]
|
124
|
+
|
125
|
+
puts_table(rows: rows)
|
123
126
|
end
|
124
127
|
|
125
128
|
def show_domain
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
puts " No domains"
|
130
|
-
next
|
131
|
-
end
|
129
|
+
rows = []
|
130
|
+
headings = ["No.", "Domain", "Aliases", "Mailboxes","Max Quota (MB)",
|
131
|
+
"Active", "Description"]
|
132
132
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
133
|
+
puts_title("Domains")
|
134
|
+
if Domain.without_all.empty?
|
135
|
+
puts "No domains"
|
136
|
+
return
|
137
|
+
end
|
138
|
+
|
139
|
+
Domain.without_all.each_with_index do |d, i|
|
140
|
+
no = i + 1
|
141
|
+
aliases_str = "%4d / %4s" % [d.pure_aliases.count, d.aliases_str]
|
142
|
+
mailboxes_str = "%4d / %4s" % [d.rel_mailboxes.count, d.mailboxes_str]
|
143
|
+
rows << [no.to_s, d.domain, aliases_str, mailboxes_str,
|
144
|
+
d.maxquota_str, d.active_str, d.description]
|
138
145
|
end
|
146
|
+
|
147
|
+
puts_table(headings: headings, rows: rows)
|
139
148
|
end
|
140
149
|
|
141
|
-
def add_domain(domain_name)
|
142
|
-
@base.add_domain(domain_name)
|
150
|
+
def add_domain(domain_name, description: nil)
|
151
|
+
@base.add_domain(domain_name, description: description)
|
143
152
|
puts_registered(domain_name, "a domain")
|
144
153
|
end
|
145
154
|
|
@@ -173,6 +182,7 @@ module PostfixAdmin
|
|
173
182
|
domain.mailboxes = options[:mailboxes] if options[:mailboxes]
|
174
183
|
domain.maxquota = options[:maxquota] if options[:maxquota]
|
175
184
|
domain.active = options[:active] unless options[:active].nil?
|
185
|
+
domain.description = options[:description] if options[:description]
|
176
186
|
domain.save!
|
177
187
|
|
178
188
|
puts "Successfully updated #{domain_name}"
|
@@ -186,36 +196,44 @@ module PostfixAdmin
|
|
186
196
|
|
187
197
|
def show_admin(domain_name = nil)
|
188
198
|
admins = domain_name ? Admin.select { |a| a.rel_domains.exists?(domain_name) } : Admin.all
|
189
|
-
|
190
|
-
report("Admins", index) do
|
191
|
-
if admins.empty?
|
192
|
-
puts " No admins"
|
193
|
-
next
|
194
|
-
end
|
199
|
+
headings = %w[No. Admin Domains Active]
|
195
200
|
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
201
|
+
puts_title("Admins")
|
202
|
+
if admins.empty?
|
203
|
+
puts "No admins"
|
204
|
+
return
|
200
205
|
end
|
206
|
+
|
207
|
+
rows = []
|
208
|
+
admins.each_with_index do |a, i|
|
209
|
+
no = i + 1
|
210
|
+
domains = a.super_admin? ? 'Super Admin' : a.rel_domains.count
|
211
|
+
rows << [no.to_s, a.username, domains.to_s, a.active_str]
|
212
|
+
end
|
213
|
+
|
214
|
+
puts_table(headings: headings, rows: rows)
|
201
215
|
end
|
202
216
|
|
203
217
|
def show_address(domain_name)
|
204
218
|
domain_check(domain_name)
|
205
219
|
|
220
|
+
rows = []
|
206
221
|
mailboxes = Domain.find(domain_name).rel_mailboxes
|
207
|
-
|
208
|
-
report("Addresses", index) do
|
209
|
-
if mailboxes.empty?
|
210
|
-
puts " No addresses"
|
211
|
-
next
|
212
|
-
end
|
222
|
+
headings = ["No.", "Email", "Name", "Quota (MB)", "Active", "Maildir"]
|
213
223
|
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
224
|
+
puts_title("Addresses")
|
225
|
+
if mailboxes.empty?
|
226
|
+
puts "No addresses"
|
227
|
+
return
|
228
|
+
end
|
229
|
+
|
230
|
+
mailboxes.each_with_index do |m, i|
|
231
|
+
no = i + 1
|
232
|
+
rows << [no.to_s, m.username, m.name, m.quota_mb_str,
|
233
|
+
m.active_str, m.maildir]
|
218
234
|
end
|
235
|
+
|
236
|
+
puts_table(headings: headings, rows: rows)
|
219
237
|
end
|
220
238
|
|
221
239
|
def show_alias(domain_name)
|
@@ -228,21 +246,24 @@ module PostfixAdmin
|
|
228
246
|
end
|
229
247
|
|
230
248
|
show_alias_base("Forwards", forwards)
|
249
|
+
puts
|
231
250
|
show_alias_base("Aliases", aliases)
|
232
251
|
end
|
233
252
|
|
234
253
|
def show_admin_domain(user_name)
|
235
254
|
admin = Admin.find(user_name)
|
255
|
+
puts_title("Admin Domains (#{user_name})")
|
236
256
|
if admin.rel_domains.empty?
|
237
|
-
puts "\nNo
|
257
|
+
puts "\nNo domains for #{user_name}"
|
238
258
|
return
|
239
259
|
end
|
240
260
|
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
261
|
+
rows = []
|
262
|
+
admin.rel_domains.each_with_index do |d, i|
|
263
|
+
no = i + 1
|
264
|
+
rows << [no.to_s, d.domain]
|
245
265
|
end
|
266
|
+
puts_table(rows: rows, headings: %w[No. Domain])
|
246
267
|
end
|
247
268
|
|
248
269
|
def add_admin(user_name, password, super_admin = false, scheme = nil)
|
@@ -270,9 +291,8 @@ module PostfixAdmin
|
|
270
291
|
def add_account(address, password, scheme = nil, name = nil)
|
271
292
|
validate_password(password)
|
272
293
|
|
273
|
-
@base.add_account(address, hashed_password(password, scheme), name)
|
294
|
+
@base.add_account(address, hashed_password(password, scheme), name: name)
|
274
295
|
puts_registered(address, "an account")
|
275
|
-
show_account_details(address)
|
276
296
|
end
|
277
297
|
|
278
298
|
def add_alias(address, goto)
|
@@ -324,11 +344,25 @@ module PostfixAdmin
|
|
324
344
|
puts_deleted(address)
|
325
345
|
end
|
326
346
|
|
327
|
-
def log
|
328
|
-
|
329
|
-
|
330
|
-
|
347
|
+
def log(domain: nil, last: nil)
|
348
|
+
headings = %w[Timestamp Admin Domain Action Data]
|
349
|
+
rows = []
|
350
|
+
|
351
|
+
logs = if domain
|
352
|
+
Log.where(domain: domain)
|
353
|
+
else
|
354
|
+
Log.all
|
355
|
+
end
|
356
|
+
|
357
|
+
logs = logs.last(last) if last
|
358
|
+
|
359
|
+
logs.each do |l|
|
360
|
+
# TODO: Consider if zone should be included ('%Z').
|
361
|
+
time = l.timestamp.strftime("%Y-%m-%d %X")
|
362
|
+
rows << [time, l.username, l.domain, l.action, l.data]
|
331
363
|
end
|
364
|
+
|
365
|
+
puts_table(headings: headings, rows: rows)
|
332
366
|
end
|
333
367
|
|
334
368
|
def dump
|
@@ -365,17 +399,58 @@ module PostfixAdmin
|
|
365
399
|
|
366
400
|
private
|
367
401
|
|
402
|
+
def show_general_summary
|
403
|
+
rows = []
|
404
|
+
title = "Summary"
|
405
|
+
rows << ["Domains", Domain.without_all.count]
|
406
|
+
rows << ["Admins", Admin.count]
|
407
|
+
rows << ["Mailboxes", Mailbox.count]
|
408
|
+
rows << ["Aliases", Alias.pure.count]
|
409
|
+
|
410
|
+
puts_title(title)
|
411
|
+
puts_table(rows: rows)
|
412
|
+
end
|
413
|
+
|
414
|
+
def show_domain_summary(domain_name)
|
415
|
+
domain_name = domain_name.downcase
|
416
|
+
domain_check(domain_name)
|
417
|
+
|
418
|
+
rows = []
|
419
|
+
domain = Domain.find(domain_name)
|
420
|
+
rows << ["Mailboxes", "%4d / %4s" % [domain.rel_mailboxes.count, domain.mailboxes_str]]
|
421
|
+
rows << ["Aliases", "%4d / %4s" % [domain.pure_aliases.count, domain.aliases_str]]
|
422
|
+
rows << ["Max Quota (MB)", domain.maxquota_str]
|
423
|
+
rows << ["Active", domain.active_str]
|
424
|
+
rows << ["Description", domain.description]
|
425
|
+
|
426
|
+
puts_title(domain_name)
|
427
|
+
puts_table(rows: rows)
|
428
|
+
end
|
429
|
+
|
430
|
+
def show_domain_details(domain_name)
|
431
|
+
show_admin(domain_name)
|
432
|
+
puts
|
433
|
+
show_address(domain_name)
|
434
|
+
puts
|
435
|
+
show_alias(domain_name)
|
436
|
+
end
|
437
|
+
|
368
438
|
def show_alias_base(title, addresses)
|
369
|
-
|
370
|
-
|
371
|
-
puts " No #{title.downcase}"
|
372
|
-
next
|
373
|
-
end
|
439
|
+
rows = []
|
440
|
+
puts_title(title)
|
374
441
|
|
375
|
-
|
376
|
-
|
377
|
-
|
442
|
+
if addresses.empty?
|
443
|
+
puts "No #{title.downcase}"
|
444
|
+
return
|
378
445
|
end
|
446
|
+
|
447
|
+
headings = ["No.", "Address", "Active", "Go to"]
|
448
|
+
addresses.each_with_index do |a, i|
|
449
|
+
no = i + 1
|
450
|
+
rows << [no.to_s, a.address, a.active_str, a.goto]
|
451
|
+
end
|
452
|
+
|
453
|
+
puts_table(headings: headings, rows: rows)
|
379
454
|
end
|
380
455
|
|
381
456
|
def puts_registered(name, as_str)
|
@@ -387,7 +462,7 @@ module PostfixAdmin
|
|
387
462
|
end
|
388
463
|
|
389
464
|
def config_file
|
390
|
-
|
465
|
+
File.expand_path(CLI.config_file)
|
391
466
|
end
|
392
467
|
|
393
468
|
def load_config
|
@@ -408,17 +483,12 @@ module PostfixAdmin
|
|
408
483
|
File.chmod(0600, file)
|
409
484
|
end
|
410
485
|
|
411
|
-
def
|
412
|
-
puts
|
486
|
+
def puts_table(args)
|
487
|
+
puts Terminal::Table.new(args)
|
413
488
|
end
|
414
489
|
|
415
|
-
def
|
416
|
-
puts "
|
417
|
-
print_line if index
|
418
|
-
puts index if index
|
419
|
-
print_line
|
420
|
-
yield
|
421
|
-
print_line
|
490
|
+
def puts_title(title)
|
491
|
+
puts "| #{title} |"
|
422
492
|
end
|
423
493
|
|
424
494
|
def account_check(user_name)
|
@@ -468,23 +538,10 @@ module PostfixAdmin
|
|
468
538
|
end
|
469
539
|
end
|
470
540
|
|
471
|
-
def max_str(value)
|
472
|
-
case value
|
473
|
-
when 0
|
474
|
-
'--'
|
475
|
-
when -1
|
476
|
-
'0'
|
477
|
-
else
|
478
|
-
value.to_s
|
479
|
-
end
|
480
|
-
end
|
481
|
-
|
482
|
-
private
|
483
|
-
|
484
541
|
def hashed_password(password, in_scheme = nil)
|
542
|
+
prefix = @base.config[:passwordhash_prefix]
|
485
543
|
scheme = in_scheme || @base.config[:scheme]
|
486
|
-
|
487
|
-
PostfixAdmin::Doveadm.password(password, scheme)
|
544
|
+
PostfixAdmin::Doveadm.password(password, scheme, prefix)
|
488
545
|
end
|
489
546
|
end
|
490
547
|
end
|
data/lib/postfix_admin/domain.rb
CHANGED
@@ -8,91 +8,82 @@ module PostfixAdmin
|
|
8
8
|
message: "must be a valid domain name" }
|
9
9
|
validates :transport, presence: true
|
10
10
|
|
11
|
+
# max aliases (Disabled: -1, Unlimited: 0)
|
11
12
|
validates :aliases, presence: true,
|
12
13
|
numericality: { only_integer: true,
|
13
|
-
greater_than_or_equal_to:
|
14
|
+
greater_than_or_equal_to: -1 }
|
15
|
+
# max mailboxes (Disabled: -1, Unlimited: 0)
|
14
16
|
validates :mailboxes, presence: true,
|
15
17
|
numericality: { only_integer: true,
|
16
|
-
greater_than_or_equal_to:
|
18
|
+
greater_than_or_equal_to: -1 }
|
19
|
+
|
20
|
+
# max quota (MB) for each mailbox (Unlimited: 0)
|
21
|
+
# It's not sure what 'disabled' means for max quota.
|
22
|
+
# So it's better not to allow users to set `maxquota` to -1.
|
17
23
|
validates :maxquota, presence: true,
|
18
24
|
numericality: { only_integer: true,
|
19
25
|
greater_than_or_equal_to: 0 }
|
20
26
|
|
27
|
+
# mailboxes that belong to this domain
|
21
28
|
has_many :rel_mailboxes, class_name: "Mailbox", foreign_key: :domain,
|
22
29
|
dependent: :destroy
|
30
|
+
# aliases that belong to this domain
|
23
31
|
has_many :rel_aliases, class_name: "Alias", foreign_key: :domain,
|
24
32
|
dependent: :destroy
|
25
33
|
|
34
|
+
# It causes errors to set `dependent: :destroy` as other columns
|
35
|
+
# because the domain_admins table doesn't have a single primary key.
|
36
|
+
#
|
37
|
+
# PostfixAdmin::DomainAdmin Load (0.5ms) SELECT `domain_admins`.* FROM `domain_admins` WHERE `domain_admins`.`domain` = 'example.com'
|
38
|
+
# PostfixAdmin::DomainAdmin Destroy (1.1ms) DELETE FROM `domain_admins` WHERE `domain_admins`.`` IS NULL
|
39
|
+
#
|
40
|
+
# ActiveRecord::StatementInvalid: Mysql2::Error: Unknown column 'domain_admins.' in 'where clause'
|
41
|
+
# from /usr/local/bundle/gems/mysql2-0.5.4/lib/mysql2/client.rb:148:in `_query'
|
42
|
+
# Caused by Mysql2::Error: Unknown column 'domain_admins.' in 'where clause'
|
43
|
+
# from /usr/local/bundle/gems/mysql2-0.5.4/lib/mysql2/client.rb:148:in `_query'
|
44
|
+
#
|
45
|
+
# It works well with `dependent: :delete_all` instead.
|
46
|
+
#
|
47
|
+
# PostfixAdmin::DomainAdmin Destroy (0.4ms) DELETE FROM `domain_admins` WHERE `domain_admins`.`domain` = 'example.com'
|
26
48
|
has_many :domain_admins, foreign_key: :domain, dependent: :delete_all
|
49
|
+
|
27
50
|
has_many :admins, through: :domain_admins
|
28
51
|
|
29
52
|
before_validation do |domain|
|
30
|
-
domain.domain = domain.domain
|
53
|
+
domain.domain = domain.domain&.downcase
|
31
54
|
domain.transport = "virtual"
|
32
55
|
end
|
33
56
|
|
34
57
|
scope :without_all, -> { where.not(domain: "ALL") }
|
35
58
|
|
59
|
+
# aliases that don't belong to a mailbox
|
36
60
|
def pure_aliases
|
37
61
|
rel_aliases.pure
|
38
62
|
end
|
39
63
|
|
40
|
-
def aliases_unlimited?
|
41
|
-
aliases.zero?
|
42
|
-
end
|
43
|
-
|
44
|
-
def mailboxes_unlimited?
|
45
|
-
mailboxes.zero?
|
46
|
-
end
|
47
|
-
|
48
64
|
def aliases_str
|
49
|
-
|
65
|
+
max_num_str(aliases)
|
50
66
|
end
|
51
67
|
|
52
68
|
def mailboxes_str
|
53
|
-
|
54
|
-
end
|
55
|
-
|
56
|
-
def aliases_short_str
|
57
|
-
num_short_str(aliases)
|
58
|
-
end
|
59
|
-
|
60
|
-
def mailboxes_short_str
|
61
|
-
num_short_str(mailboxes)
|
69
|
+
max_num_str(mailboxes)
|
62
70
|
end
|
63
71
|
|
64
72
|
def maxquota_str
|
65
|
-
|
66
|
-
"Unlimited"
|
67
|
-
else
|
68
|
-
"#{maxquota} MB"
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
def maxquota_short_str
|
73
|
-
if maxquota.zero?
|
74
|
-
"--"
|
75
|
-
else
|
76
|
-
"#{maxquota} MB"
|
77
|
-
end
|
73
|
+
max_num_str(maxquota)
|
78
74
|
end
|
79
75
|
|
80
76
|
private
|
81
77
|
|
82
|
-
def
|
83
|
-
|
78
|
+
def max_num_str(num)
|
79
|
+
case num
|
80
|
+
when -1
|
81
|
+
"Disabled"
|
82
|
+
when 0
|
84
83
|
"Unlimited"
|
85
84
|
else
|
86
85
|
num.to_s
|
87
86
|
end
|
88
87
|
end
|
89
|
-
|
90
|
-
def num_short_str(num)
|
91
|
-
if num.zero?
|
92
|
-
"--"
|
93
|
-
else
|
94
|
-
num.to_s
|
95
|
-
end
|
96
|
-
end
|
97
88
|
end
|
98
89
|
end
|
@@ -9,24 +9,25 @@ module PostfixAdmin
|
|
9
9
|
result.split
|
10
10
|
end
|
11
11
|
|
12
|
-
def self.password(in_password, in_scheme)
|
12
|
+
def self.password(in_password, in_scheme, prefix)
|
13
13
|
password = Shellwords.escape(in_password)
|
14
|
-
scheme
|
15
|
-
|
14
|
+
scheme = Shellwords.escape(in_scheme)
|
15
|
+
_stdin, stdout, stderr = Open3.popen3("#{self.command_name} -s #{scheme} -p #{password}")
|
16
|
+
|
16
17
|
if stderr.readlines.to_s =~ /Fatal:/
|
17
18
|
raise Error, stderr.readlines
|
18
19
|
else
|
19
|
-
stdout.readlines.first.chomp
|
20
|
+
res = stdout.readlines.first.chomp
|
21
|
+
if prefix
|
22
|
+
res
|
23
|
+
else
|
24
|
+
res.gsub("{#{scheme}}", "")
|
25
|
+
end
|
20
26
|
end
|
21
27
|
end
|
22
28
|
|
23
29
|
def self.command_name
|
24
|
-
|
25
|
-
Open3.capture3("doveadm pw -l")[2].exited?
|
26
|
-
"doveadm pw"
|
27
|
-
rescue Errno::ENOENT
|
28
|
-
"dovecotpw"
|
29
|
-
end
|
30
|
+
"doveadm pw"
|
30
31
|
end
|
31
32
|
end
|
32
33
|
end
|
@@ -12,9 +12,13 @@ module PostfixAdmin
|
|
12
12
|
message: "must be a valid email address" }
|
13
13
|
validates :maildir, presence: true, uniqueness: { case_sensitive: false }
|
14
14
|
validates :local_part, presence: true
|
15
|
+
|
16
|
+
# quota (KB)
|
15
17
|
validates :quota, presence: true,
|
16
18
|
numericality: { only_integer: true,
|
17
19
|
greater_than_or_equal_to: 0 }
|
20
|
+
|
21
|
+
# quota (MB), which actually doesn't exist in DB
|
18
22
|
validates :quota_mb, presence: true,
|
19
23
|
numericality: { only_integer: true,
|
20
24
|
greater_than_or_equal_to: 0 }
|
@@ -63,7 +67,7 @@ module PostfixAdmin
|
|
63
67
|
mailbox.quota = 0
|
64
68
|
end
|
65
69
|
mailbox.username = "#{mailbox.local_part}@#{mailbox.domain}"
|
66
|
-
mailbox.maildir
|
70
|
+
mailbox.maildir ||= "#{mailbox.domain}/#{mailbox.username}/"
|
67
71
|
mailbox.build_alias(local_part: mailbox.local_part, goto: mailbox.username,
|
68
72
|
domain: mailbox.domain)
|
69
73
|
end
|
@@ -77,12 +81,16 @@ module PostfixAdmin
|
|
77
81
|
end
|
78
82
|
end
|
79
83
|
|
80
|
-
def
|
81
|
-
|
82
|
-
|
84
|
+
def quota_mb_str
|
85
|
+
case quota
|
86
|
+
when -1
|
87
|
+
# It's not sure what 'disabled' means for quota.
|
88
|
+
"Disabled"
|
89
|
+
when 0
|
90
|
+
"Unlimited"
|
83
91
|
else
|
84
|
-
|
85
|
-
|
92
|
+
mb_size = quota / KB_TO_MB
|
93
|
+
mb_size.to_s
|
86
94
|
end
|
87
95
|
end
|
88
96
|
end
|