#!/usr/bin/perl -wT

#----------------------------------------------------------------------
# heading     : Administration
# description : Rsync
# navigation  : 4000 4863
#
# Author Stephen Noble dungog.net
#
#----------------------------------------------------------------------

package esmith;

use strict;
use CGI ':all';
use CGI::Carp qw(fatalsToBrowser);

use esmith::cgi;
use esmith::config;
use esmith::util;
use esmith::db;

sub showInitial ($$);                       # main form
sub showHelp ($);

sub modifyRsync ($$);                       # secure form
sub performModifyRsync ($);                 # secure action

sub showAnon    ($$);                       # anon form
sub performShowAnon ($);                    # anon action

sub showIbay    ($$);                       # ibay form
sub performAnon ($);                        # start stop anon rsync
sub modifyIbay  ($);                        # toggle ibay public access

sub deleteRsync ($);
sub deleteItem ($);

sub performRsyncNow ($);                    # run backup now

sub rsyncNowResults ($);                    # view logs
sub recentRsync ($);

BEGIN
{
    # Clear PATH and related environment variables so that calls to
    # external programs do not cause results to be tainted. See
    # "perlsec" manual page for details.

    $ENV {'PATH'} = '';
    $ENV {'SHELL'} = '/bin/bash';
    delete $ENV {'ENV'};
}

esmith::util::setRealToEffective ();

$CGI::POST_MAX=1024 * 100;  # max 100K posts
$CGI::DISABLE_UPLOADS = 1;  # no uploads

my %conf;
tie %conf, 'esmith::config';

my %dungog;
tie %dungog, 'esmith::config', '/home/e-smith/db/dungog';

my %accounts;
tie %accounts, 'esmith::config', '/home/e-smith/db/accounts';

my %Labels = ('*' => 'Daily',
              '1-5' => 'Week days',
              '0,6' => 'Week ends',
              'sun'   => 'Sunday',
              '1'   => 'Monday',
              '2'   => 'Tuesday',
              '3'   => 'Wednesday',
              '4'   => 'Thursday',
              '5'   => 'Friday',
              '6'   => 'Saturday');

my %Labels2 = ('senddirs' => 'send one or more directories to another server',
               'senddirs2self' => 'save directories to self. eg 2nd hardrive',
               'sendservers' => 'send one directory to many servers',
               'receivedirs' => 'receive one or more directories from one server',
               'receiveservers'  => 'receive one directory from many servers');

my %Labels5 = ('test'    => 'Test. Only displays what rsync would do and does no actual copying',
               'verbose' => 'Copies but still gives a verbose display of what it is doing',
               'stats'   => 'Copies and just displays statistics',
               'quiet'   => 'Copies and does no display at all');

my %Labels4 = ('none'    => 'no limit',
              '10'   => '10 kb/s',
              '25'  => '25 kb/s',
              '50'  => '50 kb/s',
              '100' => '100 kb/s',
              '500' => '500 kb/s',
              '1000' => '1 mb/s',
              '2000' => '2 mb/s',
              '4000' => '4 mb/s',
              '6000' => '6 mb/s',
              '10000' => '10 mb/s',
              '20000' => '20 mb/s');

my %Labels3 = ('*/2' => 'Each 2 hours',
               '*/4' => 'Each 4 hours',
               '*/8' => 'Each 8 hours',
               '*/12' => 'Each 12 hours',
               '9-17' => '9am-5pm',
               '8-18' => '8am-6pm',
               '1'  => '1 am',
               '2'  => '2 am',
               '3'  => '3 am',
               '4'  => '4 am',
               '5'  => '5 am',
               '6'  => '6 am',
               '7'  => '7 am',
               '8'  => '8 am',
               '9'  => '9 am',
               '10' => '10 am',
               '11' => '11 am',
               '12' => 'noon',
               '13' => '1 pm',
               '14' => '2 pm',
               '15' => '3 pm',
               '16' => '4 pm',
               '17' => '5 pm',
               '18' => '6 pm',
               '19' => '7 pm',
               '20' => '8 pm',
               '21' => '9 pm',
               '22' => '10 pm',
               '23' => '11 pm');

#------------------------------------------------------------
# examine state parameter and display the appropriate form
#------------------------------------------------------------
my $q = new CGI;

if (! grep (/^state$/, $q->param))
{
      showInitial ($q, '');
}

elsif ($q->param ('state') eq "choosesub")
{
    if ($q->param ('action') eq "Save Settings")
    {
        performModifyRsync ($q);
    }

    if ($q->param ('action') eq "Save Job")
    {
        performShowAnon ($q);
    }

    elsif ($q->param ('action') eq "Run this rsync job now")
    {
        performRsyncNow ($q);
    }
}

elsif ($q->param ('state') eq "help")
{
    showHelp ($q);
}

#secure form
elsif ($q->param ('state') eq "modifyrsync")
{
    modifyRsync ($q, '');
}
elsif ($q->param ('state') eq "performmodify")
{
    performModifyRsync ($q);
}

#tasks
elsif ($q->param ('state') eq "deletersync")
{
    deleteRsync ($q);
}
elsif ($q->param ('state') eq "deleteitem")
{
    deleteItem ($q);
}
#show results
elsif ($q->param ('state') eq "rsyncNowResults")
{
    rsyncNowResults ($q);
}
elsif ($q->param ('state') eq "recentRsync")
{
    recentRsync ($q);
}

#ibay setup form
elsif ($q->param ('state') eq "showibay")
{
    showIbay ($q, '');
}
elsif ($q->param ('state') eq "performAnon")
{
    performAnon ($q);
}
elsif ($q->param ('state') eq "modifyibay")
{
    modifyIbay ($q);
}

#anon form
elsif ($q->param ('state') eq "showAnon")
{
    showAnon ($q, '');
}
elsif ($q->param ('state') eq "performShowAnon")
{
    performShowAnon ($q);
}

else
{
    esmith::cgi::genStateError ($q, \%conf);
}

exit (0);

#########################################################################
sub showInitial ($$)
{
    my ($q, $msg) = @_;

    if ($msg eq '')
    {
          esmith::cgi::genHeaderNonCacheable
            ($q, \%conf, 'Rsync file transfer');
    }
    else
    {
        esmith::cgi::genHeaderNonCacheable
            ($q, \%conf, 'Rsync file transfer');

        print $q->h4 ('Operation status report');
        print $q->p ($msg);
        print $q->hr;
    }

    print $q->startform (-method => 'POST', -action => $q->url (-absolute => 1));

    #short intro
    print $q->Tr (esmith::cgi::genTextRow ($q, $q->p ('Automatically mirror your data to another server.
                     Use either secure rsync using ssh keys, or anonymous rsync.
                     Transfers have been scheduled for sets highlighted in red.
                     You also have a transfer now option for each set')));

    #check to see if cron is running
    unless ( -e "/var/run/crond.pid" )
    {
      print $q->p (esmith::cgi::genSmallRedCell ($q, "NOTE: cron is not ".
              "running. It really should be, besides not automating our backups, lots of system tasks are not ".
              "performed. As root, on the command line, type 'service crond start' then 'ps -A' to check."));
    }

    #configure and select set
    print $q->h4 ('Transfer Sets Summary - Send and Retrieve files via rsync using SSH');

    print $q->p ($q->a ({href => $q->url (-absolute => 1) . "?state=modifyrsync&&rsync=new"},
                    'Click here'),
                    'to add a transfer set.');

    print "<table border=1 cellspacing=1 cellpadding=4>";

    #header
    print $q->Tr (esmith::cgi::genSmallCell ($q, $q->b ('Description')),
                  esmith::cgi::genSmallCell ($q, $q->b ('Frequency')),
                  esmith::cgi::genSmallCell ($q, $q->b ('Time')),
                  $q->td ('&nbsp;'),
                  $q->td ('&nbsp;'));

    #get rsyncs/shares
    my @rsyncs = ();
    foreach (sort keys %dungog)
    {
        push (@rsyncs, $_)
        if (db_get_type(\%dungog, $_) eq "rsync");
    }

    my $rsync = '';
    foreach $rsync (sort @rsyncs)
    {
        my $desc    = db_get_prop(\%dungog, $rsync, "desc")    || '';
        my $freq    = db_get_prop(\%dungog, $rsync, "freq")    || '';
        my $day     = db_get_prop(\%dungog, $rsync, "day")     || '';
        my $hour    = db_get_prop(\%dungog, $rsync, "hour")    || '';
        my $min     = db_get_prop(\%dungog, $rsync, "min")     || '';

        #modify frequency value for display
        my $daylabel = ($Labels{$day});
        if ($freq eq 'hourly')
        {
          $hour = '*';
        }
        elsif ($freq eq 'on')
        {
          $freq = $daylabel;
        }

        if ($freq eq 'off')
        {
          print $q->Tr (esmith::cgi::genSmallCell ($q, $desc),
                        esmith::cgi::genSmallCell ($q, $freq),
                        esmith::cgi::genSmallCell ($q, ''),
                        esmith::cgi::genSmallCell ($q,
                        $q->a ({href => $q->url (-absolute => 1)
                                 . "?state=modifyrsync&&rsync="
                                 . $rsync}, 'Modify/Run...')),
                        esmith::cgi::genSmallCell ($q,
                        $q->a ({href => $q->url (-absolute => 1)
                                 . "?state=deletersync&&rsync="
                                       . $rsync}, 'Remove...')));
        }
        else
        {
          print $q->Tr (esmith::cgi::genSmallCell ($q, $desc),
                      esmith::cgi::genSmallRedCell ($q, $freq),
                      esmith::cgi::genSmallRedCell ($q, $hour.':'.$min),
                      esmith::cgi::genSmallCell ($q,
                      $q->a ({href => $q->url (-absolute => 1)
                                 . "?state=modifyrsync&&rsync="
                                 . $rsync}, 'Modify/Run...')),
                      esmith::cgi::genSmallCell ($q,
                      $q->a ({href => $q->url (-absolute => 1)
                                 . "?state=deletersync&&rsync="
                                 . $rsync}, 'Remove...')));
        }
    }

    print '</table>';
    
    print '<br />';
    
    #configure and select set
    print $q->h4 ('Transfer Sets Summary - Anonymous rsync retrieve from remote');

    print $q->p ($q->a ({href => $q->url (-absolute => 1) . "?state=showAnon&&rsync=new"},
                   'Click here'),
                   'to add a transfer set.');

    print "<table border=1 cellspacing=1 cellpadding=4>";

    #header
    print $q->Tr (esmith::cgi::genSmallCell ($q, $q->b ('Description')),
                esmith::cgi::genSmallCell ($q, $q->b ('Frequency')),
                esmith::cgi::genSmallCell ($q, $q->b ('Time')),
                $q->td ('&nbsp;'),
                $q->td ('&nbsp;'));

    #get rsyncs/shares
    my @rsyncAnon = ();
    foreach (sort keys %dungog)
    {
      push (@rsyncAnon, $_)
      if (db_get_type(\%dungog, $_) eq "rsyncAnon");
    }

    $rsync = '';
    foreach $rsync (sort @rsyncAnon)
    {
      my $desc    = db_get_prop(\%dungog, $rsync, "desc")    || '';
      my $freq    = db_get_prop(\%dungog, $rsync, "freq")    || '';
      my $day     = db_get_prop(\%dungog, $rsync, "day")     || '';
      my $hour    = db_get_prop(\%dungog, $rsync, "hour")    || '';
      my $min     = db_get_prop(\%dungog, $rsync, "min")     || '';

      #modify frequency value for display
      my $daylabel = ($Labels{$day});
      if ($freq eq 'hourly')
      {
        $hour = '*';
      }
      elsif ($freq eq 'on')
      {
        $freq = $daylabel;
      }

      if ($freq eq 'off')
      {
        print $q->Tr (esmith::cgi::genSmallCell ($q, $desc),
                      esmith::cgi::genSmallCell ($q, $freq),
                      esmith::cgi::genSmallCell ($q, ''),
                      esmith::cgi::genSmallCell ($q,
                      $q->a ({href => $q->url (-absolute => 1)
                               . "?state=showAnon&&rsync="
                               . $rsync}, 'Modify/Run...')),
                      esmith::cgi::genSmallCell ($q,
                      $q->a ({href => $q->url (-absolute => 1)
                               . "?state=deletersync&&rsync="
                                     . $rsync}, 'Remove...')));
      }
      else
      {
        print $q->Tr (esmith::cgi::genSmallCell ($q, $desc),
                    esmith::cgi::genSmallRedCell ($q, $freq),
                    esmith::cgi::genSmallRedCell ($q, $hour.':'.$min),
                    esmith::cgi::genSmallCell ($q,
                    $q->a ({href => $q->url (-absolute => 1)
                               . "?state=showAnon&&rsync="
                               . $rsync}, 'Modify/Run...')),
                    esmith::cgi::genSmallCell ($q,
                    $q->a ({href => $q->url (-absolute => 1)
                               . "?state=deletersync&&rsync="
                               . $rsync}, 'Remove...')));
      }
    }
    print '</table>';
    
    print '<br />';
    
    #configure and select set
    print $q->h4 ('Configure your server as an Anonymous Rsync server');

    # check to see if e-smith-rsyncd is installed
    # old code - unless ( -e "/etc/e-smith/events/actions/conf-rsyncd" )
    # use existing rsync key to decide whether the following section can be configured
    # test for rsyncd property
    my $rsyncd_status = db_get(\%conf, "rsyncd");
    
    unless ($rsyncd_status ne '')
    {
      print $q->p (esmith::cgi::genSmallCell ($q, 'To make files available for download by
              Anonymous Rsync to you need to install
              smeserver-shared-folders, see help below'));
    }
    else
    {
      print $q->p ($q->a ({href => $q->url (-absolute => 1) . "?state=showibay"},
                'Click here'),
                'to view which ibays are setup to allow anonymous rsync
                downloads. You can also enable or disable anonymous rsync to your server');
    }

    print $q->endform;
    print $q->p ($q->hr, $q->font ({size => "-1"}, "Copyright dungog.net",
          $q->a ({href => $q->url (-absolute => 1) . "?state=help"}, 'Rsync help'), " ..."));

    print '</FONT>';
    print '</DIV>';
    print $q->end_html;
}

#    -------------********* secure Sets ***********---------------------------

sub modifyRsync ($$)
{
    my ($q, $msg) = @_;
    my $rsync = $q->param ('rsync') ||'';
    my $editlocaldir     = $q->param ('editlocaldir')     ||'';
    my $editremoteserver = $q->param ('editremoteserver') ||'';
    my $editremotedir    = $q->param ('editremotedir')    ||'';

    if ($msg eq '')
    {
          esmith::cgi::genHeaderNonCacheable
            ($q, \%conf, 'Modify the selected rsync job.');
    }
    else
    {
          esmith::cgi::genHeaderNonCacheable
            ($q, \%conf, 'Modify the selected rsync job.');

        print $q->p ($msg);
        print $q->hr;
    }

    if ($rsync eq 'new')
    {
       my $random = int(rand(999999));
       $rsync = 'rsync'.$random;
    }

    # find system users
    my @userlist = ('');
    foreach (keys %accounts)
    {
       push (@userlist, $_)
           if (db_get_type(\%accounts, $_) eq 'user');
    }

    my $localdirlist = db_get_prop(\%dungog, "$rsync", "localdirlist") || 'none';
    my @localdirlist = split(/,/, $localdirlist);
    my $remoteserverlist = db_get_prop(\%dungog, "$rsync", "remoteserverlist") || 'none';
    my @remoteserverlist = split(/,/, $remoteserverlist);
    my $remotedirlist = db_get_prop(\%dungog, "$rsync", "remotedirlist") || 'none';
    my @remotedirlist = split(/,/, $remotedirlist);

    print $q->startform (-method => 'POST', -action => $q->url (-absolute => 1));

    print $q->table ({border => 0, cellspacing => 0, cellpadding => 4},

        esmith::cgi::genTextRow ($q, $q->p ($q->a ({href => $q->url (-absolute => 1) . "?state=help"},
                'Please review'),
           "the help page for explanations and suggestions on how to setup.")),

        esmith::cgi::genNameValueRow ($q,
                      "Description",
                      "desc",
                      db_get_prop(\%dungog, $rsync, "desc") || '',),

        esmith::cgi::genWidgetRow ($q, "Select a user for rsync to run as",
               $q->popup_menu (-name    => "user",
                               -values  => [ sort @userlist , 'root' ],
                               -default => db_get_prop(\%dungog, $rsync, "user") || '')),

        esmith::cgi::genWidgetRow ($q, "Method",
               $q->popup_menu (-name    => "method",
                               -values  => ['senddirs','sendservers','receivedirs','receiveservers','senddirs2self'],
                               -default => db_get_prop(\%dungog, $rsync, "method") || '',
                               -labels => \%Labels2)),

        esmith::cgi::genWidgetRow ($q, "Options",
               $q->popup_menu (-name    => "options",
                               -values  => ['test','verbose','stats','quiet'],
                               -default => db_get_prop(\%dungog, $rsync, "options") || 'test',
                               -labels => \%Labels5)));

        my $delete = db_get_prop(\%dungog, $rsync, "delete") || 'no';
        my $tick = '';
	      if ($delete eq 'on')
        {
          $tick = "checked";
        }

        print $q->Tr ($q->td ("<input type=\"checkbox\" name=\"delete\" $tick>"),
		      esmith::cgi::genTextRow ($q, ('Delete files that do not exist on the sending side. <font color="red">Always use Testing option first.</font>')));

        my $compress = db_get_prop(\%dungog, $rsync, "compress") || 'no';
        $tick = '';
	      if ($compress eq 'on')
        {
          $tick = "checked";
        }

        print $q->Tr ($q->br);
        print $q->Tr ($q->td ("<input type=\"checkbox\" name=\"compress\" $tick>"),
		      esmith::cgi::genTextRow ($q, ('Compress file data. This option is useful on slow links.')));

        my $mail = db_get_prop(\%dungog, $rsync, "mail") || '';
        $tick = '';
	      if ($mail eq 'on')
        {
          $tick = "checked";
        }

        print $q->Tr ($q->br);
        print $q->Tr ($q->td ("<input type=\"checkbox\" name=\"mail\" $tick>"),
		      esmith::cgi::genTextRow ($q, ('If automating, email any output to admin.')));

        my $dbdump = db_get_prop(\%dungog, $rsync, "dbdump") || '';
        $tick = '';
	      if ($dbdump eq 'on')
        {
          $tick = "checked";
        }

        print $q->Tr ($q->br);
        print $q->Tr ($q->td ("<input type=\"checkbox\" name=\"dbdump\" $tick>"),
		      esmith::cgi::genTextRow ($q, ('Include mysql tables in your transfer -
		      requires you to send the dir /home/e-smith/db/mysql or a parent dir.')));

        print $q->p (esmith::cgi::genWidgetRow ($q, "Limit I/O bandwidth, KBytes per second",
                      $q->popup_menu (-name    => 'bwlimit',
                           -values  => ['none','10','25','50','100','500','1000','2000','4000','6000','10000','20000'],
                           -default => db_get_prop(\%dungog, $rsync, "bwlimit") || 'none',
                           -labels => \%Labels4)));

        print $q->table ({border => 0, cellspacing => 0, cellpadding => 4},

        esmith::cgi::genTextRow ($q,  ("Create /opt/$rsync.ex and/or /opt/$rsync.in 
                                    to exclude or include directories or files from this rsync job, 
                                    see help below. If either file exists the options are added into the script below after a panel save.")),

        esmith::cgi::genTextRow ($q,  ('To run a command or shell script before or after the
                                   rsync job enter the command below. Leave blank for no command.')),

        esmith::cgi::genNameValueRow ($q,
                      "Pre rsync command, eg, /usr/bin/moveaway",
                      "pre",
                      db_get_prop(\%dungog, $rsync, "pre") || '',),

        esmith::cgi::genNameValueRow ($q,
                      "Post rsync command, eg, /usr/bin/moveback",
                      "post",
                      db_get_prop(\%dungog, $rsync, "post") || '',),

        esmith::cgi::genTextRow ($q, $q->p ("<b>Local server - $conf{SystemName}.$conf{DomainName}</b>")),

        #local directory
        esmith::cgi::genTextRow ($q, $q->p ("<b>Local directory</b>")));
        print "<table border=1 cellspacing=1 cellpadding=4>";
        foreach (sort @localdirlist)
        {
          print $q->Tr (esmith::cgi::genSmallCell ($q, $_),
                        esmith::cgi::genSmallCell ($q,
                        $q->a ({href => $q->url (-absolute => 1)
                                 . "?state=modifyrsync&&rsync=$rsync&&editlocaldir="
                                 . $_}, 'Copy & Edit... ')),
                        esmith::cgi::genSmallCell ($q,
                        $q->a ({href => $q->url (-absolute => 1)
                                 . "?state=deleteitem&&type=localdir&&rsync=$rsync&&item="
                                 . $_}, 'Remove...')));
        }
        print '</table>';

        print $q->table ({border => 0, cellspacing => 0, cellpadding => 4},
        esmith::cgi::genNameValueRow ($q,
                      "add directory, eg,/home/e-smith/files/ibays/vital",
                      "localdir",
                      "$editlocaldir",),

        #remote servers
        esmith::cgi::genTextRow ($q, $q->p ('<b>Remote user@server</b> user is required. Can be different to local user<br />
                                            See Help - Changing the port for using different ports')));

        print $q->table ({border => 1, cellspacing => 1, cellpadding => 4});
        foreach (sort @remoteserverlist)
        {
          print $q->Tr (esmith::cgi::genSmallCell ($q, $_),
                        esmith::cgi::genSmallCell ($q,
                        $q->a ({href => $q->url (-absolute => 1)
                                 . "?state=modifyrsync&&rsync=$rsync&&editremoteserver="
                                 . $_}, 'Copy & Edit... ')),
                        esmith::cgi::genSmallCell ($q,
                        $q->a ({href => $q->url (-absolute => 1)
                                 . "?state=deleteitem&&type=remoteserver&&rsync=$rsync&&item="
                                 . $_}, 'Remove...')));
        }
        print '</table>';

        print $q->table ({border => 0, cellspacing => 0, cellpadding => 4},
        esmith::cgi::genNameValueRow ($q,
                      "add server",
                      "remoteserver",
                      "$editremoteserver",),

        #remote directory
        esmith::cgi::genTextRow ($q, $q->p ('<b>Remote directory</b>')));

        print $q->table ({border => 1, cellspacing => 1, cellpadding => 4});
        foreach (sort @remotedirlist)
        {
          print $q->Tr (esmith::cgi::genSmallCell ($q, $_),
                        esmith::cgi::genSmallCell ($q,
                        $q->a ({href => $q->url (-absolute => 1)
                                 . "?state=modifyrsync&&rsync=$rsync&&editremotedir="
                                 . $_}, 'Copy & Edit... ')),
                        esmith::cgi::genSmallCell ($q,
                        $q->a ({href => $q->url (-absolute => 1)
                                 . "?state=deleteitem&&type=remotedir&&rsync=$rsync&&item="
                                 . $_}, 'Remove...')));
        }
        print '</table>';

        print $q->table ({border => 0, cellspacing => 0, cellpadding => 4},
        esmith::cgi::genNameValueRow ($q,
                      "add directory",
                      "remotedir",
                      "$editremotedir",));

        #extra table just to align fields better
        print '</table>';
        print $q->table ({border => 0, cellspacing => 0, cellpadding => 4},

        #time for automation
        #esmith::cgi::genTextRow ($q, $q->p ('<b>Timing</b>')),

        esmith::cgi::genTextRow ($q, $q->p ('Select the time and enable to automate<br>
                                 Alternatively add this job to cron using the Cron panel, the command to give is <br>
                                 '."/usr/bin/dungogrsync-$rsync")),

        esmith::cgi::genWidgetRow ($q, "Automatic backups",
                        $q->popup_menu (-name    => 'freq',
                                        -values  => ['off','on','hourly'] ,
                                        -default => db_get_prop(\%dungog, $rsync, "freq") || 'off',)),

        esmith::cgi::genWidgetRow ($q, "Backup time, minute",
                        $q->popup_menu (-name    => 'min',
                                        -values  => ['1','4','7','10','13','16','19','22','25','28','31','33','36','39','42','45','48','51','54','57','59'] ,
                                        -default => db_get_prop(\%dungog, $rsync, "min") || '16',)),

        esmith::cgi::genWidgetRow ($q, "Backup time, 24 hour",
                        $q->popup_menu (-name    => 'hour',
                                        -values  => ['*/2','*/4','*/8','*/12','8-18','9-17','1','2','3','4','5','6','7','8','9','10','11','12','13','14','15','16','17','18','19','20','21','22','23'] ,
                                        -default => db_get_prop(\%dungog, $rsync, "hour") || '3',
                                        -labels  => \%Labels3)),

        esmith::cgi::genWidgetRow ($q, "Backup time, day of week",
                        $q->popup_menu (-name    => 'day',
                                        -values  => ['*','1-5','0,6','sun','1','2','3','4','5','6'] ,
                                        -default => db_get_prop(\%dungog, $rsync, "day") || '4',
                                        -labels  => \%Labels)));

    print $q->Tr (esmith::cgi::genButtonRow ($q,
      $q->submit (-name => 'action', -value => 'Save Settings')));

    print $q->Tr (esmith::cgi::genTextRow ($q, $q->p ('After saving your settings
                     you can run the backup immediately by clicking below.
                     You should also save after creating or deleting includes or excludes
                     or relevant db commands eg override allow')));

    print $q->Tr (esmith::cgi::genButtonRow ($q,
      $q->submit (-name => 'action', -value => 'Run this rsync job now')));

    if ( -e "/usr/bin/dungogrsync-$rsync" )
    {
      print $q->h4 ("Current rsync script file - /usr/bin/dungogrsync-$rsync");
      print "<table border=1 cellspacing=1 cellpadding=4>";

      print '<font color = "green">';
      print "<pre>";
      open (INF,"/usr/bin/dungogrsync-$rsync")
      or die ("can't open rsync script: $1");

      while (<INF>)
      {
          print "$_";
      }

      close INF;
      print "</pre>\n";
    }

    print '</font>';
    print '<font color = "black">';
    print $q->hidden (-name => 'state', -override => 1, -default => 'choosesub');
    print $q->hidden (-name => 'rsync', -override => 1, -default => $rsync);

    print $q->endform;
    print $q->p ($q->hr, $q->font ({size => "-1"}, "Copyright dungog.net",
          $q->a ({href => $q->url (-absolute => 1) . "?state=help"}, 'Rsync help'), " ..."));

    print '</FONT>';
    print '</DIV>';
    print $q->end_html;
}

sub performModifyRsync ($)
{
    my ($q) = @_;
    my $rsync        = $q->param ('rsync');
    my $desc         = $q->param ('desc')        || '';
    my $method       = $q->param ('method')      || '';
    my $options      = $q->param ('options')     || '';
    my $delete       = $q->param ('delete')      || '';
    my $compress     = $q->param ('compress')    || '';
    my $mail         = $q->param ('mail')        || '';
    my $dbdump       = $q->param ('dbdump')      || '';
    my $pre          = $q->param ('pre')         || '';
    my $post         = $q->param ('post')        || '';
    my $user         = $q->param ('user')        || '';
    my $localdir     = $q->param ('localdir')    || '';
    my $remoteserver = $q->param ('remoteserver')|| '';
    my $remotedir    = $q->param ('remotedir')   || '';
    my $freq         = $q->param ('freq')        || '';
    my $hour         = $q->param ('hour')        || '';
    my $min          = $q->param ('min')         || '';
    my $day          = $q->param ('day')         || '';
    my $bwlimit      = $q->param ('bwlimit')     || '';

    if ($rsync =~ /^([a-z][\-\_\.a-z0-9]*)$/)
    {
        $rsync = $1;
    }

    #check for invalid characters
    foreach ($localdir, $remoteserver, $remotedir)
    {
      if ($_ =~ /^([\/\-\_\.\ \"\@A-Za-z0-9]*)$/)
      {
        $_ = $1;
      }
      else
      {
        modifyRsync ($q, "Error: You have invalid characters [$_], use only /\-\_\.A-Za-z0-9");
        return;
      }
    }

    #test that non blank entries are directories or files, then add to $localdirlist
    if ( $localdir ne '' )
    {
      my $test = $localdir;
      $test =~ s/"//g;
      unless ( -e $test )
      {
        modifyRsync ($q, "Error, local server directory or file does not exist. [$test]");
        return;
      }
      else
      {
        my $localdirlist = db_get_prop(\%dungog, $rsync, "localdirlist");
        db_set_prop(\%dungog, $rsync, "localdirlist", "$localdir,$localdirlist");
      }
    }

    #add non blank entries  to $remoteserverlist
    if ( $remoteserver ne '' )
    {
      my $remoteserverlist = db_get_prop(\%dungog, $rsync, "remoteserverlist");
      db_set_prop(\%dungog, $rsync, "remoteserverlist", "$remoteserver,$remoteserverlist");
    }

    #add non blank entries  to $remotedirlist
    if ( $remotedir ne '' )
    {
      my $remotedirlist = db_get_prop(\%dungog, $rsync, "remotedirlist");
      db_set_prop(\%dungog, $rsync, "remotedirlist", "$remotedir,$remotedirlist");
    }

    unless (exists $dungog {$rsync})
    {
      db_set(\%dungog, $rsync, 'rsync');
    }
    db_set_prop(\%dungog, $rsync, "desc",     $desc);
    db_set_prop(\%dungog, $rsync, "method",   $method);
    db_set_prop(\%dungog, $rsync, "options",  $options);
    db_set_prop(\%dungog, $rsync, "delete",   $delete);
    db_set_prop(\%dungog, $rsync, "compress", $compress);
    db_set_prop(\%dungog, $rsync, "user",     $user);
    db_set_prop(\%dungog, $rsync, "mail",     $mail);
    db_set_prop(\%dungog, $rsync, "dbdump",   $dbdump);
    db_set_prop(\%dungog, $rsync, "pre",      $pre);
    db_set_prop(\%dungog, $rsync, "post",     $post);
    db_set_prop(\%dungog, $rsync, "freq",     $freq);
    db_set_prop(\%dungog, $rsync, "day",      $day);
    db_set_prop(\%dungog, $rsync, "hour",     $hour);
    db_set_prop(\%dungog, $rsync, "min",      $min);
    db_set_prop(\%dungog, $rsync, "bwlimit",  $bwlimit);

    system ("/sbin/e-smith/signal-event conf-dungogrsync $rsync") == 0
        or die ("Error occurred while updating system configuration.\n");

    system ("/sbin/e-smith/expand-template /etc/crontab") == 0
        or die ("Error occurred expanding crontab.\n");

    modifyRsync ($q, 'settings saved');
    return;

}

#    -------------********* secure Sets ***********---------------------------

sub showAnon ($$)
{
    my ($q, $msg) = @_;
    my $rsync = $q->param ('rsync') ||'';

    if ($msg eq '')
    {
          esmith::cgi::genHeaderNonCacheable
            ($q, \%conf, 'Modify the selected rsync job.');
    }
    else
    {
          esmith::cgi::genHeaderNonCacheable
            ($q, \%conf, 'Modify the selected rsync job.');

        print $q->p ($msg);
        print $q->hr;
    }

    if ($rsync eq 'new')
    {
       my $random = int(rand(999999));
       $rsync = 'rsync'.$random;
    }

    print $q->startform (-method => 'POST', -action => $q->url (-absolute => 1));

    print $q->table ({border => 0, cellspacing => 0, cellpadding => 4},

        esmith::cgi::genTextRow ($q, $q->p ('<b>General</b>')),

        esmith::cgi::genTextRow ($q, $q->p ('This form allows you to mirror a set of files from a remote
                                             server to your local server, when the remote files are being made
                                             available with a rsync server.')),

        esmith::cgi::genNameValueRow ($q,
                      "Description",
                      "desc",
                      db_get_prop(\%dungog, $rsync, "desc") || '',),

        esmith::cgi::genWidgetRow ($q, "Options",
               $q->popup_menu (-name    => "options",
                               -values  => ['test','verbose','stats','quiet'],
                               -default => db_get_prop(\%dungog, $rsync, "options") || 'stats',
                               -labels => \%Labels5)));

        my $compress = db_get_prop(\%dungog, $rsync, "compress") || 'no';
        my $tick = '';
	      if ($compress eq 'on')
        {
          $tick = "checked";
        }

        print $q->Tr ($q->br);
        print $q->Tr ($q->td ("<input type=\"checkbox\" name=\"compress\" $tick>"),
		      esmith::cgi::genTextRow ($q, ('Compress file data, This option is useful on slow links.')));


        my $mail = db_get_prop(\%dungog, $rsync, "mail") || '';
        $tick = '';
	      if ($mail eq 'on')
        {
          $tick = "checked";
        }

        print $q->Tr ($q->br);
        print $q->Tr ($q->td ("<input type=\"checkbox\" name=\"mail\" $tick>"),
		      esmith::cgi::genTextRow ($q, ('If automating, email any output to admin.')));

        print $q->p (esmith::cgi::genWidgetRow ($q, "limit I/O bandwidth, KBytes per second",
                      $q->popup_menu (-name    => 'bwlimit',
                           -values  => ['none','1','2.5','5','10','25','50','100','1000'],
                           -default => db_get_prop(\%dungog, $rsync, "bwlimit") || 'none',
                           -labels => \%Labels4)));

        print $q->Tr ($q->br);

        print $q->Tr (esmith::cgi::genTextRow ($q, $q->p ('<b>Locations</b>')));

        #remote server
        print $q->Tr (esmith::cgi::genTextRow ($q, $q->p ('The remote server is the source you will be mirroring,
                               it will be in the form domain.dom::directory')));

        print $q->Tr (esmith::cgi::genNameValueRow ($q,
                      "Remote server and directory",
                      "remoteserver",
                      db_get_prop(\%dungog, $rsync, "remoteserver") || '',));

        print $q->Tr (esmith::cgi::genTextRow ($q, $q->p ('The local directory is the destination for the copy
                               enter an existing directory on your local server,
                               eg. /home/e-smith/files/ibays/anibay/files/data')));
        #local directory
        print $q->Tr (esmith::cgi::genNameValueRow ($q,
                      "Local directory",
                      "localdir",
                      db_get_prop(\%dungog, $rsync, "localdir") || '',));

        #extra table just to align fields better
        print '</table>';
        print $q->table ({border => 0, cellspacing => 0, cellpadding => 4},

        #time for automation
        #esmith::cgi::genTextRow ($q, $q->p ('<b>Timing</b>')),

        esmith::cgi::genTextRow ($q, $q->p ('Select the time, and day or days to run.
                                             Hourly runs each hour at the nominated minutes past the hour.
                                             Enabling automatic backups by selecting on,
                                             allows you to choose which days to run.')),

        esmith::cgi::genWidgetRow ($q, "Automatic backups",
                        $q->popup_menu (-name    => 'freq',
                                        -values  => ['off','on','hourly'] ,
                                        -default => db_get_prop(\%dungog, $rsync, "freq") || 'off',)),

        esmith::cgi::genWidgetRow ($q, "Backup time, minute",
                        $q->popup_menu (-name    => 'min',
                                        -values  => ['1','4','7','10','13','16','19','22','25','28','31','33','36','39','42','45','48','51','54','57','59'] ,
                                        -default => db_get_prop(\%dungog, $rsync, "min") || '16',)),

        esmith::cgi::genWidgetRow ($q, "Backup time, 24 hour",
                        $q->popup_menu (-name    => 'hour',
                                        -values  => ['1','2','3','4','5','6','7','8','9','10','11','12','13','14','15','16','17','18','19','20','21','22','23'] ,
                                        -default => db_get_prop(\%dungog, $rsync, "hour") || '3',
                                        -labels  => \%Labels3)),

        esmith::cgi::genWidgetRow ($q, "Backup time, day of week",
                        $q->popup_menu (-name    => 'day',
                                        -values  => ['*','1-5','0,6','sun','1','2','3','4','5','6'] ,
                                        -default => db_get_prop(\%dungog, $rsync, "day") || '4',
                                        -labels  => \%Labels)),

        esmith::cgi::genTextRow ($q, $q->p ('Click Save to enable the above settings')));

    print $q->Tr (esmith::cgi::genButtonRow ($q,
      $q->submit (-name => 'action', -value => 'Save Job')));

    print $q->Tr (esmith::cgi::genTextRow ($q, $q->p ('After saving your settings
                     you can run the backup immediately by clicking below.')));

    print $q->Tr (esmith::cgi::genButtonRow ($q,
      $q->submit (-name => 'action', -value => 'Run this rsync job now')));

    print $q->h4 ('Current rsync script file');

    if ( -e "/usr/bin/dungogrsync-$rsync" )
    {
      print "<table border=1 cellspacing=1 cellpadding=4>";

      print '<font color = "green">';
      print "<pre>";
      open (INF,"/usr/bin/dungogrsync-$rsync")
      or die ("can't open rsync script: $1");

      while (<INF>)
      {
          print "$_";
      }

      close INF;
      print "</pre>\n";
    }

    print '</font>';
    print '<font color = "black">';
    print $q->hidden (-name => 'state', -override => 1, -default => 'choosesub');
    print $q->hidden (-name => 'rsync', -override => 1, -default => $rsync);

    print $q->endform;
    print $q->p ($q->hr, $q->font ({size => "-1"}, "Copyright dungog.net",
          $q->a ({href => $q->url (-absolute => 1) . "?state=help"}, 'Rsync help'), " ..."));

    print '</FONT>';
    print '</DIV>';
    print $q->end_html;
}

sub performShowAnon ($)
{
    my ($q) = @_;
    my $rsync        = $q->param ('rsync');
    my $desc         = $q->param ('desc')        || '';
    my $options      = $q->param ('options')     || '';
    my $compress     = $q->param ('compress')    || '';
    my $mail         = $q->param ('mail')        || '';
    my $localdir     = $q->param ('localdir')    || '';
    my $remoteserver = $q->param ('remoteserver')|| '';
    my $freq         = $q->param ('freq')        || '';
    my $hour         = $q->param ('hour')        || '';
    my $min          = $q->param ('min')         || '';
    my $day          = $q->param ('day')         || '';
    my $bwlimit      = $q->param ('bwlimit')     || '';

    if ($rsync =~ /^([a-z][\-\_\.a-z0-9]*)$/)
    {
        $rsync = $1;
    }

    if ($desc =~ /^([a-z][\-\_\.a-z0-9]*)$/)
    {
        $desc = $1;
    }

    unless (exists $dungog {$rsync})
    {
      db_set(\%dungog, $rsync, 'rsyncAnon');
    }
    db_set_prop(\%dungog, $rsync, "desc",         $desc);
    db_set_prop(\%dungog, $rsync, "options",      $options);
    db_set_prop(\%dungog, $rsync, "compress",     $compress);
    db_set_prop(\%dungog, $rsync, "mail",         $mail);
    db_set_prop(\%dungog, $rsync, "freq",         $freq);
    db_set_prop(\%dungog, $rsync, "day",          $day);
    db_set_prop(\%dungog, $rsync, "hour",         $hour);
    db_set_prop(\%dungog, $rsync, "min",          $min);
    db_set_prop(\%dungog, $rsync, "bwlimit",      $bwlimit);

    #check for invalid characters
    if ($remoteserver =~ /^([\/\-\_\.A-Za-z0-9]*::[\/\-\_\.A-Za-z0-9]*)$/)
    {
      $_ = $1;
    }
    else
    {
      showAnon ($q, "Error: You have invalid characters for the remote server [$remoteserver], ".
                    "use only /\-\_\.A-Za-z0-9 with the domain and directory separated by ::");
      return;
    }

    db_set_prop(\%dungog, $rsync, "remoteserver", $remoteserver);

    if ($localdir =~ /^([\/\-\_\.A-Za-z0-9]*)$/)
    {
      $_ = $1;
    }
    else
    {
      showAnon ($q, "Error: You have invalid characters in the local directory [$localdir], use only /\-\_\.A-Za-z0-9");
      return;
    }

    #test that local directory exists
    unless ( -e "$localdir" )
    {
      showAnon ($q, "Error, local server directory does not exist. [$localdir]");
      return;
    }

    db_set_prop(\%dungog, $rsync, "localdir", $localdir);

    system ("/sbin/e-smith/signal-event conf-dungogrsyncanon $rsync") == 0
        or die ("Error occurred while updating system configuration.\n");

    system ("/sbin/e-smith/expand-template /etc/crontab") == 0
        or die ("Error occurred expanding crontab.\n");

    showAnon ($q, 'settings saved');
    return;}


#   -------------   ibay anon setup ------------------------

sub showIbay ($$)
{
    my ($q, $msg) = @_;

    if ($msg eq '')
    {
          esmith::cgi::genHeaderNonCacheable
            ($q, \%conf, 'Anonymous Rsync');
    }
    else
    {
        esmith::cgi::genHeaderNonCacheable
            ($q, \%conf, 'Anonymous Rsync');

        print $q->h4 ('Operation status report');
        print $q->p ($msg);
        print $q->hr;
    }

    print $q->startform (-method => 'POST', -action => $q->url (-absolute => 1));

    #configure and select set
    print $q->h4 ('Shared Folders');

    # setup ibays to be used for anonymous ibay
    my @ibayAccounts = ();
    foreach (sort keys %accounts)
    {
      push (@ibayAccounts, $_)
          if (db_get_type(\%accounts, $_) eq "share");
    }

    unless (scalar @ibayAccounts)
    {
      print $q->p ($q->b ('There are no shared folders in the system.'));
    }
    else
    {
      #short intro
      print $q->p ($q->p ('<font style color="red">This is not fully functioning currently.</font></br>
	         Shared Folders shown in red have Global Access set to <br />
           <p>Use the <a href=shares>Shared Folders</a> panel for finer control</P>'));

      unless ( -e '/etc/rsyncd.conf')
      {
          print $q->p ($q->p ('This is not fully functioning currently.<br>
                               It requires smeserver-shared-folders to enable rsyncd<br>
                               Work still required.'
                               ));
      }

      print $q->p ($q->a ({href => $q->url (-absolute => 1) . "?state=recentRsync"},
                   'Click here'),
                   'to view logs of recent access to your rsync server.');

      print "<table border=1 cellspacing=1 cellpadding=4>";

      print $q->Tr (esmith::cgi::genSmallCell ($q, $q->b ('Name')),
                    esmith::cgi::genSmallCell ($q, $q->b ('Description')),
                    esmith::cgi::genSmallCell ($q, $q->b ('Status')),
                    esmith::cgi::genSmallCell ($q, $q->b ('Modify'))
                   );

      my $ibay = '';
      foreach $ibay (sort @ibayAccounts)
      {
	      unless ($ibay eq 'Primary')
	      {
          my $name = db_get_prop(\%accounts, "$ibay", 'Name') || '';
          my $rsyncAccess = db_get_prop(\%accounts, "$ibay", "rsyncAccess") || '';
          
# vvv This whole section can probably go unless we want to set perms on a shared folder
#          my $PublicAccess = db_get_prop(\%accounts, "$ibay", "PublicAccess") || '';
#          my $Group = db_get_prop(\%accounts, "$ibay", "Group") || '';
#          my $UserAccess = db_get_prop(\%accounts, "$ibay", "UserAccess") || '';
#          my $perms ='check';

#          if ($UserAccess eq 'wr-group-rd-everyone')
#          {
#              $perms = 'ok';
#          }
#          elsif ($Group eq 'shared')
#          {
#              $perms = 'ok';
#          }
# ^^^ To here 
# Not sure about this - we should be able to toggle access for each share

print "<br />";

          unless ($rsyncAccess eq 'global')
          {
              print $q->Tr (esmith::cgi::genSmallCell ($q, $ibay),
                            esmith::cgi::genSmallCell ($q, $name),
                            esmith::cgi::genSmallCell ($q, $rsyncAccess),
                            esmith::cgi::genSmallCell ($q,
                              $q->a ({href => $q->url (-absolute => 1)
                                 . "?state=modifyibay&&rsyncAccess=global&share="
                                 . $ibay}, 'Set Global...')));
          }
          else
          {
              print $q->Tr (esmith::cgi::genSmallRedCell ($q, $ibay),
                            esmith::cgi::genSmallRedCell ($q, $name),
                            esmith::cgi::genSmallRedCell ($q, $rsyncAccess),
                            esmith::cgi::genSmallCell ($q,
                             $q->a ({href => $q->url (-absolute => 1)
                                        . "?state=modifyibay&&rsyncAccess=local&share="
                                        . $ibay}, 'Set Local...')));
          }
        }
      }
      print '</table>';
    }

    # enable/disable rsyncd
    
    print $q->h4 ('Status');
    print $q->p ($q->p ('Disable or enable access to files on your server via Anonymous Rsync'));

    print $q->p (esmith::cgi::genWidgetRow ($q, "Anonymous Rsyncd",
                        $q->popup_menu (-name    => 'status',
                             -values  => ['enabled','disabled'],
                             -default => db_get_prop(\%conf, 'rsyncd', 'status') || 'disabled')));

    print $q->p (esmith::cgi::genButtonRow ($q,
                   $q->submit (-name  => 'action',
                               -value => 'Save')));

    print $q->hidden (-name     => 'state',
                      -override => 1,
                      -default  => 'performAnon');

    print $q->endform;
    print $q->p ($q->hr, $q->font ({size => "-1"}, "Copyright dungog.net",
          $q->a ({href => $q->url (-absolute => 1) . "?state=help"}, 'Rsync help'), " ..."));

    print '</FONT>';
    print '</DIV>';
    print $q->end_html;
}

sub modifyIbay ($)
{
    my ($q) = @_;
    my $ibay         = $q->param ('share')         ||'';
    my $rsyncAccess = $q->param ('rsyncAccess') ||'local';

    if ($ibay =~ /^([a-z][\-\_\.a-z0-9]*)$/)
    {
        $ibay = $1;
    }

    db_set_prop(\%accounts, $ibay, "rsyncAccess", $rsyncAccess);

    system ("/sbin/e-smith/signal-event share-modify $ibay") == 0
        or die ("Error occurred :signal-event share-modify.\n");

    showIbay ($q, "Successfully modified Shared Folder.");
}
#ibay anon
sub performAnon ($)
{
    my ($q) = @_;
    my $status    = $q->param ('status')  ||'';

    if ($status =~ /^([a-z][\-\_\.a-z0-9]*)$/)
    {
        $status = $1;
    }

    unless (exists $conf {'rsyncd'})
    {
     db_set(\%conf, 'rsyncd', 'service');
    }

    db_set_prop(\%conf, 'rsyncd', 'status', $status);
    if ($status eq'enabled') {
        db_set_prop(\%conf, 'rsyncd', 'access', 'public');
    }
    else {
        db_set_prop(\%conf, 'rsyncd', 'access', 'private');
    }
    
    

    system ("/sbin/e-smith/expand-template /etc/rsyncd.conf") == 0
        or die ("Error occurred :expand-template /etc/rsyncd.conf.\n");

    system ("/sbin/e-smith/signal-event remoteaccess-update") == 0
        or die ("Error occurred :signal-event remoteaccess-update.\n");

    showIbay ($q, "Successfully altered status.");
}

sub deleteRsync ($)
{
    my ($q) = @_;
    my $rsync = $q->param ('rsync') ||'';

    if ($rsync =~ /^([a-z][\-\_\.a-z0-9]*)$/)
    {
        $rsync = $1;
    }

    db_delete(\%dungog, "$rsync");

    system ("/sbin/e-smith/signal-event conf-dungogrsync $rsync") == 0
        or die ("Error occurred :conf-dungogrsync $rsync.\n");
    system ("/bin/rm /usr/bin/dungogrsync-$rsync") == 0
        or die ("Error occurred :rm /usr/bin/dungogrsync.\n");

    showInitial ($q, "Successfully removed rsync job.");
}

sub deleteItem ($)
{
    my ($q) = @_;

    my $type  = $q->param ('type') ||'';
    my $item  = $q->param ('item') ||'';
    my $rsync = $q->param ('rsync') ||'';
    my @add  = ();
    my @list = ();

    if ($rsync =~ /^([a-z][\-\_\.a-z0-9]*)$/)
    {
        $rsync = $1;
    }

    if ($type eq 'localdir')
    {
      my $localdirlist = db_get_prop(\%dungog, "$rsync", "localdirlist") || 'none';
      my @localdirlist = split(/,/, $localdirlist);

      foreach (@localdirlist)
      {
        unless ($_ eq $item) { push(@add, "$_") }
        @list = join(',', @add);
      }

      db_set_prop(\%dungog, $rsync, "localdirlist", "@list");
    }

    if ($type eq 'remoteserver')
    {
      my $remoteserverlist = db_get_prop(\%dungog, "$rsync", "remoteserverlist") || 'none';
      my @remoteserverlist = split(/,/, $remoteserverlist);

      foreach (@remoteserverlist)
      {
        unless ($_ eq $item) { push(@add, "$_") }
        @list = join(',', @add);
      }

      db_set_prop(\%dungog, $rsync, "remoteserverlist", "@list");
    }

    if ($type eq 'remotedir')
    {
      my $remotedirlist = db_get_prop(\%dungog, "$rsync", "remotedirlist") || 'none';
      my @remotedirlist = split(/,/, $remotedirlist);

      foreach (@remotedirlist)
      {
        unless ($_ eq $item) { push(@add, "$_") }
        @list = join(',', @add);
      }

      db_set_prop(\%dungog, $rsync, "remotedirlist", "@list");
    }


    system ("/sbin/e-smith/signal-event conf-dungogrsync $rsync") == 0
        or die ("Error occurred while updating system configuration.\n");

    system ("/sbin/e-smith/expand-template /etc/crontab") == 0
        or die ("Error occurred expanding crontab.\n");

    modifyRsync ($q, "Settings saved, $item removed");

}

#backup now any backupset
sub performRsyncNow ($)
{
    my ($q) = @_;
    my $rsync = $q->param ('rsync');
    my $user  = $q->param ('user');

    if ($rsync =~ /^([a-z][\-\_\.a-z0-9]*)$/)
    {
        $rsync = $1;
    }

    if ($user =~ /^([a-z][\-\_\.a-z0-9]*)$/)
    {
        $user = $1;
    }

    esmith::util::backgroundCommand (1, "/sbin/e-smith/signal-event", "run-rsync", "$rsync", "$user");

    esmith::cgi::genHeaderNonCacheable
	      ($q, \%conf, "Backup request initiated.");

    print $q->p ($q->a ({href => $q->url (-absolute => 1) . "?state=rsyncNowResults"},
                'Click here'),
                'for progressive information on the transfer.');

    print $q->endform;
    print $q->p ($q->hr, $q->font ({size => "-1"}, "Copyright dungog.net",
          $q->a ({href => $q->url (-absolute => 1) . "?state=help"}, 'Rsync help'), " ..."));

    print '</FONT>';
    print '</DIV>';
    print $q->end_html;
}

sub rsyncNowResults ($)
{
    my ($q) = @_;

      esmith::cgi::genHeaderNonCacheable
	      ($q, \%conf, "Backup request initiated.");

    print $q->startform (-method => 'POST', -action => $q->url (-absolute => 1));

    print $q->h4 ('Current rsync activity file');

    print $q->p ($q->a ({href => $q->url (-absolute => 1) . "?state=rsyncNowResults"},
                'Click here'),
                'to refresh this page for progessive information.');

    if ( -e "/var/log/dungogrsyncnow" )
    {
      print "<table border=1 cellspacing=1 cellpadding=4>";

      print '<font color = "green">';
      print "<pre>";
      open (INF,"/var/log/dungogrsyncnow")
      or die ("can't open rsync log file: $1");

      while (<INF>)
      {
          print "$_";
      }

      close INF;
      print "</pre>\n";
    }

    print '</font>';
    print '<font color = "black">';
    print $q->endform;
    print $q->p ($q->hr, $q->font ({size => "-1"}, "Copyright dungog.net",
          $q->a ({href => $q->url (-absolute => 1) . "?state=help"}, 'Rsync help'), " ..."));

    print '</FONT>';
    print '</DIV>';
    print $q->end_html;

}

sub recentRsync ($)
{
    my ($q) = @_;

    esmith::cgi::genHeaderNonCacheable
	      ($q, \%conf, "Recent rsync activity");

    system ("/bin/cat /var/log/messages |/bin/grep rsyncd > /tmp/recentrsync") == 0
        or die ("Error occurred viewing usage.\n");

    print $q->startform (-method => 'POST', -action => $q->url (-absolute => 1));

    if ( -e "/tmp/recentrsync" )
    {
      print "<table border=1 cellspacing=1 cellpadding=4>";

      print '<font color = "green">';
      print "<pre>";
      open (INF,"/tmp/recentrsync")
      or die ("can't open rsync log file: $1");

      while (<INF>)
      {
          print "$_";
      }

      close INF;
      print "</pre>\n";
    }

    print '</font>';
    print '<font color = "black">';
    print $q->endform;
    print $q->p ($q->hr, $q->font ({size => "-1"}, "Copyright dungog.net",
          $q->a ({href => $q->url (-absolute => 1) . "?state=help"}, 'Rsync help'), " ..."));

    print '</FONT>';
    print '</DIV>';
    print $q->end_html;

}

sub showHelp ($)
{
    my ($q) = @_;

    esmith::cgi::genHeaderNonCacheable
        ($q, \%conf, 'Rsync Help');

    print $q->startform (-method => 'POST',
                         -action => $q->url (-absolute => 1));

    print $q->table ({border => 0, cellspacing => 0, cellpadding => 4},

        esmith::cgi::genTextRow ($q, $q->p ('<pre>
* <a href="#1">General</a>
* <a href="#2">Secure rsync</a>
* <a href="#2s">SSH keys</a>
* <a href="#3port">Changing the port</a>
* <a href="#3">Rsync Anonymous server</a>
* <a href="#4">Rsync Anonymous client</a>
* <a href="#5">Exclude / Include syntax</a>
* <a href="#6">Advanced</a>

<a name="1"><h3>General</h3></a>
Transfer files between your sme server and other servers with ssh and rsync installed.
Automate the process using a secure ssh connection.
Distribute to or collect data from other servers automatically.

<a name="2"><h3>Secure rsync</h3></a>
<b>Safeguards</b>
To prevent server resources being overused only one rsync job is 
permitted to run at once, this can be overridden with the db command
db dungog setprop rsync overlap allow

Emails to admin warn of this and other errors, and advise of success, please check for them.

<b>Method</b>
For multiple directory or server transfers it could get very confusing,
so only enter multiple entries for one catagory, if you enter extra entries
only one is used. See the advanced section below for workarounds

<b>Notes</b>
You can transfer a single file instead of a directory.
Enclose directories or filenames that include spaces with a double quote

Save the panel settings in stages, it is frustrating to have to re-enter data
when you get an error becasue your local path/filename is wrong 

Remember to remove the extra server or directory record after using the copy&Edit... link

Rsync will attempt to transfer data at the I/O bandwith rate, 
the actual rate depends on network traffic and hardware

<a name="2s"><h3>SSH Keys</h3></a>
To automate the transfer you need to establish a secure connection
without being prompted for a password. To connect with ssh to the user
on a remote server you need to place the public key of whichever user
you are using on this server into ~/.ssh/authorized_keys on the remote user/server.

cd /home/e-smith/files/users/dummy/.ssh
ssh-keygen -t rsa [accept file name id_rsa][don\'t enter a passphrase]
ssh dummy@remote-server.net mkdir .ssh
scp id_rsa.pub dummy@remote-server.net:.ssh/dummy.localserver.pub
ssh dummy@remote-server.net
cd .ssh
cat dummy.localserver.pub >> authorized_keys
[or for the first key
scp id_rsa.pub dummy@remote-server.net:.ssh/authorized_keys ]

now you should be able to "ssh dummy@remote-server.net" without a passord prompt

<a name="3port"><h3>Changing the port</h3></a> to connect to a remote server
 
dungog-rsync and dungog-backup
can use a db setting to find the ssh port of your remote servers

set the port for any server with the following commands

run this once
# /sbin/e-smith/db dungog set remoteport setting

then for each server add a matching server entry
# /sbin/e-smith/db dungog setprop remoteport root@mail.server2.com 2211

verify
# /sbin/e-smith/db dungog show remoteport
  remoteport=setting
     root@mail.server2.com=2211
     root@www.server3.com=2212

<a name="3"><h3>Rsync Anonymous server TODO</h3></a>
Highly experimental - do not use

This used to require e-smith-rsyncd but you can now get rsyncd
functionality via the smeserver-shared-folders contrib

To make files available for download by Anonymous Rsync you place them
in the files directory of an ibay and make it globally accesable.
The panel shows which servers have the correct settings and warns of
possible errors in permissions. It has an option to enable or disable
anonymous rsync access.


<a name="4"><h3>Rsync Anonymous client</h3></a>

Used to pull files from an anonymous rsync server

<a name="5"><h3>Exclude / Include syntax</h3></a>

here is a simple example
# cat /opt/rsync55622.ex 
/home/e-smith/files/ibays/backup/
*.bak

Note. the exlude file is matched first
From man rsync

 --exclude-from=FILE
        This option allows you to selectively exclude  cer-
        tain  files  from  the  list  of files to be trans-
        ferred. This is most useful in combination  with  a
        recursive transfer.

        This option adds all exclude patterns listed in the
        file FILE to the exclude list.  Blank lines in FILE
        and lines starting with ";" or "#" are ignored.  If
        FILE is - the  list  will  be  read  from  standard
        input.

EXCLUDE PATTERNS
       The exclude and include patterns specified to rsync  allow
       for  flexible  selection  of  which  files to transfer and
       which files to skip.

       rsync builds an ordered list of include/exclude options as
       specified  on the command line. When a filename is encoun-
       tered, rsync checks the name against each  exclude/include
       pattern  in  turn. The first matching pattern is acted on.
       If it is an exclude pattern, then that file is skipped. If
       it  is  an  include  pattern  then  that  filename  is not
       skipped. If no matching include/exclude pattern  is  found
       then the filename is not skipped.

       Note  that  when  used  with  -r (which is implied by -a),
       every subcomponent of every path is visited from top down,
       so  include/exclude  patterns  get  applied recursively to
       each subcomponent.

       The patterns can take several forms. The rules are:

       o      if  the  pattern starts with a / then it is matched
              against the start of the filename, otherwise it  is
              matched  against  the  end  of  the filename.  Thus
              "/foo" would match a file called "foo" at the  base
              of  the tree.  On the other hand, "foo" would match
              any file called "foo" anywhere in the tree  because
              the algorithm is applied recursively from top down;
              it behaves as if each path component gets a turn at
              being the end of the file name.

       o      if  the  pattern  ends  with  a / then it will only
              match a directory, not a file, link or device.

       o      if the pattern contains a wildcard  character  from
              the  set  *?[  then  expression matching is applied
              using the shell filename matching rules.  Otherwise
              a simple string match is used.

       o      if the pattern includes a double asterisk "**" then
              all wildcards in the pattern  will  match  slashes,
              otherwise they will stop at slashes.

       o      if  the pattern contains a / (not counting a trail-
              ing /) then it is matched against  the  full  file-
              name,  including any leading directory. If the pat-
              tern does not contain a / then it  is  matched  only
              against   the  final  component  of  the  filename.
              Again,  remember  that  the  algorithm  is  applied
              recursively  so "full filename" can actually be any
              portion of a path.

       o      if the pattern starts with "+ " (a plus followed by
              a  space)  then  it is always considered an include
              pattern, even if specified as part  of  an  exclude
              option. The "+ " part is discarded before matching.

       o      if the pattern starts with "- " (a  minus  followed
              by a space) then it is always considered an exclude
              pattern, even if specified as part  of  an  include
              option. The "- " part is discarded before matching.

       o      if the pattern is a single exclamation mark !  then
              the current include/exclude list is reset, removing
              all previously defined patterns.

       The +/- rules are most useful in exclude  lists,  allowing
       you  to  have  a  single  exclude  list that contains both
       include and exclude options.

       If you end an exclude list with --exclude "*",  note  that
       since the algorithm is applied recursively that unless you
       explicitly include parent directories of files you want to
       include  then the algorithm will stop at the parent direc
       tories and never see the files below them.  To include all
       directories,  use --include "*/" before the --exclude "*".

       Here are some exclude/include examples:

       o      *.o would exclude all filenames
              matching *.o

       o      /foo  would exclude a file in the base
              directory called foo

       o       "foo/" would exclude any directory called
              foo

       o      /foo/*/bar  would  exclude  any  file
              called bar two levels below a base directory called
              foo

       o      /foo/**/bar  would  exclude  any  file
              called bar two or more levels below a  base  direc-
              tory called foo

       o      */
              *.c
              * would
              include all directories and C source files

       o      foo/
              foo/bar.c
              *  would  include only foo/bar.c (the foo/ direc-
              tory must be explicitly included  or  it  would  be
              excluded by the "*")

<a name="6"><h3>Advanced</h3></a>

Not all rsync options can be applied with the panel, if you have
a need to further refine your script you can

1.
the panel creates shell scripts in /usr/bin/ called dungogrsync-rsync*
eg.
-rwxr-xr-x  1 cathie   admin 467 Sep 30 21:34 /usr/bin/dungogrsync-rsync55622
-rwxr-xr-x  1 stephen  admin 593 Oct  1 01:28 /usr/bin/dungogrsync-rsync608624

2.
if automated, these are called from cron

3.
you can take these files,
copy and/or combine them into a new files so they are not overwritten
then add the new file into cron, eg with dungog-cron.

* combining the files will let one job finish before a new one starts

* you can add other options,
either generate a dummy script with the panel to check syntax,
or read the manual, man rsync or google

</pre>   ')));

    print $q->endform;
    print $q->p ($q->hr, $q->font ({size => "-1"}, "Copyright dungog.net",
          $q->a ({href => $q->url (-absolute => 1) . ""}, 'Return to main'), " ..."));
    return;
}