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/README.md b/README.md new file mode 100644 index 0000000..dc499f6 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# Email warmup bot diff --git a/autoload.php b/autoload.php new file mode 100644 index 0000000..431adea --- /dev/null +++ b/autoload.php @@ -0,0 +1,15 @@ +imap, + $credentials->login, + $credentials->password + ); + + // set the callback for every e-mail + $spooler->setEmailHandler( function ( $body, $headers, $info ) use ( $credentials ) { + + message( "connected" ); + + // complete message + $email_raw = $headers . $body; + + // '' + $return_path = null; + $subject = null; + + $parser = mailparse_msg_create(); // MUST be destroyed at the end of the script + mailparse_msg_parse( $parser, $email_raw ); + $structure = mailparse_msg_get_structure( $parser ); // Ex. ["1", "1.1", "1.2"] + foreach( $structure as $part_label ) { // Search among each e-mail part + $part = mailparse_msg_get_part( $parser, $part_label ); // Parse a specified part + $part_data = mailparse_msg_get_part_data( $part ); // Get parsed part data, header and meta values + $headers = $part_data['headers']; + + // asd + $subject = $headers['subject'] ?? null; + + $return_path = $headers[ 'return-path' ] + ?? $headers[ 'from' ] + ?? null; + + if( $return_path ) { + + // sometime this is a damn array + if( is_array( $return_path ) ) { + $return_path = $return_path[0]; + } + + break; + } + } + + // extract 'something@asd.it' from '' or 'Foo ' + $return_path_email = null; + if( $return_path ) { + $return_path_data = mailparse_rfc822_parse_addresses( $return_path ); + foreach( $return_path_data as $email_data ) { + + // address infos + $return_path_address = $email_data['address']; + break; + } + + } + + if( $return_path_address ) { + + $reply_message = "Ciao bello!\n"; + $reply_message .= reply_email( $body ); + + message( sprintf( + "trying to send email from %s to $return_path_address", + $credentials->from + ) ); + + //TODO: in reply to + // https://stackoverflow.com/questions/45690336/do-all-email-clients-use-in-reply-to-field-in-email-header + + + $result = NetSMTPSender::send( $credentials, $return_path_address, "Re: $subject", $reply_message, $additional_headers = '', $more = '' ); + if( $result ) { + message( sprintf( + "sent email to %s", + $return_path_address + ) ); + } + + $delete = true; + } else { + $delete = false; + } + + return $delete; + } ); + + // open the connection + $spooler->open(); + + // just process all and then quit + $spooler->processAll(); + + // close the connection + $spooler->close(); + + message( "wait" ); + + // wait some time + sleep( IMAPBOT_CYCLE_SLEEP ); + + } + + } catch( Exception $e ) { + + printf( + "SMTP bot error (%s): %s\n", + get_class( $e ), + $e->getMessage() + ); + + printf( + " Trace: %s\n", + $e->getTraceAsString() + ); + + // we can't just stop looping because sometime the user sends a wrong command and we get an Exception + // $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 + message( "quit after SIG $signo" ); + + // quit + if( $GLOBALS['error'] ) { + exit( 1 ); + } else { + exit( 0 ); + } +} + +/** + * Print a message to standard output with a date + * + * @param string $message + */ +function message( $message ) { + printf( "[%s] %s\n", date( 'c' ), $message ); +} + +function strippa_minchia( $message ) { + throw new Exception( "to be implemented asd to strip the minch" ); +} + +/** + * asd + * >asd + */ +function reply_email( $message ) { + + $message = trim( $message ); + + $message = str_replace( "\n", "\n>", ">$message" ); + + return $message; +} diff --git a/config-example.php b/config-example.php new file mode 100644 index 0000000..8e6b2c4 --- /dev/null +++ b/config-example.php @@ -0,0 +1,33 @@ + 'Valerio Bozzolan 1', + 'from' => 'whoaaaaa@succhia.cz', + 'login' => 'whoaaaaa@succhia.cz', + 'host' => 'ssl://mail.reyboz.it', + 'port' => 465, + 'auth' => 'PLAIN', + 'imap' => '{mail.reyboz.it:993/imap/ssl}INBOX', + 'password' => 'REDACTED REDACTED REDACTED', +] ); + +// define another mailbox +MailboxCredentialsCollector::addFromArgs( [ + 'name' => 'Valerio Bozzolan 2', + 'from' => 'whoaaaab@succhia.cz', + 'login' => 'whoaaaab@succhia.cz', + 'host' => 'ssl://mail.reyboz.it', + 'port' => 465, + 'auth' => 'PLAIN', + 'imap' => '{mail.reyboz.it:993/imap/ssl}INBOX', + 'password' => 'REDACTED REDACTED REDACTED', +] ); diff --git a/include/class-MailboxCredentials.php b/include/class-MailboxCredentials.php new file mode 100644 index 0000000..a8aa974 --- /dev/null +++ b/include/class-MailboxCredentials.php @@ -0,0 +1,43 @@ +host = $args['host']; + $asd->port = $args['port']; + $asd->from = $args['from']; + $asd->login = $args['login']; + $asd->password = $args['password']; + $asd->auth = $args['auth']; + $asd->debug = $args['debug'] ?? false; + $asd->name = $args['name'] ?? $args['from']; + $asd->imap = $args['imap']; + + return $asd; + } + + public function isDebug() { + return $this->debug ?? false; + } + +} diff --git a/include/class-MailboxCredentialsCollector.php b/include/class-MailboxCredentialsCollector.php new file mode 100644 index 0000000..ad23d1c --- /dev/null +++ b/include/class-MailboxCredentialsCollector.php @@ -0,0 +1,15 @@ + [ + 'verify_peer_name' => false, + 'verify_peer' => false, + ], + ]; + + if( ! ($smtp = new Net_SMTP( $credentials->host, $credentials->port, null, false, 0, $socket_options ) ) ) { + error_wp_net_smtp( + 'Unable to instantiate Net_SMTP object', + $smtp->getUserInfo() + ); + return false; + } + + if( $credentials->isDebug() ) { + $smtp->setDebug( true ); + } + + if( PEAR::isError( $e = $smtp->connect() ) ) { + error_wp_net_smtp( + 'Error connect', + $e->getMessage() + ); + return false; + } + + if( PEAR::isError( $e = $smtp->auth( $credentials->login, $credentials->password, $credentials->auth, true, '', true ) ) ) { + error_wp_net_smtp( + 'Error auth', + $e->getMessage() + ); + return false; + } + + if( PEAR::isError( $smtp->mailFrom( $credentials->from ) ) ) { + error_wp_net_smtp( + 'Error set from', + $res->getMessage() + ); + return false; + } + + foreach( $to as $i => $single_to ) { + if( filter_var( $single_to, FILTER_VALIDATE_EMAIL ) === false ) { + unset( $to[$i] ); + + error_wp_net_smtp( + 'Wrong e-mail address stripped out', + $single_to + ); + continue; + } + + if( PEAR::isError( $res = $smtp->rcptTo( $single_to ) ) ) { + error_wp_net_smtp( + 'Error set To', + $res->getMessage() + ); + return false; + } + } + + if( count( $to ) === 0 ) { + error_wp_net_smtp( 'No email sent', 'no addresses' ); + return false; + } + + $headers = [ + 'MIME-Version' => '1.0', + 'Subject' => $subject, + 'To' => implode( ',', $to ), + 'From' => sprintf( + '%s <%s>', + $credentials->name, + $credentials->from + ), + 'Content-Type' => sprintf( + 'text/plain;charset=%s', + 'utf-8' + ), + 'X-Mailer' => 'Net/SMTP.php via WordPress in Debian GNU/Linux asd', + ]; + + $merge = []; + foreach( $headers as $header => $value ) { + $value = trim( $value ); + $merge[] = sprintf('%s: %s', $header, $value); + } + $headers = $additional_headers . implode( "\r\n" , $merge ); + + $error = PEAR::isError( $smtp->data( "$headers\r\n$message" ) ); + + $smtp->disconnect(); + + return ! $error; + + } +} +