<?php
	/**
	* SMTP mailer
	* @author Itzchak Rehberg <izzysoft@qumran.org>
	* @author Joseph Engo <jengo@phpgroupware.org>
	* @copyright Copyright (C) 2000,2001 Itzchak Rehberg
	* @copyright Portions Copyright (C) 2004 Free Software Foundation, Inc. http://www.fsf.org/
	* @license http://www.fsf.org/licenses/lgpl.html GNU Lesser General Public License
	* @package phpgwapi
	* @subpackage network
	* @version $Id: class.send.inc.php,v 1.13.2.2.2.6 2004/11/06 15:34:26 powerstat Exp $
	*/

	/**
	* SMTP mailer
	* 
	* @package phpgwapi
	* @subpackage network
	* This module should replace php's mail() function. It is fully syntax
	* compatible. In addition, when an error occures, a detailed error info
	* is stored in the array $send->err (see ../inc/email/global.inc.php for
	* details on this variable).
	*/
	class send
	{
		var $err    = array('code','msg','desc');
		var $to_res = array();

		function send()
		{
			$this->err['code'] = ' ';
			$this->err['msg']  = ' ';
			$this->err['desc'] = ' ';
		}

		function msg($service, $to, $subject, $body, $msgtype='', $cc='', $bcc='', $from='', $sender='', $content_type='', $boundary='Message-Boundary')
		{
			if ($from == '')
			{
				$from = $GLOBALS['phpgw_info']['user']['fullname'].' <'.$GLOBALS['phpgw_info']['user']['preferences']['email']['address'].'>';
			}
			if ($sender == '')
			{
				$sender = $GLOBALS['phpgw_info']['user']['fullname'].' <'.$GLOBALS['phpgw_info']['user']['preferences']['email']['address'].'>';
			}

			if ($service == "email")
			{
				$now = getdate();
				$header  = 'Date: '.gmdate('D, d M Y H:i:s').' +0000'."\n";
				$header .= 'From: '.$from."\n";
				if($from != $sender)
				{
					$header .= 'Sender: '.$sender."\n";
				}
				$header .= 'Reply-To: '.$GLOBALS['phpgw_info']['user']['preferences']['email']['address']."\n";
				$header .= 'To: '.$to."\n";
				if (!empty($cc))
				{
					$header .= 'Cc: '.$cc."\n";
				}
				if (!empty($bcc))
				{
					$header .= 'Bcc: '.$bcc."\n";
				}
				if (!empty($msgtype))
				{
					$header .= 'X-phpGW-Type: '.$msgtype."\n";
				}
				$header .= 'X-Mailer: phpGroupWare (http://www.phpgroupware.org)'."\n";

				/* // moved to email/send_message.php
				if ($GLOBALS['phpgw_info']['user']['preferences']['email']['email_sig'] && $attach_sig)
				{
					//$body .= "\n-----\n".$GLOBALS['phpgw_info']['user']['preferences']['email']['email_sig'];
					$get_sig = $this->sig_html_to_text($GLOBALS['phpgw_info']['user']['preferences']['email']['email_sig']);
					$body .= "\n-----\n" .$get_sig;
				}
				*/

				if (empty($content_type))
				{
					$content_type ='plain';
				}

				if (ereg($boundary, $body)) 
				{
					$header .= 'Subject: ' . stripslashes($subject) . "\n"
						. 'MIME-Version: 1.0'."\n"
						. 'Content-Type: multipart/mixed;'."\n"
						. " boundary=\"$boundary\"\n\n"
						. "--$boundary\n"
						. 'Content-type: text/' .$content_type . '; charset="'.lang('charset').'"'."\n";
//					if (!empty($msgtype))
//					{
//						$header .= "Content-type: text/' .$content_type . '; phpgw-type=".$msgtype."\n";
//					}

					$header .= 'Content-Disposition: inline'."\n"
						. 'Content-transfer-encoding: 7BIT'."\n\n"
						. $body;
					$body = "";
				}
				else
				{
					$header .= 'Subject: '.stripslashes($subject)."\n"
						. 'MIME-version: 1.0'."\n"
						. 'Content-type: text/' .$content_type . '; charset="'.lang('charset').'"'."\n";
					if (!empty($msgtype))
					{
						$header .= 'Content-type: text/' .$content_type . '; phpgw-type='.$msgtype."\n";
					}
					$header .= 'Content-Disposition: inline'."\n"
						. 'Content-description: Mail message body'."\n";
				}
				if ($GLOBALS['phpgw_info']['user']['preferences']['email']['mail_server_type'] == 'imap' && $GLOBALS['phpgw_info']['user']['apps']['email'] &&
				    $GLOBALS['phpgw_info']['user']['preferences']['email']['use_sent_folder'])
				{
					if(!is_object($GLOBALS['phpgw']->msg))
					{
						$GLOBALS['phpgw']->msg = CreateObject('email.mail_msg');
					}
					$args_array = Array();
					$args_array['do_login'] = True;
					$args_array['folder'] = $GLOBALS['phpgw_info']['user']['preferences']['email']['sent_folder_name'];
					$GLOBALS['phpgw']->msg->begin_request($args_array);
					$GLOBALS['phpgw']->msg->phpgw_append('Sent', $header."\n".$body, "\\Seen");
					$GLOBALS['phpgw']->msg->end_request();
				}
				if (strlen($cc)>1)
				{
					$to .= ','.$cc;
				}

				if (strlen($bcc)>1)
				{
					$to .= ','.$bcc;
				}
				$returnccode = $this->smail($to, '', $body, $header);

				return $returnccode;
			}
			elseif ($type == 'nntp')
			{
				// nothing is here?
			}
		}

		// ==================================================[ some sub-functions ]===

		/*!
		@function encode_subject
		@abstract encode 8-bit chars in subject-line
		@author ralfbecker
		@note the quoted subjects get a header stateing the charset (eg. "=?iso-8859-1?Q?"), the \
			8-bit chars as '=XX' (XX is the hex-representation of the char) and a trainling '?='.
		*/
		function encode_subject($subject)
		{
			$enc_start = $enc_end = 0;

			$words = explode(' ',$subject);
			foreach($words as $w => $word)
			{
				$str = '';

				for ($i = 0; $i < strlen($word); ++$i)
				{
					if (($n = ord($word[$i])) > 127 || $word[$i] == '=') {
						$str .= sprintf('=%0X',$n);
						if (!$enc_start)
						{
							$enc_start = $w+1;
						}
						$enc_end = $w+1;
					}
					else
					{
						$str .= $word[$i];
					}
				}
				$strs[] = $str;
				//echo "word='$word', start=$enc_start, end=$enc_end, encoded='$str'<br />\n";
			}
			if (!$enc_start)
			{
				return $subject;
			}
			$str = '';
			foreach ($strs as $w => $s)
			{
				$str .= $str != '' ? ' ' : '';

				if ($enc_start == $w+1)	// first word to encode
				{
					$str .= '=?'.lang('charset').'?Q?';
				}
				$str .= $w+1 > $enc_end ? str_replace('=3D','=',$s) : $s;

				if ($enc_end == $w+1)	// last word to encode
				{
					$str .= '?=';
				}
			}
			//echo "<p>send::encode_subject('$subject')='$str'</p>\n";
			return $str;
		}

		function socket2msg($socket)
		{
			$followme = '-';
			$this->err['msg'] = '';
			do
			{
				$rmsg = fgets($socket,255);
				// echo "< $rmsg<br />\n";
				$this->err['code'] = substr($rmsg,0,3);
				$followme = substr($rmsg,3,1);
				$this->err['msg'] = substr($rmsg,4);
				if (substr($this->err["code"],0,1) != 2 && substr($this->err["code"],0,1) != 3)
				{
					$rc  = fclose($socket);
					return False;
				}
				if ($followme == ' ')
				{
					break;
				}
			}
			while ($followme == '-');
			return True;
		}

		function msg2socket($socket,$message)
		{
			// send single line\n
			// echo "raw> $message<br />\n";
			// echo "hex> ".bin2hex($message)."<br />\n";
			$rc = fputs($socket,"$message");
			if (!$rc)
			{
				$this->err['code'] = '420';
				$this->err['msg']  = 'lost connection';
				$this->err['desc'] = 'Lost connection to smtp server.';
				$rc  = fclose($socket);
				return False;
			}
			return True;
		}

		function put2socket($socket,$message)
		{
			// check for multiple lines 1st
			$pos = strpos($message,"\n");
			if (!is_int($pos))
			{
				// no new line found
				$message .= "\r\n";
				$this->msg2socket($socket,$message);
			}
			else
			{
				// multiple lines, we have to split it
				do
				{
					$msglen = $pos + 1;
					$msg = substr($message,0,$msglen);
					$message = substr($message,$msglen);
					$pos = strpos($msg,"\r\n");
					if (!is_int($pos))
					{
						// line not terminated
						$msg = chop($msg)."\r\n";
					}
					$pos = strpos($msg,'.');  // escape leading periods
					if (is_int($pos) && !$pos)
					{
						$msg = '.' . $msg;
					}
					if (!$this->msg2socket($socket,$msg))
					{
						return False;
					}
					$pos = strpos($message,"\n");
				}
				while (strlen($message)>0);
			}
			return True;
		}

		function check_header($subject,$header)
		{
			// check if header contains subject and is correctly terminated
			$header = chop($header);
			$header .= "\n";
			if (is_string($subject) && !$subject)
			{
				// no subject specified
				return $header;
			}
			$theader = strtolower($header);
			$pos  = strpos($theader,"\nsubject:");
			if (is_int($pos))
			{
				// found after a new line
				return $header;
			}
			$pos = strpos($theader,'subject:');
			if (is_int($pos) && !$pos)
			{
				// found at start
				return $header;
			}
			$pos = substr($subject,"\n");
			if (!is_int($pos))
			{
				$subject .= "\n";
			}
			$subject = 'Subject: ' .$subject;
			$header .= $subject;
			return $header;
		}

		function sig_html_to_text($sig)
		{
			// convert HTML chars for  '  and  "  in the email sig to normal text
			$sig_clean = $sig;
			$sig_clean = ereg_replace('&quot;', '"', $sig_clean);
			$sig_clean = ereg_replace('&#039;', '\'', $sig_clean);
			return $sig_clean;
		}

 // ==============================================[ main function: smail() ]===

		function smail($to,$subject,$message,$header)
		{
			$fromuser = $GLOBALS['phpgw_info']['user']['preferences']['email']['address'];
			$mymachine = $GLOBALS['phpgw_info']['server']['hostname'];
			// error code and message of failed connection
			$errcode = '';
			$errmsg = '';
			// timeout in secs
			$timeout = 5;

			// now we try to open the socket and check, if any smtp server responds
			$smtp_port = $GLOBALS['phpgw_info']['server']['smtp_port'] ? $GLOBALS['phpgw_info']['server']['smtp_port'] : 25;
			$socket = fsockopen($GLOBALS['phpgw_info']['server']['smtp_server'],$smtp_port,$errcode,$errmsg,$timeout);
			if (!$socket)
			{
				$this->err['code'] = '420';
				$this->err['msg']  = $errcode . ':' . $errmsg;
				$this->err['desc'] = 'Connection to '.$GLOBALS['phpgw_info']['server']['smtp_server'].':'.$GLOBALS['phpgw_info']['server']['smtp_port'].' failed - could not open socket.';
				return False;
			}
			else
			{
				$rrc = $this->socket2msg($socket);
			}

			// now we can send our message. 1st we identify ourselves and the sender
			$cmds = array (
				"\$src = \$this->msg2socket(\$socket,\"HELO \$mymachine\r\n\");",
				"\$rrc = \$this->socket2msg(\$socket);",
				"\$src = \$this->msg2socket(\$socket,\"MAIL FROM:<\$fromuser>\r\n\");",
				"\$rrc = \$this->socket2msg(\$socket);"
			);
			for ($src=True,$rrc=True,$i=0; $i<count($cmds);$i++)
			{
				eval ($cmds[$i]);
				if (!$src || !$rrc)
				{
					return False;
				}
			}

			// now we've got to evaluate the $to's
			$toaddr = explode(",",$to);
			$numaddr = count($toaddr);
			for ($i=0; $i<$numaddr; $i++)
			{
				$src = $this->msg2socket($socket,'RCPT TO:<'.$toaddr[$i].">\r\n");
				$rrc = $this->socket2msg($socket);
				// for lateron validation
				$this->to_res[$i]['addr'] = $toaddr[$i];
				$this->to_res[$i]['code'] = $this->err['code'];
				$this->to_res[$i]['msg']  = $this->err['msg'];
				$this->to_res[$i]['desc'] = $this->err['desc'];
			}

			//now we have to make sure that at least one $to-address was accepted
			$stop = 1;
			for ($i=0;$i<count($this->to_res);$i++)
			{
				$rc = substr($this->to_res[$i]['code'],0,1);
				if ($rc == 2)
				{
					// at least to this address we can deliver
					$stop = 0;
				}
			}
			if ($stop)
			{
				// no address found we can deliver to
				return False;
			}

			// now we can go to deliver the message!
			if (!$this->msg2socket($socket,"DATA\r\n"))
			{
				return False;
			}
			if (!$this->socket2msg($socket))
			{
				return False;
			}
			if ($header != "")
			{
				$header = $this->check_header($subject,$header);
				if (!$this->put2socket($socket,$header))
				{
					return False;
				}
				if (!$this->put2socket($socket,"\r\n"))
				{
					return False;
				}
			}
			$message  = chop($message);
			$message .= "\n";
			if (!$this->put2socket($socket,$message))
			{
				return False;
			}
			if (!$this->msg2socket($socket,".\r\n"))
			{
				return False;
			}
			if (!$this->socket2msg($socket))
			{
				return False;
			}
			if (!$this->msg2socket($socket,"QUIT\r\n"))
			{
				return False;
			}
			do
			{
				$closing = $this->socket2msg($socket);
			}
			while ($closing);
			return True;
		}
	} /* end of class */
