]> git.donarmstrong.com Git - roundcube.git/blob - program/steps/mail/func.inc
Imported Upstream version 0.1~rc2
[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 875 2007-10-17 06:50:28Z thomasb $
19
20 */
21
22 require_once('lib/html2text.inc');
23 require_once('lib/enriched.inc');
24
25
26 $EMAIL_ADDRESS_PATTERN = '/([a-z0-9][a-z0-9\-\.\+\_]*@[a-z0-9]([a-z0-9\-][.]?)*[a-z0-9]\\.[a-z]{2,5})/i';
27
28 if (empty($_SESSION['mbox']))
29   $_SESSION['mbox'] = $IMAP->get_mailbox_name();
30
31 // set imap properties and session vars
32 if ($mbox = get_input_value('_mbox', RCUBE_INPUT_GPC))
33   {
34   $IMAP->set_mailbox($mbox);
35   $_SESSION['mbox'] = $mbox;
36   }
37
38 if (!empty($_GET['_page']))
39   {
40   $IMAP->set_page((int)$_GET['_page']);
41   $_SESSION['page'] = (int)$_GET['_page'];
42   }
43
44 // set mailbox to INBOX if not set
45 if (empty($_SESSION['mbox']))
46   $_SESSION['mbox'] = $IMAP->get_mailbox_name();
47
48 // set default sort col/order to session
49 if (!isset($_SESSION['sort_col']))
50   $_SESSION['sort_col'] = $CONFIG['message_sort_col'];
51 if (!isset($_SESSION['sort_order']))
52   $_SESSION['sort_order'] = $CONFIG['message_sort_order'];
53
54 // set message set for search result
55 if (!empty($_REQUEST['_search']) && isset($_SESSION['search'][$_REQUEST['_search']]))
56   $IMAP->set_search_set($_SESSION['search'][$_REQUEST['_search']]);
57
58
59 // define url for getting message parts
60 if (strlen($_GET['_uid']))
61   $GET_URL = rcmail_url('get', array('_mbox'=>$IMAP->get_mailbox_name(), '_uid'=>get_input_value('_uid', RCUBE_INPUT_GET)));
62
63
64 // set current mailbox in client environment
65 $OUTPUT->set_env('mailbox', $IMAP->get_mailbox_name());
66 $OUTPUT->set_env('quota', $IMAP->get_capability('quota'));
67
68 if ($CONFIG['trash_mbox'])
69   $OUTPUT->set_env('trash_mailbox', $CONFIG['trash_mbox']);
70 if ($CONFIG['drafts_mbox'])
71   $OUTPUT->set_env('drafts_mailbox', $CONFIG['drafts_mbox']);
72 if ($CONFIG['junk_mbox'])
73   $OUTPUT->set_env('junk_mailbox', $CONFIG['junk_mbox']);
74
75 if (!$OUTPUT->ajax_call)
76   rcube_add_label('checkingmail', 'deletemessage', 'movemessagetotrash');
77
78 // set page title
79 if (empty($_action) || $_action == 'list')
80   $OUTPUT->set_pagetitle(rcube_charset_convert($IMAP->get_mailbox_name(), 'UTF-7'));
81
82
83
84 // return the message list as HTML table
85 function rcmail_message_list($attrib)
86   {
87   global $IMAP, $CONFIG, $COMM_PATH, $OUTPUT;
88
89   $skin_path = $CONFIG['skin_path'];
90   $image_tag = '<img src="%s%s" alt="%s" border="0" />';
91
92   // check to see if we have some settings for sorting
93   $sort_col   = $_SESSION['sort_col'];
94   $sort_order = $_SESSION['sort_order'];
95   
96   // add some labels to client
97   rcube_add_label('from', 'to');
98
99   // get message headers
100   $a_headers = $IMAP->list_headers('', '', $sort_col, $sort_order);
101
102   // add id to message list table if not specified
103   if (!strlen($attrib['id']))
104     $attrib['id'] = 'rcubemessagelist';
105
106   // allow the following attributes to be added to the <table> tag
107   $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id', 'cellpadding', 'cellspacing', 'border', 'summary'));
108
109   $out = '<table' . $attrib_str . ">\n";
110
111
112   // define list of cols to be displayed
113   $a_show_cols = is_array($CONFIG['list_cols']) ? $CONFIG['list_cols'] : array('subject');
114   $a_sort_cols = array('subject', 'date', 'from', 'to', 'size');
115
116   $mbox = $IMAP->get_mailbox_name();
117   
118   // show 'to' instead of from in sent messages
119   if (($mbox==$CONFIG['sent_mbox'] || $mbox==$CONFIG['drafts_mbox']) && ($f = array_search('from', $a_show_cols))
120       && !array_search('to', $a_show_cols))
121     $a_show_cols[$f] = 'to';
122   
123   // add col definition
124   $out .= '<colgroup>';
125   $out .= '<col class="icon" />';
126
127   foreach ($a_show_cols as $col)
128     $out .= sprintf('<col class="%s" />', $col);
129
130   $out .= '<col class="icon" />';
131   $out .= "</colgroup>\n";
132
133   // add table title
134   $out .= "<thead><tr>\n<td class=\"icon\">&nbsp;</td>\n";
135
136   $javascript = '';
137   foreach ($a_show_cols as $col)
138     {
139     // get column name
140     $col_name = Q(rcube_label($col));
141
142     // make sort links
143     $sort = '';
144     if ($IMAP->get_capability('sort') && in_array($col, $a_sort_cols))
145       {
146       // have buttons configured
147       if (!empty($attrib['sortdescbutton']) || !empty($attrib['sortascbutton']))
148         {
149         $sort = '&nbsp;&nbsp;';
150
151         // asc link
152         if (!empty($attrib['sortascbutton']))
153           {
154           $sort .= $OUTPUT->button(array(
155             'command' => 'sort',
156             'prop' => $col.'_ASC',
157             'image' => $attrib['sortascbutton'],
158             'align' => 'absmiddle',
159             'title' => 'sortasc'));
160           }       
161         
162         // desc link
163         if (!empty($attrib['sortdescbutton']))
164           {
165           $sort .= $OUTPUT->button(array(
166             'command' => 'sort',
167             'prop' => $col.'_DESC',
168             'image' => $attrib['sortdescbutton'],
169             'align' => 'absmiddle',
170             'title' => 'sortdesc'));
171           }
172         }
173       // just add a link tag to the header
174       else
175         {
176         $col_name = sprintf(
177           '<a href="./#sort" onclick="return %s.command(\'sort\',\'%s\',this)" title="%s">%s</a>',
178           JS_OBJECT_NAME,
179           $col,
180           rcube_label('sortby'),
181           $col_name);
182         }
183       }
184       
185     $sort_class = $col==$sort_col ? " sorted$sort_order" : '';
186
187     // put it all together
188     $out .= '<td class="'.$col.$sort_class.'" id="rcmHead'.$col.'">' . "$col_name$sort</td>\n";    
189     }
190
191   $out .= '<td class="icon">'.($attrib['attachmenticon'] ? sprintf($image_tag, $skin_path, $attrib['attachmenticon'], '') : '')."</td>\n";
192   $out .= "</tr></thead>\n<tbody>\n";
193
194   // no messages in this mailbox
195   if (!sizeof($a_headers))
196     $OUTPUT->show_message('nomessagesfound', 'notice');
197
198
199   $a_js_message_arr = array();
200
201   // create row for each message
202   foreach ($a_headers as $i => $header)  //while (list($i, $header) = each($a_headers))
203     {
204     $message_icon = $attach_icon = '';
205     $js_row_arr = array();
206     $zebra_class = $i%2 ? 'even' : 'odd';
207
208     // set messag attributes to javascript array
209     if ($header->deleted)
210       $js_row_arr['deleted'] = true;
211     if (!$header->seen)
212       $js_row_arr['unread'] = true;
213     if ($header->answered)
214       $js_row_arr['replied'] = true;
215     // set message icon  
216     if ($attrib['deletedicon'] && $header->deleted)
217       $message_icon = $attrib['deletedicon'];
218     else if ($attrib['unreadicon'] && !$header->seen)
219       $message_icon = $attrib['unreadicon'];
220     else if ($attrib['repliedicon'] && $header->answered)
221       $message_icon = $attrib['repliedicon'];
222     else if ($attrib['messageicon'])
223       $message_icon = $attrib['messageicon'];
224     
225     // set attachment icon
226     if ($attrib['attachmenticon'] && preg_match("/multipart\/[mr]/i", $header->ctype))
227       $attach_icon = $attrib['attachmenticon'];
228         
229     $out .= sprintf('<tr id="rcmrow%d" class="message%s%s %s">'."\n",
230                     $header->uid,
231                     $header->seen ? '' : ' unread',
232                     $header->deleted ? ' deleted' : '',
233                     $zebra_class);    
234     
235     $out .= sprintf("<td class=\"icon\">%s</td>\n", $message_icon ? sprintf($image_tag, $skin_path, $message_icon, '') : '');
236     
237     // format each col
238     foreach ($a_show_cols as $col)
239       {
240       if ($col=='from' || $col=='to')
241         $cont = Q(rcmail_address_string($header->$col, 3, $attrib['addicon']), 'show');
242       else if ($col=='subject')
243         {
244         $action = $mbox==$CONFIG['drafts_mbox'] ? 'compose' : 'show';
245         $uid_param = $mbox==$CONFIG['drafts_mbox'] ? '_draf_uid' : '_uid';
246         $cont = Q(rcube_imap::decode_mime_string($header->$col, $header->charset));
247         if (empty($cont)) $cont = Q(rcube_label('nosubject'));
248         $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);
249         }
250       else if ($col=='size')
251         $cont = show_bytes($header->$col);
252       else if ($col=='date')
253         $cont = format_date($header->date);
254       else
255         $cont = Q($header->$col);
256         
257       $out .= '<td class="'.$col.'">' . $cont . "</td>\n";
258       }
259
260     $out .= sprintf("<td class=\"icon\">%s</td>\n", $attach_icon ? sprintf($image_tag, $skin_path, $attach_icon, '') : '');
261     $out .= "</tr>\n";
262     
263     if (sizeof($js_row_arr))
264       $a_js_message_arr[$header->uid] = $js_row_arr;
265     }
266   
267   // complete message table
268   $out .= "</tbody></table>\n";
269   
270   
271   $message_count = $IMAP->messagecount();
272   
273   // set client env
274   $OUTPUT->add_gui_object('mailcontframe', 'mailcontframe');
275   $OUTPUT->add_gui_object('messagelist', $attrib['id']);
276   $OUTPUT->set_env('messagecount', $message_count);
277   $OUTPUT->set_env('current_page', $IMAP->list_page);
278   $OUTPUT->set_env('pagecount', ceil($message_count/$IMAP->page_size));
279   $OUTPUT->set_env('sort_col', $sort_col);
280   $OUTPUT->set_env('sort_order', $sort_order);
281   
282   if ($attrib['messageicon'])
283     $OUTPUT->set_env('messageicon', $skin_path . $attrib['messageicon']);
284   if ($attrib['deletedicon'])
285     $OUTPUT->set_env('deletedicon', $skin_path . $attrib['deletedicon']);
286   if ($attrib['unreadicon'])
287     $OUTPUT->set_env('unreadicon', $skin_path . $attrib['unreadicon']);
288   if ($attrib['repliedicon'])
289     $OUTPUT->set_env('repliedicon', $skin_path . $attrib['repliedicon']);
290   if ($attrib['attachmenticon'])
291     $OUTPUT->set_env('attachmenticon', $skin_path . $attrib['attachmenticon']);
292   
293   $OUTPUT->set_env('messages', $a_js_message_arr);
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 function rcmail_print_body($part, $safe=FALSE, $plain=FALSE)
486   {
487   global $IMAP, $REMOTE_OBJECTS;
488   
489   $body = is_array($part->replaces) ? strtr($part->body, $part->replaces) : $part->body;
490
491   // convert html to text/plain
492   if ($part->ctype_secondary=='html' && $plain)
493     {
494     $txt = new html2text($body, false, true);
495     $body = $txt->get_text();
496     $part->ctype_secondary = 'plain';
497     }
498     
499   // text/html
500   if ($part->ctype_secondary=='html')
501     {
502     // remove charset specification in HTML message
503     $body = preg_replace('/charset=[a-z0-9\-]+/i', '', $body);
504
505     if (!$safe)  // remove remote images and scripts
506       {
507       $remote_patterns = array('/<img\s+(.*)src=(["\']?)([hftps]{3,5}:\/{2}[^"\'\s]+)(\2|\s|>)/Ui',
508                                '/(src|background)=(["\']?)([hftps]{3,5}:\/{2}[^"\'\s]+)(\2|\s|>)/Ui',
509                                '/(<base.*href=["\']?)([hftps]{3,5}:\/{2}[^"\'\s]+)([^<]*>)/i',
510                                '/(<link.*href=["\']?)([hftps]{3,5}:\/{2}[^"\'\s]+)([^<]*>)/i',
511                                '/url\s*\(["\']?([hftps]{3,5}:\/{2}[^"\'\s]+)["\']?\)/i',
512                                '/url\s*\(["\']?([\.\/]+[^"\'\s]+)["\']?\)/i',
513                                '/<script.+<\/script>/Umis');
514
515       $remote_replaces = array('<img \\1src=\\2./program/blocked.gif\\4',
516                                '',
517                                '',
518                                '',
519                                'none',
520                                'none',
521                                '');
522       
523       // set flag if message containes remote obejcts that where blocked
524       foreach ($remote_patterns as $pattern)
525         {
526         if (preg_match($pattern, $body))
527           {
528           $REMOTE_OBJECTS = TRUE;
529           break;
530           }
531         }
532
533       $body = preg_replace($remote_patterns, $remote_replaces, $body);
534       }
535
536     return Q($body, 'show', FALSE);
537     }
538
539   // text/enriched
540   if ($part->ctype_secondary=='enriched')
541     {
542     return Q(enriched_to_html($body), 'show');
543     }
544   else
545     {
546     // make links and email-addresses clickable
547     $convert_patterns = $convert_replaces = $replace_strings = array();
548     
549     $url_chars = 'a-z0-9_\-\+\*\$\/&%=@#:;';
550     $url_chars_within = '\?\.~,!';
551
552     $convert_patterns[] = "/([\w]+):\/\/([a-z0-9\-\.]+[a-z]{2,4}([$url_chars$url_chars_within]*[$url_chars])?)/ie";
553     $convert_replaces[] = "rcmail_str_replacement('<a href=\"\\1://\\2\" target=\"_blank\">\\1://\\2</a>', \$replace_strings)";
554
555     $convert_patterns[] = "/([^\/:]|\s)(www\.)([a-z0-9\-]{2,}[a-z]{2,4}([$url_chars$url_chars_within]*[$url_chars])?)/ie";
556     $convert_replaces[] = "rcmail_str_replacement('\\1<a href=\"http://\\2\\3\" target=\"_blank\">\\2\\3</a>', \$replace_strings)";
557     
558     $convert_patterns[] = '/([a-z0-9][a-z0-9\-\.\+\_]*@[a-z0-9]([a-z0-9\-][.]?)*[a-z0-9]\\.[a-z]{2,5})/ie';
559     $convert_replaces[] = "rcmail_str_replacement('<a href=\"mailto:\\1\" onclick=\"return ".JS_OBJECT_NAME.".command(\'compose\',\'\\1\',this)\">\\1</a>', \$replace_strings)";
560     
561     if ($part->ctype_parameters['format'] != 'flowed')
562       $body = wordwrap(trim($body), 80);
563
564     $body = preg_replace($convert_patterns, $convert_replaces, $body);
565
566     // split body into single lines
567     $a_lines = preg_split('/\r?\n/', $body);
568     $quote_level = 0;
569
570     // colorize quoted parts
571     for($n=0; $n<sizeof($a_lines); $n++)
572       {
573       $line = $a_lines[$n];
574       $quotation = '';
575       $q = 0;
576       
577       if (preg_match('/^(>+\s*)/', $line, $regs))
578         {
579         $q = strlen(preg_replace('/\s/', '', $regs[1]));
580         $line = substr($line, strlen($regs[1]));
581
582         if ($q > $quote_level)
583           $quotation = str_repeat('<blockquote>', $q - $quote_level);
584         else if ($q < $quote_level)
585           $quotation = str_repeat("</blockquote>", $quote_level - $q);
586         }
587       else if ($quote_level > 0)
588         $quotation = str_repeat("</blockquote>", $quote_level);
589
590       $quote_level = $q;
591       $a_lines[$n] = $quotation . Q($line, 'replace', FALSE);
592       }
593
594     // insert the links for urls and mailtos
595     $body = preg_replace("/##string_replacement\{([0-9]+)\}##/e", "\$replace_strings[\\1]", join("\n", $a_lines));
596     
597     return "<div class=\"pre\">".$body."\n</div>";
598     }
599   }
600
601
602
603 // add a string to the replacement array and return a replacement string
604 function rcmail_str_replacement($str, &$rep)
605   {
606   static $count = 0;
607   $rep[$count] = stripslashes($str);
608   return "##string_replacement{".($count++)."}##";
609   }
610
611
612 function rcmail_parse_message(&$structure, $arg=array(), $recursive=FALSE)
613   {
614   global $IMAP;
615   static $sa_inline_objects = array();
616
617   // arguments are: (bool)$prefer_html, (string)$get_url
618   extract($arg);
619
620   $a_attachments = array();
621   $a_return_parts = array();
622   $out = '';
623
624   $message_ctype_primary = strtolower($structure->ctype_primary);
625   $message_ctype_secondary = strtolower($structure->ctype_secondary);
626
627   // show message headers
628   if ($recursive && is_array($structure->headers) && isset($structure->headers['subject']))
629     {
630     $c = new stdClass;
631     $c->type = 'headers';
632     $c->headers = &$structure->headers;
633     $a_return_parts[] = $c;
634     }
635
636   // print body if message doesn't have multiple parts
637   if ($message_ctype_primary=='text')
638     {
639     $structure->type = 'content';
640     $a_return_parts[] = &$structure;
641     }
642
643   // message contains alternative parts
644   else if ($message_ctype_primary=='multipart' && $message_ctype_secondary=='alternative' && is_array($structure->parts))
645     {
646     // get html/plaintext parts
647     $plain_part = $html_part = $print_part = $related_part = NULL;
648     
649     foreach ($structure->parts as $p => $sub_part)
650       {
651       $rel_parts = $attachmnts = null;
652       $sub_ctype_primary = strtolower($sub_part->ctype_primary);
653       $sub_ctype_secondary = strtolower($sub_part->ctype_secondary);
654
655       // check if sub part is 
656       if ($sub_ctype_primary=='text' && $sub_ctype_secondary=='plain')
657         $plain_part = $p;
658       else if ($sub_ctype_primary=='text' && $sub_ctype_secondary=='html')
659         $html_part = $p;
660       else if ($sub_ctype_primary=='text' && $sub_ctype_secondary=='enriched')
661         $enriched_part = $p;
662       else if ($sub_ctype_primary=='multipart' && ($sub_ctype_secondary=='related' || $sub_ctype_secondary=='mixed'))
663         $related_part = $p;
664       }
665       
666     // parse related part (alternative part could be in here)
667     if ($related_part!==NULL)
668     {
669       list($rel_parts, $attachmnts) = rcmail_parse_message($structure->parts[$related_part], $arg, TRUE);
670       $a_attachments = array_merge($a_attachments, $attachmnts);
671     }
672     
673     // merge related parts if any
674     if ($rel_parts && $prefer_html && !$html_part)
675       $a_return_parts = array_merge($a_return_parts, $rel_parts);
676
677     // choose html/plain part to print
678     else if ($html_part!==NULL && $prefer_html)
679       $print_part = &$structure->parts[$html_part];
680     else if ($enriched_part!==NULL)
681       $print_part = &$structure->parts[$enriched_part];
682     else if ($plain_part!==NULL)
683       $print_part = &$structure->parts[$plain_part];
684
685     // show message body
686     if (is_object($print_part))
687       {
688       $print_part->type = 'content';
689       $a_return_parts[] = $print_part;
690       }
691     // show plaintext warning
692     else if ($html_part!==NULL && empty($a_return_parts))
693       {
694       $c = new stdClass;
695       $c->type = 'content';
696       $c->body = rcube_label('htmlmessage');
697       $c->ctype_primary = 'text';
698       $c->ctype_secondary = 'plain';
699       
700       $a_return_parts[] = $c;
701       }
702                                 
703     // add html part as attachment
704     if ($html_part!==NULL && $structure->parts[$html_part]!==$print_part)
705       {
706       $html_part = &$structure->parts[$html_part];
707       $html_part->filename = rcube_label('htmlmessage');
708       $html_part->mimetype = 'text/html';
709       
710       $a_attachments[] = $html_part;
711       }
712     }
713
714   // message contains multiple parts
715   else if (is_array($structure->parts) && !empty($structure->parts))
716     {
717     for ($i=0; $i<count($structure->parts); $i++)
718       {
719       $mail_part = &$structure->parts[$i];
720       $primary_type = strtolower($mail_part->ctype_primary);
721       $secondary_type = strtolower($mail_part->ctype_secondary);
722
723       // multipart/alternative
724       if ($primary_type=='multipart')
725         {
726         list($parts, $attachmnts) = rcmail_parse_message($mail_part, $arg, TRUE);
727
728         $a_return_parts = array_merge($a_return_parts, $parts);
729         $a_attachments = array_merge($a_attachments, $attachmnts);
730         }
731
732       // part text/[plain|html] OR message/delivery-status
733       else if (($primary_type=='text' && ($secondary_type=='plain' || $secondary_type=='html') && $mail_part->disposition!='attachment') ||
734                ($primary_type=='message' && $secondary_type=='delivery-status'))
735         {
736         $mail_part->type = 'content';
737         $a_return_parts[] = $mail_part;
738         }
739
740       // part message/*
741       else if ($primary_type=='message')
742         {
743         list($parts, $attachmnts) = rcmail_parse_message($mail_part, $arg, TRUE);
744           
745         $a_return_parts = array_merge($a_return_parts, $parts);
746         $a_attachments = array_merge($a_attachments, $attachmnts);
747         }
748
749       // part is file/attachment
750       else if ($mail_part->disposition=='attachment' || $mail_part->disposition=='inline' || $mail_part->headers['content-id'] ||
751                (empty($mail_part->disposition) && $mail_part->filename))
752         {
753         // skip apple resource forks
754         if ($message_ctype_secondary=='appledouble' && $secondary_type=='applefile')
755           continue;
756
757         // part belongs to a related message
758         if ($message_ctype_secondary=='related' && $mail_part->headers['content-id'])
759           {
760           $mail_part->content_id = preg_replace(array('/^</', '/>$/'), '', $mail_part->headers['content-id']);
761           $sa_inline_objects[] = $mail_part;
762           }
763         // is regular attachment
764         else
765           {
766           if (!$mail_part->filename)
767             $mail_part->filename = 'file_'.$mail_part->mime_id;
768           $a_attachments[] = $mail_part;
769           }
770         }
771       }
772
773     // if this was a related part try to resolve references
774     if ($message_ctype_secondary=='related' && sizeof($sa_inline_objects))
775       {
776       $a_replaces = array();
777         
778       foreach ($sa_inline_objects as $inline_object)
779         $a_replaces['cid:'.$inline_object->content_id] = htmlspecialchars(sprintf($get_url, $inline_object->mime_id));
780       
781       // add replace array to each content part
782       // (will be applied later when part body is available)
783       for ($i=0; $i<count($a_return_parts); $i++)
784         {
785         if ($a_return_parts[$i]->type=='content')
786           $a_return_parts[$i]->replaces = $a_replaces;
787         }
788       }
789     }
790
791   // message is single part non-text
792   else if ($structure->filename)
793     $a_attachments[] = $structure;
794
795   return array($a_return_parts, $a_attachments);
796   }
797
798
799
800
801 // return table with message headers
802 function rcmail_message_headers($attrib, $headers=NULL)
803   {
804   global $IMAP, $OUTPUT, $MESSAGE;
805   static $sa_attrib;
806   
807   // keep header table attrib
808   if (is_array($attrib) && !$sa_attrib)
809     $sa_attrib = $attrib;
810   else if (!is_array($attrib) && is_array($sa_attrib))
811     $attrib = $sa_attrib;
812   
813   
814   if (!isset($MESSAGE))
815     return FALSE;
816
817   // get associative array of headers object
818   if (!$headers)
819     $headers = is_object($MESSAGE['headers']) ? get_object_vars($MESSAGE['headers']) : $MESSAGE['headers'];
820   
821   $header_count = 0;
822   
823   // allow the following attributes to be added to the <table> tag
824   $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id', 'cellpadding', 'cellspacing', 'border', 'summary'));
825   $out = '<table' . $attrib_str . ">\n";
826
827   // show these headers
828   $standard_headers = array('subject', 'from', 'organization', 'to', 'cc', 'bcc', 'reply-to', 'date');
829   
830   foreach ($standard_headers as $hkey)
831     {
832     if (!$headers[$hkey])
833       continue;
834
835     if ($hkey=='date' && !empty($headers[$hkey]))
836       $header_value = format_date(strtotime($headers[$hkey]));
837     else if (in_array($hkey, array('from', 'to', 'cc', 'bcc', 'reply-to')))
838       $header_value = Q(rcmail_address_string($headers[$hkey], NULL, $attrib['addicon']), 'show');
839     else
840       $header_value = Q(rcube_imap::decode_mime_string($headers[$hkey], $headers['charset']));
841
842     $out .= "\n<tr>\n";
843     $out .= '<td class="header-title">'.Q(rcube_label($hkey)).":&nbsp;</td>\n";
844     $out .= '<td class="'.$hkey.'" width="90%">'.$header_value."</td>\n</tr>";
845     $header_count++;
846     }
847
848   $out .= "\n</table>\n\n";
849
850   return $header_count ? $out : '';  
851   }
852
853
854
855 function rcmail_message_body($attrib)
856   {
857   global $CONFIG, $OUTPUT, $MESSAGE, $IMAP, $GET_URL, $REMOTE_OBJECTS;
858   
859   if (!is_array($MESSAGE['parts']) && !$MESSAGE['body'])
860     return '';
861     
862   if (!$attrib['id'])
863     $attrib['id'] = 'rcmailMsgBody';
864
865   $safe_mode = (bool)$_GET['_safe'];
866   $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id'));
867   $out = '<div '. $attrib_str . ">\n";
868   
869   $header_attrib = array();
870   foreach ($attrib as $attr => $value)
871     if (preg_match('/^headertable([a-z]+)$/i', $attr, $regs))
872       $header_attrib[$regs[1]] = $value;
873
874
875   // this is an ecrypted message
876   // -> create a plaintext body with the according message
877   if (!sizeof($MESSAGE['parts']) && $MESSAGE['headers']->ctype=='multipart/encrypted')
878     {
879     $p = new stdClass;
880     $p->type = 'content';
881     $p->ctype_primary = 'text';
882     $p->ctype_secondary = 'plain';
883     $p->body = rcube_label('encryptedmessage');
884     $MESSAGE['parts'][0] = $p;
885     }
886   
887   if ($MESSAGE['parts'])
888     {
889     foreach ($MESSAGE['parts'] as $i => $part)
890       {
891       if ($part->type=='headers')
892         $out .= rcmail_message_headers(sizeof($header_attrib) ? $header_attrib : NULL, $part->headers);
893       else if ($part->type=='content')
894         {
895         if (empty($part->ctype_parameters) || empty($part->ctype_parameters['charset']))
896           $part->ctype_parameters['charset'] = $MESSAGE['headers']->charset;
897
898         // fetch part if not available
899         if (!isset($part->body))
900           $part->body = $IMAP->get_message_part($MESSAGE['UID'], $part->mime_id, $part);
901
902         $body = rcmail_print_body($part, $safe_mode, !$CONFIG['prefer_html']);
903         $out .= '<div class="message-part">';
904         
905         if ($part->ctype_secondary != 'plain')
906           $out .= rcmail_sanitize_html($body, $attrib['id']);
907         else
908           $out .= $body;
909
910         $out .= "</div>\n";
911         }
912       }
913     }
914   else
915     $out .= $MESSAGE['body'];
916
917
918   $ctype_primary = strtolower($MESSAGE['structure']->ctype_primary);
919   $ctype_secondary = strtolower($MESSAGE['structure']->ctype_secondary);
920   
921   // list images after mail body
922   if (get_boolean($attrib['showimages']) && $ctype_primary=='multipart' &&
923       !empty($MESSAGE['attachments']) && !strstr($message_body, '<html') && strlen($GET_URL))
924     {
925     foreach ($MESSAGE['attachments'] as $attach_prop)
926       {
927       if (strpos($attach_prop->mimetype, 'image/')===0)
928         $out .= sprintf("\n<hr />\n<p align=\"center\"><img src=\"%s&amp;_part=%s\" alt=\"%s\" title=\"%s\" /></p>\n",
929                         htmlspecialchars($GET_URL), $attach_prop->mime_id,
930                         $attach_prop->filename,
931                         $attach_prop->filename);
932       }
933     }
934   
935   // tell client that there are blocked remote objects
936   if ($REMOTE_OBJECTS && !$safe_mode)
937     $OUTPUT->set_env('blockedobjects', true);
938
939   $out .= "\n</div>";
940   return $out;
941   }
942
943
944
945 // modify a HTML message that it can be displayed inside a HTML page
946 function rcmail_sanitize_html($body, $container_id)
947   {
948   // remove any null-byte characters before parsing
949   $body = preg_replace('/\x00/', '', $body);
950   
951   $base_url = "";
952   $last_style_pos = 0;
953   $body_lc = strtolower($body);
954   
955   // check for <base href>
956   if (preg_match(($base_reg = '/(<base.*href=["\']?)([hftps]{3,5}:\/{2}[^"\'\s]+)([^<]*>)/i'), $body, $base_regs))
957     $base_url = $base_regs[2];
958   
959   // find STYLE tags
960   while (($pos = strpos($body_lc, '<style', $last_style_pos)) && ($pos2 = strpos($body_lc, '</style>', $pos)))
961     {
962     $pos = strpos($body_lc, '>', $pos)+1;
963
964     // replace all css definitions with #container [def]
965     $styles = rcmail_mod_css_styles(substr($body, $pos, $pos2-$pos), $container_id, $base_url);
966
967     $body = substr($body, 0, $pos) . $styles . substr($body, $pos2);
968     $body_lc = strtolower($body);
969     $last_style_pos = $pos2;
970     }
971
972
973   // remove SCRIPT tags
974   foreach (array('script', 'applet', 'object', 'embed', 'iframe') as $tag)
975     {
976     while (($pos = strpos($body_lc, '<'.$tag)) && ($pos2 = strpos($body_lc, '</'.$tag.'>', $pos)))
977       {
978       $pos2 += strlen('</'.$tag.'>');
979       $body = substr($body, 0, $pos) . substr($body, $pos2, strlen($body)-$pos2);
980       $body_lc = strtolower($body);
981       }
982     }
983
984   // replace event handlers on any object
985   while ($body != $prev_body)
986     {
987     $prev_body = $body;
988     $body = preg_replace('/(<[^!][^>]*\s)(on[^=>]+)=([^>]+>)/im', '$1__removed=$3', $body);
989     $body = preg_replace('/(<[^!][^>]*\shref=["\']?)(javascript:)([^>]*?>)/im', '$1null:$3', $body);
990     }
991
992   // resolve <base href>
993   if ($base_url)
994     {
995     $body = preg_replace('/(src|background|href)=(["\']?)([\.\/]+[^"\'\s]+)(\2|\s|>)/Uie', "'\\1=\"'.make_absolute_url('\\3', '$base_url').'\"'", $body);
996     $body = preg_replace('/(url\s*\()(["\']?)([\.\/]+[^"\'\)\s]+)(\2)\)/Uie', "'\\1\''.make_absolute_url('\\3', '$base_url').'\')'", $body);
997     $body = preg_replace($base_reg, '', $body);
998     }
999     
1000   // modify HTML links to open a new window if clicked
1001   $body = preg_replace('/<(a|link)\s+([^>]+)>/Uie', "rcmail_alter_html_link('\\1','\\2', '$container_id');", $body);
1002
1003   // add comments arround html and other tags
1004   $out = preg_replace(array(
1005       '/(<!DOCTYPE.+)/i',
1006       '/(<\/?html[^>]*>)/i',
1007       '/(<\/?head[^>]*>)/i',
1008       '/(<title[^>]*>.*<\/title>)/Ui',
1009       '/(<\/?meta[^>]*>)/i'),
1010     '<!--\\1-->',
1011     $body);
1012
1013   $out = preg_replace(
1014     array(
1015       '/<body([^>]*)>/i',
1016       '/<\/body>/i',
1017     ),
1018     array(
1019       '<div class="rcmBody"\\1>',
1020       '</div>',
1021     ),
1022     $out);
1023
1024   // quote <? of php and xml files that are specified as text/html
1025   $out = preg_replace(array('/<\?/', '/\?>/'), array('&lt;?', '?&gt;'), $out);
1026
1027   return $out;
1028   }
1029
1030
1031 // parse link attributes and set correct target
1032 function rcmail_alter_html_link($tag, $attrs, $container_id)
1033   {
1034   $in = preg_replace('/=([^("|\'|\s)]+)(\s|$)/', '="\1"', $in);
1035   $attrib = parse_attrib_string($attrs);
1036   
1037   if ($tag == 'link' && preg_match('/^https?:\/\//i', $attrib['href']))
1038     $attrib['href'] = "./bin/modcss.php?u=" . urlencode($attrib['href']) . "&amp;c=" . urlencode($container_id);
1039
1040   else if (stristr((string)$attrib['href'], 'mailto:'))
1041     $attrib['onclick'] = sprintf(
1042       "return %s.command('compose','%s',this)",
1043       JS_OBJECT_NAME,
1044       JQ(substr($attrib['href'], 7)));
1045   
1046   else if (!empty($attrib['href']) && $attrib['href']{0}!='#')
1047     $attrib['target'] = '_blank';
1048
1049   return "<$tag" . create_attrib_string($attrib, array('href','name','target','onclick','id','class','style','title','rel','type','media')) . ' />';
1050   }
1051
1052
1053 function rcmail_has_html_part($message_parts)
1054 {
1055    if (!is_array($message_parts))
1056       return FALSE;
1057
1058    // check all message parts
1059    foreach ($message_parts as $pid => $part)
1060    {
1061       $mimetype = strtolower($part->ctype_primary.'/'.$part->ctype_secondary);
1062       if ($mimetype=='text/html')
1063       {
1064          return TRUE;
1065       }
1066    }
1067     
1068    return FALSE;
1069 }
1070
1071 // return first HTML part of a message
1072 function rcmail_first_html_part($message_struct)
1073   {
1074   global $IMAP;
1075
1076   if (!is_array($message_struct['parts']))
1077     return FALSE;
1078     
1079   $html_part = NULL;
1080
1081   // check all message parts
1082   foreach ($message_struct['parts'] as $pid => $part)
1083     {
1084     $mimetype = strtolower($part->ctype_primary.'/'.$part->ctype_secondary);
1085     if ($mimetype=='text/html')
1086       {
1087       $html_part = $IMAP->get_message_part($message_struct['UID'], $pid, $part);
1088       }
1089     }
1090
1091   if ($html_part)
1092     {
1093     // remove special chars encoding
1094     //$trans = array_flip(get_html_translation_table(HTML_ENTITIES));
1095     //$html_part = strtr($html_part, $trans);
1096
1097     return $html_part;
1098     }
1099
1100   return FALSE;
1101 }
1102
1103
1104 // return first text part of a message
1105 function rcmail_first_text_part($message_struct)
1106   {
1107   global $IMAP;
1108
1109   if (empty($message_struct['parts']))
1110     return $message_struct['UID'] ? $IMAP->get_body($message_struct['UID']) : false;
1111
1112   // check all message parts
1113   foreach ($message_struct['parts'] as $pid => $part)
1114     {
1115     $mimetype = strtolower($part->ctype_primary.'/'.$part->ctype_secondary);
1116
1117     if ($mimetype=='text/plain')
1118       return $IMAP->get_message_part($message_struct['UID'], $pid, $part);
1119
1120     else if ($mimetype=='text/html')
1121       {
1122       $html_part = $IMAP->get_message_part($message_struct['UID'], $pid, $part);
1123       
1124       // remove special chars encoding
1125       $trans = array_flip(get_html_translation_table(HTML_ENTITIES));
1126       $html_part = strtr($html_part, $trans);
1127
1128       // create instance of html2text class
1129       $txt = new html2text($html_part);
1130       return $txt->get_text();
1131       }
1132     }
1133
1134   return FALSE;
1135   }
1136
1137
1138 // decode address string and re-format it as HTML links
1139 function rcmail_address_string($input, $max=NULL, $addicon=NULL)
1140   {
1141   global $IMAP, $PRINT_MODE, $CONFIG, $OUTPUT, $EMAIL_ADDRESS_PATTERN;
1142   
1143   $a_parts = $IMAP->decode_address_list($input);
1144
1145   if (!sizeof($a_parts))
1146     return $input;
1147
1148   $c = count($a_parts);
1149   $j = 0;
1150   $out = '';
1151
1152   foreach ($a_parts as $part)
1153     {
1154     $j++;
1155     if ($PRINT_MODE)
1156       $out .= sprintf('%s &lt;%s&gt;', Q($part['name']), $part['mailto']);
1157     else if (preg_match($EMAIL_ADDRESS_PATTERN, $part['mailto']))
1158       {
1159       $out .= sprintf('<a href="mailto:%s" onclick="return %s.command(\'compose\',\'%s\',this)" class="rcmContactAddress" title="%s">%s</a>',
1160                       Q($part['mailto']),
1161                       JS_OBJECT_NAME,
1162                       JQ($part['mailto']),
1163                       Q($part['mailto']),
1164                       Q($part['name']));
1165                       
1166       if ($addicon)
1167         $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>',
1168                         JS_OBJECT_NAME,
1169                         urlencode($part['string']),
1170                         rcube_label('addtoaddressbook'),
1171                         $CONFIG['skin_path'],
1172                         $addicon);
1173       }
1174     else
1175       {
1176       if ($part['name'])
1177         $out .= Q($part['name']);
1178       if ($part['mailto'])
1179         $out .= (strlen($out) ? ' ' : '') . sprintf('&lt;%s&gt;', Q($part['mailto']));
1180       }
1181       
1182     if ($c>$j)
1183       $out .= ','.($max ? '&nbsp;' : ' ');
1184         
1185     if ($max && $j==$max && $c>$j)
1186       {
1187       $out .= '...';
1188       break;
1189       }        
1190     }
1191     
1192   return $out;
1193   }
1194
1195
1196 function rcmail_message_part_controls()
1197   {
1198   global $CONFIG, $IMAP, $MESSAGE;
1199   
1200   if (!is_array($MESSAGE) || !is_array($MESSAGE['parts']) || !($_GET['_uid'] && $_GET['_part']) || !$MESSAGE['parts'][$_GET['_part']])
1201     return '';
1202     
1203   $part = &$MESSAGE['parts'][$_GET['_part']];
1204   
1205   $attrib_str = create_attrib_string($attrib, array('id', 'class', 'style', 'cellspacing', 'cellpadding', 'border', 'summary'));
1206   $out = '<table '. $attrib_str . ">\n";
1207   
1208   if ($filename)
1209     {
1210     $out .= sprintf('<tr><td class="title">%s</td><td>%s</td><td>[<a href="./?%s">%s</a>]</tr>'."\n",
1211                     Q(rcube_label('filename')),
1212                     Q($part->filename),
1213                     str_replace('_frame=', '_download=', $_SERVER['QUERY_STRING']),
1214                     Q(rcube_label('download')));
1215     }
1216     
1217   if ($part->size)
1218     $out .= sprintf('<tr><td class="title">%s</td><td>%s</td></tr>'."\n",
1219                     Q(rcube_label('filesize')),
1220                     show_bytes($part->size));
1221   
1222   $out .= "\n</table>";
1223   
1224   return $out;
1225   }
1226
1227
1228
1229 function rcmail_message_part_frame($attrib)
1230   {
1231   global $MESSAGE;
1232   
1233   $part = $MESSAGE['parts'][$_GET['_part']];
1234   $ctype_primary = strtolower($part->ctype_primary);
1235
1236   $attrib['src'] = Q('./?'.str_replace('_frame=', ($ctype_primary=='text' ? '_show=' : '_preload='), $_SERVER['QUERY_STRING']));
1237
1238   $attrib_str = create_attrib_string($attrib, array('id', 'class', 'style', 'src', 'width', 'height'));
1239   $out = '<iframe '. $attrib_str . "></iframe>";
1240     
1241   return $out;
1242   }
1243
1244
1245 // clear message composing settings
1246 function rcmail_compose_cleanup()
1247   {
1248   if (!isset($_SESSION['compose']))
1249     return;
1250
1251   // remove attachment files from temp dir
1252   if (is_array($_SESSION['compose']['attachments']))
1253     foreach ($_SESSION['compose']['attachments'] as $attachment)
1254       @unlink($attachment['path']);
1255   
1256   unset($_SESSION['compose']);
1257   }
1258
1259
1260 // register UI objects
1261 $OUTPUT->add_handlers(array(
1262   'mailboxlist' => 'rcmail_mailbox_list',
1263   'messages' => 'rcmail_message_list',
1264   'messagecountdisplay' => 'rcmail_messagecount_display',
1265   'quotadisplay' => 'rcmail_quota_display',
1266   'messageheaders' => 'rcmail_message_headers',
1267   'messagebody' => 'rcmail_message_body',
1268   'messagecontentframe' => 'rcmail_messagecontent_frame',
1269   'messagepartframe' => 'rcmail_message_part_frame',
1270   'messagepartcontrols' => 'rcmail_message_part_controls',
1271   'searchform' => 'rcmail_search_form'
1272 ));
1273
1274 ?>