GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Issues (3647)

symphony/lib/toolkit/class.emailgateway.php (54 issues)

1
<?php
2
3
/**
4
 * @package toolkit
5
 */
6
7
/**
8
 * The standard exception to be thrown by all email gateways.
9
 */
10
class EmailGatewayException extends Exception
11
{
12
    /**
13
     * Creates a new exception, and logs the error.
14
     *
15
     * @param string $message
16
     * @param integer $code
17
     * @param Exception $previous
18
     *  The previous exception, if nested. See
19
     *  http://www.php.net/manual/en/language.exceptions.extending.php
20
     */
21
    public function __construct($message, $code = 0, $previous = null)
0 ignored issues
show
Incorrect spacing between argument "$code" and equals sign; expected 0 but found 1
Loading history...
Incorrect spacing between default value and equals sign for argument "$code"; expected 0 but found 1
Loading history...
Incorrect spacing between argument "$previous" and equals sign; expected 0 but found 1
Loading history...
Incorrect spacing between default value and equals sign for argument "$previous"; expected 0 but found 1
Loading history...
22
    {
23
        $trace = $this->getTrace();
24
        // Best-guess to retrieve classname of email-gateway.
25
        // Might fail in non-standard uses, will then return an
26
        // empty string.
27
        $gateway_class = $trace[1]['class']?' (' . $trace[1]['class'] . ')':'';
0 ignored issues
show
Inline shorthand IF statement requires brackets around comparison
Loading history...
Inline shorthand IF statement requires 1 space before THEN; 0 found
Loading history...
Inline shorthand IF statement requires 1 space after THEN; 0 found
Loading history...
Inline shorthand IF statement requires 1 space before ELSE; 0 found
Loading history...
Inline shorthand IF statement requires 1 space after ELSE; 0 found
Loading history...
28
        Symphony::Log()->pushToLog(__('Email Gateway Error') . $gateway_class  . ': ' . $message, $code, true);
29
30
        // CDATA the $message: Do not trust input from others
31
        $message = General::wrapInCDATA(trim($message));
32
        parent::__construct($message);
33
    }
34
}
35
36
/**
37
 * The validation exception to be thrown by all email gateways.
38
 * This exception is thrown if data does not pass validation.
39
 */
40
class EmailValidationException extends EmailGatewayException
41
{
42
}
43
44
/**
45
 * A base class for email gateways.
46
 * All email-gateways should extend this class in order to work.
47
 *
48
 * @todo add validation to all set functions.
49
 */
50
abstract class EmailGateway
51
{
52
    protected $_recipients = array();
53
    protected $_sender_name;
54
    protected $_sender_email_address;
55
    protected $_subject;
56
    protected $_body;
57
    protected $_text_plain;
58
    protected $_text_html;
59
    protected $_attachments = array();
60
    protected $_validate_attachment_errors = true;
61
    protected $_reply_to_name;
62
    protected $_reply_to_email_address;
63
    protected $_header_fields = array();
64
    protected $_boundary_mixed;
65
    protected $_boundary_alter;
66
    protected $_text_encoding = 'quoted-printable';
67
68
    /**
69
     * Indicates whether the connection to the SMTP server should be
70
     * kept alive, or closed after sending each email. Defaults to false.
71
     *
72
     * @since Symphony 2.3.1
73
     * @var boolean
74
     */
75
    protected $_keepalive = false;
76
77
    /**
78
     * The constructor sets the `_boundary_mixed` and `_boundary_alter` variables
79
     * to be unique hashes based off PHP's `uniqid` function.
80
     */
81
    public function __construct()
82
    {
83
        $this->_boundary_mixed = '=_mix_'.md5(uniqid());
84
        $this->_boundary_alter = '=_alt_'.md5(uniqid());
85
    }
86
87
    /**
88
     * The destructor ensures that any open connections to the Email Gateway
89
     * is closed.
90
     */
91
    public function __destruct()
92
    {
93
        $this->closeConnection();
94
    }
95
96
    /**
97
     * Sends the actual email. This function should be implemented in the
98
     * Email Gateway itself and should return true or false if the email
99
     * was successfully sent.
100
     * See the default gateway for an example.
101
     *
102
     * @return boolean
103
     */
104
    abstract public function send();
105
106
    /**
107
     * Open new connection to the email server.
108
     * This function is used to allow persistent connections.
109
     *
110
     * @since Symphony 2.3.1
111
     * @return boolean
112
     */
113
    public function openConnection()
114
    {
115
        $this->_keepalive = true;
116
        return true;
117
    }
118
119
    /**
120
     * Close the connection to the email Server.
121
     * This function is used to allow persistent connections.
122
     *
123
     * @since Symphony 2.3.1
124
     * @return boolean
125
     */
126
    public function closeConnection()
127
    {
128
        $this->_keepalive = false;
129
        return true;
130
    }
131
132
    /**
133
     * Sets the sender-email and sender-name.
134
     *
135
     * @param string $email
136
     *  The email-address emails will be sent from.
137
     * @param string $name
138
     *  The name the emails will be sent from.
139
     * @throws EmailValidationException
140
     * @return void
141
     */
142
    public function setFrom($email, $name)
143
    {
144
        $this->setSenderEmailAddress($email);
145
        $this->setSenderName($name);
146
    }
147
148
    /**
149
     * Does some basic checks to validate the
150
     * value of a header field. Currently only checks
151
     * if the value contains a carriage return or a new line.
152
     *
153
     * @param string $value
154
     *
155
     * @return boolean
156
     */
157
    protected function validateHeaderFieldValue($value)
158
    {
159
        // values can't contains carriage returns or new lines
160
        $carriage_returns = preg_match('%[\r\n]%', $value);
161
162
        return !$carriage_returns;
163
    }
164
165
    /**
166
     * Sets the sender-email.
167
     *
168
     * @throws EmailValidationException
169
     * @param string $email
170
     *  The email-address emails will be sent from.
171
     * @return void
172
     */
173
    public function setSenderEmailAddress($email)
174
    {
175
        if (!$this->validateHeaderFieldValue($email)) {
176
            throw new EmailValidationException(__('Sender Email Address can not contain carriage return or newlines.'));
177
        }
178
179
        $this->_sender_email_address = $email;
180
    }
181
182
    /**
183
     * Sets the sender-name.
184
     *
185
     * @throws EmailValidationException
186
     * @param string $name
187
     *  The name emails will be sent from.
188
     * @return void
189
     */
190
    public function setSenderName($name)
191
    {
192
        if (!$this->validateHeaderFieldValue($name)) {
193
            throw new EmailValidationException(__('Sender Name can not contain carriage return or newlines.'));
194
        }
195
196
        $this->_sender_name = $name;
197
    }
198
199
    /**
200
     * Sets the recipients.
201
     *
202
     * @param string|array $email
203
     *  The email-address(es) to send the email to.
204
     * @throws EmailValidationException
205
     * @return void
206
     */
207
    public function setRecipients($email)
208
    {
209
        if (!is_array($email)) {
210
            $email = explode(',', $email);
211
            // trim all values
212
            array_walk($email, function(&$val) {
0 ignored issues
show
Expected 1 space after FUNCTION keyword; 0 found
Loading history...
213
                return $val = trim($val);
214
            });
215
            // remove empty elements
216
            $email = array_filter($email);
217
        }
218
219
        foreach ($email as $e) {
220
            if (!$this->validateHeaderFieldValue($e)) {
221
                throw new EmailValidationException(__('Recipient address can not contain carriage return or newlines.'));
222
            }
223
        }
224
225
        $this->_recipients = $email;
226
    }
227
228
    /**
229
     * This functions takes a string to be used as the plaintext
230
     * content for the Email
231
     *
232
     * @todo sanitizing and security checking
233
     * @param string $text_plain
234
     */
235
    public function setTextPlain($text_plain)
236
    {
237
        $this->_text_plain = $text_plain;
238
    }
239
240
    /**
241
     * This functions takes a string to be used as the HTML
242
     * content for the Email
243
     *
244
     * @todo sanitizing and security checking
245
     * @param string $text_html
246
     */
247
    public function setTextHtml($text_html)
248
    {
249
        $this->_text_html = $text_html;
250
    }
251
252
    /**
253
     * This function sets one or multiple attachment files
254
     * to the email.
255
     *
256
     * Passing `null` to this function will
257
     * erase the current values with an empty array.
258
     *
259
     * @param string|array $files
260
     *   Accepts the same parameters format as `EmailGateway::addAttachment()`
261
     *   but you can also all multiple values at once if all files are
262
     *   wrap in a array.
263
     *
264
     *   Example:
265
     *   ````
266
     *   $email->setAttachments(array(
267
     *      array(
268
     *          'file' => 'http://example.com/foo.txt',
269
     *          'charset' => 'UTF-8'
270
     *      ),
271
     *      'path/to/your/webspace/foo/bar.csv',
272
     *      ...
273
     *   ));
274
     *   ````
275
     */
276
    public function setAttachments($files)
277
    {
278
        // Always erase
279
        $this->_attachments = array();
280
281
        // check if we have an input value
282
        if ($files == null) {
283
            return;
284
        }
285
286
        // make sure we are dealing with an array
287
        if (!is_array($files)) {
288
            $files = array($files);
289
        }
290
291
        // Append each attachment one by one in order
292
        // to normalize each input
293
        foreach ($files as $key => $file) {
294
            if (is_numeric($key)) {
295
                // key is numeric, assume keyed array or string
296
                $this->appendAttachment($file);
297
            } else {
298
                // key is not numeric, assume key is filename
299
                // and file is a string, key needs to be preserved
300
                $this->appendAttachment(array($key => $file));
301
            }
302
        }
303
    }
304
305
    /**
306
     * Appends one file attachment to the attachments array.
307
     *
308
     * @since Symphony 2.3.5
309
     *
310
     * @param string|array $file
311
     *   Can be a string representing the file path, absolute or relative, i.e.
312
     *   `'http://example.com/foo.txt'` or `'path/to/your/webspace/foo/bar.csv'`.
313
     *
314
     *   Can also be a keyed array. This will enable more options, like setting the
315
     *   charset used by mail agent to open the file or a different filename.
316
     *
317
     *   Example:
318
     *   ````
319
     *   $email->appendAttachment(array(
320
     *      'file' => 'http://example.com/foo.txt',
321
     *      'filename' => 'bar.txt',
322
     *      'charset' => 'UTF-8',
323
     *      'mime-type' => 'text/csv',
324
     *   ));
325
     *   ````
326
     */
327
    public function appendAttachment($file)
328
    {
329
        if (!is_array($file)) {
330
            // treat the param as string (old format)
331
            $file = array(
332
                'file' => $file,
333
                'filename' => null,
334
                'charset' => null,
335
            );
336
337
            // is array, but not the right key
338
        } elseif (!isset($file['file'])) {
339
            // another (un-documented) old format: key is filename
340
            $keys = array_keys($file);
341
            $file = array(
342
                'file' => $file[$keys[0]],
343
                'filename' => is_numeric($keys[0]) ? null : $keys[0],
344
                'charset' => null,
345
            );
346
        }
347
348
        // push properly formatted file entry
349
        $this->_attachments[] = $file;
350
    }
351
352
    /**
353
     * Sets the property `$_validate_attachment_errors`
354
     *
355
     * This property is true by default, so sending will break if any attachment
356
     * can not be loaded; if it is false, attachment errors error will be ignored.
357
     *
358
     * @since Symphony 2.7
359
     * @param boolean $validate_attachment_errors
360
     * @return void
361
     */
362
    public function setValidateAttachmentErrors($validate_attachment_errors)
363
    {
364
        if (!is_bool($validate_attachment_errors)) {
0 ignored issues
show
The condition is_bool($validate_attachment_errors) is always true.
Loading history...
365
            throw new EmailGatewayException(__('%s accepts boolean values only.', array('<code>setValidateAttachmentErrors</code>')));
366
        } else {
367
            $this->_validate_attachment_errors = $validate_attachment_errors;
368
        }
369
    }
370
371
    /**
372
     * @todo Document this function
373
     * @throws EmailGatewayException
374
     * @param string $encoding
375
     *  Must be either `quoted-printable` or `base64`.
376
     */
377
    public function setTextEncoding($encoding = null)
0 ignored issues
show
Incorrect spacing between argument "$encoding" and equals sign; expected 0 but found 1
Loading history...
Incorrect spacing between default value and equals sign for argument "$encoding"; expected 0 but found 1
Loading history...
378
    {
379
        if ($encoding == 'quoted-printable') {
380
            $this->_text_encoding = 'quoted-printable';
381
        } elseif ($encoding == 'base64') {
382
            $this->_text_encoding = 'base64';
383
        } elseif (!$encoding) {
384
            $this->_text_encoding = false;
385
        } else {
386
            throw new EmailGatewayException(__('%1$s is not a supported encoding type. Please use %2$s or %3$s. You can also use %4$s for no encoding.', array($encoding, '<code>quoted-printable</code>', '<code>base-64</code>', '<code>false</code>')));
387
        }
388
    }
389
390
    /**
391
     * Sets the subject.
392
     *
393
     * @param string $subject
394
     *  The subject that the email will have.
395
     * @return void
396
     */
397
    public function setSubject($subject)
398
    {
399
        //TODO: sanitizing and security checking;
400
        $this->_subject = $subject;
401
    }
402
403
    /**
404
     * Sets the reply-to-email.
405
     *
406
     * @throws EmailValidationException
407
     * @param string $email
408
     *  The email-address emails should be replied to.
409
     * @return void
410
     */
411
    public function setReplyToEmailAddress($email)
412
    {
413
        if (preg_match('%[\r\n]%', $email)) {
414
            throw new EmailValidationException(__('Reply-To Email Address can not contain carriage return or newlines.'));
415
        }
416
417
        $this->_reply_to_email_address = $email;
418
    }
419
420
    /**
421
     * Sets the reply-to-name.
422
     *
423
     * @throws EmailValidationException
424
     * @param string $name
425
     *  The name emails should be replied to.
426
     * @return void
427
     */
428
    public function setReplyToName($name)
429
    {
430
        if (preg_match('%[\r\n]%', $name)) {
431
            throw new EmailValidationException(__('Reply-To Name can not contain carriage return or newlines.'));
432
        }
433
434
        $this->_reply_to_name = $name;
435
    }
436
437
    /**
438
     * Sets all configuration entries from an array.
439
     * This enables extensions like the ENM to create email settings panes that work regardless of the email gateway.
440
     * Every gateway should extend this method to add their own settings.
441
     *
442
     * @throws EmailValidationException
443
     * @param array $config
444
     * @since Symphony 2.3.1
445
     *  All configuration entries stored in a single array. The array should have the format of the $_POST array created by the preferences HTML.
446
     * @return boolean
447
     */
448
    public function setConfiguration($config)
449
    {
450
        return true;
451
    }
452
453
    /**
454
     * Appends a single header field to the header fields array.
455
     * The header field should be presented as a name/body pair.
456
     *
457
     * @throws EmailGatewayException
458
     * @param string $name
459
     *  The header field name. Examples are From, Bcc, X-Sender and Reply-to.
460
     * @param string $body
461
     *  The header field body.
462
     * @return void
463
     */
464
    public function appendHeaderField($name, $body)
465
    {
466
        if (is_array($body)) {
0 ignored issues
show
The condition is_array($body) is always false.
Loading history...
467
            throw new EmailGatewayException(__('%s accepts strings only; arrays are not allowed.', array('<code>appendHeaderField</code>')));
468
        }
469
470
        $this->_header_fields[$name] = $body;
471
    }
472
473
    /**
474
     * Appends one or more header fields to the header fields array.
475
     * Header fields should be presented as an array with name/body pairs.
476
     *
477
     * @param array $header_array
478
     *  The header fields. Examples are From, X-Sender and Reply-to.
479
     * @throws EmailGatewayException
480
     * @return void
481
     */
482
    public function appendHeaderFields(array $header_array = array())
0 ignored issues
show
Incorrect spacing between argument "$header_array" and equals sign; expected 0 but found 1
Loading history...
Incorrect spacing between default value and equals sign for argument "$header_array"; expected 0 but found 1
Loading history...
483
    {
484
        foreach ($header_array as $name => $body) {
485
            $this->appendHeaderField($name, $body);
486
        }
487
    }
488
489
    /**
490
     * Makes sure the Subject, Sender Email and Recipients values are
491
     * all set and are valid. The message body is checked in
492
     * `prepareMessageBody`
493
     *
494
     * @see prepareMessageBody()
495
     * @throws EmailValidationException
496
     * @return boolean
497
     */
498
    public function validate()
499
    {
500
        if (strlen(trim($this->_subject)) <= 0) {
501
            throw new EmailValidationException(__('Email subject cannot be empty.'));
502
        } elseif (strlen(trim($this->_sender_email_address)) <= 0) {
503
            throw new EmailValidationException(__('Sender email address cannot be empty.'));
504
        } elseif (!filter_var($this->_sender_email_address, FILTER_VALIDATE_EMAIL)) {
505
            throw new EmailValidationException(__('Sender email address must be a valid email address.'));
506
        } else {
507
            foreach ($this->_recipients as $address) {
508
                if (strlen(trim($address)) <= 0) {
509
                    throw new EmailValidationException(__('Recipient email address cannot be empty.'));
510
                } elseif (!filter_var($address, FILTER_VALIDATE_EMAIL)) {
511
                    throw new EmailValidationException(__('The email address ‘%s’ is invalid.', array($address)));
512
                }
513
            }
514
        }
515
516
        return true;
517
    }
518
519
    /**
520
     * Build the message body and the content-describing header fields
521
     *
522
     * The result of this building is an updated body variable in the
523
     * gateway itself.
524
     *
525
     * @throws EmailGatewayException
526
     * @throws Exception
527
     * @return boolean
528
     */
529
    protected function prepareMessageBody()
530
    {
531
        $attachments = $this->getSectionAttachments();
532
        if ($attachments) {
533
            $this->appendHeaderFields($this->contentInfoArray('multipart/mixed'));
534
            if (!empty($this->_text_plain) && !empty($this->_text_html)) {
535
                $this->_body = $this->boundaryDelimiterLine('multipart/mixed')
536
                            . $this->contentInfoString('multipart/alternative')
537
                            . $this->getSectionMultipartAlternative()
538
                            . $attachments
539
                ;
0 ignored issues
show
Space found before semicolon; expected "$attachments;" but found "$attachments
;"
Loading history...
540
            } elseif (!empty($this->_text_plain)) {
541
                $this->_body = $this->boundaryDelimiterLine('multipart/mixed')
542
                            . $this->contentInfoString('text/plain')
543
                            . $this->getSectionTextPlain()
544
                            . $attachments
545
                ;
0 ignored issues
show
Space found before semicolon; expected "$attachments;" but found "$attachments
;"
Loading history...
546
            } elseif (!empty($this->_text_html)) {
547
                $this->_body = $this->boundaryDelimiterLine('multipart/mixed')
548
                            . $this->contentInfoString('text/html')
549
                            . $this->getSectionTextHtml()
550
                            . $attachments
551
                ;
0 ignored issues
show
Space found before semicolon; expected "$attachments;" but found "$attachments
;"
Loading history...
552
            } else {
553
                $this->_body = $attachments;
554
            }
0 ignored issues
show
No blank line found after control structure
Loading history...
555
            $this->_body .= $this->finalBoundaryDelimiterLine('multipart/mixed');
556
        } elseif (!empty($this->_text_plain) && !empty($this->_text_html)) {
557
            $this->appendHeaderFields($this->contentInfoArray('multipart/alternative'));
558
            $this->_body = $this->getSectionMultipartAlternative();
559
        } elseif (!empty($this->_text_plain)) {
560
            $this->appendHeaderFields($this->contentInfoArray('text/plain'));
561
            $this->_body = $this->getSectionTextPlain();
562
        } elseif (!empty($this->_text_html)) {
563
            $this->appendHeaderFields($this->contentInfoArray('text/html'));
564
            $this->_body = $this->getSectionTextHtml();
565
        } else {
566
            throw new EmailGatewayException(__('No attachments or body text was set. Can not send empty email.'));
567
        }
568
    }
569
570
    /**
571
     * Build multipart email section. Used by sendmail and smtp classes to
572
     * send multipart email.
573
     *
574
     * Will return a string containing the section. Can be used to send to
575
     * an email server directly.
576
     * @return string
577
     */
578
    protected function getSectionMultipartAlternative()
579
    {
580
        $output = $this->boundaryDelimiterLine('multipart/alternative')
581
                . $this->contentInfoString('text/plain')
582
                . $this->getSectionTextPlain()
583
                . $this->boundaryDelimiterLine('multipart/alternative')
584
                . $this->contentInfoString('text/html')
585
                . $this->getSectionTextHtml()
586
                . $this->finalBoundaryDelimiterLine('multipart/alternative')
0 ignored issues
show
Space after closing parenthesis of function call prohibited
Loading history...
587
        ;
0 ignored issues
show
Space found before semicolon; expected ");" but found ")
;"
Loading history...
588
589
        return $output;
590
    }
591
592
    /**
593
     * Builds the attachment section of a multipart email.
594
     *
595
     * Will return a string containing the section. Can be used to send to
596
     * an email server directly.
597
     *
598
     * @throws EmailGatewayException
599
     * @throws Exception
600
     * @return string
601
     */
602
    protected function getSectionAttachments()
603
    {
604
        $output = '';
605
606
        foreach ($this->_attachments as $key => $file) {
607
            $tmp_file = false;
608
609
            // If the attachment is a URL, download the file to a temporary location.
610
            // This prevents downloading the file twice - once for info, once for data.
611
            if (filter_var($file['file'], FILTER_VALIDATE_URL)) {
612
                $gateway = new Gateway();
613
                $gateway->init($file['file']);
614
                $gateway->setopt('TIMEOUT', 30);
615
                $file_content = @$gateway->exec();
616
617
                $tmp_file = tempnam(TMP, 'attachment');
0 ignored issues
show
The constant TMP was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
618
                General::writeFile($tmp_file, $file_content, Symphony::Configuration()->get('write_mode', 'file'));
0 ignored issues
show
It seems like Symphony::Configuration(...t('write_mode', 'file') can also be of type array; however, parameter $perm of General::writeFile() does only seem to accept integer|string, maybe add an additional type check? ( Ignorable by Annotation )

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

618
                General::writeFile($tmp_file, $file_content, /** @scrutinizer ignore-type */ Symphony::Configuration()->get('write_mode', 'file'));
Loading history...
619
620
                $original_filename = $file['file'];
621
                $file['file'] = $tmp_file;
622
623
                // Without this the temporary filename will be used. Ugly!
624
                if (is_null($file['filename'])) {
625
                    $file['filename'] = basename($original_filename);
626
                }
627
            } else {
628
                $file_content = @file_get_contents($file['file']);
629
            }
630
631
            if ($file_content !== false && !empty($file_content)) {
632
                $output .= $this->boundaryDelimiterLine('multipart/mixed')
633
                     . $this->contentInfoString($file['mime-type'], $file['file'], $file['filename'], $file['charset'])
634
                     . EmailHelper::base64ContentTransferEncode($file_content);
0 ignored issues
show
It seems like $file_content can also be of type true; however, parameter $data of EmailHelper::base64ContentTransferEncode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

634
                     . EmailHelper::base64ContentTransferEncode(/** @scrutinizer ignore-type */ $file_content);
Loading history...
635
            } else {
636
                if ($this->_validate_attachment_errors) {
637
                    if (!$tmp_file === false) {
638
                        $filename = $original_filename;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $original_filename does not seem to be defined for all execution paths leading up to this point.
Loading history...
639
                    } else {
640
                        $filename = $file['file'];
641
                    }
642
643
                    throw new EmailGatewayException(__('The content of the file `%s` could not be loaded.', array($filename)));
644
                }
645
            }
646
647
            if (!$tmp_file === false) {
648
                General::deleteFile($tmp_file);
0 ignored issues
show
It seems like $tmp_file can also be of type false; however, parameter $file of General::deleteFile() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

648
                General::deleteFile(/** @scrutinizer ignore-type */ $tmp_file);
Loading history...
649
            }
650
        }
0 ignored issues
show
No blank line found after control structure
Loading history...
651
        return $output;
652
    }
653
654
    /**
655
     * Builds the text section of a text/plain email.
656
     *
657
     * Will return a string containing the section. Can be used to send to
658
     * an email server directly.
659
     * @return string
660
     */
661
    protected function getSectionTextPlain()
662
    {
663
        if ($this->_text_encoding == 'quoted-printable') {
664
            return EmailHelper::qpContentTransferEncode($this->_text_plain)."\r\n";
665
        } elseif ($this->_text_encoding == 'base64') {
666
            // don't add CRLF if using base64 - spam filters don't
667
            // like this
668
            return EmailHelper::base64ContentTransferEncode($this->_text_plain);
669
        }
670
671
        return $this->_text_plain."\r\n";
672
    }
673
674
    /**
675
     * Builds the html section of a text/html email.
676
     *
677
     * Will return a string containing the section. Can be used to send to
678
     * an email server directly.
679
     * @return string
680
     */
681
    protected function getSectionTextHtml()
682
    {
683
        if ($this->_text_encoding == 'quoted-printable') {
684
            return EmailHelper::qpContentTransferEncode($this->_text_html)."\r\n";
685
        } elseif ($this->_text_encoding == 'base64') {
686
            // don't add CRLF if using base64 - spam filters don't
687
            // like this
688
            return EmailHelper::base64ContentTransferEncode($this->_text_html);
689
        }
0 ignored issues
show
No blank line found after control structure
Loading history...
690
        return $this->_text_html."\r\n";
691
    }
692
693
    /**
694
     * Builds the right content-type/encoding types based on file and
695
     * content-type.
696
     *
697
     * Will try to match a common description, based on the $type param.
698
     * If nothing is found, will return a base64 attached file disposition.
699
     *
700
     * Can be used to send to an email server directly.
701
     *
702
     * @param string $type optional mime-type
703
     * @param string $file optional the path of the attachment
704
     * @param string $filename optional the name of the attached file
705
     * @param string $charset optional the charset of the attached file
706
     *
707
     * @return array
708
     */
709
    public function contentInfoArray($type = null, $file = null, $filename = null, $charset = null)
0 ignored issues
show
Incorrect spacing between argument "$type" and equals sign; expected 0 but found 1
Loading history...
Incorrect spacing between default value and equals sign for argument "$type"; expected 0 but found 1
Loading history...
Incorrect spacing between argument "$file" and equals sign; expected 0 but found 1
Loading history...
Incorrect spacing between default value and equals sign for argument "$file"; expected 0 but found 1
Loading history...
Incorrect spacing between argument "$filename" and equals sign; expected 0 but found 1
Loading history...
Incorrect spacing between default value and equals sign for argument "$filename"; expected 0 but found 1
Loading history...
Incorrect spacing between argument "$charset" and equals sign; expected 0 but found 1
Loading history...
Incorrect spacing between default value and equals sign for argument "$charset"; expected 0 but found 1
Loading history...
710
    {
711
        // Common descriptions
712
        $description = array(
713
            'multipart/mixed' => array(
714
                'Content-Type' => 'multipart/mixed; boundary="'
715
                                  .$this->getBoundary('multipart/mixed').'"',
716
            ),
717
            'multipart/alternative' => array(
718
                'Content-Type' => 'multipart/alternative; boundary="'
719
                                  .$this->getBoundary('multipart/alternative').'"',
720
            ),
721
            'text/plain' => array(
722
                'Content-Type'              => 'text/plain; charset=UTF-8',
723
                'Content-Transfer-Encoding' => $this->_text_encoding ? $this->_text_encoding : '8bit',
0 ignored issues
show
Inline shorthand IF statement requires brackets around comparison
Loading history...
724
            ),
725
            'text/html' => array(
726
                'Content-Type'              => 'text/html; charset=UTF-8',
727
                'Content-Transfer-Encoding' => $this->_text_encoding ? $this->_text_encoding : '8bit',
0 ignored issues
show
Inline shorthand IF statement requires brackets around comparison
Loading history...
728
            ),
729
        );
730
731
        // Try common
732
        if (!empty($type) && !empty($description[$type])) {
733
            // return it if found
734
            return $description[$type];
735
        }
736
737
        // assure we have a file name
738
        $filename = !is_null($filename) ? $filename : basename($file);
739
740
        // Format charset for insertion in content-type, if needed
741
        if (!empty($charset)) {
742
            $charset = sprintf('charset=%s;', $charset);
743
        } else {
744
            $charset = '';
745
        }
0 ignored issues
show
No blank line found after control structure
Loading history...
746
        // if the mime type is not set, try to obtain using the getMimeType
747
        if (empty($type)) {
748
            //assume that the attachment mimetime is appended
749
            $type = General::getMimeType($file);
0 ignored issues
show
Bug Best Practice introduced by
The method General::getMimeType() is not static, but was called statically. ( Ignorable by Annotation )

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

749
            /** @scrutinizer ignore-call */ 
750
            $type = General::getMimeType($file);
Loading history...
750
        }
0 ignored issues
show
No blank line found after control structure
Loading history...
751
        // Return binary description
752
        return array(
753
            'Content-Type'              => $type.';'.$charset.' name="'.$filename.'"',
754
            'Content-Transfer-Encoding' => 'base64',
755
            'Content-Disposition'       => 'attachment; filename="' .$filename .'"',
756
        );
757
    }
758
759
    /**
760
     * Creates the properly formatted InfoString based on the InfoArray.
761
     *
762
     * @see EmailGateway::contentInfoArray()
763
     *
764
     * @return string|null
765
     */
766
    protected function contentInfoString($type = null, $file = null, $filename = null, $charset = null)
0 ignored issues
show
Incorrect spacing between argument "$type" and equals sign; expected 0 but found 1
Loading history...
Incorrect spacing between default value and equals sign for argument "$type"; expected 0 but found 1
Loading history...
Incorrect spacing between argument "$file" and equals sign; expected 0 but found 1
Loading history...
Incorrect spacing between default value and equals sign for argument "$file"; expected 0 but found 1
Loading history...
Incorrect spacing between argument "$filename" and equals sign; expected 0 but found 1
Loading history...
Incorrect spacing between default value and equals sign for argument "$filename"; expected 0 but found 1
Loading history...
Incorrect spacing between argument "$charset" and equals sign; expected 0 but found 1
Loading history...
Incorrect spacing between default value and equals sign for argument "$charset"; expected 0 but found 1
Loading history...
767
    {
768
        $data = $this->contentInfoArray($type, $file, $filename, $charset);
769
        $fields = array();
770
771
        foreach ($data as $key => $value) {
772
            $fields[] = EmailHelper::fold(sprintf('%s: %s', $key, $value));
773
        }
774
775
        return !empty($fields) ? implode("\r\n", $fields)."\r\n\r\n" : null;
776
    }
777
778
    /**
779
     * Returns the bondary based on the $type parameter
780
     *
781
     * @param string $type the multipart type
782
     * @return string|void
783
     */
784
    protected function getBoundary($type)
785
    {
786
        switch ($type) {
787
            case 'multipart/mixed':
788
                return $this->_boundary_mixed;
789
            case 'multipart/alternative':
790
                return $this->_boundary_alter;
791
        }
792
    }
793
794
    /**
795
     * @param string $type
796
     * @return string
797
     */
798
    protected function boundaryDelimiterLine($type)
799
    {
800
        // As requested by RFC 2046: 'The CRLF preceding the boundary
801
        // delimiter line is conceptually attached to the boundary.'
802
        return $this->getBoundary($type) ? "\r\n--".$this->getBoundary($type)."\r\n" : null;
803
    }
804
805
    /**
806
     * @param string $type
807
     * @return string
808
     */
809
    protected function finalBoundaryDelimiterLine($type)
810
    {
811
        return $this->getBoundary($type) ? "\r\n--".$this->getBoundary($type)."--\r\n" : null;
812
    }
813
814
    /**
815
     * Sets a property.
816
     *
817
     * Magic function, supplied by php.
818
     * This function will try and find a method of this class, by
819
     * camelcasing the name, and appending it with set.
820
     * If the function can not be found, an exception will be thrown.
821
     *
822
     * @param string $name
823
     *  The property name.
824
     * @param string $value
825
     *  The property value;
826
     * @throws EmailGatewayException
827
     * @return void|boolean
828
     */
829
    public function __set($name, $value)
830
    {
831
        if (method_exists(get_class($this), 'set'.$this->__toCamel($name, true))) {
832
            return $this->{'set'.$this->__toCamel($name, true)}($value);
833
        } else {
834
            throw new EmailGatewayException(__('The %1$s gateway does not support the use of %2$s', array(get_class($this), $name)));
835
        }
836
    }
837
838
    /**
839
     * Gets a property.
840
     *
841
     * Magic function, supplied by php.
842
     * This function will attempt to find a variable set with `$name` and
843
     * returns it. If the variable is not set, it will return false.
844
     *
845
     * @since Symphony 2.2.2
846
     * @param string $name
847
     *  The property name.
848
     * @return boolean|mixed
849
     */
850
    public function __get($name)
851
    {
852
        return isset($this->{'_'.$name}) ? $this->{'_'.$name} : false;
853
    }
854
855
    /**
856
     * The preferences to add to the preferences pane in the admin area.
857
     *
858
     * @return XMLElement
859
     */
860
    public function getPreferencesPane()
861
    {
862
        return new XMLElement('fieldset');
863
    }
864
865
    /**
866
     * Internal function to turn underscored variables into camelcase, for
867
     * use in methods.
868
     * Because Symphony has a difference in naming between properties and
869
     * methods (underscored vs camelcased) and the Email class uses the
870
     * magic __set function to find property-setting-methods, this
871
     * conversion is needed.
872
     *
873
     * @param string $string
874
     *  The string to convert
875
     * @param boolean $caseFirst
876
     *  If this is true, the first character will be uppercased. Useful
877
     *  for method names (setName).
878
     *  If set to false, the first character will be lowercased. This is
879
     *  default behaviour.
880
     * @return string
881
     */
882
    private function __toCamel($string, $caseFirst = false)
0 ignored issues
show
Incorrect spacing between argument "$caseFirst" and equals sign; expected 0 but found 1
Loading history...
Incorrect spacing between default value and equals sign for argument "$caseFirst"; expected 0 but found 1
Loading history...
883
    {
884
        $string = strtolower($string);
885
        $a = explode('_', $string);
886
        $a = array_map('ucfirst', $a);
887
888
        if (!$caseFirst) {
889
            $a[0] = lcfirst($a[0]);
890
        }
891
892
        return implode('', $a);
893
    }
894
895
    /**
896
     * The reverse of the __toCamel function.
897
     *
898
     * @param string $string
899
     *  The string to convert
900
     * @return string
901
     */
902
    private function __fromCamel($string)
903
    {
904
        $string[0] = strtolower($string[0]);
905
906
        return preg_replace_callback('/([A-Z])/', function($c) {
0 ignored issues
show
Expected 1 space after FUNCTION keyword; 0 found
Loading history...
907
            return "_" . strtolower($c[1]);
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal _ does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
908
        }, $string);
909
    }
910
}
911