source: github/program/steps/mail/func.inc @ 47124c22

HEADcourier-fixdev-browser-capabilitiespdorelease-0.6release-0.7release-0.8
Last change on this file since 47124c22 was 47124c22, checked in by thomascube <thomas@…>, 5 years ago

Changed codebase to PHP5 with autoloader + added some new classes from the devel-vnext branch

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