a505a0f47beed4f36327019ba156f531d323366b
[freeside.git] / FS / FS / part_export / vpopmail.pm
1 package FS::part_export::vpopmail;
2
3 use vars qw(@ISA @saltset $exportdir);
4 use Fcntl qw(:flock);
5 use File::Path;
6 use FS::UID qw( datasrc );
7 use FS::part_export;
8
9 @ISA = qw(FS::part_export);
10
11 @saltset = ( 'a'..'z' , 'A'..'Z' , '0'..'9' , '.' , '/' );
12
13 sub rebless { shift; }
14
15 sub _export_insert {
16   my($self, $svc_acct) = (shift, shift);
17   $self->vpopmail_queue( $svc_acct->svcnum, 'insert',
18     $svc_acct->username,
19     crypt($svc_acct->_password,$saltset[int(rand(64))].$saltset[int(rand(64))]),
20     $svc_acct->domain,
21     $svc_acct->quota,
22     $svc_acct->finger,
23   );
24 }
25
26 sub _export_replace {
27   my( $self, $new, $old ) = (shift, shift, shift);
28
29   my $cpassword = crypt(
30     $new->_password, $saltset[int(rand(64))].$saltset[int(rand(64))]
31   );
32
33   return "can't change username with vpopmail"
34     if $old->username ne $new->username;
35
36   #no.... if mail can't be preserved, better to disallow username changes
37   #if ($old->username ne $new->username || $old->domain ne $new->domain ) {
38   #  vpopmail_queue( $svc_acct->svcnum, 'delete', 
39   #    $old->username, $old->domain
40   #  );
41   #  vpopmail_queue( $svc_acct->svcnum, 'insert', 
42   #    $new->username,
43   #    $cpassword,
44   #    $new->domain,
45   #  );
46
47   return '' unless $old->_password ne $new->_password;
48
49   $self->vpopmail_queue( $new->svcnum, 'replace',
50     $new->username, $cpassword, $new->domain, $new->quota, $new->finger );
51 }
52
53 sub _export_delete {
54   my( $self, $svc_acct ) = (shift, shift);
55   $self->vpopmail_queue( $svc_acct->svcnum, 'delete',
56     $svc_acct->username, $svc_acct->domain );
57 }
58
59 #a good idea to queue anything that could fail or take any time
60 sub vpopmail_queue {
61   my( $self, $svcnum, $method ) = (shift, shift, shift);
62
63   my $exportdir = "/usr/local/etc/freeside/export." . datasrc;
64   mkdir $exportdir, 0700 or die $! unless -d $exportdir;
65   $exportdir .= "/vpopmail";
66   mkdir $exportdir, 0700 or die $! unless -d $exportdir;
67   $exportdir .= '/'. $self->machine;
68   mkdir $exportdir, 0700 or die $! unless -d $exportdir;
69   mkdir "$exportdir/domains", 0700 or die $! unless -d "$exportdir/domains";
70
71   my $queue = new FS::queue {
72     'svcnum' => $svcnum,
73     'job'    => "FS::part_export::vpopmail::vpopmail_$method",
74   };
75   $queue->insert(
76     $exportdir,
77     $self->machine,
78     $self->option('dir'),
79     $self->option('uid'),
80     $self->option('gid'),
81     $self->option('restart'),
82     @_
83   );
84 }
85
86 sub vpopmail_insert { #subroutine, not method
87   my( $exportdir, $machine, $dir, $uid, $gid, $restart ) = splice @_,0,6;
88   my( $username, $password, $domain, $quota, $finger ) = @_;
89
90   mkdir "$exportdir/domains/$domain", 0700 or die $!
91     unless -d "$exportdir/domains/$domain";
92
93   (open(VPASSWD, ">>$exportdir/domains/$domain/vpasswd")
94     and flock(VPASSWD,LOCK_EX)
95   ) or die "can't open vpasswd file for $username\@$domain: ".
96            "$exportdir/domains/$domain/vpasswd: $!";
97   print VPASSWD join(":",
98     $username,
99     $password,
100     '1',
101     '0',
102     $finger,
103     "$dir/domains/$domain/$username",
104     $quota ? $quota.'S' : 'NOQUOTA',
105   ), "\n";
106
107   flock(VPASSWD,LOCK_UN);
108   close(VPASSWD);
109
110   for my $mkdir (
111     grep { ! -d $_ } map { "$exportdir/domains/$domain/$username$_" }
112         ( '', qw( /Maildir /Maildir/cur /Maildir/new /Maildir/tmp ) )
113   ) {
114     mkdir $mkdir, 0700 or die "can't mkdir $mkdir: $!";
115   }
116
117   vpopmail_sync( $exportdir, $machine, $dir, $uid, $gid, $restart );
118
119 }
120
121 sub vpopmail_replace { #subroutine, not method
122   my( $exportdir, $machine, $dir, $uid, $gid, $restart ) = splice @_,0,6;
123   my( $username, $password, $domain, $quota, $finger ) = @_;
124   
125   (open(VPASSWD, "$exportdir/domains/$domain/vpasswd")
126     and flock(VPASSWD,LOCK_EX)
127   ) or die "can't open $exportdir/domains/$domain/vpasswd: $!";
128
129   open(VPASSWDTMP, ">$exportdir/domains/$domain/vpasswd.tmp")
130     or die "Can't open $exportdir/domains/$domain/vpasswd.tmp: $!";
131
132   while (<VPASSWD>) {
133     my ($mailbox, $pw, $vuid, $vgid, $vfinger, $vdir, $vquota, @rest) =
134       split(':', $_);
135     if ( $username ne $mailbox ) {
136       print VPASSWDTMP $_;
137       next
138     }
139     print VPASSWDTMP join (':',
140       $mailbox,
141       $password,
142       '1',
143       '0',
144       $finger,
145       "$dir/domains/$domain/$username", #$vdir
146       $quota ? $quota.'S' : 'NOQUOTA',
147     ), "\n";
148   }
149
150   close(VPASSWDTMP);
151
152   rename "$exportdir/domains/$domain/vpasswd.tmp", "$exportdir/domains/$domain/vpasswd"
153     or die "Can't rename $exportdir/domains/$domain/vpasswd.tmp: $!";
154
155   flock(VPASSWD,LOCK_UN);
156   close(VPASSWD);
157
158   vpopmail_sync( $exportdir, $machine, $dir, $uid, $gid, $restart );
159
160 }
161
162 sub vpopmail_delete { #subroutine, not method
163   my( $exportdir, $machine, $dir, $uid, $gid, $restart ) = splice @_,0,6;
164   my( $username, $domain ) = @_;
165   
166   (open(VPASSWD, "$exportdir/domains/$domain/vpasswd")
167     and flock(VPASSWD,LOCK_EX)
168   ) or die "can't open $exportdir/domains/$domain/vpasswd: $!";
169
170   open(VPASSWDTMP, ">$exportdir/domains/$domain/vpasswd.tmp")
171     or die "Can't open $exportdir/domains/$domain/vpasswd.tmp: $!";
172
173   while (<VPASSWD>) {
174     my ($mailbox, $rest) = split(':', $_);
175     print VPASSWDTMP $_ unless $username eq $mailbox;
176   }
177
178   close(VPASSWDTMP);
179
180   rename "$exportdir/domains/$domain/vpasswd.tmp",
181          "$exportdir/domains/$domain/vpasswd"
182     or die "Can't rename $exportdir/domains/$domain/vpasswd.tmp: $!";
183
184   flock(VPASSWD,LOCK_UN);
185   close(VPASSWD);
186
187   rmtree "$exportdir/domains/$domain/$username"
188     or die "can't rmtree $exportdir/domains/$domain/$username: $!";
189
190   vpopmail_sync( $exportdir, $machine, $dir, $uid, $gid, $restart );
191 }
192
193 sub vpopmail_sync {
194   my( $exportdir, $machine, $dir, $uid, $gid, $restart ) = splice @_,0,6;
195   
196   chdir $exportdir;
197 #  my @args = ( $rsync, "-rlpt", "-e", $ssh, "domains/",
198 #               "vpopmail\@$machine:$dir/domains/"  );
199 #  system {$args[0]} @args;
200
201   eval "use File::Rsync;";
202   die $@ if $@;
203
204   my $rsync = File::Rsync->new({ rsh => 'ssh' });
205
206   $rsync->exec( {
207     recursive => 1,
208     perms     => 1,
209     times     => 1,
210     src       => "$exportdir/domains/",
211     dest      => "vpopmail\@$machine:$dir/domains/",
212   } ); # true/false return value from exec is not working, alas
213   if ( $rsync->err ) {
214     die "error uploading to vpopmail\@$machine:$dir/domains/ : ".
215         'exit status: '. $rsync->status. ', '.
216         'STDERR: '. join(" / ", $rsync->err). ', '.
217         'STDOUT: '. join(" / ", $rsync->out);
218   }
219
220   eval "use Net::SSH qw(ssh);";
221   die $@ if $@;
222
223   ssh("vpopmail\@$machine", $restart) if $restart;
224 }
225
226