]> git.donarmstrong.com Git - roundcube.git/blob - program/steps/mail/func.inc
Imported Upstream version 0.1~rc1~dfsg
[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 575 2007-05-18 12:35: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');
77
78
79 // return the mailboxlist in HTML
80 function rcmail_mailbox_list($attrib)
81   {
82   global $IMAP, $CONFIG, $OUTPUT, $COMM_PATH;
83   static $s_added_script = FALSE;
84   static $a_mailboxes;
85
86   // add some labels to client
87   rcube_add_label('purgefolderconfirm');
88   rcube_add_label('deletemessagesconfirm');
89   
90 // $mboxlist_start = rcube_timer();
91   
92   $type = $attrib['type'] ? $attrib['type'] : 'ul';
93   $add_attrib = $type=='select' ? array('style', 'class', 'id', 'name', 'onchange') :
94                                   array('style', 'class', 'id');
95                                   
96   if ($type=='ul' && !$attrib['id'])
97     $attrib['id'] = 'rcmboxlist';
98
99   // allow the following attributes to be added to the <ul> tag
100   $attrib_str = create_attrib_string($attrib, $add_attrib);
101  
102   $out = '<' . $type . $attrib_str . ">\n";
103   
104   // add no-selection option
105   if ($type=='select' && $attrib['noselection'])
106     $out .= sprintf('<option value="0">%s</option>'."\n",
107                     rcube_label($attrib['noselection']));
108   
109   // get mailbox list
110   $mbox_name = $IMAP->get_mailbox_name();
111   
112   // for these mailboxes we have localized labels
113   $special_mailboxes = array('inbox', 'sent', 'drafts', 'trash', 'junk');
114
115
116   // build the folders tree
117   if (empty($a_mailboxes))
118     {
119     // get mailbox list
120     $a_folders = $IMAP->list_mailboxes();
121     $delimiter = $IMAP->get_hierarchy_delimiter();
122     $a_mailboxes = array();
123
124 // rcube_print_time($mboxlist_start, 'list_mailboxes()');
125
126     foreach ($a_folders as $folder)
127       rcmail_build_folder_tree($a_mailboxes, $folder, $delimiter);
128     }
129
130 // var_dump($a_mailboxes);
131
132   if ($type=='select')
133     $out .= rcmail_render_folder_tree_select($a_mailboxes, $special_mailboxes, $mbox_name, $attrib['maxlength']);
134    else
135     $out .= rcmail_render_folder_tree_html($a_mailboxes, $special_mailboxes, $mbox_name, $attrib['maxlength']);
136
137 // rcube_print_time($mboxlist_start, 'render_folder_tree()');
138
139
140   if ($type=='ul')
141     $OUTPUT->add_gui_object('mailboxlist', $attrib['id']);
142
143   return $out . "</$type>";
144   }
145
146
147
148
149 // create a hierarchical array of the mailbox list
150 function rcmail_build_folder_tree(&$arrFolders, $folder, $delm='/', $path='')
151   {
152   $pos = strpos($folder, $delm);
153   if ($pos !== false)
154     {
155     $subFolders = substr($folder, $pos+1);
156     $currentFolder = substr($folder, 0, $pos);
157     }
158   else
159     {
160     $subFolders = false;
161     $currentFolder = $folder;
162     }
163
164   $path .= $currentFolder;
165
166   if (!isset($arrFolders[$currentFolder]))
167     {
168     $arrFolders[$currentFolder] = array('id' => $path,
169                                         'name' => rcube_charset_convert($currentFolder, 'UTF-7'),
170                                         'folders' => array());
171     }
172
173   if (!empty($subFolders))
174     rcmail_build_folder_tree($arrFolders[$currentFolder]['folders'], $subFolders, $delm, $path.$delm);
175   }
176   
177
178 // return html for a structured list <ul> for the mailbox tree
179 function rcmail_render_folder_tree_html(&$arrFolders, &$special, &$mbox_name, $maxlength, $nestLevel=0)
180   {
181   global $COMM_PATH, $IMAP, $CONFIG, $OUTPUT;
182
183   $idx = 0;
184   $out = '';
185   foreach ($arrFolders as $key => $folder)
186     {
187     $zebra_class = ($nestLevel*$idx)%2 ? 'even' : 'odd';
188     $title = '';
189
190     $folder_lc = strtolower($folder['id']);
191     if (in_array($folder_lc, $special))
192       $foldername = rcube_label($folder_lc);
193     else
194       {
195       $foldername = $folder['name'];
196
197       // shorten the folder name to a given length
198       if ($maxlength && $maxlength>1)
199         {
200         $fname = abbrevate_string($foldername, $maxlength);
201         if ($fname != $foldername)
202           $title = ' title="'.Q($foldername).'"';
203         $foldername = $fname;
204         }
205       }
206
207     // add unread message count display
208     if ($unread_count = $IMAP->messagecount($folder['id'], 'RECENT', ($folder['id']==$mbox_name)))
209       $foldername .= sprintf(' (%d)', $unread_count);
210
211     // make folder name safe for ids and class names
212     $folder_id = preg_replace('/[^A-Za-z0-9\-_]/', '', $folder['id']);
213     $class_name = preg_replace('/[^a-z0-9\-_]/', '', $folder_lc);
214
215     // set special class for Sent, Drafts, Trash and Junk
216     if ($folder['id']==$CONFIG['sent_mbox'])
217       $class_name = 'sent';
218     else if ($folder['id']==$CONFIG['drafts_mbox'])
219       $class_name = 'drafts';
220     else if ($folder['id']==$CONFIG['trash_mbox'])
221       $class_name = 'trash';
222     else if ($folder['id']==$CONFIG['junk_mbox'])
223       $class_name = 'junk';
224
225     $js_name = htmlspecialchars(JQ($folder['id']));
226     $out .= sprintf('<li id="rcmli%s" class="mailbox %s %s%s%s"><a href="%s"'.
227                     ' onclick="return %s.command(\'list\',\'%s\',this)"'.
228                     ' onmouseover="return %s.focus_folder(\'%s\')"' .
229                     ' onmouseout="return %s.unfocus_folder(\'%s\')"' .
230                     ' onmouseup="return %s.folder_mouse_up(\'%s\')"%s>%s</a>',
231                     $folder_id,
232                     $class_name,
233                     $zebra_class,
234                     $unread_count ? ' unread' : '',
235                     $folder['id']==$mbox_name ? ' selected' : '',
236                     Q(rcmail_url('', array('_mbox' => $folder['id']))),
237                     JS_OBJECT_NAME,
238                     $js_name,
239                     JS_OBJECT_NAME,
240                     $js_name,
241                     JS_OBJECT_NAME,
242                     $js_name,
243                     JS_OBJECT_NAME,
244                     $js_name,
245                     $title,
246                     Q($foldername));
247
248     if (!empty($folder['folders']))
249       $out .= "\n<ul>\n" . rcmail_render_folder_tree_html($folder['folders'], $special, $mbox_name, $maxlength, $nestLevel+1) . "</ul>\n";
250
251     $out .= "</li>\n";
252     $idx++;
253     }
254
255   return $out;
256   }
257
258
259 // return html for a flat list <select> for the mailbox tree
260 function rcmail_render_folder_tree_select(&$arrFolders, &$special, &$mbox_name, $maxlength, $nestLevel=0)
261   {
262   global $IMAP, $OUTPUT;
263
264   $idx = 0;
265   $out = '';
266   foreach ($arrFolders as $key=>$folder)
267     {
268     $folder_lc = strtolower($folder['id']);
269     if (in_array($folder_lc, $special))
270       $foldername = rcube_label($folder_lc);
271     else
272       {
273       $foldername = $folder['name'];
274       
275       // shorten the folder name to a given length
276       if ($maxlength && $maxlength>1)
277         $foldername = abbrevate_string($foldername, $maxlength);
278       }
279
280     $out .= sprintf('<option value="%s">%s%s</option>'."\n",
281                     htmlspecialchars($folder['id']),
282                     str_repeat('&nbsp;', $nestLevel*4),
283                     Q($foldername));
284
285     if (!empty($folder['folders']))
286       $out .= rcmail_render_folder_tree_select($folder['folders'], $special, $mbox_name, $maxlength, $nestLevel+1);
287
288     $idx++;
289     }
290
291   return $out;
292   }
293
294
295 // return the message list as HTML table
296 function rcmail_message_list($attrib)
297   {
298   global $IMAP, $CONFIG, $COMM_PATH, $OUTPUT;
299
300   $skin_path = $CONFIG['skin_path'];
301   $image_tag = '<img src="%s%s" alt="%s" border="0" />';
302
303   // check to see if we have some settings for sorting
304   $sort_col   = $_SESSION['sort_col'];
305   $sort_order = $_SESSION['sort_order'];
306   
307   // add some labels to client
308   rcube_add_label('from', 'to');
309
310   // get message headers
311   $a_headers = $IMAP->list_headers('', '', $sort_col, $sort_order);
312
313   // add id to message list table if not specified
314   if (!strlen($attrib['id']))
315     $attrib['id'] = 'rcubemessagelist';
316
317   // allow the following attributes to be added to the <table> tag
318   $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id', 'cellpadding', 'cellspacing', 'border', 'summary'));
319
320   $out = '<table' . $attrib_str . ">\n";
321
322
323   // define list of cols to be displayed
324   $a_show_cols = is_array($CONFIG['list_cols']) ? $CONFIG['list_cols'] : array('subject');
325   $a_sort_cols = array('subject', 'date', 'from', 'to', 'size');
326
327   $mbox = $IMAP->get_mailbox_name();
328   
329   // show 'to' instead of from in sent messages
330   if (($mbox==$CONFIG['sent_mbox'] || $mbox==$CONFIG['drafts_mbox']) && ($f = array_search('from', $a_show_cols))
331       && !array_search('to', $a_show_cols))
332     $a_show_cols[$f] = 'to';
333   
334   // add col definition
335   $out .= '<colgroup>';
336   $out .= '<col class="icon" />';
337
338   foreach ($a_show_cols as $col)
339     $out .= sprintf('<col class="%s" />', $col);
340
341   $out .= '<col class="icon" />';
342   $out .= "</colgroup>\n";
343
344   // add table title
345   $out .= "<thead><tr>\n<td class=\"icon\">&nbsp;</td>\n";
346
347   $javascript = '';
348   foreach ($a_show_cols as $col)
349     {
350     // get column name
351     $col_name = Q(rcube_label($col));
352
353     // make sort links
354     $sort = '';
355     if ($IMAP->get_capability('sort') && in_array($col, $a_sort_cols))
356       {
357       // have buttons configured
358       if (!empty($attrib['sortdescbutton']) || !empty($attrib['sortascbutton']))
359         {
360         $sort = '&nbsp;&nbsp;';
361
362         // asc link
363         if (!empty($attrib['sortascbutton']))
364           {
365           $sort .= $OUTPUT->button(array(
366             'command' => 'sort',
367             'prop' => $col.'_ASC',
368             'image' => $attrib['sortascbutton'],
369             'align' => 'absmiddle',
370             'title' => 'sortasc'));
371           }       
372         
373         // desc link
374         if (!empty($attrib['sortdescbutton']))
375           {
376           $sort .= $OUTPUT->button(array(
377             'command' => 'sort',
378             'prop' => $col.'_DESC',
379             'image' => $attrib['sortdescbutton'],
380             'align' => 'absmiddle',
381             'title' => 'sortdesc'));
382           }
383         }
384       // just add a link tag to the header
385       else
386         {
387         $col_name = sprintf(
388           '<a href="./#sort" onclick="return %s.command(\'sort\',\'%s\',this)" title="%s">%s</a>',
389           JS_OBJECT_NAME,
390           $col,
391           rcube_label('sortby'),
392           $col_name);
393         }
394       }
395       
396     $sort_class = $col==$sort_col ? " sorted$sort_order" : '';
397
398     // put it all together
399     $out .= '<td class="'.$col.$sort_class.'" id="rcmHead'.$col.'">' . "$col_name$sort</td>\n";    
400     }
401
402   $out .= '<td class="icon">'.($attrib['attachmenticon'] ? sprintf($image_tag, $skin_path, $attrib['attachmenticon'], '') : '')."</td>\n";
403   $out .= "</tr></thead>\n<tbody>\n";
404
405   // no messages in this mailbox
406   if (!sizeof($a_headers))
407     {
408     $out .= sprintf('<tr><td colspan="%d">%s</td></tr>',
409                     sizeof($a_show_cols)+2,
410                     Q(rcube_label('nomessagesfound')));
411     }
412
413
414   $a_js_message_arr = array();
415
416   // create row for each message
417   foreach ($a_headers as $i => $header)  //while (list($i, $header) = each($a_headers))
418     {
419     $message_icon = $attach_icon = '';
420     $js_row_arr = array();
421     $zebra_class = $i%2 ? 'even' : 'odd';
422
423     // set messag attributes to javascript array
424     if ($header->deleted)
425       $js_row_arr['deleted'] = true;
426     if (!$header->seen)
427       $js_row_arr['unread'] = true;
428     if ($header->answered)
429       $js_row_arr['replied'] = true;
430     // set message icon  
431     if ($attrib['deletedicon'] && $header->deleted)
432       $message_icon = $attrib['deletedicon'];
433     else if ($attrib['unreadicon'] && !$header->seen)
434       $message_icon = $attrib['unreadicon'];
435     else if ($attrib['repliedicon'] && $header->answered)
436       $message_icon = $attrib['repliedicon'];
437     else if ($attrib['messageicon'])
438       $message_icon = $attrib['messageicon'];
439     
440     // set attachment icon
441     if ($attrib['attachmenticon'] && preg_match("/multipart\/[mr]/i", $header->ctype))
442       $attach_icon = $attrib['attachmenticon'];
443         
444     $out .= sprintf('<tr id="rcmrow%d" class="message%s%s %s">'."\n",
445                     $header->uid,
446                     $header->seen ? '' : ' unread',
447                     $header->deleted ? ' deleted' : '',
448                     $zebra_class);    
449     
450     $out .= sprintf("<td class=\"icon\">%s</td>\n", $message_icon ? sprintf($image_tag, $skin_path, $message_icon, '') : '');
451     
452     // format each col
453     foreach ($a_show_cols as $col)
454       {
455       if ($col=='from' || $col=='to')
456         $cont = Q(rcmail_address_string($header->$col, 3, $attrib['addicon']), 'show');
457       else if ($col=='subject')
458         {
459         $action = $mbox==$CONFIG['drafts_mbox'] ? 'compose' : 'show';
460         $uid_param = $mbox==$CONFIG['drafts_mbox'] ? '_draf_uid' : '_uid';
461         $cont = Q(rcube_imap::decode_mime_string($header->$col, $header->charset));
462         if (empty($cont)) $cont = Q(rcube_label('nosubject'));
463         $cont = sprintf('<a href="%s" onclick="return false">%s</a>', Q(rcmail_url($action, array($uid_param=>$header->uid, '_mbox'=>$mbox))), $cont);
464         }
465       else if ($col=='size')
466         $cont = show_bytes($header->$col);
467       else if ($col=='date')
468         $cont = format_date($header->date);
469       else
470         $cont = Q($header->$col);
471         
472       $out .= '<td class="'.$col.'">' . $cont . "</td>\n";
473       }
474
475     $out .= sprintf("<td class=\"icon\">%s</td>\n", $attach_icon ? sprintf($image_tag, $skin_path, $attach_icon, '') : '');
476     $out .= "</tr>\n";
477     
478     if (sizeof($js_row_arr))
479       $a_js_message_arr[$header->uid] = $js_row_arr;
480     }
481   
482   // complete message table
483   $out .= "</tbody></table>\n";
484   
485   
486   $message_count = $IMAP->messagecount();
487   
488   // set client env
489   $OUTPUT->add_gui_object('mailcontframe', 'mailcontframe');
490   $OUTPUT->add_gui_object('messagelist', $attrib['id']);
491   $OUTPUT->set_env('messagecount', $message_count);
492   $OUTPUT->set_env('current_page', $IMAP->list_page);
493   $OUTPUT->set_env('pagecount', ceil($message_count/$IMAP->page_size));
494   $OUTPUT->set_env('sort_col', $sort_col);
495   $OUTPUT->set_env('sort_order', $sort_order);
496   
497   if ($attrib['messageicon'])
498     $OUTPUT->set_env('messageicon', $skin_path . $attrib['messageicon']);
499   if ($attrib['deletedicon'])
500     $OUTPUT->set_env('deletedicon', $skin_path . $attrib['deletedicon']);
501   if ($attrib['unreadicon'])
502     $OUTPUT->set_env('unreadicon', $skin_path . $attrib['unreadicon']);
503   if ($attrib['repliedicon'])
504     $OUTPUT->set_env('repliedicon', $skin_path . $attrib['repliedicon']);
505   if ($attrib['attachmenticon'])
506     $OUTPUT->set_env('attachmenticon', $skin_path . $attrib['attachmenticon']);
507   
508   $OUTPUT->set_env('messages', $a_js_message_arr);
509   
510   $OUTPUT->include_script('list.js');
511   
512   return $out;
513   }
514
515
516 // return javascript commands to add rows to the message list
517 function rcmail_js_message_list($a_headers, $insert_top=FALSE)
518   {
519   global $CONFIG, $IMAP, $OUTPUT;
520
521   $a_show_cols = is_array($CONFIG['list_cols']) ? $CONFIG['list_cols'] : array('subject');
522   $mbox = $IMAP->get_mailbox_name();
523
524   // show 'to' instead of from in sent messages
525   if (($mbox == $CONFIG['sent_mbox'] || $mbox == $CONFIG['drafts_mbox'])
526       && (($f = array_search('from', $a_show_cols)) !== false) && array_search('to', $a_show_cols) === false)
527     $a_show_cols[$f] = 'to';
528
529   $OUTPUT->command('set_message_coltypes', $a_show_cols);
530
531   // loop through message headers
532   for ($n=0; $a_headers[$n]; $n++)
533     {
534     $header = $a_headers[$n];
535     $a_msg_cols = array();
536     $a_msg_flags = array();
537
538     // format each col; similar as in rcmail_message_list()
539     foreach ($a_show_cols as $col)
540       {
541       if ($col=='from' || $col=='to')
542         $cont = Q(rcmail_address_string($header->$col, 3), 'show');
543       else if ($col=='subject')
544         {
545         $action = $mbox==$CONFIG['drafts_mbox'] ? 'compose' : 'show';
546         $uid_param = $mbox==$CONFIG['drafts_mbox'] ? '_draf_uid' : '_uid';
547         $cont = Q(rcube_imap::decode_mime_string($header->$col, $header->charset));
548         if (!$cont) $cont = Q(rcube_label('nosubject'));
549         $cont = sprintf('<a href="%s" onclick="return false">%s</a>', Q(rcmail_url($action, array($uid_param=>$header->uid, '_mbox'=>$mbox))), $cont);
550         }
551       else if ($col=='size')
552         $cont = show_bytes($header->$col);
553       else if ($col=='date')
554         $cont = format_date($header->date);
555       else
556         $cont = Q($header->$col);
557           
558       $a_msg_cols[$col] = $cont;
559       }
560
561     $a_msg_flags['deleted'] = $header->deleted ? 1 : 0;
562     $a_msg_flags['unread'] = $header->seen ? 0 : 1;
563     $a_msg_flags['replied'] = $header->answered ? 1 : 0;
564     $OUTPUT->command('add_message_row',
565       $header->uid,
566       $a_msg_cols,
567       $a_msg_flags,
568       preg_match("/multipart\/m/i", $header->ctype),
569       $insert_top);
570     }
571   }
572
573
574 // return an HTML iframe for loading mail content
575 function rcmail_messagecontent_frame($attrib)
576   {
577   global $OUTPUT;
578   
579   if (empty($attrib['id']))
580     $attrib['id'] = 'rcmailcontentwindow';
581
582   // allow the following attributes to be added to the <iframe> tag
583   $attrib_str = create_attrib_string($attrib, array('id', 'class', 'style', 'src', 'width', 'height', 'frameborder'));
584   $framename = $attrib['id'];
585
586   $out = sprintf('<iframe name="%s"%s></iframe>'."\n",
587          $framename,
588          $attrib_str);
589
590   $OUTPUT->set_env('contentframe', $framename);
591   $OUTPUT->set_env('blankpage', $attrib['src'] ? $OUTPUT->abs_url($attrib['src']) : 'program/blank.gif');
592
593   return $out;
594   }
595
596
597 function rcmail_messagecount_display($attrib)
598   {
599   global $IMAP, $OUTPUT;
600   
601   if (!$attrib['id'])
602     $attrib['id'] = 'rcmcountdisplay';
603
604   $OUTPUT->add_gui_object('countdisplay', $attrib['id']);
605
606   // allow the following attributes to be added to the <span> tag
607   $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id'));
608
609   
610   $out = '<span' . $attrib_str . '>';
611   $out .= rcmail_get_messagecount_text();
612   $out .= '</span>';
613   return $out;
614   }
615
616
617 function rcmail_quota_display($attrib)
618   {
619   global $OUTPUT, $COMM_PATH;
620
621   if (!$attrib['id'])
622     $attrib['id'] = 'rcmquotadisplay';
623
624   $OUTPUT->add_gui_object('quotadisplay', $attrib['id']);
625
626   // allow the following attributes to be added to the <span> tag
627   $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id'));
628
629   $out = '<span' . $attrib_str . '>';
630   $out .= rcmail_quota_content($attrib['display']);
631   $out .= '</span>';
632   return $out;
633   }
634
635
636 function rcmail_quota_content($display)
637   {
638   global $IMAP, $COMM_PATH;
639
640   if (!$IMAP->get_capability('QUOTA'))
641     $quota_text = rcube_label('unknown');
642   else if ($quota = $IMAP->get_quota())
643     {
644     $quota_text = sprintf("%s / %s (%.0f%%)",
645                           show_bytes($quota["used"] * 1024),
646                           show_bytes($quota["total"] * 1024),
647                           $quota["percent"]);
648
649     // show quota as image (by Brett Patterson)
650     if ($display == 'image' && function_exists('imagegif'))
651       {
652       $attrib = array('width' => 100, 'height' => 14);
653       $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" />',
654                             $quota['used'], $quota['total'],
655                             $attrib['width'], $attrib['height'],
656                             $attrib['width'], $attrib['height'],
657                             $quota_text,
658                             show_bytes($quota["used"] * 1024),
659                             show_bytes($quota["total"] * 1024));
660       }
661     }
662   else
663     $quota_text = rcube_label('unlimited');
664
665   return $quota_text;
666   }
667
668
669 function rcmail_get_messagecount_text($count=NULL, $page=NULL)
670   {
671   global $IMAP, $MESSAGE;
672   
673   if (isset($MESSAGE['index']))
674     {
675     return rcube_label(array('name' => 'messagenrof',
676                              'vars' => array('nr'  => $MESSAGE['index']+1,
677                                              'count' => $count!==NULL ? $count : $IMAP->messagecount())));
678     }
679
680   if ($page===NULL)
681     $page = $IMAP->list_page;
682     
683   $start_msg = ($page-1) * $IMAP->page_size + 1;
684   $max = $count!==NULL ? $count : $IMAP->messagecount();
685
686   if ($max==0)
687     $out = rcube_label('mailboxempty');
688   else
689     $out = rcube_label(array('name' => 'messagesfromto',
690                               'vars' => array('from'  => $start_msg,
691                                               'to'    => min($max, $start_msg + $IMAP->page_size - 1),
692                                               'count' => $max)));
693
694   return Q($out);
695   }
696
697
698 function rcmail_print_body($part, $safe=FALSE, $plain=FALSE)
699   {
700   global $IMAP, $REMOTE_OBJECTS;
701   
702   $body = is_array($part->replaces) ? strtr($part->body, $part->replaces) : $part->body;
703
704   // convert html to text/plain
705   if ($part->ctype_secondary=='html' && $plain)
706     {
707     $txt = new html2text($body, false, true);
708     $body = $txt->get_text();
709     $part->ctype_secondary = 'plain';
710     }
711     
712   // text/html
713   if ($part->ctype_secondary=='html')
714     {
715     // remove charset specification in HTML message
716     $body = preg_replace('/charset=[a-z0-9\-]+/i', '', $body);
717
718     if (!$safe)  // remove remote images and scripts
719       {
720       $remote_patterns = array('/<img\s+(.*)src=(["\']?)([hftps]{3,5}:\/{2}[^"\'\s]+)(\2|\s|>)/Ui',
721                                '/(src|background)=(["\']?)([hftps]{3,5}:\/{2}[^"\'\s]+)(\2|\s|>)/Ui',
722                                '/(<base.*href=["\']?)([hftps]{3,5}:\/{2}[^"\'\s]+)([^<]*>)/i',
723                                '/(<link.*href=["\']?)([hftps]{3,5}:\/{2}[^"\'\s]+)([^<]*>)/i',
724                                '/url\s*\(["\']?([hftps]{3,5}:\/{2}[^"\'\s]+)["\']?\)/i',
725                                '/url\s*\(["\']?([\.\/]+[^"\'\s]+)["\']?\)/i',
726                                '/<script.+<\/script>/Umis');
727
728       $remote_replaces = array('<img \\1src=\\2./program/blocked.gif\\4',
729                                '',
730                                '',
731                                '',
732                                'none',
733                                'none',
734                                '');
735       
736       // set flag if message containes remote obejcts that where blocked
737       foreach ($remote_patterns as $pattern)
738         {
739         if (preg_match($pattern, $body))
740           {
741           $REMOTE_OBJECTS = TRUE;
742           break;
743           }
744         }
745
746       $body = preg_replace($remote_patterns, $remote_replaces, $body);
747       }
748
749     return Q($body, 'show', FALSE);
750     }
751
752   // text/enriched
753   if ($part->ctype_secondary=='enriched')
754     {
755     return Q(enriched_to_html($body), 'show');
756     }
757   else
758     {
759     // make links and email-addresses clickable
760     $convert_patterns = $convert_replaces = $replace_strings = array();
761     
762     $url_chars = 'a-z0-9_\-\+\*\$\/&%=@#:;';
763     $url_chars_within = '\?\.~,!';
764
765     $convert_patterns[] = "/([\w]+):\/\/([a-z0-9\-\.]+[a-z]{2,4}([$url_chars$url_chars_within]*[$url_chars])?)/ie";
766     $convert_replaces[] = "rcmail_str_replacement('<a href=\"\\1://\\2\" target=\"_blank\">\\1://\\2</a>', \$replace_strings)";
767
768     $convert_patterns[] = "/([^\/:]|\s)(www\.)([a-z0-9\-]{2,}[a-z]{2,4}([$url_chars$url_chars_within]*[$url_chars])?)/ie";
769     $convert_replaces[] = "rcmail_str_replacement('\\1<a href=\"http://\\2\\3\" target=\"_blank\">\\2\\3</a>', \$replace_strings)";
770     
771     $convert_patterns[] = '/([a-z0-9][a-z0-9\-\.\+\_]*@[a-z0-9]([a-z0-9\-][.]?)*[a-z0-9]\\.[a-z]{2,5})/ie';
772     $convert_replaces[] = "rcmail_str_replacement('<a href=\"mailto:\\1\" onclick=\"return ".JS_OBJECT_NAME.".command(\'compose\',\'\\1\',this)\">\\1</a>', \$replace_strings)";
773     
774     if ($part->ctype_parameters['format'] != 'flowed')
775       $body = wordwrap(trim($body), 80);
776
777     $body = preg_replace($convert_patterns, $convert_replaces, $body);
778
779     // split body into single lines
780     $a_lines = preg_split('/\r?\n/', $body);
781     $quote_level = 0;
782
783     // colorize quoted parts
784     for($n=0; $n<sizeof($a_lines); $n++)
785       {
786       $line = $a_lines[$n];
787       $quotation = '';
788       $q = 0;
789       
790       if (preg_match('/^(>+\s*)/', $line, $regs))
791         {
792         $q = strlen(preg_replace('/\s/', '', $regs[1]));
793         $line = substr($line, strlen($regs[1]));
794
795         if ($q > $quote_level)
796           $quotation = str_repeat('<blockquote>', $q - $quote_level);
797         else if ($q < $quote_level)
798           $quotation = str_repeat("</blockquote>", $quote_level - $q);
799         }
800       else if ($quote_level > 0)
801         $quotation = str_repeat("</blockquote>", $quote_level);
802
803       $quote_level = $q;
804       $a_lines[$n] = $quotation . Q($line, 'replace', FALSE);
805       }
806
807     // insert the links for urls and mailtos
808     $body = preg_replace("/##string_replacement\{([0-9]+)\}##/e", "\$replace_strings[\\1]", join("\n", $a_lines));
809     
810     return "<div class=\"pre\">".$body."\n</div>";
811     }
812   }
813
814
815
816 // add a string to the replacement array and return a replacement string
817 function rcmail_str_replacement($str, &$rep)
818   {
819   static $count = 0;
820   $rep[$count] = stripslashes($str);
821   return "##string_replacement{".($count++)."}##";
822   }
823
824
825 function rcmail_parse_message(&$structure, $arg=array(), $recursive=FALSE)
826   {
827   global $IMAP;
828   static $sa_inline_objects = array();
829
830   // arguments are: (bool)$prefer_html, (string)$get_url
831   extract($arg);
832
833   $a_attachments = array();
834   $a_return_parts = array();
835   $out = '';
836
837   $message_ctype_primary = strtolower($structure->ctype_primary);
838   $message_ctype_secondary = strtolower($structure->ctype_secondary);
839
840   // show message headers
841   if ($recursive && is_array($structure->headers) && isset($structure->headers['subject']))
842     {
843     $c = new stdClass;
844     $c->type = 'headers';
845     $c->headers = &$structure->headers;
846     $a_return_parts[] = $c;
847     }
848
849   // print body if message doesn't have multiple parts
850   if ($message_ctype_primary=='text')
851     {
852     $structure->type = 'content';
853     $a_return_parts[] = &$structure;
854     }
855
856   // message contains alternative parts
857   else if ($message_ctype_primary=='multipart' && $message_ctype_secondary=='alternative' && is_array($structure->parts))
858     {
859     // get html/plaintext parts
860     $plain_part = $html_part = $print_part = $related_part = NULL;
861     
862     foreach ($structure->parts as $p => $sub_part)
863       {
864       $sub_ctype_primary = strtolower($sub_part->ctype_primary);
865       $sub_ctype_secondary = strtolower($sub_part->ctype_secondary);
866
867       // check if sub part is 
868       if ($sub_ctype_primary=='text' && $sub_ctype_secondary=='plain')
869         $plain_part = $p;
870       else if ($sub_ctype_primary=='text' && $sub_ctype_secondary=='html')
871         $html_part = $p;
872       else if ($sub_ctype_primary=='text' && $sub_ctype_secondary=='enriched')
873         $enriched_part = $p;
874       else if ($sub_ctype_primary=='multipart' && $sub_ctype_secondary=='related')
875         $related_part = $p;
876       }
877
878     // parse related part (alternative part could be in here)
879     if ($related_part!==NULL && $prefer_html)
880       {
881       list($parts, $attachmnts) = rcmail_parse_message($structure->parts[$related_part], $arg, TRUE);
882       $a_return_parts = array_merge($a_return_parts, $parts);
883       $a_attachments = array_merge($a_attachments, $attachmnts);
884       }
885
886     // print html/plain part
887     else if ($html_part!==NULL && $prefer_html)
888       $print_part = &$structure->parts[$html_part];
889     else if ($enriched_part!==NULL)
890       $print_part = &$structure->parts[$enriched_part];
891     else if ($plain_part!==NULL)
892       $print_part = &$structure->parts[$plain_part];
893
894     // show message body
895     if (is_object($print_part))
896       {
897       $print_part->type = 'content';
898       $a_return_parts[] = $print_part;
899       }
900     // show plaintext warning
901     else if ($html_part!==NULL)
902       {
903       $c = new stdClass;
904       $c->type = 'content';
905       $c->body = rcube_label('htmlmessage');
906       $c->ctype_primary = 'text';
907       $c->ctype_secondary = 'plain';
908       
909       $a_return_parts[] = $c;
910       }
911                                 
912     // add html part as attachment
913     if ($html_part!==NULL && $structure->parts[$html_part]!==$print_part)
914       {
915       $html_part = &$structure->parts[$html_part];
916       $html_part->filename = rcube_label('htmlmessage');
917       $html_part->mimetype = 'text/html';
918       
919       $a_attachments[] = $html_part;
920       }
921     }
922
923   // message contains multiple parts
924   else if (is_array($structure->parts) && !empty($structure->parts))
925     {
926     for ($i=0; $i<count($structure->parts); $i++)
927       {
928       $mail_part = &$structure->parts[$i];
929       $primary_type = strtolower($mail_part->ctype_primary);
930       $secondary_type = strtolower($mail_part->ctype_secondary);
931
932       // multipart/alternative
933       if ($primary_type=='multipart')
934         {
935         list($parts, $attachmnts) = rcmail_parse_message($mail_part, $arg, TRUE);
936
937         $a_return_parts = array_merge($a_return_parts, $parts);
938         $a_attachments = array_merge($a_attachments, $attachmnts);
939         }
940
941       // part text/[plain|html] OR message/delivery-status
942       else if (($primary_type=='text' && ($secondary_type=='plain' || $secondary_type=='html') && $mail_part->disposition!='attachment') ||
943                ($primary_type=='message' && $secondary_type=='delivery-status'))
944         {
945         $mail_part->type = 'content';
946         $a_return_parts[] = $mail_part;
947         }
948
949       // part message/*
950       else if ($primary_type=='message')
951         {
952         list($parts, $attachmnts) = rcmail_parse_message($mail_part, $arg, TRUE);
953           
954         $a_return_parts = array_merge($a_return_parts, $parts);
955         $a_attachments = array_merge($a_attachments, $attachmnts);
956         }
957
958       // part is file/attachment
959       else if ($mail_part->disposition=='attachment' || $mail_part->disposition=='inline' || $mail_part->headers['content-id'] ||
960                (empty($mail_part->disposition) && $mail_part->filename))
961         {
962         // skip apple resource forks
963         if ($message_ctype_secondary=='appledouble' && $secondary_type=='applefile')
964           continue;
965
966         // part belongs to a related message
967         if ($message_ctype_secondary=='related' && $mail_part->headers['content-id'])
968           {
969           $mail_part->content_id = preg_replace(array('/^</', '/>$/'), '', $mail_part->headers['content-id']);
970           $sa_inline_objects[] = $mail_part;
971           }
972         // is regular attachment
973         else
974           {
975           if (!$mail_part->filename)
976             $mail_part->filename = 'file_'.$mail_part->mime_id;
977           $a_attachments[] = $mail_part;
978           }
979         }
980       }
981
982     // if this was a related part try to resolve references
983     if ($message_ctype_secondary=='related' && sizeof($sa_inline_objects))
984       {
985       $a_replaces = array();
986         
987       foreach ($sa_inline_objects as $inline_object)
988         $a_replaces['cid:'.$inline_object->content_id] = htmlspecialchars(sprintf($get_url, $inline_object->mime_id));
989       
990       // add replace array to each content part
991       // (will be applied later when part body is available)
992       for ($i=0; $i<count($a_return_parts); $i++)
993         {
994         if ($a_return_parts[$i]->type=='content')
995           $a_return_parts[$i]->replaces = $a_replaces;
996         }
997       }
998     }
999
1000   // message is single part non-text
1001   else if ($structure->filename)
1002     $a_attachments[] = $structure;
1003
1004   return array($a_return_parts, $a_attachments);
1005   }
1006
1007
1008
1009
1010 // return table with message headers
1011 function rcmail_message_headers($attrib, $headers=NULL)
1012   {
1013   global $IMAP, $OUTPUT, $MESSAGE;
1014   static $sa_attrib;
1015   
1016   // keep header table attrib
1017   if (is_array($attrib) && !$sa_attrib)
1018     $sa_attrib = $attrib;
1019   else if (!is_array($attrib) && is_array($sa_attrib))
1020     $attrib = $sa_attrib;
1021   
1022   
1023   if (!isset($MESSAGE))
1024     return FALSE;
1025
1026   // get associative array of headers object
1027   if (!$headers)
1028     $headers = is_object($MESSAGE['headers']) ? get_object_vars($MESSAGE['headers']) : $MESSAGE['headers'];
1029   
1030   $header_count = 0;
1031   
1032   // allow the following attributes to be added to the <table> tag
1033   $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id', 'cellpadding', 'cellspacing', 'border', 'summary'));
1034   $out = '<table' . $attrib_str . ">\n";
1035
1036   // show these headers
1037   $standard_headers = array('subject', 'from', 'organization', 'to', 'cc', 'bcc', 'reply-to', 'date');
1038   
1039   foreach ($standard_headers as $hkey)
1040     {
1041     if (!$headers[$hkey])
1042       continue;
1043
1044     if ($hkey=='date' && !empty($headers[$hkey]))
1045       $header_value = format_date(strtotime($headers[$hkey]));
1046     else if (in_array($hkey, array('from', 'to', 'cc', 'bcc', 'reply-to')))
1047       $header_value = Q(rcmail_address_string($headers[$hkey], NULL, $attrib['addicon']), 'show');
1048     else
1049       $header_value = Q(rcube_imap::decode_mime_string($headers[$hkey], $headers['charset']));
1050
1051     $out .= "\n<tr>\n";
1052     $out .= '<td class="header-title">'.Q(rcube_label($hkey)).":&nbsp;</td>\n";
1053     $out .= '<td class="'.$hkey.'" width="90%">'.$header_value."</td>\n</tr>";
1054     $header_count++;
1055     }
1056
1057   $out .= "\n</table>\n\n";
1058
1059   return $header_count ? $out : '';  
1060   }
1061
1062
1063
1064 function rcmail_message_body($attrib)
1065   {
1066   global $CONFIG, $OUTPUT, $MESSAGE, $IMAP, $GET_URL, $REMOTE_OBJECTS;
1067   
1068   if (!is_array($MESSAGE['parts']) && !$MESSAGE['body'])
1069     return '';
1070     
1071   if (!$attrib['id'])
1072     $attrib['id'] = 'rcmailMsgBody';
1073
1074   $safe_mode = (bool)$_GET['_safe'];
1075   $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id'));
1076   $out = '<div '. $attrib_str . ">\n";
1077   
1078   $header_attrib = array();
1079   foreach ($attrib as $attr => $value)
1080     if (preg_match('/^headertable([a-z]+)$/i', $attr, $regs))
1081       $header_attrib[$regs[1]] = $value;
1082
1083
1084   // this is an ecrypted message
1085   // -> create a plaintext body with the according message
1086   if (!sizeof($MESSAGE['parts']) && $MESSAGE['headers']->ctype=='multipart/encrypted')
1087     {
1088     $p = new stdClass;
1089     $p->type = 'content';
1090     $p->ctype_primary = 'text';
1091     $p->ctype_secondary = 'plain';
1092     $p->body = rcube_label('encryptedmessage');
1093     $MESSAGE['parts'][0] = $p;
1094     }
1095   
1096   if ($MESSAGE['parts'])
1097     {
1098     foreach ($MESSAGE['parts'] as $i => $part)
1099       {
1100       if ($part->type=='headers')
1101         $out .= rcmail_message_headers(sizeof($header_attrib) ? $header_attrib : NULL, $part->headers);
1102       else if ($part->type=='content')
1103         {
1104         if (empty($part->ctype_parameters) || empty($part->ctype_parameters['charset']))
1105           $part->ctype_parameters['charset'] = $MESSAGE['headers']->charset;
1106
1107         // fetch part if not available
1108         if (!isset($part->body))
1109           $part->body = $IMAP->get_message_part($MESSAGE['UID'], $part->mime_id, $part);
1110
1111         $body = rcmail_print_body($part, $safe_mode, !$CONFIG['prefer_html']);
1112         $out .= '<div class="message-part">';
1113         
1114         if ($part->ctype_secondary != 'plain')
1115           $out .= rcmail_sanitize_html($body, $attrib['id']);
1116         else
1117           $out .= $body;
1118
1119         $out .= "</div>\n";
1120         }
1121       }
1122     }
1123   else
1124     $out .= $MESSAGE['body'];
1125
1126
1127   $ctype_primary = strtolower($MESSAGE['structure']->ctype_primary);
1128   $ctype_secondary = strtolower($MESSAGE['structure']->ctype_secondary);
1129   
1130   // list images after mail body
1131   if (get_boolean($attrib['showimages']) && $ctype_primary=='multipart' && $ctype_secondary=='mixed' &&
1132       sizeof($MESSAGE['attachments']) && !strstr($message_body, '<html') && strlen($GET_URL))
1133     {
1134     foreach ($MESSAGE['attachments'] as $attach_prop)
1135       {
1136       if (strpos($attach_prop->mimetype, 'image/')===0)
1137         $out .= sprintf("\n<hr />\n<p align=\"center\"><img src=\"%s&amp;_part=%s\" alt=\"%s\" title=\"%s\" /></p>\n",
1138                         htmlspecialchars($GET_URL), $attach_prop->mime_id,
1139                         $attach_prop->filename,
1140                         $attach_prop->filename);
1141       }
1142     }
1143   
1144   // tell client that there are blocked remote objects
1145   if ($REMOTE_OBJECTS && !$safe_mode)
1146     $OUTPUT->set_env('blockedobjects', true);
1147
1148   $out .= "\n</div>";
1149   return $out;
1150   }
1151
1152
1153
1154 // modify a HTML message that it can be displayed inside a HTML page
1155 function rcmail_sanitize_html($body, $container_id)
1156   {
1157   // remove any null-byte characters before parsing
1158   $body = preg_replace('/\x00/', '', $body);
1159   
1160   $last_style_pos = 0;
1161   $body_lc = strtolower($body);
1162   
1163   // find STYLE tags
1164   while (($pos = strpos($body_lc, '<style', $last_style_pos)) && ($pos2 = strpos($body_lc, '</style>', $pos)))
1165     {
1166     $pos = strpos($body_lc, '>', $pos)+1;
1167
1168     // replace all css definitions with #container [def]
1169     $styles = rcmail_mod_css_styles(substr($body, $pos, $pos2-$pos), $container_id);
1170
1171     $body = substr($body, 0, $pos) . $styles . substr($body, $pos2);
1172     $body_lc = strtolower($body);
1173     $last_style_pos = $pos2;
1174     }
1175
1176
1177   // remove SCRIPT tags
1178   foreach (array('script', 'applet', 'object', 'embed', 'iframe') as $tag)
1179     {
1180     while (($pos = strpos($body_lc, '<'.$tag)) && ($pos2 = strpos($body_lc, '</'.$tag.'>', $pos)))
1181       {
1182       $pos2 += strlen('</'.$tag.'>');
1183       $body = substr($body, 0, $pos) . substr($body, $pos2, strlen($body)-$pos2);
1184       $body_lc = strtolower($body);
1185       }
1186     }
1187
1188   // replace event handlers on any object
1189   while ($body != $prev_body)
1190     {
1191     $prev_body = $body;
1192     $body = preg_replace('/(<[^!][^>]*\s)(on[^=>]+)=([^>]+>)/im', '$1__removed=$3', $body);
1193     $body = preg_replace('/(<[^!][^>]*\shref=["\']?)(javascript:)([^>]*?>)/im', '$1null:$3', $body);
1194     }
1195
1196   // resolve <base href>
1197   $base_reg = '/(<base.*href=["\']?)([hftps]{3,5}:\/{2}[^"\'\s]+)([^<]*>)/i';
1198   if (preg_match($base_reg, $body, $regs))
1199     {
1200     $base_url = $regs[2];
1201     $body = preg_replace('/(src|background|href)=(["\']?)([\.\/]+[^"\'\s]+)(\2|\s|>)/Uie', "'\\1=\"'.make_absolute_url('\\3', '$base_url').'\"'", $body);
1202     $body = preg_replace('/(url\s*\()(["\']?)([\.\/]+[^"\'\)\s]+)(\2)\)/Uie', "'\\1\''.make_absolute_url('\\3', '$base_url').'\')'", $body);
1203     $body = preg_replace($base_reg, '', $body);
1204     }
1205     
1206   // modify HTML links to open a new window if clicked
1207   $body = preg_replace('/<a\s+([^>]+)>/Uie', "rcmail_alter_html_link('\\1');", $body);
1208
1209   // add comments arround html and other tags
1210   $out = preg_replace(array(
1211       '/(<!DOCTYPE.+)/i',
1212       '/(<\/?html[^>]*>)/i',
1213       '/(<\/?head[^>]*>)/i',
1214       '/(<title[^>]*>.*<\/title>)/Ui',
1215       '/(<\/?meta[^>]*>)/i'),
1216     '<!--\\1-->',
1217     $body);
1218
1219   $out = preg_replace(array('/<body([^>]*)>/i',
1220                             '/<\/body>/i'),
1221                       array('<div class="rcmBody"\\1>',
1222                             '</div>'),
1223                       $out);
1224
1225   // quote <? of php and xml files that are specified as text/html
1226   $out = preg_replace(array('/<\?/', '/\?>/'), array('&lt;?', '?&gt;'), $out);
1227
1228   return $out;
1229   }
1230
1231
1232 // parse link attributes and set correct target
1233 function rcmail_alter_html_link($in)
1234   {
1235   $in = preg_replace('/=([^("|\'|\s)]+)(\s|$)/', '="\1"', $in);
1236   $attrib = parse_attrib_string($in);
1237
1238   if (stristr((string)$attrib['href'], 'mailto:'))
1239     $attrib['onclick'] = sprintf("return %s.command('compose','%s',this)",
1240                                  JS_OBJECT_NAME,
1241                                  JQ(substr($attrib['href'], 7)));
1242   else if (!empty($attrib['href']) && $attrib['href']{0}!='#')
1243     $attrib['target'] = '_blank';
1244   
1245   return '<a' . create_attrib_string($attrib, array('href', 'name', 'target', 'onclick', 'id', 'class', 'style', 'title')) . '>';
1246   }
1247
1248
1249 // replace all css definitions with #container [def]
1250 function rcmail_mod_css_styles($source, $container_id)
1251   {
1252   $a_css_values = array();
1253   $last_pos = 0;
1254   
1255   // cut out all contents between { and }
1256   while (($pos = strpos($source, '{', $last_pos)) && ($pos2 = strpos($source, '}', $pos)))
1257     {
1258     $key = sizeof($a_css_values);
1259     $a_css_values[$key] = substr($source, $pos+1, $pos2-($pos+1));
1260     $source = substr($source, 0, $pos+1) . "<<str_replacement[$key]>>" . substr($source, $pos2, strlen($source)-$pos2);
1261     $last_pos = $pos+2;
1262     }
1263
1264   // remove html commends and add #container to each tag selector.
1265   // also replace body definition because we also stripped off the <body> tag
1266   $styles = preg_replace(array('/(^\s*<!--)|(-->\s*$)/', '/(^\s*|,\s*|\}\s*)([a-z0-9\._][a-z0-9\.\-_]*)/im', '/<<str_replacement\[([0-9]+)\]>>/e', "/$container_id\s+body/i"),
1267                          array('', "\\1#$container_id \\2", "\$a_css_values[\\1]", "$container_id div.rcmBody"),
1268                          $source);
1269
1270   return $styles;
1271   }
1272
1273
1274 function rcmail_has_html_part($message_parts)
1275 {
1276    if (!is_array($message_parts))
1277       return FALSE;
1278
1279    // check all message parts
1280    foreach ($message_parts as $pid => $part)
1281    {
1282       $mimetype = strtolower($part->ctype_primary.'/'.$part->ctype_secondary);
1283       if ($mimetype=='text/html')
1284       {
1285          return TRUE;
1286       }
1287    }
1288     
1289    return FALSE;
1290 }
1291
1292 // return first HTML part of a message
1293 function rcmail_first_html_part($message_struct)
1294   {
1295   global $IMAP;
1296
1297   if (!is_array($message_struct['parts']))
1298     return FALSE;
1299     
1300   $html_part = NULL;
1301
1302   // check all message parts
1303   foreach ($message_struct['parts'] as $pid => $part)
1304     {
1305     $mimetype = strtolower($part->ctype_primary.'/'.$part->ctype_secondary);
1306     if ($mimetype=='text/html')
1307       {
1308       $html_part = $IMAP->get_message_part($message_struct['UID'], $pid, $part);
1309       }
1310     }
1311
1312   if ($html_part)
1313     {
1314     // remove special chars encoding
1315     //$trans = array_flip(get_html_translation_table(HTML_ENTITIES));
1316     //$html_part = strtr($html_part, $trans);
1317
1318     return $html_part;
1319     }
1320
1321   return FALSE;
1322 }
1323
1324
1325 // return first text part of a message
1326 function rcmail_first_text_part($message_struct)
1327   {
1328   global $IMAP;
1329
1330   if (empty($message_struct['parts']))
1331     return $message_struct['UID'] ? $IMAP->get_body($message_struct['UID']) : false;
1332
1333   // check all message parts
1334   foreach ($message_struct['parts'] as $pid => $part)
1335     {
1336     $mimetype = strtolower($part->ctype_primary.'/'.$part->ctype_secondary);
1337
1338     if ($mimetype=='text/plain')
1339       return $IMAP->get_message_part($message_struct['UID'], $pid, $part);
1340
1341     else if ($mimetype=='text/html')
1342       {
1343       $html_part = $IMAP->get_message_part($message_struct['UID'], $pid, $part);
1344       
1345       // remove special chars encoding
1346       $trans = array_flip(get_html_translation_table(HTML_ENTITIES));
1347       $html_part = strtr($html_part, $trans);
1348
1349       // create instance of html2text class
1350       $txt = new html2text($html_part);
1351       return $txt->get_text();
1352       }
1353     }
1354
1355   return FALSE;
1356   }
1357
1358
1359 // decode address string and re-format it as HTML links
1360 function rcmail_address_string($input, $max=NULL, $addicon=NULL)
1361   {
1362   global $IMAP, $PRINT_MODE, $CONFIG, $OUTPUT, $EMAIL_ADDRESS_PATTERN;
1363   
1364   $a_parts = $IMAP->decode_address_list($input);
1365
1366   if (!sizeof($a_parts))
1367     return $input;
1368
1369   $c = count($a_parts);
1370   $j = 0;
1371   $out = '';
1372
1373   foreach ($a_parts as $part)
1374     {
1375     $j++;
1376     if ($PRINT_MODE)
1377       $out .= sprintf('%s &lt;%s&gt;', Q($part['name']), $part['mailto']);
1378     else if (preg_match($EMAIL_ADDRESS_PATTERN, $part['mailto']))
1379       {
1380       $out .= sprintf('<a href="mailto:%s" onclick="return %s.command(\'compose\',\'%s\',this)" class="rcmContactAddress" title="%s">%s</a>',
1381                       Q($part['mailto']),
1382                       JS_OBJECT_NAME,
1383                       JQ($part['mailto']),
1384                       Q($part['mailto']),
1385                       Q($part['name']));
1386                       
1387       if ($addicon)
1388         $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>',
1389                         JS_OBJECT_NAME,
1390                         urlencode($part['string']),
1391                         rcube_label('addtoaddressbook'),
1392                         $CONFIG['skin_path'],
1393                         $addicon);
1394       }
1395     else
1396       {
1397       if ($part['name'])
1398         $out .= Q($part['name']);
1399       if ($part['mailto'])
1400         $out .= (strlen($out) ? ' ' : '') . sprintf('&lt;%s&gt;', Q($part['mailto']));
1401       }
1402       
1403     if ($c>$j)
1404       $out .= ','.($max ? '&nbsp;' : ' ');
1405         
1406     if ($max && $j==$max && $c>$j)
1407       {
1408       $out .= '...';
1409       break;
1410       }        
1411     }
1412     
1413   return $out;
1414   }
1415
1416
1417 function rcmail_message_part_controls()
1418   {
1419   global $CONFIG, $IMAP, $MESSAGE;
1420   
1421   if (!is_array($MESSAGE) || !is_array($MESSAGE['parts']) || !($_GET['_uid'] && $_GET['_part']) || !$MESSAGE['parts'][$_GET['_part']])
1422     return '';
1423     
1424   $part = &$MESSAGE['parts'][$_GET['_part']];
1425   
1426   $attrib_str = create_attrib_string($attrib, array('id', 'class', 'style', 'cellspacing', 'cellpadding', 'border', 'summary'));
1427   $out = '<table '. $attrib_str . ">\n";
1428   
1429   if ($filename)
1430     {
1431     $out .= sprintf('<tr><td class="title">%s</td><td>%s</td><td>[<a href="./?%s">%s</a>]</tr>'."\n",
1432                     Q(rcube_label('filename')),
1433                     Q($part->filename),
1434                     str_replace('_frame=', '_download=', $_SERVER['QUERY_STRING']),
1435                     Q(rcube_label('download')));
1436     }
1437     
1438   if ($part->size)
1439     $out .= sprintf('<tr><td class="title">%s</td><td>%s</td></tr>'."\n",
1440                     Q(rcube_label('filesize')),
1441                     show_bytes($part->size));
1442   
1443   $out .= "\n</table>";
1444   
1445   return $out;
1446   }
1447
1448
1449
1450 function rcmail_message_part_frame($attrib)
1451   {
1452   global $MESSAGE;
1453   
1454   $part = $MESSAGE['parts'][$_GET['_part']];
1455   $ctype_primary = strtolower($part->ctype_primary);
1456
1457   $attrib['src'] = './?'.str_replace('_frame=', ($ctype_primary=='text' ? '_show=' : '_preload='), $_SERVER['QUERY_STRING']);
1458
1459   $attrib_str = create_attrib_string($attrib, array('id', 'class', 'style', 'src', 'width', 'height'));
1460   $out = '<iframe '. $attrib_str . "></iframe>";
1461     
1462   return $out;
1463   }
1464
1465
1466 // clear message composing settings
1467 function rcmail_compose_cleanup()
1468   {
1469   if (!isset($_SESSION['compose']))
1470     return;
1471
1472   // remove attachment files from temp dir
1473   if (is_array($_SESSION['compose']['attachments']))
1474     foreach ($_SESSION['compose']['attachments'] as $attachment)
1475       @unlink($attachment['path']);
1476   
1477   unset($_SESSION['compose']);
1478   }
1479
1480
1481 // register UI objects
1482 $OUTPUT->add_handlers(array(
1483   'mailboxlist' => 'rcmail_mailbox_list',
1484   'messages' => 'rcmail_message_list',
1485   'messagecountdisplay' => 'rcmail_messagecount_display',
1486   'quotadisplay' => 'rcmail_quota_display',
1487   'messageheaders' => 'rcmail_message_headers',
1488   'messagebody' => 'rcmail_message_body',
1489   'messagecontentframe' => 'rcmail_messagecontent_frame',
1490   'messagepartframe' => 'rcmail_message_part_frame',
1491   'messagepartcontrols' => 'rcmail_message_part_controls',
1492   'searchform' => 'rcmail_search_form'
1493 ));
1494
1495 ?>