add postfix export
[freeside.git] / FS / FS / part_export.pm
index 300e888..0d91961 100644 (file)
@@ -274,10 +274,6 @@ sub check {
   ;
   return $error if $error;
 
-  $self->machine =~ /^([\w\-\.]*)$/
-    or return "Illegal machine: ". $self->machine;
-  $self->machine($1);
-
   $self->nodomain =~ /^(Y?)$/ or return "Illegal nodomain: ". $self->nodomain;
   $self->nodomain($1);
 
@@ -285,7 +281,7 @@ sub check {
 
   #check exporttype?
 
-  ''; #no error
+  $self->SUPER::check;
 }
 
 #=item part_svc
@@ -580,13 +576,13 @@ tie my %shellcommands_options, 'Tie::IxHash',
                         type =>'checkbox',
                       },
   'suspend' => { label=>'Suspension command',
-                 default=>'',
+                 default=>'usermod -L $username',
                },
   'suspend_stdin' => { label=>'Suspension command STDIN',
                        default=>'',
                      },
   'unsuspend' => { label=>'Unsuspension command',
-                   default=>'',
+                   default=>'usermod -U $username',
                  },
   'unsuspend_stdin' => { label=>'Unsuspension command STDIN',
                          default=>'',
@@ -667,8 +663,21 @@ END
   },
 ;
 
+tie my %router_options, 'Tie::IxHash',
+  'protocol' => {
+         label=>'Protocol',
+         type =>'select',
+         options => [qw(telnet ssh)],
+         default => 'telnet'},
+  'insert' => {label=>'Insert command', default=>'' },
+  'delete' => {label=>'Delete command', default=>'' },
+  'replace' => {label=>'Replace command', default=>'' },
+  'Timeout' => {label=>'Time to wait for prompt', default=>'20' },
+  'Prompt' => {label=>'Prompt string', default=>'#' }
+;
+
 tie my %domain_shellcommands_options, 'Tie::IxHash',
-  'user' => { lable=>'Remote username', default=>'root' },
+  'user' => { label=>'Remote username', default=>'root' },
   'useradd' => { label=>'Insert command',
                  default=>'',
                },
@@ -689,6 +698,20 @@ tie my %sqlradius_options, 'Tie::IxHash',
   'datasrc'  => { label=>'DBI data source ' },
   'username' => { label=>'Database username' },
   'password' => { label=>'Database password' },
+  'ignore_accounting' => {
+     type => 'checkbox',
+     label=>'Ignore accounting records from this database'
+  },
+;
+
+tie my %sqlradius_withdomain_options, 'Tie::IxHash',
+  'datasrc'  => { label=>'DBI data source ' },
+  'username' => { label=>'Database username' },
+  'password' => { label=>'Database password' },
+  'ignore_accounting' => {
+     type => 'checkbox',
+     label=>'Ignore accounting records from this database'
+  },
 ;
 
 tie my %cyrus_options, 'Tie::IxHash',
@@ -722,6 +745,41 @@ tie my %vpopmail_options, 'Tie::IxHash',
                },
 ;
 
+tie my %communigate_pro_options, 'Tie::IxHash',
+  'port'     => { label=>'Port number', default=>'106', },
+  'login'    => { label=>'The administrator account name.  The name can contain a domain part.', },
+  'password' => { label=>'The administrator account password.', },
+  'accountType' => { label=>'Type for newly-created accounts',
+                     type=>'select',
+                     options=>[qw( MultiMailbox TextMailbox MailDirMailbox )],
+                     default=>'MultiMailbox',
+                   },
+  'externalFlag' => { label=> 'Create accounts with an external (visible for legacy mailers) INBOX.',
+                      type=>'checkbox',
+                    },
+  'AccessModes' => { label=>'Access modes',
+                     default=>'Mail POP IMAP PWD WebMail WebSite',
+                   },
+;
+
+tie my %communigate_pro_singledomain_options, 'Tie::IxHash',
+  'port'     => { label=>'Port number', default=>'106', },
+  'login'    => { label=>'The administrator account name.  The name can contain a domain part.', },
+  'password' => { label=>'The administrator account password.', },
+  'domain'   => { label=>'Domain', },
+  'accountType' => { label=>'Type for newly-created accounts',
+                     type=>'select',
+                     options=>[qw( MultiMailbox TextMailbox MailDirMailbox )],
+                     default=>'MultiMailbox',
+                   },
+  'externalFlag' => { label=> 'Create accounts with an external (visible for legacy mailers) INBOX.',
+                      type=>'checkbox',
+                    },
+  'AccessModes' => { label=>'Access modes',
+                     default=>'Mail POP IMAP PWD WebMail WebSite',
+                   },
+;
+
 tie my %bind_options, 'Tie::IxHash',
   #'machine'     => { label=>'named machine' },
   'named_conf'   => { label  => 'named.conf location',
@@ -833,7 +891,7 @@ tie my %ldap_options, 'Tie::IxHash',
 ;
 
 tie my %forward_shellcommands_options, 'Tie::IxHash',
-  'user' => { lable=>'Remote username', default=>'root' },
+  'user' => { label=>'Remote username', default=>'root' },
   'useradd' => { label=>'Insert command',
                  default=>'',
                },
@@ -845,6 +903,13 @@ tie my %forward_shellcommands_options, 'Tie::IxHash',
                 },
 ;
 
+tie my %postfix_options, 'Tie::IxHash',
+  'user' => { label=>'Remote username', default=>'root' },
+  'aliases' => { label=>'aliases file location', default=>'/etc/aliases' },
+  'virtual' => { label=>'virtual file location', default=>'/etc/postfix/virtual' },
+  'mydomain' => { label=>'local domain', default=>'' },
+;
+
 #export names cannot have dashes...
 %exports = (
   'svc_acct' => {
@@ -877,13 +942,13 @@ tie my %forward_shellcommands_options, 'Tie::IxHash',
       'desc' => 'Real-time export via remote SSH (i.e. useradd, userdel, etc.)',
       'options' => \%shellcommands_options,
       'nodomain' => 'Y',
-      'notes' => 'Run remote commands via SSH.  Usernames are considered unique (also see shellcommands_withdomain).  You probably want this if the commands you are running will not accept a domain as a parameter.  You will need to <a href="../docs/ssh.html">setup SSH for unattended operation</a>.<BR><BR>Use these buttons for some useful presets:<UL><LI><INPUT TYPE="button" VALUE="Linux/NetBSD" onClick=\'this.form.useradd.value = "useradd -c $finger -d $dir -m -s $shell -u $uid -p $crypt_password $username"; this.form.useradd_stdin.value = ""; this.form.userdel.value = "userdel -r $username"; this.form.userdel_stdin.value=""; this.form.usermod.value = "usermod -c $new_finger -d $new_dir -m -l $new_username -s $new_shell -u $new_uid -p $new_crypt_password $old_username"; this.form.usermod_stdin.value = "";\'><LI><INPUT TYPE="button" VALUE="FreeBSD" onClick=\'this.form.useradd.value = "pw useradd $username -d $dir -m -s $shell -u $uid -g $gid -c $finger -h 0"; this.form.useradd_stdin.value = "$_password\n"; this.form.userdel.value = "pw userdel $username -r"; this.form.userdel_stdin.value=""; this.form.usermod.value = "pw usermod $old_username -d $new_dir -m -l $new_username -s $new_shell -u $new_uid -c $new_finger -h 0"; this.form.usermod_stdin.value = "$new__password\n";\'><LI><INPUT TYPE="button" VALUE="Just maintain directories (use with sysvshell or bsdshell)" onClick=\'this.form.useradd.value = "cp -pr /etc/skel $dir; chown -R $uid.$gid $dir"; this.form.useradd_stdin.value = ""; this.form.usermod.value = "[ -d $old_dir ] && mv $old_dir $new_dir || ( chmod u+t $old_dir; mkdir $new_dir; cd $old_dir; find . -depth -print | cpio -pdm $new_dir; chmod u-t $new_dir; chown -R $new_uid.$new_gid $new_dir; rm -rf $old_dir )"; this.form.usermod_stdin.value = ""; this.form.userdel.value = "rm -rf $dir"; this.form.userdel_stdin.value="";\'></UL>The following variables are available for interpolation (prefixed with new_ or old_ for replace operations): <UL><LI><code>$username</code><LI><code>$_password</code><LI><code>$quoted_password</code> - unencrypted password quoted for the shell<LI><code>$crypt_password</code> - encrypted password<LI><code>$uid</code><LI><code>$gid</code><LI><code>$finger</code> - GECOS, already quoted for the shell (do not add additional quotes)<LI><code>$dir</code> - home directory<LI><code>$shell</code><LI><code>$quota</code><LI>All other fields in <a href="../docs/schema.html#svc_acct">svc_acct</a> are also available.</UL>',
+      'notes' => 'Run remote commands via SSH.  Usernames are considered unique (also see shellcommands_withdomain).  You probably want this if the commands you are running will not accept a domain as a parameter.  You will need to <a href="../docs/ssh.html">setup SSH for unattended operation</a>.<BR><BR>Use these buttons for some useful presets:<UL><LI><INPUT TYPE="button" VALUE="Linux" onClick=\'this.form.useradd.value = "useradd -c $finger -d $dir -m -s $shell -u $uid -p $crypt_password $username"; this.form.useradd_stdin.value = ""; this.form.userdel.value = "userdel -r $username"; this.form.userdel_stdin.value=""; this.form.usermod.value = "usermod -c $new_finger -d $new_dir -m -l $new_username -s $new_shell -u $new_uid -p $new_crypt_password $old_username"; this.form.usermod_stdin.value = ""; this.form.suspend.value = "usermod -L $username"; this.form.suspend_stdin.value=""; this.form.unsuspend.value = "usermod -U $username"; this.form.unsuspend_stdin.value="";\'><LI><INPUT TYPE="button" VALUE="FreeBSD" onClick=\'this.form.useradd.value = "lockf /etc/passwd.lock pw useradd $username -d $dir -m -s $shell -u $uid -g $gid -c $finger -h 0"; this.form.useradd_stdin.value = "$_password\n"; this.form.userdel.value = "lockf /etc/passwd.lock pw userdel $username -r"; this.form.userdel_stdin.value=""; this.form.usermod.value = "lockf /etc/passwd.lock pw usermod $old_username -d $new_dir -m -l $new_username -s $new_shell -u $new_uid -c $new_finger -h 0"; this.form.usermod_stdin.value = "$new__password\n"; this.form.suspend.value = "lockf /etc/passwd.lock pw lock $username"; this.form.suspend_stdin.value=""; this.form.unsuspend.value = "lockf /etc/passwd.lock pw unlock $username"; this.form.unsuspend_stdin.value="";\'> Note: On FreeBSD, due to deficient locking in pw(1), you must disable the chpass(1), chsh(1), chfn(1), passwd(1), and vipw(1) commands, or replace them with wrappers that prepend "lockf /etc/passwd.lock".  Alternatively, apply the patch in <A HREF="http://www.freebsd.org/cgi/query-pr.cgi?pr=23501">FreeBSD PR#23501</A> and remove the "lockf /etc/passwd.lock" from these default commands.<LI><INPUT TYPE="button" VALUE="NetBSD/OpenBSD" onClick=\'this.form.useradd.value = "useradd -c $finger -d $dir -m -s $shell -u $uid -p $crypt_password $username"; this.form.useradd_stdin.value = ""; this.form.userdel.value = "userdel -r $username"; this.form.userdel_stdin.value=""; this.form.usermod.value = "usermod -c $new_finger -d $new_dir -m -l $new_username -s $new_shell -u $new_uid -p $new_crypt_password $old_username"; this.form.usermod_stdin.value = ""; this.form.suspend.value = ""; this.form.suspend_stdin.value=""; this.form.unsuspend.value = ""; this.form.unsuspend_stdin.value="";\'><LI><INPUT TYPE="button" VALUE="Just maintain directories (use with sysvshell or bsdshell)" onClick=\'this.form.useradd.value = "cp -pr /etc/skel $dir; chown -R $uid.$gid $dir"; this.form.useradd_stdin.value = ""; this.form.usermod.value = "[ -d $old_dir ] && mv $old_dir $new_dir || ( chmod u+t $old_dir; mkdir $new_dir; cd $old_dir; find . -depth -print | cpio -pdm $new_dir; chmod u-t $new_dir; chown -R $new_uid.$new_gid $new_dir; rm -rf $old_dir )"; this.form.usermod_stdin.value = ""; this.form.userdel.value = "rm -rf $dir"; this.form.userdel_stdin.value=""; this.form.suspend.value = ""; this.form.suspend_stdin.value=""; this.form.unsuspend.value = ""; this.form.unsuspend_stdin.value="";\'></UL>The following variables are available for interpolation (prefixed with new_ or old_ for replace operations): <UL><LI><code>$username</code><LI><code>$_password</code><LI><code>$quoted_password</code> - unencrypted password quoted for the shell<LI><code>$crypt_password</code> - encrypted password<LI><code>$uid</code><LI><code>$gid</code><LI><code>$finger</code> - GECOS, already quoted for the shell (do not add additional quotes)<LI><code>$dir</code> - home directory<LI><code>$shell</code><LI><code>$quota</code><LI>All other fields in <a href="../docs/schema.html#svc_acct">svc_acct</a> are also available.</UL>',
     },
 
     'shellcommands_withdomain' => {
       'desc' => 'Real-time export via remote SSH (vpopmail, etc.).',
       'options' => \%shellcommands_withdomain_options,
-      'notes' => 'Run remote commands via SSH.  username@domain (rather than just usernames) are considered unique (also see shellcommands).  You probably want this if the commands you are running will accept a domain as a parameter, and will allow the same username with different domains.  You will need to <a href="../docs/ssh.html">setup SSH for unattended operation</a>.<BR><BR>Use these buttons for some useful presets:<UL><LI><INPUT TYPE="button" VALUE="vpopmail" onClick=\'this.form.useradd.value = "vadduser $username@$domain"; this.form.useradd_stdin.value = "$_password\n$_password\n"; this.form.userdel.value = "vdeluser $username@$domain"; this.form.userdel_stdin.value=""; this.form.usermod.value = "vpasswd $new_username@$new_domain"; this.form.usermod_stdin.value = "$new__password\n$new__password\n"; this.form.usermod_pwonly.checked = true;\'></UL>The following variables are available for interpolation (prefixed with <code>new_</code> or <code>old_</code> for replace operations): <UL><LI><code>$username</code><LI><code>$domain</code><LI><code>$_password</code><LI><code>$quoted_password</code> - unencrypted password quoted for the shell<LI><code>$crypt_password</code> - encrypted password<LI><code>$uid</code><LI><code>$gid</code><LI><code>$finger</code> - GECOS, already quoted for the shell (do not add additional quotes)<LI><code>$dir</code> - home directory<LI><code>$shell</code><LI><code>$quota</code><LI>All other fields in <a href="../docs/schema.html#svc_acct">svc_acct</a> are also available.</UL>',
+      'notes' => 'Run remote commands via SSH.  username@domain (rather than just usernames) are considered unique (also see shellcommands).  You probably want this if the commands you are running will accept a domain as a parameter, and will allow the same username with different domains.  You will need to <a href="../docs/ssh.html">setup SSH for unattended operation</a>.<BR><BR>Use these buttons for some useful presets:<UL><LI><INPUT TYPE="button" VALUE="vpopmail" onClick=\'this.form.useradd.value = "/home/vpopmail/bin/vadduser $username\\\@$domain $quoted_password"; this.form.useradd_stdin.value = ""; this.form.userdel.value = "/home/vpopmail/bin/vdeluser $username\\\@$domain"; this.form.userdel_stdin.value=""; this.form.usermod.value = "/home/vpopmail/bin/vpasswd $new_username\\\@$new_domain $new_quoted_password"; this.form.usermod_stdin.value = ""; this.form.usermod_pwonly.checked = true;\'></UL>The following variables are available for interpolation (prefixed with <code>new_</code> or <code>old_</code> for replace operations): <UL><LI><code>$username</code><LI><code>$domain</code><LI><code>$_password</code><LI><code>$quoted_password</code> - unencrypted password quoted for the shell<LI><code>$crypt_password</code> - encrypted password<LI><code>$uid</code><LI><code>$gid</code><LI><code>$finger</code> - GECOS, already quoted for the shell (do not add additional quotes)<LI><code>$dir</code> - home directory<LI><code>$shell</code><LI><code>$quota</code><LI>All other fields in <a href="../docs/schema.html#svc_acct">svc_acct</a> are also available.</UL>',
     },
 
     'ldap' => {
@@ -893,10 +958,17 @@ tie my %forward_shellcommands_options, 'Tie::IxHash',
     },
 
     'sqlradius' => {
-      'desc' => 'Real-time export to SQL-backed RADIUS (ICRADIUS, FreeRADIUS)',
+      'desc' => 'Real-time export to SQL-backed RADIUS (FreeRADIUS, ICRADIUS, Radiator)',
       'options' => \%sqlradius_options,
       'nodomain' => 'Y',
-      'notes' => 'Real-time export of radcheck, radreply and usergroup tables to any SQL database for <a href="http://www.freeradius.org/">FreeRADIUS</a> or <a href="http://radius.innercite.com/">ICRADIUS</a>.  An existing RADIUS database will be updated in realtime, but you can use <a href="../docs/man/bin/freeside-sqlradius-reset">freeside-sqlradius-reset</a> to delete the entire RADIUS database and repopulate the tables from the Freeside database.  See the <a href="http://search.cpan.org/doc/TIMB/DBI-1.23/DBI.pm">DBI documentation</a> and the <a href="http://search.cpan.org/search?mode=module&query=DBD%3A%3A">documentation for your DBD</a> for the exact syntax of a DBI data source.',
+      'notes' => 'Real-time export of radcheck, radreply and usergroup tables to any SQL database for <a href="http://www.freeradius.org/">FreeRADIUS</a>, <a href="http://radius.innercite.com/">ICRADIUS</a> or <a href="http://www.open.com.au/radiator/">Radiator</a>.  This export does not export RADIUS realms (see also sqlradius_withdomain).  An existing RADIUS database will be updated in realtime, but you can use <a href="../docs/man/bin/freeside-sqlradius-reset">freeside-sqlradius-reset</a> to delete the entire RADIUS database and repopulate the tables from the Freeside database.  See the <a href="http://search.cpan.org/doc/TIMB/DBI/DBI.pm#connect">DBI documentation</a> and the <a href="http://search.cpan.org/search?mode=module&query=DBD%3A%3A">documentation for your DBD</a> for the exact syntax of a DBI data source.<ul><li>Using FreeRADIUS 0.9.0 with the PostgreSQL backend, the db_postgresql.sql schema and postgresql.conf queries contain incompatible changes.  This is fixed in 0.9.1.  Only new installs with 0.9.0 and PostgreSQL are affected - upgrades and other database backends and versions are unaffected.<li>Using ICRADIUS, add a dummy "op" column to your database: <blockquote><code>ALTER&nbsp;TABLE&nbsp;radcheck&nbsp;ADD&nbsp;COLUMN&nbsp;op&nbsp;VARCHAR(2)&nbsp;NOT&nbsp;NULL&nbsp;DEFAULT&nbsp;\'==\'<br>ALTER&nbsp;TABLE&nbsp;radreply&nbsp;ADD&nbsp;COLUMN&nbsp;op&nbsp;VARCHAR(2)&nbsp;NOT&nbsp;NULL&nbsp;DEFAULT&nbsp;\'==\'<br>ALTER&nbsp;TABLE&nbsp;radgroupcheck&nbsp;ADD&nbsp;COLUMN&nbsp;op&nbsp;VARCHAR(2)&nbsp;NOT&nbsp;NULL&nbsp;DEFAULT&nbsp;\'==\'<br>ALTER&nbsp;TABLE&nbsp;radgroupreply&nbsp;ADD&nbsp;COLUMN&nbsp;op&nbsp;VARCHAR(2)&nbsp;NOT&nbsp;NULL&nbsp;DEFAULT&nbsp;\'==\'</code></blockquote><li>Using Radiator, see the <a href="http://www.open.com.au/radiator/faq.html#38">Radiator FAQ</a> for configuration information.</ul>',
+    },
+
+    'sqlradius_withdomain' => {
+      'desc' => 'Real-time export to SQL-backed RADIUS (FreeRADIUS, ICRADIUS, Radiator) with realms',
+      'options' => \%sqlradius_withdomain_options,
+      'nodomain' => '',
+      'notes' => 'Real-time export of radcheck, radreply and usergroup tables to any SQL database for <a href="http://www.freeradius.org/">FreeRADIUS</a>, <a href="http://radius.innercite.com/">ICRADIUS</a> or <a href="http://www.open.com.au/radiator/">Radiator</a>.  This export exports domains to RADIUS realms (see also sqlradius).  An existing RADIUS database will be updated in realtime, but you can use <a href="../docs/man/bin/freeside-sqlradius-reset">freeside-sqlradius-reset</a> to delete the entire RADIUS database and repopulate the tables from the Freeside database.  See the <a href="http://search.cpan.org/doc/TIMB/DBI/DBI.pm#connect">DBI documentation</a> and the <a href="http://search.cpan.org/search?mode=module&query=DBD%3A%3A">documentation for your DBD</a> for the exact syntax of a DBI data source.<ul><li>Using FreeRADIUS 0.9.0 with the PostgreSQL backend, the db_postgresql.sql schema and postgresql.conf queries contain incompatible changes.  This is fixed in 0.9.1.  Only new installs with 0.9.0 and PostgreSQL are affected - upgrades and other database backends and versions are unaffected.<li>Using ICRADIUS, add a dummy "op" column to your database: <blockquote><code>ALTER&nbsp;TABLE&nbsp;radcheck&nbsp;ADD&nbsp;COLUMN&nbsp;op&nbsp;VARCHAR(2)&nbsp;NOT&nbsp;NULL&nbsp;DEFAULT&nbsp;\'==\'<br>ALTER&nbsp;TABLE&nbsp;radreply&nbsp;ADD&nbsp;COLUMN&nbsp;op&nbsp;VARCHAR(2)&nbsp;NOT&nbsp;NULL&nbsp;DEFAULT&nbsp;\'==\'<br>ALTER&nbsp;TABLE&nbsp;radgroupcheck&nbsp;ADD&nbsp;COLUMN&nbsp;op&nbsp;VARCHAR(2)&nbsp;NOT&nbsp;NULL&nbsp;DEFAULT&nbsp;\'==\'<br>ALTER&nbsp;TABLE&nbsp;radgroupreply&nbsp;ADD&nbsp;COLUMN&nbsp;op&nbsp;VARCHAR(2)&nbsp;NOT&nbsp;NULL&nbsp;DEFAULT&nbsp;\'==\'</code></blockquote><li>Using Radiator, see the <a href="http://www.open.com.au/radiator/faq.html#38">Radiator FAQ</a> for configuration information.</ul>',
     },
 
     'sqlmail' => {
@@ -932,6 +1004,19 @@ tie my %forward_shellcommands_options, 'Tie::IxHash',
       'notes' => 'Real time export to <a href="http://inter7.com/vpopmail/">vpopmail</a> text files.  <a href="http://search.cpan.org/search?dist=File-Rsync">File::Rsync</a> must be installed, and you will need to <a href="../docs/ssh.html">setup SSH for unattended operation</a> to <b>vpopmail</b>@<i>export.host</i>.',
     },
 
+    'communigate_pro' => {
+      'desc' => 'Real-time export to a CommuniGate Pro mail server',
+      'options' => \%communigate_pro_options,
+      'notes' => 'Real time export to a <a href="http://www.stalker.com/CommuniGatePro/">CommuniGate Pro</a> mail server.  The <a href="http://www.stalker.com/CGPerl/">CommuniGate Pro Perl Interface</a> must be installed as CGP::CLI.',
+    },
+
+    'communigate_pro_singledomain' => {
+      'desc' => 'Real-time export to a CommuniGate Pro mail server, one domain only',
+      'options' => \%communigate_pro_singledomain_options,
+      'nodomain' => 'Y',
+      'notes' => 'Real time export to a <a href="http://www.stalker.com/CommuniGatePro/">CommuniGate Pro</a> mail server.  This is an unusual export to CommuniGate Pro that forces all accounts into a single domain.  As CommuniGate Pro supports multiple domains, unless you have a specific reason for using this export, you probably want to use the communigate_pro export instead.  The <a href="http://www.stalker.com/CGPerl/">CommuniGate Pro Perl Interface</a> must be installed as CGP::CLI.',
+    },
+
   },
 
   'svc_domain' => {
@@ -975,7 +1060,7 @@ tie my %forward_shellcommands_options, 'Tie::IxHash',
       'desc' => 'Real-time export to SQL-backed mail server',
       'options' => \%sqlmail_options,
       #'nodomain' => 'Y',
-      'notes' => 'Database schema can be made to work with Courier IMAP and Exim.  Others could work but are untested. (...extended description from pc-intouch?...)',
+      'notes' => 'Database schema can be made to work with Courier IMAP and Exim.  Others could work but are untested. (...extended description from fire2wire?...)',
     },
 
     'forward_shellcommands' => {
@@ -983,6 +1068,14 @@ tie my %forward_shellcommands_options, 'Tie::IxHash',
       'options' => \%forward_shellcommands_options,
       'notes' => 'Run remote commands via SSH, for forwards.  You will need to <a href="../docs/ssh.html">setup SSH for unattended operation</a>.<BR><BR>Use these buttons for some useful presets:<UL><LI><INPUT TYPE="button" VALUE="text vpopmail maintenance" onClick=\'this.form.useradd.value = "[ -d /home/vpopmail/domains/$domain/$username ] && { echo \"$destination\" > /home/vpopmail/domains/$domain/$username/.qmail; chown vpopmail:vchkpw /home/vpopmail/domains/$domain/$username/.qmail; }"; this.form.userdel.value = "rm /home/vpopmail/domains/$domain/$username/.qmail"; this.form.usermod.value = "mv /home/vpopmail/domains/$old_domain/$old_username/.qmail /home/vpopmail/domains/$new_domain/$new_username; [ \"$old_destination\" != \"$new_destination\" ] && { echo \"$new_destination\" > /home/vpopmail/domains/$new_domain/$new_username/.qmail; chown vpopmail:vchkpw /home/vpopmail/domains/$new_domain/$new_username/.qmail; }";\'></UL>The following variables are available for interpolation (prefixed with <code>new_</code> or <code>old_</code> for replace operations): <UL><LI><code>$username</code><LI><code>$domain</code><LI><code>$destination</code> - forward destination<LI>All other fields in <a href="../docs/schema.html#svc_forward">svc_forward</a> are also available.</UL>',
     },
+
+    'postfix' => {
+      'desc' => 'Real-time export to Postfix text files',
+      'options' => \%postfix_options,
+      #'nodomain' => 'Y',
+      'notes' => 'Batch export of Postfix aliases and virtual files.  <a href="http://search.cpan.org/search?dist=File-Rsync">File::Rsync</a> must be installed.  Run bin/postfix.export to export the files.',
+    },
+
   },
 
   'svc_www' => {
@@ -1000,6 +1093,14 @@ tie my %forward_shellcommands_options, 'Tie::IxHash',
   },
 
   'svc_broadband' => {
+    'router' => {
+      'desc' => 'Send a command to a router.',
+      'options' => \%router_options,
+      'notes' => '',
+    },
+  },
+
+  'svc_external' => {
   },
 
 );