#!/usr/bin/perl -I../../../../.. -I. use strict; use Pos::Webtop::Web::ContentModule::Mail::Proxy; use Pos::Webtop::Web::ContentModule::Mail::Email; use Pos::Webtop::Web::ContentModule::Mail::Smtp; package Pos::Webtop::Web::ContentModule::Mail::Pop3Proxy; our @ISA = qw( Pos::Webtop::Web::ContentModule::Mail::Proxy ); sub new { my $this = shift; my $class = ref($this) || $this; my ($self,$tb,$db,$smtp,$pop3,$username,$password) = @{{@_}}{qw/self tb db smtpServer pop3Server username password/}; if ( not defined $self ) { $self = {}; bless($self,$class); } # setup base class my %args = @_; $args{'self'} = $self; $args{'tb'} = $tb; $args{'db'} = $db; Pos::Webtop::Web::ContentModule::Mail::Proxy->new( %args ); # initialize attributes $self->{'smtpServer'} = $smtp; $self->{'pop3Server'} = $pop3; $self->{'username'} = $username; $self->{'password'} = $password; $self->{'loggedIn'} = 0; # Initialize userId my $client = $tb->getClient; $self->{'userId'} = $client->getUserId; $self->{'companyId'} = $client->getCustomerId; return $self; } sub login { my $self = shift; my $successful; if ($self->{'loggedIn'} == 1) { return 1; } my $conn = $self->connect( companyId => $self->{'companyId'}, host => $self->{'pop3Server'}, port => 110); if (not defined($conn)) { return undef; } # Read the banner if (($successful = $self->readUntilResponse) == 0) { return undef; } # Write user $self->writeLine(text => "USER " . $self->{'username'}); # Read the banner if (($successful = $self->readUntilResponse) == 0) { return undef; } # Write password $self->writeLine(text => "PASS " . $self->{'password'}); # Read the banner if (($successful = $self->readUntilResponse) == 0) { return undef; } if ($successful) { $self->{'loggedIn'} = 1; } return $successful; } sub logout { my $self = shift; $self->disconnect; return 1; } sub checkEmail { my $self = shift; my $successful; my $index = 0; if (not defined($self->login())) { return undef; } # Write list $self->writeLine(text => "LIST"); # Was the list successful? if (($successful = $self->readUntilResponse) == 0) { return undef; } # Read in the list my @list = $self->readUntilDot; my @uidl = (); # Write uidl $self->writeLine(text => "UIDL"); # Was the list successful? if ($self->readUntilResponse() == 1) { @uidl = $self->readUntilDot; } foreach my $list (@list) { my ($mailId, $mailSize) = split / /, $list; my $checkThisEmail = 1; my $uidl = ""; $mailSize =~ s/(\r|\n)//g; $uidl =~ s/(\r|\n)//g; # If we ahev a valid uidl list, check to see if we've already downloaded this message. if (scalar(@uidl) == scalar(@list)) { my ($existsId) = $self->{'db'}->query(sql => " select email_id from w_user_emails where uidl = '" . $self->{'db'}->escape(dirty => $uidl[$index]) . "' AND email_size = '" . $self->{'db'}->escape(dirty => $mailSize) . "' "); $uidl = $uidl[$index]; # If we find a match, do not check this email. if (defined($existsId)) { $checkThisEmail = 0; } } if ($checkThisEmail and $self->processNewEmail(mailId => $mailId, mailSize => $mailSize, uidl => $uidl)) { # This should be configurable XXX # Delete the email #$self->writeLine(text => "DELE $mailId"); # Was the list successful? #$self->readUntilResponse; } $index++; } return 1; } sub processNewEmail { my $self = shift; my ($mailId, $mailSize, $uidl) = @{{@_}}{qw/mailId mailSize uidl/}; my $successful; # Write retr $self->writeLine(text => "RETR $mailId"); # Was the list successful? if (($successful = $self->readUntilResponse) == 0) { return undef; } my @lines = $self->readUntilDot; my $email = Pos::Webtop::Web::ContentModule::Mail::Email->new(db => $self->{'db'}, tb => $self->{'tb'}); if (defined($email)) { $email->initializeFromLines(lines => \@lines); my ($existsId) = $self->{'db'}->query(sql => " select email_id from w_user_emails where message_id = '" . $email->getAttribute(name => 'Message-Id', chomp => 1) . "' AND user_id = '" . $self->{'userId'} . "' "); if (not defined($existsId)) { $email->flagAsNew(); if (not defined($email->synchronizeToDatabase(uidl => $uidl, emailSize => $mailSize))) { return undef; } } } return 1; } sub deleteEmail { my $self = shift; my ($emailId) = @{{@_}}{qw/emailId/}; my $email = Pos::Webtop::Web::ContentModule::Mail::Email->new(db => $self->{'db'}, tb => $self->{'tb'}); $email->removeFromDisk(emailId => $emailId); $self->{'db'}->do(sql => " delete from w_user_emails where email_id = '" . $self->{'db'}->escape(dirty => $emailId) . "' AND user_id = '" . $self->{'userId'} . "' "); return 1; } sub sendEmail { my $self = shift; my ($to,$cc,$bcc,$subject,$body,$attachments) = @{{@_}}{qw/to cc bcc subject body attachments/}; my $smtp = Pos::Webtop::Web::ContentModule::Mail::Smtp->new(db => $self->{'db'}, tb => $self->{'tb'}); if (not defined($smtp)) { return undef; } $smtp->startMessage(to => $to, cc => $cc, bcc => $bcc, subject => $subject, body => $body, attachments => $attachments); if (not defined($smtp->transmit(host => $self->{'smtpServer'}, port => 25))) { return undef; } return 1; } sub createFolder { my $self = shift; my ($folderName) = @{{@_}}{qw/folderName/}; # No \.\. allowed. if ($folderName =~ /\.\./) { return undef; } # Replace a leading slash if one is found $folderName =~ s/^\\//; $self->{'db'}->do(sql => " insert into w_user_email_folders ( folder_id, user_id, folder_name ) values( " . $self->{'db'}->getNextValueString( sequence => 'w_user_email_folders_pk') . ", '" . $self->{'userId'} . "', '" . $self->{'db'}->escape(dirty => $folderName) . "' )"); return 1; } sub getFolders { my $self = shift; my @folders = $self->{'db'}->query(sql => " select folder_name, ( select count(folder_id) from w_user_emails where folder_id = x1.folder_id AND is_new = 1 ) from w_user_email_folders x1 where user_id = '" . $self->{'userId'} . "' order by upper(folder_name) asc "); return @folders; } sub moveToFolder { my $self = shift; my ($emailsSc, $folderName) = @{{@_}}{qw/emails folderName/}; if (not defined($folderName)) { return 0; } my @emails = @{ $emailsSc }; foreach my $emailId (@emails) { my $email = Pos::Webtop::Web::ContentModule::Mail::Email->new(db => $self->{'db'}, tb => $self->{'tb'}); my $copy = Pos::Webtop::Web::ContentModule::Mail::Email->new(db => $self->{'db'}, tb => $self->{'tb'}); $email->initializeFromDatabase(emailId => $emailId); $copy->copy(source => $email); # Remove the old one. $email->removeFromDisk(emailId => $emailId); # Save to the new folder $copy->{'folder'} = $folderName; $copy->persistToDisk; # Synchronize the changes $copy->synchronizeToDatabase; } return 1; } sub getEmail { my $self = shift; my ($emailId) = @{{@_}}{qw/emailId/}; my @dbemail = $self->{'db'}->query(sql => " select email_id, message_id, email_to, email_from, email_subject, to_char(email_date, 'MM/DD/YYYY HH24:MI:SS'), is_new from w_user_emails where email_id = '" . $self->{'db'}->escape(dirty => $emailId) . "' AND user_id = '" . $self->{'userId'} . "' "); if (scalar(@dbemail) == 0) { return undef; } my %hash; my $email = Pos::Webtop::Web::ContentModule::Mail::Email->new(db => $self->{'db'}, tb => $self->{'tb'}); $hash{'EMAIL_ID'} = @{ $dbemail[0] }[0]; $hash{'MESSAGE_ID'} = @{ $dbemail[0] }[1]; $hash{'EMAIL_TO'} = @{ $dbemail[0] }[2]; $hash{'EMAIL_FROM'} = @{ $dbemail[0] }[3]; $hash{'EMAIL_SUBJECT'} = @{ $dbemail[0] }[4]; $hash{'EMAIL_DATE'} = @{ $dbemail[0] }[5]; $hash{'IS_NEW'} = @{ $dbemail[0] }[6]; $email->initializeFromDatabase(emailId => $hash{'EMAIL_ID'}); $hash{'EMAIL_ATTACHMENTS'} = $email->getAttachments; $hash{'EMAIL_BODY'} = $email->getBody; $hash{'EMAIL_BODY_ENCODING'} = $email->getBodyEncoding; return %hash; } sub getPreviewEmails { my $self = shift; my ($max, $folder) = @{{@_}}{qw/max folder/}; my @emails = $self->{'db'}->query(sql => " select email_id, message_id, email_to, email_from, email_subject, to_char(email_date, 'MM/DD/YYYY HH24:MI:SS'), is_new from w_user_emails where user_id = '" . $self->{'userId'} . "' " . ((defined($folder)) ? " AND folder_id = ( select folder_id from w_user_email_folders where folder_name = '" . $self->{'db'}->escape(dirty => $folder) . "' AND user_id = '" . $self->{'userId'} . "' )" : "") . " order by email_date desc"); return @emails; } sub getAdjacentEmails { my $self = shift; my ($emailId) = @{{@_}}{qw/emailId/}; my @emails = $self->{'db'}->query(sql => " select email_id from w_user_emails where folder_id = ( select folder_id from w_user_emails where email_id = '" . $self->{'db'}->escape(dirty => $emailId) . "' AND user_id = '" . $self->{'userId'} . "' ) AND user_id = '" . $self->{'userId'} . "' order by email_date desc "); my $prevId = undef; my $nextId = undef; my $counter = 0; foreach my $id (@emails) { if ($id == $emailId) { if ($counter > 0) { $prevId = $emails[$counter-1]; } if ($counter+1 < scalar(@emails)) { $nextId = $emails[$counter+1]; } last; } $counter++; } my %hash; $hash{'PREV'} = $prevId; $hash{'NEXT'} = $nextId; return \%hash; } sub cacheCredentials { my $self = shift; my ($username, $password) = @{{@_}}{qw/username password/}; $self->{'db'}->do(sql => " update w_user_email_settings_pop3 set username = '" . $self->{'db'}->escape(dirty => $username) . "', password = '" . $self->{'db'}->escape(dirty => $password) . "' where user_id = '" . $self->{'userId'} . "' "); } sub markEmailAsRead { my $self = shift; my ($emailId) = @{{@_}}{qw/emailId/}; $self->{'db'}->do(sql => " update w_user_emails set is_new = '0' where email_id = '" . $self->{'db'}->escape(dirty => $emailId) . "' AND user_id = '" . $self->{'userId'} . "' "); return 1; } sub doesFolderExist { my $self = shift; my ($folderName) = @{{@_}}{qw/folderName/}; my ($folderId) = $self->{'db'}->query(sql => " select folder_id from w_user_email_folders where folder_name = '" . $self->{'db'}->escape(dirty => $folderName) . "' AND user_id = '" . $self->{'userId'} . "' "); if (defined($folderId) and $folderId) { return 1; } return 0; } sub getNextOutgoingMailId { my $self = shift; my $id = ""; foreach my $a (1..32) { $id .= int(rand(9)); } return $id; } sub readUntilResponse { my $self = shift; my $success = 0; my $line; my $count = 0; while (defined($line = $self->readLine)) { $count++; if ($line =~ /^(\+|-)(OK|FAIL|ERR)/i) { last; } } if ($count == 0) { $success = 1; } elsif ($line =~ /^\+OK/i) { $success = 1; } else { chomp($line); print STDERR "POP3: Got err from server: $line\n"; $success = 0; } return $success; } sub readUntilDot { my $self = shift; my $success = 0; my @lines; my $line; while (defined($line = $self->readLine)) { if ($line eq ".\r\n" or $line eq ".\n") { last; } push(@lines, $line); } return @lines; } 1;