zmldifexport 0.2.2

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.
@@ -0,0 +1,296 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+
3
+ <objectclasses group="ZimbraObjectClass" groupid="2">
4
+
5
+ <objectclass id="1" name="zimbraAccount" type="AUXILIARY">
6
+ <sup>zimbraMailRecipient</sup>
7
+ <desc>Account object</desc>
8
+ <comment>
9
+ zimbraAccount extends zimbraMailRecipient. It represents a real
10
+ account in the system (either admin or end-user) that can be logged
11
+ into, etc.
12
+
13
+ It is defined as AUXILIARY in case it needs to be mixed to an
14
+ existing directory deployment.
15
+
16
+ cn - full name, common name
17
+ co - country friendly name
18
+ company - company (company name)
19
+ displayName - name to display in admin tool, outlook uses as well
20
+ (cn is multi-valued)
21
+ gn - first name (given name)
22
+ initials - middle initial
23
+ l - city (locality)
24
+ ou - organizational unit
25
+ physicalDeliveryOfficeName - office
26
+ street - street address
27
+ postalCode - zip code
28
+ sn - last name (sir name)
29
+ st - state
30
+ telephoneNumber - phone
31
+ </comment>
32
+ </objectclass>
33
+
34
+ <objectclass id="3" name="zimbraCOS" type="STRUCTURAL">
35
+ <sup>top</sup>
36
+ <desc>Class of Service data</desc>
37
+ <comment>
38
+ zimbraCOS is the class of service object. it should have attributes
39
+ that are used as default values for accounts that belong to the COS
40
+ and do not have the same attribute defined in the account directory
41
+ entry.
42
+ </comment>
43
+ </objectclass>
44
+
45
+ <objectclass id="4" name="zimbraDomain" type="AUXILIARY">
46
+ <sup>zimbraMailRecipient</sup>
47
+ <desc>Domain object</desc>
48
+ <comment>
49
+ zimbraDomain is used to represent a domain in the directory. For
50
+ example, if we created the foo.com domain, then the dc=foo,dc=com
51
+ entry in LDAP would have an objectclass of zimbraDomain.
52
+ domain-specific configuration information (if so-needed) could be
53
+ added here.
54
+ </comment>
55
+ </objectclass>
56
+
57
+ <objectclass id="5" name="zimbraSecurityGroup" type="AUXILIARY">
58
+ <sup>top</sup>
59
+ <desc>Security Group</desc>
60
+ <comment>
61
+ zimbraSecurityGroup is used to represent a security group in the
62
+ directory. Members of this group have a zimbraMemberOf attr with
63
+ this group's zimbraId. If this group is a member of other groups,
64
+ then it will have those group's zimbraIds in its own zimbraMemberOf
65
+ list.
66
+ </comment>
67
+ </objectclass>
68
+
69
+ <objectclass id="6" name="zimbraDistributionList" type="STRUCTURAL">
70
+ <sup>top</sup>
71
+ <desc>Distribution List object</desc>
72
+ <comment>
73
+ zimbraDistributionList represents a distribution/mailing list. It
74
+ inherits from zimbraMailRecipient. Members (who can be
75
+ internal/external) are represented as zimbraMailForwardingAddress
76
+ attrs.
77
+ </comment>
78
+ </objectclass>
79
+
80
+ <objectclass id="7" name="zimbraMailRecipient" type="AUXILIARY">
81
+ <sup>top</sup>
82
+ <desc>Mail recipient object</desc>
83
+ <comment>
84
+ zimbraMailRecipient is used to represent entries in the directory
85
+ that can receive mail. i.e., they have a visible external address, that
86
+ gets expanded into one or more internal/external addresses.
87
+ </comment>
88
+ </objectclass>
89
+
90
+ <objectclass id="8" name="zimbraServer" type="STRUCTURAL">
91
+ <sup>top</sup>
92
+ <desc>Server in the cluster</desc>
93
+ <comment>
94
+ zimbraServer is used to represent a defined server within a zimbra
95
+ install. server-specific configuration information will be added
96
+ here. This includes information like which services should be
97
+ running on a server, whether or not the server is normally a
98
+ master/slave, etc.
99
+ </comment>
100
+ </objectclass>
101
+
102
+ <objectclass id="9" name="zimbraGlobalConfig" type="AUXILIARY">
103
+ <sup>top</sup>
104
+ <desc>global config</desc>
105
+ <comment>
106
+ zimbraGlobalConfig is the entry that holds all the global
107
+ configuration attrs.
108
+ </comment>
109
+ </objectclass>
110
+
111
+ <objectclass id="11" name="zimbraAlias" type="STRUCTURAL">
112
+ <sup>top</sup>
113
+ <desc>An alias to another zimbra object</desc>
114
+ <comment>
115
+ zimbraAlias is used to privision aliases
116
+ </comment>
117
+ </objectclass>
118
+
119
+ <objectclass id="12" name="zimbraMimeEntry" type="STRUCTURAL">
120
+ <sup>top</sup>
121
+ <desc>MIME type info</desc>
122
+ <comment>
123
+ zimbraMimeEntry is used to represent status about mime types
124
+ </comment>
125
+ </objectclass>
126
+
127
+ <objectclass id="13" name="zimbraObjectEntry" type="STRUCTURAL">
128
+ <sup>top</sup>
129
+ <desc>Object type info</desc>
130
+ <comment>
131
+ zimbraObjectEntry is used to represent status about object types
132
+ </comment>
133
+ </objectclass>
134
+
135
+ <objectclass id="14" name="zimbraTimeZone" type="STRUCTURAL">
136
+ <sup>top</sup>
137
+ <desc>Time Zone info</desc>
138
+ <comment>
139
+ zimbraTimeZone is used to define a timezone with daylight savings
140
+ time rules. Used in calendar.
141
+ </comment>
142
+ </objectclass>
143
+
144
+ <objectclass id="15" name="zimbraZimletEntry" type="STRUCTURAL">
145
+ <sup>top</sup>
146
+ <desc>Zimlet info</desc>
147
+ <comment>
148
+ zimbraZimletEntry is used to represent Zimlets
149
+ </comment>
150
+ </objectclass>
151
+
152
+ <objectclass id="16" name="zimbraCalendarResource" type="AUXILIARY">
153
+ <sup>top</sup>
154
+ <desc>Calendar resource object</desc>
155
+ <comment>
156
+ zimbraCalendarResource is used to represent a calendar resource This
157
+ objectclass should be mixed in to a zimbraAccount entry.
158
+ </comment>
159
+ </objectclass>
160
+
161
+ <objectclass id="17" name="zimbraIdentity" type="STRUCTURAL">
162
+ <sup>top</sup>
163
+ <desc>Account Email Identity information</desc>
164
+ <comment>
165
+ zimbraIdentity is the account identity object, which stores information about
166
+ Email "identities"
167
+ </comment>
168
+ </objectclass>
169
+
170
+ <objectclass id="18" name="zimbraDataSource" type="STRUCTURAL">
171
+ <sup>top</sup>
172
+ <desc>base class for data source objects</desc>
173
+ <comment>
174
+ zimbraDataSource is the base class for data source objects
175
+ </comment>
176
+ </objectclass>
177
+
178
+ <objectclass id="19" name="zimbraPop3DataSource" type="STRUCTURAL">
179
+ <sup>zimbraDataSource</sup>
180
+ <desc>POP3 data source object</desc>
181
+ <comment>
182
+ zimbraPop3DataSource is the base class for POP3 data source objects
183
+ </comment>
184
+ </objectclass>
185
+
186
+ <objectclass id="20" name="zimbraImapDataSource" type="STRUCTURAL">
187
+ <sup>zimbraDataSource</sup>
188
+ <desc>IMAP data source object</desc>
189
+ <comment>
190
+ zimbraImapDataSource is the base class for IMAP data source objects
191
+ </comment>
192
+ </objectclass>
193
+
194
+ <objectclass id="21" name="zimbraSignature" type="STRUCTURAL">
195
+ <sup>top</sup>
196
+ <desc>Account Email Signature information</desc>
197
+ <comment>
198
+ zimbraSignature is the account Signature object, which stores information about
199
+ Email "signatures"
200
+ </comment>
201
+ </objectclass>
202
+
203
+ <objectclass id="22" name="zimbraXMPPComponent" type="STRUCTURAL">
204
+ <sup>top</sup>
205
+ <desc>XMPP Component Configuration Record</desc>
206
+ <comment>
207
+ zimbraXMPPComponent defines information about routable XMPP services such as conference
208
+ rooms, file transfer servies, etc
209
+ </comment>
210
+ </objectclass>
211
+
212
+ <objectclass id="23" name="zimbraAclTarget" type="STRUCTURAL">
213
+ <sup>top</sup>
214
+ <desc>acl target</desc>
215
+ <comment>
216
+ target entries on which rights(acl) can be granted
217
+ </comment>
218
+ </objectclass>
219
+
220
+ <objectclass id="24" name="zimbraRssDataSource" type="STRUCTURAL">
221
+ <sup>zimbraDataSource</sup>
222
+ <desc>RSS data source object</desc>
223
+ <comment>
224
+ Represents an RSS data source.
225
+ </comment>
226
+ </objectclass>
227
+
228
+ <objectclass id="25" name="zimbraLiveDataSource" type="STRUCTURAL">
229
+ <sup>zimbraDataSource</sup>
230
+ <desc>Hotmail data source object</desc>
231
+ <comment>
232
+ Represents a Hotmail data source.
233
+ </comment>
234
+ </objectclass>
235
+
236
+ <objectclass id="26" name="zimbraGalDataSource" type="STRUCTURAL">
237
+ <sup>zimbraDataSource</sup>
238
+ <desc>GAL data source object</desc>
239
+ <comment>
240
+ Represents a GAL data source.
241
+ </comment>
242
+ </objectclass>
243
+
244
+ <objectclass id="27" name="zimbraGroup" type="AUXILIARY">
245
+ <sup>top</sup>
246
+ <desc>hold attributes for dynamic groups</desc>
247
+ <comment>
248
+ hold attributes for dynamic groups
249
+ </comment>
250
+ </objectclass>
251
+
252
+ <objectclass id="28" name="zimbraGroupDynamicUnit" type="AUXILIARY">
253
+ <sup>top</sup>
254
+ <desc>hold attributes for the dynamic entry of dynamic groups.</desc>
255
+ <comment>
256
+ Dynamic entry of a dynamic group is for members provisioned on the system.
257
+ </comment>
258
+ </objectclass>
259
+
260
+ <objectclass id="29" name="zimbraGroupStaticUnit" type="STRUCTURAL">
261
+ <sup>top</sup>
262
+ <desc>hold attributes for the static entry of dynamic groups.</desc>
263
+ <comment>
264
+ Static entry of a dynamic group is for members not provisioned on the system.
265
+ </comment>
266
+ </objectclass>
267
+
268
+ <objectclass id="30" name="zimbraShareLocator" type="STRUCTURAL">
269
+ <sup>top</sup>
270
+ <desc>Share locator</desc>
271
+ <comment>
272
+ A share locator points to the current location of the share. This
273
+ allows shared data to be relocated transparently without requiring
274
+ the share users to update their mountpoint definition.
275
+ </comment>
276
+ </objectclass>
277
+
278
+ <objectclass id="31" name="zimbraUCService" type="STRUCTURAL">
279
+ <sup>top</sup>
280
+ <desc>Unified Communication service in the cluster</desc>
281
+ <comment>
282
+ zimbraUCService is used to represent a third party UC service.
283
+ This includes information like URLs to the UC providers service and
284
+ Zimbra extensions to load for the service, etc.
285
+ </comment>
286
+ </objectclass>
287
+
288
+ <objectclass id="32" name="zimbraAlwaysOnCluster" type="STRUCTURAL">
289
+ <sup>top</sup>
290
+ <desc>AlwaysOn Cluster</desc>
291
+ <comment>
292
+ zimbraAlwaysOnCluster is used to represent a alwaysOn cluster.
293
+ </comment>
294
+ </objectclass>
295
+
296
+ </objectclasses>
@@ -0,0 +1,487 @@
1
+ require 'open-uri'
2
+ require 'nokogiri'
3
+ require 'net-ldap'
4
+
5
+ module ZmLdifExport
6
+
7
+ class << self
8
+ @options = nil
9
+ @file_path = nil
10
+
11
+ def ldif_file
12
+ begin
13
+ open @file_path
14
+ rescue Errno::ENOENT => _
15
+ puts "File #{@file_path} not found"
16
+ end
17
+ end
18
+
19
+ # Load the LDIF in Memory
20
+ def ldif_data
21
+ ldif_data = Net::LDAP::Dataset.read_ldif ldif_file
22
+ puts "File #{@file_path} not a valid LDIF file" if ldif_data.empty?
23
+ ldif_data
24
+ end
25
+
26
+ def parse_ldif
27
+ ldif_data.to_entries.each do |entry|
28
+ Domain.add Domain.new(entry) if entry.objectclass.include?(OCLASS_DOMAIN)
29
+ DistributionList.add DistributionList.new(entry) if entry.objectclass.include?(OCLASS_DL)
30
+ Account.add Account.new(entry) if valid_account_entry?(entry)
31
+ end
32
+ end
33
+
34
+ def valid_account_entry?(entry)
35
+ entry.objectclass.include?(OCLASS_ACCOUNT) && entry.dn =~ /(#{domain_to_ldif_dc}$|uid=admin|uid=spam|uid=virus|uid=nospam)/
36
+ end
37
+
38
+ # Entry point do all
39
+ def run(options)
40
+ @options = options
41
+ @file_path = @options[:ldif_file]
42
+ @domain = @options[:domain]
43
+ @command = @options[:command]
44
+
45
+ parse_ldif
46
+ self.send(@command)
47
+ end
48
+
49
+ def exportAccounts
50
+ puts "export LC_ALL=en_US.UTF-8"
51
+ puts "export LANG=en_US.UTF-8"
52
+ Account.all.each do |a|
53
+ next if a.name.first =~ /(admin|spam|ham|galsync|virus|wiki)/
54
+ puts "# zmprov ca #{a.name(:zmprov, @domain)}"
55
+ puts a.zmprov_ca(domain: @domain)
56
+ puts ""
57
+ end
58
+ end
59
+
60
+ def exportAliases
61
+ Account.all.each do |a|
62
+ next unless a.has_alias?
63
+ puts "# zmprov aaa #{a.name(:zmprov, @domain)}"
64
+ puts a.zmprov_aaa(domain: @domain)
65
+ puts ""
66
+ end
67
+ end
68
+
69
+ def exportDls
70
+ puts "export LC_ALL=en_US.UTF-8"
71
+ puts "export LANG=en_US.UTF-8"
72
+ DistributionList.all.each do |dl|
73
+ next if dl.name(:zmprov) =~ /archive/i
74
+ puts "# Create DistributionList #{dl.name(:zmprov, @domain)}"
75
+ puts dl.zmprov_cdl domain: "@#{@domain}"
76
+ puts ""
77
+ puts "# Add members to DistributionList #{dl.name(:zmprov, @domain)}"
78
+ puts dl.zmprov_adlm domain: "@#{@domain}"
79
+ puts ""
80
+ puts "# Add Alias to DistributionList #{dl.name(:zmprov, @domain)}"
81
+ puts dl.zmprov_adla domain: "@#{@domain}"
82
+ puts ""
83
+ end
84
+ end
85
+
86
+ def domain_to_ldif_dc
87
+ "dc=#{@domain.split('.').join(',dc=')}"
88
+ end
89
+
90
+ # Build the resulting command
91
+ # @param zm_cmd [String] `zmprov` or `zmmailbox`
92
+ # @param command [String] command to excute, like `ca` or `createDomain`
93
+ # @param attributes [Array] Array of attributes to pass to the command
94
+ # @param full [Boolean] If print `zm_cmd` or just the `command`
95
+ # @returns [String]
96
+ def mk_command(zm_cmd: 'zmprov', command:, resource:, attributes: {}, full: true)
97
+ comment = "# #{zm_cmd} #{command} #{resource}"
98
+ command = full ? "#{zm_cmd} #{command} #{resource}" : "#{command} #{resource}"
99
+ command = command + " \\" if attributes.keys.any?
100
+ attributes = attrs_to_cmd(attributes)
101
+
102
+ [comment, command, attributes].compact.join("\n")
103
+ end
104
+
105
+ # Return an array of attributes
106
+ def attrs_to_cmd(attributes = {})
107
+ lines = []
108
+ attributes.each do |k,v|
109
+ values = [v].flatten.compact
110
+ values.each do |value|
111
+ lines.push "#{k} #{value} \\"
112
+ end
113
+ end
114
+ return nil if lines.empty?
115
+ lines.last.gsub!(' \\', '')
116
+ return lines
117
+ end
118
+
119
+ end
120
+
121
+ # CONSTANTS
122
+ ZIMBRA_ATTRS_FILE = './data/zimbra-attrs.xml'.freeze
123
+ LDIF_FILE = ARGV[0]
124
+ OCLASS_DOMAIN = 'zimbraDomain'
125
+ OCLASS_ACCOUNT = 'zimbraAccount'
126
+ OCLASS_DL = 'zimbraDistributionList'
127
+
128
+ # LDIF_DATA = Net::LDAP::Dataset.read_ldif(open(LDIF_FILE)).to_entries
129
+ @domains = nil
130
+ @accounts = nil
131
+ @aliases = nil
132
+
133
+ class Attribute
134
+ attr_reader :name, :type
135
+
136
+ WHITE_LIST = ['zimbraId', 'zimbraMailAlias'].freeze
137
+
138
+ def initialize(xml_attr)
139
+ @name = xml_attr.attributes['name'].value
140
+ @singular = attr_is_singular?(xml_attr)
141
+ @immutable = attr_is_immutable?(xml_attr)
142
+ @type = xml_attr.attributes['type'].value
143
+ end
144
+
145
+ def singular?
146
+ @singular
147
+ end
148
+
149
+ def immutable?
150
+ @immutable
151
+ end
152
+
153
+ def multiline?
154
+ name == 'zimbraPrefMailSignatureHTML' || name == 'zimbraMailSieveScript' || name == 'zimbraPrefOutOfOfficeReply' || name == 'zimbraPrefOutOfOfficeExternalReply'
155
+ end
156
+
157
+ def text?
158
+ type == 'string' || type == 'duration' || name == 'zimbraACE' || type == 'regex'
159
+ end
160
+
161
+ def number?
162
+ type == 'integer' || type == 'long'
163
+ end
164
+
165
+ def enum?
166
+ type == 'enum'
167
+ end
168
+
169
+ def boolean?
170
+ type == "boolean"
171
+ end
172
+
173
+ def to_zmprov(value, plus = false)
174
+ if multiline?
175
+ command_multiline attr_name: name, value: value
176
+ elsif singular?
177
+ return " \\\n#{name} '#{value.join('').to_s}'" if text?
178
+ return " \\\n#{name} #{value.join('').to_i}" if number?
179
+ return " \\\n#{name} #{value.join('').to_s}"
180
+ else
181
+ line = " \\\n#{name} "
182
+ value = text? ? value.map {|v| "'#{v}'"} : value
183
+ if plus
184
+ line << value.join(" +#{name} ")
185
+ else
186
+ line << value.join(" \\\n#{name} ")
187
+ end
188
+
189
+ line
190
+ end
191
+ end
192
+
193
+ def command_multiline(attr_name:,value:)
194
+ final_wrapper = attr_name == 'zimbraMailSieveScript' ? "'" : '"'
195
+ original_wrapper = attr_name == 'zimbraMailSieveScript' ? '"' : "'"
196
+ line = "#{attr_name} #{final_wrapper}"
197
+ line << value.join('').to_s.gsub(final_wrapper, original_wrapper).split(/\n/).join("\n")
198
+ line << "#{final_wrapper} \\\n"
199
+ line
200
+ end
201
+
202
+ private
203
+
204
+ def attr_is_singular?(attr)
205
+ return false if attr.attributes['cardinality'].nil?
206
+ return true if attr.attributes['type'] == 'astring'
207
+ attr.attributes['cardinality'].value == 'single'
208
+ end
209
+
210
+ def attr_is_immutable?(attr)
211
+ return false if WHITE_LIST.include?(name)
212
+ return false if attr.attributes['immutable'].nil?
213
+ attr.attributes['immutable'].value == '1'
214
+ end
215
+ end
216
+
217
+ class ZimbraResource
218
+
219
+ BLACK_LIST_ATTRS = [
220
+ "dn", "objectclass", "o", "dc", "structuralobjectclass", "entryuuid", "creatorsname",
221
+ "createtimestamp", "entrycsn", "modifiersname", "modifytimestamp", "uid",
222
+ "zimbraaggregatequotalastusage", "zimbradomainstatus", "zimbramailstatus", "zimbraauthtokens",
223
+ "amavisarchivequarantineto", 'zimbramailhost', 'zimbramailtransport', 'zimbramailquota',
224
+ 'zimbraaccountstatus', 'zimbraarchiveaccount', 'zimbraarchiveenabled', 'zimbraauthtokenvalidityvalue',
225
+ 'zimbraPasswordModifiedTime', 'zimbrapreftimezoneid', 'zimbraquotalastwarntime', 'zimbracsrftokendata',
226
+ 'zimbraprefpop3downloadsince', 'zimbrapasswordmodifiedtime', 'zimbracosid',
227
+ 'zimbrapreflistviewcolumns', 'zimbraisadminaccount', 'zimbraisadminaccount', 'zimbraAdminSavedSearches',
228
+ 'zimbraisadmingroup', 'zimbraadminconsoleuicomponents'
229
+ ]
230
+
231
+ attr_reader :attributes, :name, :real_attrs
232
+
233
+ @immutable_attrs = nil
234
+ @attrs_xml = nil
235
+
236
+ def self.attrs_xml
237
+ return @attrs_xml if @attrs_xml
238
+ xml_doc = Nokogiri::XML(open(ZIMBRA_ATTRS_FILE))
239
+ @attrs_xml = xml_doc.xpath('//attr')
240
+ return @attrs_xml
241
+ end
242
+
243
+ def zimbra_attrs
244
+ return @zimbra_attrs if @zimbra_attrs
245
+ @zimbra_attrs = {}
246
+ ZimbraResource.attrs_xml.each do |attr|
247
+ zm_attr = Attribute.new(attr)
248
+ @zimbra_attrs[zm_attr.name.downcase] = zm_attr
249
+ end
250
+ return @zimbra_attrs
251
+ end
252
+
253
+ def name(mode = nil, domain = '')
254
+ return @name if mode.nil?
255
+ return "#{@name.join('').to_s}@#{domain}" if mode == :zmprov
256
+ end
257
+
258
+ def id
259
+ attributes['zimbraid'].first.to_s
260
+ end
261
+
262
+ def parse_attributes(ldif:, exclude: [])
263
+ attrs = {}
264
+ ldif.each_attribute do |a|
265
+ value = ldif[a]
266
+ next if invalid_attribute?(a.to_s) || exclude.include?(a) || invalid_value?(value)
267
+ attrs[a.to_s] = value
268
+ end
269
+ attrs
270
+ end
271
+
272
+ def invalid_value?(value)
273
+ return true if value.join('').to_s =~ /null/
274
+ end
275
+
276
+ def invalid_attribute?(attr_name)
277
+ return true if BLACK_LIST_ATTRS.include?(attr_name)
278
+ zm_attr = zimbra_attrs[attr_name]
279
+ zm_attr.nil? ? false : zm_attr.immutable?
280
+ end
281
+
282
+ def to_zmprov(command: 'ca')
283
+ zmprov_ca if command == 'ca'
284
+ zmprov_aaa if command == 'aaa'
285
+ zmprov_cdl if command == 'cdl'
286
+ zmprov_adlm if command == 'adlm'
287
+ zmprov_adla if command == 'adla'
288
+ end
289
+
290
+ def attr_to_zmprov(attr_name:, value:, plus: false)
291
+ zm_attr = zimbra_attrs[attr_name]
292
+ zm_attr.to_zmprov(value, plus)
293
+ end
294
+ end
295
+
296
+ class Domain < ZimbraResource
297
+ @resources = []
298
+
299
+ def self.all
300
+ return @resources
301
+ end
302
+
303
+ def self.add(resource)
304
+ @resources.push resource
305
+ end
306
+
307
+ def initialize(ldif)
308
+ @name = ldif[:zimbradomainname]
309
+ @attributes = parse_attributes(ldif: ldif, exclude: [:zimbradomainname])
310
+ end
311
+
312
+ def zmprov_cd
313
+ line = ['cd', name]
314
+ attributes.each do |name, value|
315
+ line << attr_to_zmprov(attr_name: name, value: value)
316
+ end
317
+ line.join(' ')
318
+ end
319
+
320
+ end
321
+
322
+ class Account < ZimbraResource
323
+ @resources = []
324
+ attr_accessor :ldif
325
+
326
+ def self.all
327
+ return @resources
328
+ end
329
+
330
+ def self.add(resource)
331
+ @resources.push resource
332
+ end
333
+
334
+ def has_alias?
335
+ attributes['zimbramailalias'] ? attributes['zimbramailalias'].any? : false
336
+ end
337
+
338
+ def initialize(ldif)
339
+ @name = ldif[:uid]
340
+ @ldif = ldif
341
+ @attributes = parse_attributes(ldif: ldif, exclude: [:uid])
342
+ end
343
+
344
+
345
+ def zmprov_ca(domain: '', mail_host: nil, skip: [], print: false)
346
+ line = [
347
+ '/opt/zimbra/bin/zmprov ca',
348
+ name(:zmprov, domain),
349
+ 'P4ssW04r..,. '
350
+ ]
351
+ line.push "zimbraMailHost #{mail_host} " unless mail_host.nil?
352
+ attributes.each do |name, value|
353
+ next if name =~ /(zimbramailalias|admin|zimbracosid|zimbraace|zimbrasharedItem|signature|zimbramailsievescript)/i
354
+ next if skip.include?(name)
355
+ line << attr_to_zmprov(attr_name: name, value: value)
356
+ end
357
+ cmd = line.join(' ')
358
+ puts cmd if print
359
+ return cmd
360
+ end
361
+
362
+ def zmprov_aaa(print: false, domain:)
363
+ aliases = attributes['zimbramailalias']
364
+ return if aliases.nil? || aliases.empty?
365
+ lines = []
366
+ aliases.each do |mail|
367
+ lines.push("aaa #{name(:zmprov, domain)} #{mail}")
368
+ end
369
+
370
+ cmd = lines.join("\n")
371
+ puts cmd if print
372
+ return cmd
373
+ end
374
+
375
+ def ace_to_json(zm_attr = '')
376
+ ace = {}
377
+ keys_and_values = zm_attr.split(';')
378
+ keys_and_values.each do |kv|
379
+ key, value = kv.split(':')
380
+ ace[key] = kv[value]
381
+ end
382
+ return ace
383
+ end
384
+
385
+ def aces(zma)
386
+ aces = []
387
+ zma.each do |zm|
388
+ aces.push(ace_to_json(zm))
389
+ end
390
+
391
+ return aces
392
+ end
393
+
394
+ def zmprov_sharing(print: false, domain:)
395
+ return nil if attributes['zimbrashareditem'].nil?
396
+ line = []
397
+ zimbra_shares = aces(attributes['zimbrashareditem'])
398
+ zimbra_shares.each do |ace|
399
+ next if ace['granteeName'] == 'null'
400
+ line.push("zmmailbox -z -m #{name(:zmprov, domain)} mfg '#{ace['folderPath']}' account #{ace['granteeName']} #{ace['rights']}")
401
+ end
402
+
403
+ cmd = line.size > 0 ? line.join("\n") + "\n" : nil
404
+ puts cmd if print
405
+ return cmd
406
+ end
407
+
408
+ def zmprov_signatures(print: false, domain:)
409
+ return nil if attributes['zimbraprefmailsignaturehtml'].nil?
410
+ line = []
411
+ zimbra_shares = attributes['zimbraprefmailsignaturehtml']
412
+ zimbra_shares.each do |ace|
413
+ ace = ace.gsub(/'/, '"')
414
+ line.push("ma #{name(:zmprov, domain)} zimbraPrefMailSignatureHtml '#{ace}'")
415
+ end
416
+
417
+ cmd = line.size > 0 ? line.join("\n") + "\n" : nil
418
+ puts cmd if print
419
+ return cmd
420
+ end
421
+
422
+ end
423
+
424
+ class DistributionList < ZimbraResource
425
+ @resources = []
426
+
427
+ def self.all
428
+ return @resources
429
+ end
430
+
431
+ def self.add(resource)
432
+ @resources.push resource
433
+ end
434
+
435
+ def initialize(ldif)
436
+ @name = ldif[:uid]
437
+ @attributes = parse_attributes(ldif: ldif, exclude: [:uid])
438
+ end
439
+
440
+ def zmprov_cdl(domain: '', print: false)
441
+ line = ['zmprov cdl', name(:zmprov, domain)]
442
+ attributes.each do |name, value|
443
+ next if name =~ /(zimbraMailForwardingAddress)|(zimbraMailAlias)/i
444
+ line << attr_to_zmprov(attr_name: name, value: value)
445
+ end
446
+ cmd = line.join(' ')
447
+ puts cmd if print
448
+ return cmd
449
+ end
450
+
451
+ def zmprov_adlm(domain:, print: false)
452
+ line = ['adlm', name(:zmprov, domain)]
453
+ attributes.each do |name, value|
454
+ next unless name =~ /zimbraMailForwardingAddress/i
455
+ line << value.join(' ')
456
+ end
457
+ cmd = line.join(' ')
458
+ puts cmd if print
459
+ return cmd
460
+ end
461
+
462
+ def zmprov_adla(domain:, print: false)
463
+ lines = []
464
+ attributes.each do |name, value|
465
+ next unless name =~ /zimbraMailAlias/i
466
+ value.each do |v|
467
+ lines << "adla #{name(:zmprov, domain)} #{v}"
468
+ end
469
+ end
470
+ cmd = lines.join(' ')
471
+ puts cmd if print
472
+ return cmd
473
+ end
474
+ end
475
+
476
+
477
+
478
+
479
+ # Domain.all.each do |domain|
480
+ # next if domain.name(:zmprov) =~ /archive/i
481
+ # puts domain.zmprov_cd
482
+ # puts "\n"
483
+ # end
484
+
485
+
486
+ end
487
+