Source for file getmail.php
Documentation is available at getmail.php
* 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:
* <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>
* 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 tblue246: Tilman Blumenbach
* @copyright (c)2003-2010 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.44 2010/02/08 17:50:51 efy-yury 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', 'ItemList' );
load_class( '_ext/mime_parser/rfc822_addresses.php', 'rfc822_addresses_class' );
load_class( '_ext/mime_parser/mime_parser.php', 'mime_parser_class' );
if( !$Settings->get( 'eblog_enabled' ) )
echo
T_( 'Blog by email feature is not enabled.' );
param( 'test', 'integer', 0 );
* Subject of the current email message
* @global string $subject
* post date of current message
* @global string $post_date
* message content of current email that is going to be posted
* @global string $content
* define colour constants for messages
define( 'WARNING', 'orange' );
// if it's not called by a logged in user override test settings
if( !isset
( $current_User ) ||
!$current_User->check_perm( 'options', 'edit', true ) )
* 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);
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 );
$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 )
if( $test >
0 &&
$color )
echo
'<font color="'.
$color.
'">';
if( $test >
0 &&
$color )
* 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
// Try to get from environment variable
if( !empty( $_ENV['TMP'] ) )
else if( !empty( $_ENV['TMPDIR'] ) )
else if( !empty( $_ENV['TEMP'] ) )
// Detect by creating a temporary file
// Try to use system's temporary directory
// as random name shouldn't exist
* 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 .=
'/';
$path =
$dir .
$prefix .
mt_rand();
} while( !mkdir( $path, $mode ) );
* 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
// write to these globals
$subject =
$header['Subject'];
$ddate =
$header['Date'];
$prefix =
$Settings->get( 'eblog_subject_prefix' );
// 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))
if( ! isset
( $dmonths[$match[3]] ) )
$ddate_m =
$dmonths[$match[3]];
$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 );
* 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 )
foreach( $mailAttachments as $attachment )
$filename =
strtolower( $attachment['FileName'] );
$filename =
tempnam( $mediadir, 'upload' ) .
'.' .
$attachment['SubType'];
$filename =
preg_replace( '/[^a-z0-9\-_.]/', '-', $filename );
// Check valid filename/extension: (includes check for locked filenames)
$return =
false; // return: at least one error. try with next attachment
// if file exists count up a number
$prename =
substr( $filename, 0, strrpos( $filename, '.' ) ).
'-';
$sufname =
strrchr( $filename, '.' );
$filename =
$prename.
$cnt.
$sufname;
echo_message( '✘ file already exists. Changing filename to: ' .
$filename , WARNING, 2 );
if( !rename( $attachment['DataFile'], $mediadir .
$filename ) )
$return =
false; // return: at least one error. try with next attachment
$chmod =
$Settings->get( 'fm_default_chmod_file' );
$content .=
'<img src="'.
$media_url.
$filename.
'" '.
$imginfo[3].
' />';
$content .=
'<a href="'.
$media_url.
$filename.
'">'.
basename($filename).
'</a>';
* 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
$title =
$alternate_title;
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 );
* Prepare the connection string.
$mailserver =
'{' .
$Settings->get( 'eblog_server_host' ) .
':' .
$Settings->get( 'eblog_server_port' );
switch($Settings->get('eblog_encrypt'))
switch($Settings->get('eblog_method'))
// imap needs no additional options
if($Settings->get('eblog_novalidatecert'))
$mailserver .=
'/novalidate-cert';
// Connect to mail server
$mbox =
@imap_open( $mailserver, $Settings->get( 'eblog_username' ), $Settings->get( 'eblog_password' ) );
echo_message( sprintf( /* TRANS: %s is the error message */ T_( 'Connection failed: %s' ), imap_last_error()), ERROR, 0 );
// Read messages from server
$imap_obj =
imap_check( $mbox );
for ( $index =
1; $index <=
$imap_obj->Nmsgs; $index++
)
// save mail to disk because with attachments could take up much RAM
imap_savebody( $mbox, $tmpMIME, $index );
$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;
'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 ) )
if( ! $mimeParser->Analyze( $decodedMIME[0], $parsedMIME ) )
// var_dump($parsedMIME);
// the following helps me debugging
//pre_dump($decodedMIME[0], $parsedMIME);
// TODO: handle type == "message" recursively
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 );
break; // stop after first alternative
elseif( $parsedMIME['Type'] ==
'text' )
echo_message( 'Plain-text message part saved as ' .
$parsedMIME['DataFile'], INFO, 3 );
if( isset
( $parsedMIME['Attachments'] ) &&
count($parsedMIME['Attachments']) )
foreach( $parsedMIME['Attachments'] as $attachment )
echo_message( 'Attachment: ' .
$attachment['FileName'] .
' stored as ' .
$attachment['DataFile'], INFO, 3 );
$warning_count =
count( $mimeParser->warnings );
foreach ($mimeParser->warnings as $k =>
$v)
// 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] );
$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;
echo_message( '✘ Wrong login or password. First line of text in email must be in the format "username:password".', ERROR, 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.
$current_User =
& $UserCache->get_by_login( $user_login );
$post_category =
$Settings->get( 'eblog_default_category' );
$blog_ID =
get_catblog( $post_category ); // TODO: should not die, if cat does not exist!
$Blog =
$BlogCache->get_by_ID( $blog_ID, false, false );
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, $blog_ID ) )
$mediadir =
$Blog->get_media_dir();
processAttachments( $parsedMIME['Attachments'], $mediadir, $Blog->get_media_url(), $Settings->get('eblog_add_imgtag') );
echo_message( T_( 'Unable to access media directory. No attachments processed.' ), ERROR, 0 );
// CHECK and FORMAT content
if( ( $error =
$Messages->get_string( T_( 'Cannot post, please correct these errors:' ), '', 'error' ) ) )
$Messages->clear( 'error' );
// 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();
imap_delete( $mbox, $index );
* Revision 1.44 2010/02/08 17:50:51 efy-yury
* Revision 1.43 2010/01/30 18:55:15 blueyed
* Fix "Assigning the return value of new by reference is deprecated" (PHP 5.3)
* Revision 1.42 2009/09/26 12:00:42 tblue246
* Revision 1.41 2009/09/25 07:32:51 efy-cantor
* replace get_cache to get_*cache
* Revision 1.40 2009/09/14 14:02:20 efy-arrin
* Included the ClassName in load_class() call with proper UpperCase
* Revision 1.39 2009/08/29 12:23:54 tblue246
* - Implemented checking of previously (mostly) ignored blog_media_(browse|upload|change) permissions.
* - files.ctrl.php: Removed redundant calls to User::check_perm().
* - XML-RPC APIs: Added missing permission checks.
* - items.ctrl.php: Check permission to edit item with current status (also checks user levels) for update actions.
* - XML-RPC client: Re-added check for zlib support (removed by update).
* - XML-RPC APIs: Corrected method signatures (return type).
* - Fixed wrong permission description in blog user/group permissions screen.
* - Removed wrong TRANS comment
* - de-DE: Fixed bad translation strings (double quotes + HTML attribute = mess).
* - Suppress warnings generated by move_uploaded_file().
* - File browser: Hide link to upload screen if no upload permission.
* - Further code optimizations.
* Revision 1.38 2009/08/26 16:52:15 tblue246
* Revision 1.37 2009/03/15 15:04:22 tblue246
* Revision 1.36 2009/03/08 23:57:35 fplanque
* Revision 1.35 2009/03/05 19:20:30 blueyed
* getmail.php: drop T_ usage of uncommon messages (where level>0). This saves the translators 42(!) translations. Also fixed translations to use sprintf and markers for variables.
* Revision 1.34 2009/03/03 20:01:41 blueyed
* getmail.php: minor: whitespace fixes, mostly coding style.
* Revision 1.33 2009/03/03 20:00:27 blueyed
* getmail.php: doc, minor cleanup, TODOs
* Revision 1.32 2009/01/23 22:52:29 tblue246
* Blog by mail: Ensure the month name in the "Date" header is valid.
* Revision 1.31 2008/12/31 16:04:04 tblue246
* Moving external classes needed by the blog by mail feature to inc/_ext.
* Revision 1.30 2008/12/31 15:21:24 blueyed
* TODO: please move ext. libs
* Revision 1.29 2008/12/18 01:04:17 blueyed
* getmail.php: drop $newline param for echo_message, which was true always. Small translation fix. Whitespace fixes.
* Revision 1.28 2008/10/06 11:02:27 tblue246
* Blog by mail now supports POP3 & IMAP, SSL & TLS