b2evolution PHP Cross Reference Blogging Systems

Source: /inc/cron/model/_post_by_mail.funcs.php - 892 lines - 26813 bytes - Summary - Text - Print

Description: This file implements the post by mail support functions. b2evolution - {@link http://b2evolution.net/} Released under GNU GPL License - {@link http://b2evolution.net/about/license.html}

   1  <?php
   2  /**
   3   * This file implements the post by mail support functions.
   4   *
   5   * b2evolution - {@link http://b2evolution.net/}
   6   * Released under GNU GPL License - {@link http://b2evolution.net/about/license.html}
   7   *
   8   * @copyright (c)2003-2014 by Francois Planque - {@link http://fplanque.com/}
   9   *
  10   * {@internal Below is a list of authors who have contributed to design/coding of this file: }}
  11   * @author Stephan Knauss
  12   * @author tblue246: Tilman Blumenbach
  13   * @author sam2kb: Alex
  14   *
  15   * @package admin
  16   *
  17   * @version $Id: _post_by_mail.funcs.php 6136 2014-03-08 07:59:48Z manuel $
  18   */
  19  if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );
  20  
  21  /**
  22   * Print out a debugging message with optional HTML color added
  23   *
  24   * @param string Message
  25   * @param string
  26   */
  27  function pbm_msg( $message, $cron = false )
  28  {
  29      global $is_web, $result_message, $pbm_messages;
  30  
  31      // Log all messages to $pbm_messages no matter if we are in cron mode or not
  32      // We may use this report later, display or send to the blog owner
  33      $pbm_messages[] = $message;
  34  
  35      if( $cron )
  36      {    // We are in cron mode, log the message
  37          if( $is_web )
  38              $message .= '<br />';
  39  
  40          $result_message .= $message."\n";
  41      }
  42  }
  43  
  44  /**
  45   * Connect to a mail server
  46   *
  47   * @param string Message
  48   * @return resource $mbox
  49   */
  50  function pbm_connect()
  51  {
  52      if( !extension_loaded( 'imap' ) )
  53      {    // Exit here if imap extension is not loaded
  54          pbm_msg('<b class="red">IMAP extension is NOT loaded!</b>');
  55          return false;
  56      }
  57  
  58      global $Settings;
  59  
  60      $host = $Settings->get('eblog_server_host').':'.$Settings->get('eblog_server_port');
  61      $mailserver = '{'.$host;
  62  
  63      pbm_msg('Connecting and authenticating to mail server <b>'.$host.'</b>');
  64  
  65      switch( $Settings->get('eblog_encrypt') )
  66      {
  67          case 'ssl':
  68              $mailserver .= '/ssl';
  69              break;
  70  
  71          case 'tls':
  72              $mailserver .= '/tls';
  73              break;
  74  
  75          case 'none':
  76          default:
  77              $mailserver .= '/notls';
  78              break;
  79      }
  80  
  81      switch( $Settings->get('eblog_method') )
  82      {
  83          case 'pop3':
  84          case 'pop3a':
  85              $mailserver .= '/pop3';
  86              break;
  87  
  88          case 'imap':
  89          default:
  90              // imap needs no additional options
  91              break;
  92      }
  93  
  94      if( $Settings->get('eblog_novalidatecert') )
  95      {
  96          $mailserver .= '/novalidate-cert';
  97      }
  98  
  99      $mailserver .= '}INBOX';
 100  
 101      // Connect to mail server (one retry)
 102      $mbox = @imap_open( $mailserver, $Settings->get('eblog_username'), $Settings->get('eblog_password'), NULL, 1 );
 103  
 104      if( is_null(@get_resource_type($mbox)) )
 105      {    // Not a resource
 106          $error = imap_errors();
 107          if( is_array($error) )
 108          {
 109              $error = implode( "<br />\n", $error );
 110          }
 111  
 112          pbm_msg( sprintf( /* TRANS: %s is the error message */ T_('Connection failed: %s'), $error), true );
 113          return false;
 114      }
 115      pbm_msg('<b class="green">Successfully connected!</b>');
 116  
 117      @imap_errors();
 118  
 119      return $mbox;
 120  }
 121  
 122  
 123  /**
 124   * Read messages from server and create posts
 125   *
 126   * @param resource $mbox created by pbm_connect() (by reference)
 127   * @param integer the number of messages to process
 128   * @return boolean true on success
 129   */
 130  function pbm_process_messages( & $mbox, $limit )
 131  {
 132      global $Settings;
 133      global $pbm_item_files, $pbm_messages, $pbm_items, $post_cntr, $del_cntr, $is_cron_mode;
 134  
 135      // No execution time limit
 136      set_max_execution_time(0);
 137  
 138      // Are we in test mode?
 139      $test_mode_on = $Settings->get('eblog_test_mode');
 140  
 141      $post_cntr = 0;
 142      $del_cntr = 0;
 143      for( $index = 1; $index <= $limit; $index++ )
 144      {
 145          pbm_msg('<hr /><h3>Processing message #'.$index.':</h3>');
 146  
 147          $strbody = '';
 148          $hasAttachment = false;
 149          $hasRelated = false;
 150  
 151          $pbm_item_files = array(); // reset the value for each new Item
 152  
 153          // Save email to hard drive, otherwise attachments may take a lot of RAM
 154          if( ! ($tmpMIME = tempnam( sys_get_temp_dir(), 'b2evoMail' )) )
 155          {
 156              pbm_msg( T_('Could not create temporary file.'), true );
 157              continue;
 158          }
 159          imap_savebody( $mbox, $tmpMIME, $index );
 160  
 161          // Create random temp directory for message parts
 162          $tmpDirMIME = pbm_tempdir( sys_get_temp_dir(), 'b2evo_' );
 163  
 164          $mimeParser = new mime_parser_class;
 165          $mimeParser->mbox = 0;                // Set to 0 for parsing a single message file
 166          $mimeParser->decode_headers = 1;
 167          $mimeParser->ignore_syntax_errors = 1;
 168          $mimeParser->extract_addresses = 0;
 169  
 170          $MIMEparameters = array(
 171                  'File' => $tmpMIME,
 172                  'SaveBody' => $tmpDirMIME,    // Save message body parts to a directory
 173                  'SkipBody' => 1,            // Do not retrieve or save message body parts
 174              );
 175  
 176          if( !$mimeParser->Decode( $MIMEparameters, $decodedMIME ) )
 177          {
 178              pbm_msg( sprintf( 'MIME message decoding error: %s at position %d.', $mimeParser->error, $mimeParser->error_position), true );
 179              rmdir_r( $tmpDirMIME );
 180              unlink( $tmpMIME );
 181              continue;
 182          }
 183          else
 184          {
 185              pbm_msg('MIME message decoding successful');
 186  
 187              if( ! $mimeParser->Analyze( $decodedMIME[0], $parsedMIME ) )
 188              {
 189                  pbm_msg( sprintf( 'MIME message analyse error: %s', $mimeParser->error), true );
 190                  rmdir_r( $tmpDirMIME );
 191                  unlink( $tmpMIME );
 192                  continue;
 193              }
 194  
 195              // Get message $subject and $post_date from headers (by reference)
 196              if( ! pbm_process_header( $parsedMIME, $subject, $post_date ) )
 197              {    // Couldn't process message headers
 198                  rmdir_r( $tmpDirMIME );
 199                  unlink($tmpMIME);
 200                  continue;
 201              }
 202  
 203              // TODO: handle type == "message" recursively
 204              // sam2kb> For some reason imap_qprint() demages HTML text... needs more testing
 205  
 206              if( $parsedMIME['Type'] == 'html' )
 207              {    // Mail is HTML
 208                  if( $Settings->get('eblog_html_enabled') )
 209                  {    // HTML posting enabled
 210                      pbm_msg( 'HTML message part saved as '.$parsedMIME['DataFile'] );
 211                      $html_body = file_get_contents($parsedMIME['DataFile']);
 212                  }
 213  
 214                  foreach( $parsedMIME['Alternative'] as $alternative )
 215                  {    // First try to get HTML alternative (when possible)
 216                      if( $alternative['Type'] == 'html' && $Settings->get('eblog_html_enabled') )
 217                      {    // HTML text
 218                          pbm_msg('HTML alternative message part saved as '.$alternative['DataFile']);
 219                          // sam2kb> TODO: we may need to use $html_body here instead
 220                          $strbody = file_get_contents($alternative['DataFile']);
 221                          break; // stop after first alternative
 222                      }
 223                      elseif( $alternative['Type'] == 'text' )
 224                      {    // Plain text
 225                          pbm_msg('Text alternative message part saved as '.$alternative['DataFile']);
 226                          $strbody = imap_qprint( file_get_contents($alternative['DataFile']) );
 227                          break; // stop after first alternative
 228                      }
 229                  }
 230              }
 231              elseif( $parsedMIME['Type'] == 'text' )
 232              {    // Mail is plain text
 233                  pbm_msg('Plain-text message part saved as '.$parsedMIME['DataFile']);
 234                  $strbody = imap_qprint( file_get_contents($parsedMIME['DataFile']) );
 235              }
 236  
 237              // Check for attachments
 238              if( !empty($parsedMIME['Attachments']) )
 239              {
 240                  $hasAttachment = true;
 241                  foreach( $parsedMIME['Attachments'] as $file )
 242                  {
 243                      pbm_msg('Attachment: '.$file['FileName'].' stored as '.$file['DataFile']);
 244                  }
 245              }
 246  
 247              // Check for inline images
 248              if( !empty($parsedMIME['Related']) )
 249              {
 250                  $hasRelated = true;
 251                  foreach( $parsedMIME['Related'] as $file )
 252                  {
 253                      pbm_msg('Related file with content ID: '.$file['ContentID'].' stored as '.$file['DataFile']);
 254                  }
 255              }
 256  
 257              if( count($mimeParser->warnings) > 0 )
 258              {
 259                  pbm_msg( sprintf('<h4>%d warnings during decode:</h4>', count($mimeParser->warnings)) );
 260                  foreach( $mimeParser->warnings as $k => $v )
 261                  {
 262                      pbm_msg('Warning: '.$v.' at position '.$k);
 263                  }
 264              }
 265          }
 266          unlink( $tmpMIME );
 267  
 268          if( empty($html_body) )
 269          {    // Plain text message
 270              pbm_msg('Message type: TEXT');
 271              pbm_msg('Message body: <pre style="font-size:10px">'.htmlspecialchars($strbody).'</pre>');
 272  
 273              // Process body. First fix different line-endings (dos, mac, unix), remove double newlines
 274              $content = str_replace( array("\r", "\n\n"), "\n", trim($strbody) );
 275  
 276              // First see if there's an <auth> tag with login and password
 277              if( ($auth = pbm_get_auth_tag($content)) === false )
 278              {    // No <auth> tag, let's detect legacy "username:password" on the first line
 279                  $a_body = explode( "\n", $content, 2 );
 280  
 281                  // tblue> splitting only into 2 parts allows colons in the user PW
 282                  // Note: login and password cannot include '<' !
 283                  $auth = explode( ':', strip_tags($a_body[0]), 2 );
 284  
 285                  // Drop the first line with username and password
 286                  $content = $a_body[1];
 287              }
 288          }
 289          else
 290          {    // HTML message
 291              pbm_msg('Message type: HTML');
 292  
 293              if( ($parsed_message = pbm_prepare_html_message( $html_body )) === false )
 294              {    // No 'auth' tag provided, skip to the next message
 295                  rmdir_r( $tmpDirMIME );
 296                  continue;
 297              }
 298              list($auth, $content) = $parsed_message;
 299          }
 300  
 301          // TODO: dh> should the password really get trimmed here?!
 302          $user_pass = isset($auth[1]) ? trim( remove_magic_quotes($auth[1]) ) : NULL;
 303          $user_login = trim( evo_strtolower(remove_magic_quotes($auth[0])) );
 304  
 305          if( empty($user_login) || empty($user_pass) )
 306          {
 307              pbm_msg( sprintf( T_('Please add username and password in message body in format %s.'),
 308                          '"&lt;auth&gt;username:password&lt;/auth&gt;"' ), true );
 309  
 310              rmdir_r( $tmpDirMIME );
 311              continue;
 312          }
 313  
 314          // Authenticate user
 315          pbm_msg('Authenticating user: &laquo;'.$user_login.'&raquo;');
 316          $pbmUser = & pbm_validate_user_password( $user_login, $user_pass );
 317          if( ! $pbmUser )
 318          {
 319              pbm_msg( sprintf( T_( 'Authentication failed for user &laquo;%s&raquo;' ), htmlspecialchars($user_login) ), true );
 320              rmdir_r( $tmpDirMIME );
 321              continue;
 322          }
 323  
 324          $pbmUser->get_Group(); // Load group
 325          if( ! empty($is_cron_mode) )
 326          {    // Assign current User if we are in cron mode. This is needed in order to check user permissions
 327              global $current_User;
 328              $current_User = duplicate($pbmUser);
 329          }
 330  
 331          // Activate User's locale
 332          locale_activate( $pbmUser->get('locale') );
 333  
 334          pbm_msg('<b class="green">Success</b>');
 335  
 336          if( $post_categories = xmlrpc_getpostcategories( $content ) )
 337          {
 338              $main_cat_ID = array_shift($post_categories);
 339              $extra_cat_IDs = $post_categories;
 340  
 341              pbm_msg('Extra categories: '.implode(', ', $extra_cat_IDs));
 342          }
 343          else
 344          {
 345              $main_cat_ID = $Settings->get('eblog_default_category');
 346              $extra_cat_IDs = array();
 347          }
 348          pbm_msg('Main category ID: '.$main_cat_ID);
 349  
 350          $ChapterCache = & get_ChapterCache();
 351          $pbmChapter = & $ChapterCache->get_by_ID( $main_cat_ID, false, false );
 352          if( empty($pbmChapter) )
 353          {
 354              pbm_msg( sprintf( T_('Requested category %s does not exist!'), $main_cat_ID ), true );
 355              rmdir_r( $tmpDirMIME );
 356              continue;
 357          }
 358  
 359          $blog_ID = $pbmChapter->blog_ID;
 360          pbm_msg('Blog ID: '.$blog_ID);
 361  
 362          $BlogCache = & get_BlogCache();
 363          $pbmBlog = & $BlogCache->get_by_ID( $blog_ID, false, false );
 364          if( empty($pbmBlog) )
 365          {
 366              pbm_msg( sprintf( T_('Requested blog %s does not exist!'), $blog_ID ), true );
 367              rmdir_r( $tmpDirMIME );
 368              continue;
 369          }
 370  
 371          // Check permission:
 372          pbm_msg( sprintf( 'Checking permissions for user &laquo;%s&raquo; to post to Blog #%d', $user_login, $blog_ID ) );
 373          if( !$pbmUser->check_perm( 'blog_post!published', 'edit', false, $blog_ID ) )
 374          {
 375              pbm_msg( T_('Permission denied.'), true );
 376              rmdir_r( $tmpDirMIME );
 377              continue;
 378          }
 379  
 380          if( ($hasAttachment || $hasRelated) && !$pbmUser->check_perm( 'files', 'add', false, $blog_ID ) )
 381          {
 382              pbm_msg( T_( 'You have no permission to add/upload files.' ), true );
 383              rmdir_r( $tmpDirMIME );
 384              continue;
 385          }
 386          pbm_msg('<b class="green">Success</b>');
 387  
 388          // Remove content after terminator
 389          $eblog_terminator = $Settings->get('eblog_body_terminator');
 390          if( !empty( $eblog_terminator ) && ($os_terminator = evo_strpos( $content, $eblog_terminator )) !== false )
 391          {
 392              $content = evo_substr( $content, 0, $os_terminator );
 393          }
 394  
 395          $post_title = pbm_get_post_title( $content, $subject );
 396  
 397          // Remove 'title' and 'category' tags
 398          $content = xmlrpc_removepostdata( $content );
 399  
 400          // Remove <br> tags from string start and end
 401          // We do it here because there might be extra <br> left after deletion of <auth>, <category> and <title> tags
 402          $content = preg_replace( array( '~^(\s*<br[\s/]*>\s*){1,}~i', '~(\s*<br[\s/]*>\s*){1,}$~i' ), '', $content );
 403  
 404          if( $hasAttachment || $hasRelated )
 405          {    // Handle attachments
 406              if( isset($GLOBALS['files_Module']) )
 407              {
 408                  if( $mediadir = $pbmBlog->get_media_dir() )
 409                  {
 410                      if( $hasAttachment )
 411                      {
 412                          pbm_process_attachments( $content, $parsedMIME['Attachments'], $mediadir,
 413                                      $pbmBlog->get_media_url(), $Settings->get('eblog_add_imgtag'), 'attach' );
 414                      }
 415                      if( $hasRelated )
 416                      {
 417                          pbm_process_attachments( $content, $parsedMIME['Related'], $mediadir,
 418                                      $pbmBlog->get_media_url(), true, 'related' );
 419                      }
 420                  }
 421                  else
 422                  {
 423                      pbm_msg( T_('Unable to access media directory. No attachments processed.'), true );
 424                  }
 425              }
 426              else
 427              {
 428                  pbm_msg( T_('Files module is disabled or missing!'), true );
 429              }
 430          }
 431  
 432          // CHECK and FORMAT content
 433          global $Plugins;
 434          $renderer_params = array( 'Blog' => & $pbmBlog, 'setting_name' => 'coll_apply_rendering' );
 435          $renderers = $Plugins->validate_renderer_list( $Settings->get('eblog_renderers'), $renderer_params );
 436  
 437          pbm_msg( 'Applying the following text renderers: '.implode( ', ', $renderers ) );
 438  
 439          // Do some optional filtering on the content
 440          // Typically stuff that will help the content to validate
 441          // Useful for code display
 442          // Will probably be used for validation also
 443          $Plugins_admin = & get_Plugins_admin();
 444          $params = array( 'object_type' => 'Item', 'object_Blog' => & $pbmBlog );
 445          $Plugins_admin->filter_contents( $post_title /* by ref */, $content /* by ref */, $renderers, $params );
 446  
 447          pbm_msg('Filtered post content: <pre style="font-size:10px">'.htmlspecialchars($content).'</pre>');
 448  
 449          $context = $Settings->get('eblog_html_tag_limit') ? 'commenting' : 'posting';
 450          $post_title = check_html_sanity( $post_title, $context, $pbmUser );
 451          $content = check_html_sanity( $content, $context, $pbmUser );
 452  
 453          global $Messages;
 454          if( $Messages->has_errors() )
 455          {
 456              // Make it easier for user to find and correct the errors
 457              pbm_msg( "\n".sprintf( T_('Processing message: %s'), $post_title ), true );
 458              pbm_msg( $Messages->get_string( T_('Cannot post, please correct these errors:'), 'error' ), true );
 459  
 460              $Messages->clear();
 461              rmdir_r( $tmpDirMIME );
 462              continue;
 463          }
 464  
 465          if( $test_mode_on )
 466          {    // Test mode
 467              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>' );
 468          }
 469          else
 470          {
 471              load_class( 'items/model/_item.class.php', 'Item' );
 472  
 473              global $pbm_items, $DB, $localtimenow;
 474  
 475              $post_status = 'published';
 476  
 477              pbm_msg( sprintf('<h4>Saving item "%s" in the database</h4>', $post_title ) );
 478  
 479              // INSERT NEW POST INTO DB:
 480              $edited_Item = new Item();
 481  
 482              $edited_Item->set_creator_User( $pbmUser );
 483              $edited_Item->set( $edited_Item->lasteditor_field, $pbmUser->ID );
 484  
 485              $edited_Item->set( 'title', $post_title );
 486              $edited_Item->set( 'content', $content );
 487              $edited_Item->set( 'datestart', $post_date );
 488              $edited_Item->set( 'datemodified', date('Y-m-d H:i:s',$localtimenow) );
 489  
 490              $edited_Item->set( 'main_cat_ID', $main_cat_ID );
 491              $edited_Item->set( 'extra_cat_IDs', $extra_cat_IDs );
 492              $edited_Item->set( 'status', $post_status );
 493              $edited_Item->set( 'locale', $pbmUser->locale );
 494              $edited_Item->set( 'renderers', $renderers );
 495  
 496              // INSERT INTO DB:
 497              $edited_Item->dbinsert( 'through_email' );
 498  
 499              pbm_msg( sprintf('Item created?: '.(isset($edited_Item->ID) ? 'yes' : 'no') ) );
 500  
 501              // Execute or schedule notifications & pings:
 502              $edited_Item->handle_post_processing( true );
 503  
 504              if( !empty($pbm_item_files) )
 505              {    // Attach files
 506                  $FileCache = & get_FileCache();
 507  
 508                  $order = 1;
 509                  foreach( $pbm_item_files as $filename )
 510                  {
 511                      pbm_msg( sprintf('Saving file "%s" in the database', $filename ) );
 512                      $pbmFile = & $FileCache->get_by_root_and_path( 'collection', $pbmBlog->ID, $filename );
 513                      $pbmFile->meta = 'notfound'; // Save time and don't try to load meta from DB, it's not there anyway
 514                      $pbmFile->dbsave();
 515                      pbm_msg( sprintf('File saved?: '.(isset($pbmFile->ID) ? 'yes' : 'no') ) );
 516  
 517                      pbm_msg( sprintf('Attaching file "%s" to the post', $filename ) );
 518                      // Let's make the link!
 519                      $pbmLink = new Link();
 520                      $pbmLink->set( 'itm_ID', $edited_Item->ID );
 521                      $pbmLink->set( 'file_ID', $pbmFile->ID );
 522                      $pbmLink->set( 'position', 'aftermore' );
 523                      $pbmLink->set( 'order', $order++ );
 524                      $pbmLink->dbinsert();
 525                      pbm_msg( sprintf('File attached?: '.(isset($pbmLink->ID) ? 'yes' : 'no') ) );
 526                  }
 527  
 528                  // Invalidate blog's media BlockCache
 529                  BlockCache::invalidate_key( 'media_coll_ID', $edited_Item->get_blog_ID() );
 530              }
 531  
 532              // Save posted items sorted by author user for reports
 533              $pbm_items['user_'.$pbmUser->ID][] = $edited_Item;
 534  
 535              ++$post_cntr;
 536          }
 537  
 538          pbm_msg( 'Message posting successful' );
 539  
 540          // Delete temporary directory
 541          rmdir_r( $tmpDirMIME );
 542  
 543          if( ! $test_mode_on && $Settings->get('eblog_delete_emails') )
 544          {
 545              pbm_msg( 'Marking message for deletion from inbox: '.$index );
 546              imap_delete( $mbox, $index );
 547              ++$del_cntr;
 548          }
 549      }
 550  
 551      // Expunge messages marked for deletion
 552      imap_expunge($mbox);
 553  
 554      return true;
 555  }
 556  
 557  /**
 558   * Process Header information like subject and date of a mail.
 559   *
 560   * @param array $header header as set by mime_parser_class::Analyze()
 561   * @param string message subject by reference
 562   * @param string message date by reference
 563   * @return bool true if valid subject prefix is detected
 564   */
 565  function pbm_process_header( $header, & $subject, & $post_date )
 566  {
 567      global $Settings;
 568  
 569      $subject = $header['Subject'];
 570      $ddate = $header['Date'];
 571  
 572      $prefix = $Settings->get( 'eblog_subject_prefix' );
 573      pbm_msg('Subject: '.$subject);
 574  
 575      if( evo_substr($subject, 0, evo_strlen($prefix)) !== $prefix )
 576      {
 577          pbm_msg('Subject prefix is not "'.$prefix.'", skip this email');
 578          return false;
 579      }
 580  
 581      $subject = evo_substr($subject, evo_strlen($prefix));
 582  
 583      // Parse Date
 584      if( !preg_match('#^(.{3}, )?(\d{2}) (.{3}) (\d{4}) (\d{2}):(\d{2}):(\d{2})#', $ddate, $match) )
 585      {
 586          $ddate_U = @strtotime($ddate);
 587          if( empty($ddate_U) || strlen($ddate_U) < 2 )
 588          {
 589              pbm_msg( sprintf( T_('Could not parse date header "%s"'), $ddate ), true );
 590              return false;
 591          }
 592      }
 593  
 594      if( empty($ddate_U) )
 595      {
 596          $dmonths = array(
 597              'Jan' => 1,
 598              'Feb' => 2,
 599              'Mar' => 3,
 600              'Apr' => 4,
 601              'May' => 5,
 602              'Jun' => 6,
 603              'Jul' => 7,
 604              'Aug' => 8,
 605              'Sep' => 9,
 606              'Oct' => 10,
 607              'Nov' => 11,
 608              'Dec' => 12,
 609          );
 610  
 611          $ddate_H = $match[5];
 612          $ddate_i = $match[6];
 613          $ddate_s = $match[7];
 614  
 615          if( ! isset( $dmonths[$match[3]] ) )
 616          {
 617              pbm_msg( T_('Invalid month name in message date string.'), true );
 618              return false;
 619          }
 620          $ddate_m = $dmonths[$match[3]];
 621          $ddate_d = $match[2];
 622          $ddate_Y = $match[4];
 623  
 624          $ddate_U = mktime( $ddate_H, $ddate_i, $ddate_s, $ddate_m, $ddate_d, $ddate_Y );
 625      }
 626  
 627      $post_date = date( 'Y-m-d H:i:s', $ddate_U );
 628  
 629      return true;
 630  }
 631  
 632  
 633  /**
 634   * process attachments by saving into media directory and optionally creating image tag in post
 635   *
 636   * @param string message content that is optionally manipulated by adding image tags (by reference)
 637   * @param  array $mailAttachments array containing path to attachment files
 638   * @param  string $mediadir path to media directory of blog as seen by file system
 639   * @param  string $media_url url to media directory as seen by user
 640   * @param  bool $add_img_tags should img tags be added to the post (instead of linking through the file manager)
 641   * @param  string $type defines attachment type: 'attach' or 'related'
 642   */
 643  function pbm_process_attachments( & $content, $mailAttachments, $mediadir, $media_url, $add_img_tags = true, $type = 'attach' )
 644  {
 645      global $Settings, $pbm_item_files, $filename_max_length;
 646  
 647      pbm_msg('<h4>Processing attachments</h4>');
 648  
 649      foreach( $mailAttachments as $attachment )
 650      {
 651          if( isset($attachment['FileName']) )
 652          {
 653              $filename = trim( evo_strtolower($attachment['FileName']) );
 654          }
 655          else
 656          {    // Related attachments may not have file name, we'll generate one below
 657              $filename = '';
 658          }
 659  
 660          if( $filename == '' )
 661          {
 662              $filename = 'upload_'.uniqid().'.'.$attachment['SubType'];
 663              pbm_msg( sprintf('Attachment without name. Using "%s".', htmlspecialchars($filename)) );
 664          }
 665  
 666          // Check valid filename/extension: (includes check for locked filenames)
 667          if( $error_filename = process_filename( $filename, true ) )
 668          {
 669              pbm_msg('Invalid filename: '.$error_filename);
 670              continue;
 671          }
 672  
 673          // If file exists count up a number
 674          $cnt = 0;
 675          $prename = substr( $filename, 0, strrpos( $filename, '.' ) ).'-';
 676          $sufname = strrchr( $filename, '.' );
 677          $error_in_filename = false;
 678  
 679          while( file_exists( $mediadir.$filename ) )
 680          {
 681              $filename = $prename.$cnt.$sufname;
 682              if( strlen( $filename ) > $filename_max_length )
 683              { // This is a special case, when the filename is longer then the maximum allowed
 684                  // Cut as many characters as required before the counter on the file name
 685                  $filename = fix_filename_length( $filename, strlen( $prename ) - 1 );
 686                  if( $error_in_filename = process_filename( $filename, true ) )
 687                  { // The file name is not valid, this is an unexpected situation, because the file name was already validated before
 688                      pbm_msg('Invalid filename: '.$error_filename);
 689                      break;
 690                  }
 691              }
 692              ++$cnt;
 693          }
 694          if( $error_in_filename )
 695          { // Don't create file with invalid file name
 696              continue;
 697          }
 698          pbm_msg( sprintf('New file name is <b>%s</b>', $filename) );
 699  
 700          $imginfo = NULL;
 701          if( ! $Settings->get('eblog_test_mode') )
 702          {
 703              pbm_msg( 'Saving file to: '.htmlspecialchars($mediadir.$filename) );
 704              if( !copy( $attachment['DataFile'], $mediadir.$filename ) )
 705              {
 706                  pbm_msg( 'Unable to copy uploaded file to '.htmlspecialchars($mediadir.$filename) );
 707                  continue;
 708              }
 709  
 710              // chmod uploaded file:
 711              $chmod = $Settings->get('fm_default_chmod_file');
 712              @chmod( $mediadir.$filename, octdec( $chmod ) );
 713  
 714              $imginfo = @getimagesize($mediadir.$filename);
 715              pbm_msg( 'Is this an image?: '.(is_array($imginfo) ? 'yes' : 'no') );
 716          }
 717  
 718          if( $type == 'attach' )
 719          {
 720              $content .= "\n";
 721              if( is_array($imginfo) && $add_img_tags )
 722              {
 723                  $content .= '<img src="'.$media_url.$filename.'" '.$imginfo[3].' />';
 724              }
 725              else
 726              {
 727                  pbm_msg( sprintf('The file <b>%s</b> will be attached to the post later, after we save the post in the database.', $filename) );
 728                  $pbm_item_files[] = $filename;
 729              }
 730              $content .= "\n";
 731          }
 732          elseif( !empty($attachment['ContentID']) )
 733          {    // Replace relative "cid:xxxxx" URIs with absolute URLs to media files
 734              $content = str_replace( 'cid:'.$attachment['ContentID'], $media_url.$filename, $content );
 735          }
 736      }
 737  }
 738  
 739  
 740  /**
 741   * Look inside message to get title for posting
 742   *
 743   * The message could contain a xml-tag <code><title>sample title</title></code> to specify a title for the posting.
 744   * If no tag is found we will try to get the default title from settings.
 745   * If none of these is found then the specified alternate title line is used.
 746   *
 747   * @param string $content message to search for title tag
 748   * @param string $alternate_title use this string if no title tag is found
 749   * @return string title of posting
 750   *
 751   */
 752  function pbm_get_post_title( $content, $alternate_title )
 753  {
 754      global $Settings;
 755  
 756      if( preg_match('~<title>(.+?)</title>~is', $content, $matchtitle) )
 757      {
 758          $title = $matchtitle[1];
 759      }
 760      else
 761      {
 762          $title = $Settings->get('eblog_default_title');
 763      }
 764  
 765      if( $title == '' )
 766      {
 767          $title = $alternate_title;
 768      }
 769      return $title;
 770  }
 771  
 772  
 773  /**
 774   * Extract <auth> tag with login and password
 775   *
 776   * @param string Message body (by reference)
 777   * @return array login and password
 778   */
 779  function pbm_get_auth_tag( & $content )
 780  {
 781      if( preg_match( '~<(auth)>(.+?)</\\1>~is', $content, $match ) )
 782      {
 783          // tblue> splitting only into 2 parts allows colons in the user PW
 784          // Note: login and password cannot include '<' !
 785          $auth = explode( ':', strip_tags($match[2]), 2 );
 786  
 787          // Delete 'auth' tag from post content
 788          $content = preg_replace( '~<(auth)>(.+?)</\\1>~is', '', $content );
 789  
 790          return $auth;
 791      }
 792      return false;
 793  }
 794  
 795  
 796  function pbm_prepare_html_message( $message )
 797  {
 798      pbm_msg('Message body (original): <pre style="font-size:10px">'.htmlspecialchars($message).'</pre>');
 799  
 800      $marker = 0;
 801      if( preg_match( '~<body[^>]*>(.*?)</body>~is', $message, $result ) )
 802      {    // First see if we can get contents of <body> tag
 803          $content = $result[1];
 804          $marker = 1;
 805      }
 806      elseif( preg_match( '~<html[^>]*>(.*?)</html>~is', $message, $result ) )
 807      {    // <body> was not found, use <html> contents and delete <head> section from it
 808          $content = preg_replace( '~<head[^>]*>(.*?)</head>~is', '', $result[1] );
 809          $marker = 1;
 810      }
 811  
 812      if( empty($marker) )
 813      {    // None of the above methods worked, just use the original message body
 814          $content = $message;
 815      }
 816  
 817      // First fix different line-endings (dos, mac, unix), remove double newlines
 818      $content = str_replace( array("\r", "\n\n"), "\n", trim($content) );
 819  
 820      // Decode 'category', 'title' and 'auth' tags
 821      $content = preg_replace( '~&lt;(/)?(category|title|auth)&gt;~i', '<\\1\\2>', $content );
 822  
 823      if( ($auth = pbm_get_auth_tag($content)) === false )
 824      {    // No 'auth' tag provided, exit
 825          pbm_msg( sprintf( T_('&lt;auth&gt; tag not found! Please add username and password in message body in format %s.'),
 826                      '"&lt;auth&gt;username:password&lt;/auth&gt;"' ), true );
 827          return false;
 828      }
 829  
 830      // Balance tags
 831      $content = balance_tags($content);
 832  
 833      // Remove markup that cause validator errors
 834      $patterns = array(
 835          '~ moz-do-not-send="true"~',            // Thunderbird inline image with absolute "src"
 836          '~ class="moz-signature" cols="\d+"~',    // Thunderbird signature in HTML message
 837          '~ goomoji="[^"]+"~',                    // Gmail smilies
 838      );
 839      $content = preg_replace( $patterns, '', $content );
 840  
 841      pbm_msg('Message body (processed): <pre style="font-size:10px">'.htmlspecialchars($content).'</pre>');
 842  
 843      return array( $auth, $content );
 844  }
 845  
 846  
 847  function pbm_validate_user_password( $user_login, $user_pass )
 848  {
 849      $UserCache = & get_UserCache();
 850      $User = & $UserCache->get_by_login( $user_login );
 851      if( ! $User )
 852      {
 853          return false;
 854      }
 855  
 856      // First check unhashed password
 857      if( ! $User->check_password( $user_pass, false ) )
 858      {
 859          if( preg_match( '~^[a-f0-9]{32}$~i', $user_pass ) )
 860          {    // This is a hashed password, see if it's valid
 861              // We check it here because some crazy user may use a real 32-chars password!
 862              if( $User->check_password( $user_pass, true ) )
 863              {    // Valid password
 864                  return $User;
 865              }
 866          }
 867          return false;
 868      }
 869      return $User;
 870  }
 871  
 872  
 873  /**
 874   * Create a new directory with unique name
 875   * This creates a new directory below the given path with the given prefix and a random number
 876   *
 877   * @param  string $dir base path to new directory
 878   * @param  string $prefix prefix random number with this
 879   * @param  integer $mode permissions to use
 880   * @return string path to created directory
 881   */
 882  function pbm_tempdir( $dir, $prefix = 'tmp', $mode = 0700 )
 883  {
 884      // Add trailing slash
 885      $dir = trailing_slash($dir);
 886  
 887      do { $path = $dir.$prefix.mt_rand(); } while( ! evo_mkdir( $path, $mode ) );
 888  
 889      return $path;
 890  }
 891  
 892  ?>

title

Description

title

Description

title

Description

title

title

Body