zimbra 0.0.4
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.
- 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
|
+
|