#!/usr/bin/perl # Copyright 1999-2017. Parallels IP Holdings GmbH. All Rights Reserved. package QmailAgent; use strict; use warnings; use MailAccount; use HelpFuncs; use Logging; use Mbox2Mdir; use File::Copy; use constant QMAIL_PATH => '/var/qmail/'; #use constant QMAIL_PATH => 'C:/qmail/'; sub new { my $self = { }; bless($self); return $self; } sub get_installed { my $assign = QmailAgent::QMAIL_PATH . "users/assign"; if( -e $assign ){ return 1; } return 0; } sub get_id_string{ return 'qmail'; } sub get_display_name{ return 'qmail'; } sub can_backup_accounts{ return 1; } sub can_check_account{ return 0; } sub has_account{ my ($self, $name) = @_; return 0; } sub backup_accounts{ my ( $self, $accounts ) = @_; my %pwds = load_passwdords(); my $assign = QmailAgent::QMAIL_PATH . "users/assign"; Logging::debug( "Read qmail assign file $assign" ); open(ASSIGN, "< $assign"); while(){ chomp; push @lines, $_; } close(ASSIGN); foreach my $line (@lines){ my @fields = split(":", $line); next unless defined $fields[4]; my @parts = split("/", $fields[4]); if(defined($parts[5])){ my $address = $fields[0]; if( $address =~ /^(.?)[0-9]{1,}-(.*)/s ) { $address = $2; } else{ $address = ''; } my $accountname = $parts[5] . '@' . $parts[4]; if( $accounts->add_account( $parts[4], $accountname, $pwds{$fields[4]} || '' ) ) { my $res = $self->parse_dot_qmail_file( $fields[4] ); if ( defined $res->{maildir} ) { $accounts->set_account_maildir( $accountname, $fields[4] . $res->{maildir} ); } else{ $accounts->set_account_postbox( $accountname, 0 ); } if( defined $res->{redirect} ){ $accounts->set_account_redirect_satus( $accountname, 1 ); foreach my $redir(@{$res->{redirect}}) { $accounts->set_account_redirect( $accountname, $redir ); } } $accounts->set_account_quota( $accountname, $res->{quota} ) if defined $res->{quota}; } } } } sub isReserved{ my $dir = $_[0]; return 1 if $dir eq '.' ; return 1 if $dir eq '..'; return 1 if $dir eq 'new'; return 1 if $dir eq 'cur'; return 1 if $dir eq 'tmp'; return 1 if $dir eq 'tmp'; return 1 if $dir eq 'courierimapkeywords'; return 0; } sub backupFolderMsgs{ my( $dumpproc, $dumpdata, $backupFolder, $maildir, $imapFolder, $cnt, $flags ) = @_; Logging::debug( "Backup messages from dir $maildir" ); $imapFolder =~ s/\./\//sg; opendir( DH, $maildir ); my $dir; while( ( $dir = readdir( DH ) ) ){ if( not isReserved( $dir ) ){ $$cnt++; Logging::debug( "backup mail message $$cnt $maildir/$dir" ); #print "Found file $imapFolder/$dir\r\n"; my $msgFile = "$backupFolder/Message$$cnt.msg"; my $msgFlags = undef; if( $dir =~ m/(.)+?(\:2,)(.)+?$/sg ){ $msgFlags = $3; # HelpFuncs::log_admin( "Convert flags of '$dir' to $3" ); } if( copy( "$maildir/$dir", $msgFile ) ){ &$dumpproc( $dumpdata, $msgFile, $imapFolder, $msgFlags ); } else{ HelpFuns::log_error( "Cannot copy '$maildir/$dir' to $msgFile. The message wil be lost!" ); } } } closedir( DH ); } sub backupFolder{ my( $dumpproc, $dumpdata, $backupFolder, $maildir, $imapFolder, $cnt, $checkmaildir ) = @_; if( $checkmaildir and not (-e "$maildir/maildirfolder") ){ Logging::debug( "The path '$maildir' does not contain 'maildirfolder'. Skipped." ); return; } Logging::debug( "Backup content of mail dir $maildir" ); if( -e "$maildir/new" ) { backupFolderMsgs( $dumpproc, $dumpdata, $backupFolder, "$maildir/new", $imapFolder, $cnt, '' ); } if( -e "$maildir/cur" ) { backupFolderMsgs( $dumpproc, $dumpdata, $backupFolder, "$maildir/cur", $imapFolder, $cnt, 'S' ); } #backup child dir my $dir; opendir( DH, $maildir ); while( ($dir = readdir(DH)) ){ if( not isReserved( $dir ) ){ if( -d "$maildir/$dir" ){ backupFolder( $dumpproc, $dumpdata, $backupFolder, "$maildir/$dir", "$imapFolder$dir", $cnt, 1 ); } } } closedir( DH ); } sub backupMailContent{ my( $self, $dumpproc, $dumpdata, $backupFolder, $account ) = @_; my $maildir = ${$account}{maildir}; my $username = ${$account}{name}; my $cnt = 0; if( -e $maildir ){ Logging::debug( "Backup content of $username $maildir" ); if( $maildir =~ m/(.*)(\/$)/so ) { chop $maildir; backupFolder( $dumpproc, $dumpdata, $backupFolder, $maildir, "INBOX", \$cnt, 0 ); } else { Logging::debug( "Found mailbox format. Convert to temp dir $maildir" ); my $tempdir = HelpFuncs::getUniqueDir($backupFolder, 'pmm-qa-bmc-mail'); mkdir $tempdir, 0777 or HelpFuncs::dieWithError( "Cannot make dir $tempdir" ); Mbox2Mdir::convert( $maildir, $tempdir ); Logging::debug( "Mailbox converted." ); backupFolder( $dumpproc, $dumpdata, $backupFolder, $tempdir, "INBOX", \$cnt, 0 ); Logging::debug( "Mailbox dumped." ); chdir $backupFolder; HelpFuncs::deleteFolder( $tempdir ); } } else{ Logging::debug( "Cannot backup content of $username. Mail dir is not exists. $maildir" ); } Logging::debug( "Found $cnt messages for user $username" ); } sub get_rcpthosts { my $self = shift; open(RCPTHOSTS, '<'. QmailAgent::QMAIL_PATH . 'control/rcpthosts') or return(undef, 0, "Unable to open rcpthosts file."); my @results; while () { chomp; push(@results, $_); } close(RCPTHOSTS); return @results; } sub get_virtualdomains { my $self = shift; open(VDOMS, '<'. QmailAgent::QMAIL_PATH . 'control/virtualdomains') or return(undef, 0, "Unable to open virtualdomains file."); my @results; while () { chomp; my ($domain, $user) = split(':'); push(@results, { domain => $domain, user => $user } ); } close(VDOMS); return @results; } sub get_info_from_assign { my ($self, $assign) = @_; my (@lines, @results); my %pwds = load_passwdords(); $assign ||= QmailAgent::QMAIL_PATH . "users/assign"; open(ASSIGN, "< $assign"); while(){ chomp; push @lines, $_; } close(ASSIGN); foreach my $line (@lines){ my @fields = split(":", $line); next unless defined $fields[4]; my @parts = split("/", $fields[4]); if(defined($parts[5])){ my $address = $fields[0]; if( $address =~ /^(.?)[0-9]{1,}-(.*)/s ) { $address = $2; } else{ $address = ''; } push(@results, { domain => $parts[4], mailname => $parts[5], directory => $fields[4], address => $address, pwd => $pwds{$fields[4]} || '' } ); } } return @results; } sub parse_dot_qmail_file { my ($self, $directory) = @_; my (@lines, %results); %results = ( redirect => undef, maildir => undef, quota => undef); my $res = \%results; my $dotQmail = $directory."/.qmail"; if( -e $dotQmail) { Logging::debug( "Parse qmail user config $dotQmail" ); open( DOTQMAIL, "< $dotQmail" ); while() { chomp; push @lines, $_; } close(DOTQMAIL); foreach my $line(@lines) { if ($line=~/^\&(.*)/) { push @{$res->{redirect}}, $1; Logging::debug( "Add redirect:$1" ); } if ($line=~/^[\/\.](.*)/) { $res->{maildir} = $1; Logging::debug( "Set mail dir to:$1" ); } } } $dotQmail = $directory."/mbox.quota"; if( -e $dotQmail ){ Logging::debug( "Parse qmail quota $dotQmail" ); open( DOTQMAIL, "< $dotQmail" ); my $line = ; chomp ($line ); $res->{quota} = $line; close(DOTQMAIL); Logging::debug( "Set quota to:" . $res->{quota} ); } return $res; } sub load_passwdords { my ($self, $passwd_file) = @_; $passwd_file ||= QmailAgent::QMAIL_PATH . "users/poppasswd"; Logging::debug( "Parse password file $passwd_file" ); my %result; open(PASSWD, "< $passwd_file"); while() { chomp; my @fields = split(":"); $result{$fields[3]} = $fields[1]; } close(PASSWD); return %result; } sub get_passwd_by_dir { my ($self, $directory, $passwd_file) = @_; $passwd_file ||= QmailAgent::QMAIL_PATH . "users/poppasswd"; my $result; open(PASSWD, "< $passwd_file"); while() { chomp; my @fields = split(":"); if($fields[3] eq $directory) { $result = $fields[1]; } } close(PASSWD); return $result; } 1;