zimbra 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/README +13 -0
- data/lib/zimbra.rb +55 -0
- data/lib/zimbra/account.rb +155 -0
- data/lib/zimbra/acl.rb +61 -0
- data/lib/zimbra/auth.rb +43 -0
- data/lib/zimbra/common_elements.rb +51 -0
- data/lib/zimbra/cos.rb +124 -0
- data/lib/zimbra/distribution_list.rb +207 -0
- data/lib/zimbra/domain.rb +138 -0
- data/lib/zimbra/handsoap_service.rb +73 -0
- metadata +104 -0
data/README
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
EXAMPLE USAGE
|
2
|
+
=============
|
3
|
+
|
4
|
+
Zimbra.url = 'http://example.com:7071/soap/service'
|
5
|
+
Zimbra.login('admin@example.com','secret')
|
6
|
+
|
7
|
+
d = Zimbra::Domain.create('luser.com')
|
8
|
+
|
9
|
+
dl = Zimbra::DistributionList.create('info@luser.com')
|
10
|
+
dl.admin_group = true
|
11
|
+
dl.save
|
12
|
+
|
13
|
+
|
data/lib/zimbra.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
$:.unshift(File.join(File.dirname(__FILE__)))
|
2
|
+
require 'zimbra/handsoap_service'
|
3
|
+
require 'zimbra/auth'
|
4
|
+
require 'zimbra/cos'
|
5
|
+
require 'zimbra/domain'
|
6
|
+
require 'zimbra/distribution_list'
|
7
|
+
require 'zimbra/account'
|
8
|
+
require 'zimbra/acl'
|
9
|
+
require 'zimbra/common_elements'
|
10
|
+
|
11
|
+
# Manages a Zimbra SOAP session. Offers ability to set the endpoint URL, log in, and enable debugging.
|
12
|
+
module Zimbra
|
13
|
+
class << self
|
14
|
+
|
15
|
+
# The URL that will be used to contact the Zimbra SOAP service
|
16
|
+
def url
|
17
|
+
@@url
|
18
|
+
end
|
19
|
+
# Sets the URL of the Zimbra SOAP service
|
20
|
+
def url=(url)
|
21
|
+
@@url = url
|
22
|
+
end
|
23
|
+
|
24
|
+
# Turn debugging on/off. Outputs full SOAP conversations to stdout.
|
25
|
+
# Zimbra.debug = true
|
26
|
+
# Zimbra.debug = false
|
27
|
+
def debug=(val)
|
28
|
+
Handsoap::Service.logger = (val ? $stdout : nil)
|
29
|
+
@@debug = val
|
30
|
+
end
|
31
|
+
|
32
|
+
# Whether debugging is enabled
|
33
|
+
def debug
|
34
|
+
@@debug ||= false
|
35
|
+
end
|
36
|
+
|
37
|
+
# Authorization token - obtained after successful login
|
38
|
+
def auth_token
|
39
|
+
@@auth_token
|
40
|
+
end
|
41
|
+
|
42
|
+
# Log into the zimbra SOAP service. This is required before any other action is performed
|
43
|
+
# If a login has already been performed, another login will not be attempted
|
44
|
+
def login(username, password)
|
45
|
+
return @@auth_token if defined?(@@auth_token) && @@auth_token
|
46
|
+
reset_login(username, password)
|
47
|
+
end
|
48
|
+
|
49
|
+
# re-log into the zimbra SOAP service
|
50
|
+
def reset_login(username, password)
|
51
|
+
puts "Logging into zimbra as #{username}"
|
52
|
+
@@auth_token = Auth.login(username, password)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
module Zimbra
|
2
|
+
class Account
|
3
|
+
class << self
|
4
|
+
def all
|
5
|
+
AccountService.all
|
6
|
+
end
|
7
|
+
|
8
|
+
def find_by_id(id)
|
9
|
+
AccountService.get_by_id(id)
|
10
|
+
end
|
11
|
+
|
12
|
+
def find_by_name(name)
|
13
|
+
AccountService.get_by_name(name)
|
14
|
+
end
|
15
|
+
|
16
|
+
def create(options)
|
17
|
+
account = new(options)
|
18
|
+
AccountService.create(account)
|
19
|
+
end
|
20
|
+
|
21
|
+
def acl_name
|
22
|
+
'account'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
attr_accessor :id, :name, :password, :acls, :cos_id, :delegated_admin
|
27
|
+
|
28
|
+
def initialize(options = {})
|
29
|
+
self.id = options[:id]
|
30
|
+
self.name = options[:name]
|
31
|
+
self.password = options[:password]
|
32
|
+
self.acls = options[:acls] || []
|
33
|
+
self.cos_id = (options[:cos] ? options[:cos].id : options[:cos_id])
|
34
|
+
self.delegated_admin = options[:delegated_admin]
|
35
|
+
end
|
36
|
+
|
37
|
+
def delegated_admin=(val)
|
38
|
+
@delegated_admin = Zimbra::Boolean.read(val)
|
39
|
+
end
|
40
|
+
def delegated_admin?
|
41
|
+
@delegated_admin
|
42
|
+
end
|
43
|
+
|
44
|
+
def save
|
45
|
+
AccountService.modify(self)
|
46
|
+
end
|
47
|
+
|
48
|
+
def delete
|
49
|
+
AccountService.delete(self)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class AccountService < HandsoapService
|
54
|
+
def all
|
55
|
+
xml = invoke("n2:GetAllAccountsRequest")
|
56
|
+
Parser.get_all_response(xml)
|
57
|
+
end
|
58
|
+
|
59
|
+
def create(account)
|
60
|
+
xml = invoke("n2:CreateAccountRequest") do |message|
|
61
|
+
Builder.create(message, account)
|
62
|
+
end
|
63
|
+
Parser.account_response(xml/"//n2:account")
|
64
|
+
end
|
65
|
+
|
66
|
+
def get_by_id(id)
|
67
|
+
xml = invoke("n2:GetAccountRequest") do |message|
|
68
|
+
Builder.get_by_id(message, id)
|
69
|
+
end
|
70
|
+
return nil if soap_fault_not_found?
|
71
|
+
Parser.account_response(xml/"//n2:account")
|
72
|
+
end
|
73
|
+
|
74
|
+
def get_by_name(name)
|
75
|
+
xml = invoke("n2:GetAccountRequest") do |message|
|
76
|
+
Builder.get_by_name(message, name)
|
77
|
+
end
|
78
|
+
return nil if soap_fault_not_found?
|
79
|
+
Parser.account_response(xml/"//n2:account")
|
80
|
+
end
|
81
|
+
|
82
|
+
def modify(account)
|
83
|
+
xml = invoke("n2:ModifyAccountRequest") do |message|
|
84
|
+
Builder.modify(message, account)
|
85
|
+
end
|
86
|
+
Parser.account_response(xml/'//n2:account')
|
87
|
+
end
|
88
|
+
|
89
|
+
def delete(dist)
|
90
|
+
xml = invoke("n2:DeleteAccountRequest") do |message|
|
91
|
+
Builder.delete(message, dist.id)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
class Builder
|
96
|
+
class << self
|
97
|
+
def create(message, account)
|
98
|
+
message.add 'name', account.name
|
99
|
+
message.add 'password', account.password
|
100
|
+
A.inject(message, 'zimbraCOSId', account.cos_id)
|
101
|
+
end
|
102
|
+
|
103
|
+
def get_by_id(message, id)
|
104
|
+
message.add 'account', id do |c|
|
105
|
+
c.set_attr 'by', 'id'
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def get_by_name(message, name)
|
110
|
+
message.add 'account', name do |c|
|
111
|
+
c.set_attr 'by', 'name'
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def modify(message, account)
|
116
|
+
message.add 'id', account.id
|
117
|
+
modify_attributes(message, distribution_list)
|
118
|
+
end
|
119
|
+
def modify_attributes(message, account)
|
120
|
+
if account.acls.empty?
|
121
|
+
ACL.delete_all(message)
|
122
|
+
else
|
123
|
+
account.acls.each do |acl|
|
124
|
+
acl.apply(message)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
Zimbra::A.inject(node, 'zimbraCOSId', account.cos_id)
|
128
|
+
Zimbra::A.inject(node, 'zimbraIsDelegatedAdminAccount', (delegated_admin ? 'TRUE' : 'FALSE'))
|
129
|
+
end
|
130
|
+
|
131
|
+
def delete(message, id)
|
132
|
+
message.add 'id', id
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
class Parser
|
137
|
+
class << self
|
138
|
+
def get_all_response(response)
|
139
|
+
(response/"//n2:account").map do |node|
|
140
|
+
account_response(node)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def account_response(node)
|
145
|
+
id = (node/'@id').to_s
|
146
|
+
name = (node/'@name').to_s
|
147
|
+
acls = Zimbra::ACL.read(node)
|
148
|
+
cos_id = Zimbra::A.read(node, 'zimbraCOSId')
|
149
|
+
delegated_admin = Zimbra::A.read(node, 'zimbraIsDelegatedAdminAccount')
|
150
|
+
Zimbra::Account.new(:id => id, :name => name, :acls => acls, :cos_id => cos_id, :delegated_admin => delegated_admin)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
data/lib/zimbra/acl.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
module Zimbra
|
2
|
+
class ACL
|
3
|
+
class TargetObjectNotFound < StandardError; end
|
4
|
+
|
5
|
+
TARGET_CLASSES = [Zimbra::Domain, Zimbra::DistributionList, Zimbra::Cos]
|
6
|
+
TARGET_MAPPINGS = TARGET_CLASSES.inject({}) do |hsh, klass|
|
7
|
+
hsh[klass.acl_name] = klass
|
8
|
+
hsh[klass] = klass.acl_name
|
9
|
+
hsh
|
10
|
+
end
|
11
|
+
|
12
|
+
class << self
|
13
|
+
def delete_all(xmldoc)
|
14
|
+
A.inject(xmldoc, 'zimbraACE', '', 'c' => '1')
|
15
|
+
end
|
16
|
+
|
17
|
+
def read(node)
|
18
|
+
list = A.read(node, 'zimbraACE')
|
19
|
+
return nil if list.nil?
|
20
|
+
list = [list] unless list.respond_to?(:map)
|
21
|
+
acls = list.map do |ace|
|
22
|
+
from_s(ace)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def from_zimbra(node)
|
27
|
+
from_s(node.to_s)
|
28
|
+
end
|
29
|
+
|
30
|
+
def from_s(value)
|
31
|
+
target_id, target_name, name = value.split(' ')
|
32
|
+
target_class = TARGET_MAPPINGS[target_name]
|
33
|
+
raise TargetObjectNotFound, "Target object not found for acl #{acl_string}" if target_class.nil?
|
34
|
+
new(:target_id => target_id, :target_class => target_class, :name => name)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
attr_accessor :target_id, :target_class, :name
|
39
|
+
|
40
|
+
def initialize(options = {})
|
41
|
+
if options[:target]
|
42
|
+
self.target_id = options[:target].id
|
43
|
+
self.target_class = options[:target].class
|
44
|
+
else
|
45
|
+
self.target_id = options[:target_id]
|
46
|
+
self.target_class = options[:target_class]
|
47
|
+
end
|
48
|
+
self.name = options[:name]
|
49
|
+
end
|
50
|
+
|
51
|
+
def to_zimbra_acl_value
|
52
|
+
id = target_id
|
53
|
+
type = target_class.acl_name
|
54
|
+
"#{id} #{type} #{name}"
|
55
|
+
end
|
56
|
+
|
57
|
+
def apply(xmldoc)
|
58
|
+
A.inject(xmldoc, 'zimbraACE', to_zimbra_acl_value, 'c' => '1')
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
data/lib/zimbra/auth.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
module Zimbra
|
2
|
+
class Auth
|
3
|
+
def self.login(username, password)
|
4
|
+
AuthService.login(username, password)
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
class AuthService < Handsoap::Service
|
9
|
+
include HandsoapErrors
|
10
|
+
include Zimbra::HandsoapNamespaces
|
11
|
+
extend HandsoapUriOverrides
|
12
|
+
|
13
|
+
def on_create_document(doc)
|
14
|
+
request_namespaces(doc)
|
15
|
+
end
|
16
|
+
def on_response_document(doc)
|
17
|
+
response_namespaces(doc)
|
18
|
+
end
|
19
|
+
|
20
|
+
def login(username, password)
|
21
|
+
xml = invoke('n2:AuthRequest') do |message|
|
22
|
+
Builder.auth(message, username, password)
|
23
|
+
end
|
24
|
+
Parser.auth_token(xml)
|
25
|
+
end
|
26
|
+
|
27
|
+
class Builder
|
28
|
+
class << self
|
29
|
+
def auth(message, username, password)
|
30
|
+
message.add 'name', username
|
31
|
+
message.add 'password', password
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
class Parser
|
36
|
+
class << self
|
37
|
+
def auth_token(response)
|
38
|
+
(response/'//n2:authToken').to_s
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Zimbra
|
2
|
+
class A
|
3
|
+
class << self
|
4
|
+
def inject(xmldoc, name, value, extra_attributes = {})
|
5
|
+
new(name, value, extra_attributes).inject(xmldoc)
|
6
|
+
end
|
7
|
+
|
8
|
+
def read(xmldoc, name)
|
9
|
+
nodes = (xmldoc/"//n2:a[@n='#{name}']")
|
10
|
+
return nil if nodes.nil?
|
11
|
+
if nodes.size > 1
|
12
|
+
nodes.map { |n| from_node(n, name).value }
|
13
|
+
else
|
14
|
+
from_node(nodes, name).value
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def from_node(node, name)
|
19
|
+
new(name, node.to_s)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_accessor :name, :value, :extra_attributes
|
24
|
+
|
25
|
+
def initialize(name, value, extra_attributes = {})
|
26
|
+
self.name = name
|
27
|
+
self.value = value
|
28
|
+
self.extra_attributes = extra_attributes || {}
|
29
|
+
end
|
30
|
+
|
31
|
+
def inject(xmldoc)
|
32
|
+
xmldoc.add 'a', value do |a|
|
33
|
+
a.set_attr 'n', name
|
34
|
+
extra_attributes.each do |eaname, eavalue|
|
35
|
+
a.set_attr eaname, eavalue
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class Boolean
|
42
|
+
def self.read(value)
|
43
|
+
case value
|
44
|
+
when 'TRUE' then true
|
45
|
+
when 'FALSE' then false
|
46
|
+
when true then true
|
47
|
+
else false
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/lib/zimbra/cos.rb
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
module Zimbra
|
2
|
+
class Cos
|
3
|
+
class << self
|
4
|
+
def find_by_id(id)
|
5
|
+
CosService.get_by_id(id)
|
6
|
+
end
|
7
|
+
|
8
|
+
def find_by_name(name)
|
9
|
+
CosService.get_by_name(name)
|
10
|
+
end
|
11
|
+
|
12
|
+
def create(name)
|
13
|
+
CosService.create(name)
|
14
|
+
end
|
15
|
+
|
16
|
+
def acl_name
|
17
|
+
'cos'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_accessor :id, :name, :acls
|
22
|
+
|
23
|
+
def initialize(id, name, acls = [])
|
24
|
+
self.id = id
|
25
|
+
self.name = name
|
26
|
+
self.acls = acls || []
|
27
|
+
end
|
28
|
+
|
29
|
+
def save
|
30
|
+
CosService.modify(self)
|
31
|
+
end
|
32
|
+
|
33
|
+
def delete
|
34
|
+
CosService.delete(self)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class CosService < HandsoapService
|
39
|
+
def get_by_id(id)
|
40
|
+
response = invoke("n2:GetCosRequest") do |message|
|
41
|
+
Builder.get_by_id(message, id)
|
42
|
+
end
|
43
|
+
return nil if soap_fault_not_found?
|
44
|
+
Parser.cos_response(response/"//n2:cos")
|
45
|
+
end
|
46
|
+
|
47
|
+
def get_by_name(name)
|
48
|
+
response = invoke("n2:GetCosRequest") do |message|
|
49
|
+
Builder.get_by_name(message, name)
|
50
|
+
end
|
51
|
+
return nil if soap_fault_not_found?
|
52
|
+
Parser.cos_response(response/"//n2:cos")
|
53
|
+
end
|
54
|
+
|
55
|
+
def create(name)
|
56
|
+
response = invoke("n2:CreateCosRequest") do |message|
|
57
|
+
Builder.create(message, name)
|
58
|
+
end
|
59
|
+
Parser.cos_response(response/"//n2:cos")
|
60
|
+
end
|
61
|
+
|
62
|
+
def modify(cos)
|
63
|
+
xml = invoke("n2:ModifyCosRequest") do |message|
|
64
|
+
Builder.modify(message, cos)
|
65
|
+
end
|
66
|
+
Parser.cos_response(xml/'//n2:cos')
|
67
|
+
end
|
68
|
+
|
69
|
+
def delete(cos)
|
70
|
+
xml = invoke("n2:DeleteCosRequest") do |message|
|
71
|
+
Builder.delete(message, cos)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class Builder
|
76
|
+
class << self
|
77
|
+
def get_by_id(message, id)
|
78
|
+
message.add 'cos', id do |c|
|
79
|
+
c.set_attr 'by', 'id'
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def get_by_name(message, name)
|
84
|
+
message.add 'cos', name do |c|
|
85
|
+
c.set_attr "by", 'name'
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def create(message, name)
|
90
|
+
message.add 'name', name
|
91
|
+
end
|
92
|
+
|
93
|
+
def modify(message, cos)
|
94
|
+
message.add 'id', cos.id
|
95
|
+
modify_attributes(message, cos)
|
96
|
+
end
|
97
|
+
def modify_attributes(message, cos)
|
98
|
+
if cos.acls.empty?
|
99
|
+
ACL.delete_all(message)
|
100
|
+
else
|
101
|
+
cos.acls.each do |acl|
|
102
|
+
acl.apply(message)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def delete(message, cos)
|
108
|
+
message.add 'id', cos.id
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
class Parser
|
114
|
+
class << self
|
115
|
+
def cos_response(node)
|
116
|
+
id = (node/'@id').to_s
|
117
|
+
name = (node/'@name').to_s
|
118
|
+
acls = Zimbra::ACL.read(node)
|
119
|
+
Zimbra::Cos.new(id, name, acls)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,207 @@
|
|
1
|
+
module Zimbra
|
2
|
+
class DistributionList
|
3
|
+
class << self
|
4
|
+
def all
|
5
|
+
DistributionListService.all
|
6
|
+
end
|
7
|
+
|
8
|
+
def find_by_id(id)
|
9
|
+
DistributionListService.get_by_id(id)
|
10
|
+
end
|
11
|
+
|
12
|
+
def find_by_name(name)
|
13
|
+
DistributionListService.get_by_name(name)
|
14
|
+
end
|
15
|
+
|
16
|
+
def create(name)
|
17
|
+
DistributionListService.create(name)
|
18
|
+
end
|
19
|
+
|
20
|
+
def acl_name
|
21
|
+
'grp'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
attr_accessor :id, :name, :admin_console_ui_components, :admin_group, :members
|
26
|
+
|
27
|
+
def initialize(options = {})
|
28
|
+
options.each { |name, value| self.send("#{name}=", value) }
|
29
|
+
@original_members = self.members.dup
|
30
|
+
end
|
31
|
+
|
32
|
+
def admin_console_ui_components
|
33
|
+
@admin_console_ui_components ||= []
|
34
|
+
end
|
35
|
+
|
36
|
+
def members
|
37
|
+
@members ||= []
|
38
|
+
end
|
39
|
+
|
40
|
+
def new_members
|
41
|
+
self.members - @original_members
|
42
|
+
end
|
43
|
+
|
44
|
+
def removed_members
|
45
|
+
@original_members - self.members
|
46
|
+
end
|
47
|
+
|
48
|
+
def admin_group=(val)
|
49
|
+
@admin_group = Zimbra::Boolean.read(val)
|
50
|
+
end
|
51
|
+
def admin_group?
|
52
|
+
@admin_group
|
53
|
+
end
|
54
|
+
|
55
|
+
def delete
|
56
|
+
DistributionListService.delete(self)
|
57
|
+
end
|
58
|
+
|
59
|
+
def save
|
60
|
+
DistributionListService.modify(self)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class DistributionListService < HandsoapService
|
65
|
+
def all
|
66
|
+
xml = invoke("n2:GetAllDistributionListsRequest")
|
67
|
+
Parser.get_all_response(xml)
|
68
|
+
end
|
69
|
+
|
70
|
+
def get_by_id(id)
|
71
|
+
xml = invoke("n2:GetDistributionListRequest") do |message|
|
72
|
+
Builder.get_by_id(message, id)
|
73
|
+
end
|
74
|
+
return nil if soap_fault_not_found?
|
75
|
+
Parser.distribution_list_response(xml/'//n2:dl')
|
76
|
+
end
|
77
|
+
|
78
|
+
def get_by_name(name)
|
79
|
+
xml = invoke("n2:GetDistributionListRequest") do |message|
|
80
|
+
Builder.get_by_name(message, name)
|
81
|
+
end
|
82
|
+
return nil if soap_fault_not_found?
|
83
|
+
Parser.distribution_list_response(xml/'//n2:dl')
|
84
|
+
end
|
85
|
+
|
86
|
+
def create(name)
|
87
|
+
xml = invoke("n2:CreateDistributionListRequest") do |message|
|
88
|
+
Builder.create(message, name)
|
89
|
+
end
|
90
|
+
Parser.distribution_list_response(xml/'//n2:dl')
|
91
|
+
end
|
92
|
+
|
93
|
+
def modify(dist)
|
94
|
+
xml = invoke("n2:ModifyDistributionListRequest") do |message|
|
95
|
+
Builder.modify(message, dist)
|
96
|
+
end
|
97
|
+
Parser.distribution_list_response(xml/'//n2:dl')
|
98
|
+
|
99
|
+
modify_members(dist)
|
100
|
+
end
|
101
|
+
|
102
|
+
def modify_members(distribution_list)
|
103
|
+
distribution_list.new_members.each do |member|
|
104
|
+
add_member(distribution_list, member)
|
105
|
+
end
|
106
|
+
distribution_list.removed_members.each do |member|
|
107
|
+
remove_member(distribution_list, member)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def add_member(distribution_list, member)
|
112
|
+
xml = invoke("n2:AddDistributionListMemberRequest") do |message|
|
113
|
+
Builder.add_member(message, distribution_list.id, member)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def remove_member(distribution_list, member)
|
118
|
+
xml = invoke("n2:RemoveDistributionListMemberRequest") do |message|
|
119
|
+
Builder.remove_member(message, distribution_list.id, member)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def delete(dist)
|
124
|
+
xml = invoke("n2:DeleteDistributionListRequest") do |message|
|
125
|
+
Builder.delete(message, dist.id)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
module Builder
|
130
|
+
class << self
|
131
|
+
def create(message, name)
|
132
|
+
message.add 'name', name
|
133
|
+
end
|
134
|
+
|
135
|
+
def get_by_id(message, id)
|
136
|
+
message.add 'dl', id do |d|
|
137
|
+
d.set_attr 'by', 'id'
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def get_by_name(message, name)
|
142
|
+
message.add 'dl', name do |d|
|
143
|
+
d.set_attr "by", 'name'
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def modify(message, distribution_list)
|
148
|
+
message.add 'id', distribution_list.id
|
149
|
+
modify_attributes(message, distribution_list)
|
150
|
+
end
|
151
|
+
|
152
|
+
def modify_attributes(message, distribution_list)
|
153
|
+
modify_admin_console_ui_components(message, distribution_list)
|
154
|
+
modify_is_admin_group(message, distribution_list)
|
155
|
+
end
|
156
|
+
|
157
|
+
def modify_admin_console_ui_components(message, distribution_list)
|
158
|
+
if distribution_list.admin_console_ui_components.empty?
|
159
|
+
A.inject(message, 'zimbraAdminConsoleUIComponents', '')
|
160
|
+
else
|
161
|
+
distribution_list.admin_console_ui_components.each do |component|
|
162
|
+
A.inject(message, 'zimbraAdminConsoleUIComponents', component)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def modify_is_admin_group(message, distribution_list)
|
168
|
+
A.inject(message, 'zimbraIsAdminGroup', (distribution_list.admin_group? ? 'TRUE' : 'FALSE'))
|
169
|
+
end
|
170
|
+
|
171
|
+
def add_member(message, distribution_list_id, member)
|
172
|
+
message.add 'id', distribution_list_id
|
173
|
+
message.add 'dlm', member
|
174
|
+
end
|
175
|
+
|
176
|
+
def remove_member(message, distribution_list_id, member)
|
177
|
+
message.add 'id', distribution_list_id
|
178
|
+
message.add 'dlm', member
|
179
|
+
end
|
180
|
+
|
181
|
+
def delete(message, id)
|
182
|
+
message.add 'id', id
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
module Parser
|
187
|
+
class << self
|
188
|
+
def get_all_response(response)
|
189
|
+
items = response/"//n2:dl"
|
190
|
+
items.map { |i| distribution_list_response(i) }
|
191
|
+
end
|
192
|
+
|
193
|
+
def distribution_list_response(node)
|
194
|
+
id = (node/'@id').to_s
|
195
|
+
name = (node/'@name').to_s
|
196
|
+
ui_components = A.read(node, 'zimbraAdminConsoleUIComponents')
|
197
|
+
admin_group = A.read(node, 'zimbraIsAdminGroup')
|
198
|
+
members = (node/"//n2:dlm").map { |n| n.to_s }
|
199
|
+
|
200
|
+
Zimbra::DistributionList.new(:id => id, :name => name,
|
201
|
+
:admin_console_ui_components => ui_components, :admin_group => admin_group,
|
202
|
+
:members => members)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
module Zimbra
|
2
|
+
class Domain
|
3
|
+
class << self
|
4
|
+
def all
|
5
|
+
DomainService.all
|
6
|
+
end
|
7
|
+
|
8
|
+
def find_by_id(id)
|
9
|
+
DomainService.get_by_id(id)
|
10
|
+
end
|
11
|
+
|
12
|
+
def find_by_name(name)
|
13
|
+
DomainService.get_by_name(name)
|
14
|
+
end
|
15
|
+
|
16
|
+
def create(name, attributes = {})
|
17
|
+
DomainService.create(name, attributes)
|
18
|
+
end
|
19
|
+
|
20
|
+
def acl_name
|
21
|
+
'domain'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
attr_accessor :id, :name, :acls
|
26
|
+
|
27
|
+
def initialize(id, name, acls = [])
|
28
|
+
self.id = id
|
29
|
+
self.name = name
|
30
|
+
self.acls = acls || []
|
31
|
+
end
|
32
|
+
|
33
|
+
def save
|
34
|
+
DomainService.modify(self)
|
35
|
+
end
|
36
|
+
|
37
|
+
def delete
|
38
|
+
DomainService.delete(self)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class DomainService < HandsoapService
|
43
|
+
def all
|
44
|
+
xml = invoke("n2:GetAllDomainsRequest")
|
45
|
+
Parser.get_all_response(xml)
|
46
|
+
end
|
47
|
+
|
48
|
+
def create(name, attributes = {})
|
49
|
+
xml = invoke("n2:CreateDomainRequest") do |message|
|
50
|
+
Builder.create(message, name)
|
51
|
+
end
|
52
|
+
Parser.domain_response(xml/"//n2:domain")
|
53
|
+
end
|
54
|
+
|
55
|
+
def get_by_id(id)
|
56
|
+
xml = invoke("n2:GetDomainRequest") do |message|
|
57
|
+
Builder.get_by_id(message, id)
|
58
|
+
end
|
59
|
+
return nil if soap_fault_not_found?
|
60
|
+
Parser.domain_response(xml/"//n2:domain")
|
61
|
+
end
|
62
|
+
|
63
|
+
def get_by_name(name)
|
64
|
+
xml = invoke("n2:GetDomainRequest") do |message|
|
65
|
+
Builder.get_by_name(message, name)
|
66
|
+
end
|
67
|
+
return nil if soap_fault_not_found?
|
68
|
+
Parser.domain_response(xml/"//n2:domain")
|
69
|
+
end
|
70
|
+
|
71
|
+
def modify(domain)
|
72
|
+
xml = invoke("n2:ModifyDomainRequest") do |message|
|
73
|
+
Builder.modify(message, domain)
|
74
|
+
end
|
75
|
+
Parser.domain_response(xml/'//n2:domain')
|
76
|
+
end
|
77
|
+
|
78
|
+
def delete(dist)
|
79
|
+
xml = invoke("n2:DeleteDomainRequest") do |message|
|
80
|
+
Builder.delete(message, dist.id)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
class Builder
|
85
|
+
class << self
|
86
|
+
def create(message, name)
|
87
|
+
message.add 'name', name
|
88
|
+
end
|
89
|
+
|
90
|
+
def get_by_id(message, id)
|
91
|
+
message.add 'domain', id do |c|
|
92
|
+
c.set_attr 'by', 'id'
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def get_by_name(message, name)
|
97
|
+
message.add 'domain', name do |c|
|
98
|
+
c.set_attr 'by', 'name'
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def modify(message, domain)
|
103
|
+
message.add 'id', domain.id
|
104
|
+
modify_attributes(message, domain)
|
105
|
+
end
|
106
|
+
def modify_attributes(message, domain)
|
107
|
+
if domain.acls.empty?
|
108
|
+
ACL.delete_all(message)
|
109
|
+
else
|
110
|
+
domain.acls.each do |acl|
|
111
|
+
acl.apply(message)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def delete(message, id)
|
117
|
+
message.add 'id', id
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
class Parser
|
122
|
+
class << self
|
123
|
+
def get_all_response(response)
|
124
|
+
(response/"//n2:domain").map do |node|
|
125
|
+
domain_response(node)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def domain_response(node)
|
130
|
+
id = (node/'@id').to_s
|
131
|
+
name = (node/'@name').to_s
|
132
|
+
acls = Zimbra::ACL.read(node)
|
133
|
+
Zimbra::Domain.new(id, name, acls)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'handsoap'
|
2
|
+
|
3
|
+
module Zimbra
|
4
|
+
module HandsoapErrors
|
5
|
+
class SOAPFault < StandardError; end
|
6
|
+
|
7
|
+
@@response = nil
|
8
|
+
|
9
|
+
def on_http_error(response)
|
10
|
+
@@response = response
|
11
|
+
return nil if soap_fault_not_found?
|
12
|
+
report_error(response) if http_error?
|
13
|
+
end
|
14
|
+
def report_error(response)
|
15
|
+
message = response.body.scan(/<soap:faultstring>(.*)<\/soap:faultstring>/).first
|
16
|
+
raise SOAPFault, message
|
17
|
+
end
|
18
|
+
def on_after_create_http_request(request)
|
19
|
+
@@response = nil
|
20
|
+
end
|
21
|
+
|
22
|
+
def soap_fault_not_found?
|
23
|
+
@@response && @@response.body =~ /no such/
|
24
|
+
end
|
25
|
+
def http_error?
|
26
|
+
@@response && (500..599).include?(@@response.status)
|
27
|
+
end
|
28
|
+
def http_not_found?
|
29
|
+
@@response && (400..499).include?(@@response.status)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
module HandsoapNamespaces
|
34
|
+
def request_namespaces(doc)
|
35
|
+
doc.alias 'n1', "urn:zimbra"
|
36
|
+
doc.alias 'n2', "urn:zimbraAdmin"
|
37
|
+
doc.alias 'env', 'http://schemas.xmlsoap.org/soap/envelope/'
|
38
|
+
end
|
39
|
+
def response_namespaces(doc)
|
40
|
+
doc.add_namespace 'n2', "urn:zimbraAdmin"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
module HandsoapUriOverrides
|
45
|
+
def uri
|
46
|
+
Zimbra.url
|
47
|
+
end
|
48
|
+
def envelope_namespace
|
49
|
+
'http://www.w3.org/2003/05/soap-envelope'
|
50
|
+
end
|
51
|
+
def request_content_type
|
52
|
+
"application/soap+xml"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class HandsoapService < Handsoap::Service
|
57
|
+
include HandsoapErrors
|
58
|
+
include HandsoapNamespaces
|
59
|
+
extend HandsoapUriOverrides
|
60
|
+
|
61
|
+
def on_create_document(doc)
|
62
|
+
request_namespaces(doc)
|
63
|
+
header = doc.find("Header")
|
64
|
+
header.add "n1:context" do |s|
|
65
|
+
s.set_attr "env:mustUnderstand", "0"
|
66
|
+
s.add "n1:authToken", Zimbra.auth_token
|
67
|
+
end
|
68
|
+
end
|
69
|
+
def on_response_document(doc)
|
70
|
+
response_namespaces(doc)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
metadata
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: zimbra
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 23
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 4
|
10
|
+
version: 0.0.4
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Derek Kastner
|
14
|
+
- Matt Wilson
|
15
|
+
autorequire:
|
16
|
+
bindir: bin
|
17
|
+
cert_chain: []
|
18
|
+
|
19
|
+
date: 2009-11-18 00:00:00 -05:00
|
20
|
+
default_executable:
|
21
|
+
dependencies:
|
22
|
+
- !ruby/object:Gem::Dependency
|
23
|
+
name: rspec
|
24
|
+
prerelease: false
|
25
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ">="
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
hash: 3
|
31
|
+
segments:
|
32
|
+
- 0
|
33
|
+
version: "0"
|
34
|
+
type: :development
|
35
|
+
version_requirements: *id001
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: handsoap
|
38
|
+
prerelease: false
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
hash: 3
|
45
|
+
segments:
|
46
|
+
- 0
|
47
|
+
version: "0"
|
48
|
+
type: :runtime
|
49
|
+
version_requirements: *id002
|
50
|
+
description: Interface to Zimbra management API
|
51
|
+
email: derek@vedit.com mwilson@vedit.com
|
52
|
+
executables: []
|
53
|
+
|
54
|
+
extensions: []
|
55
|
+
|
56
|
+
extra_rdoc_files: []
|
57
|
+
|
58
|
+
files:
|
59
|
+
- README
|
60
|
+
- lib/zimbra/account.rb
|
61
|
+
- lib/zimbra/acl.rb
|
62
|
+
- lib/zimbra/auth.rb
|
63
|
+
- lib/zimbra/common_elements.rb
|
64
|
+
- lib/zimbra/cos.rb
|
65
|
+
- lib/zimbra/distribution_list.rb
|
66
|
+
- lib/zimbra/domain.rb
|
67
|
+
- lib/zimbra/handsoap_service.rb
|
68
|
+
- lib/zimbra.rb
|
69
|
+
has_rdoc: true
|
70
|
+
homepage:
|
71
|
+
licenses: []
|
72
|
+
|
73
|
+
post_install_message:
|
74
|
+
rdoc_options: []
|
75
|
+
|
76
|
+
require_paths:
|
77
|
+
- lib
|
78
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
79
|
+
none: false
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
hash: 3
|
84
|
+
segments:
|
85
|
+
- 0
|
86
|
+
version: "0"
|
87
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
88
|
+
none: false
|
89
|
+
requirements:
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
hash: 3
|
93
|
+
segments:
|
94
|
+
- 0
|
95
|
+
version: "0"
|
96
|
+
requirements: []
|
97
|
+
|
98
|
+
rubyforge_project:
|
99
|
+
rubygems_version: 1.3.7
|
100
|
+
signing_key:
|
101
|
+
specification_version: 2
|
102
|
+
summary: SOAP Interface to Zimbra
|
103
|
+
test_files: []
|
104
|
+
|