diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b74decb --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/config.php diff --git a/bot.php b/bot.php new file mode 100644 index 0000000..0041a1c --- /dev/null +++ b/bot.php @@ -0,0 +1,167 @@ +#!/usr/bin/php +setEmailHandler( function ( $body, $headers, $info ) { + + # The following code is under Apache license (c) Phabricator, Phacility + # https://secure.phabricator.com/source/phabricator/browse/master/scripts/mail/mail_handler.php + # https://secure.phabricator.com/source/phabricator/browse/master/LICENSE + + // complete message + $message = $headers . $body; + + $parser = new MimeMailParser(); + $parser->setText( $message ); + + $content = array(); + foreach (array('text', 'html') as $part) { + $part_body = $parser->getMessageBody($part); + + if (strlen($part_body) && !phutil_is_utf8($part_body)) { + $part_headers = $parser->getMessageBodyHeaders($part); + if (!is_array($part_headers)) { + $part_headers = array(); + } + $content_type = idx($part_headers, 'content-type'); + if (preg_match('/charset="(.*?)"/', $content_type, $matches) || + preg_match('/charset=(\S+)/', $content_type, $matches)) { + $part_body = phutil_utf8_convert($part_body, 'UTF-8', $matches[1]); + } + } + + $content[$part] = $part_body; + } + + $headers = $parser->getHeaders(); + $headers['subject'] = phutil_decode_mime_header($headers['subject']); + $headers['from'] = phutil_decode_mime_header($headers['from']); + + $received = new PhabricatorMetaMTAReceivedMail(); + $received->setHeaders($headers); + $received->setBodies($content); + + $attachments = array(); + foreach ($parser->getAttachments() as $attachment) { + if (preg_match('@text/(plain|html)@', $attachment->getContentType()) && + $attachment->getContentDisposition() == 'inline') { + // If this is an "inline" attachment with some sort of text content-type, + // do not treat it as a file for attachment. MimeMailParser already picked + // it up in the getMessageBody() call above. We still want to treat 'inline' + // attachments with other content types (e.g., images) as attachments. + continue; + } + + $file = PhabricatorFile::newFromFileData( + $attachment->getContent(), + array( + 'name' => $attachment->getFilename(), + 'viewPolicy' => PhabricatorPolicies::POLICY_NOONE, + )); + $attachments[] = $file->getPHID(); + } + + $delete = true; + + try { + $received->setAttachments($attachments); + $received->save(); + $received->processReceivedMail(); + } catch (Exception $e) { + + // do not delete messages with errors + $delete = false; + + $received + ->setMessage(pht('EXCEPTION: %s', $e->getMessage())) + ->save(); + + throw $e; + } + + return $delete; +} ); + +// flag indicating that an error raised +$error = false; + +// flag indicating that we can continue to scan again the mailbox +$loop = true; + +do { + try { + // open the connection + $spooler->open(); + + // just process all and then quit + $spooler->processAll(); + + // close the connection + $spooler->close(); + + // wait some time + sleep( IMAPBOT_CYCLE_SLEEP ); + + } catch( Exception $e ) { + printf( + "SMTP bot error (%s): %s\n", + get_class( $e ), + $e->getMessage() + ); + $loop = false; + $error = true; + } + +} while( $loop ); + +/** + * Operating system signal handler + * + * @param $signo int + * @param $siginfo mixed + */ +function sig_handler( $signo, $siginfo ) { + + // stop looping + $GLOBALS['loop'] = false; + + // eventually close the spooler + $GLOBALS['spooler']->close(); + + // just warn about this signal + echo "Quitted by SIG $signo\n"; + + // quit + if( $GLOBALS['error'] ) { + exit( 1 ); + } else { + exit( 0 ); + } +} diff --git a/config-example.php b/config-example.php new file mode 100644 index 0000000..faaa878 --- /dev/null +++ b/config-example.php @@ -0,0 +1,8 @@ +