1 #!/usr/bin/perl -wT
  2 # BEGIN SURETEC TAGGED BLOCK {{{
  3 #=============================================================================
  4 #
  5 #         FILE:  create_dovecot_shares
  6 #
  7 #        USAGE:  create_dovecot_shares --help
  8 #
  9 #  DESCRIPTION:  Create Dovecot shares and put symlinks into user Maildir
 10 #
 11 #      OPTIONS:  --username
 12 #                --group
 13 #                --clean
 14 #                --dry-run
 15 #                --History
 16 #                --share-with
 17 #                --maildir
 18 #                --override
 19 #                --prefix
 20 #                --restore
 21 #                --skip
 22 #                --Version
 23 #                --verbose
 24 #                --man
 25 #                --help
 26 #
 27 # REQUIREMENTS:  Data::Dumper
 28 #                File::Find
 29 #                Fcntl
 30 #                Getopt::Long
 31 #                List::Util
 32 #                Pod::Usage
 33 #                POSIX
 34 #                Term::ANSIColor
 35 #                Storable
 36 #
 37 #         BUGS:  N/A
 38 #        NOTES:  N/A
 39 #       AUTHOR:  Gavin Henry (GH), <ghenry@suretecsystems.com>
 40 #      COMPANY:  Suretec Systems Ltd. - http://www.suretecsystems.com
 41 #      SUPPORT:  <support@suretecsystems.com>
 42 #      VERSION:  1.01
 43 #      CREATED:  26/05/06
 44 #      UPDATED:  29/05/06
 45 #=============================================================================
 46 # END SURETEC TAGGED BLOCK }}}
 47 
 48 # Untaint environment
 49 $ENV{'PATH'}  = '/usr/local/bin:/usr/bin:/bin';
 50 $ENV{'SHELL'} = '/bin/bash';
 51 delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
 52 
 53 # Returned by Perl::MinimumVersion 0.13
 54 require 5.006;
 55 
 56 use strict;
 57 use warnings;
 58 use Data::Dumper;
 59 use File::Find;
 60 use Fcntl qw(:mode);
 61 use Getopt::Long;
 62 use List::Util qw(min);
 63 use Pod::Usage;
 64 use POSIX qw(strftime);
 65 use Term::ANSIColor qw(:constants);
 66 use Storable qw(nstore retrieve);
 67 
 68 # Unbuffer output
 69 $| = 1;
 70 
 71 # Normally use Readonly for this, but want to keep to Core Modules
 72 our $VERSION        = '1.01';
 73 our $SHARED         = '/dovecot-shared';
 74 our $PROGNAME       = 'create_dovecot_shares';
 75 our $SAVED          = '/var/cache/dovecot_shares.hist';
 76 our $HUMAN_RUN_DATE = strftime "%a %b %e %H:%M:%S %Y", localtime;
 77 our $NOW            = time;
 78 
 79 #-----------------------------------------------------------------------------
 80 # Standard Suretec method for parsing program arguments
 81 #
 82 # "perldoc Getopt::Long" for more info
 83 #-----------------------------------------------------------------------------
 84 
 85 die "Sorry, you need to be the root user - Suretec.\n" if ( $< != 0 );
 86 
 87 my %options;
 88 my ( @users, @share_with, @maildir, @skip, );
 89 
 90 GetOptions(
 91     \%options,
 92     qw( group=s override dry-run prefix=s restore:s History clean Version
 93       verbose help|? man ),
 94     'username=s'   => \@users,
 95     'share-with=s' => \@share_with,
 96     'maildir=s'    => \@maildir,
 97     'skip=s'       => \@skip,
 98 );
 99 
100 pod2usage(1) if $options{help} or ( !keys %options );
101 pod2usage( -verbose => 2 ) if $options{man};
102 
103 print "This is $PROGNAME version $VERSION\n" if $options{Version};
104 
105 #-----------------------------------------------------------------------------
106 # Begin
107 #-----------------------------------------------------------------------------
108 my %SAVE;
109 
110 # we only use $File::Find:name, so we set no_chdir here
111 my %find_options = (
112     untaint         => 1,
113     untaint_pattern => qr{^([-+@\w\s&!\.\{\}#]+)$},
114     no_chdir        => '1',
115     wanted          => \&emails_and_dirs,
116 );
117 
118 # Load previous save
119 my $SAVE = retrieve("$SAVED") if -e $SAVED;
120 
121 # Reset counter
122 my $run;
123 if ( defined $SAVE->{counter} ) {
124 
125     # We only keep a history of 10 runs
126     $SAVE->{counter} = 0 if $SAVE->{counter} == 10;
127 
128     # Track from the counter
129     $run = $SAVE->{counter};
130 }
131 
132 # New run
133 ++$run and ++$SAVE->{counter};
134 
135 @share_with = split_args(@share_with);
136 @users      = split_args(@users);
137 @maildir    = split_args(@maildir);
138 @skip       = split_args(@skip);
139 
140 # Initialise directory and file start no. for save_previous
141 my $dir_num  = 0;
142 my $file_num = 0;
143 
144 #-----------------------------------------------------------------------------
145 # Here we are checking the group we got passed
146 #-----------------------------------------------------------------------------
147 my $share_group;
148 if ( $options{group} ) {
149 
150     die "Need username and user to share!\n" if ( !@share_with or !@users );
151 
152     die "Group: '$options{group}' does not exist! (typo?)\n"
153       if !defined getgrnam( $options{group} );
154 
155     # getgrnam returns ($name, $passwd, $gid, $members)
156     # i.e $group_details[0] is $name etc. etc.
157     my @group_details = getgrnam( $options{group} );
158 
159     # Check they are members
160     for my $user (@users) {
161 
162         # A user, by default is a member of their own group
163         next if $group_details[0] eq $user;
164 
165         die "Username: '$user' is not a valid user!\n"
166           if !defined getpwnam($user);
167 
168         die "User '$user' is not a member of the '$options{group}' group\n"
169           if not $group_details[3] =~ m{$user};
170     }
171 
172     die "Refusing to create a dovecot share with the share group set to"
173       . " 'root' (gid: $group_details[2])!\n"
174       if $group_details[2] == 0;
175 
176     $share_group = $group_details[2];
177 }
178 
179 #-----------------------------------------------------------------------------
180 # Here we start the actual share creation
181 #-----------------------------------------------------------------------------
182 my @homes;
183 if (@users) {
184     die "Need users to share with!\n" if !@share_with;
185 
186     for my $u (@share_with) {
187         die "Can not share with '$u': User '$u' does not exist! (typo?)\n"
188           if !defined getpwnam($u);
189     }
190 
191     die "Need to set group for shares! Please provide --group e.g."
192       . " --group=sharedmail\n"
193       if !$options{group};
194 
195     @homes = map { '/home/' . clean_user($_) } @users;
196     find( \%find_options, @homes );    # this populates %SAVE if it's not
197                                        # retreived above
198 
199     # We only want to use these Maildirs if passed
200     if (@maildir) {
201         for my $maildir (@maildir) {
202             die "Maildir Directory: '$maildir' doesn't exist!\n"
203               if -d !$maildir;
204             create_share($maildir);
205         }
206         exit 0;
207     }
208 
209     # $SAVE has been populated by File::Find
210     for my $dir ( keys %{ $SAVE->{run}{$run}{directory} } ) {
211         create_share( $SAVE->{run}{$run}{directory}{$dir}{dir}{name} );
212     }
213 }
214 
215 #-----------------------------------------------------------------------------
216 # Here we are printing to STDOUT, everything in the History file
217 #-----------------------------------------------------------------------------
218 if ( $options{History} ) {
219     my $SAVE = retrieve($SAVED)
220       if -e $SAVED
221       or die "History file: '$SAVED' does not exist\n"
222       . "Has $PROGNAME been run before?\n";
223 
224     local $Data::Dumper::Varname  = 'Dovecot-share History:';
225     local $Data::Dumper::Sortkeys = 1;
226     print Dumper($SAVE), "\n";
227     exit 0;
228 }
229 
230 #-----------------------------------------------------------------------------
231 # Here we are restoring from the history file on the file system
232 #-----------------------------------------------------------------------------
233 if ( defined $options{restore} ) {    # restore:s sets an empty string ''.
234     my $SAVE = retrieve($SAVED)       # so if no run passed, we restore
235       if -e $SAVED                    # last run
236       or die "History file: '$SAVED' does not exist\n"
237       . "Has $PROGNAME been run before?\n";
238 
239     my $restore;
240     if ( $options{restore} =~ m{\d+} ) {
241         $restore = $options{restore};
242         print "Restoring run: '$restore'\n";
243     }
244     else {
245 
246         # Would use one of the various Date::* modules from
247         # the CPAN here, but don't want to use any non-core
248         # modules
249         my @runs;
250         for my $run_num ( keys %{ $SAVE->{run} } ) {
251             push @runs,
252               ( [ $run_num, $SAVE->{run}{$run_num}{restore_check_date} ] );
253         }
254 
255         my @elapsed_time;
256         my %run_with;
257         for my $time (@runs) {
258             my $difference = $NOW - $time->[1];    # restore_check_date above
259             $run_with{ $time->[0] } = $difference; # Save, to find run below
260             push @elapsed_time, $difference;
261 
262         }
263         my $last_run = min @elapsed_time;          # Closest run to $NOW
264 
265         for my $to_restore ( keys %run_with ) {
266             $restore = $to_restore
267               if $run_with{$to_restore} == $last_run;    # Find the run key
268         }
269 
270         my $seconds = $last_run % 60;
271         $last_run = ( $last_run - $seconds ) / 60;
272         my $minutes = $last_run % 60;
273         $last_run = ( $last_run - $minutes ) / 60;
274         my $hours = $last_run % 24;
275         $last_run = ( $last_run - $hours ) / 24;
276         my $days  = $last_run % 7;
277         my $weeks = ( $last_run - $days ) / 7;
278 
279         print "Restoring the most recent run (run '$restore'), which was:\n"
280           . " $weeks week/s, $days day/s, $hours hour/s,"
281           . " $minutes minute/s and $seconds second/s ago.\n";
282     }
283 
284     for my $dir ( keys %{ $SAVE->{run}{$restore}{directory} } ) {
285         if ( $SAVE->{run}{$restore}{directory}{$dir}{dir}{name} ) {
286             print BOLD GREEN, "***** DRY RUN *****\n", RESET;
287             print BOLD GREEN, "Restoring $SAVE->{run}{$restore}{directory}{$dir}{dir}{name}"
288               . " with uid:"
289               . " '$SAVE->{run}{$restore}{directory}{$dir}{uid}', "
290               . "gid '$SAVE->{run}{$restore}{directory}{$dir}{gid}', "
291               . "and mode '$SAVE->{run}{$restore}{directory}{$dir}{mode}'\n", RESET
292               if $options{verbose};
293 
294             restore_emails_and_dirs(
295                 $SAVE->{run}{$restore}{directory}{$dir}{uid},
296                 $SAVE->{run}{$restore}{directory}{$dir}{gid},
297                 $SAVE->{run}{$restore}{directory}{$dir}{dir}{name},
298                 $SAVE->{run}{$restore}{directory}{$dir}{mode}
299             );
300         }
301 
302         for my $file (
303             keys %{ $SAVE->{run}{$restore}{directory}{$dir}{dir}{file} } )
304         {
305             if ( $SAVE->{run}{$restore}{directory}{$dir}{dir}{file}{$file}
306                 {name} )
307             {
308                 print BOLD GREEN,
309 "Restoring $SAVE->{run}{$restore}{directory}{$dir}{dir}{file}{$file}{name}"
310                   . " with uid:"
311                   . " '$SAVE->{run}{$restore}{directory}{$dir}{dir}{file}{$file}{uid}', "
312                   . "gid '$SAVE->{run}{$restore}{directory}{$dir}{dir}{file}{$file}{gid}', "
313                   . "and mode '$SAVE->{run}{$restore}{directory}{$dir}{dir}{file}{$file}{mode}'\n", RESET
314                   if $options{verbose};
315 
316                 restore_emails_and_dirs(
317                     $SAVE->{run}{$restore}{directory}{$dir}{dir}{file}{$file}
318                       {uid},
319                     $SAVE->{run}{$restore}{directory}{$dir}{dir}{file}{$file}
320                       {gid},
321                     $SAVE->{run}{$restore}{directory}{$dir}{dir}{file}{$file}
322                       {name},
323                     $SAVE->{run}{$restore}{directory}{$dir}{dir}{file}{$file}
324                       {mode}
325                 );
326 
327             }
328         }
329     }
330     exit 0;
331 }
332 
333 #-----------------------------------------------------------------------------
334 # Here we are removing the history file on the file system
335 #-----------------------------------------------------------------------------
336 if ( $options{clean} ) {
337     print
338 "Are you sure you want to remove file: '$SAVED' ? [y/n (Enter to cancel)] ";
339     chomp( my $answer = <STDIN> );
340 
341     if ( $answer eq 'y' ) {
342         unlink($SAVED)
343           or die "Failed to delete '$SAVED': $!\n";
344         print "Deleted $SAVED\n";
345         exit 0;
346     }
347     else {
348         print "Done.\n";
349         exit 0;
350     }
351 }
352 
353 #-----------------------------------------------------------------------------
354 # clean_user($username)
355 #
356 # Untaint a username, and check it's valid
357 #-----------------------------------------------------------------------------
358 sub clean_user {
359     my $unclean_user = shift;
360     if ( $unclean_user =~ qr{^([-+@\w.]+)$} ) {
361         my $clean = $1;
362         return $clean;
363     }
364     else {
365         die "Username: '$unclean_user' invalid!\n";
366     }
367 
368     return;
369 }
370 
371 #-----------------------------------------------------------------------------
372 # clean_dir($dir)
373 #
374 # Untaint a directory
375 #-----------------------------------------------------------------------------
376 sub clean_dir {
377     my $unclean_dir = shift;
378     my ($clean_dir) = $unclean_dir =~ /^([-+@\w.\/\s&!\.]+)$/;
379     return $clean_dir;
380 }
381 
382 #-----------------------------------------------------------------------------
383 # clean_file($dir)
384 #
385 # Untaint a file
386 #-----------------------------------------------------------------------------
387 sub clean_file {
388     my $unclean_file = shift;
389     my ($clean_file) = $unclean_file =~ /^([-+@\w.\/\s,&!\.:=_]+)$/;
390     return $clean_file;
391 }
392 
393 #-----------------------------------------------------------------------------
394 # split_args(@args_to_split)
395 #
396 # Split up our commandline args - ghenry,john,james etc.
397 #-----------------------------------------------------------------------------
398 sub split_args {
399     my @to_split = @_;
400     my @split = split( /,/, join( ',', @to_split ) );
401     return @split;
402 }
403 
404 #-----------------------------------------------------------------------------
405 # restore_emails_and_dirs($uid, $group, $to_change, $mode)
406 #
407 # Function for restoring changes made during dovecot shares
408 #-----------------------------------------------------------------------------
409 sub restore_emails_and_dirs {
410     my $uid       = shift;
411     my $group     = shift;
412     my $to_change = shift;
413     my $mode      = shift;
414 
415     return if $options{'dry-run'};
416             
417     chown $uid, $group, $to_change
418       or warn "Couldn't change ownership of '$to_change': $!\n";
419 
420     chmod oct($mode), $to_change
421       or warn "Couldn't change permissions of '$to_change': $!\n";
422 
423     return;
424 }
425 
426 #-----------------------------------------------------------------------------
427 # emails_and_dirs
428 #
429 # Callback for File::Find when creating dovecot shares
430 #-----------------------------------------------------------------------------
431 sub emails_and_dirs {
432 
433     # Only files in Maildir
434     return if $File::Find::name !~ m{Maildir};
435 
436     # No dovecot system files
437     return
438       if $File::Find::name =~ m{subscriptions|dovecot|public|control|index};
439 
440     # No symlinks
441     return if -l $File::Find::name;
442 
443     # Record permissions we have found right away
444     my ( $mode, $uid, $gid ) = ( stat($File::Find::name) )[ 2, 4, 5 ];
445 
446     $mode = sprintf "%04o", S_IMODE($mode);
447 
448     save_previous( $run, $File::Find::name, $mode, $uid, $gid );
449 
450     return;
451 }
452 
453 #-----------------------------------------------------------------------------
454 # create_share($maildir)
455 #
456 # Main function for creating dovecot shares
457 #-----------------------------------------------------------------------------
458 sub create_share {
459     my $dir = shift;
460 
461     # untaint $dir
462     $dir =~ /^([-+@\w.\/\s&!\.\{\}#]+)$/;
463     $dir = $1;
464 
465     # Grabbing the new, cur and tmp folders here
466     my @change_perms_on;
467     push @change_perms_on, $dir;
468 
469     # We don't want to add a dovecot-shared to new, cur or tmp dirs
470     return if $dir =~ m{(new|cur|tmp)$};
471 
472     if ( ( -e $dir . $SHARED ) && !$options{override} ) {
473         print BOLD GREEN, "***** DRY RUN *****\n", RESET
474           if $options{'dry-run'};
475         print RED,
476           "Directory: $dir already shared (try --override)," . " skipping!\n",
477           RESET;
478     }
479     else {
480 
481         # Check for dirs to skip straight away
482         if (@skip) {
483             for my $skip (@skip) {
484                 return if $dir =~ m{$skip$};
485             }
486         }
487 
488         print BOLD GREEN, "***** DRY RUN *****\n", RESET
489           if $options{'dry-run'};
490 
491         for my $u (@share_with) {
492             print GREEN, "Sharing Maildir: $dir with user: '$u'\n", RESET;
493         }
494 
495         return if $options{'dry-run'};
496 
497         # Save to filesystem before we do anything, so we can
498         # do a --restore in case we mess up. $SAVE is setup in
499         # emails_and_dirs()
500         nstore( $SAVE, $SAVED )
501           or die "Can't save history: $!\n";
502 
503         # More untainting
504         my $dovecot_file = clean_dir( $dir . $SHARED );
505 
506         # Remove dovecot-shared, only if --override is set, as we enter
507         # this branch if dovecot-shared exists
508         if ( -e $dovecot_file ) {
509             unlink $dovecot_file
510               or die "Can't remove '$dovecot_file: $!\n";
511         }
512 
513         open my $DOVECOT_SHARED, ">>", $dovecot_file
514           or die "Couldn't create $dovecot_file: $!\n";
515         close $DOVECOT_SHARED;
516 
517         if ( -e $dovecot_file && $options{verbose} ) {
518             print "Created $dovecot_file\n";
519         }
520 
521         # pick off the owner of the dir and use that for all perm changes
522         my $uid = ( stat($dir) )[4];
523 
524         # untaint $uid
525         $uid =~ /^([\d]+)$/;
526         $uid = $1;
527 
528         # untaint $share_group
529         $share_group =~ /^([\d]+)$/;
530         $share_group = $1;
531 
532         # change perms on dirs and dovecot-shared
533         for my $to_change ( $dir, $dovecot_file, @homes, @change_perms_on ) {
534             change_perms( $uid, $to_change, $share_group );
535         }
536 
537         # change_perms on e-mails, so they can be read
538         for my $dir ( keys %{ $SAVE->{run}{$run}{directory} } ) {
539             for my $file (
540                 keys %{ $SAVE->{run}{$run}{directory}{$dir}{dir}{file} } )
541             {
542                 change_perms(
543                     $uid,
544                     clean_file(
545                         $SAVE->{run}{$run}{directory}{$dir}{dir}{file}{$file}
546                           {name}
547                     ),
548                     $share_group
549                 );
550             }
551         }
552 
553         # create the symlink
554         for my $user (@share_with) {
555             for my $orig_user (@users) {
556                 my $prefix = $options{prefix} || ucfirst($orig_user);
557 
558                 # grab the maildir
559                 my ($orig_maildir) = $dir =~ m{Maildir/(.*)$};
560                 $orig_maildir = '.TOP-INBOX' if !defined $orig_maildir;
561 
562                 # biiiiggggg concatenation :-(
563                 my $symlink =
564                   clean_dir(
565                     '/home/' . $user . '/Maildir/.' . $prefix . $orig_maildir );
566 
567                 next if -l $symlink;    # Don't create same symlink
568 
569                 symlink $dir, $symlink
570                   or warn
571                   "Couldn't create symlink in '/home/$user/Maildir/': $!\n";
572 
573                 if ( -l $symlink && $options{verbose} ) {
574                     print "Symlink at: '$symlink'\n";
575                 }
576             }
577         }
578     }
579     return;
580 }
581 
582 #-----------------------------------------------------------------------------
583 # change_perms($uid, $to_change, $share_group)
584 #
585 # Function for changing File Permissions
586 #-----------------------------------------------------------------------------
587 sub change_perms {
588     my $uid         = shift;
589     my $to_change   = shift;
590     my $share_group = shift;
591 
592     print "Changing ownership and permissions of: $to_change"
593       . " to: 'rwxrws--- $uid $share_group'\n"
594       if $options{verbose};
595 
596     chown $uid, $share_group, $to_change
597       or warn "Couldn't change ownership of '$to_change': $!\n";
598 
599     # can use 02770 here without oct, but keeps similarity bewteen
600     # command line chmod
601     chmod oct(2770), $to_change
602       or warn "Couldn't change permissions of '$to_change': $!\n";
603 
604     return;
605 }
606 
607 #-----------------------------------------------------------------------------
608 # save_previous($run, $dir_or_file, $mode, $uid, $gid)
609 #
610 # Function for saving File Permissions etc. before making changes for the
611 # Dovecot Shares
612 #-----------------------------------------------------------------------------
613 sub save_previous {
614     my $run         = shift;
615     my $dir_or_file = shift;
616     my $mode        = shift;
617     my $uid         = shift;
618     my $gid         = shift;
619 
620     # Setup our dates
621     $SAVE->{run}{$run}{run_date}           = $HUMAN_RUN_DATE;
622     $SAVE->{run}{$run}{restore_check_date} = $NOW;
623 
624     if ( -d $dir_or_file ) {
625         ++$dir_num;
626         $SAVE->{run}{$run}{directory}{$dir_num}{dir}{name} = $dir_or_file;
627         $SAVE->{run}{$run}{directory}{$dir_num}{mode}      = $mode;
628         $SAVE->{run}{$run}{directory}{$dir_num}{uid}       = $uid;
629         $SAVE->{run}{$run}{directory}{$dir_num}{gid}       = $gid;
630     }
631     elsif ( -f $dir_or_file ) {
632         ++$file_num;
633         for my $dir ( keys %{ $SAVE->{run}{$run}{directory} } ) {
634             if (
635                 $dir_or_file =~ $SAVE->{run}{$run}{directory}{$dir}{dir}{name} )
636             {
637                 $SAVE->{run}{$run}{directory}{$dir}{dir}{file}{$file_num}
638                   {name} = $dir_or_file;
639                 $SAVE->{run}{$run}{directory}{$dir}{dir}{file}{$file_num}
640                   {mode} = $mode;
641                 $SAVE->{run}{$run}{directory}{$dir}{dir}{file}{$file_num}{uid} =
642                   $uid;
643                 $SAVE->{run}{$run}{directory}{$dir}{dir}{file}{$file_num}{gid} =
644                   $gid;
645             }
646         }
647     }
648     else {
649         return;
650     }
651 }
652 
653 # {{{ Documentation
654 __END__
655 
656 =pod
657 
658 =head1 NAME
659 
660 create_dovecot_shares - Create Dovecot shares and put symlinks into user Maildirs
661 
662 =head1 VERSION
663 
664 This document describes create_dovecot_shares version 1.01
665 
666 =head1 SYNOPSIS
667 
668     [root@suretec home]$ create_dovecot_shares [OPTIONS] 
669 
670     INPUT OPTIONS:
671     --username      Username/Usernames you want to share the Maildir of, comma seperated
672     --group         Group for Maildir share ownership 
673     --dry-run       Doesn't make any changes, just shows what would happen
674     --share-with    List of users to share with (creates symlinks in their Maildir), comma seperated
675     --maildir       List or Maildirs to share, or all found in home directory if not specified
676     --override      Overrides all existing shares (dovecot-shared file and perms) if found
677     --skip          Skips existing share with same name if found, comma seperated 
678     --prefix        Prefix for shares. Default is username.INBOX etc.
679     --restore       Restore to before changes. Takes a run number, e.g. 5 runs back
680 
681 
682     OUTPUT OPTIONS:
683     --History       Prints Summary of last Create Share runs (we keep 10.)
684     --clean         Removes the dovecot_shares.hist file from F</var/cache>
685     --Version       Print program version
686     --verbose       Print in fine detail what is happening
687     --help          List this help
688     --man           List the full create_dovecot_shares manpage
689 
690     EXAMPLES:
691     [root@suretec home]$ create_dovecot_shares --username=ghenry --group=support --skip=/home/ghenry/Maildir,/home/ghenry/Maildir/.Sent --share-with=john,admin --dry-run
692 
693     [root@suretec home]$ create_dovecot_shares --username=ghenry,john,jack --group=accounts --share-with=philip --override
694 
695     [root@suretec home]$ create_dovecot_shares --username=ghenry --share-with=john --group=accounts --prefix=OFFICE 
696 
697     [root@suretec home]$ create_dovecot_shares --username=ghenry --group=accounts --maildir=/home/john/Maildir/.Sent
698 
699     [root@suretec home]$ create_dovecot_shares --restore 5  
700 
701     [root@suretec home]$ create_dovecot_shares --History
702 
703 =head1 DESCRIPTION
704 
705 Creating lots of F<dovecot-shared> files, changing permissions and creating symlinks is
706  a pain, especially when dealing with more than a handle of users. 
707 
708 C<create_dovecot_shares> helps.
709 
710 It modifies users home directories and Maildirs (permissions) for sharing via 
711 Dovecot, and creates symlinks into the Maildir you want the shares accessed 
712 from.
713 
714 There's even a C<restore> option, to roll back any changes you made with a 
715 history of 10 runs.
716 
717 See L<"SYNOPSIS"> for examples.
718 
719 Must be root.
720 
721 =head1 README
722 
723 Creating lots of F<dovecot-shared> files for the Dovecot IMAP Server, changing permissions and creating symlinks is a pain, especially when dealing with more than a handle of users. 
724 
725 create_dovecot_shares helps
726 
727 =head1 CONFIGURATION AND ENVIRONMENT
728 
729 This script requires no configuration files or environment variables.
730 
731 It does set:
732 
733     $ENV{'PATH'}= '/usr/local/bin:/usr/bin:/bin';
734     $ENV{'SHELL'}= '/bin/bash';
735     delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
736 
737 =head1 PREREQUISITES
738 
739 =over
740 
741 =item *
742 L<Data::Dumper>
743 
744 =item * 
745 L<File::Find>
746 
747 =item * 
748 L<Fcntl>
749 
750 =item * 
751 L<Getopt::Long>
752 
753 =item * 
754 L<List::Util>
755 
756 =item * 
757 L<Pod::Usage>
758 
759 =item * 
760 L<POSIX>
761 
762 =item *
763 L<Term::ANSIColor>
764 
765 =item * 
766 L<Storable>
767 
768 =back
769 
770 =head1 OSNAMES
771 
772 linux
773 
774 =head1 SCRIPT CATEGORIES
775 
776 Mail
777 
778 =head1 INCOMPATIBILITIES
779 
780 None reported.
781 
782 =head1 BUGS AND LIMITATIONS
783 
784 No bugs have been reported.
785 
786 Please report any bugs or feature requests to C<support@suretecsystems.com> or
787 call the number on L<http://www.suretecsystems.com/contact>.
788 
789 =head1 AUTHOR
790 
791 Gavin Henry, C<< <ghenry@suretecsystems.com> >>
792 
793 =head1 LICENCE AND COPYRIGHT
794 
795 Copyright (c) 2006, Suretec Systems Ltd. L<http://www.suretecsystems.com>
796 
797 Copyright (c) 2006,  Gavin Henry, C<< <ghenry@suretecsystems.com> >>. All rights reserved.
798 
799 This module is free software; you can redistribute it and/or
800 modify it under the terms of the GPL V2. See perldoc L<perlgpl>.
801 
802 =head1 DISCLAIMER OF WARRANTY
803 
804 BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
805 FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
806 OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
807 PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
808 EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
809 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
810 ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
811 YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
812 NECESSARY SERVICING, REPAIR, OR CORRECTION.
813 
814 IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
815 WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
816 REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
817 LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
818 OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
819 THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
820 RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
821 FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
822 
823 =begin pod_to_ignore
824 # }}} 
825 


syntax highlighted by Code2HTML, v. 0.9.1