mailshears 0.0.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 +7 -0
- data/Rakefile +32 -0
- data/bin/install-fixtures.sh +27 -0
- data/bin/mailshears +124 -0
- data/doc/LICENSE +661 -0
- data/doc/TODO +16 -0
- data/doc/mailshears.example.conf.yml +37 -0
- data/doc/man1/mailshears.1 +184 -0
- data/lib/common/agendav_plugin.rb +54 -0
- data/lib/common/configuration.rb +116 -0
- data/lib/common/davical_plugin.rb +104 -0
- data/lib/common/domain.rb +64 -0
- data/lib/common/dovecot_plugin.rb +130 -0
- data/lib/common/errors.rb +15 -0
- data/lib/common/exit_codes.rb +9 -0
- data/lib/common/filesystem.rb +43 -0
- data/lib/common/plugin.rb +238 -0
- data/lib/common/postfixadmin_plugin.rb +180 -0
- data/lib/common/roundcube_plugin.rb +96 -0
- data/lib/common/runner.rb +73 -0
- data/lib/common/user.rb +120 -0
- data/lib/common/user_interface.rb +53 -0
- data/lib/mailshears.rb +7 -0
- data/lib/mv/mv_dummy_runner.rb +45 -0
- data/lib/mv/mv_plugin.rb +40 -0
- data/lib/mv/mv_runner.rb +56 -0
- data/lib/mv/plugins/agendav.rb +46 -0
- data/lib/mv/plugins/davical.rb +43 -0
- data/lib/mv/plugins/dovecot.rb +64 -0
- data/lib/mv/plugins/postfixadmin.rb +70 -0
- data/lib/mv/plugins/roundcube.rb +44 -0
- data/lib/prune/plugins/agendav.rb +13 -0
- data/lib/prune/plugins/davical.rb +13 -0
- data/lib/prune/plugins/dovecot.rb +11 -0
- data/lib/prune/plugins/postfixadmin.rb +13 -0
- data/lib/prune/plugins/roundcube.rb +14 -0
- data/lib/prune/prune_dummy_runner.rb +44 -0
- data/lib/prune/prune_plugin.rb +66 -0
- data/lib/prune/prune_runner.rb +34 -0
- data/lib/rm/plugins/agendav.rb +38 -0
- data/lib/rm/plugins/davical.rb +38 -0
- data/lib/rm/plugins/dovecot.rb +48 -0
- data/lib/rm/plugins/postfixadmin.rb +114 -0
- data/lib/rm/plugins/roundcube.rb +42 -0
- data/lib/rm/rm_dummy_runner.rb +39 -0
- data/lib/rm/rm_plugin.rb +77 -0
- data/lib/rm/rm_runner.rb +51 -0
- data/mailshears.gemspec +39 -0
- data/test/mailshears.test.conf.yml +36 -0
- data/test/mailshears_test.rb +250 -0
- data/test/sql/agendav-fixtures.sql +9 -0
- data/test/sql/agendav.sql +157 -0
- data/test/sql/davical-fixtures.sql +23 -0
- data/test/sql/davical.sql +4371 -0
- data/test/sql/postfixadmin-fixtures.sql +48 -0
- data/test/sql/postfixadmin.sql +737 -0
- data/test/sql/roundcube-fixtures.sql +4 -0
- data/test/sql/roundcube.sql +608 -0
- data/test/test_mv.rb +174 -0
- data/test/test_prune.rb +121 -0
- data/test/test_rm.rb +154 -0
- metadata +133 -0
data/doc/TODO
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
* There is essentially no error handling. We report errors, but we
|
2
|
+
don't fail when we see one. The main reason for this is that we
|
3
|
+
don't know when each plugin will be run. If the first plugin
|
4
|
+
encounters an error, we could quit right there. But what if the
|
5
|
+
third one fails after the first two succeed? We would need some kind
|
6
|
+
of rollback mechanism.
|
7
|
+
|
8
|
+
For "mv", a rollback is conceivable. But with "rm", there's no going
|
9
|
+
back. Maybe relying on the user to interpret the output and go
|
10
|
+
fix stuff himself is the best we can do?
|
11
|
+
|
12
|
+
* Add OpenDKIM support.
|
13
|
+
|
14
|
+
* Make a release.
|
15
|
+
|
16
|
+
* Implement moving of domains.
|
@@ -0,0 +1,37 @@
|
|
1
|
+
i_mean_business: false
|
2
|
+
plugins: [agendav, davical, dovecot, postfixadmin, roundcube]
|
3
|
+
|
4
|
+
agendav_dbhost: localhost
|
5
|
+
agendav_dbport: 5432
|
6
|
+
agendav_dbopts:
|
7
|
+
agendav_dbtty:
|
8
|
+
agendav_dbuser: postgres
|
9
|
+
agendav_dbpass:
|
10
|
+
agendav_dbname: agendav
|
11
|
+
|
12
|
+
davical_dbhost: localhost
|
13
|
+
davical_dbport: 5432
|
14
|
+
davical_dbopts:
|
15
|
+
davical_dbtty:
|
16
|
+
davical_dbuser: postgres
|
17
|
+
davical_dbpass:
|
18
|
+
davical_dbname: davical
|
19
|
+
|
20
|
+
dovecot_mail_root: /var/spool/mail/vhosts
|
21
|
+
|
22
|
+
postfixadmin_dbhost: localhost
|
23
|
+
postfixadmin_dbport: 5432
|
24
|
+
postfixadmin_dbopts:
|
25
|
+
postfixadmin_dbtty:
|
26
|
+
postfixadmin_dbuser: postgres
|
27
|
+
postfixadmin_dbpass:
|
28
|
+
postfixadmin_dbname: postfixadmin
|
29
|
+
|
30
|
+
roundcube_dbhost: localhost
|
31
|
+
roundcube_dbport: 5432
|
32
|
+
roundcube_dbopts:
|
33
|
+
roundcube_dbtty:
|
34
|
+
roundcube_dbuser: postgres
|
35
|
+
roundcube_dbpass:
|
36
|
+
roundcube_dbname: roundcube
|
37
|
+
|
@@ -0,0 +1,184 @@
|
|
1
|
+
.TH mailshears 1
|
2
|
+
|
3
|
+
.SH NAME
|
4
|
+
mailshears \- mangle your mail garden
|
5
|
+
.SH SYNOPSIS
|
6
|
+
|
7
|
+
\fBmailshears\fR [ [\fBprune\fR] | [\fBrm\fR <\fItargets\fR>] | [\fBmv\fR <\fIsrc\fR> <\fIdst\fR>] ]
|
8
|
+
|
9
|
+
.SH DESCRIPTION
|
10
|
+
|
11
|
+
Managing a mail system with virtual users is annoying. The
|
12
|
+
authoritative database of users is stored in one table, but every
|
13
|
+
other piece of software keeps its own database of users.
|
14
|
+
|
15
|
+
If you're using PostfixAdmin to manage your users, what happens when
|
16
|
+
you delete a user? Chances are, nothing happens: mail directories are
|
17
|
+
left behind, webmail preferences are saved, address books become
|
18
|
+
orphaned. That's what mailshears was designed to fix. It cleans up
|
19
|
+
after you remove a user or domain.
|
20
|
+
|
21
|
+
Another stupidly difficult task is renaming a single email
|
22
|
+
account. It's easy to move the user in one database, but then all of
|
23
|
+
the remaining filesystem directories and databases need to be updated
|
24
|
+
as well. Since these two tasks are related, mailshears does them both.
|
25
|
+
|
26
|
+
It is assumed that you use the \fBPostfixAdmin schema\fI with
|
27
|
+
\fBPostgreSQL\fR for your virtual users and domains. The rest of the
|
28
|
+
functionality is provided by plugins. The following are supported at
|
29
|
+
the moment:
|
30
|
+
\#
|
31
|
+
.IP \(bu 2
|
32
|
+
Agendav (database)
|
33
|
+
.IP \(bu
|
34
|
+
DAViCal (datbase)
|
35
|
+
.IP \(bu
|
36
|
+
Dovecot (filesystem)
|
37
|
+
.IP \(bu
|
38
|
+
PostfixAdmin (database)
|
39
|
+
.IP \(bu
|
40
|
+
Roundcube (database)
|
41
|
+
|
42
|
+
You are free to pick and choose the plugins that you need. The Dovecot
|
43
|
+
plugin is a misnomer: as long as your mail is stored on disk in
|
44
|
+
directories of the form \(dqexample.com/user\(dq, it should work for
|
45
|
+
you.
|
46
|
+
|
47
|
+
.SH MODES
|
48
|
+
Mailshears has three modes, each of which takes different
|
49
|
+
arguments. The default mode prunes leftover stuff.
|
50
|
+
\#
|
51
|
+
.IP \(bu 2
|
52
|
+
\fBprune\fR
|
53
|
+
|
54
|
+
The default \(dqprune\(dq mode removes leftovers from your mail
|
55
|
+
system. The list of current users is collected from PostfixAdmin. The
|
56
|
+
other plugins are then queried to find any extra users/domains to
|
57
|
+
remove.
|
58
|
+
\#
|
59
|
+
.IP \(bu
|
60
|
+
\fBrm\fR
|
61
|
+
|
62
|
+
The \(dqrm\(dq mode removes a list of users and/or domains called
|
63
|
+
\fItargets\fR.
|
64
|
+
\#
|
65
|
+
.IP \(bu
|
66
|
+
\fBmv\fR
|
67
|
+
|
68
|
+
The \(dqmv\(dq mode renames the \fIsrc\fR user to \fIdst\fR. The
|
69
|
+
source user and destination domains must exist. Domains may not be
|
70
|
+
moved.
|
71
|
+
|
72
|
+
.SH EXAMPLES
|
73
|
+
|
74
|
+
Pruning leftover users and domains:
|
75
|
+
|
76
|
+
.nf
|
77
|
+
.I $ mailshears
|
78
|
+
mailshears, 2015-11-08 17:01:47 -0500 (Plugin: PrunePlugin)
|
79
|
+
-----------------------------------------------------------
|
80
|
+
AgendavPrune - Removed user booger@example.com.
|
81
|
+
DavicalPrune - Removed user booger@example.com (Principal ID: 2).
|
82
|
+
DovecotPrune - Removed user booger@example.com (/tmp/mailshears-test/example.com/booger).
|
83
|
+
DovecotPrune - Removed user jeremy@example.com (/tmp/mailshears-test/example.com/jeremy).
|
84
|
+
RoundcubePrune - Removed user booger@example.com (User ID: 2).
|
85
|
+
.fi
|
86
|
+
|
87
|
+
Removing a specific user:
|
88
|
+
|
89
|
+
.nf
|
90
|
+
.I $ mailshears rm adam@example.net
|
91
|
+
mailshears, 2015-11-08 17:04:42 -0500 (Plugin: RmPlugin)
|
92
|
+
--------------------------------------------------------
|
93
|
+
AgendavRm - Removed user adam@example.net.
|
94
|
+
DavicalRm - User adam@example.net not found.
|
95
|
+
DovecotRm - Removed user adam@example.net (/tmp/mailshears-test/example.net/adam).
|
96
|
+
PostfixadminRm - Removed user adam@example.net.
|
97
|
+
RoundcubeRm - Removed user adam@example.net (User ID: 3).
|
98
|
+
.fi
|
99
|
+
|
100
|
+
Removing a domain:
|
101
|
+
|
102
|
+
.nf
|
103
|
+
.I $ mailshears rm example.net
|
104
|
+
mailshears, 2015-11-08 17:05:42 -0500 (Plugin: RmPlugin)
|
105
|
+
--------------------------------------------------------
|
106
|
+
AgendavRm - Removed domain example.net.
|
107
|
+
DavicalRm - Domain example.net not found.
|
108
|
+
DovecotRm - Removed domain example.net (/tmp/mailshears-test/example.net).
|
109
|
+
PostfixadminRm - Removed domain example.net.
|
110
|
+
RoundcubeRm - Removed domain example.net.
|
111
|
+
.fi
|
112
|
+
|
113
|
+
Renaming an existing user:
|
114
|
+
|
115
|
+
.nf
|
116
|
+
.I $ mailshears mv alice@example.com alice@example.net
|
117
|
+
mailshears, 2015-11-08 17:06:29 -0500 (Plugin: MvPlugin)
|
118
|
+
--------------------------------------------------------
|
119
|
+
AgendavMv - Source user alice@example.com not found.
|
120
|
+
DavicalMv - Moved user alice@example.com (Principal ID: 1) to alice@example.net (Principal ID: 1).
|
121
|
+
DovecotMv - Moved user alice@example.com (/tmp/mailshears-test/example.com/alice) to alice@example.net (/tmp/mailshears-test/example.net/alice).
|
122
|
+
PostfixadminMv - Moved user alice@example.com to alice@example.net.
|
123
|
+
RoundcubeMv - Moved user alice@example.com (User ID: 1) to alice@example.net (User ID: 1).
|
124
|
+
.fi
|
125
|
+
|
126
|
+
.SH CONFIGURATION
|
127
|
+
|
128
|
+
Mailshears is configured with a YAML file containing all of the
|
129
|
+
database settings and plugin information. This file should be located
|
130
|
+
at ~/.mailshears.conf.yml, in your home directory.
|
131
|
+
|
132
|
+
The following two settings are global:
|
133
|
+
\#
|
134
|
+
.IP \(bu 2
|
135
|
+
\fIi_mean_business\fR (default: false) If this is set to false, mailshears
|
136
|
+
will output some \(dqwhat if\(dq information but won't actually
|
137
|
+
(re)move anything.
|
138
|
+
\#
|
139
|
+
.IP \(bu
|
140
|
+
\fIplugins\fR (default: ['postfixadmin'])
|
141
|
+
A list of enabled plugins. Valid values are \(dqagendav\(dq,
|
142
|
+
\(dqdavical\(dq, \(dqdovecot\(dq, \(dqpostfixadmin\(dq, and
|
143
|
+
\(dqroundcube\(dq.
|
144
|
+
.P
|
145
|
+
The \(dqdovecot\(dq plugin supports the following:
|
146
|
+
\#
|
147
|
+
.IP \(bu 2
|
148
|
+
\fIdovecot_mail_root\fR (default: /tmp/mailshears-test)
|
149
|
+
|
150
|
+
The location of your mail store. The domain directories should be
|
151
|
+
located one level beneath dovecot_mail_root.
|
152
|
+
.P
|
153
|
+
The database plugins all have the same configutation options:
|
154
|
+
connections settings preceded by the plugin name. So in what follows,
|
155
|
+
<plugin> would be replaced by \(dqagendav\(dq, \(dqdavical\(dq, or so
|
156
|
+
on. Their meanings should be self-explanatory.
|
157
|
+
\#
|
158
|
+
.IP \(bu 2
|
159
|
+
\fI<plugin>_dbhost\fR (default: localhost)
|
160
|
+
\#
|
161
|
+
.IP \(bu
|
162
|
+
\fI<plugin>_dbport\fR (default: 5432)
|
163
|
+
\#
|
164
|
+
.IP \(bu
|
165
|
+
\fI<plugin>_dbopts\fR (default: empty)
|
166
|
+
\#
|
167
|
+
.IP \(bu
|
168
|
+
\fI<plugin>_dbtty\fR (default: empty)
|
169
|
+
\#
|
170
|
+
.IP \(bu
|
171
|
+
\fI<plugin>_dbuser\fR (default: 'postgres')
|
172
|
+
\#
|
173
|
+
.IP \(bu
|
174
|
+
\fI<plugin>_dbpass\fR (default: empty)
|
175
|
+
\#
|
176
|
+
.IP \(bu
|
177
|
+
\fI<plugin>_dbname\fR (default: <plugin>)
|
178
|
+
.P
|
179
|
+
An example configuration file, mailshears.example.conf.yml, is shipped
|
180
|
+
with mailshears.
|
181
|
+
|
182
|
+
.SH BUGS
|
183
|
+
|
184
|
+
Send bugs to michael@orlitzky.com.
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'common/plugin'
|
2
|
+
require 'common/user'
|
3
|
+
|
4
|
+
# Code that all Agendav plugins ({AgendavPrune}, {AgendavRm},
|
5
|
+
# {AgendavMv}) share.
|
6
|
+
module AgendavPlugin
|
7
|
+
|
8
|
+
# We implement the Plugin "interface."
|
9
|
+
include Plugin
|
10
|
+
|
11
|
+
|
12
|
+
# Initialize this Agendav {Plugin} with values in *cfg*.
|
13
|
+
#
|
14
|
+
# @param cfg [Configuration] the configuration for this plugin.
|
15
|
+
#
|
16
|
+
def initialize(cfg)
|
17
|
+
@db_hash = {
|
18
|
+
:host => cfg.agendav_dbhost,
|
19
|
+
:port => cfg.agendav_dbport,
|
20
|
+
:options => cfg.agendav_dbopts,
|
21
|
+
:tty => cfg.agendav_dbtty,
|
22
|
+
:dbname => cfg.agendav_dbname,
|
23
|
+
:user => cfg.agendav_dbuser,
|
24
|
+
:password => cfg.agendav_dbpass }
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
# Return a list of Agendav users.
|
29
|
+
#
|
30
|
+
# @return [Array<User>] a list of users contained in the
|
31
|
+
# Agendav database.
|
32
|
+
#
|
33
|
+
def list_users()
|
34
|
+
users = []
|
35
|
+
|
36
|
+
connection = PG::Connection.new(@db_hash)
|
37
|
+
|
38
|
+
sql_query = '(SELECT username FROM prefs)'
|
39
|
+
sql_query += 'UNION'
|
40
|
+
sql_query += '(SELECT user_from FROM shared);'
|
41
|
+
|
42
|
+
begin
|
43
|
+
connection.query(sql_query) do |result|
|
44
|
+
users = result.field_values('username')
|
45
|
+
end
|
46
|
+
ensure
|
47
|
+
# Make sure the connection gets closed even if the query explodes.
|
48
|
+
connection.close()
|
49
|
+
end
|
50
|
+
|
51
|
+
return users.map{ |u| User.new(u) }
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
# A configuration object that knows how to read options out of a file
|
4
|
+
# in <tt>~/.mailshears.conf.yml</tt>. The configuration options can be
|
5
|
+
# accessed via methods even though the internal representation is a
|
6
|
+
# hash.
|
7
|
+
#
|
8
|
+
# === Examples
|
9
|
+
#
|
10
|
+
# >> cfg = Configuration.new()
|
11
|
+
# >> cfg.i_mean_business()
|
12
|
+
# => true
|
13
|
+
#
|
14
|
+
class Configuration
|
15
|
+
|
16
|
+
# The default path to the user's configuration file.
|
17
|
+
USERCONF_PATH = ENV['HOME'] + '/.mailshears.conf.yml'
|
18
|
+
|
19
|
+
# The hash structure in which we store our configuration options
|
20
|
+
# internally.
|
21
|
+
@dict = {}
|
22
|
+
|
23
|
+
|
24
|
+
# Initialize a {Configuration} object with the config file at *path*.
|
25
|
+
#
|
26
|
+
# @param path [String] the path to the configuration file to
|
27
|
+
# load. We check for a file named ".mailshears.conf.yml" in the
|
28
|
+
# user's home directory by default.
|
29
|
+
#
|
30
|
+
def initialize(path = USERCONF_PATH)
|
31
|
+
cfg = default_configuration()
|
32
|
+
|
33
|
+
# Now, load the user configuration which will override the
|
34
|
+
# variables defined above.
|
35
|
+
begin
|
36
|
+
user_config = YAML.load(File.open(path))
|
37
|
+
|
38
|
+
# Write our own update() method for Ruby 1.8.
|
39
|
+
user_config.each do |key, value|
|
40
|
+
cfg[key] = value
|
41
|
+
end
|
42
|
+
rescue Errno::ENOENT
|
43
|
+
# If the user config file doesn't exist, whatever.
|
44
|
+
end
|
45
|
+
|
46
|
+
# Convert all of the keys to symbols.
|
47
|
+
cfg = cfg.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
|
48
|
+
|
49
|
+
@dict = cfg
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
# Replace all missing method calls with hash lookups. This lets us
|
54
|
+
# retrieve the values in our option hash by using methods named
|
55
|
+
# after the associated keys.
|
56
|
+
#
|
57
|
+
# @param sym [Symbol] the method that was called.
|
58
|
+
#
|
59
|
+
# @return [Object] the config file value associated with *sym*.
|
60
|
+
#
|
61
|
+
def method_missing(sym, *args)
|
62
|
+
return @dict[sym]
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
private;
|
67
|
+
|
68
|
+
|
69
|
+
# A default config hash.
|
70
|
+
#
|
71
|
+
# @return [Hash] sensible default configuration values.
|
72
|
+
#
|
73
|
+
def default_configuration()
|
74
|
+
d = {}
|
75
|
+
|
76
|
+
d['i_mean_business'] = false
|
77
|
+
d['plugins'] = ['postfixadmin']
|
78
|
+
|
79
|
+
d['agendav_dbhost'] = 'localhost'
|
80
|
+
d['agendav_dbport'] = 5432
|
81
|
+
d['agendav_dbopts'] = ''
|
82
|
+
d['agendav_dbtty'] = ''
|
83
|
+
d['agendav_dbuser'] = 'postgres'
|
84
|
+
d['agendav_dbpass'] = ''
|
85
|
+
d['agendav_dbname'] = 'agendav'
|
86
|
+
|
87
|
+
d['davical_dbhost'] = 'localhost'
|
88
|
+
d['davical_dbport'] = 5432
|
89
|
+
d['davical_dbopts'] = ''
|
90
|
+
d['davical_dbtty'] = ''
|
91
|
+
d['davical_dbuser'] = 'postgres'
|
92
|
+
d['davical_dbpass'] = ''
|
93
|
+
d['davical_dbname'] = 'davical'
|
94
|
+
|
95
|
+
d['dovecot_mail_root'] = '/tmp/mailshears-test'
|
96
|
+
|
97
|
+
d['postfixadmin_dbhost'] = 'localhost'
|
98
|
+
d['postfixadmin_dbport'] = 5432
|
99
|
+
d['postfixadmin_dbopts'] = ''
|
100
|
+
d['postfixadmin_dbtty'] = ''
|
101
|
+
d['postfixadmin_dbuser'] = 'postgres'
|
102
|
+
d['postfixadmin_dbpass'] = ''
|
103
|
+
d['postfixadmin_dbname'] = 'postfixadmin'
|
104
|
+
|
105
|
+
d['roundcube_dbhost'] = 'localhost'
|
106
|
+
d['roundcube_dbport'] = 5432
|
107
|
+
d['roundcube_dbopts'] = ''
|
108
|
+
d['roundcube_dbtty'] = ''
|
109
|
+
d['roundcube_dbuser'] = 'postgres'
|
110
|
+
d['roundcube_dbpass'] = ''
|
111
|
+
d['roundcube_dbname'] = 'roundcube'
|
112
|
+
|
113
|
+
return d
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'common/plugin'
|
2
|
+
require 'common/user'
|
3
|
+
|
4
|
+
# Code that all DAViCal plugins ({DavicalPrune}, {DavicalRm}, and
|
5
|
+
# {DavicalMv}) will share.
|
6
|
+
#
|
7
|
+
module DavicalPlugin
|
8
|
+
|
9
|
+
# We implement the Plugin "interface."
|
10
|
+
include Plugin
|
11
|
+
|
12
|
+
# Initialize this DAViCal {Plugin} with values in *cfg*.
|
13
|
+
#
|
14
|
+
# @param cfg [Configuration] the configuration for this plugin.
|
15
|
+
#
|
16
|
+
def initialize(cfg)
|
17
|
+
@db_hash = {
|
18
|
+
:host => cfg.davical_dbhost,
|
19
|
+
:port => cfg.davical_dbport,
|
20
|
+
:options => cfg.davical_dbopts,
|
21
|
+
:tty => cfg.davical_dbtty,
|
22
|
+
:dbname => cfg.davical_dbname,
|
23
|
+
:user => cfg.davical_dbuser,
|
24
|
+
:password => cfg.davical_dbpass }
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
# Describe the given DAViCal user who is assumed to exist.
|
29
|
+
#
|
30
|
+
# @param user [User] the {User} object whose description we want.
|
31
|
+
#
|
32
|
+
# @return [String] a String describing the given *user* in terms
|
33
|
+
# of his DAViCal "Principal ID".
|
34
|
+
#
|
35
|
+
def describe_user(user)
|
36
|
+
principal_id = self.get_principal_id(user)
|
37
|
+
return "Principal ID: #{principal_id}"
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
#
|
42
|
+
# Produce a list of DAViCal users.
|
43
|
+
#
|
44
|
+
# This method remains public for use in testing.
|
45
|
+
#
|
46
|
+
# @return [Array<User>] an array of {User} objects, one for each
|
47
|
+
# user found in the DAViCal database.
|
48
|
+
#
|
49
|
+
def list_users()
|
50
|
+
usernames = []
|
51
|
+
|
52
|
+
connection = PG::Connection.new(@db_hash)
|
53
|
+
|
54
|
+
# User #1 is the super-user, and not tied to an email address.
|
55
|
+
sql_query = 'SELECT username FROM usr WHERE user_no > 1;'
|
56
|
+
|
57
|
+
begin
|
58
|
+
connection.query(sql_query) do |result|
|
59
|
+
usernames = result.field_values('username')
|
60
|
+
end
|
61
|
+
ensure
|
62
|
+
# Make sure the connection gets closed even if the query explodes.
|
63
|
+
connection.close()
|
64
|
+
end
|
65
|
+
|
66
|
+
return usernames.map{ |u| User.new(u) }
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
protected;
|
71
|
+
|
72
|
+
|
73
|
+
# Find the "Principal ID" of the given user.
|
74
|
+
#
|
75
|
+
# @param user [User] the user whose Principal ID we want.
|
76
|
+
#
|
77
|
+
# @return [Fixnum] an integer representing the user's Principal ID
|
78
|
+
# that we obtained from the DAViCal database.
|
79
|
+
#
|
80
|
+
def get_principal_id(user)
|
81
|
+
principal_id = nil
|
82
|
+
|
83
|
+
connection = PG::Connection.new(@db_hash)
|
84
|
+
|
85
|
+
sql_query = 'SELECT principal.principal_id '
|
86
|
+
sql_query += 'FROM (principal INNER JOIN usr '
|
87
|
+
sql_query += ' ON principal.user_no = usr.user_no) '
|
88
|
+
sql_query += 'WHERE usr.username = $1;'
|
89
|
+
|
90
|
+
begin
|
91
|
+
connection.query(sql_query, [user.to_s()]) do |result|
|
92
|
+
if result.num_tuples > 0
|
93
|
+
principal_id = result[0]['principal_id']
|
94
|
+
end
|
95
|
+
end
|
96
|
+
ensure
|
97
|
+
# Make sure the connection gets closed even if the query explodes.
|
98
|
+
connection.close()
|
99
|
+
end
|
100
|
+
|
101
|
+
return principal_id
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|