0byt3m1n1
Path:
/
data
/
applications
/
aps.bak
/
b2evolution
/
3.3.1-5
/
standard
/
htdocs
/
cron
/
[
Home
]
File: getmail.php
<?php /** * pop3-2-b2 mail to blog * * modified for 2.4.1 by Stephan Knauss. Contact me by PM in {@link http://forums.b2evolution.net/} (user stephankn) * or send a mail to stephankn at users.sourceforge.net * * Uses MIME E-mail message parser classes written by Manuel Lemos: {@link http://www.phpclasses.org/browse/package/3169.html} * * This script can be called with a parameter "test" to specify what * should be done and what level of debug output to generate: * <ul> * <li>level 0: default. Process everything, no debug output, no html (called by cronjob)</li> * <li>level 1: Test only connection to server, do not process messages</li> * <li>level 2: additionally process messages, but do not post</li> * <li>level 3: do everything with extended verbosity</li> * </ul> * Only messages for "level 0" should get marked for translation (using T_()). * * b2evolution - {@link http://b2evolution.net/} * Released under GNU GPL License - {@link http://b2evolution.net/about/license.html} * * @author Stephan Knauss * @author tblue246: Tilman Blumenbach * * @copyright (c)2003-2009 by Francois PLANQUE - {@link http://fplanque.net/} * This file built upon code from original b2 - http://cafelog.com/ * * @todo check different encodings. only tested with iso-8859-1 * @todo try more exotic email clients like mobile phones * @todo tested and working with thunderbird (text, html, signed), yahoo mail (text, html), outlook webmail, K800i * @todo Allow the user to choose whether to upload attachments to the blog media folder or to his user root. * * @version $Id: getmail.php,v 1.37 2009/03/15 15:04:22 tblue246 Exp $ */ /** * load b2evolution configuration */ require_once dirname( __FILE__ ) . '/../conf/_config.php'; require_once $inc_path . '_main.inc.php'; load_class('items/model/_itemlist.class.php'); load_funcs('files/model/_file.funcs.php'); load_class( '_ext/mime_parser/rfc822_addresses.php' ); load_class( '_ext/mime_parser/mime_parser.php' ); if( !$Settings->get( 'eblog_enabled' ) ) { echo T_( 'Blog by email feature is not enabled.' ); debug_info(); exit(); } param( 'test', 'integer', 0 ); /** * Subject of the current email message * * @global string $subject */ $subject = ''; /** * post date of current message * * @global string $post_date */ $post_date = ''; /** * message content of current email that is going to be posted * * @global string $content */ $content = ''; /**#@+ * define colour constants for messages */ define( 'INFO', 'black' ); define( 'SUCCESS', 'green' ); define( 'WARNING', 'orange' ); define( 'ERROR', 'red' ); /**#@-*/ // if it's not called by a logged in user override test settings if( !isset( $current_User ) || !$current_User->check_perm( 'options', 'edit', true ) ) { $test = 0; } /** * Whether to do real posting. * * It is set to true if the setting eblog_test_mode is set to false *and* * the test parameter is not set to 2. */ $do_real_posting = (!$Settings->get( 'eblog_test_mode' ) && $test != 2); if( ! $do_real_posting ) { echo_message( T_('You configured test mode in the settings or set $test to 2. Nothing will be posted to the database/mediastore nor will your inbox be altered.'), WARNING, 0 ); } if( $test > 0 ) { //error_reporting (0); // TODO: I don't find a header to include for this popup window. // There should exist one in b2evo. So right now no valid HTML $page_title = T_( 'Blog by email' ); echo '<html><head><title>' . $page_title . '</title></head><body>'; } /** * Print out a debugging message with optional HTML color added. * * This function only outputs any additional HTML (colors, <br />) if * $test is greater than 0. * * @global integer The test level * @param string $strmessage The message to print * @param string $color optional colour so use * @param integer $level optional level to limit output to that level */ function echo_message( $strmessage , $color = '', $level = 0 ) { global $test; if( $level <= $test ) { if( $test > 0 && $color ) { echo '<font color="'.$color.'">'; } echo $strmessage; if( $test > 0 && $color ) { echo '</font>'; } if( $test > 0 ) { echo '<br />'; } echo "\n"; } } /** * Provide sys_get_temp_dir for older versions of PHP (< 5.2.1). * * code posted on php.net by minghong at gmail dot com * Based on {@link http://www.phpit.net/article/creating-zip-tar-archives-dynamically-php/2/} * * @return string path to system temporary directory */ if( !function_exists( 'sys_get_temp_dir' ) ) { function sys_get_temp_dir() { // Try to get from environment variable if( !empty( $_ENV['TMP'] ) ) { return realpath( $_ENV['TMP'] ); } else if( !empty( $_ENV['TMPDIR'] ) ) { return realpath( $_ENV['TMPDIR'] ); } else if( !empty( $_ENV['TEMP'] ) ) { return realpath( $_ENV['TEMP'] ); } // Detect by creating a temporary file else { // Try to use system's temporary directory // as random name shouldn't exist $temp_file = tempnam( md5( uniqid( rand(), true ) ), '' ); if( $temp_file ) { $temp_dir = realpath( dirname( $temp_file ) ); unlink( $temp_file ); return $temp_dir; } else { return false; } } } } /** * Create a new directory with unique name. * This creates a new directory below the given path with the given prefix and a random number. * * @param string $dir base path to new directory * @param string $prefix prefix random number with this * @param integer $mode permissions to use * @return string path to created directory */ function tempdir( $dir, $prefix = 'tmp', $mode = 0700 ) { if( substr( $dir, -1 ) != '/' ) $dir .= '/'; do { $path = $dir . $prefix . mt_rand(); } while( !mkdir( $path, $mode ) ); return $path; } /** * Process Header information like subject and date of a mail. * * @global string The subject of the current message (write) * @global string The post date of the current message (write) * @global object b2evo settings (read) * @global integer The test level (read) * @param array $header header as set by mime_parser_class::Analyze() * @return bool true if valid subject prefix is detected */ function processHeader( &$header ) { // write to these globals global $subject, $post_date; // read these globals global $Settings, $test; $subject = $header['Subject']; $ddate = $header['Date']; $prefix = $Settings->get( 'eblog_subject_prefix' ); echo_message( 'Subject: ' . $subject, INFO, 3 ); if(substr($subject, 0, strlen($prefix)) !== $prefix) { echo_message( '✘ The subject prefix is not ' . '"' . $prefix . '"', WARNING, 2 ); return false; } // Parse Date. // TODO: dh> use strftime (after format validation)? or strptime (PHP>=5.1.0) // of the form '20 Mar 2002 20:32:37' if(!preg_match('#^(.{3}, )?(\d{2}) (.{3}) (\d{4}) (\d{2}):(\d{2}):(\d{2})#', $ddate, $match)) { echo_message(T_('Could not parse date header!'), ERROR, 0); //pre_dump($ddate); return false; } $dmonths = array( 'Jan' => 1, 'Feb' => 2, 'Mar' => 3, 'Apr' => 4, 'May' => 5, 'Jun' => 6, 'Jul' => 7, 'Aug' => 8, 'Sep' => 9, 'Oct' => 10, 'Nov' => 11, 'Dec' => 12, ); $ddate_H = $match[5]; $ddate_i = $match[6]; $ddate_s = $match[7]; if( ! isset( $dmonths[$match[3]] ) ) { echo_message( T_( 'Invalid month name in message date string.' ), ERROR, 0 ); return false; } $ddate_m = $dmonths[$match[3]]; $ddate_d = $match[2]; $ddate_Y = $match[4]; $ddate_U = mktime( $ddate_H, $ddate_i, $ddate_s, $ddate_m, $ddate_d, $ddate_Y ); $post_date = date( 'Y-m-d H:i:s', $ddate_U ); return true; } /** * process attachments by saving into media directory and optionally creating image tag in post * * @global string message content that is optionally manipulated by adding image tags * @global bool do we really post? * @global object global b2evo settings * @param array $mailAttachments array containing path to attachment files * @param string $mediadir path to media directory of blog as seen by file system * @param string $media_url url to media directory as seen by user * @param bool $add_img_tags should img tags be added (instead of adding a normal link) * @return bool true for sucessfull execution */ function processAttachments( $mailAttachments, $mediadir, $media_url, $add_img_tags = true ) { global $content; global $do_real_posting; global $Settings; $return = true; echo_message( 'Processing attachments', INFO, 3 ); foreach( $mailAttachments as $attachment ) { $filename = strtolower( $attachment['FileName'] ); if( $filename == '' ) { $filename = tempnam( $mediadir, 'upload' ) . '.' . $attachment['SubType']; echo_message( '➜ ' . sprintf('Attachment without name. Using "%s".', htmlspecialchars( $filename )), WARNING, 2 ); } $filename = preg_replace( '/[^a-z0-9\-_.]/', '-', $filename ); // Check valid filename/extension: (includes check for locked filenames) if( $error_filename = validate_filename( $filename, false ) ) { echo_message( '✘ ' . 'Invalid filename: '.$error_filename, WARNING, 2 ); $return = false; // return: at least one error. try with next attachment continue; } // if file exists count up a number $cnt = 0; $prename = substr( $filename, 0, strrpos( $filename, '.' ) ).'-'; $sufname = strrchr( $filename, '.' ); while( file_exists( $mediadir . $filename ) ) { $filename = $prename.$cnt.$sufname; echo_message( '✘ file already exists. Changing filename to: ' . $filename , WARNING, 2 ); ++$cnt; } if( $do_real_posting ) { echo_message( '➜ Saving file to: ' . htmlspecialchars( $mediadir . $filename ), INFO, 3 ); if( !rename( $attachment['DataFile'], $mediadir . $filename ) ) { echo_message( '✘ Problem saving upload to ' . htmlspecialchars( $mediadir . $filename ), WARNING, 2 ); $return = false; // return: at least one error. try with next attachment continue; } // chmod uploaded file: $chmod = $Settings->get( 'fm_default_chmod_file' ); @chmod( $mediadir . $filename, octdec( $chmod ) ); } $imginfo = @getimagesize($mediadir.$filename); echo_message('Attachment is an image: '.(is_array($imginfo) ? T_('yes') : T_('no')), INFO, 3 ); $content .= "\n"; if(is_array($imginfo) && $add_img_tags) { $content .= '<img src="'.$media_url.$filename.'" '.$imginfo[3].' />'; } else { $content .= '<a href="'.$media_url.$filename.'">'.basename($filename).'</a>'; } $content .= "\n"; } return $return; } /** * look inside message to get title for posting. * * The message could contain a xml-tag <code><title>sample title</title></code> to specify a title for the posting. * If not tag is found there could be a global $post_default_title containing a global default title. * If none of these is found then the specified alternate title line is used. * * @param string $content message to search for title tag * @param string $alternate_title use this string if no title tag is found * @return string title of posting * * @see $post_default_title */ function get_post_title( $content, $alternate_title ) { $title = xmlrpc_getposttitle( $content ); if( $title == '' ) { $title = $alternate_title; } return $title; } if( ! extension_loaded( 'imap' ) ) { echo_message( T_( 'The php_imap extension is not available to PHP on this server. Please load it in php.ini or ask your hosting provider to do so.' ), ERROR, 0 ); exit; } echo_message( 'Connecting and authenticating to mail server.', INFO, 1 ); /** * Prepare the connection string. */ $mailserver = '{' . $Settings->get( 'eblog_server_host' ) . ':' . $Settings->get( 'eblog_server_port' ); switch($Settings->get('eblog_encrypt')) { case 'ssl': $mailserver .= '/ssl'; break; case 'tls': $mailserver .= '/tls'; break; case 'none': default: $mailserver .= '/notls'; break; } switch($Settings->get('eblog_method')) { case 'pop3': case 'pop3a': $mailserver .= '/pop3'; break; case 'imap': default: // imap needs no additional options break; } if($Settings->get('eblog_novalidatecert')) { $mailserver .= '/novalidate-cert'; } $mailserver .= '}INBOX'; //pre_dump($mailserver); // Connect to mail server $mbox = @imap_open( $mailserver, $Settings->get( 'eblog_username' ), $Settings->get( 'eblog_password' ) ); if( ! $mbox ) { echo_message( sprintf( /* TRANS: %s is the error message */ T_( 'Connection failed: %s' ), imap_last_error()), ERROR, 0 ); exit(); } @imap_errors(); echo_message( '✔ Success', SUCCESS, 1 ); if( $test == 1 ) { echo_message( 'All tests completed.', INFO, 1 ); imap_close( $mbox ); exit(); } // Read messages from server echo_message( 'Reading messages from server.', INFO, 2 ); $imap_obj = imap_check( $mbox ); echo_message( ' ➜ ' . $imap_obj->Nmsgs . ' ' . 'messages', INFO, 2 ); $delMsgs = 0; for ( $index = 1; $index <= $imap_obj->Nmsgs; $index++ ) { echo_message( '<strong>Message #' . $index . ':</strong>', INFO, 2 ); $strbody = ''; $hasAttachment = false; // save mail to disk because with attachments could take up much RAM if(!($tmpMIME = tempnam( sys_get_temp_dir(), 'b2evoMail' ))) { echo_message( T_('Could not create temporary file.'), ERROR, 0 ); continue; } imap_savebody( $mbox, $tmpMIME, $index ); $tmpDirMIME = tempdir( sys_get_temp_dir(), 'b2evo' ); $mimeParser = new mime_parser_class; $mimeParser->mbox = 0; // Set to 0 for parsing a single message file $mimeParser->decode_bodies = 1; $mimeParser->ignore_syntax_errors = 1; $mimeParser->extract_addresses = 0; $MIMEparameters = array( 'File' => $tmpMIME, 'SaveBody' => $tmpDirMIME, // Save the message body parts to a directory 'SkipBody' => 1, // Do not retrieve or save message body parts ); if( !$mimeParser->Decode( $MIMEparameters, $decodedMIME ) ) { echo_message( sprintf(T_('MIME message decoding error: %s at position %d.'), $mimeParser->error, $mimeParser->error_position), ERROR, 0 ); rmdir_r( $tmpDirMIME ); unlink( $tmpMIME ); continue; } else { echo_message( '✔ MIME message decoding successful.', INFO, 3 ); if( ! $mimeParser->Analyze( $decodedMIME[0], $parsedMIME ) ) { echo_message( sprintf(T_('MIME message analyse error: %s'), $mimeParser->error), ERROR, 0 ); // var_dump($parsedMIME); rmdir_r( $tmpDirMIME ); unlink( $tmpMIME ); continue; } // the following helps me debugging //pre_dump($decodedMIME[0], $parsedMIME); if(!processHeader($parsedMIME)) { rmdir_r( $tmpDirMIME ); unlink($tmpMIME); continue; } // TODO: handle type == "message" recursively // mail is html if( $parsedMIME['Type'] == 'html' ){ foreach ( $parsedMIME['Alternative'] as $alternative ){ if( $alternative['Type'] == 'text' ){ echo_message( 'HTML alternative message part saved as ' . $alternative['DataFile'], INFO, 3 ); $strbody = imap_qprint( file_get_contents( $alternative['DataFile'] ) ); break; // stop after first alternative } } } // mail is plain text elseif( $parsedMIME['Type'] == 'text' ) { echo_message( 'Plain-text message part saved as ' . $parsedMIME['DataFile'], INFO, 3 ); $strbody = imap_qprint( file_get_contents( $parsedMIME['DataFile'] ) ); } // Check for attachments if( isset( $parsedMIME['Attachments'] ) && count($parsedMIME['Attachments']) ) { $hasAttachment = true; foreach( $parsedMIME['Attachments'] as $attachment ) { echo_message( 'Attachment: ' . $attachment['FileName'] . ' stored as ' . $attachment['DataFile'], INFO, 3 ); } } $warning_count = count( $mimeParser->warnings ); if( $warning_count > 0 ) { echo_message( '✘ ' . $warning_count . ' warnings during decode: ', WARNING, 2 ); foreach ($mimeParser->warnings as $k => $v) { echo_message( '✘ ' . 'Warning: ' . $v . ' at position ' . $k, WARNING, 3 ); } } } unlink( $tmpMIME ); // var_dump($strbody); // process body. First fix different line-endings (dos, mac, unix), remove double newlines $strbody = str_replace( array("\r", "\n\n"), "\n", $strbody ); $a_body = explode( "\n", $strbody, 2 ); // tblue> splitting only into 2 parts allows colons in the user PW $a_authentication = explode( ':', $a_body[0], 2 ); $content = trim( $a_body[1] ); echo_message( 'Message content:' . ' <code>' . htmlspecialchars( $content ) . '</code>', INFO, 3 ); $user_login = trim( $a_authentication[0] ); // TODO: dh> should the password really get trimmed here?! $user_pass = isset($a_authentication[1]) ? trim($a_authentication[1]) : null; // authenticate user echo_message( 'Authenticating user: ' . $user_login, INFO, 3 ); if( !user_pass_ok( $user_login, $user_pass ) ) { echo_message( sprintf(T_( 'Authentication failed for user %s.' ), htmlspecialchars($user_login)), ERROR, 0 ); echo_message( '✘ Wrong login or password. First line of text in email must be in the format "username:password".', ERROR, 3 ); rmdir_r( $tmpDirMIME ); continue; } else { echo_message( '✔ Success.', SUCCESS, 3 ); } $subject = trim( substr($subject, strlen($Settings->get( 'eblog_subject_prefix' ))) ); // remove content after terminator $eblog_terminator = $Settings->get( 'eblog_body_terminator' ); if( !empty( $eblog_terminator ) && ($os_terminator = strpos( $content, $eblog_terminator )) !== false) { $content = substr( $content, 0, $os_terminator ); } // check_html_sanity needs local user set. $UserCache = & get_Cache( 'UserCache' ); $current_User = & $UserCache->get_by_login( $user_login ); $post_title = get_post_title( $content, $subject ); if( ! ( $post_category = xmlrpc_getpostcategory( $content ) ) ) { $post_category = $Settings->get( 'eblog_default_category' ); } echo_message( 'Category ID: ' . htmlspecialchars( $post_category ), INFO, 3 ); $content = xmlrpc_removepostdata( $content ); $blog_ID = get_catblog( $post_category ); // TODO: should not die, if cat does not exist! echo_message( 'Blog ID: ' . $blog_ID, INFO, 3 ); $BlogCache = & get_Cache( 'BlogCache' ); $Blog = $BlogCache->get_by_ID( $blog_ID, false, false ); if( empty( $Blog ) ) { echo_message( sprintf( T_('Blog #%d not found!'), $blog_ID ), ERROR, 0 ); rmdir_r( $tmpDirMIME ); continue; } // Check permission: echo_message( sprintf( 'Checking permissions for user «%s» to post to Blog #%d', $user_login, $blog_ID ), INFO, 3 ); if( !$current_User->check_perm( 'blog_post!published', 'edit', false, $blog_ID ) || ( $hasAttachment && !$current_User->check_perm( 'files', 'add', false ) ) ) { echo_message( T_( 'Permission denied.' ), ERROR, 0 ); rmdir_r( $tmpDirMIME ); continue; } else { echo_message( '✔ Pass.', SUCCESS, 3 ); } // handle attachments if( $hasAttachment ) { $mediadir = $Blog->get_media_dir(); if( $mediadir ) { processAttachments( $parsedMIME['Attachments'], $mediadir, $Blog->get_media_url(), $Settings->get('eblog_add_imgtag') ); } else { echo_message( T_( 'Unable to access media directory. No attachments processed.' ), ERROR, 0 ); } } // CHECK and FORMAT content $post_title = check_html_sanity( trim( $post_title ), 'posting', false ); $content = check_html_sanity( trim( $content ), 'posting', $Settings->get( 'AutoBR' ) ); if( ( $error = $Messages->get_string( T_( 'Cannot post, please correct these errors:' ), '', 'error' ) ) ) { echo_message( $error, ERROR, 0 ); $Messages->clear( 'error' ); rmdir_r( $tmpDirMIME ); continue; } if( $do_real_posting ) { // INSERT NEW POST INTO DB: $edited_Item = & new Item(); $post_ID = $edited_Item->insert( $current_User->ID, $post_title, $content, $post_date, $post_category, array(), 'published', $current_User->locale ); // Execute or schedule notifications & pings: $edited_Item->handle_post_processing(); } echo_message( '✔ Message posting successful.', SUCCESS, 2 ); echo_message( '➜ Post title: ' . htmlspecialchars( $post_title ), INFO, 3 ); echo_message( '➜ Post content: ' . htmlspecialchars( $content ), INFO, 3 ); rmdir_r( $tmpDirMIME ); echo_message( 'Marking message for deletion: '.$index, INFO, 3 ); imap_delete( $mbox, $index ); ++$delMsgs; } if( $do_real_posting ) { imap_expunge( $mbox ); echo_message( sprintf('Deleted %d processed message(s) from inbox.', $delMsgs), INFO, 3 ); } imap_close( $mbox ); if( $test > 0 ) { // TODO: I don't find a footer to include in this popup. b2evo should include one... echo '</body>'; } ?>