PageRenderTime 47ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/include/SugarPHPMailer.php

https://github.com/mikmagic/sugarcrm_dev
PHP | 401 lines | 242 code | 48 blank | 111 comment | 52 complexity | 3274e482b96fdaa19f916a24aa63cdcc MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, LGPL-2.1, BSD-3-Clause, AGPL-3.0
  1. <?php
  2. if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
  3. /*********************************************************************************
  4. * SugarCRM Community Edition is a customer relationship management program developed by
  5. * SugarCRM, Inc. Copyright (C) 2004-2011 SugarCRM Inc.
  6. *
  7. * This program is free software; you can redistribute it and/or modify it under
  8. * the terms of the GNU Affero General Public License version 3 as published by the
  9. * Free Software Foundation with the addition of the following permission added
  10. * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
  11. * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
  12. * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
  13. *
  14. * This program is distributed in the hope that it will be useful, but WITHOUT
  15. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  16. * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
  17. * details.
  18. *
  19. * You should have received a copy of the GNU Affero General Public License along with
  20. * this program; if not, see http://www.gnu.org/licenses or write to the Free
  21. * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  22. * 02110-1301 USA.
  23. *
  24. * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
  25. * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
  26. *
  27. * The interactive user interfaces in modified source and object code versions
  28. * of this program must display Appropriate Legal Notices, as required under
  29. * Section 5 of the GNU Affero General Public License version 3.
  30. *
  31. * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
  32. * these Appropriate Legal Notices must retain the display of the "Powered by
  33. * SugarCRM" logo. If the display of the logo is not reasonably feasible for
  34. * technical reasons, the Appropriate Legal Notices must display the words
  35. * "Powered by SugarCRM".
  36. ********************************************************************************/
  37. /*********************************************************************************
  38. * Description: TODO: To be written.
  39. * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc.
  40. * All Rights Reserved.
  41. * Contributor(s): ______________________________________..
  42. ********************************************************************************/
  43. require_once('include/phpmailer/class.phpmailer.php');
  44. require_once('include/OutboundEmail/OutboundEmail.php');
  45. class SugarPHPMailer extends PHPMailer {
  46. var $oe; // OutboundEmail
  47. var $protocol = "tcp://";
  48. var $preppedForOutbound = false;
  49. var $disclosureEnabled;
  50. var $disclosureText;
  51. var $isHostEmpty = false;
  52. var $opensslOpened = true;
  53. /**
  54. * Sole constructor
  55. */
  56. function SugarPHPMailer() {
  57. global $locale;
  58. global $current_user;
  59. global $sugar_config;
  60. $admin = new Administration();
  61. $admin->retrieveSettings();
  62. if(isset($admin->settings['disclosure_enable']) && !empty($admin->settings['disclosure_enable'])) {
  63. $this->disclosureEnabled = true;
  64. $this->disclosureText = $admin->settings['disclosure_text'];
  65. }
  66. $this->oe = new OutboundEmail();
  67. $this->oe->getUserMailerSettings($current_user);
  68. $this->SetLanguage('en', 'include/phpmailer/language/');
  69. $this->PluginDir = 'include/phpmailer/';
  70. $this->Mailer = 'smtp';
  71. // cn: i18n
  72. $this->CharSet = $locale->getPrecedentPreference('default_email_charset');
  73. $this->Encoding = 'quoted-printable';
  74. $this->IsHTML(false); // default to plain-text email
  75. $this->Hostname = $sugar_config['host_name'];
  76. $this->WordWrap = 996;
  77. // cn: gmail fix
  78. $this->protocol = ($this->oe->mail_smtpssl == 1) ? "ssl://" : $this->protocol;
  79. }
  80. /**
  81. * Prefills outbound details
  82. */
  83. function setMailer() {
  84. global $current_user;
  85. require_once("include/OutboundEmail/OutboundEmail.php");
  86. $oe = new OutboundEmail();
  87. $oe = $oe->getUserMailerSettings($current_user, $mailer_id, $ieId);
  88. // ssl or tcp - keeping outside isSMTP b/c a default may inadvertantly set ssl://
  89. $this->protocol = ($oe->mail_smtpssl) ? "ssl://" : "tcp://";
  90. if($oe->mail_sendtype == "SMTP")
  91. {
  92. //Set mail send type information
  93. $this->Mailer = "smtp";
  94. $this->Host = $oe->mail_smtpserver;
  95. $this->Port = $oe->mail_smtpport;
  96. if ($oe->mail_smtpssl == 1) {
  97. $this->SMTPSecure = 'ssl';
  98. } // if
  99. if ($oe->mail_smtpssl == 2) {
  100. $this->SMTPSecure = 'tls';
  101. } // if
  102. if($oe->mail_smtpauth_req) {
  103. $this->SMTPAuth = TRUE;
  104. $this->Username = $oe->mail_smtpuser;
  105. $this->Password = $oe->mail_smtppass;
  106. }
  107. }
  108. else
  109. $this->Mailer = "sendmail";
  110. }
  111. /**
  112. * Prefills mailer for system
  113. */
  114. function setMailerForSystem() {
  115. require_once("include/OutboundEmail/OutboundEmail.php");
  116. $oe = new OutboundEmail();
  117. $oe = $oe->getSystemMailerSettings();
  118. // ssl or tcp - keeping outside isSMTP b/c a default may inadvertantly set ssl://
  119. $this->protocol = ($oe->mail_smtpssl) ? "ssl://" : "tcp://";
  120. if($oe->mail_sendtype == "SMTP")
  121. {
  122. //Set mail send type information
  123. $this->Mailer = "smtp";
  124. $this->Host = $oe->mail_smtpserver;
  125. $this->Port = $oe->mail_smtpport;
  126. if ($oe->mail_smtpssl == 1) {
  127. $this->SMTPSecure = 'ssl';
  128. } // if
  129. if ($oe->mail_smtpssl == 2) {
  130. $this->SMTPSecure = 'tls';
  131. } // if
  132. if($oe->mail_smtpauth_req) {
  133. $this->SMTPAuth = TRUE;
  134. $this->Username = $oe->mail_smtpuser;
  135. $this->Password = $oe->mail_smtppass;
  136. }
  137. }
  138. else
  139. $this->Mailer = "sendmail";
  140. }
  141. /**
  142. * Attaches all fs, string, and binary attachments to the message.
  143. * Returns an empty string on failure.
  144. * @access private
  145. * @return string
  146. */
  147. function AttachAll() {
  148. // Return text of body
  149. $mime = array();
  150. // Add all attachments
  151. for($i = 0; $i < count($this->attachment); $i++) {
  152. // Check for string attachment
  153. $bString = $this->attachment[$i][5];
  154. if ($bString) {
  155. $string = $this->attachment[$i][0];
  156. } else {
  157. $path = $this->attachment[$i][0];
  158. }
  159. // cn: overriding parent class' method to perform encode on the following
  160. $filename = $this->EncodeHeader(trim($this->attachment[$i][1]));
  161. $name = $this->EncodeHeader(trim($this->attachment[$i][2]));
  162. $encoding = $this->attachment[$i][3];
  163. $type = $this->attachment[$i][4];
  164. $disposition = $this->attachment[$i][6];
  165. $cid = $this->attachment[$i][7];
  166. $mime[] = sprintf("--%s%s", $this->boundary[1], $this->LE);
  167. $mime[] = sprintf("Content-Type: %s; name=\"%s\"%s", $type, $name, $this->LE);
  168. $mime[] = sprintf("Content-Transfer-Encoding: %s%s", $encoding, $this->LE);
  169. if($disposition == "inline") {
  170. $mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LE);
  171. }
  172. $mime[] = sprintf("Content-Disposition: %s; filename=\"%s\"%s", $disposition, $name, $this->LE.$this->LE);
  173. // Encode as string attachment
  174. if($bString) {
  175. $mime[] = $this->EncodeString($string, $encoding);
  176. if($this->IsError()) { return ""; }
  177. $mime[] = $this->LE.$this->LE;
  178. } else {
  179. $mime[] = $this->EncodeFile($path, $encoding);
  180. if($this->IsError()) {
  181. return "";
  182. }
  183. $mime[] = $this->LE.$this->LE;
  184. }
  185. }
  186. $mime[] = sprintf("--%s--%s", $this->boundary[1], $this->LE);
  187. return join("", $mime);
  188. }
  189. /**
  190. * handles Charset translation for all visual parts of the email.
  191. * @param string charset Default = ''
  192. */
  193. function prepForOutbound() {
  194. global $locale;
  195. if($this->preppedForOutbound == false) {
  196. //bug 28534. We should not set it to true to circumvent the following convertion as each email is independent.
  197. //$this->preppedForOutbound = true; // flag so we don't redo this
  198. $OBCharset = $locale->getPrecedentPreference('default_email_charset');
  199. // handle disclosure
  200. if($this->disclosureEnabled) {
  201. $this->Body .= "<br />&nbsp;<br />{$this->disclosureText}";
  202. $this->AltBody .= "\r\r{$this->disclosureText}";
  203. }
  204. // body text
  205. $this->Body = from_html($locale->translateCharset(trim($this->Body), 'UTF-8', $OBCharset));
  206. $this->AltBody = from_html($locale->translateCharset(trim($this->AltBody), 'UTF-8', $OBCharset));
  207. $subjectUTF8 = from_html(trim($this->Subject));
  208. $subject = $locale->translateCharset($subjectUTF8, 'UTF-8', $OBCharset);
  209. $this->Subject = $locale->translateCharset($subjectUTF8, 'UTF-8', $OBCharset);
  210. // HTML email RFC compliance
  211. if($this->ContentType == "text/html") {
  212. if(strpos($this->Body, '<html') === false) {
  213. $head=<<<eoq
  214. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  215. <html xmlns="http://www.w3.org/1999/xhtml">
  216. <head>
  217. <meta http-equiv="Content-Type" content="text/html; charset={$OBCharset}" />
  218. <title>{$subject}</title>
  219. </head>
  220. <body>
  221. eoq;
  222. $this->Body = $head.$this->Body."</body></html>";
  223. }
  224. }
  225. // Headers /////////////////////////////////
  226. // the below is done in PHPMailer::CreateHeader();
  227. //$this->Subject = $locale->translateCharsetMIME(trim($this->Subject), 'UTF-8', $locale->getPrecedentPreference('default_email_charset'));
  228. $this->FromName = $locale->translateCharset(trim($this->FromName), 'UTF-8', $OBCharset);
  229. /*
  230. foreach($this->ReplyTo as $k => $v) {
  231. $this->ReplyTo[$k][1] = $locale->translateCharset(trim($v[1]), 'UTF-8', $OBCharset);
  232. }
  233. // TO: fields
  234. foreach($this->to as $k => $toArr) {
  235. $this->to[$k][1] = $locale->translateCharset(trim($toArr[1]), 'UTF-8', $OBCharset);
  236. }
  237. // CC: fields
  238. foreach($this->cc as $k => $ccAddr) {
  239. $this->cc[$k][1] = $locale->translateCharset(trim($ccAddr[1]), 'UTF-8', $OBCharset);
  240. }
  241. // BCC: fields
  242. foreach($this->bcc as $k => $bccAddr) {
  243. $this->bcc[$k][1] = $locale->translateCharset(trim($bccAddr[1]), 'UTF-8', $OBCharset);
  244. }
  245. */
  246. }
  247. }
  248. /**
  249. * @param notes array of note beans
  250. */
  251. function handleAttachments($notes) {
  252. global $sugar_config;
  253. //replace references to cache/images with cid tag
  254. $this->Body = str_replace($GLOBALS['sugar_config']['cache_dir'].'images/','cid:',$this->Body);
  255. if (empty($notes)) {
  256. return;
  257. }
  258. // cn: bug 4864 - reusing same SugarPHPMailer class, need to clear attachments
  259. $this->ClearAttachments();
  260. require_once('include/upload_file.php');
  261. //Handle legacy attachments
  262. $fileBasePath = "{$sugar_config['upload_dir']}";
  263. $filePatternSearch = "{$sugar_config['upload_dir']}";
  264. $filePatternSearch = str_replace("/", "\/", $filePatternSearch);
  265. if(strpos($this->Body, "\"{$fileBasePath}")) {
  266. $matches = array();
  267. preg_match_all("/{$filePatternSearch}.+?\"/i", $this->Body, $matches);
  268. foreach($matches[0] as $match) {
  269. $filename = str_replace($fileBasePath, '', $match);
  270. $filename = urldecode(substr($filename, 0, -1));
  271. $cid = $filename;
  272. $file_location = clean_path(getcwd()."/{$sugar_config['upload_dir']}{$filename}");
  273. $mime_type = "image/".strtolower(substr($filename, strrpos($filename, ".")+1, strlen($filename)));
  274. if(file_exists($file_location)) {
  275. $this->AddEmbeddedImage($file_location, $cid, $filename, 'base64', $mime_type);
  276. }
  277. }
  278. //replace references to cache with cid tag
  279. $this->Body = str_replace($fileBasePath,'cid:',$this->Body);
  280. }
  281. //Handle secure embeded images.
  282. $noteImgRegex = "/<img[^>]*[\s]+src[^=]*=\"index.php\?entryPoint=download(\&amp;|\&)id=([^\&]*)[^>]*>/im";
  283. $embededImageMatches = array();
  284. preg_match_all($noteImgRegex, $this->Body, $embededImageMatches,PREG_SET_ORDER);
  285. foreach ($embededImageMatches as $singleMatch )
  286. {
  287. $fullMatch = $singleMatch[0];
  288. $noteId = $singleMatch[2];
  289. $cid = $noteId;
  290. $filename = $noteId;
  291. //Retrieve note for mimetype
  292. $tmpNote = new Note();
  293. $tmpNote->retrieve($noteId);
  294. //Replace the src part of img tag with new cid tag
  295. $cidRegex = "/src=\"([^\"]*)\"/im";
  296. $replaceMatch = preg_replace($cidRegex, "src=\"cid:$noteId\"", $fullMatch);
  297. //Replace the body, old tag for new tag
  298. $this->Body = str_replace($fullMatch, $replaceMatch, $this->Body);
  299. //Attach the file
  300. $file_location = clean_path(getcwd()."/{$sugar_config['upload_dir']}{$noteId}");
  301. if(file_exists($file_location))
  302. $this->AddEmbeddedImage($file_location, $cid, $filename, 'base64', $tmpNote->file_mime_type);
  303. }
  304. //Handle regular attachments.
  305. foreach($notes as $note) {
  306. $mime_type = 'text/plain';
  307. $file_location = '';
  308. $filename = '';
  309. if($note->object_name == 'Note') {
  310. if (! empty($note->file->temp_file_location) && is_file($note->file->temp_file_location)) {
  311. $file_location = $note->file->temp_file_location;
  312. $filename = $note->file->original_file_name;
  313. $mime_type = $note->file->mime_type;
  314. } else {
  315. $file_location = rawurldecode(UploadFile::get_file_path($note->filename,$note->id));
  316. $filename = $note->id.$note->filename;
  317. $mime_type = $note->file_mime_type;
  318. }
  319. } elseif($note->object_name == 'DocumentRevision') { // from Documents
  320. $filename = $note->id.$note->filename;
  321. $file_location = getcwd().'/'.$GLOBALS['sugar_config']['upload_dir'].$filename;
  322. $mime_type = $note->file_mime_type;
  323. }
  324. $filename = substr($filename, 36, strlen($filename)); // strip GUID for PHPMailer class to name outbound file
  325. if (!$note->embed_flag) {
  326. $this->AddAttachment($file_location, $filename, 'base64', $mime_type);
  327. } // else
  328. }
  329. }
  330. /**
  331. * overloads class.phpmailer's SetError() method so that we can log errors in sugarcrm.log
  332. *
  333. */
  334. function SetError($msg) {
  335. $GLOBALS['log']->fatal("SugarPHPMailer encountered an error: {$msg}");
  336. parent::SetError($msg);
  337. }
  338. function SmtpConnect() {
  339. $connection = parent::SmtpConnect();
  340. if (!$connection) {
  341. global $app_strings;
  342. if(isset($this->oe) && $this->oe->type == "system") {
  343. $this->SetError($app_strings['LBL_EMAIL_INVALID_SYSTEM_OUTBOUND']);
  344. } else {
  345. $this->SetError($app_strings['LBL_EMAIL_INVALID_PERSONAL_OUTBOUND']);
  346. } // else
  347. }
  348. return $connection;
  349. } // fn
  350. } // end class definition