78 || password::ask( "IMAP (" . $Cf->acl_admin . ") password: " ); |
78 || password::ask( "IMAP (" . $Cf->acl_admin . ") password: " ); |
79 |
79 |
80 $imap = Mail::IMAPTalk->new( |
80 $imap = Mail::IMAPTalk->new( |
81 Server => $Cf->imap_server, |
81 Server => $Cf->imap_server, |
82 Port => $Cf->imap_port |
82 Port => $Cf->imap_port |
83 ) |
83 ) |
84 or die "Can't connect to IMAP Server '", $Cf->imap_server, |
84 or die "Can't connect to IMAP Server '", $Cf->imap_server, |
85 "', Port '", $Cf->imap_port, "': ", $@; |
85 "', Port '", $Cf->imap_port, "': ", $@; |
86 $imap->login( $Cf->acl_admin, $acl_password ) or die $@; |
86 $imap->login( $Cf->acl_admin, $acl_password ) or die $@; |
87 die "IMAP Server does not advertise acl support" unless $imap->capability->{acl}; |
87 die "IMAP Server does not advertise acl support" |
|
88 unless $imap->capability->{acl}; |
88 |
89 |
89 # requires an imap connection |
90 # requires an imap connection |
90 my $ns = $imap->namespace() or die "No public namespaces available: $@"; |
91 my $ns = $imap->namespace() or die "No public namespaces available: $@"; |
91 $nspat = []; |
92 $nspat = []; |
92 for (@{$ns->[2]}) { |
93 for ( @{ $ns->[2] } ) { |
93 (my $n = $_->[0]) =~ s/$_->[1]$//; |
94 ( my $n = $_->[0] ) =~ s/$_->[1]$//; |
94 push @{$nspat}, [qr/\Q$n\E($_->[1]|$)/, $_->[1]]; |
95 push @{$nspat}, [ qr/\Q$n\E($_->[1]|$)/, $_->[1] ]; |
95 } |
96 } |
96 |
97 |
97 if ( $Cf->add ) { _modify() } |
98 if ( $Cf->add ) { _modify() } |
98 elsif ( $Cf->delete ) { $Cf->acl('delete') ; _modify() } |
99 elsif ( $Cf->delete ) { $Cf->acl('delete'); _modify() } |
99 elsif ( $Cf->list ) { _list() } |
100 elsif ( $Cf->list ) { _list() } |
100 elsif ( $Cf->modify ) { _modify() } |
101 elsif ( $Cf->modify ) { _modify() } |
101 else { die "Need action (--add|--delete|--list|--modify)\n" } |
102 else { die "Need action (--add|--delete|--list|--modify)\n" } |
102 |
103 |
103 } |
104 } |
108 # dn: uid=USER,... |
109 # dn: uid=USER,... |
109 my @users; |
110 my @users; |
110 @ARGV or die "Need user(s)\n"; |
111 @ARGV or die "Need user(s)\n"; |
111 $Cf->folder ~~ [] and die "Need folders(s)\n"; |
112 $Cf->folder ~~ [] and die "Need folders(s)\n"; |
112 $Cf->acl or die "Need acl\n"; |
113 $Cf->acl or die "Need acl\n"; |
113 $Cf->recursive and $Cf->create and die "Use either --recursive or --create but not both\n"; |
114 $Cf->recursive |
|
115 and $Cf->create |
|
116 and die "Use either --recursive or --create but not both\n"; |
114 |
117 |
115 my $r = $ldap->search( |
118 my $r = $ldap->search( |
116 base => $ubase, |
119 base => $ubase, |
117 filter => "(|" . join( "", map { "(uid=$_)" } @ARGV ) . ")" |
120 filter => "(|" . join( "", map { "(uid=$_)" } @ARGV ) . ")" |
118 ); |
121 ); |
119 die $r->error if $r->code; |
122 die $r->error if $r->code; |
120 unless ($r->count) { |
123 unless ( $r->count ) { |
121 prompt('No matching user found in ldap. Continue? (y/N)', "n\n") =~ /y/i or exit 0; |
124 prompt( 'No matching user found in ldap. Continue? (y/N)', "n\n" ) =~ |
|
125 /y/i |
|
126 or exit 0; |
122 @users = @ARGV; |
127 @users = @ARGV; |
123 } |
128 } |
124 |
129 |
125 while (my $e = ($r->shift_entry or shift @users)) { |
130 while ( my $e = ( $r->shift_entry or shift @users ) ) { |
126 |
131 |
127 my ($user, $dn); |
132 my ( $user, $dn ); |
128 |
133 |
129 if (ref $e eq 'Net::LDAP::Entry') { |
134 if ( ref $e eq 'Net::LDAP::Entry' ) { |
130 $user = $e->get_value("uid"); |
135 $user = $e->get_value("uid"); |
131 $dn = $e->dn; |
136 $dn = $e->dn; |
132 } else { |
137 } else { |
133 $user = $e; |
138 $user = $e; |
134 $dn = '[dn not available]'; |
139 $dn = '[dn not available]'; |
136 |
141 |
137 my $modified = 0; |
142 my $modified = 0; |
138 verbose "$user:\n"; |
143 verbose "$user:\n"; |
139 verbose "\t$dn...\n"; |
144 verbose "\t$dn...\n"; |
140 |
145 |
141 for my $folder (@{$Cf->folder}) { |
146 for my $folder ( @{ $Cf->folder } ) { |
142 |
147 |
143 $imap->create($folder) or die "Can't create folder '$folder': $@" if $Cf->create; |
148 $imap->create($folder) |
144 |
149 or die "Can't create folder '$folder': $@" |
145 for my $f (@{acl_folders($folder)}) { |
150 if $Cf->create; |
146 |
151 |
147 if ($Cf->acl eq 'delete') { |
152 for my $f ( @{ acl_folders($folder) } ) { |
148 $imap->deleteacl($f, $user) or die "Can't delete acl: $@"; |
153 |
|
154 if ( $Cf->acl eq 'delete' ) { |
|
155 $imap->deleteacl( $f, $user ) or die "Can't delete acl: $@"; |
149 verbose "\t$f: none\n"; |
156 verbose "\t$f: none\n"; |
150 } else { |
157 } else { |
151 $imap->setacl($f, $user, $Cf->acl) or die "Can't set acl: $@"; |
158 $imap->setacl( $f, $user, $Cf->acl ) |
|
159 or die "Can't set acl: $@"; |
152 verbose "\t$f: " . $Cf->acl . "\n"; |
160 verbose "\t$f: " . $Cf->acl . "\n"; |
153 } |
161 } |
154 |
162 |
155 } |
163 } |
156 |
164 |
157 } |
165 } |
|
166 |
158 # Fix: iusMailOptions wurde erst später eingeführt, bei Bedarf also hinzufügen |
167 # Fix: iusMailOptions wurde erst später eingeführt, bei Bedarf also hinzufügen |
159 #if (!grep /^iusMailOptions$/, @{$e->get("objectClass")}) { |
168 #if (!grep /^iusMailOptions$/, @{$e->get("objectClass")}) { |
160 #$e->add(objectClass => "iusMailOptions"); |
169 #$e->add(objectClass => "iusMailOptions"); |
161 #} |
170 #} |
162 |
171 |
176 |
185 |
177 #@ARGV = ("*") unless @ARGV; |
186 #@ARGV = ("*") unless @ARGV; |
178 |
187 |
179 die "option acl_admin required\n" unless $Cf->acl_admin; |
188 die "option acl_admin required\n" unless $Cf->acl_admin; |
180 |
189 |
181 if ($Cf->aclgroups) { |
190 if ( $Cf->aclgroups ) { |
182 |
191 |
183 warn "--folder option ignored when listing groups" unless $Cf->folder ~~ []; |
192 warn "--folder option ignored when listing groups" |
|
193 unless $Cf->folder ~~ []; |
184 list_groups(@ARGV); |
194 list_groups(@ARGV); |
185 |
195 |
186 } elsif (@ARGV) { |
196 } elsif (@ARGV) { |
187 |
197 |
188 # my $uid = $ARGV[0]; |
198 # my $uid = $ARGV[0]; |
189 # # searching by more than use user may be too expensive |
199 # # searching by more than use user may be too expensive |
190 # die "Searching by more than one user not supported" unless @ARGV == 1 or $uid =~ /\*/; |
200 # die "Searching by more than one user not supported" unless @ARGV == 1 or $uid =~ /\*/; |
191 #list_by_user($_) for @ARGV; |
201 #list_by_user($_) for @ARGV; |
192 |
202 |
193 warn "--folder option ignored when listing by user" unless $Cf->folder ~~ []; |
203 warn "--folder option ignored when listing by user" |
194 list_by_user($imap, @ARGV); |
204 unless $Cf->folder ~~ []; |
195 |
205 list_by_user( $imap, @ARGV ); |
196 } elsif (not $Cf->folder ~~ []) { |
206 |
197 |
207 } elsif ( not $Cf->folder ~~ [] ) { |
198 list_by_folder($_) for @{$Cf->folder}; |
208 |
|
209 list_by_folder($_) for @{ $Cf->folder }; |
199 |
210 |
200 } else { |
211 } else { |
201 |
212 |
202 die "Need either user or --folder. If you really want to search all users then supply the pattern '*'."; |
213 die |
|
214 "Need either user or --folder. If you really want to search all users then supply the pattern '*'."; |
203 |
215 |
204 } |
216 } |
205 |
217 |
206 } |
218 } |
207 |
219 |
208 sub list_groups(@) { |
220 sub list_groups(@) { |
209 |
221 |
210 @_ = ('*') unless @_; |
222 @_ = ('*') unless @_; |
211 my @ag = split ',', $Cf->imap_aclgroups; |
223 my @ag = split ',', $Cf->imap_aclgroups; |
212 my $ag_att = AT_ACLGROUPS; |
224 my $ag_att = AT_ACLGROUPS; |
213 my $filter = "(&($ag_att=*)" |
225 my $filter = |
214 . "(|" . join( "", map { "(uid=$_)" } @_ ) . "))"; |
226 "(&($ag_att=*)" . "(|" . join( "", map { "(uid=$_)" } @_ ) . "))"; |
215 my $r = $ldap->search( |
227 my $r = $ldap->search( |
216 attrs => ['uid', AT_ACLGROUPS], |
228 attrs => [ 'uid', AT_ACLGROUPS ], |
217 filter => $filter, |
229 filter => $filter, |
218 base => $ubase, |
230 base => $ubase, |
219 ); |
231 ); |
220 die $r->error if $r->code; |
232 die $r->error if $r->code; |
221 |
233 |
222 unless ($r->count) { |
234 unless ( $r->count ) { |
223 print ("No aclgroups found in ldap\n"); |
235 print("No aclgroups found in ldap\n"); |
224 exit 0; |
236 exit 0; |
225 } |
237 } |
226 |
238 |
227 my $users; |
239 my $users; |
228 while (my $e = ($r->shift_entry)) { |
240 while ( my $e = ( $r->shift_entry ) ) { |
229 my $uid = $e->get_value('uid'); |
241 my $uid = $e->get_value('uid'); |
230 my @ag_cur = split ',', $e->get_value($ag_att); |
242 my @ag_cur = split ',', $e->get_value($ag_att); |
231 for (@ag) { |
243 for (@ag) { |
232 $users->{$_} = defined $users->{$_} |
244 $users->{$_} = |
233 ? [@{$users->{$_}}, $uid] |
245 defined $users->{$_} |
234 : [ $uid ] |
246 ? [ @{ $users->{$_} }, $uid ] |
235 if $_ ~~ @ag_cur |
247 : [$uid] |
236 } |
248 if $_ ~~ @ag_cur; |
237 } |
249 } |
238 |
250 } |
239 print "$_:\n\t", join("\n\t", @{$users->{$_}}), "\n\n" for keys %{$users}; |
251 |
|
252 print "$_:\n\t", join( "\n\t", @{ $users->{$_} } ), "\n\n" |
|
253 for keys %{$users}; |
240 |
254 |
241 } |
255 } |
242 |
256 |
243 sub list_by_user($@) { |
257 sub list_by_user($@) { |
244 |
258 |
245 my $imap = shift; |
259 my $imap = shift; |
246 my $filter = "(|" . join( "", map { "(uid=$_)" } @_ ) . ")"; |
260 my $filter = "(|" . join( "", map { "(uid=$_)" } @_ ) . ")"; |
|
261 |
247 #my $filter = "(uid=$uid)"; |
262 #my $filter = "(uid=$uid)"; |
248 my $r = $ldap->search( |
263 my $r = $ldap->search( |
249 filter => $filter, |
264 filter => $filter, |
250 base => $ubase, |
265 base => $ubase, |
251 ); |
266 ); |
252 die $r->error if $r->code; |
267 die $r->error if $r->code; |
253 my @users; |
268 my @users; |
254 unless ($r->count) { |
269 unless ( $r->count ) { |
255 verbose("No matching users found in ldap.\n"); |
270 verbose("No matching users found in ldap.\n"); |
256 @users = @_; |
271 @users = @_; |
257 } |
272 } |
258 |
273 |
259 while (my $e = ($r->shift_entry or shift @users)) { |
274 while ( my $e = ( $r->shift_entry or shift @users ) ) { |
260 |
275 |
261 my ($uid, $cn, $mr); |
276 my ( $uid, $cn, $mr ); |
262 if (ref $e eq 'Net::LDAP::Entry') { |
277 if ( ref $e eq 'Net::LDAP::Entry' ) { |
263 $uid = $e->get_value("uid"); |
278 $uid = $e->get_value("uid"); |
264 $cn = join( ", ", $e->get_value("cn") ); |
279 $cn = join( ", ", $e->get_value("cn") ); |
265 $mr = $e->get_value(AT_PRIMARYADDRESS) || ""; # ?? |
280 $mr = $e->get_value(AT_PRIMARYADDRESS) || ""; # ?? |
266 } else { |
281 } else { |
267 $uid = $e; |
282 $uid = $e; |
268 $cn = '[cn not available]'; |
283 $cn = '[cn not available]'; |
269 $mr = '[address not available]'; |
284 $mr = '[address not available]'; |
270 } |
285 } |
298 # ], |
315 # ], |
299 # ... |
316 # ... |
300 # ] |
317 # ] |
301 my $hasacl; |
318 my $hasacl; |
302 my $ns = $imap->namespace() or die "No public namespaces available: $@"; |
319 my $ns = $imap->namespace() or die "No public namespaces available: $@"; |
|
320 |
303 # uns interessieren nur 'public' namespaces |
321 # uns interessieren nur 'public' namespaces |
304 for my $n (@{$ns->[2]}) { |
322 for my $n ( @{ $ns->[2] } ) { |
305 |
323 |
306 my $folders = imap_rlist( '', $n->[0], $n->[1] ); |
324 my $folders = imap_rlist( '', $n->[0], $n->[1] ); |
307 for my $f ( @{$folders} ) { |
325 for my $f ( @{$folders} ) { |
308 |
326 |
309 #next if '\\Noselect' ~~ $f->[0]; |
327 #next if '\\Noselect' ~~ $f->[0]; |
310 my $perms = $imap->getacl( $f ) or die "Can't getacl '$f': $@"; |
328 my $perms = $imap->getacl($f) or die "Can't getacl '$f': $@"; |
311 my ($u, $p); |
329 my ( $u, $p ); |
312 while ($u = shift @{$perms} and $p = shift @{$perms}) { |
330 while ( $u = shift @{$perms} and $p = shift @{$perms} ) { |
313 next unless $u eq $uid; |
331 next unless $u eq $uid; |
314 $hasacl = 1; |
332 $hasacl = 1; |
315 print "\t$f: $u [$p]\n"; |
333 print "\t$f: $u [$p]\n"; |
316 } |
334 } |
317 |
335 |
318 } |
336 } |
319 |
337 |
320 } |
338 } |
321 |
339 |
322 print "\tno acl found on listable folders in shared namespaces\n" unless $hasacl; |
340 print "\tno acl found on listable folders in shared namespaces\n" |
|
341 unless $hasacl; |
323 print "\n"; |
342 print "\n"; |
324 |
343 |
325 } |
344 } |
326 |
345 |
327 } |
346 } |
328 |
347 |
329 sub list_by_folder($) { |
348 sub list_by_folder($) { |
330 |
349 |
331 my ($folder) = @_; |
350 my ($folder) = @_; |
332 |
351 |
333 for my $f ( @{acl_folders($folder)} ) { |
352 for my $f ( @{ acl_folders($folder) } ) { |
334 |
353 |
335 my $hasacl; |
354 my $hasacl; |
336 print "$f\n"; |
355 print "$f\n"; |
337 |
356 |
338 my $perms = $imap->getacl( $f ) or die $@; |
357 my $perms = $imap->getacl($f) or die $@; |
339 my ($u, $p); |
358 my ( $u, $p ); |
340 while ($u = shift @{$perms} and $p = shift @{$perms}) { |
359 while ( $u = shift @{$perms} and $p = shift @{$perms} ) { |
341 |
360 |
342 # '#user' will be listed when we have a global acl for 'user' |
361 # '#user' will be listed when we have a global acl for 'user' |
343 my $gl = $u =~ /^#/ ? ' [global acl]' : ''; |
362 my $gl = $u =~ /^#/ ? ' [global acl]' : ''; |
344 my $gr = $u =~ /^\$/ ? ' [group acl]' : ''; |
363 my $gr = $u =~ /^\$/ ? ' [group acl]' : ''; |
345 $hasacl = 1; |
364 $hasacl = 1; |
346 print "\t$u [$p]$gr$gl\n"; |
365 print "\t$u [$p]$gr$gl\n"; |
347 } |
366 } |
348 |
367 |
349 print "\tno acl found\n" unless $hasacl; |
368 print "\tno acl found\n" unless $hasacl; |
380 } |
399 } |
381 } |
400 } |
382 |
401 |
383 sub imap_list($$) { |
402 sub imap_list($$) { |
384 |
403 |
385 my ($ref, $folder) = @_; |
404 my ( $ref, $folder ) = @_; |
386 |
405 |
387 my $list = $imap->list($ref, $folder) or die "Can't list('$ref', '$folder'): $@"; |
406 my $list = $imap->list( $ref, $folder ) |
|
407 or die "Can't list('$ref', '$folder'): $@"; |
|
408 |
388 # single folder sieht wie folgt aus: [[flag1, flag2, ...], separator, foldername] |
409 # single folder sieht wie folgt aus: [[flag1, flag2, ...], separator, foldername] |
389 ref $list and return [ map $_->[2], @{$list} ]; |
410 ref $list and return [ map $_->[2], @{$list} ]; |
|
411 |
390 # assuming empty result list otherwise |
412 # assuming empty result list otherwise |
391 return []; |
413 return []; |
392 |
414 |
393 } |
415 } |
394 |
416 |
395 sub imap_rlist($$$) { |
417 sub imap_rlist($$$) { |
396 |
418 |
397 my ($ref, $folder, $sep) = @_; |
419 my ( $ref, $folder, $sep ) = @_; |
398 $folder =~ s/$sep+$//; |
420 $folder =~ s/$sep+$//; |
399 |
421 |
400 my $list = imap_list($ref, $folder); |
422 my $list = imap_list( $ref, $folder ); |
401 push @{$list}, @{imap_list($ref, "$folder$sep*")} if $Cf->recursive; |
423 push @{$list}, @{ imap_list( $ref, "$folder$sep*" ) } if $Cf->recursive; |
402 return $list; |
424 return $list; |
403 } |
425 } |
404 |
426 |
405 sub acl_folders($) { |
427 sub acl_folders($) { |
406 |
428 |
407 my ($f) = @_; |
429 my ($f) = @_; |
408 my $folders; |
430 my $folders; |
409 |
431 |
410 for my $np (@{$nspat}) { |
432 for my $np ( @{$nspat} ) { |
|
433 |
411 # don't modify $f! |
434 # don't modify $f! |
412 (my $ft = $f) =~ s/$np->[1]$//; |
435 ( my $ft = $f ) =~ s/$np->[1]$//; |
413 return imap_rlist('', $f, $np->[1]) if ($ft =~ /$np->[0]/); |
436 return imap_rlist( '', $f, $np->[1] ) if ( $ft =~ /$np->[0]/ ); |
414 } |
437 } |
415 |
438 |
416 die "Foldername '$f' must begin with the name of a shared namespace\n"; |
439 die "Foldername '$f' must begin with the name of a shared namespace\n"; |
417 |
440 |
418 } |
441 } |
419 |
442 |
420 sub prompt($$) { |
443 sub prompt($$) { |
421 my ($prompt, $default) = @_; |
444 my ( $prompt, $default ) = @_; |
422 print $prompt, substr($default, 0, 1), "\b"; |
445 print $prompt, substr( $default, 0, 1 ), "\b"; |
423 ReadMode 4; my $r = ReadKey(0); ReadMode 0; |
446 ReadMode 4; |
424 if ($r eq "\n") { $r = $default } |
447 my $r = ReadKey(0); |
425 else { $r .= substr($default, 1) } |
448 ReadMode 0; |
|
449 if ( $r eq "\n" ) { $r = $default } |
|
450 else { $r .= substr( $default, 1 ) } |
426 print $r; |
451 print $r; |
427 return $r; |
452 return $r; |
428 } |
453 } |
429 |
454 |
430 1; |
455 1; |