]> git.donarmstrong.com Git - roundcube.git/blob - program/steps/mail/func.inc
Imported Upstream version 0.1
[roundcube.git] / program / steps / mail / func.inc
1 <?php
2
3 /*
4  +-----------------------------------------------------------------------+
5  | program/steps/mail/func.inc                                           |
6  |                                                                       |
7  | This file is part of the RoundCube Webmail client                     |
8  | Copyright (C) 2005-2007, RoundCube Dev. - Switzerland                 |
9  | Licensed under the GNU GPL                                            |
10  |                                                                       |
11  | PURPOSE:                                                              |
12  |   Provide webmail functionality and GUI objects                       |
13  |                                                                       |
14  +-----------------------------------------------------------------------+
15  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
16  +-----------------------------------------------------------------------+
17
18  $Id: func.inc 1125 2008-02-20 22:47:06Z thomasb $
19
20 */
21
22 require_once('lib/html2text.inc');
23 require_once('lib/enriched.inc');
24 require_once('include/rcube_smtp.inc');
25
26
27 $EMAIL_ADDRESS_PATTERN = '/([a-z0-9][a-z0-9\-\.\+\_]*@[a-z0-9]([a-z0-9\-][.]?)*[a-z0-9]\\.[a-z]{2,5})/i';
28
29 if (empty($_SESSION['mbox']))
30   $_SESSION['mbox'] = $IMAP->get_mailbox_name();
31
32 // set imap properties and session vars
33 if ($mbox = get_input_value('_mbox', RCUBE_INPUT_GPC))
34   $IMAP->set_mailbox(($_SESSION['mbox'] = $mbox));
35
36 if (!empty($_GET['_page']))
37   $IMAP->set_page(($_SESSION['page'] = intval($_GET['_page'])));
38
39 // set mailbox to INBOX if not set
40 if (empty($_SESSION['mbox']))
41   $_SESSION['mbox'] = $IMAP->get_mailbox_name();
42
43 // set default sort col/order to session
44 if (!isset($_SESSION['sort_col']))
45   $_SESSION['sort_col'] = $CONFIG['message_sort_col'];
46 if (!isset($_SESSION['sort_order']))
47   $_SESSION['sort_order'] = $CONFIG['message_sort_order'];
48
49 // set message set for search result
50 if (!empty($_REQUEST['_search']) && isset($_SESSION['search'][$_REQUEST['_search']]))
51   {
52   $IMAP->set_search_set($_SESSION['search'][$_REQUEST['_search']]);
53   $OUTPUT->set_env('search_request', $_REQUEST['_search']);
54   $OUTPUT->set_env('search_text', $_SESSION['last_text_search']);
55   }
56
57
58 // define url for getting message parts
59 if (strlen($_GET['_uid']))
60   $GET_URL = rcmail_url('get', array('_mbox'=>$IMAP->get_mailbox_name(), '_uid'=>get_input_value('_uid', RCUBE_INPUT_GET)));
61
62
63 // set current mailbox in client environment
64 $OUTPUT->set_env('mailbox', $IMAP->get_mailbox_name());
65 $OUTPUT->set_env('quota', $IMAP->get_capability('quota'));
66
67 if ($CONFIG['trash_mbox'])
68   $OUTPUT->set_env('trash_mailbox', $CONFIG['trash_mbox']);
69 if ($CONFIG['drafts_mbox'])
70   $OUTPUT->set_env('drafts_mailbox', $CONFIG['drafts_mbox']);
71 if ($CONFIG['junk_mbox'])
72   $OUTPUT->set_env('junk_mailbox', $CONFIG['junk_mbox']);
73
74 if (!$OUTPUT->ajax_call)
75   rcube_add_label('checkingmail', 'deletemessage', 'movemessagetotrash');
76
77 // set page title
78 if (empty($_action) || $_action == 'list')
79   $OUTPUT->set_pagetitle(rcmail_localize_foldername($IMAP->get_mailbox_name()));
80
81
82
83 // return the message list as HTML table
84 function rcmail_message_list($attrib)
85   {
86   global $IMAP, $CONFIG, $COMM_PATH, $OUTPUT;
87
88   $skin_path = $CONFIG['skin_path'];
89   $image_tag = '<img src="%s%s" alt="%s" border="0" />';
90
91   // check to see if we have some settings for sorting
92   $sort_col   = $_SESSION['sort_col'];
93   $sort_order = $_SESSION['sort_order'];
94   
95   // add some labels to client
96   rcube_add_label('from', 'to');
97
98   // get message headers
99   $a_headers = $IMAP->list_headers('', '', $sort_col, $sort_order);
100
101   // add id to message list table if not specified
102   if (!strlen($attrib['id']))
103     $attrib['id'] = 'rcubemessagelist';
104
105   // allow the following attributes to be added to the <table> tag
106   $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id', 'cellpadding', 'cellspacing', 'border', 'summary'));
107
108   $out = '<table' . $attrib_str . ">\n";
109
110
111   // define list of cols to be displayed
112   $a_show_cols = is_array($CONFIG['list_cols']) ? $CONFIG['list_cols'] : array('subject');
113   $a_sort_cols = array('subject', 'date', 'from', 'to', 'size');
114
115   $mbox = $IMAP->get_mailbox_name();
116   
117   // show 'to' instead of from in sent messages
118   if (($mbox==$CONFIG['sent_mbox'] || $mbox==$CONFIG['drafts_mbox']) && ($f = array_search('from', $a_show_cols))
119       && !array_search('to', $a_show_cols))
120     $a_show_cols[$f] = 'to';
121   
122   // add col definition
123   $out .= '<colgroup>';
124   $out .= '<col class="icon" />';
125
126   foreach ($a_show_cols as $col)
127     $out .= sprintf('<col class="%s" />', $col);
128
129   $out .= '<col class="icon" />';
130   $out .= "</colgroup>\n";
131
132   // add table title
133   $out .= "<thead><tr>\n<td class=\"icon\">&nbsp;</td>\n";
134
135   $javascript = '';
136   foreach ($a_show_cols as $col)
137     {
138     // get column name
139     $col_name = Q(rcube_label($col));
140
141     // make sort links
142     $sort = '';
143     if ($IMAP->get_capability('sort') && in_array($col, $a_sort_cols))
144       {
145       // have buttons configured
146       if (!empty($attrib['sortdescbutton']) || !empty($attrib['sortascbutton']))
147         {
148         $sort = '&nbsp;&nbsp;';
149
150         // asc link
151         if (!empty($attrib['sortascbutton']))
152           {
153           $sort .= $OUTPUT->button(array(
154             'command' => 'sort',
155             'prop' => $col.'_ASC',
156             'image' => $attrib['sortascbutton'],
157             'align' => 'absmiddle',
158             'title' => 'sortasc'));
159           }       
160         
161         // desc link
162         if (!empty($attrib['sortdescbutton']))
163           {
164           $sort .= $OUTPUT->button(array(
165             'command' => 'sort',
166             'prop' => $col.'_DESC',
167             'image' => $attrib['sortdescbutton'],
168             'align' => 'absmiddle',
169             'title' => 'sortdesc'));
170           }
171         }
172       // just add a link tag to the header
173       else
174         {
175         $col_name = sprintf(
176           '<a href="./#sort" onclick="return %s.command(\'sort\',\'%s\',this)" title="%s">%s</a>',
177           JS_OBJECT_NAME,
178           $col,
179           rcube_label('sortby'),
180           $col_name);
181         }
182       }
183       
184     $sort_class = $col==$sort_col ? " sorted$sort_order" : '';
185
186     // put it all together
187     $out .= '<td class="'.$col.$sort_class.'" id="rcmHead'.$col.'">' . "$col_name$sort</td>\n";    
188     }
189
190   $out .= '<td class="icon">'.($attrib['attachmenticon'] ? sprintf($image_tag, $skin_path, $attrib['attachmenticon'], '') : '')."</td>\n";
191   $out .= "</tr></thead>\n<tbody>\n";
192
193   // no messages in this mailbox
194   if (!sizeof($a_headers))
195     $OUTPUT->show_message('nomessagesfound', 'notice');
196
197
198   $a_js_message_arr = array();
199
200   // create row for each message
201   foreach ($a_headers as $i => $header)  //while (list($i, $header) = each($a_headers))
202     {
203     $message_icon = $attach_icon = '';
204     $js_row_arr = array();
205     $zebra_class = $i%2 ? 'even' : 'odd';
206
207     // set messag attributes to javascript array
208     if ($header->deleted)
209       $js_row_arr['deleted'] = true;
210     if (!$header->seen)
211       $js_row_arr['unread'] = true;
212     if ($header->answered)
213       $js_row_arr['replied'] = true;
214     // set message icon  
215     if ($attrib['deletedicon'] && $header->deleted)
216       $message_icon = $attrib['deletedicon'];
217     else if ($attrib['unreadicon'] && !$header->seen)
218       $message_icon = $attrib['unreadicon'];
219     else if ($attrib['repliedicon'] && $header->answered)
220       $message_icon = $attrib['repliedicon'];
221     else if ($attrib['messageicon'])
222       $message_icon = $attrib['messageicon'];
223     
224     // set attachment icon
225     if ($attrib['attachmenticon'] && preg_match("/multipart\/[mr]/i", $header->ctype))
226       $attach_icon = $attrib['attachmenticon'];
227         
228     $out .= sprintf('<tr id="rcmrow%d" class="message%s%s %s">'."\n",
229                     $header->uid,
230                     $header->seen ? '' : ' unread',
231                     $header->deleted ? ' deleted' : '',
232                     $zebra_class);    
233     
234     $out .= sprintf("<td class=\"icon\">%s</td>\n", $message_icon ? sprintf($image_tag, $skin_path, $message_icon, '') : '');
235     
236     // format each col
237     foreach ($a_show_cols as $col)
238       {
239       if ($col=='from' || $col=='to')
240         $cont = Q(rcmail_address_string($header->$col, 3, $attrib['addicon']), 'show');
241       else if ($col=='subject')
242         {
243         $action = $mbox==$CONFIG['drafts_mbox'] ? 'compose' : 'show';
244         $uid_param = $mbox==$CONFIG['drafts_mbox'] ? '_draf_uid' : '_uid';
245         $cont = Q(rcube_imap::decode_mime_string($header->$col, $header->charset));
246         if (empty($cont)) $cont = Q(rcube_label('nosubject'));
247         $cont = sprintf('<a href="%s" onclick="return rcube_event.cancel(event)">%s</a>', Q(rcmail_url($action, array($uid_param=>$header->uid, '_mbox'=>$mbox))), $cont);
248         }
249       else if ($col=='size')
250         $cont = show_bytes($header->$col);
251       else if ($col=='date')
252         $cont = format_date($header->date);
253       else
254         $cont = Q($header->$col);
255         
256       $out .= '<td class="'.$col.'">' . $cont . "</td>\n";
257       }
258
259     $out .= sprintf("<td class=\"icon\">%s</td>\n", $attach_icon ? sprintf($image_tag, $skin_path, $attach_icon, '') : '');
260     $out .= "</tr>\n";
261     
262     if (sizeof($js_row_arr))
263       $a_js_message_arr[$header->uid] = $js_row_arr;
264     }
265   
266   // complete message table
267   $out .= "</tbody></table>\n";
268   
269   
270   $message_count = $IMAP->messagecount();
271   
272   // set client env
273   $OUTPUT->add_gui_object('mailcontframe', 'mailcontframe');
274   $OUTPUT->add_gui_object('messagelist', $attrib['id']);
275   $OUTPUT->set_env('messagecount', $message_count);
276   $OUTPUT->set_env('current_page', $IMAP->list_page);
277   $OUTPUT->set_env('pagecount', ceil($message_count/$IMAP->page_size));
278   $OUTPUT->set_env('sort_col', $sort_col);
279   $OUTPUT->set_env('sort_order', $sort_order);
280   
281   if ($attrib['messageicon'])
282     $OUTPUT->set_env('messageicon', $skin_path . $attrib['messageicon']);
283   if ($attrib['deletedicon'])
284     $OUTPUT->set_env('deletedicon', $skin_path . $attrib['deletedicon']);
285   if ($attrib['unreadicon'])
286     $OUTPUT->set_env('unreadicon', $skin_path . $attrib['unreadicon']);
287   if ($attrib['repliedicon'])
288     $OUTPUT->set_env('repliedicon', $skin_path . $attrib['repliedicon']);
289   if ($attrib['attachmenticon'])
290     $OUTPUT->set_env('attachmenticon', $skin_path . $attrib['attachmenticon']);
291   
292   $OUTPUT->set_env('messages', $a_js_message_arr);
293   $OUTPUT->set_env('coltypes', $a_show_cols);
294   
295   $OUTPUT->include_script('list.js');
296   
297   return $out;
298   }
299
300
301 // return javascript commands to add rows to the message list
302 function rcmail_js_message_list($a_headers, $insert_top=FALSE)
303   {
304   global $CONFIG, $IMAP, $OUTPUT;
305
306   $a_show_cols = is_array($CONFIG['list_cols']) ? $CONFIG['list_cols'] : array('subject');
307   $mbox = $IMAP->get_mailbox_name();
308
309   // show 'to' instead of from in sent messages
310   if (($mbox == $CONFIG['sent_mbox'] || $mbox == $CONFIG['drafts_mbox'])
311       && (($f = array_search('from', $a_show_cols)) !== false) && array_search('to', $a_show_cols) === false)
312     $a_show_cols[$f] = 'to';
313
314   $OUTPUT->command('set_message_coltypes', $a_show_cols);
315
316   // loop through message headers
317   foreach ($a_headers as $n => $header)
318     {
319     $a_msg_cols = array();
320     $a_msg_flags = array();
321     
322     if (empty($header))
323       continue;
324
325     // format each col; similar as in rcmail_message_list()
326     foreach ($a_show_cols as $col)
327       {
328       if ($col=='from' || $col=='to')
329         $cont = Q(rcmail_address_string($header->$col, 3), 'show');
330       else if ($col=='subject')
331         {
332         $action = $mbox==$CONFIG['drafts_mbox'] ? 'compose' : 'show';
333         $uid_param = $mbox==$CONFIG['drafts_mbox'] ? '_draf_uid' : '_uid';
334         $cont = Q(rcube_imap::decode_mime_string($header->$col, $header->charset));
335         if (!$cont) $cont = Q(rcube_label('nosubject'));
336         $cont = sprintf('<a href="%s" onclick="return rcube_event.cancel(event)">%s</a>', Q(rcmail_url($action, array($uid_param=>$header->uid, '_mbox'=>$mbox))), $cont);
337         }
338       else if ($col=='size')
339         $cont = show_bytes($header->$col);
340       else if ($col=='date')
341         $cont = format_date($header->date);
342       else
343         $cont = Q($header->$col);
344           
345       $a_msg_cols[$col] = $cont;
346       }
347
348     $a_msg_flags['deleted'] = $header->deleted ? 1 : 0;
349     $a_msg_flags['unread'] = $header->seen ? 0 : 1;
350     $a_msg_flags['replied'] = $header->answered ? 1 : 0;
351     $OUTPUT->command('add_message_row',
352       $header->uid,
353       $a_msg_cols,
354       $a_msg_flags,
355       preg_match("/multipart\/m/i", $header->ctype),
356       $insert_top);
357     }
358   }
359
360
361 // return an HTML iframe for loading mail content
362 function rcmail_messagecontent_frame($attrib)
363   {
364   global $OUTPUT;
365   
366   if (empty($attrib['id']))
367     $attrib['id'] = 'rcmailcontentwindow';
368
369   // allow the following attributes to be added to the <iframe> tag
370   $attrib_str = create_attrib_string($attrib, array('id', 'class', 'style', 'src', 'width', 'height', 'frameborder'));
371   $framename = $attrib['id'];
372
373   $out = sprintf('<iframe name="%s"%s></iframe>'."\n",
374          $framename,
375          $attrib_str);
376
377   $OUTPUT->set_env('contentframe', $framename);
378   $OUTPUT->set_env('blankpage', $attrib['src'] ? $OUTPUT->abs_url($attrib['src']) : 'program/blank.gif');
379
380   return $out;
381   }
382
383
384 function rcmail_messagecount_display($attrib)
385   {
386   global $IMAP, $OUTPUT;
387   
388   if (!$attrib['id'])
389     $attrib['id'] = 'rcmcountdisplay';
390
391   $OUTPUT->add_gui_object('countdisplay', $attrib['id']);
392
393   // allow the following attributes to be added to the <span> tag
394   $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id'));
395
396   
397   $out = '<span' . $attrib_str . '>';
398   $out .= rcmail_get_messagecount_text();
399   $out .= '</span>';
400   return $out;
401   }
402
403
404 function rcmail_quota_display($attrib)
405   {
406   global $OUTPUT, $COMM_PATH;
407
408   if (!$attrib['id'])
409     $attrib['id'] = 'rcmquotadisplay';
410
411   $OUTPUT->add_gui_object('quotadisplay', $attrib['id']);
412
413   // allow the following attributes to be added to the <span> tag
414   $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id'));
415
416   $out = '<span' . $attrib_str . '>';
417   $out .= rcmail_quota_content($attrib['display']);
418   $out .= '</span>';
419   return $out;
420   }
421
422
423 function rcmail_quota_content($display)
424   {
425   global $IMAP, $COMM_PATH;
426
427   if (!$IMAP->get_capability('QUOTA'))
428     $quota_text = rcube_label('unknown');
429   else if ($quota = $IMAP->get_quota())
430     {
431     $quota_text = sprintf("%s / %s (%.0f%%)",
432                           show_bytes($quota["used"] * 1024),
433                           show_bytes($quota["total"] * 1024),
434                           $quota["percent"]);
435
436     // show quota as image (by Brett Patterson)
437     if ($display == 'image' && function_exists('imagegif'))
438       {
439       $attrib = array('width' => 100, 'height' => 14);
440       $quota_text = sprintf('<img src="./bin/quotaimg.php?u=%s&amp;q=%d&amp;w=%d&amp;h=%d" width="%d" height="%d" alt="%s" title="%s / %s" />',
441                             $quota['used'], $quota['total'],
442                             $attrib['width'], $attrib['height'],
443                             $attrib['width'], $attrib['height'],
444                             $quota_text,
445                             show_bytes($quota["used"] * 1024),
446                             show_bytes($quota["total"] * 1024));
447       }
448     }
449   else
450     $quota_text = rcube_label('unlimited');
451
452   return $quota_text;
453   }
454
455
456 function rcmail_get_messagecount_text($count=NULL, $page=NULL)
457   {
458   global $IMAP, $MESSAGE;
459   
460   if (isset($MESSAGE['index']))
461     {
462     return rcube_label(array('name' => 'messagenrof',
463                              'vars' => array('nr'  => $MESSAGE['index']+1,
464                                              'count' => $count!==NULL ? $count : $IMAP->messagecount())));
465     }
466
467   if ($page===NULL)
468     $page = $IMAP->list_page;
469     
470   $start_msg = ($page-1) * $IMAP->page_size + 1;
471   $max = $count!==NULL ? $count : $IMAP->messagecount();
472
473   if ($max==0)
474     $out = rcube_label('mailboxempty');
475   else
476     $out = rcube_label(array('name' => 'messagesfromto',
477                               'vars' => array('from'  => $start_msg,
478                                               'to'    => min($max, $start_msg + $IMAP->page_size - 1),
479                                               'count' => $max)));
480
481   return Q($out);
482   }
483
484
485 /* Stolen from Squirrelmail */
486 function sq_deent(&$attvalue, $regex, $hex=false)
487   {
488   $ret_match = false;
489   preg_match_all($regex, $attvalue, $matches);
490   if (is_array($matches) && sizeof($matches[0]) > 0)
491     {
492     $repl = Array();
493     for ($i = 0; $i < sizeof($matches[0]); $i++)
494       {
495       $numval = $matches[1][$i];
496       if ($hex)
497         $numval = hexdec($numval);
498       $repl{$matches[0][$i]} = chr($numval);
499       }
500     $attvalue = strtr($attvalue, $repl);
501     return true;
502     }
503   else
504     return false;
505   }
506
507
508 /* Stolen verbatim from Squirrelmail */
509 function sq_defang(&$attvalue)
510   {
511   /* Skip this if there aren't ampersands or backslashes. */
512   if ((strpos($attvalue, '&') === false) &&
513       (strpos($attvalue, '\\') === false))
514     return;
515   $m = false;
516   do
517     {
518     $m = false;
519     $m = $m || sq_deent($attvalue, '/\&#0*(\d+);*/s');
520     $m = $m || sq_deent($attvalue, '/\&#x0*((\d|[a-f])+);*/si', true);
521     $m = $m || sq_deent($attvalue, '/\\\\(\d+)/s', true);
522     } while ($m == true);
523   $attvalue = stripslashes($attvalue);
524   }
525
526
527 function rcmail_html_filter($html)
528   {
529   preg_match_all('/<\/?\w+((\s+\w+(\s*=\s*(?:".*?"|\'.*?\'|[^\'">\s]+))?)+\s*|\s*)\/?>/', $html, $tags);
530
531   /* From Squirrelmail: Translate all dangerous Unicode or Shift_JIS characters which are accepted by
532    * IE as regular characters. */
533   $replace = array(array('&#x029F;', '&#0671;',  /* L UNICODE IPA Extension */
534                          '&#x0280;', '&#0640;',  /* R UNICODE IPA Extension */
535                          '&#x0274;', '&#0628;',  /* N UNICODE IPA Extension */
536                          '&#xFF25;', '&#65317;', /* Unicode FULLWIDTH LATIN CAPITAL LETTER E */
537                          '&#xFF45;', '&#65349;', /* Unicode FULLWIDTH LATIN SMALL LETTER E */
538                          '&#xFF38;', '&#65336;', /* Unicode FULLWIDTH LATIN CAPITAL LETTER X */
539                          '&#xFF58;', '&#65368;', /* Unicode FULLWIDTH LATIN SMALL LETTER X */
540                          '&#xFF30;', '&#65328;', /* Unicode FULLWIDTH LATIN CAPITAL LETTER P */
541                          '&#xFF50;', '&#65360;', /* Unicode FULLWIDTH LATIN SMALL LETTER P */
542                          '&#xFF32;', '&#65330;', /* Unicode FULLWIDTH LATIN CAPITAL LETTER R */
543                          '&#xFF52;', '&#65362;', /* Unicode FULLWIDTH LATIN SMALL LETTER R */
544                          '&#xFF33;', '&#65331;', /* Unicode FULLWIDTH LATIN CAPITAL LETTER S */
545                          '&#xFF53;', '&#65363;', /* Unicode FULLWIDTH LATIN SMALL LETTER S */
546                          '&#xFF29;', '&#65321;', /* Unicode FULLWIDTH LATIN CAPITAL LETTER I */
547                          '&#xFF49;', '&#65353;', /* Unicode FULLWIDTH LATIN SMALL LETTER I */
548                          '&#xFF2F;', '&#65327;', /* Unicode FULLWIDTH LATIN CAPITAL LETTER O */
549                          '&#xFF4F;', '&#65359;', /* Unicode FULLWIDTH LATIN SMALL LETTER O */
550                          '&#xFF2E;', '&#65326;', /* Unicode FULLWIDTH LATIN CAPITAL LETTER N */
551                          '&#xFF4E;', '&#65358;', /* Unicode FULLWIDTH LATIN SMALL LETTER N */
552                          '&#xFF2C;', '&#65324;', /* Unicode FULLWIDTH LATIN CAPITAL LETTER L */
553                          '&#xFF4C;', '&#65356;', /* Unicode FULLWIDTH LATIN SMALL LETTER L */
554                          '&#xFF35;', '&#65333;', /* Unicode FULLWIDTH LATIN CAPITAL LETTER U */
555                          '&#xFF55;', '&#65365;', /* Unicode FULLWIDTH LATIN SMALL LETTER U */
556                          '&#x207F;', '&#8319;' , /* Unicode SUPERSCRIPT LATIN SMALL LETTER N */
557                          "\xEF\xBC\xA5", /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER E */
558                                          /* in unicode this is some Chinese char range */
559                          "\xEF\xBD\x85", /* Shift JIS FULLWIDTH LATIN SMALL LETTER E */
560                          "\xEF\xBC\xB8", /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER X */
561                          "\xEF\xBD\x98", /* Shift JIS FULLWIDTH LATIN SMALL LETTER X */
562                          "\xEF\xBC\xB0", /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER P */
563                          "\xEF\xBD\x90", /* Shift JIS FULLWIDTH LATIN SMALL LETTER P */
564                          "\xEF\xBC\xB2", /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER R */
565                          "\xEF\xBD\x92", /* Shift JIS FULLWIDTH LATIN SMALL LETTER R */
566                          "\xEF\xBC\xB3", /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER S */
567                          "\xEF\xBD\x93", /* Shift JIS FULLWIDTH LATIN SMALL LETTER S */
568                          "\xEF\xBC\xA9", /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER I */
569                          "\xEF\xBD\x89", /* Shift JIS FULLWIDTH LATIN SMALL LETTER I */
570                          "\xEF\xBC\xAF", /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER O */
571                          "\xEF\xBD\x8F", /* Shift JIS FULLWIDTH LATIN SMALL LETTER O */
572                          "\xEF\xBC\xAE", /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER N */
573                          "\xEF\xBD\x8E", /* Shift JIS FULLWIDTH LATIN SMALL LETTER N */
574                          "\xEF\xBC\xAC", /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER L */
575                          "\xEF\xBD\x8C", /* Shift JIS FULLWIDTH LATIN SMALL LETTER L */
576                          "\xEF\xBC\xB5", /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER U */
577                          "\xEF\xBD\x95", /* Shift JIS FULLWIDTH LATIN SMALL LETTER U */
578                          "\xE2\x81\xBF", /* Shift JIS FULLWIDTH SUPERSCRIPT N */
579                          "\xCA\x9F",   /* L UNICODE IPA Extension */
580                          "\xCA\x80",   /* R UNICODE IPA Extension */
581                          "\xC9\xB4"),  /* N UNICODE IPA Extension */
582                    array('l', 'l', 'r', 'r', 'n', 'n', 'E', 'E', 'e', 'e', 'X', 'X', 'x', 'x',
583                          'P', 'P', 'p', 'p', 'R', 'R', 'r', 'r', 'S', 'S', 's', 's', 'I', 'I',
584                          'i', 'i', 'O', 'O', 'o', 'o', 'N', 'N', 'n', 'n', 'L', 'L', 'l', 'l',
585                          'U', 'U', 'u', 'u', 'n', 'n', 'E', 'e', 'X', 'x', 'P', 'p', 'R', 'r',
586                          'S', 's', 'I', 'i', 'O', 'o', 'N', 'n', 'L', 'l', 'U', 'u', 'n', 'l', 'r', 'n'));
587   if ((count($tags)>3) && (count($tags[3])>0))
588     foreach ($tags[3] as $nr=>$value)
589       {
590       /* Remove comments */
591       $newvalue = preg_replace('/(\/\*.*\*\/)/','$2',$value);
592       /* Translate dangerous characters */
593       $newvalue = str_replace($replace[0], $replace[1], $newvalue);
594       sq_defang($newvalue);
595       /* Rename dangerous CSS */
596       $newvalue = preg_replace('/expression/i', 'idiocy', $newvalue);
597       $newvalue = preg_replace('/url/i', 'idiocy', $newvalue);
598       $newattrs = preg_replace('/'.preg_quote($value, '/').'$/', $newvalue, $tags[1][$nr]);
599       $newtag = preg_replace('/'.preg_quote($tags[1][$nr], '/').'/', $newattrs, $tags[0][$nr]);
600       $html = preg_replace('/'.preg_quote($tags[0][$nr], '/').'/', $newtag, $html);
601       }
602   return $html;
603   }
604
605
606 function rcmail_print_body($part, $safe=FALSE, $plain=FALSE)
607   {
608   global $IMAP, $REMOTE_OBJECTS;
609   
610   $body = is_array($part->replaces) ? strtr($part->body, $part->replaces) : $part->body;
611
612   // convert html to text/plain
613   if ($part->ctype_secondary=='html' && $plain)
614     {
615     $txt = new html2text($body, false, true);
616     $body = $txt->get_text();
617     $part->ctype_secondary = 'plain';
618     }
619     
620   // text/html
621   if ($part->ctype_secondary=='html')
622     {
623     // remove charset specification in HTML message
624     $body = preg_replace('/charset=[a-z0-9\-]+/i', '', $body);
625
626     if (!$safe)  // remove remote images and scripts
627       {
628       $remote_patterns = array('/<img\s+(.*)src=(["\']?)([hftps]{3,5}:\/{2}[^"\'\s]+)(\2|\s|>)/Ui',
629                                '/(src|background)=(["\']?)([hftps]{3,5}:\/{2}[^"\'\s]+)(\2|\s|>)/Ui',
630                                '/(<base.*href=["\']?)([hftps]{3,5}:\/{2}[^"\'\s]+)([^<]*>)/i',
631                                '/(<link.*href=["\']?)([hftps]{3,5}:\/{2}[^"\'\s]+)([^<]*>)/i',
632                                '/url\s*\(["\']?([hftps]{3,5}:\/{2}[^"\'\s]+)["\']?\)/i',
633                                '/url\s*\(["\']?([\.\/]+[^"\'\s]+)["\']?\)/i',
634                                '/<script.+<\/script>/Umis');
635
636       $remote_replaces = array('<img \\1src=\\2./program/blocked.gif\\4',
637                                '',
638                                '',
639                                '',
640                                'none',
641                                'none',
642                                '');
643       
644       // set flag if message containes remote obejcts that where blocked
645       foreach ($remote_patterns as $pattern)
646         {
647         if (preg_match($pattern, $body))
648           {
649           $REMOTE_OBJECTS = TRUE;
650           break;
651           }
652         }
653
654       $body = preg_replace($remote_patterns, $remote_replaces, $body);
655       }
656
657     return Q(rcmail_html_filter($body), 'show', FALSE);
658     }
659
660   // text/enriched
661   if ($part->ctype_secondary=='enriched')
662     {
663     return Q(enriched_to_html($body), 'show');
664     }
665   else
666     {
667     // make links and email-addresses clickable
668     $convert_patterns = $convert_replaces = $replace_strings = array();
669     
670     $url_chars = 'a-z0-9_\-\+\*\$\/&%=@#:;';
671     $url_chars_within = '\?\.~,!';
672
673     $convert_patterns[] = "/([\w]+):\/\/([a-z0-9\-\.]+[a-z]{2,4}([$url_chars$url_chars_within]*[$url_chars])?)/ie";
674     $convert_replaces[] = "rcmail_str_replacement('<a href=\"\\1://\\2\" target=\"_blank\">\\1://\\2</a>', \$replace_strings)";
675
676     $convert_patterns[] = "/([^\/:]|\s)(www\.)([a-z0-9\-]{2,}[a-z]{2,4}([$url_chars$url_chars_within]*[$url_chars])?)/ie";
677     $convert_replaces[] = "rcmail_str_replacement('\\1<a href=\"http://\\2\\3\" target=\"_blank\">\\2\\3</a>', \$replace_strings)";
678     
679     $convert_patterns[] = '/([a-z0-9][a-z0-9\-\.\+\_]*@[a-z0-9]([a-z0-9\-][.]?)*[a-z0-9]\\.[a-z]{2,5})/ie';
680     $convert_replaces[] = "rcmail_str_replacement('<a href=\"mailto:\\1\" onclick=\"return ".JS_OBJECT_NAME.".command(\'compose\',\'\\1\',this)\">\\1</a>', \$replace_strings)";
681     
682     if ($part->ctype_parameters['format'] != 'flowed')
683       $body = wordwrap(trim($body), 80);
684
685     $body = preg_replace($convert_patterns, $convert_replaces, $body);
686
687     // split body into single lines
688     $a_lines = preg_split('/\r?\n/', $body);
689     $quote_level = 0;
690
691     // colorize quoted parts
692     for($n=0; $n<sizeof($a_lines); $n++)
693       {
694       $line = $a_lines[$n];
695       $quotation = '';
696       $q = 0;
697       
698       if (preg_match('/^(>+\s*)/', $line, $regs))
699         {
700         $q = strlen(preg_replace('/\s/', '', $regs[1]));
701         $line = substr($line, strlen($regs[1]));
702
703         if ($q > $quote_level)
704           $quotation = str_repeat('<blockquote>', $q - $quote_level);
705         else if ($q < $quote_level)
706           $quotation = str_repeat("</blockquote>", $quote_level - $q);
707         }
708       else if ($quote_level > 0)
709         $quotation = str_repeat("</blockquote>", $quote_level);
710
711       $quote_level = $q;
712       $a_lines[$n] = $quotation . Q($line, 'replace', FALSE);
713       }
714
715     // insert the links for urls and mailtos
716     $body = preg_replace("/##string_replacement\{([0-9]+)\}##/e", "\$replace_strings[\\1]", join("\n", $a_lines));
717     
718     return "<div class=\"pre\">".$body."\n</div>";
719     }
720   }
721
722
723
724 // add a string to the replacement array and return a replacement string
725 function rcmail_str_replacement($str, &$rep)
726   {
727   static $count = 0;
728   $rep[$count] = stripslashes($str);
729   return "##string_replacement{".($count++)."}##";
730   }
731
732
733 function rcmail_parse_message(&$structure, $arg=array(), $recursive=FALSE)
734   {
735   global $IMAP;
736   static $sa_inline_objects = array();
737
738   // arguments are: (bool)$prefer_html, (string)$get_url
739   extract($arg);
740
741   $a_attachments = array();
742   $a_return_parts = array();
743   $out = '';
744
745   $message_ctype_primary = strtolower($structure->ctype_primary);
746   $message_ctype_secondary = strtolower($structure->ctype_secondary);
747
748   // show message headers
749   if ($recursive && is_array($structure->headers) && isset($structure->headers['subject']))
750     {
751     $c = new stdClass;
752     $c->type = 'headers';
753     $c->headers = &$structure->headers;
754     $a_return_parts[] = $c;
755     }
756
757   // print body if message doesn't have multiple parts
758   if ($message_ctype_primary=='text')
759     {
760     $structure->type = 'content';
761     $a_return_parts[] = &$structure;
762     }
763     
764   // message contains alternative parts
765   else if ($message_ctype_primary=='multipart' && $message_ctype_secondary=='alternative' && is_array($structure->parts))
766     {
767     // get html/plaintext parts
768     $plain_part = $html_part = $print_part = $related_part = NULL;
769     
770     foreach ($structure->parts as $p => $sub_part)
771       {
772       $rel_parts = $attachmnts = null;
773       $sub_ctype_primary = strtolower($sub_part->ctype_primary);
774       $sub_ctype_secondary = strtolower($sub_part->ctype_secondary);
775
776       // check if sub part is 
777       if ($sub_ctype_primary=='text' && $sub_ctype_secondary=='plain')
778         $plain_part = $p;
779       else if ($sub_ctype_primary=='text' && $sub_ctype_secondary=='html')
780         $html_part = $p;
781       else if ($sub_ctype_primary=='text' && $sub_ctype_secondary=='enriched')
782         $enriched_part = $p;
783       else if ($sub_ctype_primary=='multipart' && ($sub_ctype_secondary=='related' || $sub_ctype_secondary=='mixed'))
784         $related_part = $p;
785       }
786       
787     // parse related part (alternative part could be in here)
788     if ($related_part!==NULL)
789     {
790       list($rel_parts, $attachmnts) = rcmail_parse_message($structure->parts[$related_part], $arg, TRUE);
791       $a_attachments = array_merge($a_attachments, $attachmnts);
792     }
793     
794     // merge related parts if any
795     if ($rel_parts && $prefer_html && !$html_part)
796       $a_return_parts = array_merge($a_return_parts, $rel_parts);
797
798     // choose html/plain part to print
799     else if ($html_part!==NULL && $prefer_html)
800       $print_part = &$structure->parts[$html_part];
801     else if ($enriched_part!==NULL)
802       $print_part = &$structure->parts[$enriched_part];
803     else if ($plain_part!==NULL)
804       $print_part = &$structure->parts[$plain_part];
805
806     // show message body
807     if (is_object($print_part))
808       {
809       $print_part->type = 'content';
810       $a_return_parts[] = $print_part;
811       }
812     // show plaintext warning
813     else if ($html_part!==NULL && empty($a_return_parts))
814       {
815       $c = new stdClass;
816       $c->type = 'content';
817       $c->body = rcube_label('htmlmessage');
818       $c->ctype_primary = 'text';
819       $c->ctype_secondary = 'plain';
820       
821       $a_return_parts[] = $c;
822       }
823                                 
824     // add html part as attachment
825     if ($html_part!==NULL && $structure->parts[$html_part]!==$print_part)
826       {
827       $html_part = &$structure->parts[$html_part];
828       $html_part->filename = rcube_label('htmlmessage');
829       $html_part->mimetype = 'text/html';
830       
831       $a_attachments[] = $html_part;
832       }
833     }
834
835   // message contains multiple parts
836   else if (is_array($structure->parts) && !empty($structure->parts))
837     {
838     for ($i=0; $i<count($structure->parts); $i++)
839       {
840       $mail_part = &$structure->parts[$i];
841       $primary_type = strtolower($mail_part->ctype_primary);
842       $secondary_type = strtolower($mail_part->ctype_secondary);
843
844       // multipart/alternative
845       if ($primary_type=='multipart')
846         {
847         list($parts, $attachmnts) = rcmail_parse_message($mail_part, $arg, TRUE);
848
849         $a_return_parts = array_merge($a_return_parts, $parts);
850         $a_attachments = array_merge($a_attachments, $attachmnts);
851         }
852
853       // part text/[plain|html] OR message/delivery-status
854       else if (($primary_type=='text' && ($secondary_type=='plain' || $secondary_type=='html') && $mail_part->disposition!='attachment') ||
855                ($primary_type=='message' && ($secondary_type=='delivery-status' || $secondary_type=='disposition-notification')))
856         {
857         $mail_part->type = 'content';
858         $a_return_parts[] = $mail_part;
859         }
860
861       // part message/*
862       else if ($primary_type=='message')
863         {
864         list($parts, $attachmnts) = rcmail_parse_message($mail_part, $arg, TRUE);
865           
866         $a_return_parts = array_merge($a_return_parts, $parts);
867         $a_attachments = array_merge($a_attachments, $attachmnts);
868         }
869         
870       // ignore "virtual" protocol parts
871       else if ($primary_type=='protocol')
872         continue;
873
874       // part is file/attachment
875       else if ($mail_part->disposition=='attachment' || $mail_part->disposition=='inline' || $mail_part->headers['content-id'] ||
876                (empty($mail_part->disposition) && $mail_part->filename))
877         {
878         // skip apple resource forks
879         if ($message_ctype_secondary=='appledouble' && $secondary_type=='applefile')
880           continue;
881
882         // part belongs to a related message
883         if ($message_ctype_secondary=='related' && $mail_part->headers['content-id'])
884           {
885           $mail_part->content_id = preg_replace(array('/^</', '/>$/'), '', $mail_part->headers['content-id']);
886           $sa_inline_objects[] = $mail_part;
887           }
888         // is regular attachment
889         else
890           {
891           if (!$mail_part->filename)
892             $mail_part->filename = 'Part '.$mail_part->mime_id;
893           $a_attachments[] = $mail_part;
894           }
895         }
896       }
897
898     // if this was a related part try to resolve references
899     if ($message_ctype_secondary=='related' && sizeof($sa_inline_objects))
900       {
901       $a_replaces = array();
902         
903       foreach ($sa_inline_objects as $inline_object)
904         $a_replaces['cid:'.$inline_object->content_id] = htmlspecialchars(sprintf($get_url, $inline_object->mime_id));
905       
906       // add replace array to each content part
907       // (will be applied later when part body is available)
908       for ($i=0; $i<count($a_return_parts); $i++)
909         {
910         if ($a_return_parts[$i]->type=='content')
911           $a_return_parts[$i]->replaces = $a_replaces;
912         }
913       }
914     }
915
916   // message is single part non-text
917   else if ($structure->filename)
918     $a_attachments[] = $structure;
919
920   return array($a_return_parts, $a_attachments);
921   }
922
923
924
925
926 // return table with message headers
927 function rcmail_message_headers($attrib, $headers=NULL)
928   {
929   global $IMAP, $OUTPUT, $MESSAGE;
930   static $sa_attrib;
931   
932   // keep header table attrib
933   if (is_array($attrib) && !$sa_attrib)
934     $sa_attrib = $attrib;
935   else if (!is_array($attrib) && is_array($sa_attrib))
936     $attrib = $sa_attrib;
937   
938   
939   if (!isset($MESSAGE))
940     return FALSE;
941
942   // get associative array of headers object
943   if (!$headers)
944     $headers = is_object($MESSAGE['headers']) ? get_object_vars($MESSAGE['headers']) : $MESSAGE['headers'];
945   
946   $header_count = 0;
947   
948   // allow the following attributes to be added to the <table> tag
949   $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id', 'cellpadding', 'cellspacing', 'border', 'summary'));
950   $out = '<table' . $attrib_str . ">\n";
951
952   // show these headers
953   $standard_headers = array('subject', 'from', 'organization', 'to', 'cc', 'bcc', 'reply-to', 'date');
954   
955   foreach ($standard_headers as $hkey)
956     {
957     if (!$headers[$hkey])
958       continue;
959
960     if ($hkey=='date' && !empty($headers[$hkey]))
961       $header_value = format_date(strtotime($headers[$hkey]));
962     else if (in_array($hkey, array('from', 'to', 'cc', 'bcc', 'reply-to')))
963       $header_value = Q(rcmail_address_string($headers[$hkey], NULL, $attrib['addicon']), 'show');
964     else
965       $header_value = Q(rcube_imap::decode_mime_string($headers[$hkey], $headers['charset']));
966
967     $out .= "\n<tr>\n";
968     $out .= '<td class="header-title">'.Q(rcube_label($hkey)).":&nbsp;</td>\n";
969     $out .= '<td class="'.$hkey.'" width="90%">'.$header_value."</td>\n</tr>";
970     $header_count++;
971     }
972
973   $out .= "\n</table>\n\n";
974
975   return $header_count ? $out : '';  
976   }
977
978
979
980 function rcmail_message_body($attrib)
981   {
982   global $CONFIG, $OUTPUT, $MESSAGE, $IMAP, $GET_URL, $REMOTE_OBJECTS;
983   
984   if (!is_array($MESSAGE['parts']) && !$MESSAGE['body'])
985     return '';
986     
987   if (!$attrib['id'])
988     $attrib['id'] = 'rcmailMsgBody';
989
990   $safe_mode = $MESSAGE['is_safe'] || intval($_GET['_safe']);
991   $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id'));
992   $out = '<div '. $attrib_str . ">\n";
993   
994   $header_attrib = array();
995   foreach ($attrib as $attr => $value)
996     if (preg_match('/^headertable([a-z]+)$/i', $attr, $regs))
997       $header_attrib[$regs[1]] = $value;
998
999
1000   // this is an ecrypted message
1001   // -> create a plaintext body with the according message
1002   if (!sizeof($MESSAGE['parts']) && $MESSAGE['headers']->ctype=='multipart/encrypted')
1003     {
1004     $p = new stdClass;
1005     $p->type = 'content';
1006     $p->ctype_primary = 'text';
1007     $p->ctype_secondary = 'plain';
1008     $p->body = rcube_label('encryptedmessage');
1009     $MESSAGE['parts'][0] = $p;
1010     }
1011   
1012   if ($MESSAGE['parts'])
1013     {
1014     foreach ($MESSAGE['parts'] as $i => $part)
1015       {
1016       if ($part->type=='headers')
1017         $out .= rcmail_message_headers(sizeof($header_attrib) ? $header_attrib : NULL, $part->headers);
1018       else if ($part->type=='content')
1019         {
1020         if (empty($part->ctype_parameters) || empty($part->ctype_parameters['charset']))
1021           $part->ctype_parameters['charset'] = $MESSAGE['headers']->charset;
1022
1023         // fetch part if not available
1024         if (!isset($part->body))
1025           $part->body = $IMAP->get_message_part($MESSAGE['UID'], $part->mime_id, $part);
1026
1027         $body = rcmail_print_body($part, $safe_mode, !$CONFIG['prefer_html']);
1028         $out .= '<div class="message-part">';
1029         
1030         if ($part->ctype_secondary != 'plain')
1031           $out .= rcmail_sanitize_html($body, $attrib['id']);
1032         else
1033           $out .= $body;
1034
1035         $out .= "</div>\n";
1036         }
1037       }
1038     }
1039   else
1040     $out .= $MESSAGE['body'];
1041
1042
1043   $ctype_primary = strtolower($MESSAGE['structure']->ctype_primary);
1044   $ctype_secondary = strtolower($MESSAGE['structure']->ctype_secondary);
1045   
1046   // list images after mail body
1047   if (get_boolean($attrib['showimages']) && $ctype_primary=='multipart' &&
1048       !empty($MESSAGE['attachments']) && !strstr($message_body, '<html') && strlen($GET_URL))
1049     {
1050     foreach ($MESSAGE['attachments'] as $attach_prop)
1051       {
1052       if (strpos($attach_prop->mimetype, 'image/')===0)
1053         $out .= sprintf("\n<hr />\n<p align=\"center\"><img src=\"%s&amp;_part=%s\" alt=\"%s\" title=\"%s\" /></p>\n",
1054                         htmlspecialchars($GET_URL), $attach_prop->mime_id,
1055                         $attach_prop->filename,
1056                         $attach_prop->filename);
1057       }
1058     }
1059   
1060   // tell client that there are blocked remote objects
1061   if ($REMOTE_OBJECTS && !$safe_mode)
1062     $OUTPUT->set_env('blockedobjects', true);
1063
1064   $out .= "\n</div>";
1065   return $out;
1066   }
1067
1068
1069
1070 // modify a HTML message that it can be displayed inside a HTML page
1071 function rcmail_sanitize_html($body, $container_id)
1072   {
1073   // remove any null-byte characters before parsing
1074   $body = preg_replace('/\x00/', '', $body);
1075   
1076   $base_url = "";
1077   $last_style_pos = 0;
1078   $body_lc = strtolower($body);
1079   
1080   // check for <base href>
1081   if (preg_match(($base_reg = '/(<base.*href=["\']?)([hftps]{3,5}:\/{2}[^"\'\s]+)([^<]*>)/i'), $body, $base_regs))
1082     $base_url = $base_regs[2];
1083   
1084   // find STYLE tags
1085   while (($pos = strpos($body_lc, '<style', $last_style_pos)) && ($pos2 = strpos($body_lc, '</style>', $pos)))
1086     {
1087     $pos = strpos($body_lc, '>', $pos)+1;
1088
1089     // replace all css definitions with #container [def]
1090     $styles = rcmail_mod_css_styles(substr($body, $pos, $pos2-$pos), $container_id, $base_url);
1091
1092     $body = substr($body, 0, $pos) . $styles . substr($body, $pos2);
1093     $body_lc = strtolower($body);
1094     $last_style_pos = $pos2;
1095     }
1096
1097
1098   // remove SCRIPT tags
1099   foreach (array('script', 'applet', 'object', 'embed', 'iframe') as $tag)
1100     {
1101     while (($pos = strpos($body_lc, '<'.$tag)) && (($pos2 = strpos($body_lc, '</'.$tag.'>', $pos)) || ($pos3 = strpos($body_lc, '>', $pos))))
1102       {
1103       $end = $pos2 ? $pos2 + strlen('</'.$tag.'>') : $pos3 + 1;
1104       $body = substr($body, 0, $pos) . substr($body, $end, strlen($body)-$end);
1105       $body_lc = strtolower($body);
1106       }
1107     }
1108
1109   // replace event handlers on any object
1110   while ($body != $prev_body)
1111     {
1112     $prev_body = $body;
1113     $body = preg_replace('/(<[^!][^>]*\s)on(?:load|unload|click|dblclick|mousedown|mouseup|mouseover|mousemove|mouseout|focus|blur|keypress|keydown|keyup|submit|reset|select|change)=([^>]+>)/im', '$1__removed=$2', $body);
1114     $body = preg_replace('/(<[^!][^>]*\shref=["\']?)(javascript:)([^>]*?>)/im', '$1null:$3', $body);
1115     }
1116
1117   // resolve <base href>
1118   if ($base_url)
1119     {
1120     $body = preg_replace('/(src|background|href)=(["\']?)([\.\/]+[^"\'\s]+)(\2|\s|>)/Uie', "'\\1=\"'.make_absolute_url('\\3', '$base_url').'\"'", $body);
1121     $body = preg_replace('/(url\s*\()(["\']?)([\.\/]+[^"\'\)\s]+)(\2)\)/Uie', "'\\1\''.make_absolute_url('\\3', '$base_url').'\')'", $body);
1122     $body = preg_replace($base_reg, '', $body);
1123     }
1124     
1125   // modify HTML links to open a new window if clicked
1126   $body = preg_replace('/<(a|link)\s+([^>]+)>/Uie', "rcmail_alter_html_link('\\1','\\2', '$container_id');", $body);
1127
1128   // add comments arround html and other tags
1129   $out = preg_replace(array(
1130       '/(<!DOCTYPE.+)/i',
1131       '/(<\/?html[^>]*>)/i',
1132       '/(<\/?head[^>]*>)/i',
1133       '/(<title[^>]*>.*<\/title>)/Ui',
1134       '/(<\/?meta[^>]*>)/i'),
1135     '<!--\\1-->',
1136     $body);
1137
1138   $out = preg_replace(
1139     array(
1140       '/<body([^>]*)>/i',
1141       '/<\/body>/i',
1142     ),
1143     array(
1144       '<div class="rcmBody"\\1>',
1145       '</div>',
1146     ),
1147     $out);
1148
1149   // quote <? of php and xml files that are specified as text/html
1150   $out = preg_replace(array('/<\?/', '/\?>/'), array('&lt;?', '?&gt;'), $out);
1151
1152   return $out;
1153   }
1154
1155
1156 // parse link attributes and set correct target
1157 function rcmail_alter_html_link($tag, $attrs, $container_id)
1158   {
1159   $in = preg_replace('/=([^("|\'|\s)]+)(\s|$)/', '="\1"', $in);
1160   $attrib = parse_attrib_string($attrs);
1161   
1162   if ($tag == 'link' && preg_match('/^https?:\/\//i', $attrib['href']))
1163     $attrib['href'] = "./bin/modcss.php?u=" . urlencode($attrib['href']) . "&amp;c=" . urlencode($container_id);
1164
1165   else if (stristr((string)$attrib['href'], 'mailto:'))
1166     $attrib['onclick'] = sprintf(
1167       "return %s.command('compose','%s',this)",
1168       JS_OBJECT_NAME,
1169       JQ(substr($attrib['href'], 7)));
1170   
1171   else if (!empty($attrib['href']) && $attrib['href']{0}!='#')
1172     $attrib['target'] = '_blank';
1173
1174   return "<$tag" . create_attrib_string($attrib, array('href','name','target','onclick','id','class','style','title','rel','type','media')) . ' />';
1175   }
1176
1177
1178 function rcmail_has_html_part($message_parts)
1179 {
1180    if (!is_array($message_parts))
1181       return FALSE;
1182
1183    // check all message parts
1184    foreach ($message_parts as $pid => $part)
1185    {
1186       $mimetype = strtolower($part->ctype_primary.'/'.$part->ctype_secondary);
1187       if ($mimetype=='text/html')
1188       {
1189          return TRUE;
1190       }
1191    }
1192     
1193    return FALSE;
1194 }
1195
1196 // return first HTML part of a message
1197 function rcmail_first_html_part($message_struct)
1198   {
1199   global $IMAP;
1200
1201   if (!is_array($message_struct['parts']))
1202     return FALSE;
1203     
1204   $html_part = NULL;
1205
1206   // check all message parts
1207   foreach ($message_struct['parts'] as $pid => $part)
1208     {
1209     $mimetype = strtolower($part->ctype_primary.'/'.$part->ctype_secondary);
1210     if ($mimetype=='text/html')
1211       {
1212       $html_part = $IMAP->get_message_part($message_struct['UID'], $pid, $part);
1213       }
1214     }
1215
1216   if ($html_part)
1217     {
1218     // remove special chars encoding
1219     //$trans = array_flip(get_html_translation_table(HTML_ENTITIES));
1220     //$html_part = strtr($html_part, $trans);
1221
1222     return $html_part;
1223     }
1224
1225   return FALSE;
1226 }
1227
1228
1229 // return first text part of a message
1230 function rcmail_first_text_part($message_struct)
1231   {
1232   global $IMAP;
1233
1234   if (empty($message_struct['parts']))
1235     return $message_struct['UID'] ? $IMAP->get_body($message_struct['UID']) : false;
1236
1237   // check all message parts
1238   foreach ($message_struct['parts'] as $pid => $part)
1239     {
1240     $mimetype = strtolower($part->ctype_primary.'/'.$part->ctype_secondary);
1241
1242     if ($mimetype=='text/plain')
1243       return $IMAP->get_message_part($message_struct['UID'], $pid, $part);
1244
1245     else if ($mimetype=='text/html')
1246       {
1247       $html_part = $IMAP->get_message_part($message_struct['UID'], $pid, $part);
1248       
1249       // remove special chars encoding
1250       $trans = array_flip(get_html_translation_table(HTML_ENTITIES));
1251       $html_part = strtr($html_part, $trans);
1252
1253       // create instance of html2text class
1254       $txt = new html2text($html_part);
1255       return $txt->get_text();
1256       }
1257     }
1258
1259   return FALSE;
1260   }
1261
1262
1263 // decode address string and re-format it as HTML links
1264 function rcmail_address_string($input, $max=NULL, $addicon=NULL)
1265   {
1266   global $IMAP, $PRINT_MODE, $CONFIG, $OUTPUT, $EMAIL_ADDRESS_PATTERN;
1267   
1268   $a_parts = $IMAP->decode_address_list($input);
1269
1270   if (!sizeof($a_parts))
1271     return $input;
1272
1273   $c = count($a_parts);
1274   $j = 0;
1275   $out = '';
1276
1277   foreach ($a_parts as $part)
1278     {
1279     $j++;
1280     if ($PRINT_MODE)
1281       $out .= sprintf('%s &lt;%s&gt;', Q($part['name']), $part['mailto']);
1282     else if (preg_match($EMAIL_ADDRESS_PATTERN, $part['mailto']))
1283       {
1284       $out .= sprintf('<a href="mailto:%s" onclick="return %s.command(\'compose\',\'%s\',this)" class="rcmContactAddress" title="%s">%s</a>',
1285                       Q($part['mailto']),
1286                       JS_OBJECT_NAME,
1287                       JQ($part['mailto']),
1288                       Q($part['mailto']),
1289                       Q($part['name']));
1290                       
1291       if ($addicon)
1292         $out .= sprintf('&nbsp;<a href="#add" onclick="return %s.command(\'add-contact\',\'%s\',this)" title="%s"><img src="%s%s" alt="add" border="0" /></a>',
1293                         JS_OBJECT_NAME,
1294                         urlencode($part['string']),
1295                         rcube_label('addtoaddressbook'),
1296                         $CONFIG['skin_path'],
1297                         $addicon);
1298       }
1299     else
1300       {
1301       if ($part['name'])
1302         $out .= Q($part['name']);
1303       if ($part['mailto'])
1304         $out .= (strlen($out) ? ' ' : '') . sprintf('&lt;%s&gt;', Q($part['mailto']));
1305       }
1306       
1307     if ($c>$j)
1308       $out .= ','.($max ? '&nbsp;' : ' ');
1309         
1310     if ($max && $j==$max && $c>$j)
1311       {
1312       $out .= '...';
1313       break;
1314       }        
1315     }
1316     
1317   return $out;
1318   }
1319
1320
1321 function rcmail_message_part_controls()
1322   {
1323   global $CONFIG, $IMAP, $MESSAGE;
1324   
1325   $part = asciiwords(get_input_value('_part', RCUBE_INPUT_GPC));
1326   if (!is_array($MESSAGE) || !is_array($MESSAGE['parts']) || !($_GET['_uid'] && $_GET['_part']) || !$MESSAGE['parts'][$part])
1327     return '';
1328     
1329   $part = $MESSAGE['parts'][$part];
1330   $attrib_str = create_attrib_string($attrib, array('id', 'class', 'style', 'cellspacing', 'cellpadding', 'border', 'summary'));
1331   $out = '<table '. $attrib_str . ">\n";
1332   
1333   if ($part->filename)
1334     {
1335     $out .= sprintf('<tr><td class="title">%s</td><td>%s</td><td>[<a href="./?%s">%s</a>]</tr>'."\n",
1336                     Q(rcube_label('filename')),
1337                     Q($part->filename),
1338                     str_replace('_frame=', '_download=', $_SERVER['QUERY_STRING']),
1339                     Q(rcube_label('download')));
1340     }
1341     
1342   if ($part->size)
1343     $out .= sprintf('<tr><td class="title">%s</td><td>%s</td></tr>'."\n",
1344                     Q(rcube_label('filesize')),
1345                     show_bytes($part->size));
1346   
1347   $out .= "\n</table>";
1348   
1349   return $out;
1350   }
1351
1352
1353
1354 function rcmail_message_part_frame($attrib)
1355   {
1356   global $MESSAGE;
1357   
1358   $part = $MESSAGE['parts'][asciiwords(get_input_value('_part', RCUBE_INPUT_GPC))];
1359   $ctype_primary = strtolower($part->ctype_primary);
1360
1361   $attrib['src'] = Q('./?'.str_replace('_frame=', ($ctype_primary=='text' ? '_show=' : '_preload='), $_SERVER['QUERY_STRING']));
1362
1363   $attrib_str = create_attrib_string($attrib, array('id', 'class', 'style', 'src', 'width', 'height'));
1364   $out = '<iframe '. $attrib_str . "></iframe>";
1365     
1366   return $out;
1367   }
1368
1369
1370 // clear message composing settings
1371 function rcmail_compose_cleanup()
1372   {
1373   if (!isset($_SESSION['compose']))
1374     return;
1375
1376   // remove attachment files from temp dir
1377   if (is_array($_SESSION['compose']['attachments']))
1378     foreach ($_SESSION['compose']['attachments'] as $attachment)
1379       @unlink($attachment['path']);
1380   
1381   unset($_SESSION['compose']);
1382   }
1383   
1384
1385 /**
1386  * Send the given message compose object using the configured method
1387  */
1388 function rcmail_deliver_message(&$message, $from, $mailto)
1389 {
1390   global $CONFIG;
1391
1392   $headers = $message->headers();
1393   $msg_body = $message->get();
1394   
1395   // send thru SMTP server using custom SMTP library
1396   if ($CONFIG['smtp_server'])
1397     {
1398     // generate list of recipients
1399     $a_recipients = array($mailto);
1400   
1401     if (strlen($headers['Cc']))
1402       $a_recipients[] = $headers['Cc'];
1403     if (strlen($headers['Bcc']))
1404       $a_recipients[] = $headers['Bcc'];
1405   
1406     // clean Bcc from header for recipients
1407     $send_headers = $headers;
1408     unset($send_headers['Bcc']);
1409
1410     // send message
1411     $smtp_response = array();
1412     $sent = smtp_mail($from, $a_recipients, ($foo = $message->txtHeaders($send_headers)), $msg_body, $smtp_response);
1413
1414     // log error
1415     if (!$sent)
1416       raise_error(array('code' => 800, 'type' => 'smtp', 'line' => __LINE__, 'file' => __FILE__,
1417                         'message' => "SMTP error: ".join("\n", $smtp_response)), TRUE, FALSE);
1418     }
1419   
1420   // send mail using PHP's mail() function
1421   else
1422     {
1423     // unset some headers because they will be added by the mail() function
1424     $headers_enc = $message->headers($headers);
1425     $headers_php = $message->_headers;
1426     unset($headers_php['To'], $headers_php['Subject']);
1427     
1428     // reset stored headers and overwrite
1429     $message->_headers = array();
1430     $header_str = $message->txtHeaders($headers_php);
1431   
1432     if (ini_get('safe_mode'))
1433       $sent = mail($headers_enc['To'], $headers_enc['Subject'], $msg_body, $header_str);
1434     else
1435       $sent = mail($headers_enc['To'], $headers_enc['Subject'], $msg_body, $header_str, "-f$from");
1436     }
1437   
1438   if ($sent)  // remove MDN headers after sending
1439     unset($headers['Return-Receipt-To'], $headers['Disposition-Notification-To']);
1440   
1441   $message->_headers = array();
1442   $message->headers($headers);
1443   
1444   return $sent;
1445 }
1446
1447
1448 function rcmail_send_mdn($uid)
1449 {
1450   global $CONFIG, $USER, $IMAP;
1451   
1452   $message = array('UID' => $uid);
1453   $message['headers'] = $IMAP->get_headers($message['UID']);
1454   $message['subject'] = rcube_imap::decode_mime_string($message['headers']->subject, $message['headers']->charset);
1455   
1456   if ($message['headers']->mdn_to && !$message['headers']->mdn_sent)
1457   {
1458     $identity = $USER->get_identity();
1459     $sender = format_email_recipient($identity['email'], $identity['name']);
1460     $recipient = array_shift($IMAP->decode_address_list($message['headers']->mdn_to));
1461     $mailto = $recipient['mailto'];
1462
1463     $compose = new rc_mail_mime(rcmail_header_delm());
1464     $compose->setParam(array(
1465       'text_encoding' => 'quoted-printable',
1466       'html_encoding' => 'quoted-printable',
1467       'head_encoding' => 'quoted-printable',
1468       'head_charset'  => RCMAIL_CHARSET,
1469       'html_charset'  => RCMAIL_CHARSET,
1470       'text_charset'  => RCMAIL_CHARSET,
1471     ));
1472     
1473     // compose headers array
1474     $headers = array(
1475       'Date' => date('r'),
1476       'From' => $sender,
1477       'To'   => $message['headers']->mdn_to,
1478       'Subject' => rcube_label('receiptread') . ': ' . $message['subject'],
1479       'Message-ID' => sprintf('<%s@%s>', md5(uniqid('rcmail'.rand(),true)), rcmail_mail_domain($_SESSION['imap_host'])),
1480       'X-Sender' => $identity['email'],
1481       'Content-Type' => 'multipart/report; report-type=disposition-notification',
1482     );
1483     
1484     if (!empty($CONFIG['useragent']))
1485       $headers['User-Agent'] = $CONFIG['useragent'];
1486
1487     $body = rcube_label("yourmessage") . "\r\n\r\n" .
1488       "\t" . rcube_label("to") . ': ' . rcube_imap::decode_mime_string($message['headers']->to, $message['headers']->charset) . "\r\n" .
1489       "\t" . rcube_label("subject") . ': ' . $message['subject'] . "\r\n" .
1490       "\t" . rcube_label("sent") . ': ' . format_date(strtotime($message['headers']->date), $CONFIG['date_long']) . "\r\n" .
1491       "\r\n" . rcube_label("receiptnote") . "\r\n";
1492     
1493     $ua = !empty($CONFIG['useragent']) ? $CONFIG['useragent'] : "RoundCube Webmail (Version ".RCMAIL_VERSION.")";
1494     $report = "Reporting-UA: $ua\r\n";
1495     
1496     if ($message['headers']->to)
1497         $report .= "Original-Recipient: {$message['headers']->to}\r\n";
1498     
1499     $report .= "Final-Recipient: rfc822; {$identity['email']}\r\n" .
1500                "Original-Message-ID: {$message['headers']->messageID}\r\n" .
1501                "Disposition: manual-action/MDN-sent-manually; displayed\r\n";
1502     
1503     $compose->headers($headers, true);
1504     $compose->setTXTBody($body);
1505     $compose->addAttachment($report, 'message/disposition-notification', 'MDNPart2.txt', false, '7bit', 'inline');
1506
1507     $sent = rcmail_deliver_message($compose, $identity['email'], $mailto);
1508
1509     if ($sent)
1510     {
1511       $IMAP->set_flag($message['UID'], 'MDNSENT');
1512       return true;
1513     }
1514   }
1515   
1516   return false;
1517 }
1518
1519
1520 // register UI objects
1521 $OUTPUT->add_handlers(array(
1522   'mailboxlist' => 'rcmail_mailbox_list',
1523   'messages' => 'rcmail_message_list',
1524   'messagecountdisplay' => 'rcmail_messagecount_display',
1525   'quotadisplay' => 'rcmail_quota_display',
1526   'messageheaders' => 'rcmail_message_headers',
1527   'messagebody' => 'rcmail_message_body',
1528   'messagecontentframe' => 'rcmail_messagecontent_frame',
1529   'messagepartframe' => 'rcmail_message_part_frame',
1530   'messagepartcontrols' => 'rcmail_message_part_controls',
1531   'searchform' => 'rcmail_search_form'
1532 ));
1533
1534 ?>