4960ec8e |
1 | <?php |
2 | |
69298c28 |
3 | /** |
4 | * Deliver.class.php |
5 | * |
6 | * Copyright (c) 1999-2002 The SquirrelMail Project Team |
7 | * Licensed under the GNU GPL. For full terms see the file COPYING. |
8 | * |
9 | * This contains all the functions needed to send messages through |
10 | * a delivery backend. |
11 | * |
12 | * $Id$ |
13 | */ |
14 | |
4960ec8e |
15 | class Deliver { |
16 | |
59387d1c |
17 | function mail($message, $stream=false) { |
3b53d87f |
18 | $rfc822_header = $message->rfc822_header; |
19 | if (count($message->entities)) { |
59387d1c |
20 | $boundary = $this->mimeBoundary(); |
1274f430 |
21 | $rfc822_header->content_type->properties['boundary']='"'.$boundary.'"'; |
3b53d87f |
22 | } else { |
23 | $boundary=''; |
24 | } |
1274f430 |
25 | $reply_rfc822_header = (isset($message->reply_rfc822_header) |
26 | ? $message->reply_rfc822_header : ''); |
27 | $header = $this->prepareRFC822_Header($rfc822_header, $reply_rfc822_header); |
3b53d87f |
28 | $raw_length = strlen($header); |
29 | if ($stream) { |
59387d1c |
30 | $this->preWriteToStream($header); |
31 | $this->writeToStream($stream, $header); |
32 | } else { |
1274f430 |
33 | /* DEBUG */ |
34 | $out = htmlspecialchars($header); |
35 | $out = str_replace("\r\n",'<BR>',$out); |
36 | echo $out; |
3b53d87f |
37 | } |
59387d1c |
38 | $this->writeBody($message, $stream, $raw_length, $boundary); |
3b53d87f |
39 | return $raw_length; |
40 | } |
41 | |
42 | function writeBody($message, $stream, &$length_raw, $boundary='') { |
1274f430 |
43 | if ($boundary && !$message->rfc822_header) { |
3b53d87f |
44 | $s = '--'.$boundary."\r\n"; |
45 | $s .= $this->prepareMIME_Header($message, $boundary); |
46 | $length_raw += strlen($s); |
47 | if ($stream) { |
48 | $this->preWriteToStream($s); |
49 | $this->writeToStream($stream, $s); |
1274f430 |
50 | } else { |
51 | /* DEBUG */ |
52 | $out = htmlspecialchars($s); |
53 | $out = str_replace("\r\n",'<BR>',$out); |
54 | echo $out; |
3b53d87f |
55 | } |
56 | } |
59387d1c |
57 | $this->writeBodyPart($message, $stream, $length_raw); |
3b53d87f |
58 | $boundary_depth = substr_count($message->entity_id,'.'); |
59 | if ($boundary_depth) { |
60 | $boundary .= '_part'.$boundary_depth; |
61 | } |
1274f430 |
62 | $last = false; |
3b53d87f |
63 | for ($i=0, $entCount=count($message->entities);$i<$entCount;$i++) { |
59387d1c |
64 | $msg = $this->writeBody($message->entities[$i], $stream, $length_raw, $boundary); |
1274f430 |
65 | if ($i == $entCount-1) $last = true; |
3b53d87f |
66 | } |
1274f430 |
67 | if ($boundary && $last) { |
68 | $s = "\r\n--".$boundary."--\r\n\r\n"; |
3b53d87f |
69 | $length_raw += strlen($s); |
70 | if ($stream) { |
71 | $this->preWriteToStream($s); |
72 | $this->writeToStream($stream, $s); |
1274f430 |
73 | } else { |
74 | /* DEBUG */ |
75 | $out = htmlspecialchars($s); |
76 | $out = str_replace("\r\n",'<BR>',$out); |
77 | echo $out; |
3b53d87f |
78 | } |
79 | } |
80 | } |
81 | |
82 | function writeBodyPart($message, $stream, &$length) { |
1274f430 |
83 | if ($message->mime_header) { |
84 | $type0 = $message->mime_header->type0; |
85 | } else { |
86 | $type0 = $message->rfc822_header->content_type->type0; |
87 | } |
88 | switch ($type0) { |
3b53d87f |
89 | case 'text': |
90 | case 'message': |
91 | if ($message->body_part) { |
92 | $body_part = $message->body_part; |
93 | $length += $this->clean_crlf($body_part); |
94 | if ($stream) { |
95 | $this->preWriteToStream($body_part); |
59387d1c |
96 | $this->writeToStream($stream, $body_part); |
1274f430 |
97 | } else { |
98 | /* DEBUG */ |
99 | $out = htmlspecialchars($tmp); |
100 | $out = str_replace("\r\n",'<BR>',$out); |
101 | echo $out; |
3b53d87f |
102 | } |
103 | } elseif ($message->att_local_name) { |
104 | $filename = $message->att_local_name; |
105 | $file = fopen ($filename, 'rb'); |
106 | while ($tmp = fgets($file, 4096)) { |
107 | $length += $this->clean_crlf($tmp); |
108 | if ($stream) { |
109 | $this->preWriteToStream($tmp); |
110 | $this->writeToStream($stream, $tmp); |
59387d1c |
111 | } else { |
112 | /* DEBUG */ |
113 | $out = htmlspecialchars($tmp); |
114 | $out = str_replace("\r\n",'<BR>',$out); |
115 | echo $out; |
116 | } |
3b53d87f |
117 | } |
118 | fclose($file); |
119 | } |
120 | break; |
121 | default: |
122 | if ($message->body_part) { |
123 | $body_part = $message->body_part; |
124 | $length += $this->clean_crlf($body_part); |
125 | if ($stream) { |
59387d1c |
126 | $this->writeToStream($stream, $body_part); |
127 | } else { |
128 | /* DEBUG */ |
129 | $out = htmlspecialchars($body_part); |
130 | $out = str_replace("\r\n",'<BR>',$out); |
131 | echo $out; |
132 | } |
3b53d87f |
133 | } elseif ($message->att_local_name) { |
134 | $filename = $message->att_local_name; |
135 | $file = fopen ($filename, 'rb'); |
1274f430 |
136 | while ($tmp = fread($file, 570)) { |
3b53d87f |
137 | $encoded = chunk_split(base64_encode($tmp)); |
1274f430 |
138 | |
3b53d87f |
139 | if ($stream) { |
140 | $this->writeToStream($stream, $encoded); |
59387d1c |
141 | } else { |
142 | /* DEBUG */ |
143 | $out = htmlspecialchars($encoded); |
144 | $out = str_replace("\r\n",'<BR>',$out); |
145 | echo $out; |
3b53d87f |
146 | } |
147 | } |
148 | fclose($file); |
149 | } |
150 | break; |
151 | } |
1274f430 |
152 | $body_part_trailing = "\r\n"; |
153 | $length += strlen($body_part_trailing); |
154 | if ($stream) { |
155 | $this->preWriteToStream($body_part_trailing); |
156 | $this->writeToStream($stream, $body_part_trailing); |
157 | } else { |
158 | /* DEBUG */ |
159 | $out = htmlspecialchars($body_part_trailing); |
160 | $out = str_replace("\r\n",'<BR>',$out); |
161 | echo $out; |
162 | } |
3b53d87f |
163 | } |
164 | |
69298c28 |
165 | function clean_crlf(&$s) { |
3b53d87f |
166 | $s = str_replace("\r\n", "\n", $s); |
167 | $s = str_replace("\r", "\n", $s); |
168 | $s = str_replace("\n", "\r\n", $s); |
169 | return strlen($s); |
170 | } |
171 | |
69298c28 |
172 | function preWriteToStream(&$s) { |
3b53d87f |
173 | } |
174 | |
175 | function writeToStream($stream, $data) { |
1274f430 |
176 | fputs($stream, $data); |
3b53d87f |
177 | } |
178 | |
179 | function initStream($message, $length=0, $host='', $port='', $user='', $pass='') { |
180 | return $stream; |
181 | } |
59387d1c |
182 | |
183 | function getBcc($bcc) { |
184 | return false; |
185 | } |
3b53d87f |
186 | |
187 | function prepareMIME_Header($message, $boundary) { |
59387d1c |
188 | $mime_header = $message->mime_header; |
189 | $rn="\r\n"; |
4960ec8e |
190 | $header = array(); |
3b53d87f |
191 | |
59387d1c |
192 | $contenttype = 'Content-Type: '. $mime_header->type0 .'/'. |
193 | $mime_header->type1; |
3b53d87f |
194 | if (count($message->entities)) { |
195 | $contenttype .= ";\r\n " . 'boundary="'.$boundary.'"'; |
196 | } |
4960ec8e |
197 | if (isset($mime_header->parameters['name'])) { |
1274f430 |
198 | $contenttype .= '; name="'. |
4960ec8e |
199 | encodeHeader($mime_header->parameters['name']). '"'; |
200 | } |
1274f430 |
201 | if (isset($mime_header->parameters['charset'])) { |
202 | $charset = $mime_header->parameters['charset']; |
203 | $contenttype .= '; charset="'. |
204 | encodeHeader($charset). '"'; |
205 | } |
206 | |
207 | |
4960ec8e |
208 | $header[] = $contenttype . $rn; |
209 | if ($mime_header->description) { |
210 | $header[] .= 'Content-Description: ' . $mime_header->description . $rn; |
211 | } |
212 | if ($mime_header->encoding) { |
1274f430 |
213 | $encoding = $mime_header->encoding; |
4960ec8e |
214 | $header[] .= 'Content-Transfer-Encoding: ' . $mime_header->encoding . $rn; |
1274f430 |
215 | } else { |
216 | if ($mime_header->type0 == 'text' || $mime_header->type0 == 'message') { |
217 | $header[] .= 'Content-Transfer-Encoding: 8bit' . $rn; |
218 | } else { |
219 | $header[] .= 'Content-Transfer-Encoding: base64' . $rn; |
220 | } |
4960ec8e |
221 | } |
222 | if ($mime_header->id) { |
223 | $header[] .= 'Content-ID: ' . $mime_header->id . $rn; |
224 | } |
225 | if ($mime_header->disposition) { |
1274f430 |
226 | $disposition = $mime_header->disposition; |
227 | $contentdisp = 'Content-Disposition: ' . $disposition->name; |
228 | if ($disposition->getProperty('filename')) { |
229 | $contentdisp .= '; filename="'. |
230 | encodeHeader($disposition->getProperty('filename')). '"'; |
4960ec8e |
231 | } |
232 | $header[] = $contentdisp . $rn; |
233 | } |
234 | if ($mime_header->md5) { |
235 | $header[] .= 'Content-MD5: ' . $mime_header->md5 . $rn; |
236 | } |
237 | if ($mime_header->language) { |
238 | $header[] .= 'Content-Language: ' . $mime_header->language . $rn; |
239 | } |
240 | |
241 | $cnt = count($header); |
242 | $hdr_s = ''; |
243 | for ($i = 0 ; $i < $cnt ; $i++) { |
59387d1c |
244 | $hdr_s .= $this->foldLine($header[$i], 78, ' '); |
4960ec8e |
245 | } |
246 | $header = $hdr_s; |
3b53d87f |
247 | $header .= $rn; /* One blank line to separate mimeheader and body-entity */ |
248 | return $header; |
4960ec8e |
249 | } |
250 | |
1274f430 |
251 | function prepareRFC822_Header($rfc822_header, $reply_rfc822_header) { |
4960ec8e |
252 | global $REMOTE_ADDR, $SERVER_NAME, $REMOTE_PORT; |
253 | global $version, $useSendmail, $username; |
254 | global $HTTP_VIA, $HTTP_X_FORWARDED_FOR; |
255 | global $REMOTE_HOST; |
59387d1c |
256 | $rn = "\r\n"; |
4960ec8e |
257 | /* This creates an RFC 822 date */ |
59387d1c |
258 | $date = date("D, j M Y H:i:s ", mktime()) . $this->timezone(); |
4960ec8e |
259 | /* Create a message-id */ |
260 | $message_id = '<' . $REMOTE_PORT . '.' . $REMOTE_ADDR . '.'; |
261 | $message_id .= time() . '.squirrel@' . $SERVER_NAME .'>'; |
262 | /* Make an RFC822 Received: line */ |
59387d1c |
263 | if (isset($REMOTE_HOST)) { |
264 | $received_from = "$REMOTE_HOST ([$REMOTE_ADDR])"; |
265 | } else { |
266 | $received_from = $REMOTE_ADDR; |
267 | } |
268 | if (isset($HTTP_VIA) || isset ($HTTP_X_FORWARDED_FOR)) { |
269 | if ($HTTP_X_FORWARDED_FOR == '') { |
270 | $HTTP_X_FORWARDED_FOR = 'unknown'; |
271 | } |
272 | $received_from .= " (proxying for $HTTP_X_FORWARDED_FOR)"; |
273 | } |
274 | $header = array(); |
275 | $header[] = "Received: from $received_from" . $rn; |
276 | $header[] = " (SquirrelMail authenticated user $username)" . $rn; |
277 | $header[] = " by $SERVER_NAME with HTTP;" . $rn; |
278 | $header[] = " $date" . $rn; |
279 | /* Insert the rest of the header fields */ |
280 | $header[] = 'Message-ID: '. $message_id . $rn; |
1274f430 |
281 | if ($reply_rfc822_header->message_id) { |
282 | $header[] = 'In-Reply-To: '.$reply_rfc822_header->message_id . $rn; |
283 | $references = $this->calculate_references($reply_rfc822_header); |
59387d1c |
284 | $header[] = 'References: '.$references . $rn; |
285 | } |
286 | $header[] = "Date: $date" . $rn; |
287 | $header[] = 'Subject: '.encodeHeader($rfc822_header->subject) . $rn; |
288 | $header[] = 'From: '. encodeHeader($rfc822_header->getAddr_s('from')) . $rn; |
289 | /* RFC2822 if from contains more then 1 address */ |
290 | if (count($rfc822_header->from) > 1) { |
291 | $header[] = 'Sender: '. encodeHeader($rfc822_header->getAddr_s('sender')) . $rn; |
292 | } |
293 | $header[] = 'To: '. encodeHeader($rfc822_header->getAddr_s('to')) . $rn; // Who it's TO |
294 | if (count($rfc822_header->cc)) { |
295 | $header[] = 'Cc: '. encodeHeader($rfc822_header->getAddr_s('cc')) . $rn; |
296 | } |
297 | if (count($rfc822_header->reply_to)) { |
298 | $header[] = 'Reply-To: '. encodeHeader($rfc822_header->getAddr_s('reply_to')) . $rn; |
299 | } |
300 | /* Sendmail should return true. Default = false */ |
1274f430 |
301 | $bcc = $this->getBcc(); |
59387d1c |
302 | if ($bcc && count($rfc822_header->bcc)) { |
303 | $header[] = 'Bcc: '. encodeHeader($rfc822_header->getAddr_s('bcc')) . $rn; |
304 | } |
305 | /* Identify SquirrelMail */ |
306 | $header[] = "X-Mailer: SquirrelMail (version $version)" . $rn; |
307 | /* Do the MIME-stuff */ |
308 | $header[] = "MIME-Version: 1.0" . $rn; |
309 | $contenttype = 'Content-Type: '. $rfc822_header->content_type->type0 .'/'. |
310 | $rfc822_header->content_type->type1; |
311 | if (count($rfc822_header->content_type->properties)) { |
1274f430 |
312 | foreach ($rfc822_header->content_type->properties as $k => $v) { |
313 | if ($k && $v) { |
314 | $contenttype .= ';' .$k.'='.$v; |
315 | } |
59387d1c |
316 | } |
317 | } |
318 | $header[] = $contenttype . $rn; |
319 | if ($rfc822_header->dnt) { |
320 | $dnt = $rfc822_header->getAddr_s('dnt'); |
321 | /* Pegasus Mail */ |
322 | $header[] = 'X-Confirm-Reading-To: '.$dnt. $rn; |
323 | /* RFC 2298 */ |
324 | $header[] = 'Disposition-Notification-To: '.$dnt. $rn; |
325 | } |
326 | if ($rfc822_header->priority) { |
327 | $prio = $rfc822_header->priority; |
328 | $header[] = 'X-Priority: '.$prio. $rn; |
329 | switch($prio) { |
330 | case 1: $header[] = 'Importance: High'. $rn; break; |
331 | case 3: $header[] = 'Importance: Normal'. $rn; break; |
332 | case 5: $header[] = 'Importance: Low'. $rn; break; |
333 | default: break; |
334 | } |
335 | } |
336 | /* Insert headers from the $more_headers array */ |
337 | if(count($rfc822_header->more_headers)) { |
338 | reset($rfc822_header->more_headers); |
339 | foreach ($rfc822_header->more_headers as $k => $v) { |
340 | $header[] = $k.': '.$v .$rn; |
341 | } |
342 | } |
343 | $cnt = count($header); |
344 | $hdr_s = ''; |
345 | for ($i = 0 ; $i < $cnt ; $i++) { |
346 | $hdr_s .= $this->foldLine($header[$i], 78, ' '); |
347 | } |
348 | $header = $hdr_s; |
349 | $header .= $rn; /* One blank line to separate header and body */ |
350 | return $header; |
4960ec8e |
351 | } |
352 | |
353 | /* |
354 | * function for cleanly folding of headerlines |
355 | */ |
1274f430 |
356 | function foldLine($line, $length, $pre='') { |
357 | $cnt = strlen($line); |
358 | $res = ''; |
359 | if ($cnt > $length) { |
360 | $fold_string = "\r\n " . $pre; |
361 | $length -=strlen($fold_string); |
362 | for ($i=0;$i<($cnt-$length);$i++) { |
363 | $fold_pos = 0; |
364 | /* first try to fold at delimiters */ |
365 | for ($j=($i+$length); $j>$i; --$j) { |
366 | switch ($line{$j}) { |
367 | case (','): |
368 | case (';'): $fold_pos = $i = $j; break; |
369 | default: break; |
370 | } |
4960ec8e |
371 | } |
1274f430 |
372 | if (!$fold_pos) { /* not succeed yet so we try at spaces & = */ |
373 | for ($j=($i+$length); $j>$i; $j--) { |
374 | switch ($line{$j}) { |
375 | case (' '): |
376 | case ('='): $fold_pos = $i = $j; break; |
377 | default: break; |
378 | } |
379 | } |
380 | } |
381 | if (!$fold_pos) { /* clean folding didn't work */ |
382 | $i = $j = $fold_pos = $i+$length; |
383 | } |
384 | $line = substr_replace($line,$line{$fold_pos}.$fold_string, |
385 | $fold_pos,1); |
386 | $cnt += strlen($fold_string); |
387 | $i = $j + strlen($fold_string); |
388 | } |
389 | } |
390 | return $line; |
4960ec8e |
391 | } |
3b53d87f |
392 | |
3b53d87f |
393 | |
59387d1c |
394 | function mimeBoundary () { |
395 | static $mimeBoundaryString; |
3b53d87f |
396 | |
59387d1c |
397 | if ( !isset( $mimeBoundaryString ) || |
398 | $mimeBoundaryString == '') { |
399 | $mimeBoundaryString = '----=_' . date( 'YmdHis' ) . '_' . |
400 | mt_rand( 10000, 99999 ); |
401 | } |
402 | return $mimeBoundaryString; |
3b53d87f |
403 | } |
404 | |
59387d1c |
405 | /* Time offset for correct timezone */ |
406 | function timezone () { |
407 | global $invert_time; |
3b53d87f |
408 | |
59387d1c |
409 | $diff_second = date('Z'); |
410 | if ($invert_time) { |
411 | $diff_second = - $diff_second; |
412 | } |
413 | if ($diff_second > 0) { |
414 | $sign = '+'; |
415 | } else { |
416 | $sign = '-'; |
417 | } |
418 | $diff_second = abs($diff_second); |
419 | $diff_hour = floor ($diff_second / 3600); |
420 | $diff_minute = floor (($diff_second-3600*$diff_hour) / 60); |
421 | $zonename = '('.strftime('%Z').')'; |
422 | $result = sprintf ("%s%02d%02d %s", $sign, $diff_hour, $diff_minute, |
3b53d87f |
423 | $zonename); |
59387d1c |
424 | return ($result); |
425 | } |
3b53d87f |
426 | |
1274f430 |
427 | function calculate_references($hdr) { |
428 | $refer = $hdr->references; |
59387d1c |
429 | if (strlen($refer) > 2) { |
1274f430 |
430 | $refer .= ' ' . $hdr->message_id; |
59387d1c |
431 | } else { |
1274f430 |
432 | if ($hdr->in_reply_to) { |
433 | $refer .= $hdr->in_reply_to . ' ' . $hdr->message_id; |
59387d1c |
434 | } else { |
1274f430 |
435 | $refer .= $hdr->message_id; |
59387d1c |
436 | } |
437 | } |
438 | trim($refer); |
439 | return $refer; |
440 | } |
441 | } |
4960ec8e |
442 | ?> |