<?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.4.8.2 2004/11/29 13:13:05 kazuyan 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_en
	{
		var $err    = array('code','msg','desc');
		var $to_res = array();

		function send_en()
		{
			$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 */
	
	class send_ja extends send_en
	{
		var $codecv;
		
		function send_ja()
		{
			$this->codecv = CreateObject('phpgwapi.codecv');
		}
				
		function msg($service, $to, $subject, $body, $msgtype='', $cc='', $bcc='', $from='', $sender='', $content_type='')
		{
			$account_name = $GLOBALS['phpgw']->accounts->id2name(get_account_id());
			if ($from == '')
			{
				if (isset($GLOBALS['phpgw_info']['user']['preferences']['email']['address']) && $GLOBALS['phpgw_info']['user']['preferences']['email']['address'] != '')
					$from = $GLOBALS['phpgw_info']['user']['preferences']['email']['address'];
				if (empty($from) && isset($GLOBALS['phpgw_info']['server']['mail_suffix']) && $GLOBALS['phpgw_info']['server']['mail_suffix'] != '' && $account_name)
					$from = $account_name.'@'.$GLOBALS['phpgw_info']['server']['mail_suffix'];
				if ($from && isset($GLOBALS['phpgw_info']['user']['fullname']) && $GLOBALS['phpgw_info']['user']['fullname'] != '')
					$from = '"'.$GLOBALS['phpgw_info']['user']['fullname'].'" <'.$from.'>';
			}
			if ($sender == '')
			{
				if (isset($GLOBALS['phpgw_info']['user']['preferences']['email']['address']) && $GLOBALS['phpgw_info']['user']['preferences']['email']['address'] != '')
					$sender = $GLOBALS['phpgw_info']['user']['preferences']['email']['address'];
				if (empty($sender) && isset($GLOBALS['phpgw_info']['server']['mail_suffix']) && $GLOBALS['phpgw_info']['server']['mail_suffix'] != '' && $account_name)
					$sender = $account_name.'@'.$GLOBALS['phpgw_info']['server']['mail_suffix'];
				if ($sender && isset($GLOBALS['phpgw_info']['user']['fullname']) && $GLOBALS['phpgw_info']['user']['fullname'] != '')
					$sender = '"'.$GLOBALS['phpgw_info']['user']['fullname'].'" <'.$sender.'>';
			}

			$to = $this->encode_header($this->codecv->in2out($to));	
			$subject = $this->encode_header($this->codecv->in2out($subject));
			$from = $this->encode_header($this->codecv->in2out($from));
			$sender = $this->encode_header($this->codecv->in2out($sender));
			$body = $this->codecv->in2out($body);
			
			if ($service == "email")
			{
				$now = getdate();
				if (phpversion() >= '4.0.4')
					$header  = 'Date: '.date('r')."\n";
				else 
				{
					$offset = date('Z');
					$header  = 'Date: '.gmdate('D, d M Y H:i:s').chr(0x20).sprintf(($offset >= 0 ? "+%02d00" : "-%02d00"), abs($offset)/3600)."\n";
				}					
				$header .= 'From: '.$from."\n";
				if($from != $sender && $sender != '')
				{
					$header .= 'Sender: '.$sender."\n";
				}
				if (isset($GLOBALS['phpgw_info']['user']['preferences']['email']['address']) && $GLOBALS['phpgw_info']['user']['preferences']['email']['address'] != '')
					$header .= 'Reply-To: '.$GLOBALS['phpgw_info']['user']['preferences']['email']['address']."\n";
				elseif (isset($GLOBALS['phpgw_info']['server']['mail_suffix']) && $GLOBALS['phpgw_info']['server']['mail_suffix'] != '' && $account_name)
					$header .= 'Reply-To: '.$GLOBALS['phpgw']->accounts->id2name(get_account_id()).'@'.$GLOBALS['phpgw_info']['server']['mail_suffix']."\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('Message-Boundary', $body)) 
				{
					$header .= 'Subject: ' . stripslashes($subject) . "\n"
						. 'MIME-Version: 1.0'."\n"
						. 'Content-Type: multipart/mixed;'."\n"
						. ' boundary="Message-Boundary"'."\n\n"
						. '--Message-Boundary'."\n"
						. 'Content-type: text/' .$content_type . '; charset=US-ASCII'."\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('outcharset').'"'."\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?
			}			
		}

		function encode_header($data)
		{
			// check extra email address format
			$validate = createobject('phpgwapi.validator');
			if (preg_match_all('|\"([^\"]*)\"\s+<([^>]*)>|', $data, $regs))
			{
				for($i = 0;$i < count($regs[1]); $i++)
				{
					$str_replace = str_replace(' ', chr(0), $regs[1][$i]);
					$data = str_replace($regs[1][$i], $str_replace, $data);
					if (!$extra_email && $validate->is_email($regs[2][$i]))
						$extra_email = True;
				}
			}
			//$words[] = $data;
			$words = explode(' ', $data);
			
			for($i=0; $i<count($words); $i++)
			{
				//echo 'words['.$i.'] in loop: '.$words[$i].'<br>';
				$words[$i] = str_replace(chr(0), ' ', $words[$i]);
				// my interpetation of what to encode from RFC2045, RFC2047, and RFC2822
				// all these chars seem to cause trouble, so encode them
				if (preg_match('/'
					. '['.chr(1).'-'.chr(31).']'
					. '['.chr(33).'-'.chr(38).']'
					.'|[\\'.chr(39).']'
					.'|['.chr(40).'-'.chr(46).']'
					.'|[\\'.chr(47).']'
					.'|['.chr(61).'-'.chr(62).']'
					.'|['.chr(64).']'
					.'|['.chr(91).'-'.chr(94).']'
					.'|['.chr(96).']'
					.'|['.chr(123).'-'.chr(255).']'
					.'/', $words[$i]))
				{
					if ($extra_email && preg_match('|\"([^\"]*)\"|',$words[$i],$matches))
						$words[$i] = $matches[1];
					else
						unset($matches);
					$words[$i] = $this->encode_ISO2022JP_word($words[$i]);
					if (is_array($matches))
						$words[$i] = '"'.$words[$i].'"';	
				}
			}			
			// reassemble the string
			$encoded_str = implode(' ',$words);
			return $encoded_str;
		}		

		// SUB-FUNCTION - do not call directly, used by "encode_header()"
		function encode_ISO2022JP_word($string)
		{
			$base64_prefix = '=?' . lang('outcharset') . '?B?';
			$base64_suffix = '?=';
			$new_str = '';
	
			if ($this->codecv->get_charcode($string) == "US-ASCII")
			{
				return $string;
			}
			else
			{
				$enstring = $string;
			}
	
			$maxlen = 75 - 7 - strlen(lang('outcharset'));
			$maxlen -= $maxlen % 4;
			$enstring = $this->codecv->mb_chunk_split($enstring, $maxlen, "\r\n");
			$array_str = explode("\r\n", rtrim($enstring));
			while (list($idx, $enstring) = each($array_str))
			{
				$enstring = base64_encode($this->codecv->in2out($enstring));
				if ($idx == 0)
				{
					$array_str[$idx] = $base64_prefix . $enstring . $base64_suffix;
				}
				else
					$array_str[$idx] = "\t" . $base64_prefix . $enstring . $base64_suffix;
			}
			$new_str = implode($array_str, "\r\n");
	
			return  $new_str;
		}
		
		/*!
		@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-2022-jp?B?")
		*/
		function encode_subject($subject)
		{
			return $this->encode_header($subject);	
		}
	}
	if ($GLOBALS['phpgw_info']['user']['preferences']['common']['lang'] == 'ja')
		$parent_class_name = 'send_ja';
	else 
		$parent_class_name = 'send_en';
	if (!class_exists('send'))
		eval ("class send extends $parent_class_name {}");
