Xataface Email Module  0.3.2
Email/Mailmerge Module for Xataface
 All Data Structures Files Functions Variables Pages
MIME5.php
Go to the documentation of this file.
1 <?php
2 
3 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
4  * *
5  * XPertMailer is a PHP Mail Class that can send and read messages in MIME format. *
6  * This file is part of the XPertMailer package (http://xpertmailer.sourceforge.net/) *
7  * Copyright (C) 2007 Tanase Laurentiu Iulian *
8  * *
9  * This library is free software; you can redistribute it and/or modify it under the *
10  * terms of the GNU Lesser General Public License as published by the Free Software *
11  * Foundation; either version 2.1 of the License, or (at your option) any later version. *
12  * *
13  * This library is distributed in the hope that it will be useful, but WITHOUT ANY *
14  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A *
15  * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. *
16  * *
17  * You should have received a copy of the GNU Lesser General Public License along with *
18  * this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, *
19  * Fifth Floor, Boston, MA 02110-1301, USA *
20  * *
21  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
22 
23 if (!class_exists('FUNC5')) require_once 'FUNC5.php';
24 
25 class MIME5 {
26 
27  const LE = PHP_EOL;
28  const HLEN = 52;
29  const MLEN = 73;
30 
31  const HCHARSET = 'utf-8';
32  const MCHARSET = 'us-ascii';
33 
34  const HENCDEF = 'quoted-printable';
35  const MENCDEF = 'quoted-printable';
36 
37  static public $hencarr = array('quoted-printable' => '', 'base64' => '');
38  static public $mencarr = array('7bit' => '', '8bit' => '', 'quoted-printable' => '', 'base64' => '', 'binary' => '');
39 
40  static public $qpkeys = array(
41  "\x00","\x01","\x02","\x03","\x04","\x05","\x06","\x07",
42  "\x08","\x09","\x0A","\x0B","\x0C","\x0D","\x0E","\x0F",
43  "\x10","\x11","\x12","\x13","\x14","\x15","\x16","\x17",
44  "\x18","\x19","\x1A","\x1B","\x1C","\x1D","\x1E","\x1F",
45  "\x7F","\x80","\x81","\x82","\x83","\x84","\x85","\x86",
46  "\x87","\x88","\x89","\x8A","\x8B","\x8C","\x8D","\x8E",
47  "\x8F","\x90","\x91","\x92","\x93","\x94","\x95","\x96",
48  "\x97","\x98","\x99","\x9A","\x9B","\x9C","\x9D","\x9E",
49  "\x9F","\xA0","\xA1","\xA2","\xA3","\xA4","\xA5","\xA6",
50  "\xA7","\xA8","\xA9","\xAA","\xAB","\xAC","\xAD","\xAE",
51  "\xAF","\xB0","\xB1","\xB2","\xB3","\xB4","\xB5","\xB6",
52  "\xB7","\xB8","\xB9","\xBA","\xBB","\xBC","\xBD","\xBE",
53  "\xBF","\xC0","\xC1","\xC2","\xC3","\xC4","\xC5","\xC6",
54  "\xC7","\xC8","\xC9","\xCA","\xCB","\xCC","\xCD","\xCE",
55  "\xCF","\xD0","\xD1","\xD2","\xD3","\xD4","\xD5","\xD6",
56  "\xD7","\xD8","\xD9","\xDA","\xDB","\xDC","\xDD","\xDE",
57  "\xDF","\xE0","\xE1","\xE2","\xE3","\xE4","\xE5","\xE6",
58  "\xE7","\xE8","\xE9","\xEA","\xEB","\xEC","\xED","\xEE",
59  "\xEF","\xF0","\xF1","\xF2","\xF3","\xF4","\xF5","\xF6",
60  "\xF7","\xF8","\xF9","\xFA","\xFB","\xFC","\xFD","\xFE",
61  "\xFF");
62 
63  static public $qpvrep = array(
64  "=00","=01","=02","=03","=04","=05","=06","=07",
65  "=08","=09","=0A","=0B","=0C","=0D","=0E","=0F",
66  "=10","=11","=12","=13","=14","=15","=16","=17",
67  "=18","=19","=1A","=1B","=1C","=1D","=1E","=1F",
68  "=7F","=80","=81","=82","=83","=84","=85","=86",
69  "=87","=88","=89","=8A","=8B","=8C","=8D","=8E",
70  "=8F","=90","=91","=92","=93","=94","=95","=96",
71  "=97","=98","=99","=9A","=9B","=9C","=9D","=9E",
72  "=9F","=A0","=A1","=A2","=A3","=A4","=A5","=A6",
73  "=A7","=A8","=A9","=AA","=AB","=AC","=AD","=AE",
74  "=AF","=B0","=B1","=B2","=B3","=B4","=B5","=B6",
75  "=B7","=B8","=B9","=BA","=BB","=BC","=BD","=BE",
76  "=BF","=C0","=C1","=C2","=C3","=C4","=C5","=C6",
77  "=C7","=C8","=C9","=CA","=CB","=CC","=CD","=CE",
78  "=CF","=D0","=D1","=D2","=D3","=D4","=D5","=D6",
79  "=D7","=D8","=D9","=DA","=DB","=DC","=DD","=DE",
80  "=DF","=E0","=E1","=E2","=E3","=E4","=E5","=E6",
81  "=E7","=E8","=E9","=EA","=EB","=EC","=ED","=EE",
82  "=EF","=F0","=F1","=F2","=F3","=F4","=F5","=F6",
83  "=F7","=F8","=F9","=FA","=FB","=FC","=FD","=FE",
84  "=FF");
85 
86  static public function unique($add = null) {
87  return md5(microtime(true).$add);
88  }
89 
90  static public function is_printable($str = null, $debug = null) {
91  if (!FUNC5::is_debug($debug)) $debug = debug_backtrace();
92  if (!is_string($str)) FUNC5::trace($debug, 'invalid argument type');
93  else {
94  $contain = implode('', self::$qpkeys);
95  return (strcspn($str, $contain) == strlen($str));
96  }
97  }
98 
99  static public function qp_encode($str = null, $len = null, $end = null, $debug = null) {
100  if (!FUNC5::is_debug($debug)) $debug = debug_backtrace();
101  $err = array();
102  if (!is_string($str)) $err[] = 'invalid argument type';
103  if ($len == null) $len = self::MLEN;
104  else if (!(is_int($len) && $len > 1)) $err[] = 'invalid line length value';
105  if ($end == null) $end = self::LE;
106  else if (!is_string($end)) $err[] = 'invalid line end value';
107  if (count($err) > 0) FUNC5::trace($debug, implode(', ', $err));
108  else {
109  if ($str == '') return $str;
110  else {
111  $out = array();
112  foreach (explode($end, $str) as $line) {
113  if ($line == '') $out[] = '';
114  else {
115  $line = str_replace('=', '=3D', $line);
116  $line = str_replace(self::$qpkeys, self::$qpvrep, $line);
117  preg_match_all('/.{1,'.$len.'}([^=]{0,2})?/', $line, $match);
118  $mcnt = count($match[0]);
119  for ($i = 0; $i < $mcnt; $i++) {
120  $line = (substr($match[0][$i], -1) == ' ') ? substr($match[0][$i], 0, -1).'=20' : $match[0][$i];
121  if (($i+1) < $mcnt) $line .= '=';
122  $out[] = $line;
123  }
124  }
125  }
126  return implode($end, $out);
127  }
128  }
129  }
130 
131  static public function encode_header($str = null, $charset = null, $encoding = null, $len = null, $end = null, $debug = null) {
132  if (!FUNC5::is_debug($debug)) $debug = debug_backtrace();
133  $err = array();
134  if (!is_string($str)) $err[] = 'invalid argument type';
135  if ($charset == null) $charset = self::HCHARSET;
136  else if (!is_string($charset)) $err[] = 'invalid charset type';
137  else if (!(strlen($charset) >= 2 && FUNC5::is_alpha($charset, true, '-'))) $err[] = 'invalid charset value';
138  if ($encoding == null) $encoding = self::HENCDEF;
139  else if (!is_string($encoding)) $err[] = 'invalid encoding type';
140  else {
141  $encoding = strtolower(FUNC5::str_clear($encoding));
142  if (!isset(self::$hencarr[$encoding])) $err[] = 'invalid encoding value';
143  }
144  if ($len == null) $len = self::HLEN;
145  else if (!(is_int($len) && $len > 1)) $err[] = 'invalid line length value';
146  if ($end == null) $end = self::LE;
147  else if (!is_string($end)) $err[] = 'invalid line end value';
148  if (count($err) > 0) FUNC5::trace($debug, implode(', ', $err));
149  else {
150  if ($str == '') return $str;
151  else {
152  $enc = false;
153  $dif = $len - strlen('=?'.$charset.'?X??=');
154  if ($encoding == 'quoted-printable') {
155  if (!self::is_printable($str)) {
156  $new = (($dif-4) > 2) ? ($dif-4) : $len;
157  $enc = self::qp_encode($str, $new, $end);
158  $enc = str_replace(array('?', ' ', '='.$end), array('=3F', '_', $end), $enc);
159  }
160  } else if ($encoding == 'base64') {
161  $new = ($dif > 3) ? $dif : $len;
162  if ($new > 3) {
163  for ($i = $new; $i > 2; $i--) {
164  $crt = '';
165  for ($j = 0; $j <= $i; $j++) $crt .= 'x';
166  if (strlen(base64_encode($crt)) <= $new) {
167  $new = $i;
168  break;
169  }
170  }
171  }
172  $cnk = rtrim(chunk_split($str, $new, $end));
173  $imp = array();
174  foreach (explode($end, $cnk) as $line) if ($line != '') $imp[] = base64_encode($line);
175  $enc = implode($end, $imp);
176  }
177  $res = array();
178  if ($enc) {
179  $chr = ($encoding == 'base64') ? 'B' : 'Q';
180  foreach (explode($end, $enc) as $val) if ($val != '') $res[] = '=?'.$charset.'?'.$chr.'?'.$val.'?=';
181  } else {
182  $cnk = rtrim(chunk_split($str, $len, $end));
183  foreach (explode($end, $cnk) as $val) if ($val != '') $res[] = $val;
184  }
185  return implode($end."\t", $res);
186  }
187  }
188  }
189 
190  static public function decode_header($str = null, $debug = null) {
191  if (!FUNC5::is_debug($debug)) $debug = debug_backtrace();
192  if (!is_string($str)) FUNC5::trace($debug, 'invalid argument type');
193  else {
194  $str = trim(FUNC5::str_clear($str));
195  $arr = array();
196  if ($str == '') $arr[] = array('charset' => self::HCHARSET, 'value' => '');
197  else {
198  foreach (preg_split('/(?<!\\?(?i)q)\\?\\=/', $str, -1, PREG_SPLIT_NO_EMPTY) as $str1) {
199  foreach (explode('=?', $str1, 2) as $str2) {
200  $def = false;
201  if (count($exp = explode('?B?', $str2)) == 2) {
202  if (strlen($exp[0]) >= 2 && FUNC5::is_alpha($exp[0], true, '-') && trim($exp[1]) != '') $def = array('charset' => $exp[0], 'value' => base64_decode(trim($exp[1])));
203  } else if (count($exp = explode('?b?', $str2)) == 2) {
204  if (strlen($exp[0]) >= 2 && FUNC5::is_alpha($exp[0], true, '-') && trim($exp[1]) != '') $def = array('charset' => $exp[0], 'value' => base64_decode(trim($exp[1])));
205  } else if (count($exp = explode('?Q?', $str2)) == 2) {
206  if (strlen($exp[0]) >= 2 && FUNC5::is_alpha($exp[0], true, '-') && $exp[1] != '') $def = array('charset' => $exp[0], 'value' => quoted_printable_decode(str_replace('_', ' ', $exp[1])));
207  } else if (count($exp = explode('?q?', $str2)) == 2) {
208  if (strlen($exp[0]) >= 2 && FUNC5::is_alpha($exp[0], true, '-') && $exp[1] != '') $def = array('charset' => $exp[0], 'value' => quoted_printable_decode(str_replace('_', ' ', $exp[1])));
209  }
210  if ($def) {
211  if ($def['value'] != '') $arr[] = array('charset' => $def['charset'], 'value' => $def['value']);
212  } else {
213  if ($str2 != '') $arr[] = array('charset' => self::HCHARSET, 'value' => $str2);
214  }
215  }
216  }
217  }
218  return $arr;
219  }
220  }
221 
222  static public function decode_content($str = null, $encoding = null, $debug = null) {
223  if (!FUNC5::is_debug($debug)) $debug = debug_backtrace();
224  $err = array();
225  if (!is_string($str)) $err[] = 'invalid content type';
226  if ($encoding == null) $encoding = '7bit';
227  else if (!is_string($encoding)) $err[] = 'invalid encoding type';
228  else {
229  $encoding = strtolower($encoding);
230  if (!isset(self::$mencarr[$encoding])) $err[] = 'invalid encoding value';
231  }
232  if (count($err) > 0) FUNC5::trace($debug, implode(', ', $err));
233  else {
234  if ($encoding == 'base64') {
235  $str = trim(FUNC5::str_clear($str));
236  return base64_decode($str);
237  } else if ($encoding == 'quoted-printable') {
238  return quoted_printable_decode($str);
239  } else return $str;
240  }
241  }
242 
243  static public function message($content = null, $type = null, $name = null, $charset = null, $encoding = null, $disposition = null, $id = null, $len = null, $end = null, $debug = null) {
244  if (!FUNC5::is_debug($debug)) $debug = debug_backtrace();
245  $err = array();
246  if (!(is_string($content) && $content != '')) $err[] = 'invalid content type';
247  if ($type == null) $type = 'application/octet-stream';
248  else if (is_string($type)) {
249  $type = trim(FUNC5::str_clear($type));
250  if (strlen($type) < 4) $err[] = 'invalid type value';
251  } else $err[] = 'invalid type';
252  if (is_string($name)) {
253  $name = trim(FUNC5::str_clear($name));
254  if ($name == '') $err[] = 'invalid name value';
255  } else if ($name != null) $err[] = 'invalid name type';
256  if ($charset == null) $charset = self::MCHARSET;
257  else if (!is_string($charset)) $err[] = 'invalid charset type';
258  else if (!(strlen($charset) >= 2 && FUNC5::is_alpha($charset, true, '-'))) $err[] = 'invalid charset value';
259  if ($encoding == null) $encoding = self::MENCDEF;
260  else if (!is_string($encoding)) $err[] = 'invalid encoding type';
261  else {
262  $encoding = strtolower(FUNC5::str_clear($encoding));
263  if (!isset(self::$mencarr[$encoding])) $err[] = 'invalid encoding value';
264  }
265  if ($disposition == null) $disposition = 'inline';
266  else if (is_string($disposition)) {
267  $disposition = strtolower(FUNC5::str_clear($disposition));
268  if (!($disposition == 'inline' || $disposition == 'attachment')) $err[] = 'invalid disposition value';
269  } else $err[] = 'invalid disposition type';
270  if (is_string($id)) {
271  $id = FUNC5::str_clear($id, array(' '));
272  if ($id == '') $err[] = 'invalid id value';
273  } else if ($id != null) $err[] = 'invalid id type';
274  if ($len == null) $len = self::MLEN;
275  else if (!(is_int($len) && $len > 1)) $err[] = 'invalid line length value';
276  if ($end == null) $end = self::LE;
277  else if (!is_string($end)) $err[] = 'invalid line end value';
278  if (count($err) > 0) FUNC5::trace($debug, implode(', ', $err));
279  else {
280  $header = ''.
281  'Content-Type: '.$type.';'.$end."\t".'charset="'.$charset.'"'.
282  (($name == null) ? '' : ';'.$end."\t".'name="'.$name.'"').$end.
283  'Content-Transfer-Encoding: '.$encoding.$end.
284  'Content-Disposition: '.$disposition.
285  (($name == null) ? '' : ';'.$end."\t".'filename="'.$name.'"').
286  (($id == null) ? '' : $end.'Content-ID: <'.$id.'>');
287  if ($encoding == '7bit' || $encoding == '8bit') $content = wordwrap(self::fix_eol($content), $len, $end, true);
288  else if ($encoding == 'base64') $content = rtrim(chunk_split(base64_encode($content), $len, $end));
289  else if ($encoding == 'quoted-printable') $content = self::qp_encode(self::fix_eol($content), $len, $end);
290  return array('header' => $header, 'content' => $content);
291  }
292  }
293 
294  static public function compose($text = null, $html = null, $attach = null, $uniq = null, $end = null, $debug = null) {
295  if (!FUNC5::is_debug($debug)) $debug = debug_backtrace();
296  $err = array();
297  if ($text == null && $html == null) $err[] = 'message is not set';
298  else {
299  if ($text != null) {
300  if (!(is_array($text) && isset($text['header'], $text['content']) && is_string($text['header']) && is_string($text['content']) && self::isset_header($text['header'], 'content-type', 'text/plain', $debug))) $err[] = 'invalid text message type';
301  }
302  if ($html != null) {
303  if (!(is_array($html) && isset($html['header'], $html['content']) && is_string($html['header']) && is_string($html['content']) && self::isset_header($html['header'], 'content-type', 'text/html', $debug))) $err[] = 'invalid html message type';
304  }
305  }
306  if ($attach != null) {
307  if (is_array($attach) && count($attach) > 0) {
308  foreach ($attach as $arr) {
309  if (!(is_array($arr) && isset($arr['header'], $arr['content']) && is_string($arr['header']) && is_string($arr['content']) && (self::isset_header($arr['header'], 'content-disposition', 'inline', $debug) || self::isset_header($arr['header'], 'content-disposition', 'attachment', $debug)))) {
310  $err[] = 'invalid attachment type';
311  break;
312  }
313  }
314  } else $err[] = 'invalid attachment format';
315  }
316  if ($end == null) $end = self::LE;
317  else if (!is_string($end)) $err[] = 'invalid line end value';
318  if (count($err) > 0) FUNC5::trace($debug, implode(', ', $err));
319  else {
320  $multipart = false;
321  if ($text && $html) $multipart = true;
322  if ($attach) $multipart = true;
323  $header = $body = array();
324  $header[] = 'Date: '.date('r');
325  $header[] = base64_decode('WC1NYWlsZXI6IFhQTTQgdi4wLjUgPCB3d3cueHBlcnRtYWlsZXIuY29tID4=');
326  if ($multipart) {
327  $uniq = ($uniq == null) ? 0 : intval($uniq);
328  $boundary1 = '=_1.'.self::unique($uniq++);
329  $boundary2 = '=_2.'.self::unique($uniq++);
330  $boundary3 = '=_3.'.self::unique($uniq++);
331  $disp['inline'] = $disp['attachment'] = false;
332  if ($attach != null) {
333  foreach ($attach as $darr) {
334  if (self::isset_header($darr['header'], 'content-disposition', 'inline', $debug)) $disp['inline'] = true;
335  else if (self::isset_header($darr['header'], 'content-disposition', 'attachment', $debug)) $disp['attachment'] = true;
336  }
337  }
338  $hstr = 'Content-Type: multipart/%s;'.$end."\t".'boundary="%s"';
339  $bstr = '--%s'.$end.'%s'.$end.$end.'%s';
340  $body[] = 'This is a message in MIME Format. If you see this, your mail reader does not support this format.'.$end;
341  if ($text && $html) {
342  if ($disp['inline'] && $disp['attachment']) {
343  $header[] = sprintf($hstr, 'mixed', $boundary1);
344  $body[] = '--'.$boundary1;
345  $body[] = sprintf($hstr, 'related', $boundary2).$end;
346  $body[] = '--'.$boundary2;
347  $body[] = sprintf($hstr, 'alternative', $boundary3).$end;
348  $body[] = sprintf($bstr, $boundary3, $text['header'], $text['content']);
349  $body[] = sprintf($bstr, $boundary3, $html['header'], $html['content']);
350  $body[] = '--'.$boundary3.'--';
351  foreach ($attach as $desc) if (self::isset_header($desc['header'], 'content-disposition', 'inline', $debug)) $body[] = sprintf($bstr, $boundary2, $desc['header'], $desc['content']);
352  $body[] = '--'.$boundary2.'--';
353  foreach ($attach as $desc) if (self::isset_header($desc['header'], 'content-disposition', 'attachment', $debug)) $body[] = sprintf($bstr, $boundary1, $desc['header'], $desc['content']);
354  $body[] = '--'.$boundary1.'--';
355  } else if ($disp['inline']) {
356  $header[] = sprintf($hstr, 'related', $boundary1);
357  $body[] = '--'.$boundary1;
358  $body[] = sprintf($hstr, 'alternative', $boundary2).$end;
359  $body[] = sprintf($bstr, $boundary2, $text['header'], $text['content']);
360  $body[] = sprintf($bstr, $boundary2, $html['header'], $html['content']);
361  $body[] = '--'.$boundary2.'--';
362  foreach ($attach as $desc) $body[] = sprintf($bstr, $boundary1, $desc['header'], $desc['content']);
363  $body[] = '--'.$boundary1.'--';
364  } else if ($disp['attachment']) {
365  $header[] = sprintf($hstr, 'mixed', $boundary1);
366  $body[] = '--'.$boundary1;
367  $body[] = sprintf($hstr, 'alternative', $boundary2).$end;
368  $body[] = sprintf($bstr, $boundary2, $text['header'], $text['content']);
369  $body[] = sprintf($bstr, $boundary2, $html['header'], $html['content']);
370  $body[] = '--'.$boundary2.'--';
371  foreach ($attach as $desc) $body[] = sprintf($bstr, $boundary1, $desc['header'], $desc['content']);
372  $body[] = '--'.$boundary1.'--';
373  } else {
374  $header[] = sprintf($hstr, 'alternative', $boundary1);
375  $body[] = sprintf($bstr, $boundary1, $text['header'], $text['content']);
376  $body[] = sprintf($bstr, $boundary1, $html['header'], $html['content']);
377  $body[] = '--'.$boundary1.'--';
378  }
379  } else if ($text) {
380  $header[] = sprintf($hstr, 'mixed', $boundary1);
381  $body[] = sprintf($bstr, $boundary1, $text['header'], $text['content']);
382  foreach ($attach as $desc) $body[] = sprintf($bstr, $boundary1, $desc['header'], $desc['content']);
383  $body[] = '--'.$boundary1.'--';
384  } else if ($html) {
385  if ($disp['inline'] && $disp['attachment']) {
386  $header[] = sprintf($hstr, 'mixed', $boundary1);
387  $body[] = '--'.$boundary1;
388  $body[] = sprintf($hstr, 'related', $boundary2).$end;
389  $body[] = sprintf($bstr, $boundary2, $html['header'], $html['content']);
390  foreach ($attach as $desc) if (self::isset_header($desc['header'], 'content-disposition', 'inline', $debug)) $body[] = sprintf($bstr, $boundary2, $desc['header'], $desc['content']);
391  $body[] = '--'.$boundary2.'--';
392  foreach ($attach as $desc) if (self::isset_header($desc['header'], 'content-disposition', 'attachment', $debug)) $body[] = sprintf($bstr, $boundary1, $desc['header'], $desc['content']);
393  $body[] = '--'.$boundary1.'--';
394  } else if ($disp['inline']) {
395  $header[] = sprintf($hstr, 'related', $boundary1);
396  $body[] = sprintf($bstr, $boundary1, $html['header'], $html['content']);
397  foreach ($attach as $desc) $body[] = sprintf($bstr, $boundary1, $desc['header'], $desc['content']);
398  $body[] = '--'.$boundary1.'--';
399  } else if ($disp['attachment']) {
400  $header[] = sprintf($hstr, 'mixed', $boundary1);
401  $body[] = sprintf($bstr, $boundary1, $html['header'], $html['content']);
402  foreach ($attach as $desc) $body[] = sprintf($bstr, $boundary1, $desc['header'], $desc['content']);
403  $body[] = '--'.$boundary1.'--';
404  }
405  }
406  } else {
407  if ($text) {
408  $header[] = $text['header'];
409  $body[] = $text['content'];
410  } else if ($html) {
411  $header[] = $html['header'];
412  $body[] = $html['content'];
413  }
414  }
415  $header[] = 'MIME-Version: 1.0';
416  return array('header' => implode($end, $header), 'content' => implode($end, $body));
417  }
418  }
419 
420  static public function isset_header($str = null, $name = null, $value = null, $debug = null) {
421  if (!FUNC5::is_debug($debug)) $debug = debug_backtrace();
422  $err = array();
423  if (!(is_string($str) && $str != '')) $err[] = 'invalid header type';
424  if (!(is_string($name) && strlen($name) > 1 && FUNC5::is_alpha($name, true, '-'))) $err[] = 'invalid name type';
425  if ($value != null && !is_string($value)) $err[] = 'invalid value type';
426  if (count($err) > 0) FUNC5::trace($debug, implode(', ', $err));
427  else {
428  $ret = false;
429  if ($exp = self::split_header($str, $debug)) {
430  foreach ($exp as $harr) {
431  if (strtolower($harr['name']) == strtolower($name)) {
432  if ($value != null) $ret = (strtolower($harr['value']) == strtolower($value)) ? $harr['value'] : false;
433  else $ret = $harr['value'];
434  if ($ret) break;
435  }
436  }
437  }
438  return $ret;
439  }
440  }
441 
442  static public function split_header($str = null, $debug = null) {
443  if (!FUNC5::is_debug($debug)) $debug = debug_backtrace();
444  if (!(is_string($str) && $str != '')) FUNC5::trace($debug, 'invalid header value');
445  else {
446  $str = str_replace(array(";\r\n\t", "; \r\n\t", ";\r\n ", "; \r\n "), '; ', $str);
447  $str = str_replace(array(";\n\t", "; \n\t", ";\n ", "; \n "), '; ', $str);
448  $str = str_replace(array("\r\n\t", "\r\n "), '', $str);
449  $str = str_replace(array("\n\t", "\n "), '', $str);
450  $arr = array();
451  foreach (explode("\n", $str) as $line) {
452  $line = trim(FUNC5::str_clear($line));
453  if ($line != '') {
454  if (count($exp1 = explode(':', $line, 2)) == 2) {
455  $name = rtrim($exp1[0]);
456  $val1 = ltrim($exp1[1]);
457  if (strlen($name) > 1 && FUNC5::is_alpha($name, true, '-') && $val1 != '') {
458  $name = ucfirst($name);
459  $hadd = array();
460  if (substr(strtolower($name), 0, 8) == 'content-') {
461  $exp2 = explode('; ', $val1);
462  $cnt2 = count($exp2);
463  if ($cnt2 > 1) {
464  for ($i = 1; $i < $cnt2; $i++) {
465  if (count($exp3 = explode('=', $exp2[$i], 2)) == 2) {
466  $hset = trim($exp3[0]);
467  $hval = trim($exp3[1], ' "');
468  if ($hset != '' && $hval != '') $hadd[strtolower($hset)] = $hval;
469  }
470  }
471  }
472  }
473  $val2 = (count($hadd) > 0) ? trim($exp2[0]) : $val1;
474  $arr[] = array('name' => $name, 'value' => $val2, 'content' => $hadd);
475  }
476  }
477  }
478  }
479  if (count($arr) > 0) return $arr;
480  else FUNC5::trace($debug, 'invalid header value', 1);
481  }
482  }
483 
484  static public function split_message($str = null, $debug = null) {
485  if (!FUNC5::is_debug($debug)) $debug = debug_backtrace();
486  if (!(is_string($str) && $str != '')) FUNC5::trace($debug, 'invalid message value');
487  else {
488  $ret = false;
489  if (strpos($str, "\r\n\r\n")) $ret = explode("\r\n\r\n", $str, 2);
490  else if (strpos($str, "\n\n")) $ret = explode("\n\n", $str, 2);
491  if ($ret) return array('header' => trim($ret[0]), 'content' => $ret[1]);
492  else return false;
493  }
494  }
495 
496  static public function split_mail($str = null, &$headers, &$body, $debug = null) {
497  if (!FUNC5::is_debug($debug)) $debug = debug_backtrace();
498  $headers = $body = false;
499  if (!$part = self::split_message($str, $debug)) return false;
500  if (!$harr = self::split_header($part['header'], $debug)) return false;
501  $type = $boundary = false;
502  foreach ($harr as $hnum) {
503  if (strtolower($hnum['name']) == 'content-type') {
504  $type = strtolower($hnum['value']);
505  foreach ($hnum['content'] as $hnam => $hval) {
506  if (strtolower($hnam) == 'boundary') {
507  $boundary = $hval;
508  break;
509  }
510  }
511  if ($boundary) break;
512  }
513  }
514  $headers = $harr;
515  $body = array();
516  if (substr($type, 0, strlen('multipart/')) == 'multipart/' && $boundary && strstr($part['content'], '--'.$boundary.'--')) $body = self::_parts($part['content'], $boundary, strtolower(substr($type, strlen('multipart/'))), $debug);
517  if (count($body) == 0) $body[] = self::_content($str, $debug);
518  }
519 
520  static private function _parts($str = null, $boundary = null, $multipart = null, $debug = null) {
521  if (!FUNC5::is_debug($debug)) $debug = debug_backtrace();
522  $err = array();
523  if (!(is_string($str) && $str != '')) $err[] = 'invalid content value';
524  if (!(is_string($boundary) && $boundary != '')) $err[] = 'invalid boundary value';
525  if (!(is_string($multipart) && $multipart != '')) $err[] = 'invalid multipart value';
526  if (count($err) > 0) FUNC5::trace($debug, implode(', ', $err));
527  else {
528  $ret = array();
529  if (count($exp = explode('--'.$boundary.'--', $str)) == 2) {
530  if (count($exp = explode('--'.$boundary, $exp[0])) > 2) {
531  $cnt = 0;
532  foreach ($exp as $split) {
533  $cnt++;
534  if ($cnt > 1 && $part = self::split_message($split, $debug)) {
535  if ($harr = self::split_header($part['header'], $debug)) {
536  $type = $newb = false;
537  foreach ($harr as $hnum) {
538  if (strtolower($hnum['name']) == 'content-type') {
539  $type = strtolower($hnum['value']);
540  foreach ($hnum['content'] as $hnam => $hval) {
541  if (strtolower($hnam) == 'boundary') {
542  $newb = $hval;
543  break;
544  }
545  }
546  if ($newb) break;
547  }
548  }
549  if (substr($type, 0, strlen('multipart/')) == 'multipart/' && $newb && strstr($part['content'], '--'.$newb.'--')) $ret = self::_parts($part['content'], $newb, $multipart.'|'.strtolower(substr($type, strlen('multipart/'))), $debug);
550  else {
551  $res = self::_content($split, $debug);
552  $res['multipart'] = $multipart;
553  $ret[] = $res;
554  }
555  }
556  }
557  }
558  }
559  }
560  return $ret;
561  }
562  }
563 
564  static private function _content($str = null, $debug = null) {
565  if (!FUNC5::is_debug($debug)) $debug = debug_backtrace();
566  if (!(is_string($str) && $str != '')) FUNC5::trace($debug, 'invalid content value');
567  else {
568  if (!$part = self::split_message($str, $debug)) return null;
569  if (!$harr = self::split_header($part['header'], $debug)) return null;
570  $body = array();
571  $clen = strlen('content-');
572  $encoding = false;
573  foreach ($harr as $hnum) {
574  if (substr(strtolower($hnum['name']), 0, $clen) == 'content-') {
575  $name = strtolower(substr($hnum['name'], $clen));
576  if ($name == 'transfer-encoding') $encoding = strtolower($hnum['value']);
577  else if ($name == 'id') $body[$name] = array('value' => trim($hnum['value'], '<>'), 'extra' => $hnum['content']);
578  else $body[$name] = array('value' => $hnum['value'], 'extra' => $hnum['content']);
579  }
580  }
581  if ($encoding == 'base64' || $encoding == 'quoted-printable') $body['content'] = self::decode_content($part['content'], $encoding, $debug);
582  else {
583  if ($encoding) $body['transfer-encoding'] = $encoding;
584  $body['content'] = $part['content'];
585  }
586  if (substr($body['content'], -2) == "\r\n") $body['content'] = substr($body['content'], 0, -2);
587  else if (substr($body['content'], -1) == "\n") $body['content'] = substr($body['content'], 0, -1);
588  return $body;
589  }
590  }
591 
592  static public function fix_eol($str = null, $debug = null) {
593  if (!FUNC5::is_debug($debug)) $debug = debug_backtrace();
594  if (!(is_string($str) && $str != '')) FUNC5::trace($debug, 'invalid content value');
595  else {
596  $str = str_replace("\r\n", "\n", $str);
597  $str = str_replace("\r", "\n", $str);
598  if (self::LE != "\n") $str = str_replace("\n", self::LE, $str);
599  return $str;
600  }
601  }
602 
603 }
604 
605 ?>