diff --git a/.gitignore b/.gitignore index 1d6ff14..f89d33a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +config.ini + # ---> Windows # Windows thumbnail cache files Thumbs.db @@ -35,30 +37,30 @@ $RECYCLE.BIN/ .nfs* # ---> macOS -*.DS_Store -.AppleDouble -.LSOverride - -# Icon must end with two \r -Icon - - -# Thumbnails -._* - -# Files that might appear in the root of a volume -.DocumentRevisions-V100 -.fseventsd -.Spotlight-V100 -.TemporaryItems -.Trashes -.VolumeIcon.icns -.com.apple.timemachine.donotpresent - -# Directories potentially created on remote AFP share -.AppleDB -.AppleDesktop -Network Trash Folder -Temporary Items -.apdisk +*.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk diff --git a/ImapMailMerge.php b/ImapMailMerge.php new file mode 100755 index 0000000..6f75f98 --- /dev/null +++ b/ImapMailMerge.php @@ -0,0 +1,47 @@ +#!/usr/bin/php +, with the destination clients + * specified via Return-To Headers. + * + * Note!!!: + * Each user must be whitelisted in IMAPMailMerge->safelist. + */ + +//error_reporting(-1); +//ini_set('display_errors', 'On'); +//set_error_handler("var_dump"); +//ini_set("mail.log", "/home4/dubtempo/custom_scripts/imapmailmerge/mail.log"); +//ini_set("mail.add_x_header", TRUE); + +$GoMailMerge = new MailMerge(); + +class MailMerge +{ + + public function __construct() + { + + require_once __DIR__ . '/autoload.php'; + + $imap_connection = new \DT\ImapMailMerge\Imap\Connection; + $process_safelist = new DT\ImapMailMerge\Main\ProcessSafelist; + $process_inbox = new DT\ImapMailMerge\Main\ProcessInbox; + $process_messages = new DT\ImapMailMerge\Main\ProcessMessages; + + } + +} \ No newline at end of file diff --git a/autoload.php b/autoload.php new file mode 100755 index 0000000..ec3e19d --- /dev/null +++ b/autoload.php @@ -0,0 +1,39 @@ +config = parse_ini_file( dirname(dirname(dirname(__FILE__))) . "/config/config.ini", true); + $this->sender = new \DT\ImapMailMerge\Imap\Sender; + $this->imapConnection(); + } + + private function imapConnection() + { + + if ( + self::$imapConn = imap_open( + $this->config['imapServer'] . "INBOX", + $this->config['mainEmail'], + $this->config['mainEmailPassword'] + ) + ) { + // silence on success + // this can be uncommented for testing + /* + $this->sender->imapEmailLog( + $this->config['adminEmail'], + "IMAPMailMerge: Connected to " . $this->config['mainEmail'] . "!", + "No issue there!" + ); + */ + } else { + $this->sender->imapEmailLog( + $this->config['adminEmail'], + "IMAPMailMerge: Couldn't Connect to " . $this->config['mainEmail'] . "!", + "Issue with the imap connection. Details:" . PHP_EOL . PHP_EOL . json_encode(imap_errors()) . PHP_EOL . PHP_EOL + ); + die(); + } + + } + +} \ No newline at end of file diff --git a/src/Imap/Folders.php b/src/Imap/Folders.php new file mode 100755 index 0000000..add24b0 --- /dev/null +++ b/src/Imap/Folders.php @@ -0,0 +1,31 @@ +config = parse_ini_file( dirname(dirname(dirname(__FILE__))) . "/config/config.ini", true); + $this->imap = new \DT\ImapMailMerge\Imap\Connection; + } + + public function imapDir($dir) + { + $mbox_pre = $this->imap; + $mbox = $mbox_pre::$imapConn; + imap_reopen( + $mbox, + "" . $this->config['imapServer'] . "$dir" + ); + } + + + public function imapCopyToSent() { + // this would be a cool feature to add + } +} \ No newline at end of file diff --git a/src/Imap/Sender.php b/src/Imap/Sender.php new file mode 100755 index 0000000..87b8ca8 --- /dev/null +++ b/src/Imap/Sender.php @@ -0,0 +1,53 @@ +imapSend( self::$config['scriptEmail'], $to, $subject, $body, $headers ); + } + + public function imapSend($from, $to, $subject, $body, $headers) + { + + // prevent user/script details being exposed in X-PHP-Script header + $oldphpself = (isset($SERVER['PHP_SELF'])) ? $SERVER['PHP_SELF'] : ""; + $oldremoteaddr = (isset($SERVER['REMOTE_ADDR'])) ? $SERVER['REMOTE_ADDR'] : ""; + + if (!empty($oldphpself) || !empty($oldremoteaddr)) { + unset($SERVER['PHP_SELF']); + unset($SERVER['REMOTE_ADDR']); + } + + // send the email + $sendmail = mail($to, $subject, $body, $headers, "-f $from"); + + // restore obfuscated server variables + $SERVER['PHP_SELF'] = $oldphpself; + $SERVER['REMOTE_ADDR'] = $oldremoteaddr; + + // process mail return + if ($sendmail == true) { + //echo "Message sent successfully to $to..."; + return '*status=Sent*:' . $to; + } else { + //echo "Message could not be sent to $to..."; + //print_r(error_get_last()); + return '*status=Not Sent*:' . $to; + } + + } + +} \ No newline at end of file diff --git a/src/Imap/Stringer.php b/src/Imap/Stringer.php new file mode 100755 index 0000000..bc53a6e --- /dev/null +++ b/src/Imap/Stringer.php @@ -0,0 +1,70 @@ +subject . PHP_EOL . PHP_EOL . "From:" . PHP_EOL . " " . $template->accountAddress . PHP_EOL . PHP_EOL . "To:" . PHP_EOL . " "; + $itr = 1; + if ($safeReturn) { + $results = explode('*status=', $safeReturn); + foreach ($results as $result) { + if(empty($result)) continue; + $resultSplit = explode('*:', $result); + $resultValue = $resultSplit[0]; + $addressValue = $resultSplit[1]; + $returnStr .= "#" . $itr . ": [" . $resultValue . "] " . $addressValue . PHP_EOL . PHP_EOL . " "; + $itr++; + } + } + $returnStr .= PHP_EOL . "*** NOTE *** : Please check your inbox for any bounced (ie: Return to Sender) emails. If an address you entered was not valid, you will see such a message and will need to resend your message to that client." . PHP_EOL; + + return $returnStr; + + } + + public function createMessageInfoStringOld($header, $template, $toClients) + { + return "Subject:" . PHP_EOL . " " . $template->subject . PHP_EOL . PHP_EOL + . "From:" . PHP_EOL . " " . $template->accountAddress . PHP_EOL . PHP_EOL + . "To:" . PHP_EOL . " " . $this->createReplyToMessageString($toClients) . PHP_EOL . PHP_EOL; + //. "Body:" . PHP_EOL . " " . $template->body . PHP_EOL . PHP_EOL; + } + + public function createReplyToMessageString($toClients = "") + { + $returnStr = ""; + $itr = 1; + if ($toClients) { + foreach ($toClients as $addressKey => $addressValue) { + $returnStr .= "#" . $itr . ": " . $addressValue . PHP_EOL . PHP_EOL . " "; + $itr++; + } + } + return $returnStr; + } +} \ No newline at end of file diff --git a/src/Main/ProcessInbox.php b/src/Main/ProcessInbox.php new file mode 100755 index 0000000..706541b --- /dev/null +++ b/src/Main/ProcessInbox.php @@ -0,0 +1,48 @@ +config = parse_ini_file( dirname(dirname(dirname(__FILE__))) . "/config/config.ini", true); + $this->imap = new \DT\ImapMailMerge\Imap\Connection; + $this->folders = new \DT\ImapMailMerge\Imap\Folders; + + $this->imapProcessInbox(); + + } + + public function imapProcessInbox() + { + + $this->folders->imapDir('INBOX'); + + $mbox_pre = $this->imap; + $mbox = $mbox_pre::$imapConn; + + $numMessages = imap_num_msg($mbox); + + for ($i = $numMessages; $i > 0; $i--) { + $msgId = imap_uid($mbox, $i); + $header = (imap_header($mbox, $i)) ? imap_header($mbox, $i) : ""; + $account = (isset($header->from[0])) ? $header->from[0] : ""; + $accountSlug = strtolower($account->mailbox); + + if (in_array($accountSlug, $this->config['safelist'])) { + imap_mail_move($mbox, $msgId, 'INBOX.' . $accountSlug, CP_UID); + imap_expunge($mbox); + } + + } + + imap_expunge($mbox); + + } +} \ No newline at end of file diff --git a/src/Main/ProcessMessages.php b/src/Main/ProcessMessages.php new file mode 100755 index 0000000..d0885db --- /dev/null +++ b/src/Main/ProcessMessages.php @@ -0,0 +1,191 @@ +config = parse_ini_file( dirname(dirname(dirname(__FILE__))) . "/config/config.ini", true); + $this->imap = new \DT\ImapMailMerge\Imap\Connection; + $this->sender = new \DT\ImapMailMerge\Imap\Sender; + $this->folders = new \DT\ImapMailMerge\Imap\Folders; + $this->stringer = new \DT\ImapMailMerge\Imap\Stringer; + + $this->imapProcessMessages(); + + } + + public function imapProcessMessages() + { + + for ($i = count($this->config['safelist']); $i > 0; $i--) { + + $accountSlug = $this->config['safelist'][$i-1]; + $accountFolder = 'INBOX.' . $accountSlug; + + $this->folders->imapDir($accountFolder); + + $mbox_pre = $this->imap; + $mbox = $mbox_pre::$imapConn; + + $numMessages = imap_num_msg($mbox); + + if (($numMessages) === 0) { + continue; + } + + for ($i = $numMessages; $i > 0; $i--) { + $msgId = imap_uid($mbox, $i); + + $header = (imap_header($mbox, $i)) ? imap_header($mbox, $i) : ""; + $structure = (imap_fetchstructure($mbox, $i)) ? imap_fetchstructure($mbox, $i) : ""; + + $account = (isset($header->from[0])) ? $header->from[0] : ""; + $toClients = $this->stringer->sanitizeEmailAddresses($header->reply_toaddress); + + $accountSafeName = (isset($account->personal)) ? $account->personal : ""; + + $template = new \StdClass(); + $template->{"header"} = (isset($header)) ? $header : ""; + $template->{"body"} = (imap_body($mbox, $i)) ? imap_body($mbox, $i) : ""; + $template->{"body_text"} = imap_fetchbody($mbox, $i, 1); + $template->{"body_html"} = imap_fetchbody($mbox, $i, 2); + $template->{"subject"} = (isset($header->subject)) ? $header->subject : ""; + $template->{"accountFrom"} = imap_rfc822_write_address($account->mailbox, $account->host, $accountSafeName); + $template->{"accountAddress"} = $account->mailbox . '@' . $account->host; + + // get boundary from header structure + + $params = $structure->parameters; + + foreach ($params as $itr => $keys) { + foreach ($keys as $key => $value) { + if ($key === 'attribute' && $value === 'boundary') { + $template->{"boundary"} = $keys->value; + } + } + } + + // get plain text and html charsets + + $headers = "Reply-To: $template->accountAddress" . PHP_EOL; + $headers .= "From: $template->accountFrom" . PHP_EOL; + $headers .= "Organization: " . $this->config['organization'] . PHP_EOL; + $headers .= "MIME-Version: 1.0" . PHP_EOL; + $headers .= "Content-Transfer-Encoding: binary" . PHP_EOL; + //$headers .= "Return-Path: <" . $template->accountAddress . ">" . PHP_EOL; + //$headers .= "X-PHP-Script: " . PHP_EOL; + $headers .= "X-Mailer: PHP". phpversion() . PHP_EOL; + $headers .= "X-Priority: 1". PHP_EOL; + $headers .= "X-MSMail-Priority: Normal". PHP_EOL; + $headers .= "Importance: Normal". PHP_EOL; + + if ($template->body_html) { + $headers .= "Content-Type: multipart/alternative; charset=ISO-8859-1; boundary=" . $template->boundary . PHP_EOL; + } elseif ($template->body_text) { + $headers .= "Content-Type: text/plain; charset=US-ASCII" . PHP_EOL; + $headers .= "Content-Type: text/plain; charset=US-ASCII" . PHP_EOL; + $headers .= "Content-Transfer-Encoding: 7bit" . PHP_EOL; + } + + // test if no Reply To was set, if so, move to error and continue + + $replyToEmpty = false; + + if (empty($toClients)) { + $replyToEmpty = true; + } + + // break if attempting to send to more than the max limit of recipients + + if (count($toClients) > $this->config['limitRecipients']) { + imap_mail_move($mbox, $msgId, 'INBOX.'.$accountSlug.'.error', CP_UID); + imap_expunge($mbox); + $this->sender->imapEmailLog( + $template->accountFrom, + "IMAPMailMerge: Error!", + "Too Many Recipients in MailMerge Message" . PHP_EOL . PHP_EOL . PHP_EOL + . $this->stringer->createMessageInfoString($header, $template, $toClients) + ); + continue; + } + + if ($replyToEmpty) { + imap_mail_move($mbox, $msgId, 'INBOX.'.$accountSlug.'.error', CP_UID); + imap_expunge($mbox); + $this->sender->imapEmailLog( + $template->accountFrom, + "IMAPMailMerge: Error!", + "No Return-To In MailMerge Message" . PHP_EOL . PHP_EOL . PHP_EOL + . $this->stringer->createMessageInfoString($header, $template, $toClients) + ); + continue; + } + + // compose mail for each Reply To + + $safeReturn = ''; + foreach ($toClients as $replytoaddr) { + + $template->{"clientTo"} = $replytoaddr; + // below not used ??? + //$recipient = (isset($replytoaddr)) ? imap_rfc822_parse_adrlist($replytoaddr, 'netpaypayroll.com') : ""; + //$accountSafeName = (isset($recipient->personal)) ? $recipient->personal : ""; + + // send email + + $safeReturn .= $this->sender->imapSend( + $template->accountAddress, + $template->clientTo, + $template->subject, + $template->body, + $headers + ); + + } + + // move email + + if (strpos($safeReturn, 'Not Sent')) { + imap_mail_move($mbox, $msgId, 'INBOX.'.$accountSlug.'.fail', CP_UID); + imap_expunge($mbox); + $this->sender->imapEmailLog( + $template->accountFrom, + "IMAPMailMerge: Fail!", + "Problem Sending MailMerge Message" . PHP_EOL . PHP_EOL . PHP_EOL + . $this->stringer->createMessageInfoString($header, $template, $safeReturn) + ); + } elseif (strpos($safeReturn, 'Sent')) { + imap_mail_move($mbox, $msgId, 'INBOX.'.$accountSlug.'.success', CP_UID); + imap_expunge($mbox); + $this->sender->imapEmailLog( + $template->accountFrom, + "IMAPMailMerge: Success!", + "Sucessfully Sent MailMerge Message" . PHP_EOL . PHP_EOL . PHP_EOL + . $this->stringer->createMessageInfoString($header, $template, $safeReturn) + ); + } else { + imap_mail_move($mbox, $msgId, 'INBOX.'.$accountSlug.'.error', CP_UID); + imap_expunge($mbox); + $this->sender->imapEmailLog( + $template->accountFrom, + "IMAPMailMerge: Error!", + "Problem Sending MailMerge Message" . PHP_EOL . PHP_EOL . PHP_EOL + . $this->stringer->createMessageInfoString($header, $template, $safeReturn) + ); + } + + } + + } + + } + +} \ No newline at end of file diff --git a/src/Main/ProcessSafelist.php b/src/Main/ProcessSafelist.php new file mode 100755 index 0000000..79699f6 --- /dev/null +++ b/src/Main/ProcessSafelist.php @@ -0,0 +1,67 @@ +config = parse_ini_file( dirname(dirname(dirname(__FILE__))) . "/config/config.ini", true); + $this->sender = new \DT\ImapMailMerge\Imap\Sender; + $this->imap = new \DT\ImapMailMerge\Imap\Connection; + $this->imapProcessSafelist(); + + } + + /** + * [imapProcessSafelist description] + * @return [type] [description] + */ + public function imapProcessSafelist() + { + + $mbox_pre = $this->imap; + $mbox = $mbox_pre::$imapConn; + + $dirs = imap_list($mbox, $this->config['imapServer'], "*"); + + for ($i = count($this->config['safelist']); $i > 0; $i--) { + $accountSlug = $this->config['safelist'][$i-1]; + + $accountDir = $this->config['imapServer'].'INBOX.'.$accountSlug; + $accountError = $this->config['imapServer'].'INBOX.'.$accountSlug.'.error'; + $accountSuccess = $this->config['imapServer'].'INBOX.'.$accountSlug.'.success'; + $accountFail = $this->config['imapServer'].'INBOX.'.$accountSlug.'.fail'; + + $accountDirs = array($accountDir, $accountError, $accountSuccess, $accountFail); + + foreach ($accountDirs as $dir) { + if (array_search($dir, $dirs) === false) { + if (! imap_createmailbox($mbox, $dir)) { + $this->sender->imapEmailLog( + $this->config['adminEmail'], + "IMAPMailMerge: Process SafeList Errored!", + "Issue with the imap directories." + ); + } else { + imap_subscribe($mbox, $dir); + } + } + } + + } + + } + +} \ No newline at end of file