postfix_admin 0.2.1 → 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.
@@ -1,6 +1,7 @@
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
@@ -29,55 +30,49 @@ module PostfixAdmin
29
30
  name = name.downcase if name
30
31
 
31
32
  if name =~ /@/
33
+ # address like argument
32
34
  if Admin.exists?(name)
33
- show_admin_details(name)
34
- end
35
-
36
- if Mailbox.exists?(name)
37
- show_account_details(name)
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)
38
42
  elsif Alias.exists?(name)
43
+ # alias
39
44
  show_alias_details(name)
45
+ else
46
+ raise Error, "Could not find admin/mailbox/alias #{name}"
40
47
  end
41
48
 
42
49
  return
43
50
  end
44
51
 
45
52
  show_summary(name)
53
+ puts
46
54
 
47
55
  if name
48
- show_admin(name)
49
- show_address(name)
50
- show_alias(name)
56
+ # domain name
57
+ show_domain_details(name)
51
58
  else
59
+ # no argument: show all domains and admins
52
60
  show_domain
61
+ puts
53
62
  show_admin
54
63
  end
55
64
  end
56
65
 
57
66
  def show_summary(domain_name = nil)
58
- title = "Summary"
59
67
  if domain_name
60
- domain_name = domain_name.downcase
61
- domain_check(domain_name)
62
- title = "Summary of #{domain_name}"
63
- end
64
-
65
- report(title) do
66
- if domain_name
67
- domain = Domain.find(domain_name)
68
- puts "Mailboxes : %4d / %4s" % [domain.rel_mailboxes.count, max_str(domain.mailboxes)]
69
- puts "Aliases : %4d / %4s" % [domain.pure_aliases.count, max_str(domain.aliases)]
70
- puts "Max Quota : %4d MB" % domain.maxquota
71
- puts "Active : %3s" % domain.active_str
72
- else
73
- puts "Domains : %4d" % Domain.without_all.count
74
- puts "Admins : %4d" % Admin.count
75
- puts "Mailboxes : %4d" % Mailbox.count
76
- puts "Aliases : %4d" % Alias.pure.count
77
- end
68
+ show_domain_summary(domain_name)
69
+ else
70
+ show_general_summary
78
71
  end
79
72
  end
80
73
 
74
+ # Set up a domain
75
+ # Add a domain, add an admin, and grant the admin access to the domain
81
76
  def setup_domain(domain_name, password)
82
77
  admin = "admin@#{domain_name}"
83
78
  add_domain(domain_name)
@@ -85,62 +80,75 @@ module PostfixAdmin
85
80
  add_admin_domain(admin, domain_name)
86
81
  end
87
82
 
88
- def show_account_details(user_name)
83
+ def show_account_details(user_name, display_password: false)
89
84
  account_check(user_name)
90
85
  mailbox = Mailbox.find(user_name)
91
86
  mail_alias = Alias.find(user_name)
92
87
 
93
- report("Mailbox") do
94
- puts "Address : %s" % mailbox.username
95
- puts "Name : %s" % mailbox.name
96
- puts "Password : %s" % mailbox.password
97
- puts "Quota : %d MB" % max_str(mailbox.quota / KB_TO_MB)
98
- puts "Go to : %s" % mail_alias.goto
99
- puts "Active : %s" % mailbox.active_str
100
- end
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)
101
98
  end
102
99
 
103
- def show_admin_details(name)
100
+ def show_admin_details(name, display_password: false)
104
101
  admin_check(name)
105
102
  admin = Admin.find(name)
106
103
 
107
- report("Admin") do
108
- puts "Name : %s" % admin.username
109
- puts "Password : %s" % admin.password
110
- puts "Domains : %s" % (admin.super_admin? ? "ALL" : admin.rel_domains.count)
111
- puts "Role : %s" % (admin.super_admin? ? "Super admin" : "Admin")
112
- puts "Active : %s" % admin.active_str
113
- end
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)
114
113
  end
115
114
 
116
115
  def show_alias_details(name)
117
116
  alias_check(name)
118
117
  mail_alias = Alias.find(name)
119
- report("Alias") do
120
- puts "Address : %s" % mail_alias.address
121
- puts "Go to : %s" % mail_alias.goto
122
- puts "Active : %s" % mail_alias.active_str
123
- end
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)
124
126
  end
125
127
 
126
128
  def show_domain
127
- index = " No. Domain Aliases Mailboxes Quota (MB) Active"
128
- report('Domains', index) do
129
- if Domain.without_all.empty?
130
- puts " No domains"
131
- next
132
- end
129
+ rows = []
130
+ headings = ["No.", "Domain", "Aliases", "Mailboxes","Max Quota (MB)",
131
+ "Active", "Description"]
133
132
 
134
- Domain.without_all.each_with_index do |d, i|
135
- puts "%4d %-30s %3d /%3s %3d /%3s %10d %-3s" %
136
- [i+1, d.domain, d.pure_aliases.count, max_str(d.aliases),
137
- d.rel_mailboxes.count, max_str(d.mailboxes), d.maxquota, d.active_str]
138
- end
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]
139
145
  end
146
+
147
+ puts_table(headings: headings, rows: rows)
140
148
  end
141
149
 
142
- def add_domain(domain_name)
143
- @base.add_domain(domain_name)
150
+ def add_domain(domain_name, description: nil)
151
+ @base.add_domain(domain_name, description: description)
144
152
  puts_registered(domain_name, "a domain")
145
153
  end
146
154
 
@@ -174,6 +182,7 @@ module PostfixAdmin
174
182
  domain.mailboxes = options[:mailboxes] if options[:mailboxes]
175
183
  domain.maxquota = options[:maxquota] if options[:maxquota]
176
184
  domain.active = options[:active] unless options[:active].nil?
185
+ domain.description = options[:description] if options[:description]
177
186
  domain.save!
178
187
 
179
188
  puts "Successfully updated #{domain_name}"
@@ -187,36 +196,44 @@ module PostfixAdmin
187
196
 
188
197
  def show_admin(domain_name = nil)
189
198
  admins = domain_name ? Admin.select { |a| a.rel_domains.exists?(domain_name) } : Admin.all
190
- index = " No. Admin Domains Active"
191
- report("Admins", index) do
192
- if admins.empty?
193
- puts " No admins"
194
- next
195
- end
199
+ headings = %w[No. Admin Domains Active]
196
200
 
197
- admins.each_with_index do |a, i|
198
- domains = a.super_admin? ? 'Super admin' : a.rel_domains.count
199
- puts "%4d %-40s %11s %-3s" % [i+1, a.username, domains, a.active_str]
200
- end
201
+ puts_title("Admins")
202
+ if admins.empty?
203
+ puts "No admins"
204
+ return
201
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)
202
215
  end
203
216
 
204
217
  def show_address(domain_name)
205
218
  domain_check(domain_name)
206
219
 
220
+ rows = []
207
221
  mailboxes = Domain.find(domain_name).rel_mailboxes
208
- index = " No. Email Name Quota (MB) Active Maildir"
209
- report("Addresses", index) do
210
- if mailboxes.empty?
211
- puts " No addresses"
212
- next
213
- end
222
+ headings = ["No.", "Email", "Name", "Quota (MB)", "Active", "Maildir"]
214
223
 
215
- mailboxes.each_with_index do |m, i|
216
- quota = m.quota.to_f/ KB_TO_MB.to_f
217
- puts "%4d %-30s %-20s %10s %-3s %s" % [i+1, m.username, m.name, max_str(quota.to_i), m.active_str, m.maildir]
218
- end
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]
219
234
  end
235
+
236
+ puts_table(headings: headings, rows: rows)
220
237
  end
221
238
 
222
239
  def show_alias(domain_name)
@@ -229,21 +246,24 @@ module PostfixAdmin
229
246
  end
230
247
 
231
248
  show_alias_base("Forwards", forwards)
249
+ puts
232
250
  show_alias_base("Aliases", aliases)
233
251
  end
234
252
 
235
253
  def show_admin_domain(user_name)
236
254
  admin = Admin.find(user_name)
255
+ puts_title("Admin Domains (#{user_name})")
237
256
  if admin.rel_domains.empty?
238
- puts "\nNo domain in database"
257
+ puts "\nNo domains for #{user_name}"
239
258
  return
240
259
  end
241
260
 
242
- report("Domains (#{user_name})", " No. Domain") do
243
- admin.rel_domains.each_with_index do |d, i|
244
- puts "%4d %-30s" % [i + 1, d.domain]
245
- end
261
+ rows = []
262
+ admin.rel_domains.each_with_index do |d, i|
263
+ no = i + 1
264
+ rows << [no.to_s, d.domain]
246
265
  end
266
+ puts_table(rows: rows, headings: %w[No. Domain])
247
267
  end
248
268
 
249
269
  def add_admin(user_name, password, super_admin = false, scheme = nil)
@@ -271,9 +291,8 @@ module PostfixAdmin
271
291
  def add_account(address, password, scheme = nil, name = nil)
272
292
  validate_password(password)
273
293
 
274
- @base.add_account(address, hashed_password(password, scheme), name)
294
+ @base.add_account(address, hashed_password(password, scheme), name: name)
275
295
  puts_registered(address, "an account")
276
- show_account_details(address)
277
296
  end
278
297
 
279
298
  def add_alias(address, goto)
@@ -325,11 +344,25 @@ module PostfixAdmin
325
344
  puts_deleted(address)
326
345
  end
327
346
 
328
- def log
329
- Log.all.each do |l|
330
- time = l.timestamp.strftime("%Y-%m-%d %X %Z")
331
- puts "#{time} #{l.username} #{l.domain} #{l.action} #{l.data}"
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]
332
363
  end
364
+
365
+ puts_table(headings: headings, rows: rows)
333
366
  end
334
367
 
335
368
  def dump
@@ -366,17 +399,58 @@ module PostfixAdmin
366
399
 
367
400
  private
368
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
+
369
438
  def show_alias_base(title, addresses)
370
- report(title, " No. Address Active Go to") do
371
- if addresses.empty?
372
- puts " No #{title.downcase}"
373
- next
374
- end
439
+ rows = []
440
+ puts_title(title)
375
441
 
376
- addresses.each_with_index do |a, i|
377
- puts "%4d %-40s %-3s %s" % [i+1, a.address, a.active_str, a.goto]
378
- end
442
+ if addresses.empty?
443
+ puts "No #{title.downcase}"
444
+ return
379
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)
380
454
  end
381
455
 
382
456
  def puts_registered(name, as_str)
@@ -388,7 +462,7 @@ module PostfixAdmin
388
462
  end
389
463
 
390
464
  def config_file
391
- config_file = File.expand_path(CLI.config_file)
465
+ File.expand_path(CLI.config_file)
392
466
  end
393
467
 
394
468
  def load_config
@@ -409,17 +483,12 @@ module PostfixAdmin
409
483
  File.chmod(0600, file)
410
484
  end
411
485
 
412
- def print_line
413
- puts "-"*120
486
+ def puts_table(args)
487
+ puts Terminal::Table.new(args)
414
488
  end
415
489
 
416
- def report(title, index = nil)
417
- puts "\n[#{title}]"
418
- print_line if index
419
- puts index if index
420
- print_line
421
- yield
422
- print_line
490
+ def puts_title(title)
491
+ puts "| #{title} |"
423
492
  end
424
493
 
425
494
  def account_check(user_name)
@@ -469,23 +538,9 @@ module PostfixAdmin
469
538
  end
470
539
  end
471
540
 
472
- def max_str(value)
473
- case value
474
- when 0
475
- '--'
476
- when -1
477
- '0'
478
- else
479
- value.to_s
480
- end
481
- end
482
-
483
- private
484
-
485
541
  def hashed_password(password, in_scheme = nil)
486
542
  prefix = @base.config[:passwordhash_prefix]
487
543
  scheme = in_scheme || @base.config[:scheme]
488
- puts "scheme: #{scheme}"
489
544
  PostfixAdmin::Doveadm.password(password, scheme, prefix)
490
545
  end
491
546
  end
@@ -12,7 +12,6 @@ module DovecotCramMD5Password
12
12
  end
13
13
 
14
14
  attr_reader :password_unencrypted
15
- attr_accessor :password_unencrypted_confirmation
16
15
  end
17
16
 
18
17
  def password_unencrypted=(unencrypted_password)
@@ -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: 0 }
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: 0 }
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.downcase unless domain.domain.empty?
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
- num_str(aliases)
65
+ max_num_str(aliases)
50
66
  end
51
67
 
52
68
  def mailboxes_str
53
- num_str(mailboxes)
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
- if maxquota.zero?
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 num_str(num)
83
- if num.zero?
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
@@ -11,8 +11,9 @@ module PostfixAdmin
11
11
 
12
12
  def self.password(in_password, in_scheme, prefix)
13
13
  password = Shellwords.escape(in_password)
14
- scheme = Shellwords.escape(in_scheme)
15
- stdin, stdout, stderr = Open3.popen3("#{self.command_name} -s #{scheme} -p #{password}")
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
@@ -26,12 +27,7 @@ module PostfixAdmin
26
27
  end
27
28
 
28
29
  def self.command_name
29
- begin
30
- Open3.capture3("doveadm pw -l")[2].exited?
31
- "doveadm pw"
32
- rescue Errno::ENOENT
33
- "dovecotpw"
34
- end
30
+ "doveadm pw"
35
31
  end
36
32
  end
37
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 = "#{mailbox.domain}/#{mailbox.username}/"
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 quota_str
81
- if quota.zero?
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
- quota_mb = quota / KB_TO_MB
85
- "#{quota_mb} MB"
92
+ mb_size = quota / KB_TO_MB
93
+ mb_size.to_s
86
94
  end
87
95
  end
88
96
  end