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