add domain_sql export for new mailserver config and modify acct_sql export for same
authorivan <ivan>
Tue, 19 Sep 2006 16:49:24 +0000 (16:49 +0000)
committerivan <ivan>
Tue, 19 Sep 2006 16:49:24 +0000 (16:49 +0000)
FS/FS/part_export/acct_sql.pm
FS/FS/part_export/domain_sql.pm [new file with mode: 0644]
FS/FS/svc_acct.pm

index 4b92e80..e228237 100644 (file)
@@ -60,6 +60,16 @@ my $postfix_courierimap_alias_map =
   join('\n', map "$_ $postfix_courierimap_alias_map{$_}",
                  keys %postfix_courierimap_alias_map      );
 
+tie my %postfix_native_mailbox_map, 'Tie::IxHash',
+  'userid'   => 'email',
+  'uid'      => 'uid',
+  'gid'      => 'gid',
+  'password' => 'ldap_password',
+;
+my $postfix_native_mailbox_map =
+  join('\n', map "$_ $postfix_native_mailbox_map{$_}",
+                 keys %postfix_native_mailbox_map      );
+
 %info = (
   'svc'      => 'svc_acct',
   'desc'     => 'Real-time export of accounts to SQL databases '.
@@ -94,6 +104,10 @@ to be configured for different mail server setups.
     this.form.schema.value = "$postfix_courierimap_alias_map";
     this.form.primary_key.value = "address";
   '>
+  <LI><INPUT TYPE="button" VALUE="postfix_native_mailbox" onClick='
+    this.form.table.value = "users";
+    this.form.schema.value = "$postfix_native_map";
+    this.form.primary_key.value = "userid";
 </UL>
 END
 );
diff --git a/FS/FS/part_export/domain_sql.pm b/FS/FS/part_export/domain_sql.pm
new file mode 100644 (file)
index 0000000..3dcb7a2
--- /dev/null
@@ -0,0 +1,228 @@
+package FS::part_export::domain_sql;
+
+use vars qw(@ISA %info);
+use Tie::IxHash;
+use FS::part_export;
+
+@ISA = qw(FS::part_export);
+
+#quite a bit of false laziness w/acct_sql - some stuff should be generalized
+#out to a "dababase base class"
+
+tie my %options, 'Tie::IxHash',
+  'datasrc'            => { label => 'DBI data source' },
+  'username'           => { label => 'Database username' },
+  'password'           => { label => 'Database password' },
+  'table'              => { label => 'Database table' },
+  'schema'             => { label =>
+                              'Database schema mapping to Freeside methods.',
+                            type  => 'textarea',
+                          },
+  'static'             => { label =>
+                              'Database schema mapping to static values.',
+                            type  => 'textarea',
+                          },
+  'primary_key'        => { label => 'Database primary key' },
+;
+
+tie my %postfix_transport_map, 
+  'domain' => 'domain'
+;
+my $postfix_transport_map = 
+  join('\n', map "$_ $postfix_transport_map{$_}",
+                 keys %postfix_transport_map      );
+tie my %postfix_transport_static,
+  'transport' => 'virtual:',
+;
+my $postfix_transport_static = 
+  join('\n', map "$_ $postfix_transport_static{$_}",
+                 keys %postfix_transport_static      );
+
+%info  = (
+  'svc'     => 'svc_domain',
+  'desc'    => 'Real time export of domains to SQL databases '.
+               '(postfix, others?)',
+  'options' => \%options,
+  'notes'   => <<END
+Export domains (svc_domain records) to SQL databases.  Currently this is a
+simple export with a default for Postfix, but it can be extended for other
+uses.
+
+<BR><BR>Use these buttons for useful presets:
+<UL>
+  <LI><INPUT TYPE="button" VALUE="postfix_transport" onClick='
+    this.form.table.value = "transport";
+    this.form.schema.value = "$postfix_transport_map";
+    this.form.static.value = "$postfix_transport_static";
+    this.form.primary_key.value = "domain";
+  '>
+</UL>
+END
+);
+
+sub _schema_map { shift->_map('schema'); }
+sub _static_map { shift->_map('static'); }
+
+sub _map {
+  my $self = shift;
+  map { /^\s*(\S+)\s*(\S+)\s*$/ } split("\n", $self->option(shift) );
+}
+
+sub _export_insert {
+  my($self, $svc_domain) = (shift, shift);
+
+  my %schema = $self->_schema_map;
+  my %static = $self->_static_map;
+
+  my %record = ( map { $_ => $static{$_}       } keys %static ),
+               ( map { $_ => $svc_domain->$_() } keys %schema );
+
+  my $err_or_queue = 
+    $self->domain_sql_queue(
+      $svc_domain->svcnum,
+      'insert',
+      $self->option('table'),
+      %record
+    );
+  return $err_or_queue unless ref($err_or_queue);
+
+  '';
+}
+
+sub _export_replace {
+  my($self, $new, $old) = (shift, shift, shift);
+
+  my %schema = $self->_schema_map;
+  my %static = $self->_static_map;
+
+  my @primary_key = ();
+  if ( $self->option('primary_key') =~ /,/ ) {
+    foreach my $key ( split(/\s*,\s*/, $self->option('primary_key') ) ) {
+      my $keymap = $schema{$key};
+      push @primary_key, $old->$keymap();
+    }
+  } else {
+    my $keymap = $map{$self->option('primary_key')};
+    push @primary_key, $old->$keymap();
+  }
+
+  my %record = ( map { $_ => $static{$_}       } keys %static ),
+               ( map { $_ => $svc_domain->$_() } keys %schema );
+
+  my $err_or_queue = $self->domain_sql_queue(
+    $new->svcnum,
+    'replace',
+    $self->option('table'),
+    $self->option('primary_key'), @primary_key, 
+    %record,
+  );
+  return $err_or_queue unless ref($err_or_queue);
+  '';
+}
+
+sub _export_delete {
+  my ( $self, $svc_domain ) = (shift, shift);
+
+  my %schema = $self->_schema_map;
+  my %static = $self->_static_map;
+
+  my %primary_key = ();
+  if ( $self->option('primary_key') =~ /,/ ) {
+    foreach my $key ( split(/\s*,\s*/, $self->option('primary_key') ) ) {
+      my $keymap = $map{$key};
+      $primary_key{ $key } = $svc_domain->$keymap();
+    }
+  } else {
+    my $keymap = $map{$self->option('primary_key')};
+    $primary_key{ $self->option('primary_key') } = $svc_domain->$keymap(),
+  }
+
+  my $err_or_queue = $self->domain_sql_queue(
+    $svc_domain->svcnum,
+    'delete',
+    $self->option('table'),
+    %primary_key,
+    #$self->option('primary_key') => $svc_domain->$keymap(),
+  );
+  return $err_or_queue unless ref($err_or_queue);
+  '';
+}
+
+sub domain_sql_queue {
+  my( $self, $svcnum, $method ) = (shift, shift, shift);
+  my $queue = new FS::queue {
+    'svcnum' => $svcnum,
+    'job'    => "FS::part_export::domain_sql::domain_sql_$method",
+  };
+  $queue->insert(
+    $self->option('datasrc'),
+    $self->option('username'),
+    $self->option('password'),
+    @_,
+  ) or $queue;
+}
+
+sub domain_sql_insert { #subroutine, not method
+  my $dbh = domain_sql_connect(shift, shift, shift);
+  my( $table, %record ) = @_;
+
+  my $sth = $dbh->prepare(
+    "INSERT INTO $table ( ". join(", ", keys %record).
+    " ) VALUES ( ". join(", ", map '?', keys %record ). " )"
+  ) or die $dbh->errstr;
+
+  $sth->execute( values(%record) )
+    or die "can't insert into $table table: ". $sth->errstr;
+
+  $dbh->disconnect;
+}
+
+sub domain_sql_delete { #subroutine, not method
+  my $dbh = domain_sql_connect(shift, shift, shift);
+  my( $table, %record ) = @_;
+
+  my $sth = $dbh->prepare(
+    "DELETE FROM $table WHERE ". join(' AND ', map "$_ = ? ", keys %record )
+  ) or die $dbh->errstr;
+
+  $sth->execute( map $record{$_}, keys %record )
+    or die "can't delete from $table table: ". $sth->errstr;
+
+  $dbh->disconnect;
+}
+
+sub domain_sql_replace { #subroutine, not method
+  my $dbh = domain_sql_connect(shift, shift, shift);
+
+  my( $table, $pkey ) = ( shift, shift );
+
+  my %primary_key = ();
+  if ( $pkey =~ /,/ ) {
+    foreach my $key ( split(/\s*,\s*/, $pkey ) ) {
+      $primary_key{$key} = shift;
+    }
+  } else {
+    $primary_key{$pkey} = shift;
+  }
+
+  my %record = @_;
+
+  my $sth = $dbh->prepare(
+    "UPDATE $table".
+    ' SET '.   join(', ',    map "$_ = ?", keys %record      ).
+    ' WHERE '. join(' AND ', map "$_ = ?", keys %primary_key )
+  ) or die $dbh->errstr;
+
+  $sth->execute( values(%record), values(%primary_key) );
+
+  $dbh->disconnect;
+}
+
+sub domain_sql_connect {
+  #my($datasrc, $username, $password) = @_;
+  #DBI->connect($datasrc, $username, $password) or die $DBI::errstr;
+  DBI->connect(@_) or die $DBI::errstr;
+}
+
+1;
+
index b201f23..003ad90 100644 (file)
@@ -1551,13 +1551,52 @@ sub crypt_password {
     } elsif ( $encryption eq 'md5' ) {
       unix_md5_crypt( $self->_password );
     } elsif ( $encryption eq 'blowfish' ) {
-      die "unknown encryption method $encryption";
+      croak "unknown encryption method $encryption";
     } else {
-      die "unknown encryption method $encryption";
+      croak "unknown encryption method $encryption";
     }
   }
 }
 
+=item ldap_password [ DEFAULT_ENCRYPTION_TYPE ]
+
+Returns an encrypted password in "LDAP" format, with a curly-bracked prefix
+describing the format, for example, "{CRYPT}94pAVyK/4oIBk" or
+"{PLAIN-MD5}5426824942db4253f87a1009fd5d2d4f".
+
+The optional DEFAULT_ENCRYPTION_TYPE is not yet used, but the idea is for it
+to work the same as the B</crypt_password> method.
+
+=cut
+
+sub ldap_password {
+  my $self = shift;
+  #eventually should check a "password-encoding" field
+  if ( length($self->_password) == 13 ) { #crypt
+    return '{CRYPT}'. $self->_password;
+  } elsif ( $self->_password =~ /^\$1\$(.*)$/ && length($1) == 31 ) { #passwdMD5
+    return '{MD5}'. $1;
+  } elsif ( $self->_password =~ /^\$2a?\$(.*)$/ ) { #Blowfish
+    die "Blowfish encryption not supported in this context, svcnum ".
+        $self->svcnum. "\n";
+  } else { #plaintext
+    return '{PLAIN}'. self->_password;
+    #my $encryption = ( scalar(@_) && $_[0] ) ? shift : 'crypt';
+    #if ( $encryption eq 'crypt' ) {
+    #  return '{CRYPT}'. crypt(
+    #    $self->_password,
+    #    $saltset[int(rand(64))].$saltset[int(rand(64))]
+    #  );
+    #} elsif ( $encryption eq 'md5' ) {
+    #  unix_md5_crypt( $self->_password );
+    #} elsif ( $encryption eq 'blowfish' ) {
+    #  croak "unknown encryption method $encryption";
+    #} else {
+    #  croak "unknown encryption method $encryption";
+    #}
+  }
+}
+
 =item virtual_maildir
 
 Returns $domain/maildirs/$username/