<?php

/**
  * BLOG:CMS: PHP/MySQL Personal Content Management System (CMS)
  * http://blogcms.com/
  * ----------------------------------------------------------------
  *
  * Copyright (C) 2003-2005 Radek HULÁN
  * http://hulan.cz/contact/
  *
  * Based on: 
  * ----------------------------------------------------------------
  * Nucleus: PHP/MySQL Weblog CMS (http://nucleuscms.org/) 
  * Copyright (C) 2002-2003 The Nucleus Group
  *
  * ----------------------------------------------------------------
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  * as published by the Free Software Foundation; either version 2
  * of the License, or (at your option) any later version.
**/

/**
   NP_PostMan usage:
    - point your browser to www.yoursite.com/action.php?action=plugin&name=PostMan
    or
    - set wget / lynx to your cron to extract messages automatically (like in every 5 minutes)
**/
   
global $DIR_PLUGINS;
if (!is_dir($DIR_PLUGINS)) die('System is not configured properly');
require($DIR_PLUGINS."postman/POP3.php");
require($DIR_PLUGINS."postman/mimeDecode.php");


class NP_PostMan extends NucleusPlugin {
   function getName() { return 'PostMan';    }
   function getAuthor()  { return 'Radek HULAN 1.1-2.3, anand &amp; Ihra - 1.0';    }
   function getURL() {   return 'http://blogcms.com/'; }
   function getVersion() { return '2.3'; }
   function getDescription( ) { return 'Post to blog by mail and MMS. Usage: Point your browser to www.yoursite.com/action.php?action=plugin&name=PostMan. Or set wget / lynx to your cron to extract messages automatically (like in every 5 minutes).'; }
   function supportsFeature($feature) { if ($feature=='SqlTablePrefix') return 1; else return 0;}
   
   function install() {
       $this->createOption('MailUser','Enter the mail user name.','text','user');
       $this->createOption('MailPassword','Enter the mail password.','password','password');
       $this->createOption('MailURL','Enter the mail host','text','localhost');
       $this->createOption('MailPort','Enter the mail POP3 port','text','110');
       $this->createOption('DefaultUser','Enter the BLOG:CMS user name.','text','user');
       $this->createOption('DefaultPassword','Enter the BLOG:CMS password.','password','password');
       $this->createOption('MustContainPrefix','Should the mail contain a prefix in body or in subject?','yesno','yes');
       $this->createOption('SubjectPrefix','If yes, what is the prefix','text','BLOGTHIS:');
       $this->createOption('OutputToBrowser','Send output to browser ?','yesno','yes');
       $this->createOption('DefaultPublish','Publish straight away ?','yesno','yes');
	   $this->createOption('MaxWidth','Maximum picture width in px?','text','350');
	   $this->createOption('EnlargeText','Text to show when larger image exists?','text','Enlarge this image?');
	   $this->createOption('JPEGQuality','JPEG picture quality (0-100)','text','85');
	   $this->createOption('AllowedAttachement','Allowed attachement file extensions, separated by comma','text','avi,mp3,mpg,mpeg,doc,xls,pdf,zip,rar');
	   $this->createOption('TextAttachement','Text to show with file attachement:','text','Attachement for download: ');
   }

   function init() {
       $this->moreKeyword     = '[more]';
       $this->categoryKeyword = 'category';
       $this->blogKeyword     = 'blog';
	   $this->subjectKeyword  = 'subject';
       $this->optionsKeyword  = '[options]';
       $this->type            = "html";
       $this->user            = '';
       $this->password        = '';
	   $this->pictures = array('jpg','jpeg','gif','png');
   }
   
   function doAction($type) {
       global $manager, $blog, $CONF;

      // connect to pop3-server
      $npop3 =& new Net_POP3();
      if (!$npop3->connect($this->getOption('MailURL'),$this->getOption('MailPort') )) 
	  	die('Cannot connect to mail server');
      if (!$npop3->login($this->getOption('MailUser'), $this->getOption('MailPassword'))) 
	  	die('Cannot login to mail server');
      $num = $npop3->numMsg();
	  
	  if (!$num) echo ('There are no new messages to process.');
      
      // to mime::decode
      $params['include_bodies'] = TRUE;
      $params['decode_bodies']  = FALSE;
      $params['decode_headers'] = TRUE;

      // login to BLOG:CMS
      $this->user     = $this->getOption('DefaultUser');
      $this->password = $this->getOption('DefaultPassword');
      $mem = new MEMBER();
      if (!$mem->login($this->user, $this->password)) {
        echo "Could not log in";
        return ;
      }
   
      // default blog a team rights?
      $blog    =& $manager->getBlog($CONF['DefaultBlog']);
      $blogid = $blog->blogid;
      if (!BLOG::existsID($blogid)) {
        echo "No such blog";
        return ;
      }
      if (!$mem->teamRights($blogid)) {
        echo "Not a team member";
        return;
      }

      for ($msg = 1; $msg <= $num; $msg++) {
         // get email
         $message = $npop3->getMsg($msg);
         $params['input'] = $message;
         $structure = Mail_mimeDecode::decode($params);

         // from
         $from = trim($structure->headers['from']);
         if (preg_match('/^[^<>]+<([^<>]+)>$/',$from,$matches)) $from = $matches[1];

         // content
         $body = $this->get_content($structure, $mem->getID());

         // subject
         $subject = $structure->headers['subject'];
		 // convert subject charset based on body charset
         if (isset($this->charset))
         	if (function_exists('iconv'))
            		$subject = iconv($this->charset,_CHARSET,$subject);
			elseif (function_exists('mb_convert_encoding'))
				$subject = mb_convert_encoding($subject,_CHARSET,$this->charset);
				
         if ( preg_match('/'.$this->getOption('SubjectPrefix').'/', $subject) ||
		      preg_match('/'.$this->getOption('SubjectPrefix').'/', $body) ||
               ($this->getOption('MustContainPrefix') == "no") )
         {
            // remove BLOGIT: prefix
			if ( $this->getOption('MustContainPrefix') == "yes") 
				$subject = trim(str_replace($this->getOption('SubjectPrefix'), '', $subject));

            // Remove everything before p.smil -attachment (operator-specified "not supporting mms" -message)
            if (strpos($body, '.smil')) {
               $smilPos = intval(strpos($body, '.smil'));
               $body    = substr($body,$smilPos+17, strlen($body)-$smilPos);
            }
              
            // body
            $body = trim($body);

			// other parameters			
            $category = $blog->getDefaultCategory();
            $timestamp = $blog->getCorrectTime();

            // blog/category options in subject
            if (preg_match('/'.$this->optionsKeyword.'/', $subject)) {
              // different blog?
              if (preg_match('/'.$this->blogKeyword.'/', $subject)) $blogid  = $this->parseToken($subject,$this->blogKeyword);
              // different category?
              if (preg_match('/'.$this->categoryKeyword.'/', $subject))  $category  = getCatIDFromName($this->parseToken($subject,$this->categoryKeyword));
              $optionPos = intval(strpos($subject, $this->optionsKeyword));
              if ( $optionPos ) $subject = substr($subject, 0, $optionPos);
              $subject = trim($subject);
            }
			
            // blog/category options in body (might be both, but so which one?)
            if (trim($this->getOption('SubjectPrefix')) && preg_match('/'.$this->getOption('SubjectPrefix').'/', $body)) {
			  // subject
			  if (preg_match('/'.$this->subjectKeyword.'/', $body)) $subject  = $this->parseToken($body,$this->subjectKeyword);
              // different blog?
              if (preg_match('/'.$this->blogKeyword.'/', $body)) $blogid  = $this->parseToken($body,$this->blogKeyword);
              // different category?
              if (preg_match('/'.$this->categoryKeyword.'/', $body)) $category  = getCatIDFromName($this->parseToken($body,$this->categoryKeyword));
              $prefixplace =0;
              if ( $this->getOption('MustContainPrefix') == "yes") {
				  $prefixplace = strpos($body,$this->getOption('SubjectPrefix'));
                  $body = trim(str_replace($this->getOption('SubjectPrefix'), '', $body));
              }
              $body = trim(substr($body,strrpos($body,';')+1,strlen($body)));
            } // body options
              
			// break article into body and more?
            $morePos = strpos($body, $this->moreKeyword);
            if ( $morePos!==false ) {
				$more = substr($body,$morePos+strlen($this->moreKeyword));
				$body = substr($body, 0, $morePos);
            } else
				$more = '';
    
            if (!$blog->isValidCategory(intval($category))) 
				$category = $blog->getDefaultCategory();   
			
			$this->_addDatedItem($blogid,$subject,$body,$more,0,$timestamp,$category,$mem->getID());

            // to debug without deleting your messages, comment this line
            $npop3->deleteMsg($msg);
   
          } // SubjectPrefix
      } // for
      $npop3->disconnect();
   }   

   function parseToken($line,$keyword) {
       $words = explode($keyword,$line);
       $words = explode('=',$words[1]);
       $word = explode(';',$words[1]);
       return $word[0];
   }

   function _addDatedItem($blogid,$title, $body, $more,$closed, $timestamp, $category, $memberid) {
      $blog = new BLOG($blogid);
      if ( $this->getOption('DefaultPublish') == 'yes' )
           $draft = 0;
       else
           $draft = 1;
      if ($closed != 1) $closed = 0;
      $itemid = $blog->additem($category, $title, $body, $more, $blogid, $memberid, $timestamp, $closed, $draft);
      if ( $this->getOption('OutputToBrowser') == 'yes' )  {
		 print "<html><head>";
		 print "<meta http-equiv='Content-Type' content='text/html; charset='"._CHARSET."' />";
		 print "</head><body>";
         print '<p><b>Blog</b>: ' . $blogid . '<br />' . "</p>\n";
         print '<p><b>Category</b>: ' . $category. '<br />' . "</p>\n";
         print '<p><b>Itemid</b>: ' . $itemid . '<br />' . "</p>\n";
         print '<p><b>Author</b>: ' . $memberid . '<br />' . "</p>\n";
         print '<p><b>Subject</b>: ' . $title . '<br />' . "</p>\n";
         print '<p><b>Body:</b></p><hr />' . $body . '<br>' . $more . '<hr />';
		 print '</body></html>';
      }
   }
   
	/*
		function _unencode()
		This function checks if the content is base64 or quoted-printable
	*/
	function _unencode ( &$part ){
	   if ( strtolower($part->headers['content-transfer-encoding']) == 'base64' ) {
		  $part->body = base64_decode($part->body);
	   }
	   if ( strtolower($part->headers['content-transfer-encoding']) == 'quoted-printable' ) {
		  $part->body = quoted_printable_decode($part->body);
	   }
	}
	
	/*
		function charsetcheck()
		decode charset correctly
	*/
	function charsetcheck ( &$part ) {
		if (function_exists('iconv'))
			return iconv($part->ctype_parameters['charset'],_CHARSET,$part->body);
		if (function_exists('mb_convert_encoding'))
			return mb_convert_encoding($part->body,_CHARSET,$part->ctype_parameters['charset']);
		return $part->body;
	}
	
	/*
		function get_content()
		explode meta-parts to find content
	*/
	function get_content ($part, $memberid) {
	   $this->_unencode($part);
	   switch ( strtolower($part->ctype_primary) ) {
	   case 'multipart':
		  $meta_return = '';
		  $this->text=false;
		  foreach ($part->parts as $section) $meta_return .= $this->get_content($section, $memberid);
		  break;
	   case 'text':
	      // if there are both html and text parts, take only the first one
	   	  if ($this->text) {
			  $meta_return='';
			  break;
		  }
		  // set charset
		  $this->charset=$part->ctype_parameters['charset'];
		  // part type
		  $part->ctype_secondary=strtolower($part->ctype_secondary);
	   	  if ($part->ctype_secondary=='html') {
		    // PROCESS HTML
			$meta_return = $this->removesignature($this->charsetcheck($part));
			// remove DOCTYPE and such
			$i = preg_match('/<body(.*?)>/i',$meta_return,$matches);
			if ($i) $meta_return=substr($meta_return,strpos($meta_return,$matches[0])+strlen($matches[0]));
			$i = preg_match('/<\/body>/i',$meta_return,$matches);
			if ($i) $meta_return=substr($meta_return,0,strpos($meta_return,$matches[0]));
			// convert HTML into XHTML
			$meta_return=str_replace('<br>','<br />',$meta_return);
			$meta_return=str_replace('<hr>','<hr />',$meta_return);
			// retain valid XHTML if there is moreKeyword
			$morePos = strpos($meta_return, $this->moreKeyword);
			if ($morePos!==false){
				$more = $this->moreKeyword.paragraph(substr($meta_return,$morePos+strlen($this->moreKeyword)),false);
				$body = paragraph(substr($meta_return, 0, $morePos),false);
				$meta_return = $body.$more;
			}
			$meta_return = trim($meta_return);
			if ($meta_return) $this->text=true;
		  } elseif ($part->ctype_secondary=='plain') {
		    // PROCESS PLAIN TEXT
			$body = $this->removesignature($this->charsetcheck($part));
			$morePos = strpos($body, $this->moreKeyword);  
			if ($morePos!==false){
				$more = $this->moreKeyword.paragraph(substr($body,$morePos+strlen($this->moreKeyword)),false);
				$body = paragraph(substr($body, 0, $morePos),false);
			} else {
				$body = paragraph($body,false);
				$more = '';
			}
		    $meta_return = $body.$more;
			$meta_return = trim($meta_return);
			if ($meta_return) $this->text=true;
		  }
		  else $meta_return = '';
		  break;
	   case 'application':
	      // only allowed file extensions
		  $allowedarr=explode(',',$this->getOption('AllowedAttachement'));
		  if (empty($part->ctype_parameters['name'])) 
		  	$part->ctype_parameters['name']=$part->d_parameters['filename'];
		  $pos=strrpos($part->ctype_parameters['name'],'.');
		  if (!$pos) { 
			  $meta_return='';
			  break;
		  }
		  $name=strtolower(substr($part->ctype_parameters['name'],0,$pos));
		  $ext=strtolower(substr($part->ctype_parameters['name'],$pos+1));
		  if (!in_array($ext,$this->pictures)) { // this is not a picture
			  if (!in_array($ext,$allowedarr)) {
				  $meta_return='';
				  break;
			  }
			  // convert app name
			  if (isset($this->charset))
				if (function_exists('iconv'))
						$part->ctype_parameters['name'] = iconv($this->charset,_CHARSET,$part->ctype_parameters['name']);
				elseif (function_exists('mb_convert_encoding'))
					$part->ctype_parameters['name'] = mb_convert_encoding($part->ctype_parameters['name'],_CHARSET,$this->charset);
			  global $DIR_MEDIA, $CONF;
			  if (!is_dir($DIR_MEDIA)) die('System is not configured properly');
			  do {
				 $filename =  "$memberid/$name-" . rand(0,9999) . '.' . $ext;
			  } while ( @file_exists($DIR_MEDIA.$filename) );
			  $fp = fopen($DIR_MEDIA.$filename,'w');
			  if (!$fp) die('Cannot write attachement to '.$DIR_MEDIA.$filename);
			  fwrite($fp, $part->body );
			  fclose($fp);
			  $size=filesize($DIR_MEDIA.$filename);
			  $meta_return .= '<p>'.$this->getOption('TextAttachement').'<a href="'.$CONF['MediaURL'].$filename.'">'.$part->ctype_parameters['name'].'</a> ['.$size.' b]</p>'."\n";
			  break;
		  }
	   case 'image':
	   case 'application':
		  if (empty($part->ctype_parameters['name'])) 
		  	$part->ctype_parameters['name']=$part->d_parameters['filename'];
		  $pos=strrpos($part->ctype_parameters['name'],'.');
		  if (!$pos) { 
			  $meta_return='';
			  break;
		  }
		  $name=strtolower(substr($part->ctype_parameters['name'],0,$pos));
		  $ext=strtolower(substr($part->ctype_parameters['name'],$pos+1));
		  if (!in_array($ext,$this->pictures)) break; // this is not a picture
	      // convert picture name
	      if (isset($this->charset))
         	if (function_exists('iconv'))
            		$part->ctype_parameters['name'] = iconv($this->charset,_CHARSET,$part->ctype_parameters['name']);
			elseif (function_exists('mb_convert_encoding'))
				$part->ctype_parameters['name'] = mb_convert_encoding($part->ctype_parameters['name'],_CHARSET,$this->charset);
		  // make sure filename does not exists yet
		  global $DIR_MEDIA, $CONF;
		  do {
			 $filename =  "$memberid/mms-" . rand(0,999999999) . '.' . $ext;
		  } while ( @file_exists($DIR_MEDIA.$filename) );
		  $fp = fopen($DIR_MEDIA.$filename, 'w');
		  if (!$fp) die('Cannot write image to '.$DIR_MEDIA.$filename);
		  fwrite($fp, $part->body);
		  fclose($fp);
		  $img = @getimagesize($DIR_MEDIA.$filename);
		  if (is_array($img)) {
			$max=intval($this->getOption('MaxWidth'));
			if (intval($img[0])>$max) {
				// got picture size, but it is bigger than allowed
				$this->checkgd();
				$ratio=floatval($max)/floatval($img[0]);
				$width=intval($img[0]*$ratio);
				$height=intval($img[1]*$ratio);
				if ($this->gd) {
					// create true thumbnail using GD/GD2
					$thumb=str_replace("$memberid/mms-","$memberid/thumb-",$filename);
					$this->createthumb($DIR_MEDIA.$filename,$DIR_MEDIA.$thumb,$width,$height,intval($this->getOption('JPEGQuality')));
					$meta_return = 
						'<div class="box">'
						.'<%image('. $thumb   .'|'.$width.'|'.$height.'|' . $part->ctype_parameters['name'] . ')%>'
						.'<%popup('. $filename. '|'.$img[0].'|'.$img[1].'|'.$this->getOption('EnlargeText') . ')%>'
						.'</div>';
				} else {
					// no GD avaialable, create thumbnail via XHTML only 
					$meta_return = 
						'<div class="box">'
						.'<%image('. $filename .'|'.$width.'|'.$height.'|' . $part->ctype_parameters['name'] . ')%>'
						.'<%popup('. $filename. '|'.$img[0].'|'.$img[1].'|'.$this->getOption('EnlargeText') . ')%>'
						.'</div>';
				}
			} else {
				// picture is small, so just embed it directly
				$meta_return = '<div class=leftbox"><%image('. $filename .'|'.$img[0].'|'.$img[1].'|' . $part->ctype_parameters['name'] . ')%></div>';
			}
		  } else {
			// do not even have image size
			$meta_return = '<div class="leftbox"><img src="'.$CONF['MediaURL'].$filename.'" alt="'.$part->ctype_parameters['name'].'" /></div>';
		  }
		  break;
	   }      
	   return $meta_return;
	}
		  
	/*
		Function removesignature()
		Keeps content until finds a line with '--', removes rest of the content
	*/
	function removesignature ( $content ) {
	   $arrcontent = explode("\n", $content);
	   $strcontent = '';
	   for ($i = 0; $i<=count($arrcontent); $i++) {
		  if ( preg_match('/^--$/',trim($arrcontent[$i])) ) break;
		  $strcontent .= $arrcontent[$i];
	   }
	   $search = array ('/ (\n|\r\n|\r)/','/(\n|\r\n|\r)/');
	   $replace = array (' ',"\n");
	   return preg_replace($search,$replace,$strcontent);      
	}

	/*
		Function checkgd()
		checks the version of gd, and returns "yes" when it's higher than 2
	*/
	function checkgd(){
		if (isset($this->gd)) return;
		ob_start();
		@phpinfo(INFO_MODULES);
		$phpinfo=ob_get_contents();
		ob_end_clean();
		$phpinfo=strip_tags($phpinfo);
		if (strlen($phpinfo)<2048) 
			$this->gd=2; // phpinfo() is probably not allowed
		else {
			$phpinfo=stristr($phpinfo,"gd version");
			if (!$phpinfo) {
				$this->gd=0;
			} else {
				for ($i=0;$i<strlen($phpinfo);$i++) 
					if (is_numeric(substr($phpinfo,$i,1))) break;
					$this->gd=intval(substr($phpinfo,$i,1));
			}
		}
	}

	/*
		Function createthumb($name,$filename,$new_w,$new_h)
		creates a resized image
		variables:
		$name		Original filename
		$filename	Filename of the resized image
		$new_w		width of resized image
		$new_h		height of resized image
	*/	
	function createthumb($from,$to,$thumb_w,$thumb_h,$quality) {
		if (preg_match("/\.jpg|jpeg/",$from))
			$src_img=@imagecreatefromjpeg($from);
		elseif (preg_match("/\.png/",$from))
			$src_img=@imagecreatefrompng($from);
		elseif (preg_match("/\.gif/",$from))
			$src_img=@imagecreatefromgif($from);
		else
			die('Unsupported image format: '.$from); 
		if (!isset($src_img)) 
			die('Cannot create thumbnail image for: '.$from);
		$old_x=imageSX($src_img);
		$old_y=imageSY($src_img);
		if ($this->gd==1 || preg_match("/\.gif/",$from) || !function_exists('imagecopyresampled')){
				$dst_img=imagecreate($thumb_w,$thumb_h);
				imagecopyresized($dst_img,$src_img,0,0,0,0,$thumb_w,$thumb_h,$old_x,$old_y); 
		} else {
			$dst_img=imagecreatetruecolor($thumb_w,$thumb_h);
			if (function_exists('imageantialias')) imageantialias($dst_img,true);
			imagecopyresampled($dst_img,$src_img,0,0,0,0,$thumb_w,$thumb_h,$old_x,$old_y);
		}
		if (preg_match("/\.png/",$from))
			@imagepng($dst_img,$to); 
		elseif (preg_match("/\.jpg|jpeg/",$from))
			@imagejpeg($dst_img,$to,$quality);
		else 
			@imagegif($dst_img,$to);
		@imagedestroy($dst_img); 
		@imagedestroy($src_img); 
	}

}   // NP_PostMan

?>