1 | <?php
|
---|
2 | /**
|
---|
3 | * Mail functions
|
---|
4 | *
|
---|
5 | * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
|
---|
6 | * @author Andreas Gohr <[email protected]>
|
---|
7 | */
|
---|
8 |
|
---|
9 | if(!defined('DOKU_INC')) die('meh.');
|
---|
10 |
|
---|
11 | // end of line for mail lines - RFC822 says CRLF but postfix (and other MTAs?)
|
---|
12 | // think different
|
---|
13 | if(!defined('MAILHEADER_EOL')) define('MAILHEADER_EOL',"\n");
|
---|
14 | #define('MAILHEADER_ASCIIONLY',1);
|
---|
15 |
|
---|
16 | /**
|
---|
17 | * Patterns for use in email detection and validation
|
---|
18 | *
|
---|
19 | * NOTE: there is an unquoted '/' in RFC2822_ATEXT, it must remain unquoted to be used in the parser
|
---|
20 | * the pattern uses non-capturing groups as captured groups aren't allowed in the parser
|
---|
21 | * select pattern delimiters with care!
|
---|
22 | *
|
---|
23 | * May not be completly RFC conform!
|
---|
24 | * @link http://www.faqs.org/rfcs/rfc2822.html (paras 3.4.1 & 3.2.4)
|
---|
25 | *
|
---|
26 | * @author Chris Smith <[email protected]>
|
---|
27 | * Check if a given mail address is valid
|
---|
28 | */
|
---|
29 | if (!defined('RFC2822_ATEXT')) define('RFC2822_ATEXT',"0-9a-zA-Z!#$%&'*+/=?^_`{|}~-");
|
---|
30 | if (!defined('PREG_PATTERN_VALID_EMAIL')) define('PREG_PATTERN_VALID_EMAIL', '['.RFC2822_ATEXT.']+(?:\.['.RFC2822_ATEXT.']+)*@(?i:[0-9a-z][0-9a-z-]*\.)+(?i:[a-z]{2,4}|museum|travel)');
|
---|
31 |
|
---|
32 | /**
|
---|
33 | * Prepare mailfrom replacement patterns
|
---|
34 | *
|
---|
35 | * @author Andreas Gohr <[email protected]>
|
---|
36 | */
|
---|
37 | function mail_setup(){
|
---|
38 | global $conf;
|
---|
39 | global $USERINFO;
|
---|
40 |
|
---|
41 | $replace = array();
|
---|
42 |
|
---|
43 | if(!empty($USERINFO['mail'])){
|
---|
44 | $replace['@MAIL@'] = $USERINFO['mail'];
|
---|
45 | }else{
|
---|
46 | $host = @parse_url(DOKU_URL,PHP_URL_HOST);
|
---|
47 | if(!$host) $host = 'example.com';
|
---|
48 | $replace['@MAIL@'] = 'noreply@'.$host;
|
---|
49 | }
|
---|
50 |
|
---|
51 | if(!empty($_SERVER['REMOTE_USER'])){
|
---|
52 | $replace['@USER@'] = $_SERVER['REMOTE_USER'];
|
---|
53 | }else{
|
---|
54 | $replace['@USER@'] = 'noreply';
|
---|
55 | }
|
---|
56 |
|
---|
57 | if(!empty($USERINFO['name'])){
|
---|
58 | $replace['@NAME@'] = $USERINFO['name'];
|
---|
59 | }else{
|
---|
60 | $replace['@NAME@'] = '';
|
---|
61 | }
|
---|
62 |
|
---|
63 | $conf['mailfrom'] = str_replace(array_keys($replace),
|
---|
64 | array_values($replace),
|
---|
65 | $conf['mailfrom']);
|
---|
66 | }
|
---|
67 |
|
---|
68 | /**
|
---|
69 | * UTF-8 autoencoding replacement for PHPs mail function
|
---|
70 | *
|
---|
71 | * Email address fields (To, From, Cc, Bcc can contain a textpart and an address
|
---|
72 | * like this: 'Andreas Gohr <[email protected]>' - the text part is encoded
|
---|
73 | * automatically. You can seperate receivers by commas.
|
---|
74 | *
|
---|
75 | * @param string $to Receiver of the mail (multiple seperated by commas)
|
---|
76 | * @param string $subject Mailsubject
|
---|
77 | * @param string $body Messagebody
|
---|
78 | * @param string $from Sender address
|
---|
79 | * @param string $cc CarbonCopy receiver (multiple seperated by commas)
|
---|
80 | * @param string $bcc BlindCarbonCopy receiver (multiple seperated by commas)
|
---|
81 | * @param string $headers Additional Headers (seperated by MAILHEADER_EOL
|
---|
82 | * @param string $params Additonal Sendmail params (passed to mail())
|
---|
83 | *
|
---|
84 | * @author Andreas Gohr <[email protected]>
|
---|
85 | * @see mail()
|
---|
86 | */
|
---|
87 | function mail_send($to, $subject, $body, $from='', $cc='', $bcc='', $headers=null, $params=null){
|
---|
88 |
|
---|
89 | $message = compact('to','subject','body','from','cc','bcc','headers','params');
|
---|
90 | return trigger_event('MAIL_MESSAGE_SEND',$message,'_mail_send_action');
|
---|
91 | }
|
---|
92 |
|
---|
93 | function _mail_send_action($data) {
|
---|
94 |
|
---|
95 | // retrieve parameters from event data, $to, $subject, $body, $from, $cc, $bcc, $headers, $params
|
---|
96 | $to = $data['to'];
|
---|
97 | $subject = $data['subject'];
|
---|
98 | $body = $data['body'];
|
---|
99 |
|
---|
100 | // add robustness in case plugin removes any of these optional values
|
---|
101 | $from = isset($data['from']) ? $data['from'] : '';
|
---|
102 | $cc = isset($data['cc']) ? $data['cc'] : '';
|
---|
103 | $bcc = isset($data['bcc']) ? $data['bcc'] : '';
|
---|
104 | $headers = isset($data['headers']) ? $data['headers'] : null;
|
---|
105 | $params = isset($data['params']) ? $data['params'] : null;
|
---|
106 |
|
---|
107 | // end additional code to support event ... original mail_send() code from here
|
---|
108 |
|
---|
109 | if(defined('MAILHEADER_ASCIIONLY')){
|
---|
110 | $subject = utf8_deaccent($subject);
|
---|
111 | $subject = utf8_strip($subject);
|
---|
112 | }
|
---|
113 |
|
---|
114 | if(!utf8_isASCII($subject)) {
|
---|
115 | $enc_subj = '=?UTF-8?Q?'.mail_quotedprintable_encode($subject,0).'?=';
|
---|
116 | // Spaces must be encoded according to rfc2047. Use the "_" shorthand
|
---|
117 | $enc_subj = preg_replace('/ /', '_', $enc_subj);
|
---|
118 |
|
---|
119 | // quoted printable has length restriction, use base64 if needed
|
---|
120 | if(strlen($subject) > 74){
|
---|
121 | $enc_subj = '=?UTF-8?B?'.base64_encode($subject).'?=';
|
---|
122 | }
|
---|
123 |
|
---|
124 | $subject = $enc_subj;
|
---|
125 | }
|
---|
126 |
|
---|
127 | $header = '';
|
---|
128 |
|
---|
129 | // No named recipients for To: in Windows (see FS#652)
|
---|
130 | $usenames = (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') ? false : true;
|
---|
131 |
|
---|
132 | $to = mail_encode_address($to,'',$usenames);
|
---|
133 | $header .= mail_encode_address($from,'From');
|
---|
134 | $header .= mail_encode_address($cc,'Cc');
|
---|
135 | $header .= mail_encode_address($bcc,'Bcc');
|
---|
136 | $header .= 'MIME-Version: 1.0'.MAILHEADER_EOL;
|
---|
137 | $header .= 'Content-Type: text/plain; charset=UTF-8'.MAILHEADER_EOL;
|
---|
138 | $header .= 'Content-Transfer-Encoding: quoted-printable'.MAILHEADER_EOL;
|
---|
139 | $header .= $headers;
|
---|
140 | $header = trim($header);
|
---|
141 |
|
---|
142 | $body = mail_quotedprintable_encode($body);
|
---|
143 |
|
---|
144 | if($params == null){
|
---|
145 | return @mail($to,$subject,$body,$header);
|
---|
146 | }else{
|
---|
147 | return @mail($to,$subject,$body,$header,$params);
|
---|
148 | }
|
---|
149 | }
|
---|
150 |
|
---|
151 | /**
|
---|
152 | * Encodes an email address header
|
---|
153 | *
|
---|
154 | * Unicode characters will be deaccented and encoded
|
---|
155 | * quoted_printable for headers.
|
---|
156 | * Addresses may not contain Non-ASCII data!
|
---|
157 | *
|
---|
158 | * Example:
|
---|
159 | * mail_encode_address("föö <[email protected]>, [email protected]","TBcc");
|
---|
160 | *
|
---|
161 | * @param string $string Multiple adresses separated by commas
|
---|
162 | * @param string $header Name of the header (To,Bcc,Cc,...)
|
---|
163 | * @param boolean $names Allow named Recipients?
|
---|
164 | */
|
---|
165 | function mail_encode_address($string,$header='',$names=true){
|
---|
166 | $headers = '';
|
---|
167 | $parts = explode(',',$string);
|
---|
168 | foreach ($parts as $part){
|
---|
169 | $part = trim($part);
|
---|
170 |
|
---|
171 | // parse address
|
---|
172 | if(preg_match('#(.*?)<(.*?)>#',$part,$matches)){
|
---|
173 | $text = trim($matches[1]);
|
---|
174 | $addr = $matches[2];
|
---|
175 | }else{
|
---|
176 | $addr = $part;
|
---|
177 | }
|
---|
178 |
|
---|
179 | // skip empty ones
|
---|
180 | if(empty($addr)){
|
---|
181 | continue;
|
---|
182 | }
|
---|
183 |
|
---|
184 | // FIXME: is there a way to encode the localpart of a emailaddress?
|
---|
185 | if(!utf8_isASCII($addr)){
|
---|
186 | msg(htmlspecialchars("E-Mail address <$addr> is not ASCII"),-1);
|
---|
187 | continue;
|
---|
188 | }
|
---|
189 |
|
---|
190 | if(!mail_isvalid($addr)){
|
---|
191 | msg(htmlspecialchars("E-Mail address <$addr> is not valid"),-1);
|
---|
192 | continue;
|
---|
193 | }
|
---|
194 |
|
---|
195 | // text was given
|
---|
196 | if(!empty($text) && $names){
|
---|
197 | // add address quotes
|
---|
198 | $addr = "<$addr>";
|
---|
199 |
|
---|
200 | if(defined('MAILHEADER_ASCIIONLY')){
|
---|
201 | $text = utf8_deaccent($text);
|
---|
202 | $text = utf8_strip($text);
|
---|
203 | }
|
---|
204 |
|
---|
205 | if(!utf8_isASCII($text)){
|
---|
206 | // put the quotes outside as in =?UTF-8?Q?"Elan Ruusam=C3=A4e"?= vs "=?UTF-8?Q?Elan Ruusam=C3=A4e?="
|
---|
207 | if (preg_match('/^"(.+)"$/', $text, $matches)) {
|
---|
208 | $text = '"=?UTF-8?Q?'.mail_quotedprintable_encode($matches[1], 0).'?="';
|
---|
209 | } else {
|
---|
210 | $text = '=?UTF-8?Q?'.mail_quotedprintable_encode($text, 0).'?=';
|
---|
211 | }
|
---|
212 | // additionally the space character should be encoded as =20 (or each
|
---|
213 | // word QP encoded separately).
|
---|
214 | // however this is needed only in mail headers, not globally in mail_quotedprintable_encode().
|
---|
215 | $text = str_replace(" ", "=20", $text);
|
---|
216 | }
|
---|
217 | }else{
|
---|
218 | $text = '';
|
---|
219 | }
|
---|
220 |
|
---|
221 | // add to header comma seperated
|
---|
222 | if($headers != ''){
|
---|
223 | $headers .= ',';
|
---|
224 | if($header) $headers .= MAILHEADER_EOL.' '; // avoid overlong mail headers
|
---|
225 | }
|
---|
226 | $headers .= $text.' '.$addr;
|
---|
227 | }
|
---|
228 |
|
---|
229 | if(empty($headers)) return null;
|
---|
230 |
|
---|
231 | //if headername was given add it and close correctly
|
---|
232 | if($header) $headers = $header.': '.$headers.MAILHEADER_EOL;
|
---|
233 |
|
---|
234 | return $headers;
|
---|
235 | }
|
---|
236 |
|
---|
237 | /**
|
---|
238 | * Check if a given mail address is valid
|
---|
239 | *
|
---|
240 | * @param string $email the address to check
|
---|
241 | * @return bool true if address is valid
|
---|
242 | */
|
---|
243 | function mail_isvalid($email){
|
---|
244 | $validator = new EmailAddressValidator;
|
---|
245 | $validator->allowLocalAddresses = true;
|
---|
246 | return $validator->check_email_address($email);
|
---|
247 | }
|
---|
248 |
|
---|
249 | /**
|
---|
250 | * Quoted printable encoding
|
---|
251 | *
|
---|
252 | * @author umu <umuAThrz.tu-chemnitz.de>
|
---|
253 | * @link http://www.php.net/manual/en/function.imap-8bit.php#61216
|
---|
254 | */
|
---|
255 | function mail_quotedprintable_encode($sText,$maxlen=74,$bEmulate_imap_8bit=true) {
|
---|
256 | // split text into lines
|
---|
257 | $aLines= preg_split("/(?:\r\n|\r|\n)/", $sText);
|
---|
258 | $cnt = count($aLines);
|
---|
259 |
|
---|
260 | for ($i=0;$i<$cnt;$i++) {
|
---|
261 | $sLine =& $aLines[$i];
|
---|
262 | if (strlen($sLine)===0) continue; // do nothing, if empty
|
---|
263 |
|
---|
264 | $sRegExp = '/[^\x09\x20\x21-\x3C\x3E-\x7E]/e';
|
---|
265 |
|
---|
266 | // imap_8bit encodes x09 everywhere, not only at lineends,
|
---|
267 | // for EBCDIC safeness encode !"#$@[\]^`{|}~,
|
---|
268 | // for complete safeness encode every character :)
|
---|
269 | if ($bEmulate_imap_8bit)
|
---|
270 | $sRegExp = '/[^\x20\x21-\x3C\x3E-\x7E]/e';
|
---|
271 |
|
---|
272 | $sReplmt = 'sprintf( "=%02X", ord ( "$0" ) ) ;';
|
---|
273 | $sLine = preg_replace( $sRegExp, $sReplmt, $sLine );
|
---|
274 |
|
---|
275 | // encode x09,x20 at lineends
|
---|
276 | {
|
---|
277 | $iLength = strlen($sLine);
|
---|
278 | $iLastChar = ord($sLine{$iLength-1});
|
---|
279 |
|
---|
280 | // !!!!!!!!
|
---|
281 | // imap_8_bit does not encode x20 at the very end of a text,
|
---|
282 | // here is, where I don't agree with imap_8_bit,
|
---|
283 | // please correct me, if I'm wrong,
|
---|
284 | // or comment next line for RFC2045 conformance, if you like
|
---|
285 | if (!($bEmulate_imap_8bit && ($i==count($aLines)-1))){
|
---|
286 | if (($iLastChar==0x09)||($iLastChar==0x20)) {
|
---|
287 | $sLine{$iLength-1}='=';
|
---|
288 | $sLine .= ($iLastChar==0x09)?'09':'20';
|
---|
289 | }
|
---|
290 | }
|
---|
291 | } // imap_8bit encodes x20 before chr(13), too
|
---|
292 | // although IMHO not requested by RFC2045, why not do it safer :)
|
---|
293 | // and why not encode any x20 around chr(10) or chr(13)
|
---|
294 | if ($bEmulate_imap_8bit) {
|
---|
295 | $sLine=str_replace(' =0D','=20=0D',$sLine);
|
---|
296 | //$sLine=str_replace(' =0A','=20=0A',$sLine);
|
---|
297 | //$sLine=str_replace('=0D ','=0D=20',$sLine);
|
---|
298 | //$sLine=str_replace('=0A ','=0A=20',$sLine);
|
---|
299 | }
|
---|
300 |
|
---|
301 | // finally split into softlines no longer than $maxlen chars,
|
---|
302 | // for even more safeness one could encode x09,x20
|
---|
303 | // at the very first character of the line
|
---|
304 | // and after soft linebreaks, as well,
|
---|
305 | // but this wouldn't be caught by such an easy RegExp
|
---|
306 | if($maxlen){
|
---|
307 | preg_match_all( '/.{1,'.($maxlen - 2).'}([^=]{0,2})?/', $sLine, $aMatch );
|
---|
308 | $sLine = implode( '=' . MAILHEADER_EOL, $aMatch[0] ); // add soft crlf's
|
---|
309 | }
|
---|
310 | }
|
---|
311 |
|
---|
312 | // join lines into text
|
---|
313 | return implode(MAILHEADER_EOL,$aLines);
|
---|
314 | }
|
---|
315 |
|
---|