Seditio Source
Root |
./othercms/b2evolution_7.2.3/inc/cron/model/_post_by_mail.funcs.php
<?php
/**
 * This file implements the post by mail support functions.
 *
 * b2evolution - {@link http://b2evolution.net/}
 * Released under GNU GPL License - {@link http://b2evolution.net/about/gnu-gpl-license}
 *
 * @license GNU GPL v2 - {@link http://b2evolution.net/about/gnu-gpl-license}
 *
 * @copyright (c)2003-2020 by Francois Planque - {@link http://fplanque.com/}
 *
 * @package admin
 */
if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );

/**
 * Print out a debugging message with optional HTML color added
 *
 * @param string Message
 * @param boolean TRUE if it is called from cron
 * @param string Message type: 'success', 'warning', 'error', 'note', NULL - to use default text without addition style color
 */
function pbm_msg( $message, $cron = false, $type = NULL )
{
    global
$is_web, $pbm_messages;

   
// Log all messages to $pbm_messages no matter if we are in cron mode or not
    // We may use this report later, display or send to the blog owner
   
$pbm_messages[] = $message;

    if(
$cron )
    {    
// Append a message to cron log if we are in cron mode:
       
cron_log_append( $message, $type );
    }

    if(
$type == 'error' )
    {    
// Store an error message in system log:
       
syslog_insert( 'Get posts by email: '.$message, 'error' );
    }
}

/**
 * Connect to an IMAP or POP mail server
 *
 * @param boolean TRUE if script is executed by cron
 * @return resource $mbox
 */
function pbm_connect( $cron = false )
{
    if( !
extension_loaded( 'imap' ) )
    {    
// Exit here if imap extension is not loaded:
       
pbm_msg( '<b class="red">'.( 'IMAP extension is NOT loaded! ').'</b>', $cron );
        return
false;
    }

    global
$Settings;

   
$host = $Settings->get('eblog_server_host').':'.$Settings->get('eblog_server_port');
   
$mailserver = '{'.$host;

   
pbm_msg( sprintf( ('Connecting and authenticating to mail server %s'), '<b>'.$host.'</b>' ), $cron );

    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';

   
// Connect to mail server (one retry)
   
$mbox = @imap_open( $mailserver, $Settings->get('eblog_username'), $Settings->get('eblog_password'), NULL, 1 );

    if(
is_null(@get_resource_type($mbox)) )
    {    
// Not a resource
       
$error = imap_errors();
        if(
is_array($error) )
        {
           
$error = implode( "<br />\n", $error );
        }

       
pbm_msg( sprintf( ('Connection failed: %s'), $error ), $cron, 'error' );
        return
false;
    }
   
pbm_msg( '<b class="green">'.('Successfully connected!').'</b>', $cron );

    @
imap_errors();

    return
$mbox;
}


/**
 * Read messages from server and create posts
 *
 * @param resource $mbox created by pbm_connect() (by reference)
 * @param integer the number of messages to process
 * @param boolean TRUE if script is executed by cron
 * @return boolean true on success
 */
function pbm_process_messages( & $mbox, $limit, $cron = false )
{
    global
$Settings, $debug;
    global
$pbm_item_files, $pbm_messages, $pbm_items, $post_cntr, $del_cntr, $is_cron_mode;

    if( empty(
$is_cron_mode ) )
    {    
// This may take a very long time if there are many messages; No execution time limit:
        // (don't apply this for cron job because it uses a setting "Max execution time")
       
set_max_execution_time( 0 );
    }

   
// Are we in test mode?
   
$test_mode_on = $Settings->get('eblog_test_mode');

   
$post_cntr = 0;
   
$del_cntr = 0;
    for(
$index = 1; $index <= $limit; $index++ )
    {    
// Repeat for as many messages as allowed...
       
pbm_msg( '<hr /><h3>'.sprintf( ('Processing message %s:'), '#'.$index ).'</h3>', $cron );

       
$html_body = '';
       
$strbody = '';
       
$hasAttachment = false;
       
$hasRelated = false;

       
$pbm_item_files = array(); // reset the value for each new Item

        // Save email to a temporary file on hard drive, otherwise BIG attachments may take a lot of RAM:
       
if( ! ($tmpMIME = tempnam( sys_get_temp_dir(), 'b2evoMail' )) )
        {
           
pbm_msg( ('Could not create temporary file.'), $cron );
            continue;
        }
       
// Save the whole body of a specific message from the mailbox:
       
imap_savebody( $mbox, $tmpMIME, $index );

       
// Create random temp directory for message parts:
       
$tmpDirMIME = pbm_tempdir( sys_get_temp_dir(), 'b2evo_' );

       
// Instanciate mime_parser.php library:
       
$mimeParser = new mime_parser_class();
       
$mimeParser->mbox = 0;                        // Set to 0 for parsing a *single* RFC 2822 message
        // NOTE: Use function mb_decode_mimeheader() instead of decoder from mime_parser_class,
        // because the class cannot properly decode KOI8-R headers:
       
$mimeParser->decode_headers = ! function_exists( 'mb_decode_mimeheader' );            // Set to 1 if it is    necessary to decode message headers that may have non-ASCII    characters and use other character set encodings
       
$mimeParser->ignore_syntax_errors = 1;    // ignore syntax errors in    malformed messages.
       
$mimeParser->extract_addresses = 0;

       
$MIMEparameters = array(
               
'File' => $tmpMIME,
               
'SaveBody' => $tmpDirMIME,    // Save message body parts to a directory
               
'SkipBody' => 1,            // Do not retrieve or save message body parts
           
);

       
// Associative array to specify parameters for the messagedata parsing and decoding operation.
       
$MIMEparameters = array(
               
'File' => $tmpMIME,            // Name of the file from which the message data will be read.
               
'SaveBody' => $tmpDirMIME,    // Save message body parts to a directory
               
'SkipBody' => 1,                // 1 means the information about the message body part structure is returned in $decodedMIME below but it does not return any body data.
           
);

       
// STEP 1: Parse and decode message data and retrieve its structure:
       
if( !$mimeParser->Decode( $MIMEparameters, $decodedMIME ) )
        {    
// error:
           
pbm_msg( sprintf( ('MIME message decoding error: %s at position %d.'), $mimeParser->error, $mimeParser->error_position ), $cron, 'error' );
           
rmdir_r( $tmpDirMIME );
           
unlink( $tmpMIME );
            continue;
        }
        else
        {    
// the specified message data was parsed successfully:
           
pbm_msg( ('MIME message decoding successful'), $cron );

           
// STEP 2: Analyze (the first) parsed message to describe its contents:
           
if( ! $mimeParser->Analyze( $decodedMIME[0], $parsedMIME ) )
            {    
// error:
               
pbm_msg( sprintf( ('MIME message analyze error: %s'), $mimeParser->error ), $cron, 'error' );
               
rmdir_r( $tmpDirMIME );
               
unlink( $tmpMIME );
                continue;
            }

           
// Get message $subject and $post_date from headers (by reference)
           
if( ! pbm_process_header( $parsedMIME, $subject, $post_date, $cron ) )
            {    
// Couldn't process message headers:
               
rmdir_r( $tmpDirMIME );
               
unlink( $tmpMIME );
                continue;
            }

           
// TODO: handle type == "message" recursively
            // sam2kb> For some reason imap_qprint() demages HTML text... needs more testing
            // yura> I replaced imap_qprint() with quoted_printable_decode() to avoid notices about invalid quoted-printable sequence
            // yura> imap_qprint() and quoted_printable_decode() do empty the message text, thus they were deleted.

           
if( $parsedMIME['Type'] == 'html' )
            {    
// Mail is HTML:
               
if( $Settings->get('eblog_html_enabled') )
                {    
// HTML posting enabled
                   
if( $debug )
                    {    
// Display this info only in debug mode:
                       
pbm_msg( sprintf( ('HTML message part saved as %s'), $parsedMIME['DataFile'] ), $cron );
                    }
                   
$html_body = file_get_contents( $parsedMIME['DataFile'] );
                }

                foreach(
$parsedMIME['Alternative'] as $alternative )
                {    
// First try to get HTML alternative (when possible)
                   
if( $alternative['Type'] == 'html' && $Settings->get('eblog_html_enabled') )
                    {    
// HTML text:
                       
if( $debug )
                        {    
// Display this info only in debug mode:
                           
pbm_msg( sprintf( ('HTML alternative message part saved as %s'), $alternative['DataFile'] ), $cron );
                        }
                       
$strbody = file_get_contents( $alternative['DataFile'] );
                        break;
// stop after first alternative
                   
}
                    elseif(
$alternative['Type'] == 'text' )
                    {    
// Plain text:
                       
if( $debug )
                        {    
// Display this info only in debug mode:
                           
pbm_msg( sprintf( ('Text alternative message part saved as %s'), $alternative['DataFile'] ), $cron );
                        }
                       
$strbody = file_get_contents( $alternative['DataFile'] );
                        break;
// stop after first alternative
                   
}
                }
            }
            elseif(
$parsedMIME['Type'] == 'text' )
            {    
// Mail is plain text:
               
if( $debug )
                {    
// Display this info only in debug mode:
                   
pbm_msg( sprintf( ('Plain-text message part saved as %s'), $parsedMIME['DataFile'] ), $cron );
                }
               
$strbody = file_get_contents( $parsedMIME['DataFile'] );
            }

           
// Check for attachments:
           
if( ! empty( $parsedMIME['Attachments'] ) )
            {
               
$hasAttachment = true;
                foreach(
$parsedMIME['Attachments'] as $file )
                {
                   
pbm_msg( sprintf( ('Attachment: %s stored as %s'), $file['FileName'], $file['DataFile'] ), $cron );
                }
            }

           
// Check for inline images:
           
if( ! empty( $parsedMIME['Related'] ) )
            {
               
$hasRelated = true;
                foreach(
$parsedMIME['Related'] as $file )
                {
                   
pbm_msg( sprintf( ('Related file with content ID: %s stored as %s'), $file['ContentID'], $file['DataFile'] ), $cron );
                }
            }

            if(
count( $mimeParser->warnings ) > 0 )
            {
               
pbm_msg( '<h4>'.sprintf( ('%d warnings during decode:'), count( $mimeParser->warnings ) ).'</h4>', $cron );
                foreach(
$mimeParser->warnings as $k => $v )
                {
                   
pbm_msg( sprintf( ('Warning: %s at position %s'), $v, $k ), $cron );
                }
            }
        }
       
unlink( $tmpMIME );

        if( empty(
$html_body ) )
        {    
// Plain-text message
           
pbm_msg( sprintf( ('Message type: %s'), 'TEXT' ), $cron );
           
pbm_msg( sprintf( ('Message body: %s'), '<pre style="font-size:10px">'.htmlspecialchars( $strbody ).'</pre>' ), $cron );

           
// Process body. First fix different line-endings (dos, mac, unix), remove double newlines
           
$content = str_replace( array( "\r", "\n\n" ), "\n", trim( $strbody ) );

           
// First see if there's an <auth> tag with login and password
           
if( ( $auth = pbm_get_auth_tag( $content ) ) === false )
            {    
// No <auth> tag, let's detect legacy "username:password" on the first line
               
$a_body = explode( "\n", $content, 2 );

               
// tblue> splitting only into 2 parts allows colons in the user PW
                // Note: login and password cannot include '<' !
               
$auth = explode( ':', strip_tags($a_body[0]), 2 );

               
// Drop the first line with username and password
               
$content = $a_body[1];
            }
        }
        else
        {    
// HTML message
           
pbm_msg( sprintf( ('Message type: %s'), 'HTML' ), $cron );

            if( (
$parsed_message = pbm_prepare_html_message( $html_body, $cron ) ) === false )
            {    
// No 'auth' tag provided, skip to the next message
               
rmdir_r( $tmpDirMIME );
                continue;
            }
            list(
$auth, $content) = $parsed_message;
        }

       
// TODO: dh> should the password really get trimmed here?!
       
$user_pass = isset($auth[1]) ? trim( $auth[1] ) : NULL;
       
$user_login = trim( utf8_strtolower( $auth[0] ) );

        if( empty(
$user_login) || empty($user_pass) )
        {
           
pbm_msg( sprintf( ('Please add username and password in message body in format %s.'),
                       
'"&lt;auth&gt;username:password&lt;/auth&gt;"' ), $cron );

           
rmdir_r( $tmpDirMIME );
            continue;
        }

       
// Authenticate user
       
pbm_msg( ('Authenticating User').': &laquo;'.$user_login.'&raquo;', $cron );
       
$pbmUser = & pbm_validate_user_password( $user_login, $user_pass );
        if( !
$pbmUser )
        {
           
pbm_msg( sprintf( ( 'Authentication failed for user &laquo;%s&raquo;' ), htmlspecialchars( $user_login ) ), $cron, 'error' );
           
rmdir_r( $tmpDirMIME );
            continue;
        }

       
$pbmUser->get_Group(); // Load group
       
if( ! empty($is_cron_mode) )
        {    
// Assign current User if we are in cron mode. This is needed in order to check user permissions
           
global $current_User;
           
$current_User = clone $pbmUser;
        }

       
// Activate User's locale
       
locale_activate( $pbmUser->get('locale') );

       
pbm_msg( '<b class="green">'.('Success').'</b>', $cron );

        if(
$post_categories = xmlrpc_getpostcategories( $content ) )
        {
           
$main_cat_ID = array_shift($post_categories);
           
$extra_cat_IDs = $post_categories;

           
pbm_msg( ('Extra categories').': '.implode( ', ', $extra_cat_IDs ), $cron );
        }
        else
        {
           
$main_cat_ID = $Settings->get('eblog_default_category');
           
$extra_cat_IDs = array();
        }
       
pbm_msg( ('Main category ID').': '.$main_cat_ID, $cron );

       
$ChapterCache = & get_ChapterCache();
       
$pbmChapter = & $ChapterCache->get_by_ID( $main_cat_ID, false, false );
        if( empty(
$pbmChapter) )
        {
           
pbm_msg( sprintf( ('Requested category %s does not exist!'), $main_cat_ID ), $cron );
           
rmdir_r( $tmpDirMIME );
            continue;
        }

       
$blog_ID = $pbmChapter->blog_ID;
       
pbm_msg( ('Blog ID').': '.$blog_ID, $cron );

       
$BlogCache = & get_BlogCache();
       
$pbmBlog = & $BlogCache->get_by_ID( $blog_ID, false, false );
        if( empty(
$pbmBlog) )
        {
           
pbm_msg( sprintf( ('Requested collection %s does not exist!'), $blog_ID ), $cron );
           
rmdir_r( $tmpDirMIME );
            continue;
        }

       
// Check permission:
       
pbm_msg( sprintf( ('Checking permissions for User &laquo;%s&raquo; to post to Collection #%d'), $user_login, $blog_ID ), $cron );
        if( !
$pbmUser->check_perm( 'blog_post!published', 'edit', false, $blog_ID ) )
        {
           
pbm_msg( ('Permission denied.'), $cron );
           
rmdir_r( $tmpDirMIME );
            continue;
        }

        if( (
$hasAttachment || $hasRelated) && !$pbmUser->check_perm( 'files', 'add', false, $blog_ID ) )
        {
           
pbm_msg( ( 'You have no permission to add/upload files.' ), $cron );
           
rmdir_r( $tmpDirMIME );
            continue;
        }
       
pbm_msg( '<b class="green">'.('Success').'</b>', $cron );

       
// Remove content after terminator
       
$eblog_terminator = $Settings->get('eblog_body_terminator');
        if( !empty(
$eblog_terminator ) && ($os_terminator = utf8_strpos( $content, $eblog_terminator )) !== false )
        {
           
$content = utf8_substr( $content, 0, $os_terminator );
        }

       
$post_title = pbm_get_post_title( $content, $subject );

       
// Remove 'title' and 'category' tags
       
$content = xmlrpc_removepostdata( $content );

       
// Remove <br> tags from string start and end
        // We do it here because there might be extra <br> left after deletion of <auth>, <category> and <title> tags
       
$content = preg_replace( array( '~^(\s*<br[\s/]*>\s*){1,}~i', '~(\s*<br[\s/]*>\s*){1,}$~i' ), '', $content );

        if(
$hasAttachment || $hasRelated )
        {    
// Handle attachments
           
if( isset($GLOBALS['files_Module']) )
            {
                if(
$mediadir = $pbmBlog->get_media_dir() )
                {
                    if(
$hasAttachment )
                    {
                       
pbm_process_attachments( $content, $parsedMIME['Attachments'], $mediadir,
                                   
$pbmBlog->get_media_url(), $Settings->get('eblog_add_imgtag'), 'attach', $cron );
                    }
                    if(
$hasRelated )
                    {
                       
pbm_process_attachments( $content, $parsedMIME['Related'], $mediadir,
                                   
$pbmBlog->get_media_url(), true, 'related', $cron );
                    }
                }
                else
                {
                   
pbm_msg( ('Unable to access media directory. No attachments processed.'), $cron );
                }
            }
            else
            {
               
pbm_msg( ('Files module is disabled or missing!'), $cron );
            }
        }

       
// CHECK and FORMAT content
       
global $Plugins;
       
$renderer_params = array( 'Blog' => & $pbmBlog, 'setting_name' => 'coll_apply_rendering' );
       
$renderers = $Plugins->validate_renderer_list( $Settings->get('eblog_renderers'), $renderer_params );

       
pbm_msg( sprintf( ('Applying the following text renderers: %s'), implode( ', ', $renderers ) ), $cron );

       
// Do some optional filtering on the content
        // Typically stuff that will help the content to validate
        // Useful for code display
        // Will probably be used for validation also
        // + APPLY RENDERING from Rendering Plugins:
       
$Plugins_admin = & get_Plugins_admin();
       
$params = array( 'object_type' => 'Item', 'object_Blog' => & $pbmBlog );
       
$Plugins_admin->filter_contents( $post_title /* by ref */, $content /* by ref */, $renderers, $params );

       
pbm_msg( sprintf( ('Filtered post content: %s'), '<pre style="font-size:10px">'.htmlspecialchars( $content ).'</pre>' ), $cron );

       
$context = $Settings->get('eblog_html_tag_limit') ? 'commenting' : 'posting';
       
$post_title = check_html_sanity( $post_title, $context, $pbmUser );
       
$content = check_html_sanity( $content, $context, $pbmUser );

        global
$Messages;
        if(
$Messages->has_errors() )
        {
           
// Make it easier for user to find and correct the errors
           
pbm_msg( "\n".sprintf( ('Processing message: %s'), $post_title ), $cron );
           
pbm_msg( $Messages->get_string( ('Cannot post, please correct these errors:'), 'error' ), $cron, 'error' );

           
$Messages->clear();
           
rmdir_r( $tmpDirMIME );
            continue;
        }

        if(
$test_mode_on )
        {    
// Test mode
           
pbm_msg( '<b class="green">'.('It looks like the post can be successfully saved in the database. However we will not do it in test mode.').'</b>', $cron );
        }
        else
        {
           
load_class( 'items/model/_item.class.php', 'Item' );

            global
$pbm_items, $DB, $localtimenow;

           
$post_status = 'published';

           
pbm_msg( '<h4>'.sprintf( ('Saving item "%s" in the database'), $post_title ).'</h4>', $cron );

           
// INSERT NEW POST INTO DB:
           
$edited_Item = new Item();

           
$edited_Item->set_creator_User( $pbmUser );
           
$edited_Item->set( $edited_Item->lasteditor_field, $pbmUser->ID );

           
$edited_Item->set( 'title', $post_title );
           
$edited_Item->set( 'content', $content );
           
$edited_Item->set( 'datestart', $post_date );
           
$edited_Item->set( 'datemodified', date('Y-m-d H:i:s',$localtimenow) );

           
$edited_Item->set( 'main_cat_ID', $main_cat_ID );
           
$edited_Item->set( 'extra_cat_IDs', $extra_cat_IDs );
           
$edited_Item->set( 'status', $post_status );
           
$edited_Item->set( 'locale', $pbmUser->locale );
           
$edited_Item->set( 'renderers', $renderers );

           
// INSERT INTO DB:
           
$edited_Item->dbinsert();

           
pbm_msg( sprintf( ('Item created?: %s'), ( isset( $edited_Item->ID ) ? 'yes' : 'no' ) ), $cron );

           
// Execute or schedule notifications & pings:
           
$edited_Item->handle_notifications( $pbmUser->ID, true );

            if( !empty(
$pbm_item_files) )
            {    
// Attach files
               
$FileCache = & get_FileCache();

               
$order = 1;
                foreach(
$pbm_item_files as $filename )
                {
                   
pbm_msg( sprintf( ('Saving file "%s" in the database'), $filename ), $cron );
                   
$pbmFile = & $FileCache->get_by_root_and_path( 'collection', $pbmBlog->ID, $filename );
                   
$pbmFile->meta = 'notfound'; // Save time and don't try to load meta from DB, it's not there anyway
                   
$pbmFile->dbsave();
                   
pbm_msg( sprintf( ('File saved?: %s'), ( isset( $pbmFile->ID ) ? 'yes' : 'no' ) ), $cron );

                   
pbm_msg( sprintf( ('Attaching file "%s" to the post'), $filename ), $cron );
                   
// Let's make the link!
                   
$pbmLink = new Link();
                   
$pbmLink->set( 'itm_ID', $edited_Item->ID );
                   
$pbmLink->set( 'file_ID', $pbmFile->ID );
                   
$pbmLink->set( 'position', 'aftermore' );
                   
$pbmLink->set( 'order', $order++ );
                   
$pbmLink->dbinsert();
                   
pbm_msg( sprintf( ('File attached?: %s'), ( isset( $pbmLink->ID ) ? 'yes' : 'no' ) ), $cron );
                }

               
// Invalidate blog's media BlockCache
               
BlockCache::invalidate_key( 'media_coll_ID', $edited_Item->get_blog_ID() );
            }

           
// Save posted items sorted by author user for reports
           
$pbm_items['user_'.$pbmUser->ID][] = $edited_Item;

            ++
$post_cntr;
        }

       
pbm_msg( ('Message posting successful'), $cron );

       
// Delete temporary directory
       
rmdir_r( $tmpDirMIME );

        if( !
$test_mode_on && $Settings->get('eblog_delete_emails') )
        {
           
pbm_msg( sprintf( ('Marking message for deletion from inbox: %s'), $index ), $cron );
           
imap_delete( $mbox, $index );
            ++
$del_cntr;
        }
    }

   
// Expunge messages marked for deletion
   
imap_expunge($mbox);

    return
true;
}

/**
 * Process Header information like subject and date of a mail.
 *
 * @param array $header header as set by mime_parser_class::Analyze()
 * @param string message subject by reference
 * @param string message date by reference
 * @param boolean TRUE if script is executed by cron
 * @return bool true if valid subject prefix is detected
 */
function pbm_process_header( $header, & $subject, & $post_date, $cron = false )
{
    global
$Settings;

   
$subject = $header['Subject'];
    if(
function_exists( 'mb_decode_mimeheader' ) )
    {    
// Decode email subject:
       
$subject = mb_decode_mimeheader( $subject );
    }
   
$ddate = $header['Date'];

   
$prefix = $Settings->get( 'eblog_subject_prefix' );
   
pbm_msg( ('Subject').': '.$subject, $cron );

    if(
utf8_substr($subject, 0, utf8_strlen($prefix)) !== $prefix )
    {
       
pbm_msg( sprintf( ('Subject prefix is not "%s", skip this email'), $prefix ), $cron );
        return
false;
    }

   
$subject = utf8_substr($subject, utf8_strlen($prefix));

   
// Parse Date
   
if( !preg_match('#^(.{3}, )?(\d{2}) (.{3}) (\d{4}) (\d{2}):(\d{2}):(\d{2})#', $ddate, $match) )
    {
       
$ddate_U = @strtotime($ddate);
        if( empty(
$ddate_U) || strlen($ddate_U) < 2 )
        {
           
pbm_msg( sprintf( ('Could not parse date header "%s"'), $ddate ), $cron );
            return
false;
        }
    }

    if( empty(
$ddate_U) )
    {
       
$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]] ) )
        {
           
pbm_msg( ('Invalid month name in message date string.'), $cron );
            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
 *
 * @param string message content that is optionally manipulated by adding image tags (by reference)
 * @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 to the post (instead of linking through the file manager)
 * @param  string $type defines attachment type: 'attach' or 'related'
 * @param boolean TRUE if script is executed by cron
 */
function pbm_process_attachments( & $content, $mailAttachments, $mediadir, $media_url, $add_img_tags = true, $type = 'attach', $cron = false )
{
    global
$Settings, $pbm_item_files, $filename_max_length;

   
pbm_msg( '<h4>'.('Processing attachments').'</h4>', $cron );

    foreach(
$mailAttachments as $attachment )
    {
        if( isset(
$attachment['FileName']) )
        {
           
$filename = trim( utf8_strtolower($attachment['FileName']) );
        }
        else
        {    
// Related attachments may not have file name, we'll generate one below
           
$filename = '';
        }

        if(
$filename == '' )
        {
           
$filename = 'upload_'.uniqid().'.'.$attachment['SubType'];
           
pbm_msg( sprintf( ('Attachment without name. Using "%s".'), htmlspecialchars( $filename ) ), $cron );
        }

       
// Check valid filename/extension: (includes check for locked filenames)
       
if( $error_filename = process_filename( $filename, true ) )
        {
           
pbm_msg( ('Invalid filename').': '.$error_filename, $cron, 'error' );
           
syslog_insert( sprintf( 'The posted by mail file %s has an unrecognized extension', '[['.$filename.']]' ), 'warning', 'file' );
            continue;
        }

       
// If file exists count up a number
       
$cnt = 0;
       
$prename = substr( $filename, 0, strrpos( $filename, '.' ) ).'-';
       
$sufname = strrchr( $filename, '.' );
       
$error_in_filename = false;

        while(
file_exists( $mediadir.$filename ) )
        {
           
$filename = $prename.$cnt.$sufname;
            if(
strlen( $filename ) > $filename_max_length )
            {
// This is a special case, when the filename is longer then the maximum allowed
                // Cut as many characters as required before the counter on the file name
               
$filename = fix_filename_length( $filename, strlen( $prename ) - 1 );
                if(
$error_in_filename = process_filename( $filename, true ) )
                {
// The file name is not valid, this is an unexpected situation, because the file name was already validated before
                   
pbm_msg( ('Invalid filename').': '.$error_filename, $cron, 'error' );
                   
syslog_insert( sprintf( 'The posted by mail file %s has an unrecognized extension', '[['.$filename.']]' ), 'warning', 'file' );
                    break;
                }
            }
            ++
$cnt;
        }
        if(
$error_in_filename )
        {
// Don't create file with invalid file name
           
continue;
        }
       
pbm_msg( sprintf( ('New file name is %s'), '<b>'.$filename.'</b>' ), $cron );

       
$imginfo = NULL;
        if( !
$Settings->get('eblog_test_mode') )
        {
           
pbm_msg( sprintf( ('Saving file to: %s'), htmlspecialchars( $mediadir.$filename ) ), $cron );
            if( !
copy( $attachment['DataFile'], $mediadir.$filename ) )
            {
               
pbm_msg( sprintf( ('Unable to copy uploaded file to %s'), htmlspecialchars( $mediadir.$filename ) ), $cron );
                continue;
            }

           
// chmod uploaded file:
           
$chmod = $Settings->get('fm_default_chmod_file');
            @
chmod( $mediadir.$filename, octdec( $chmod ) );

           
$imginfo = @getimagesize($mediadir.$filename);
           
pbm_msg( sprintf( ('Is this an image?: %s'), ( is_array( $imginfo ) ? 'yes' : 'no' ) ), $cron );
        }

        if(
$type == 'attach' )
        {
           
$content .= "\n";
            if(
is_array($imginfo) && $add_img_tags )
            {
               
$content .= '<img src="'.$media_url.$filename.'" '.$imginfo[3].' />';
            }
            else
            {
               
pbm_msg( sprintf( ('The file %s will be attached to the post later, after we save the post in the database.'), '<b>'.$filename.'</b>' ), $cron );
               
$pbm_item_files[] = $filename;
            }
           
$content .= "\n";
        }
        elseif( !empty(
$attachment['ContentID']) )
        {    
// Replace relative "cid:xxxxx" URIs with absolute URLs to media files
           
$content = str_replace( 'cid:'.$attachment['ContentID'], $media_url.$filename, $content );
        }
    }
}


/**
 * 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 no tag is found we will try to get the default title from settings.
 * 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
 *
 */
function pbm_get_post_title( $content, $alternate_title )
{
    global
$Settings;

    if(
preg_match('~<title>(.+?)</title>~is', $content, $matchtitle) )
    {
       
$title = $matchtitle[1];
    }
    else
    {
       
$title = $Settings->get('eblog_default_title');
    }

    if(
$title == '' )
    {
       
$title = $alternate_title;
    }
    return
$title;
}


/**
 * Extract <auth> tag with login and password
 *
 * @param string Message body (by reference)
 * @return array login and password
 */
function pbm_get_auth_tag( & $content )
{
    if(
preg_match( '~<(auth)>(.+?)</\\1>~is', $content, $match ) )
    {
       
// tblue> splitting only into 2 parts allows colons in the user PW
        // Note: login and password cannot include '<' !
       
$auth = explode( ':', strip_tags($match[2]), 2 );

       
// Delete 'auth' tag from post content
       
$content = preg_replace( '~<(auth)>(.+?)</\\1>~is', '', $content );

        return
$auth;
    }
    return
false;
}


/**
 * Prepare html message
 *
 * @param string Message
 * @param boolean TRUE if script is executed by cron
 * @return string Content
 */
function pbm_prepare_html_message( $message, $cron = false )
{
   
pbm_msg( sprintf( ('Message body (original): %s'), '<pre style="font-size:10px">'.htmlspecialchars( $message ).'</pre>' ), $cron );

   
$marker = 0;
    if(
preg_match( '~<body[^>]*>(.*?)</body>~is', $message, $result ) )
    {    
// First see if we can get contents of <body> tag
       
$content = $result[1];
       
$marker = 1;
    }
    elseif(
preg_match( '~<html[^>]*>(.*?)</html>~is', $message, $result ) )
    {    
// <body> was not found, use <html> contents and delete <head> section from it
       
$content = preg_replace( '~<head[^>]*>(.*?)</head>~is', '', $result[1] );
       
$marker = 1;
    }

    if( empty(
$marker) )
    {    
// None of the above methods worked, just use the original message body
       
$content = $message;
    }

   
// First fix different line-endings (dos, mac, unix), remove double newlines
   
$content = str_replace( array("\r", "\n\n"), "\n", trim($content) );

   
// Decode 'category', 'title' and 'auth' tags
   
$content = preg_replace( '~&lt;(/)?(category|title|auth)&gt;~i', '<\\1\\2>', $content );

    if( (
$auth = pbm_get_auth_tag($content)) === false )
    {    
// No 'auth' tag provided, exit
       
pbm_msg( sprintf( ('&lt;auth&gt; tag not found! Please add username and password in message body in format %s.'),
                   
'"&lt;auth&gt;username:password&lt;/auth&gt;"' ), $cron );
        return
false;
    }

   
// Balance tags
   
$content = balance_tags($content);

   
// Remove markup that cause validator errors
   
$patterns = array(
       
'~ moz-do-not-send="true"~',            // Thunderbird inline image with absolute "src"
       
'~ class="moz-signature" cols="\d+"~',    // Thunderbird signature in HTML message
       
'~ goomoji="[^"]+"~',                    // Gmail smilies
   
);
   
$content = preg_replace( $patterns, '', $content );

   
pbm_msg( sprintf( ('Message body (processed): %s'), '<pre style="font-size:10px">'.htmlspecialchars( $content ).'</pre>' ), $cron );

    return array(
$auth, $content );
}


function
pbm_validate_user_password( $user_login, $user_pass )
{
   
$UserCache = & get_UserCache();
   
$User = & $UserCache->get_by_login( $user_login );
    if( !
$User )
    {
        return
false;
    }

   
// First check unhashed password
   
if( ! $User->check_password( $user_pass, false ) )
    {
        if(
preg_match( '~^[a-f0-9]{32}$~i', $user_pass ) )
        {    
// This is a hashed password, see if it's valid
            // We check it here because some crazy user may use a real 32-chars password!
           
if( $User->check_password( $user_pass, true ) )
            {    
// Valid password
               
return $User;
            }
        }
        return
false;
    }
    return
$User;
}


/**
 * 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 pbm_tempdir( $dir, $prefix = 'tmp', $mode = 0700 )
{
   
// Add trailing slash
   
$dir = trailing_slash($dir);

    do {
$path = $dir.$prefix.mt_rand(); } while( ! evo_mkdir( $path, $mode ) );

    return
$path;
}

?>