Apply PSR-12 code style (#3886)
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / lib / system / email / EmailGrammar.class.php
CommitLineData
3eef059c 1<?php
a9229942 2
3eef059c
TD
3namespace wcf\system\email;
4
5/**
6 * Holds RFC 2045 and RFC 5322 grammar tokens and provides helper functions
7 * for dealing with these RFCs.
a9229942
TD
8 *
9 * @author Tim Duesterhus
10 * @copyright 2001-2019 WoltLab GmbH
11 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
12 * @package WoltLabSuite\Core\System\Email
13 * @since 3.0
3eef059c 14 */
a9229942
TD
15final class EmailGrammar
16{
17 /**
18 * Returns a regular expression matching the given type in RFC 5322.
19 *
20 * @param string $type
21 * @return string
22 */
23 public static function getGrammar($type)
24 {
25 switch ($type) {
26 case 'VCHAR':
27 return '[\x21-\x7E]';
28 case 'WSP':
29 return '[\x20\x09]';
30 case 'FWS':
31 return "(?:(?:" . self::getGrammar('WSP') . "*\r\n)?" . self::getGrammar('WSP') . "+)";
32 case 'CFWS':
33 // note: no support for comments
34 return self::getGrammar('FWS');
35 case 'quoted-pair':
36 return "(?:\\\\(?:" . self::getGrammar('WSP') . "|" . self::getGrammar('VCHAR') . "))";
37 case 'atext':
38 return "[a-zA-Z0-9!#$%&'*+-/=?^_`{|}~]";
39 case 'atom':
40 return "(?:" . self::getGrammar('CFWS') . "?" . self::getGrammar('atext') . "+" . self::getGrammar('CFWS') . "?)";
41 case 'id-left':
42 case 'list-label':
43 case 'dot-atom-text':
44 return "(?:" . self::getGrammar('atext') . "+(?:\\." . self::getGrammar('atext') . '+)*)';
45 case 'no-fold-literal':
46 return "(?:\\[" . self::getGrammar('dtext') . "*\\])";
47 case 'id-right':
48 return "(?:" . self::getGrammar('dot-atom-text') . "|" . self::getGrammar('no-fold-literal') . ")";
49 case 'dot-atom':
50 return "(?:" . self::getGrammar('CFWS') . "?" . self::getGrammar('dot-atom-text') . self::getGrammar('CFWS') . "?)";
51 case 'qtext':
52 return '[\x21\x23-\x5B\x5D-\x7E]';
53 case 'qcontent':
54 return "(?:" . self::getGrammar('qtext') . "|" . self::getGrammar('quoted-pair') . ")";
55 case 'quoted-string':
56 return "(?:" . self::getGrammar('CFWS') . "?\"(?:" . self::getGrammar('FWS') . "?" . self::getGrammar('qcontent') . ")*" . self::getGrammar('FWS') . "?\"" . self::getGrammar('CFWS') . "?)";
57 case 'word':
58 return "(?:" . self::getGrammar('atom') . "|" . self::getGrammar('quoted-string') . ")";
59 case 'display-name':
60 case 'phrase':
61 return "(?:" . self::getGrammar('word') . "+)";
62 case 'local-part':
63 return "(?:" . self::getGrammar('dot-atom') . "|" . self::getGrammar('quoted-string') . ")";
64 case 'dtext':
65 return '[\x21-\x5A\x5E-\x7E]';
66 case 'domain-literal':
67 return "(?:" . self::getGrammar('CFWS') . "?\\[(?:" . self::getGrammar('FWS') . "?" . self::getGrammar('dtext') . ")*" . self::getGrammar('FWS') . "?\\]" . self::getGrammar('CFWS') . "?)";
68 case 'domain':
69 return "(?:" . self::getGrammar('dot-atom') . "|" . self::getGrammar('domain-literal') . ")";
70 case 'addr-spec':
71 return "(?:" . self::getGrammar('local-part') . "@" . self::getGrammar('domain') . ")";
72 case 'angle-addr':
73 return "(?:" . self::getGrammar('CFWS') . "?<" . self::getGrammar('addr-spec') . ">" . self::getGrammar('CFWS') . "?)";
74 case 'name-addr':
75 return "(?:" . self::getGrammar('display-name') . "?" . self::getGrammar('angle-addr') . ")";
76 case 'mailbox':
77 return "(?:" . self::getGrammar('name-addr') . "|" . self::getGrammar('addr-spec') . ")";
78 case 'msg-id':
79 return "(?:" . self::getGrammar('CFWS') . "?<" . self::getGrammar('id-left') . "@" . self::getGrammar('id-right') . ">" . self::getGrammar('CFWS') . "?)";
80 }
81 }
82
83 /**
84 * Encode text using quoted printable encoding.
85 *
86 * @param string $header Header to encode
87 * @return string Encoded header
88 */
89 public static function encodeQuotedPrintableHeader($header)
90 {
91 return \mb_encode_mimeheader($header, "UTF-8", "Q", "\r\n");
92 }
93
94 /**
95 * Return text either unmodified, if it matches the 'atom' grammar,
96 * in double quotes or encoded in quoted printable. Depending on which
97 * encoding is necessary.
98 *
99 * @param string $header Header to encode
100 * @return string Encoded header
101 */
102 public static function encodeHeader($header)
103 {
104 if (!\preg_match('(^' . self::getGrammar('atom') . '$)', $header)) {
105 if (($encoded = self::encodeQuotedPrintableHeader($header)) === $header) {
106 $header = '"' . \addcslashes($header, '\\"') . '"';
107 } else {
108 $header = $encoded;
109 }
110 }
111
112 return $header;
113 }
114
115 /**
116 * Forbid creation of EmailGrammar objects.
117 */
118 private function __construct()
119 {
120 // does nothing
121 }
3eef059c 122}