]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/SugarPHPMailer.php
Release 6.2.0
[Github/sugarcrm.git] / include / SugarPHPMailer.php
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 /*********************************************************************************
39
40  * Description:  TODO: To be written.
41  * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc.
42  * All Rights Reserved.
43  * Contributor(s): ______________________________________..
44  ********************************************************************************/
45 require_once('include/phpmailer/class.phpmailer.php');
46 require_once('include/OutboundEmail/OutboundEmail.php');
47
48
49 class SugarPHPMailer extends PHPMailer {
50         var $oe; // OutboundEmail
51         var $protocol = "tcp://";
52         var $preppedForOutbound = false;
53         var $disclosureEnabled;
54         var $disclosureText;
55         var $isHostEmpty = false;
56         var $opensslOpened = true;
57
58         /**
59          * Sole constructor
60          */
61         function SugarPHPMailer() {
62                 global $locale;
63                 global $current_user;
64                 global $sugar_config;
65
66                 $admin = new Administration();
67                 $admin->retrieveSettings();
68
69                 if(isset($admin->settings['disclosure_enable']) && !empty($admin->settings['disclosure_enable'])) {
70                         $this->disclosureEnabled = true;
71                         $this->disclosureText = $admin->settings['disclosure_text'];
72                 }
73
74                 $this->oe = new OutboundEmail();
75                 $this->oe->getUserMailerSettings($current_user);
76
77                 $this->SetLanguage('en', 'include/phpmailer/language/');
78                 $this->PluginDir        = 'include/phpmailer/';
79                 $this->Mailer           = 'smtp';
80         // cn: i18n
81         $this->CharSet          = $locale->getPrecedentPreference('default_email_charset');
82                 $this->Encoding         = 'quoted-printable';
83         $this->IsHTML(false);  // default to plain-text email
84         $this->Hostname = $sugar_config['host_name'];
85         $this->WordWrap         = 996;
86                 // cn: gmail fix
87                 $this->protocol = ($this->oe->mail_smtpssl == 1) ? "ssl://" : $this->protocol;
88
89         }
90
91
92         /**
93          * Prefills outbound details
94          */
95         function setMailer() {
96                 global $current_user;
97
98                 require_once("include/OutboundEmail/OutboundEmail.php");
99                 $oe = new OutboundEmail();
100                 $oe = $oe->getUserMailerSettings($current_user, $mailer_id, $ieId);
101
102                 // ssl or tcp - keeping outside isSMTP b/c a default may inadvertantly set ssl://
103                 $this->protocol = ($oe->mail_smtpssl) ? "ssl://" : "tcp://";
104                 
105                 if($oe->mail_sendtype == "SMTP") 
106                 {
107                 //Set mail send type information
108                 $this->Mailer = "smtp";
109                 $this->Host = $oe->mail_smtpserver;
110                 $this->Port = $oe->mail_smtpport;
111             if ($oe->mail_smtpssl == 1) {
112                 $this->SMTPSecure = 'ssl';
113             } // if
114             if ($oe->mail_smtpssl == 2) {
115                 $this->SMTPSecure = 'tls';
116             } // if
117     
118                 if($oe->mail_smtpauth_req) {
119                         $this->SMTPAuth = TRUE;
120                         $this->Username = $oe->mail_smtpuser;
121                         $this->Password = $oe->mail_smtppass;
122                 }
123                 }
124                 else 
125                         $this->Mailer = "sendmail";
126         }
127
128         /**
129          * Prefills mailer for system
130          */
131         function setMailerForSystem() {
132                 require_once("include/OutboundEmail/OutboundEmail.php");
133                 $oe = new OutboundEmail();
134                 $oe = $oe->getSystemMailerSettings();
135
136                 // ssl or tcp - keeping outside isSMTP b/c a default may inadvertantly set ssl://
137                 $this->protocol = ($oe->mail_smtpssl) ? "ssl://" : "tcp://";
138                 
139                 if($oe->mail_sendtype == "SMTP") 
140                 {
141                 //Set mail send type information
142                 $this->Mailer = "smtp";
143                 $this->Host = $oe->mail_smtpserver;
144                 $this->Port = $oe->mail_smtpport;
145             if ($oe->mail_smtpssl == 1) {
146                 $this->SMTPSecure = 'ssl';
147             } // if
148             if ($oe->mail_smtpssl == 2) {
149                 $this->SMTPSecure = 'tls';
150             } // if
151                 if($oe->mail_smtpauth_req) {
152                         $this->SMTPAuth = TRUE;
153                         $this->Username = $oe->mail_smtpuser;
154                         $this->Password = $oe->mail_smtppass;
155                 }               
156                 }
157                 else 
158                   $this->Mailer = "sendmail";
159         }
160
161     /**
162      * Attaches all fs, string, and binary attachments to the message.
163      * Returns an empty string on failure.
164      * @access private
165      * @return string
166      */
167     function AttachAll() {
168         // Return text of body
169         $mime = array();
170
171         // Add all attachments
172         for($i = 0; $i < count($this->attachment); $i++) {
173             // Check for string attachment
174             $bString = $this->attachment[$i][5];
175             if ($bString) {
176                 $string = $this->attachment[$i][0];
177             } else {
178                                 $path = $this->attachment[$i][0];
179             }
180
181                         // cn: overriding parent class' method to perform encode on the following
182             $filename    = $this->EncodeHeader(trim($this->attachment[$i][1]));
183             $name        = $this->EncodeHeader(trim($this->attachment[$i][2]));
184             $encoding    = $this->attachment[$i][3];
185             $type        = $this->attachment[$i][4];
186             $disposition = $this->attachment[$i][6];
187             $cid         = $this->attachment[$i][7];
188
189             $mime[] = sprintf("--%s%s", $this->boundary[1], $this->LE);
190             $mime[] = sprintf("Content-Type: %s; name=\"%s\"%s", $type, $name, $this->LE);
191             $mime[] = sprintf("Content-Transfer-Encoding: %s%s", $encoding, $this->LE);
192
193             if($disposition == "inline") {
194                 $mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LE);
195             }
196
197             $mime[] = sprintf("Content-Disposition: %s; filename=\"%s\"%s", $disposition, $name, $this->LE.$this->LE);
198
199             // Encode as string attachment
200             if($bString) {
201                 $mime[] = $this->EncodeString($string, $encoding);
202                 if($this->IsError()) { return ""; }
203                 $mime[] = $this->LE.$this->LE;
204             } else {
205                 $mime[] = $this->EncodeFile($path, $encoding);
206
207                 if($this->IsError()) {
208                         return "";
209                 }
210                 $mime[] = $this->LE.$this->LE;
211             }
212         }
213         $mime[] = sprintf("--%s--%s", $this->boundary[1], $this->LE);
214
215         return join("", $mime);
216     }
217
218         /**
219          * handles Charset translation for all visual parts of the email.
220          * @param string charset Default = ''
221          */
222         function prepForOutbound() {
223                 global $locale;
224
225                 if($this->preppedForOutbound == false) {
226                         //bug 28534. We should not set it to true to circumvent the following convertion as each email is independent.
227                         //$this->preppedForOutbound = true; // flag so we don't redo this
228                         $OBCharset = $locale->getPrecedentPreference('default_email_charset');
229
230                         // handle disclosure
231                         if($this->disclosureEnabled) {
232                                 $this->Body .= "<br />&nbsp;<br />{$this->disclosureText}";
233                                 $this->AltBody .= "\r\r{$this->disclosureText}";
234                         }
235
236                         // body text
237                         $this->Body             = from_html($locale->translateCharset(trim($this->Body), 'UTF-8', $OBCharset));
238                         $this->AltBody          = from_html($locale->translateCharset(trim($this->AltBody), 'UTF-8', $OBCharset));
239             $subjectUTF8                = from_html(trim($this->Subject));
240             $subject                    = $locale->translateCharset($subjectUTF8, 'UTF-8', $OBCharset);
241             $this->Subject              = $locale->translateCharset($subjectUTF8, 'UTF-8', $OBCharset);
242
243                         // HTML email RFC compliance
244                         if($this->ContentType == "text/html") {
245                                 if(strpos($this->Body, '<html') === false) {
246                                         $head=<<<eoq
247 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
248 <html xmlns="http://www.w3.org/1999/xhtml">
249 <head>
250         <meta http-equiv="Content-Type" content="text/html; charset={$OBCharset}" />
251 <title>{$subject}</title>
252 </head>
253 <body>
254 eoq;
255                                         $this->Body = $head.$this->Body."</body></html>";
256                                 }
257                         }
258
259                         // Headers /////////////////////////////////
260                         // the below is done in PHPMailer::CreateHeader();
261                         //$this->Subject                        = $locale->translateCharsetMIME(trim($this->Subject), 'UTF-8', $locale->getPrecedentPreference('default_email_charset'));
262                         $this->FromName         = $locale->translateCharset(trim($this->FromName), 'UTF-8', $OBCharset);
263                         /*
264                         foreach($this->ReplyTo as $k => $v) {
265                                 $this->ReplyTo[$k][1] = $locale->translateCharset(trim($v[1]), 'UTF-8', $OBCharset);
266                         }
267                         // TO: fields
268                         foreach($this->to as $k => $toArr) {
269                                 $this->to[$k][1]        = $locale->translateCharset(trim($toArr[1]), 'UTF-8', $OBCharset);
270                         }
271                         // CC: fields
272                         foreach($this->cc as $k => $ccAddr) {
273                                 $this->cc[$k][1]        = $locale->translateCharset(trim($ccAddr[1]), 'UTF-8', $OBCharset);
274                         }
275                         // BCC: fields
276                         foreach($this->bcc as $k => $bccAddr) {
277                                 $this->bcc[$k][1]       = $locale->translateCharset(trim($bccAddr[1]), 'UTF-8', $OBCharset);
278                         }
279                         */
280
281                 }
282         }
283
284         /**
285          * @param notes array of note beans
286          */
287         function handleAttachments($notes) {
288                 global $sugar_config;
289
290         //replace references to cache/images with cid tag
291         $this->Body = str_replace($GLOBALS['sugar_config']['cache_dir'].'images/','cid:',$this->Body);
292
293                 if (empty($notes)) {
294                                 return;
295                 }
296                 // cn: bug 4864 - reusing same SugarPHPMailer class, need to clear attachments
297                 $this->ClearAttachments();
298                 require_once('include/upload_file.php');
299
300                 //Handle legacy attachments
301         $fileBasePath = "{$sugar_config['upload_dir']}";
302                 $filePatternSearch = "{$sugar_config['upload_dir']}";
303                 $filePatternSearch = str_replace("/", "\/", $filePatternSearch);
304                 if(strpos($this->Body, "\"{$fileBasePath}")) {
305                         $matches = array();
306                         preg_match_all("/{$filePatternSearch}.+?\"/i", $this->Body, $matches);
307                         foreach($matches[0] as $match) {
308                                 $filename = str_replace($fileBasePath, '', $match);
309                                 $filename = urldecode(substr($filename, 0, -1));
310                                 $cid = $filename;
311                                 $file_location = clean_path(getcwd()."/{$sugar_config['upload_dir']}{$filename}");
312                                 $mime_type = "image/".strtolower(substr($filename, strrpos($filename, ".")+1, strlen($filename)));
313                                 if(file_exists($file_location)) {
314                                         $this->AddEmbeddedImage($file_location, $cid, $filename, 'base64', $mime_type);
315                                 }
316                         }
317             //replace references to cache with cid tag
318             $this->Body = str_replace($fileBasePath,'cid:',$this->Body);
319                 }
320                 
321                 //Handle secure embeded images.
322                 $noteImgRegex = "/<img[^>]*[\s]+src[^=]*=\"index.php\?entryPoint=download(\&amp;|\&)id=([^\&]*)[^>]*>/im";
323         $embededImageMatches = array(); 
324         preg_match_all($noteImgRegex, $this->Body, $embededImageMatches,PREG_SET_ORDER);
325         
326         foreach ($embededImageMatches as $singleMatch )
327         {
328             $fullMatch = $singleMatch[0];
329             $noteId = $singleMatch[2];
330             $cid = $noteId;
331             $filename = $noteId;
332            
333             //Retrieve note for mimetype
334             $tmpNote = new Note();
335             $tmpNote->retrieve($noteId);
336             //Replace the src part of img tag with new cid tag
337             $cidRegex = "/src=\"([^\"]*)\"/im";
338             $replaceMatch = preg_replace($cidRegex, "src=\"cid:$noteId\"", $fullMatch);
339
340             //Replace the body, old tag for new tag
341             $this->Body = str_replace($fullMatch, $replaceMatch, $this->Body);
342             
343             //Attach the file
344             $file_location = clean_path(getcwd()."/{$sugar_config['upload_dir']}{$noteId}");
345             
346             if(file_exists($file_location)) 
347                                         $this->AddEmbeddedImage($file_location, $cid, $filename, 'base64', $tmpNote->file_mime_type);
348         }
349                 
350                 //Handle regular attachments.
351                 foreach($notes as $note) {
352                                 $mime_type = 'text/plain';
353                                 $file_location = '';
354                                 $filename = '';
355
356                                 if($note->object_name == 'Note') {
357                                         if (! empty($note->file->temp_file_location) && is_file($note->file->temp_file_location)) {
358                                                 $file_location = $note->file->temp_file_location;
359                                                 $filename = $note->file->original_file_name;
360                                                 $mime_type = $note->file->mime_type;
361                                         } else {
362                                                 $file_location = rawurldecode(UploadFile::get_file_path($note->filename,$note->id));
363                                                 $filename = $note->id.$note->filename;
364                                                 $mime_type = $note->file_mime_type;
365                                         }
366                                 } elseif($note->object_name == 'DocumentRevision') { // from Documents
367                                         $filename = $note->id.$note->filename;
368                                         $file_location = getcwd().'/'.$GLOBALS['sugar_config']['upload_dir'].$filename;
369                                         $mime_type = $note->file_mime_type;
370                                 }
371
372                                 $filename = substr($filename, 36, strlen($filename)); // strip GUID     for PHPMailer class to name outbound file
373                                 if (!$note->embed_flag) {
374                                         $this->AddAttachment($file_location, $filename, 'base64', $mime_type);
375                                 } // else
376                         }
377         }
378
379         /**
380          * overloads class.phpmailer's SetError() method so that we can log errors in sugarcrm.log
381          *
382          */
383         function SetError($msg) {
384                 $GLOBALS['log']->fatal("SugarPHPMailer encountered an error: {$msg}");
385                 parent::SetError($msg);
386         }
387         
388         function SmtpConnect() {
389                 $connection = parent::SmtpConnect();
390                 if (!$connection) {
391                         global $app_strings;
392                         if(isset($this->oe) && $this->oe->type == "system") {
393                                 $this->SetError($app_strings['LBL_EMAIL_INVALID_SYSTEM_OUTBOUND']);
394                         } else {
395                                 $this->SetError($app_strings['LBL_EMAIL_INVALID_PERSONAL_OUTBOUND']);
396                         } // else
397                 }
398                 return $connection;
399         } // fn
400         
401 } // end class definition