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