Passed
Branch develop (5cbde9)
by
unknown
26:38
created

CMailFile   F

Complexity

Total Complexity 254

Size/Duplication

Total Lines 1526
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 708
c 0
b 0
f 0
dl 0
loc 1526
rs 1.892
wmc 254

17 Methods

Rating   Name   Duplication   Size   Complexity  
B findHtmlImages() 0 86 10
A server_parse() 0 22 4
D write_smtpheaders() 0 55 14
B check_server_port() 0 54 11
A _encode_file() 0 16 2
A encodetorfc2822() 0 4 1
F __construct() 0 346 57
A write_mimeheaders() 0 21 5
A dump_mail() 0 28 6
A checkIfHTML() 0 18 4
A write_images() 0 23 3
B write_files() 0 37 6
F getValidAddress() 0 63 22
A buildCSS() 0 20 4
A getArrayAddress() 0 26 4
D write_body() 0 93 16
F sendfile() 0 368 85

How to fix   Complexity   

Complex Class

Complex classes like CMailFile often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use CMailFile, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Copyright (C)            Dan Potter
4
 * Copyright (C)            Eric Seigne
5
 * Copyright (C) 2000-2005  Rodolphe Quiedeville    <[email protected]>
6
 * Copyright (C) 2003       Jean-Louis Bergamo      <[email protected]>
7
 * Copyright (C) 2004-2015  Laurent Destailleur     <[email protected]>
8
 * Copyright (C) 2005-2012  Regis Houssin           <[email protected]>
9
 * Copyright (C) 2019       Frédéric France         <[email protected]>
10
 *
11
 * This program is free software; you can redistribute it and/or modify
12
 * it under the terms of the GNU General Public License as published by
13
 * the Free Software Foundation; either version 3 of the License, or
14
 * (at your option) any later version.
15
 *
16
 * This program is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
 * GNU General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU General Public License
22
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
23
 * or see http://www.gnu.org/
24
 *
25
 * Lots of code inspired from Dan Potter's CMailFile class
26
 */
27
28
/**
29
 *      \file       htdocs/core/class/CMailFile.class.php
30
 *      \brief      File of class to send emails (with attachments or not)
31
 */
32
33
/**
34
 *	Class to send emails (with attachments or not)
35
 *  Usage: $mailfile = new CMailFile($subject,$sendto,$replyto,$message,$filepath,$mimetype,$filename,$cc,$ccc,$deliveryreceipt,$msgishtml,$errors_to,$css,$trackid,$moreinheader,$sendcontext);
36
 *         $mailfile->sendfile();
37
 */
38
class CMailFile
39
{
40
	public $sendcontext;
41
	public $sendmode;
42
	public $sendsetup;
43
44
    public $subject;      	// Topic:       Subject of email
45
    public $addr_from;    	// From:		Label and EMail of sender (must include '<>'). For example '<[email protected]>' or 'John Doe <[email protected]>' or '<[email protected]>'). Note that with gmail smtps, value here is forced by google to account (but not the reply-to).
46
	// Sender:      Who send the email ("Sender" has sent emails on behalf of "From").
47
	//              Use it when the "From" is an email of a domain that is a SPF protected domain, and sending smtp server is not this domain. In such case, add Sender field with an email of the protected domain.
48
	// Return-Path: Email where to send bounds.
49
    public $reply_to;		// Reply-To:	Email where to send replies from mailer software (mailer use From if reply-to not defined, Gmail use gmail account if reply-to not defined)
50
    public $errors_to;		// Errors-To:	Email where to send errors.
51
    public $addr_to;
52
    public $addr_cc;
53
    public $addr_bcc;
54
    public $trackid;
55
56
    public $mixed_boundary;
57
    public $related_boundary;
58
    public $alternative_boundary;
59
    public $deliveryreceipt;
60
61
    public $eol;
62
    public $eol2;
63
64
	/**
65
	 * @var string Error code (or message)
66
	 */
67
	public $error='';
68
69
    public $smtps;			// Contains SMTPs object (if this method is used)
70
    public $phpmailer;		// Contains PHPMailer object (if this method is used)
71
72
	/**
73
	 * @var string CSS
74
	 */
75
	public $css;
76
	//! Defined css style for body background
77
    public $styleCSS;
78
	//! Defined background directly in body tag
79
    public $bodyCSS;
80
81
    public $headers;
82
    public $message;
83
84
	// Image
85
    public $html;
86
    public $image_boundary;
87
    public $atleastoneimage=0;    // at least one image file with file=xxx.ext into content (TODO Debug this. How can this case be tested. Remove if not used).
88
    public $html_images=array();
89
    public $images_encoded=array();
90
    public $image_types = array(
91
        'gif'  => 'image/gif',
92
		'jpg'  => 'image/jpeg',
93
		'jpeg' => 'image/jpeg',
94
		'jpe'  => 'image/jpeg',
95
		'bmp'  => 'image/bmp',
96
		'png'  => 'image/png',
97
		'tif'  => 'image/tiff',
98
        'tiff' => 'image/tiff',
99
    );
100
101
102
	/**
103
	 *	CMailFile
104
	 *
105
	 *	@param 	string	$subject             Topic/Subject of mail
106
	 *	@param 	string	$to                  Recipients emails (RFC 2822: "Name firstname <email>[, ...]" or "email[, ...]" or "<email>[, ...]"). Note: the keyword '__SUPERVISOREMAIL__' is not allowed here and must be replaced by caller.
107
	 *	@param 	string	$from                Sender email      (RFC 2822: "Name firstname <email>[, ...]" or "email[, ...]" or "<email>[, ...]")
108
	 *	@param 	string	$msg                 Message
109
	 *	@param 	array	$filename_list       List of files to attach (full path of filename on file system)
110
	 *	@param 	array	$mimetype_list       List of MIME type of attached files
111
	 *	@param 	array	$mimefilename_list   List of attached file name in message
112
	 *	@param 	string	$addr_cc             Email cc
113
	 *	@param 	string	$addr_bcc            Email bcc (Note: This is autocompleted with MAIN_MAIL_AUTOCOPY_TO if defined)
114
	 *	@param 	int		$deliveryreceipt     Ask a delivery receipt
115
	 *	@param 	int		$msgishtml           1=String IS already html, 0=String IS NOT html, -1=Unknown make autodetection (with fast mode, not reliable)
116
	 *	@param 	string	$errors_to      	 Email for errors-to
117
	 *	@param	string	$css                 Css option
118
	 *	@param	string	$trackid             Tracking string (contains type and id of related element)
119
	 *  @param  string  $moreinheader        More in header. $moreinheader must contains the "\r\n" (TODO not supported for other MAIL_SEND_MODE different than 'phpmail' and 'smtps' for the moment)
120
	 *  @param  string  $sendcontext      	 'standard', 'emailing', ... (used to define with sending mode and parameters to use)
121
	 *  @param	string	$replyto			 Reply-to email (will be set to same value than From by default if not provided)
122
	 */
123
	public function __construct($subject, $to, $from, $msg, $filename_list = array(), $mimetype_list = array(), $mimefilename_list = array(), $addr_cc = "", $addr_bcc = "", $deliveryreceipt = 0, $msgishtml = 0, $errors_to = '', $css = '', $trackid = '', $moreinheader = '', $sendcontext = 'standard', $replyto = '')
124
	{
125
		global $conf, $dolibarr_main_data_root;
126
127
		$this->sendcontext = $sendcontext;
128
129
		if (empty($replyto)) $replyto=$from;
130
131
		// Define this->sendmode
132
		$this->sendmode = '';
133
		if ($this->sendcontext == 'emailing' && !empty($conf->global->MAIN_MAIL_SENDMODE_EMAILING) && $conf->global->MAIN_MAIL_SENDMODE_EMAILING != 'default')
134
		{
135
			$this->sendmode = $conf->global->MAIN_MAIL_SENDMODE_EMAILING;
136
		}
137
		if (empty($this->sendmode)) $this->sendmode=$conf->global->MAIN_MAIL_SENDMODE;
138
		if (empty($this->sendmode)) $this->sendmode='mail';
139
140
		// We define end of line (RFC 821).
141
		$this->eol="\r\n";
142
		// We define end of line for header fields (RFC 822bis section 2.3 says header must contains \r\n).
143
		$this->eol2="\r\n";
144
		if (! empty($conf->global->MAIN_FIX_FOR_BUGGED_MTA))
145
		{
146
			$this->eol="\n";
147
			$this->eol2="\n";
148
			$moreinheader = str_replace("\r\n", "\n", $moreinheader);
149
		}
150
151
		// On defini mixed_boundary
152
		$this->mixed_boundary = "multipart_x." . time() . ".x_boundary";
153
154
		// On defini related_boundary
155
		$this->related_boundary = 'mul_'.dol_hash(uniqid("dolibarr2"), 3);	// Force md5 hash (does not contains special chars)
156
157
		// On defini alternative_boundary
158
		$this->alternative_boundary = 'mul_'.dol_hash(uniqid("dolibarr3"), 3);	// Force md5 hash (does not contains special chars)
159
160
		dol_syslog("CMailFile::CMailfile: sendmode=".$this->sendmode." charset=".$conf->file->character_set_client." from=$from, to=$to, addr_cc=$addr_cc, addr_bcc=$addr_bcc, errors_to=$errors_to, trackid=$trackid sendcontext=$sendcontext", LOG_DEBUG);
161
		dol_syslog("CMailFile::CMailfile: subject=".$subject.", deliveryreceipt=".$deliveryreceipt.", msgishtml=".$msgishtml, LOG_DEBUG);
162
163
		if (empty($subject))
164
		{
165
			dol_syslog("CMailFile::CMailfile: Try to send an email with empty subject");
166
			$this->error='ErrorSubjectIsRequired';
167
			return;
168
		}
169
		if (empty($msg))
170
		{
171
		    dol_syslog("CMailFile::CMailfile: Try to send an email with empty body");
172
		    $msg='.';     // Avoid empty message (with empty message conten show a multipart structure)
173
		}
174
175
		// Detect if message is HTML (use fast method)
176
		if ($msgishtml == -1)
177
		{
178
			$this->msgishtml = 0;
179
			if (dol_textishtml($msg)) $this->msgishtml = 1;
180
		}
181
		else
182
		{
183
			$this->msgishtml = $msgishtml;
184
		}
185
186
		global $dolibarr_main_url_root;
187
188
		// Define $urlwithroot
189
		$urlwithouturlroot=preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
190
		$urlwithroot=$urlwithouturlroot.DOL_URL_ROOT;		// This is to use external domain name found into config file
191
		//$urlwithroot=DOL_MAIN_URL_ROOT;					// This is to use same domain name than current
192
193
		// Replace relative /viewimage to absolute path
194
		$msg = preg_replace('/src="'.preg_quote(DOL_URL_ROOT, '/').'\/viewimage\.php/ims', 'src="'.$urlwithroot.'/viewimage.php', $msg, -1, $nbrep);
195
196
		if (! empty($conf->global->MAIN_MAIL_FORCE_CONTENT_TYPE_TO_HTML)) $this->msgishtml=1; // To force to send everything with content type html.
197
198
		// Detect images
199
		if ($this->msgishtml)
200
		{
201
			$this->html = $msg;
202
203
			if (! empty($conf->global->MAIN_MAIL_ADD_INLINE_IMAGES_IF_IN_MEDIAS))
204
			{
205
				$findimg = $this->findHtmlImages($dolibarr_main_data_root.'/medias');
206
			}
207
208
			// Define if there is at least one file
209
			if ($findimg)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $findimg does not seem to be defined for all execution paths leading up to this point.
Loading history...
210
			{
211
				foreach ($this->html_images as $i => $val)
212
				{
213
					if ($this->html_images[$i])
214
					{
215
						$this->atleastoneimage=1;
216
						dol_syslog("CMailFile::CMailfile: html_images[$i]['name']=".$this->html_images[$i]['name'], LOG_DEBUG);
217
					}
218
				}
219
			}
220
		}
221
222
		// Define if there is at least one file
223
		if (is_array($filename_list))
1 ignored issue
show
introduced by
The condition is_array($filename_list) is always true.
Loading history...
224
		{
225
			foreach ($filename_list as $i => $val)
226
			{
227
				if ($filename_list[$i])
228
				{
229
					$this->atleastonefile=1;
230
					dol_syslog("CMailFile::CMailfile: filename_list[$i]=".$filename_list[$i].", mimetype_list[$i]=".$mimetype_list[$i]." mimefilename_list[$i]=".$mimefilename_list[$i], LOG_DEBUG);
231
				}
232
			}
233
		}
234
235
		// Add autocopy to (Note: Adding bcc for specific modules are also done from pages)
236
		if (! empty($conf->global->MAIN_MAIL_AUTOCOPY_TO)) $addr_bcc.=($addr_bcc?', ':'').$conf->global->MAIN_MAIL_AUTOCOPY_TO;
237
238
		// Action according to choosed sending method
239
		if ($this->sendmode == 'mail')
240
		{
241
			// Use mail php function (default PHP method)
242
			// ------------------------------------------
243
244
			$smtp_headers = "";
245
			$mime_headers = "";
246
			$text_body = "";
247
			$files_encoded = "";
248
249
			// Define smtp_headers
250
			$this->subject = $subject;
251
			$this->addr_from = $from;
252
			$this->reply_to = $replyto;
253
			$this->errors_to = $errors_to;
254
			$this->addr_to = $to;
255
			$this->addr_cc = $addr_cc;
256
			$this->addr_bcc = $addr_bcc;
257
			$this->deliveryreceipt = $deliveryreceipt;
258
			$this->trackid = $trackid;
259
260
			$smtp_headers = $this->write_smtpheaders();
261
			if (! empty($moreinheader)) $smtp_headers.=$moreinheader;   // $moreinheader contains the \r\n
262
263
			// Define mime_headers
264
			$mime_headers = $this->write_mimeheaders($filename_list, $mimefilename_list);
265
266
			if (! empty($this->html))
267
			{
268
				if (!empty($css))
269
				{
270
					$this->css = $css;
271
					$this->buildCSS();    // Build a css style (mode = all) into this->styleCSS and this->bodyCSS
272
				}
273
274
				$msg = $this->html;
275
			}
276
277
			// Define body in text_body
278
			$text_body = $this->write_body($msg);
279
280
			// Add attachments to text_encoded
281
			if ($this->atleastonefile)
282
			{
283
				$files_encoded = $this->write_files($filename_list, $mimetype_list, $mimefilename_list);
284
			}
285
286
			// We now define $this->headers and $this->message
287
			$this->headers = $smtp_headers . $mime_headers;
288
			// On nettoie le header pour qu'il ne se termine pas par un retour chariot.
289
			// This avoid also empty lines at end that can be interpreted as mail injection by email servers.
290
			$this->headers = preg_replace("/([\r\n]+)$/i", "", $this->headers);
291
292
			//$this->message = $this->eol.'This is a message with multiple parts in MIME format.'.$this->eol;
293
			$this->message = 'This is a message with multiple parts in MIME format.'.$this->eol;
294
			$this->message.= $text_body . $files_encoded;
295
			$this->message.= "--" . $this->mixed_boundary . "--" . $this->eol;
296
		}
297
		elseif ($this->sendmode == 'smtps')
298
		{
299
			// Use SMTPS library
300
			// ------------------------------------------
301
302
			require_once DOL_DOCUMENT_ROOT.'/core/class/smtps.class.php';
303
			$smtps = new SMTPs();
304
			$smtps->setCharSet($conf->file->character_set_client);
305
306
			$smtps->setSubject($this->encodetorfc2822($subject));
307
			$smtps->setTO($this->getValidAddress($to, 0, 1));
308
			$smtps->setFrom($this->getValidAddress($from, 0, 1));
309
			$smtps->setTrackId($trackid);
310
			$smtps->setReplyTo($this->getValidAddress($replyto, 0, 1));
311
312
			if (! empty($moreinheader)) $smtps->setMoreInHeader($moreinheader);
313
314
			if (! empty($this->html))
315
			{
316
				if (!empty($css))
317
				{
318
					$this->css = $css;
319
					$this->buildCSS();
320
				}
321
				$msg = $this->html;
322
				$msg = $this->checkIfHTML($msg);
323
			}
324
325
			if ($this->msgishtml) $smtps->setBodyContent($msg, 'html');
326
			else $smtps->setBodyContent($msg, 'plain');
327
328
			if ($this->atleastoneimage)
329
			{
330
				foreach ($this->images_encoded as $img)
331
				{
332
					$smtps->setImageInline($img['image_encoded'], $img['name'], $img['content_type'], $img['cid']);
333
				}
334
			}
335
336
			if ($this->atleastonefile)
337
			{
338
				foreach ($filename_list as $i => $val)
339
				{
340
					$content=file_get_contents($filename_list[$i]);
341
					$smtps->setAttachment($content, $mimefilename_list[$i], $mimetype_list[$i]);
342
				}
343
			}
344
345
			$smtps->setCC($addr_cc);
346
			$smtps->setBCC($addr_bcc);
347
			$smtps->setErrorsTo($errors_to);
348
			$smtps->setDeliveryReceipt($deliveryreceipt);
349
350
			$this->smtps=$smtps;
351
		}
352
		elseif ($this->sendmode == 'swiftmailer')
353
		{
354
			// Use Swift Mailer library
355
            $host = dol_getprefix('email');
356
357
            require_once DOL_DOCUMENT_ROOT.'/includes/swiftmailer/lexer/lib/Doctrine/Common/Lexer/AbstractLexer.php';
358
359
            // egulias autoloader lib
360
            require_once DOL_DOCUMENT_ROOT.'/includes/swiftmailer/autoload.php';
361
362
            require_once DOL_DOCUMENT_ROOT.'/includes/swiftmailer/lib/swift_required.php';
363
364
            // Create the message
365
            //$this->message = Swift_Message::newInstance();
366
            $this->message = new Swift_Message();
367
            //$this->message = new Swift_SignedMessage();
368
            // Adding a trackid header to a message
369
            $headers = $this->message->getHeaders();
370
            $headers->addTextHeader('X-Dolibarr-TRACKID', $trackid . '@' . $host);
371
            $headerID = time() . '.swiftmailer-dolibarr-' . $trackid . '@' . $host;
372
            $msgid = $headers->get('Message-ID');
373
            $msgid->setId($headerID);
0 ignored issues
show
Bug introduced by
The method setId() does not exist on Swift_Mime_Header. It seems like you code against a sub-type of Swift_Mime_Header such as Swift_Mime_Headers_IdentificationHeader. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

373
            $msgid->/** @scrutinizer ignore-call */ 
374
                    setId($headerID);
Loading history...
374
            $headers->addIdHeader('References', $headerID);
375
            // TODO if (! empty($moreinheader)) ...
376
377
            // Give the message a subject
378
            try {
379
                $result = $this->message->setSubject($subject);
380
            } catch (Exception $e) {
381
                $this->errors[] =  $e->getMessage();
382
            }
383
384
            // Set the From address with an associative array
385
            //$this->message->setFrom(array('[email protected]' => 'John Doe'));
386
            if (! empty($from)) {
387
                try {
388
                    $result = $this->message->setFrom($this->getArrayAddress($from));
389
                } catch (Exception $e) {
390
                    $this->errors[] = $e->getMessage();
391
                }
392
            }
393
394
            // Set the To addresses with an associative array
395
            if (! empty($to)) {
396
                try {
397
                    $result = $this->message->setTo($this->getArrayAddress($to));
398
                } catch (Exception $e) {
399
                    $this->errors[] = $e->getMessage();
400
                }
401
            }
402
403
            if (! empty($replyto)) {
404
                try {
405
                	$result = $this->message->SetReplyTo($this->getArrayAddress($replyto));
406
                } catch (Exception $e) {
407
                    $this->errors[] = $e->getMessage();
408
                }
409
            }
410
411
            try {
412
                $result = $this->message->setCharSet($conf->file->character_set_client);
413
            } catch (Exception $e) {
414
                $this->errors[] =  $e->getMessage();
415
            }
416
417
            if (! empty($this->html)) {
418
				if (!empty($css)) {
419
					$this->css = $css;
420
					$this->buildCSS();
421
				}
422
				$msg = $this->html;
423
				$msg = $this->checkIfHTML($msg);
424
			}
425
426
			if ($this->atleastoneimage)
427
			{
428
				foreach ($this->images_encoded as $img)
429
				{
430
					//$img['fullpath'],$img['image_encoded'],$img['name'],$img['content_type'],$img['cid']
431
					$attachment = Swift_Image::fromPath($img['fullpath'], $img['content_type']);
0 ignored issues
show
Unused Code introduced by
The call to Swift_Image::fromPath() has too many arguments starting with $img['content_type']. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

431
					/** @scrutinizer ignore-call */ 
432
     $attachment = Swift_Image::fromPath($img['fullpath'], $img['content_type']);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
432
					// embed image
433
					$imgcid = $this->message->embed($attachment);
434
					// replace cid by the one created by swiftmail in html message
435
					$msg = str_replace("cid:".$img['cid'], $imgcid, $msg);
436
				}
437
			}
438
439
			if ($this->msgishtml) {
440
				$this->message->setBody($msg, 'text/html');
441
				// And optionally an alternative body
442
				$this->message->addPart(html_entity_decode(strip_tags($msg)), 'text/plain');
443
			} else {
444
				$this->message->setBody($msg, 'text/plain');
445
				// And optionally an alternative body
446
				$this->message->addPart($msg, 'text/html');
447
			}
448
449
			if ($this->atleastonefile)
450
			{
451
				foreach ($filename_list as $i => $val)
452
				{
453
					//$this->message->attach(Swift_Attachment::fromPath($filename_list[$i],$mimetype_list[$i]));
454
					$attachment = Swift_Attachment::fromPath($filename_list[$i], $mimetype_list[$i]);
455
					$this->message->attach($attachment);
456
				}
457
			}
458
459
			if (! empty($addr_cc)) $this->message->setCc($this->getArrayAddress($addr_cc));
460
			if (! empty($addr_bcc)) $this->message->setBcc($this->getArrayAddress($addr_bcc));
461
			//if (! empty($errors_to)) $this->message->setErrorsTo($this->getArrayAddress($errors_to);
462
			if (isset($deliveryreceipt) && $deliveryreceipt == 1) $this->message->setReadReceiptTo($this->getArrayAddress($from));
463
		}
464
		else
465
		{
466
			// Send mail method not correctly defined
467
			// --------------------------------------
468
			$this->error = 'Bad value for sendmode';
469
		}
470
	}
471
472
473
	/**
474
	 * Send mail that was prepared by constructor.
475
	 *
476
	 * @return    boolean     True if mail sent, false otherwise
477
	 */
478
	public function sendfile()
479
	{
480
		global $conf,$db,$langs;
481
482
		$errorlevel=error_reporting();
483
		//error_reporting($errorlevel ^ E_WARNING);   // Desactive warnings
484
485
		$res=false;
486
487
		if (empty($conf->global->MAIN_DISABLE_ALL_MAILS) || !empty($conf->global->MAIN_MAIL_FORCE_SENDTO))
488
		{
489
			require_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php';
490
			$hookmanager = new HookManager($db);
491
			$hookmanager->initHooks(array('mail'));
492
493
			$parameters=array(); $action='';
494
			$reshook = $hookmanager->executeHooks('sendMail', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
495
			if ($reshook < 0)
496
			{
497
				$this->error = "Error in hook maildao sendMail " . $reshook;
498
				dol_syslog("CMailFile::sendfile: mail end error=" . $this->error, LOG_ERR);
499
500
				return $reshook;
501
			}
502
			if ($reshook == 1)	// Hook replace standard code
503
			{
504
				return true;
505
			}
506
507
			$sendingmode = $this->sendmode;
508
			if ($this->context == 'emailing' && ! empty($conf->global->MAILING_NO_USING_PHPMAIL) && $sendingmode == 'mail')
509
			{
510
			    // List of sending methods
511
			    $listofmethods=array();
512
			    $listofmethods['mail']='PHP mail function';
513
			    //$listofmethods['simplemail']='Simplemail class';
514
			    $listofmethods['smtps']='SMTP/SMTPS socket library';
515
516
			    // EMailing feature may be a spam problem, so when you host several users/instance, having this option may force each user to use their own SMTP agent.
517
			    // You ensure that every user is using its own SMTP server when using the mass emailing module.
518
			    $linktoadminemailbefore='<a href="'.DOL_URL_ROOT.'/admin/mails.php">';
519
			    $linktoadminemailend='</a>';
520
			    $this->error = $langs->trans("MailSendSetupIs", $listofmethods[$sendingmode]);
521
			    $this->errors[] = $langs->trans("MailSendSetupIs", $listofmethods[$sendingmode]);
522
			    $this->error .= '<br>'.$langs->trans("MailSendSetupIs2", $linktoadminemailbefore, $linktoadminemailend, $langs->transnoentitiesnoconv("MAIN_MAIL_SENDMODE"), $listofmethods['smtps']);
523
			    $this->errors[] = $langs->trans("MailSendSetupIs2", $linktoadminemailbefore, $linktoadminemailend, $langs->transnoentitiesnoconv("MAIN_MAIL_SENDMODE"), $listofmethods['smtps']);
524
			    if (! empty($conf->global->MAILING_SMTP_SETUP_EMAILS_FOR_QUESTIONS))
525
			    {
526
			        $this->error .= '<br>'.$langs->trans("MailSendSetupIs3", $conf->global->MAILING_SMTP_SETUP_EMAILS_FOR_QUESTIONS);
527
			        $this->errors[] = $langs->trans("MailSendSetupIs3", $conf->global->MAILING_SMTP_SETUP_EMAILS_FOR_QUESTIONS);
528
			    }
529
                return false;
530
			}
531
532
			// Check number of recipient is lower or equal than MAIL_MAX_NB_OF_RECIPIENTS_IN_SAME_EMAIL
533
			if (empty($conf->global->MAIL_MAX_NB_OF_RECIPIENTS_TO_IN_SAME_EMAIL)) $conf->global->MAIL_MAX_NB_OF_RECIPIENTS_TO_IN_SAME_EMAIL=10;
534
			$tmparray1 = explode(',', $this->addr_to);
535
			if (count($tmparray1) > $conf->global->MAIL_MAX_NB_OF_RECIPIENTS_TO_IN_SAME_EMAIL)
536
			{
537
				$this->error = 'Too much recipients in to:';
538
				dol_syslog("CMailFile::sendfile: mail end error=" . $this->error, LOG_WARNING);
539
				return false;
540
			}
541
			if (empty($conf->global->MAIL_MAX_NB_OF_RECIPIENTS_CC_IN_SAME_EMAIL)) $conf->global->MAIL_MAX_NB_OF_RECIPIENTS_CC_IN_SAME_EMAIL=10;
542
			$tmparray2 = explode(',', $this->addr_cc);
543
			if (count($tmparray2) > $conf->global->MAIL_MAX_NB_OF_RECIPIENTS_CC_IN_SAME_EMAIL)
544
			{
545
				$this->error = 'Too much recipients in cc:';
546
				dol_syslog("CMailFile::sendfile: mail end error=" . $this->error, LOG_WARNING);
547
				return false;
548
			}
549
			if (empty($conf->global->MAIL_MAX_NB_OF_RECIPIENTS_BCC_IN_SAME_EMAIL)) $conf->global->MAIL_MAX_NB_OF_RECIPIENTS_BCC_IN_SAME_EMAIL=10;
550
			$tmparray3 = explode(',', $this->addr_bcc);
551
			if (count($tmparray3) > $conf->global->MAIL_MAX_NB_OF_RECIPIENTS_BCC_IN_SAME_EMAIL)
552
			{
553
				$this->error = 'Too much recipients in bcc:';
554
				dol_syslog("CMailFile::sendfile: mail end error=" . $this->error, LOG_WARNING);
555
				return false;
556
			}
557
			if (empty($conf->global->MAIL_MAX_NB_OF_RECIPIENTS_IN_SAME_EMAIL)) $conf->global->MAIL_MAX_NB_OF_RECIPIENTS_IN_SAME_EMAIL=10;
558
			if ((count($tmparray1)+count($tmparray2)+count($tmparray3)) > $conf->global->MAIL_MAX_NB_OF_RECIPIENTS_IN_SAME_EMAIL)
559
			{
560
				$this->error = 'Too much recipients in to:, cc:, bcc:';
561
				dol_syslog("CMailFile::sendfile: mail end error=" . $this->error, LOG_WARNING);
562
				return false;
563
			}
564
565
			$keyforsmtpserver='MAIN_MAIL_SMTP_SERVER';
566
			$keyforsmtpport  ='MAIN_MAIL_SMTP_PORT';
567
			$keyforsmtpid    ='MAIN_MAIL_SMTPS_ID';
568
			$keyforsmtppw    ='MAIN_MAIL_SMTPS_PW';
569
			$keyfortls       ='MAIN_MAIL_EMAIL_TLS';
570
			$keyforstarttls  ='MAIN_MAIL_EMAIL_STARTTLS';
571
			if ($this->sendcontext == 'emailing' && !empty($conf->global->MAIN_MAIL_SENDMODE_EMAILING) && $conf->global->MAIN_MAIL_SENDMODE_EMAILING != 'default')
572
			{
573
				$keyforsmtpserver='MAIN_MAIL_SMTP_SERVER_EMAILING';
574
				$keyforsmtpport  ='MAIN_MAIL_SMTP_PORT_EMAILING';
575
				$keyforsmtpid    ='MAIN_MAIL_SMTPS_ID_EMAILING';
576
				$keyforsmtppw    ='MAIN_MAIL_SMTPS_PW_EMAILING';
577
				$keyfortls       ='MAIN_MAIL_EMAIL_TLS_EMAILING';
578
				$keyforstarttls  ='MAIN_MAIL_EMAIL_STARTTLS_EMAILING';
579
			}
580
581
			if (!empty($conf->global->MAIN_MAIL_FORCE_SENDTO))
582
			{
583
				$this->addr_to = $conf->global->MAIN_MAIL_FORCE_SENDTO;
584
				$this->addr_cc = '';
585
				$this->addr_bcc = '';
586
			}
587
588
			// Action according to choosed sending method
589
			if ($this->sendmode == 'mail')
590
			{
591
				// Use mail php function (default PHP method)
592
				// ------------------------------------------
593
				dol_syslog("CMailFile::sendfile addr_to=".$this->addr_to.", subject=".$this->subject, LOG_DEBUG);
594
				dol_syslog("CMailFile::sendfile header=\n".$this->headers, LOG_DEBUG);
595
				//dol_syslog("CMailFile::sendfile message=\n".$message);
596
597
				// If Windows, sendmail_from must be defined
598
				if (isset($_SERVER["WINDIR"]))
599
				{
600
					if (empty($this->addr_from)) $this->addr_from = '[email protected]';
601
					@ini_set('sendmail_from', $this->getValidAddress($this->addr_from, 2));
1 ignored issue
show
Security Best Practice introduced by
It seems like you do not handle an error condition for ini_set(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

601
					/** @scrutinizer ignore-unhandled */ @ini_set('sendmail_from', $this->getValidAddress($this->addr_from, 2));

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
602
				}
603
604
				// Force parameters
605
				if (! empty($conf->global->$keyforsmtpserver)) ini_set('SMTP', $conf->global->$keyforsmtpserver);
606
				if (! empty($conf->global->$keyforsmtpport))   ini_set('smtp_port', $conf->global->$keyforsmtpport);
607
608
				$res=true;
609
				if ($res && ! $this->subject)
610
				{
611
					$this->error="Failed to send mail with php mail to HOST=".ini_get('SMTP').", PORT=".ini_get('smtp_port')."<br>Subject is empty";
612
					dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_ERR);
613
					$res=false;
614
				}
615
				$dest=$this->getValidAddress($this->addr_to, 2);
616
				if ($res && ! $dest)
617
				{
618
					$this->error="Failed to send mail with php mail to HOST=".ini_get('SMTP').", PORT=".ini_get('smtp_port')."<br>Recipient address '$dest' invalid";
619
					dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_ERR);
620
					$res=false;
621
				}
622
623
				if ($res)
624
				{
625
					$additionnalparam = '';	// By default
626
					if (! empty($conf->global->MAIN_MAIL_ALLOW_SENDMAIL_F))
627
					{
628
						// le "Return-Path" (retour des messages bounced) dans les header ne fonctionne pas avec tous les MTA
629
						// Le forcage de la valeur grace à l'option -f de sendmail est donc possible si la constante MAIN_MAIL_ALLOW_SENDMAIL_F est definie.
630
						// Having this variable defined may create problems with some sendmail (option -f refused)
631
						// Having this variable not defined may create problems with some other sendmail (option -f required)
632
						$additionnalparam .= ($additionnalparam?' ':'').(! empty($conf->global->MAIN_MAIL_ERRORS_TO) ? '-f' . $this->getValidAddress($conf->global->MAIN_MAIL_ERRORS_TO, 2) : ($this->addr_from != '' ? '-f' . $this->getValidAddress($this->addr_from, 2) : '') );
633
					}
634
					if (! empty($conf->global->MAIN_MAIL_SENDMAIL_FORCE_BA))    // To force usage of -ba option. This option tells sendmail to read From: or Sender: to setup sender
635
					{
636
						$additionnalparam .= ($additionnalparam?' ':'').'-ba';
637
					}
638
639
					if (! empty($conf->global->MAIN_MAIL_SENDMAIL_FORCE_ADDPARAM)) $additionnalparam .= ($additionnalparam?' ':'').'-U '.$additionnalparam; // Use -U to add additionnal params
640
641
					dol_syslog("CMailFile::sendfile: mail start HOST=".ini_get('SMTP').", PORT=".ini_get('smtp_port').", additionnal_parameters=".$additionnalparam, LOG_DEBUG);
642
643
					$this->message=stripslashes($this->message);
644
645
					if (! empty($conf->global->MAIN_MAIL_DEBUG)) $this->dump_mail();
646
647
					if (! empty($additionnalparam)) $res = mail($dest, $this->encodetorfc2822($this->subject), $this->message, $this->headers, $additionnalparam);
648
					else $res = mail($dest, $this->encodetorfc2822($this->subject), $this->message, $this->headers);
649
650
					if (! $res)
651
					{
652
						$langs->load("errors");
653
						$this->error="Failed to send mail with php mail";
654
						$linuxlike=1;
655
						if (preg_match('/^win/i', PHP_OS)) $linuxlike=0;
656
						if (preg_match('/^mac/i', PHP_OS)) $linuxlike=0;
657
						if (! $linuxlike)
658
						{
659
							$this->error.=" to HOST=".ini_get('SMTP').", PORT=".ini_get('smtp_port');	// This values are value used only for non linuxlike systems
660
						}
661
						$this->error.=".<br>";
662
						$this->error.=$langs->trans("ErrorPhpMailDelivery");
663
						dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_ERR);
664
					}
665
					else
666
					{
667
						dol_syslog("CMailFile::sendfile: mail end success", LOG_DEBUG);
668
					}
669
				}
670
671
				if (isset($_SERVER["WINDIR"]))
672
				{
673
					@ini_restore('sendmail_from');
1 ignored issue
show
Bug introduced by
Are you sure the usage of ini_restore('sendmail_from') is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Security Best Practice introduced by
It seems like you do not handle an error condition for ini_restore(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

673
					/** @scrutinizer ignore-unhandled */ @ini_restore('sendmail_from');

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
674
				}
675
676
				// Restore parameters
677
				if (! empty($conf->global->$keyforsmtpserver))	ini_restore('SMTP');
678
				if (! empty($conf->global->$keyforsmtpport)) 	ini_restore('smtp_port');
679
			}
680
			elseif ($this->sendmode == 'smtps')
681
			{
682
				if (! is_object($this->smtps))
683
				{
684
					$this->error="Failed to send mail with smtps lib to HOST=".$server.", PORT=".$conf->global->$keyforsmtpport."<br>Constructor of object CMailFile was not initialized without errors.";
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $server seems to be never defined.
Loading history...
685
					dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_ERR);
686
					return false;
687
				}
688
689
				// Use SMTPS library
690
				// ------------------------------------------
691
				$this->smtps->setTransportType(0);	// Only this method is coded in SMTPs library
692
693
				// Clean parameters
694
				if (empty($conf->global->$keyforsmtpserver)) $conf->global->$keyforsmtpserver=ini_get('SMTP');
695
				if (empty($conf->global->$keyforsmtpport))   $conf->global->$keyforsmtpport=ini_get('smtp_port');
696
697
				// If we use SSL/TLS
698
				$server=$conf->global->$keyforsmtpserver;
699
				$secure='';
700
				if (! empty($conf->global->$keyfortls) && function_exists('openssl_open')) $secure='ssl';
701
				if (! empty($conf->global->$keyforstarttls) && function_exists('openssl_open')) $secure='tls';
702
				$server=($secure?$secure.'://':'').$server;
703
704
				$port=$conf->global->$keyforsmtpport;
705
706
				$this->smtps->setHost($server);
707
				$this->smtps->setPort($port); // 25, 465...;
708
709
				$loginid=''; $loginpass='';
710
				if (! empty($conf->global->$keyforsmtpid))
711
				{
712
					$loginid = $conf->global->$keyforsmtpid;
713
					$this->smtps->setID($loginid);
714
				}
715
				if (! empty($conf->global->$keyforsmtppw))
716
				{
717
					$loginpass = $conf->global->$keyforsmtppw;
718
					$this->smtps->setPW($loginpass);
719
				}
720
721
				$res=true;
722
				$from=$this->smtps->getFrom('org');
723
				if ($res && ! $from)
724
				{
725
					$this->error="Failed to send mail with smtps lib to HOST=".$server.", PORT=".$conf->global->$keyforsmtpport."<br>Sender address '$from' invalid";
726
					dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_ERR);
727
					$res=false;
728
				}
729
				$dest=$this->smtps->getTo();
730
				if ($res && ! $dest)
731
				{
732
					$this->error="Failed to send mail with smtps lib to HOST=".$server.", PORT=".$conf->global->$keyforsmtpport."<br>Recipient address '$dest' invalid";
733
					dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_ERR);
734
					$res=false;
735
				}
736
737
				if ($res)
738
				{
739
					if (! empty($conf->global->MAIN_MAIL_DEBUG)) $this->smtps->setDebug(true);
740
741
					$result=$this->smtps->sendMsg();
742
					//print $result;
743
744
					if (! empty($conf->global->MAIN_MAIL_DEBUG)) $this->dump_mail();
745
746
					$result=$this->smtps->getErrors();
747
					if (empty($this->error) && empty($result))
748
					{
749
						dol_syslog("CMailFile::sendfile: mail end success", LOG_DEBUG);
750
						$res=true;
751
					}
752
					else
753
					{
754
						if (empty($this->error)) $this->error=$result;
755
						dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_ERR);
756
						$res=false;
757
					}
758
				}
759
			}
760
			elseif ($this->sendmode == 'swiftmailer')
761
			{
762
                // Use Swift Mailer library
763
                // ------------------------------------------
764
                require_once DOL_DOCUMENT_ROOT.'/includes/swiftmailer/lib/swift_required.php';
765
766
				// Clean parameters
767
				if (empty($conf->global->$keyforsmtpserver)) $conf->global->$keyforsmtpserver=ini_get('SMTP');
768
				if (empty($conf->global->$keyforsmtpport))   $conf->global->$keyforsmtpport=ini_get('smtp_port');
769
770
				// If we use SSL/TLS
771
				$server = $conf->global->$keyforsmtpserver;
772
				$secure = '';
773
				if (! empty($conf->global->$keyfortls) && function_exists('openssl_open')) $secure='ssl';
774
				if (! empty($conf->global->$keyforstarttls) && function_exists('openssl_open')) $secure='tls';
775
776
				$this->transport = new Swift_SmtpTransport($server, $conf->global->$keyforsmtpport, $secure);
777
778
				if (! empty($conf->global->$keyforsmtpid)) $this->transport->setUsername($conf->global->$keyforsmtpid);
779
				if (! empty($conf->global->$keyforsmtppw)) $this->transport->setPassword($conf->global->$keyforsmtppw);
780
				//$smtps->_msgReplyTo  = '[email protected]';
781
782
				// Create the Mailer using your created Transport
783
				$this->mailer = new Swift_Mailer($this->transport);
784
785
                // DKIM SIGN
786
                if ($conf->global->MAIN_MAIL_EMAIL_DKIM_ENABLED) {
787
                    $privateKey = $conf->global->MAIN_MAIL_EMAIL_DKIM_PRIVATE_KEY;
788
                    $domainName = $conf->global->MAIN_MAIL_EMAIL_DKIM_DOMAIN;
789
                    $selector = $conf->global->MAIN_MAIL_EMAIL_DKIM_SELECTOR;
790
                    $signer = new Swift_Signers_DKIMSigner($privateKey, $domainName, $selector);
791
                    $this->message->attachSigner($signer->ignoreHeader('Return-Path'));
792
                }
793
794
                if (! empty($conf->global->MAIN_MAIL_DEBUG)) {
795
					// To use the ArrayLogger
796
					$this->logger = new Swift_Plugins_Loggers_ArrayLogger();
797
					// Or to use the Echo Logger
798
					//$this->logger = new Swift_Plugins_Loggers_EchoLogger();
799
					$this->mailer->registerPlugin(new Swift_Plugins_LoggerPlugin($this->logger));
800
				}
801
				// send mail
802
				try {
803
					$result = $this->mailer->send($this->message);
804
				} catch (Exception $e) {
805
					$this->error =  $e->getMessage();
806
				}
807
				if (! empty($conf->global->MAIN_MAIL_DEBUG)) $this->dump_mail();
808
809
				$res = true;
810
				if (! empty($this->error) || ! $result) {
811
					dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_ERR);
812
					$res=false;
813
				}
814
				else
815
				{
816
					dol_syslog("CMailFile::sendfile: mail end success", LOG_DEBUG);
817
				}
818
			}
819
			else
820
			{
821
				// Send mail method not correctly defined
822
				// --------------------------------------
823
824
				return 'Bad value for sendmode';
825
			}
826
827
			$parameters=array(); $action='';
828
			$reshook = $hookmanager->executeHooks('sendMailAfter', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
829
			if ($reshook < 0)
830
			{
831
				$this->error = "Error in hook maildao sendMailAfter " . $reshook;
832
				dol_syslog("CMailFile::sendfile: mail end error=" . $this->error, LOG_ERR);
833
834
				return $reshook;
835
			}
836
		}
837
		else
838
		{
839
			$this->error='No mail sent. Feature is disabled by option MAIN_DISABLE_ALL_MAILS';
840
			dol_syslog("CMailFile::sendfile: ".$this->error, LOG_WARNING);
841
		}
842
843
		error_reporting($errorlevel);              // Reactive niveau erreur origine
844
845
		return $res;
846
	}
847
848
	/**
849
	 * Encode subject according to RFC 2822 - http://en.wikipedia.org/wiki/MIME#Encoded-Word
850
	 *
851
	 * @param string $stringtoencode String to encode
852
	 * @return string                string encoded
853
	 */
854
	public static function encodetorfc2822($stringtoencode)
855
	{
856
		global $conf;
857
		return '=?'.$conf->file->character_set_client.'?B?'.base64_encode($stringtoencode).'?=';
858
	}
859
860
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
861
	/**
862
	 * Read a file on disk and return encoded content for emails (mode = 'mail')
863
	 *
864
	 * @param	string	$sourcefile		Path to file to encode
865
	 * @return 	int					    <0 if KO, encoded string if OK
866
	 */
867
	private function _encode_file($sourcefile)
868
	{
869
        // phpcs:enable
870
		$newsourcefile=dol_osencode($sourcefile);
871
872
		if (is_readable($newsourcefile))
873
		{
874
			$contents = file_get_contents($newsourcefile);	// Need PHP 4.3
875
			$encoded = chunk_split(base64_encode($contents), 76, $this->eol);    // 76 max is defined into http://tools.ietf.org/html/rfc2047
876
			return $encoded;
877
		}
878
		else
879
		{
880
			$this->error="Error: Can't read file '".$sourcefile."' into _encode_file";
881
			dol_syslog("CMailFile::encode_file: ".$this->error, LOG_ERR);
882
			return -1;
883
		}
884
	}
885
886
887
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
888
	/**
889
	 *  Write content of a SMTP request into a dump file (mode = all)
890
	 *  Used for debugging.
891
	 *  Note that to see full SMTP protocol, you can use tcpdump -w /tmp/smtp -s 2000 port 25"
892
	 *
893
	 *  @return	void
894
	 */
895
	public function dump_mail()
896
	{
897
        // phpcs:enable
898
		global $conf,$dolibarr_main_data_root;
899
900
		if (@is_writeable($dolibarr_main_data_root))	// Avoid fatal error on fopen with open_basedir
901
		{
902
			$outputfile=$dolibarr_main_data_root."/dolibarr_mail.log";
903
			$fp = fopen($outputfile, "w");
904
905
			if ($this->sendmode == 'mail')
906
			{
907
				fputs($fp, $this->headers);
908
				fputs($fp, $this->eol);			// This eol is added by the mail function, so we add it in log
909
				fputs($fp, $this->message);
910
			}
911
			elseif ($this->sendmode == 'smtps')
912
			{
913
				fputs($fp, $this->smtps->log);	// this->smtps->log is filled only if MAIN_MAIL_DEBUG was set to on
914
			}
915
			elseif ($this->sendmode == 'swiftmailer')
916
			{
917
				fputs($fp, $this->logger->dump());	// this->logger is filled only if MAIN_MAIL_DEBUG was set to on
918
			}
919
920
			fclose($fp);
921
			if (! empty($conf->global->MAIN_UMASK))
922
				@chmod($outputfile, octdec($conf->global->MAIN_UMASK));
1 ignored issue
show
Security Best Practice introduced by
It seems like you do not handle an error condition for chmod(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

922
				/** @scrutinizer ignore-unhandled */ @chmod($outputfile, octdec($conf->global->MAIN_UMASK));

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
923
		}
924
	}
925
926
927
	/**
928
	 * Correct an uncomplete html string
929
	 *
930
	 * @param	string	$msg	String
931
	 * @return	string			Completed string
932
	 */
933
	public function checkIfHTML($msg)
934
	{
935
		if (!preg_match('/^[\s\t]*<html/i', $msg))
936
		{
937
			$out = "<html><head><title></title>";
938
			if (!empty($this->styleCSS)) $out.= $this->styleCSS;
939
			$out.= "</head><body";
940
			if (!empty($this->bodyCSS)) $out.= $this->bodyCSS;
941
			$out.= ">";
942
			$out.= $msg;
943
			$out.= "</body></html>";
944
		}
945
		else
946
		{
947
			$out = $msg;
948
		}
949
950
		return $out;
951
	}
952
953
	/**
954
	 * Build a css style (mode = all) into this->styleCSS and this->bodyCSS
955
	 *
956
	 * @return string
957
	 */
958
	public function buildCSS()
959
	{
960
		if (! empty($this->css))
961
		{
962
			// Style CSS
963
			$this->styleCSS = '<style type="text/css">';
964
			$this->styleCSS.= 'body {';
965
966
			if ($this->css['bgcolor'])
967
			{
968
				$this->styleCSS.= '  background-color: '.$this->css['bgcolor'].';';
969
				$this->bodyCSS.= ' bgcolor="'.$this->css['bgcolor'].'"';
970
			}
971
			if ($this->css['bgimage'])
972
			{
973
				// TODO recuperer cid
974
				$this->styleCSS.= ' background-image: url("cid:'.$this->css['bgimage_cid'].'");';
975
			}
976
			$this->styleCSS.= '}';
977
			$this->styleCSS.= '</style>';
978
		}
979
	}
980
981
982
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
983
	/**
984
	 * Create SMTP headers (mode = 'mail')
985
	 *
986
	 * @return	string headers
987
	 */
988
	public function write_smtpheaders()
989
	{
990
        // phpcs:enable
991
		global $conf;
992
		$out = "";
993
994
		$host = dol_getprefix('email');
995
996
		// Sender
997
		//$out.= "Sender: ".getValidAddress($this->addr_from,2)).$this->eol2;
998
		$out.= "From: ".$this->getValidAddress($this->addr_from, 3, 1).$this->eol2;
999
		if (! empty($conf->global->MAIN_MAIL_SENDMAIL_FORCE_BA))
1000
		{
1001
			$out.= "To: ".$this->getValidAddress($this->addr_to, 0, 1).$this->eol2;
1002
		}
1003
		// Return-Path is important because it is used by SPF. Some MTA does not read Return-Path from header but from command line. See option MAIN_MAIL_ALLOW_SENDMAIL_F for that.
1004
		$out.= "Return-Path: ".$this->getValidAddress($this->addr_from, 0, 1).$this->eol2;
1005
		if (isset($this->reply_to)  && $this->reply_to)  $out.= "Reply-To: ".$this->getValidAddress($this->reply_to, 2).$this->eol2;
1006
		if (isset($this->errors_to) && $this->errors_to) $out.= "Errors-To: ".$this->getValidAddress($this->errors_to, 2).$this->eol2;
1007
1008
		// Receiver
1009
		if (isset($this->addr_cc)   && $this->addr_cc)   $out.= "Cc: ".$this->getValidAddress($this->addr_cc, 2).$this->eol2;
1010
		if (isset($this->addr_bcc)  && $this->addr_bcc)  $out.= "Bcc: ".$this->getValidAddress($this->addr_bcc, 2).$this->eol2;    // TODO Question: bcc must not be into header, only into SMTP command "RCPT TO". Does php mail support this ?
1011
1012
		// Delivery receipt
1013
		if (isset($this->deliveryreceipt) && $this->deliveryreceipt == 1) $out.= "Disposition-Notification-To: ".$this->getValidAddress($this->addr_from, 2).$this->eol2;
1014
1015
		//$out.= "X-Priority: 3".$this->eol2;
1016
1017
		$out.= 'Date: ' . date("r") . $this->eol2;
1018
1019
		$trackid = $this->trackid;
1020
		if ($trackid)
1021
		{
1022
			// References is kept in response and Message-ID is returned into In-Reply-To:
1023
			$out.= 'Message-ID: <' . time() . '.phpmail-dolibarr-'. $trackid . '@' . $host . ">" . $this->eol2;	// Uppercase seems replaced by phpmail
1024
			$out.= 'References: <' . time() . '.phpmail-dolibarr-'. $trackid . '@' . $host . ">" . $this->eol2;
1025
			$out.= 'X-Dolibarr-TRACKID: ' . $trackid . '@' . $host. $this->eol2;
1026
		}
1027
		else
1028
		{
1029
			$out.= 'Message-ID: <' . time() . '.phpmail@' . $host . ">" . $this->eol2;
1030
		}
1031
1032
		if (! empty($_SERVER['REMOTE_ADDR'])) $out.= "X-RemoteAddr: " . $_SERVER['REMOTE_ADDR']. $this->eol2;
1033
		$out.= "X-Mailer: Dolibarr version " . DOL_VERSION ." (using php mail)".$this->eol2;
1034
		$out.= "Mime-Version: 1.0".$this->eol2;
1035
1036
		//$out.= "From: ".$this->getValidAddress($this->addr_from,3,1).$this->eol;
1037
1038
		$out.= "Content-Type: multipart/mixed;".$this->eol2." boundary=\"".$this->mixed_boundary."\"".$this->eol2;
1039
		$out.= "Content-Transfer-Encoding: 8bit".$this->eol2;		// TODO Seems to be ignored. Header is 7bit once received.
1040
1041
		dol_syslog("CMailFile::write_smtpheaders smtp_header=\n".$out);
1042
		return $out;
1043
	}
1044
1045
1046
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1047
	/**
1048
	 * Create header MIME (mode = 'mail')
1049
	 *
1050
	 * @param	array	$filename_list			Array of filenames
1051
	 * @param 	array	$mimefilename_list		Array of mime types
1052
	 * @return	string							mime headers
1053
	 */
1054
	public function write_mimeheaders($filename_list, $mimefilename_list)
1055
	{
1056
        // phpcs:enable
1057
		$mimedone=0;
1058
		$out = "";
1059
1060
		if (is_array($filename_list))
1 ignored issue
show
introduced by
The condition is_array($filename_list) is always true.
Loading history...
1061
		{
1062
			$filename_list_size=count($filename_list);
1063
			for($i=0;$i < $filename_list_size;$i++)
1064
			{
1065
				if ($filename_list[$i])
1066
				{
1067
					if ($mimefilename_list[$i]) $filename_list[$i] = $mimefilename_list[$i];
1068
					$out.= "X-attachments: $filename_list[$i]".$this->eol2;
1069
				}
1070
			}
1071
		}
1072
1073
		dol_syslog("CMailFile::write_mimeheaders mime_header=\n".$out, LOG_DEBUG);
1074
		return $out;
1075
	}
1076
1077
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1078
	/**
1079
	 * Return email content (mode = 'mail')
1080
	 *
1081
	 * @param	string		$msgtext		Message string
1082
	 * @return	string						String content
1083
	 */
1084
	public function write_body($msgtext)
1085
	{
1086
        // phpcs:enable
1087
		global $conf;
1088
1089
		$out='';
1090
1091
		$out.= "--" . $this->mixed_boundary . $this->eol;
1092
1093
		if ($this->atleastoneimage)
1094
		{
1095
			$out.= "Content-Type: multipart/alternative;".$this->eol." boundary=\"".$this->alternative_boundary."\"".$this->eol;
1096
			$out.= $this->eol;
1097
			$out.= "--" . $this->alternative_boundary . $this->eol;
1098
		}
1099
1100
		// Make RFC821 Compliant, replace bare linefeeds
1101
		$strContent = preg_replace("/(?<!\r)\n/si", "\r\n", $msgtext);	// PCRE modifier /s means new lines are common chars
1102
		if (! empty($conf->global->MAIN_FIX_FOR_BUGGED_MTA))
1103
		{
1104
			$strContent = preg_replace("/\r\n/si", "\n", $strContent);	// PCRE modifier /s means new lines are common chars
1105
		}
1106
1107
		$strContentAltText = '';
1108
		if ($this->msgishtml)
1109
		{
1110
			// Similar code to forge a text from html is also in CMailFile.class.php
1111
			$strContentAltText = preg_replace("/<br\s*[^>]*>/", " ", $strContent);
1112
			$strContentAltText = html_entity_decode(strip_tags($strContentAltText));
1113
			$strContentAltText = rtrim(wordwrap($strContentAltText, 75, empty($conf->global->MAIN_FIX_FOR_BUGGED_MTA)?"\r\n":"\n"));
1114
1115
			// Check if html header already in message, if not complete the message
1116
			$strContent = $this->checkIfHTML($strContent);
1117
		}
1118
1119
		// Make RFC2045 Compliant, split lines
1120
		//$strContent = rtrim(chunk_split($strContent));    // Function chunck_split seems ko if not used on a base64 content
1121
		// TODO Encode main content into base64 and use the chunk_split, or quoted-printable
1122
		$strContent = rtrim(wordwrap($strContent, 75, empty($conf->global->MAIN_FIX_FOR_BUGGED_MTA)?"\r\n":"\n"));   // TODO Using this method creates unexpected line break on text/plain content.
1123
1124
		if ($this->msgishtml)
1125
		{
1126
			if ($this->atleastoneimage)
1127
			{
1128
				$out.= "Content-Type: text/plain; charset=".$conf->file->character_set_client.$this->eol;
1129
				//$out.= "Content-Transfer-Encoding: 7bit".$this->eol;
1130
				$out.= $this->eol.($strContentAltText?$strContentAltText:strip_tags($strContent)).$this->eol; // Add plain text message
1131
				$out.= "--" . $this->alternative_boundary . $this->eol;
1132
				$out.= "Content-Type: multipart/related;".$this->eol." boundary=\"".$this->related_boundary."\"".$this->eol;
1133
				$out.= $this->eol;
1134
				$out.= "--" . $this->related_boundary . $this->eol;
1135
			}
1136
1137
			if (! $this->atleastoneimage && $strContentAltText && ! empty($conf->global->MAIN_MAIL_USE_MULTI_PART))    // Add plain text message part before html part
1138
			{
1139
				$out.= "Content-Type: multipart/alternative;".$this->eol." boundary=\"".$this->alternative_boundary."\"".$this->eol;
1140
				$out.= $this->eol;
1141
				$out.= "--" . $this->alternative_boundary . $this->eol;
1142
				$out.= "Content-Type: text/plain; charset=".$conf->file->character_set_client.$this->eol;
1143
				//$out.= "Content-Transfer-Encoding: 7bit".$this->eol;
1144
				$out.= $this->eol.$strContentAltText.$this->eol;
1145
				$out.= "--" . $this->alternative_boundary . $this->eol;
1146
			}
1147
1148
			$out.= "Content-Type: text/html; charset=".$conf->file->character_set_client.$this->eol;
1149
			//$out.= "Content-Transfer-Encoding: 7bit".$this->eol;	// TODO Use base64
1150
			$out.= $this->eol.$strContent.$this->eol;
1151
1152
			if (! $this->atleastoneimage && $strContentAltText && ! empty($conf->global->MAIN_MAIL_USE_MULTI_PART))    // Add plain text message part after html part
1153
			{
1154
				$out.= "--" . $this->alternative_boundary . "--". $this->eol;
1155
			}
1156
		}
1157
		else
1158
		{
1159
			$out.= "Content-Type: text/plain; charset=".$conf->file->character_set_client.$this->eol;
1160
			//$out.= "Content-Transfer-Encoding: 7bit".$this->eol;
1161
			$out.= $this->eol.$strContent.$this->eol;
1162
		}
1163
1164
		$out.= $this->eol;
1165
1166
		// Encode images
1167
		if ($this->atleastoneimage)
1168
		{
1169
			$out .= $this->write_images($this->images_encoded);
1170
			// always end related and end alternative after inline images
1171
			$out .= "--" . $this->related_boundary . "--" . $this->eol;
1172
			$out .= $this->eol . "--" . $this->alternative_boundary . "--" . $this->eol;
1173
			$out .= $this->eol;
1174
		}
1175
1176
		return $out;
1177
	}
1178
1179
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1180
	/**
1181
	 * Attach file to email (mode = 'mail')
1182
	 *
1183
	 * @param	array	$filename_list		Tableau
1184
	 * @param	array	$mimetype_list		Tableau
1185
	 * @param 	array	$mimefilename_list	Tableau
1186
	 * @return	string						Chaine fichiers encodes
1187
	 */
1188
	public function write_files($filename_list, $mimetype_list, $mimefilename_list)
1189
	{
1190
        // phpcs:enable
1191
		$out = '';
1192
1193
		$filename_list_size=count($filename_list);
1194
		for($i=0;$i < $filename_list_size;$i++)
1195
		{
1196
			if ($filename_list[$i])
1197
			{
1198
				dol_syslog("CMailFile::write_files: i=$i");
1199
				$encoded = $this->_encode_file($filename_list[$i]);
1200
				if ($encoded >= 0)
1201
				{
1202
					if ($mimefilename_list[$i]) $filename_list[$i] = $mimefilename_list[$i];
1203
					if (! $mimetype_list[$i]) {
1204
						$mimetype_list[$i] = "application/octet-stream";
1205
					}
1206
1207
					$out.= "--" . $this->mixed_boundary . $this->eol;
1208
					$out.= "Content-Disposition: attachment; filename=\"".$filename_list[$i]."\"".$this->eol;
1209
					$out.= "Content-Type: " . $mimetype_list[$i] . "; name=\"".$filename_list[$i]."\"".$this->eol;
1210
					$out.= "Content-Transfer-Encoding: base64".$this->eol;
1211
					$out.= "Content-Description: ".$filename_list[$i].$this->eol;
1212
					$out.= $this->eol;
1213
					$out.= $encoded;
1214
					$out.= $this->eol;
1215
					//$out.= $this->eol;
1216
				}
1217
				else
1218
				{
1219
					return $encoded;
1220
				}
1221
			}
1222
		}
1223
1224
		return $out;
1225
	}
1226
1227
1228
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1229
	/**
1230
	 * Attach an image to email (mode = 'mail')
1231
	 *
1232
	 * @param	array	$images_list	Array of array image
1233
	 * @return	string					Chaine images encodees
1234
	 */
1235
	public function write_images($images_list)
1236
	{
1237
        // phpcs:enable
1238
		$out = '';
1239
1240
		if (is_array($images_list))
1 ignored issue
show
introduced by
The condition is_array($images_list) is always true.
Loading history...
1241
		{
1242
			foreach ($images_list as $img)
1243
			{
1244
				dol_syslog("CMailFile::write_images: ".$img["name"]);
1245
1246
				$out.= "--" . $this->related_boundary . $this->eol; // always related for an inline image
1247
				$out.= "Content-Type: " . $img["content_type"] . "; name=\"".$img["name"]."\"".$this->eol;
1248
				$out.= "Content-Transfer-Encoding: base64".$this->eol;
1249
				$out.= "Content-Disposition: inline; filename=\"".$img["name"]."\"".$this->eol;
1250
				$out.= "Content-ID: <".$img["cid"].">".$this->eol;
1251
				$out.= $this->eol;
1252
				$out.= $img["image_encoded"];
1253
				$out.= $this->eol;
1254
			}
1255
		}
1256
1257
		return $out;
1258
	}
1259
1260
1261
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1262
	/**
1263
	 * Try to create a socket connection
1264
	 *
1265
	 * @param 	string		$host		Add ssl:// for SSL/TLS.
1266
	 * @param 	int			$port		Example: 25, 465
1267
	 * @return	int						Socket id if ok, 0 if KO
1268
	 */
1269
	public function check_server_port($host, $port)
1270
	{
1271
        // phpcs:enable
1272
		global $conf;
1273
1274
		$_retVal=0;
1275
		$timeout=5;	// Timeout in seconds
1276
1277
		if (function_exists('fsockopen'))
1278
		{
1279
			$keyforsmtpserver='MAIN_MAIL_SMTP_SERVER';
1280
			$keyforsmtpport  ='MAIN_MAIL_SMTP_PORT';
1281
			$keyforsmtpid    ='MAIN_MAIL_SMTPS_ID';
1282
			$keyforsmtppw    ='MAIN_MAIL_SMTPS_PW';
1283
			$keyfortls       ='MAIN_MAIL_EMAIL_TLS';
1284
			$keyforstarttls  ='MAIN_MAIL_EMAIL_STARTTLS';
1285
			if ($this->sendcontext == 'emailing' && !empty($conf->global->MAIN_MAIL_SENDMODE_EMAILING) && $conf->global->MAIN_MAIL_SENDMODE_EMAILING != 'default')
1286
			{
1287
				$keyforsmtpserver='MAIN_MAIL_SMTP_SERVER_EMAILING';
1288
				$keyforsmtpport  ='MAIN_MAIL_SMTP_PORT_EMAILING';
1289
				$keyforsmtpid    ='MAIN_MAIL_SMTPS_ID_EMAILING';
1290
				$keyforsmtppw    ='MAIN_MAIL_SMTPS_PW_EMAILING';
1291
				$keyfortls       ='MAIN_MAIL_EMAIL_TLS_EMAILING';
1292
				$keyforstarttls  ='MAIN_MAIL_EMAIL_STARTTLS_EMAILING';
1293
			}
1294
1295
			// If we use SSL/TLS
1296
			if (! empty($conf->global->$keyfortls) && function_exists('openssl_open')) $host='ssl://'.$host;
1297
			// tls smtp start with no encryption
1298
			//if (! empty($conf->global->MAIN_MAIL_EMAIL_STARTTLS) && function_exists('openssl_open')) $host='tls://'.$host;
1299
1300
			dol_syslog("Try socket connection to host=".$host." port=".$port);
1301
			//See if we can connect to the SMTP server
1302
            if ($socket = @fsockopen(
1303
					$host,       // Host to test, IP or domain. Add ssl:// for SSL/TLS.
1304
					$port,       // which Port number to use
1305
					$errno,      // actual system level error
1306
					$errstr,     // and any text that goes with the error
1307
					$timeout     // timeout for reading/writing data over the socket
1308
			)) {
1309
				// Windows still does not have support for this timeout function
1310
				if (function_exists('stream_set_timeout')) stream_set_timeout($socket, $timeout, 0);
1311
1312
				dol_syslog("Now we wait for answer 220");
1313
1314
				// Check response from Server
1315
				if ($_retVal = $this->server_parse($socket, "220")) $_retVal = $socket;
1316
			}
1317
			else
1318
			{
1319
				$this->error = utf8_check('Error '.$errno.' - '.$errstr)?'Error '.$errno.' - '.$errstr:utf8_encode('Error '.$errno.' - '.$errstr);
1320
			}
1321
		}
1322
		return $_retVal;
1323
	}
1324
1325
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1326
	/**
1327
	 * This function has been modified as provided by SirSir to allow multiline responses when
1328
	 * using SMTP Extensions.
1329
	 *
1330
	 * @param	resource	$socket			Socket
1331
	 * @param   string	    $response		Response string
1332
	 * @return  boolean		      			true if success
1333
	 */
1334
	public function server_parse($socket, $response)
1335
	{
1336
        // phpcs:enable
1337
		$_retVal = true;	// Indicates if Object was created or not
1338
		$server_response = '';
1339
1340
		while (substr($server_response, 3, 1) != ' ')
1341
		{
1342
			if (! ($server_response = fgets($socket, 256)) )
1343
			{
1344
				$this->error="Couldn't get mail server response codes";
1345
				return false;
1346
			}
1347
		}
1348
1349
		if( !( substr($server_response, 0, 3) == $response ) )
1350
		{
1351
			$this->error="Ran into problems sending Mail.\r\nResponse: $server_response";
1352
			$_retVal = false;
1353
		}
1354
1355
		return $_retVal;
1356
	}
1357
1358
	/**
1359
	 * Seearch images into html message and init array this->images_encoded if found
1360
	 *
1361
	 * @param	string	$images_dir		Location of physical images files
1362
	 * @return	int 		        	>0 if OK, <0 if KO
1363
	 */
1364
	public function findHtmlImages($images_dir)
1365
	{
1366
		// Build the list of image extensions
1367
		$extensions = array_keys($this->image_types);
1368
1369
		$matches = array();
1370
		preg_match_all('/(?:"|\')([^"\']+\.('.implode('|', $extensions).'))(?:"|\')/Ui', $this->html, $matches);  // If "xxx.ext" or 'xxx.ext' found
1371
1372
		if (! empty($matches))
1373
		{
1374
			$i=0;
1375
			foreach ($matches[1] as $full)
1376
			{
1377
1378
				if (preg_match('/file=([A-Za-z0-9_\-\/]+[\.]?[A-Za-z0-9]+)?$/i', $full, $regs))   // If xxx is 'file=aaa'
1379
				{
1380
					$img = $regs[1];
1381
1382
					if (file_exists($images_dir.'/'.$img))
1383
					{
1384
						// Image path in src
1385
						$src = preg_quote($full, '/');
1386
1387
						// Image full path
1388
						$this->html_images[$i]["fullpath"] = $images_dir.'/'.$img;
1389
1390
						// Image name
1391
						$this->html_images[$i]["name"] = $img;
1392
1393
						// Content type
1394
						if (preg_match('/^.+\.(\w{3,4})$/', $img, $reg))
1395
						{
1396
							$ext=strtolower($reg[1]);
1397
							$this->html_images[$i]["content_type"] = $this->image_types[$ext];
1398
						}
1399
1400
						// cid
1401
						$this->html_images[$i]["cid"] = dol_hash(uniqid(time()), 3);	// Force md5 hash (does not contains special chars)
1402
						$this->html = preg_replace("/src=\"$src\"|src='$src'/i", "src=\"cid:".$this->html_images[$i]["cid"]."\"", $this->html);
1403
					}
1404
					$i++;
1405
				}
1406
			}
1407
1408
			if (!empty($this->html_images))
1409
			{
1410
				$inline = array();
1411
1412
				$i=0;
1413
1414
				foreach ($this->html_images as $img)
1415
				{
1416
					$fullpath = $images_dir.'/'.$img["name"];
1417
1418
					// If duplicate images are embedded, they may show up as attachments, so remove them.
1419
					if (!in_array($fullpath, $inline))
1420
					{
1421
						// Read image file
1422
						if ($image = file_get_contents($fullpath))
1423
						{
1424
							// On garde que le nom de l'image
1425
							preg_match('/([A-Za-z0-9_-]+[\.]?[A-Za-z0-9]+)?$/i', $img["name"], $regs);
1426
							$imgName = $regs[1];
1427
1428
							$this->images_encoded[$i]['name'] = $imgName;
1429
							$this->images_encoded[$i]['fullpath'] = $fullpath;
1430
							$this->images_encoded[$i]['content_type'] = $img["content_type"];
1431
							$this->images_encoded[$i]['cid'] = $img["cid"];
1432
							// Encodage de l'image
1433
							$this->images_encoded[$i]["image_encoded"] = chunk_split(base64_encode($image), 68, $this->eol);
1434
							$inline[] = $fullpath;
1435
						}
1436
					}
1437
					$i++;
1438
				}
1439
			}
1440
			else
1441
			{
1442
				return -1;
1443
			}
1444
1445
			return 1;
1446
		}
1447
		else
1448
		{
1449
			return 0;
1450
		}
1451
	}
1452
1453
	/**
1454
	 * Return a formatted address string for SMTP protocol
1455
	 *
1456
	 * @param	string		$address		     Example: 'John Doe <[email protected]>, Alan Smith <[email protected]>' or '[email protected], [email protected]'
1457
	 * @param	int			$format			     0=auto, 1=emails with <>, 2=emails without <>, 3=auto + label between ", 4 label or email, 5 mailto link
1458
	 * @param	int			$encode			     0=No encode name, 1=Encode name to RFC2822
1459
	 * @param   int         $maxnumberofemail    0=No limit. Otherwise, maximum number of emails returned ($address may contains several email separated with ','). Add '...' if there is more.
1460
	 * @return	string						     If format 0: '<[email protected]>' or 'John Doe <[email protected]>' or '=?UTF-8?B?Sm9obiBEb2U=?= <[email protected]>'
1461
	 * 										     If format 1: '<[email protected]>'
1462
	 *										     If format 2: '[email protected]'
1463
	 *										     If format 3: '<[email protected]>' or '"John Doe" <[email protected]>' or '"=?UTF-8?B?Sm9obiBEb2U=?=" <[email protected]>'
1464
	 *                                           If format 4: 'John Doe' or '[email protected]' if no label exists
1465
     *                                           If format 5: <a href="mailto:[email protected]">John Doe</a> or <a href="mailto:[email protected]">[email protected]</a> if no label exists
1466
	 */
1467
	public static function getValidAddress($address, $format, $encode = 0, $maxnumberofemail = 0)
1468
	{
1469
		global $conf;
1470
1471
		$ret='';
1472
1473
		$arrayaddress=explode(',', $address);
1474
1475
		// Boucle sur chaque composant de l'adresse
1476
		$i=0;
1477
		foreach($arrayaddress as $val)
1478
		{
1479
			if (preg_match('/^(.*)<(.*)>$/i', trim($val), $regs))
1480
			{
1481
				$name  = trim($regs[1]);
1482
				$email = trim($regs[2]);
1483
			}
1484
			else
1485
			{
1486
				$name  = '';
1487
				$email = trim($val);
1488
			}
1489
1490
			if ($email)
1491
			{
1492
				$i++;
1493
1494
				$newemail='';
1495
                if ($format == 5) {
1496
                    $newemail = $name?$name:$email;
1497
                    $newemail = '<a href="mailto:'.$email.'">'.$newemail.'</a>';
1498
                }
1499
				if ($format == 4)
1500
				{
1501
					$newemail = $name?$name:$email;
1502
				}
1503
				if ($format == 2)
1504
				{
1505
					$newemail=$email;
1506
				}
1507
				if ($format == 1 || $format == 3)
1508
				{
1509
					$newemail='<'.$email.'>';
1510
				}
1511
				if ($format == 0 || $format == 3)
1512
				{
1513
					if (! empty($conf->global->MAIN_MAIL_NO_FULL_EMAIL)) $newemail='<'.$email.'>';
1514
					elseif (! $name) $newemail='<'.$email.'>';
1515
					else $newemail=($format==3?'"':'').($encode?self::encodetorfc2822($name):$name).($format==3?'"':'').' <'.$email.'>';
1516
				}
1517
1518
				$ret=($ret ? $ret.',' : '').$newemail;
1519
1520
				// Stop if we have too much records
1521
				if ($maxnumberofemail && $i >= $maxnumberofemail)
1522
				{
1523
					if (count($arrayaddress) > $maxnumberofemail) $ret.='...';
1524
					break;
1525
				}
1526
			}
1527
		}
1528
1529
		return $ret;
1530
	}
1531
1532
	/**
1533
	 * Return a formatted array of address string for SMTP protocol
1534
	 *
1535
	 * @param   string      $address        Example: 'John Doe <[email protected]>, Alan Smith <[email protected]>' or '[email protected], [email protected]'
1536
	 * @return  array                       array of email => name
1537
	 */
1538
	public function getArrayAddress($address)
1539
	{
1540
		global $conf;
1541
1542
		$ret=array();
1543
1544
		$arrayaddress=explode(',', $address);
1545
1546
		// Boucle sur chaque composant de l'adresse
1547
		foreach($arrayaddress as $val)
1548
		{
1549
			if (preg_match('/^(.*)<(.*)>$/i', trim($val), $regs))
1550
			{
1551
				$name  = trim($regs[1]);
1552
				$email = trim($regs[2]);
1553
			}
1554
			else
1555
			{
1556
				$name  = null;
1557
				$email = trim($val);
1558
			}
1559
1560
			$ret[$email]=empty($conf->global->MAIN_MAIL_NO_FULL_EMAIL)?$name:null;
1561
		}
1562
1563
		return $ret;
1564
	}
1565
}
1566