source: subversion/trunk/roundcubemail/program/steps/mail/func.inc @ 8

Last change on this file since 8 was 8, checked in by roundcube, 8 years ago

Minor bugfixes and correction of confusing License notfications

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 38.5 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, 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/html2text.inc');
23require_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// set imap properties and session vars
29if (strlen($_GET['_mbox']))
30  {
31  $IMAP->set_mailbox($_GET['_mbox']);
32  $_SESSION['mbox'] = $_GET['_mbox'];
33  }
34
35if (strlen($_GET['_page']))
36  {
37  $IMAP->set_page($_GET['_page']);
38  $_SESSION['page'] = $_GET['_page'];
39  }
40
41
42// define url for getting message parts
43if (strlen($_GET['_uid']))
44  $GET_URL = sprintf('%s&_action=get&_mbox=%s&_uid=%d', $COMM_PATH, $IMAP->get_mailbox_name(), $_GET['_uid']);
45
46
47// set current mailbox in client environment
48$OUTPUT->add_script(sprintf("%s.set_env('mailbox', '%s');", $JS_OBJECT_NAME, $IMAP->get_mailbox_name()));
49
50
51if ($CONFIG['trash_mbox'])
52  $OUTPUT->add_script(sprintf("%s.set_env('trash_mailbox', '%s');", $JS_OBJECT_NAME, $CONFIG['trash_mbox']));
53
54
55
56// return the mailboxlist in HTML
57function rcmail_mailbox_list($attrib)
58  {
59  global $IMAP, $CONFIG, $OUTPUT, $JS_OBJECT_NAME, $COMM_PATH;
60  static $s_added_script = FALSE;
61 
62  $type = $attrib['type'] ? $attrib['type'] : 'ul';
63  $add_attrib = $type=='select' ? array('style', 'class', 'id', 'name', 'onchange') :
64                                  array('style', 'class', 'id');
65                                 
66  if ($type=='ul' && !$attrib['id'])
67    $attrib['id'] = 'rcmboxlist';
68
69  // allow the following attributes to be added to the <ul> tag
70  $attrib_str = create_attrib_string($attrib, $add_attrib);
71 
72  $out = '<' . $type . $attrib_str . ">\n";
73 
74  // add no-selection option
75  if ($type=='select' && $attrib['noselection'])
76    $out .= sprintf('<option value="0">%s</option>'."\n",
77                    rcube_label($attrib['noselection']));
78 
79  // get mailbox list
80  $a_folders = $IMAP->list_mailboxes();
81  $mbox = $IMAP->get_mailbox_name();
82 
83  // for these mailboxes we have localized labels
84  $special_mailboxes = array('inbox', 'sent', 'drafts', 'trash', 'junk');
85
86  foreach ($a_folders as $i => $folder)
87    {
88    $zebra_class = $i%2 ? 'even' : 'odd';
89   
90    $folder_lc = strtolower($folder);
91    if (in_array($folder_lc, $special_mailboxes))
92      $foldername = rcube_label($folder_lc);
93    else
94      $foldername = $folder;
95       
96    if ($unread_count = $IMAP->messagecount($folder, 'UNSEEN'))
97      $foldername .= sprintf(' (%d)', $unread_count);
98     
99    // compose mailbox line
100    if ($type=='select')
101      $out .= sprintf('<option value="%s">%s</option>'."\n",
102                      $folder,
103                      rep_specialchars_output($foldername));
104   
105    else
106      $out .= sprintf('<li class="mailbox %s %s%s%s"><a href="#%s" onclick="return %s.command(\'list\',\'%s\')" onmouseup="return %s.mbox_mouse_up(\'%s\')">%s</a></li>'."\n",
107                       preg_replace('/[^a-z0-9\-_]/', '', $folder_lc),
108                       $zebra_class,
109                       $unread_count ? ' unread' : '',
110                       $folder==$mbox ? ' selected' : '',
111                       $folder,
112                       $JS_OBJECT_NAME,
113                       $folder,
114                       $JS_OBJECT_NAME,
115                       $folder,
116                       rep_specialchars_output($foldername));
117    }
118
119  if ($type=='ul')
120    $OUTPUT->add_script(sprintf("%s.gui_object('mailboxlist', '%s');", $JS_OBJECT_NAME, $attrib['id']));
121
122
123/*  this is added further up
124  if (!$s_added_script)
125    {
126    $javascript .= sprintf("%s.set_env('mailbox', '%s');", $JS_OBJECT_NAME, $mbox);
127    $OUTPUT->add_script($javascript);
128    $s_added_script = TRUE;
129    }
130*/
131  return $out . "</$type>";
132  }
133
134
135// return the message list as HTML table
136function rcmail_message_list($attrib)
137  {
138  global $IMAP, $CONFIG, $COMM_PATH, $OUTPUT, $JS_OBJECT_NAME;
139 
140  $skin_path = $CONFIG['skin_path'];
141  $image_tag = '<img src="%s%s" alt="%s" border="0" />';
142 
143  // get message headers
144  $a_headers = $IMAP->list_headers();
145
146  // add id to message list table if not specified
147  if (!strlen($attrib['id']))
148    $attrib['id'] = 'rcubemessagelist';
149
150  // allow the following attributes to be added to the <table> tag
151  $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id', 'cellpadding', 'cellspacing', 'border', 'summary'));
152
153  $out = '<table' . $attrib_str . ">\n";
154 
155  // define list of cols to be displayed
156  $a_show_cols = is_array($CONFIG['list_cols']) ? $CONFIG['list_cols'] : array('subject');
157 
158  // show 'to' instead of from in sent messages
159  if (strtolower($IMAP->get_mailbox_name())=='sent' && ($f = array_search('from', $a_show_cols)))
160    $a_show_cols[$f] = 'to';
161
162
163  // add table title
164  $out .= "<thead><tr>\n<td class=\"icon\">&nbsp;</td>\n";
165 
166  foreach ($a_show_cols as $col)
167    $out .= '<td class="'.$col.'">' . rcube_label($col) . "</td>\n";
168
169  $out .= '<td class="icon">'.($attrib['attachmenticon'] ? sprintf($image_tag, $skin_path, $attrib['attachmenticon'], '') : '')."</td>\n";
170  $out .= "</tr></thead>\n<tbody>\n";
171
172
173  // no messages in this mailbox
174  if (!sizeof($a_headers))
175    {
176    $out .= sprintf('<tr><td colspan="%d">%s</td></tr>',
177                   sizeof($a_show_cols)+2,
178                   rcube_label('nomessagesfound'));
179    }
180
181
182  $a_js_message_arr = array();
183
184  // create row for each message
185  foreach ($a_headers as $i => $header)  //while (list($i, $header) = each($a_headers))
186    {
187    $message_icon = $attach_icon = '';
188    $js_row_arr = array();
189    $zebra_class = $i%2 ? 'even' : 'odd';
190
191    // set messag attributes to javascript array
192    if (!$header->seen)
193      $js_row_arr['unread'] = true;
194    if ($header->answered)
195      $js_row_arr['replied'] = true;
196
197        // set message icon   
198    if ($attrib['unreadicon'] && !$header->seen)
199      $message_icon = $attrib['unreadicon'];
200    else if ($attrib['repliedicon'] && $header->answered)
201      $message_icon = $attrib['repliedicon'];
202    else if ($attrib['messageicon'])
203      $message_icon = $attrib['messageicon'];
204   
205        // set attachment icon
206    if ($attrib['attachmenticon'] && preg_match("/multipart\/m/i", $header->ctype))
207      $attach_icon = $attrib['attachmenticon'];
208       
209    $out .= sprintf('<tr id="rcmrow%d" class="message'.($header->seen ? '' : ' unread').' '.$zebra_class.'">'."\n", $header->uid);
210    $out .= sprintf("<td class=\"icon\">%s</td>\n", $message_icon ? sprintf($image_tag, $skin_path, $message_icon, '') : '');
211       
212    // format each col
213    foreach ($a_show_cols as $col)
214      {
215      if ($col=='from' || $col=='to')
216        $cont = rep_specialchars_output(rcmail_address_string($header->$col, 3, $attrib['addicon']));
217      else if ($col=='subject')
218        $cont = rep_specialchars_output($IMAP->decode_header($header->$col));
219      else if ($col=='size')
220        $cont = show_bytes($header->$col);
221      else if ($col=='date')
222        $cont = format_date($header->date); //date('m.d.Y G:i:s', strtotime($header->date));
223      else
224        $cont = rep_specialchars_output($header->$col);
225       
226          $out .= '<td class="'.$col.'">' . $cont . "</td>\n";
227      }
228
229    $out .= sprintf("<td class=\"icon\">%s</td>\n", $attach_icon ? sprintf($image_tag, $skin_path, $attach_icon, '') : '');
230    $out .= "</tr>\n";
231   
232    if (sizeof($js_row_arr))
233      $a_js_message_arr[$header->uid] = $js_row_arr;
234    }
235 
236  // complete message table
237  $out .= "</tbody></table>\n";
238 
239 
240  $message_count = $IMAP->messagecount();
241 
242  // set client env
243  $javascript = sprintf("%s.gui_object('messagelist', '%s');\n", $JS_OBJECT_NAME, $attrib['id']);
244  $javascript .= sprintf("%s.set_env('messagecount', %d);\n", $JS_OBJECT_NAME, $message_count);
245  $javascript .= sprintf("%s.set_env('current_page', %d);\n", $JS_OBJECT_NAME, $IMAP->list_page);
246  $javascript .= sprintf("%s.set_env('pagecount', %d);\n", $JS_OBJECT_NAME, ceil($message_count/$IMAP->page_size));
247 
248  if ($attrib['messageicon'])
249    $javascript .= sprintf("%s.set_env('messageicon', '%s%s');\n", $JS_OBJECT_NAME, $skin_path, $attrib['messageicon']);
250  if ($attrib['unreadicon'])
251    $javascript .= sprintf("%s.set_env('unreadicon', '%s%s');\n", $JS_OBJECT_NAME, $skin_path, $attrib['unreadicon']);
252  if ($attrib['repliedicon'])
253    $javascript .= sprintf("%s.set_env('repliedicon', '%s%s');\n", $JS_OBJECT_NAME, $skin_path, $attrib['repliedicon']);
254  if ($attrib['attachmenticon'])
255    $javascript .= sprintf("%s.set_env('attachmenticon', '%s%s');\n", $JS_OBJECT_NAME, $skin_path, $attrib['attachmenticon']);
256   
257  $javascript .= sprintf("%s.set_env('messages', %s);", $JS_OBJECT_NAME, array2js($a_js_message_arr));
258 
259  $OUTPUT->add_script($javascript); 
260 
261  return $out;
262  }
263
264
265
266
267// return javascript commands to add rows to the message list
268function rcmail_js_message_list($a_headers, $insert_top=FALSE)
269  {
270  global $CONFIG, $IMAP;
271
272  $commands = '';
273  $a_show_cols = is_array($CONFIG['list_cols']) ? $CONFIG['list_cols'] : array('subject');
274
275  // show 'to' instead of from in sent messages
276  if (strtolower($IMAP->get_mailbox_name())=='sent' && ($f = array_search('from', $a_show_cols)))
277    $a_show_cols[$f] = 'to';
278
279  // loop through message headers
280  for ($n=0; $a_headers[$n]; $n++)
281    {
282    $header = $a_headers[$n];
283    $a_msg_cols = array();
284    $a_msg_flags = array();
285     
286    // format each col; similar as in rcmail_message_list()
287    foreach ($a_show_cols as $col)
288      {
289      if ($col=='from' || $col=='to')
290        $cont = rep_specialchars_output(rcmail_address_string($header->$col, 3));
291      else if ($col=='subject')
292        $cont = rep_specialchars_output($IMAP->decode_header($header->$col));
293      else if ($col=='size')
294        $cont = show_bytes($header->$col);
295      else if ($col=='date')
296        $cont = format_date($header->date); //date('m.d.Y G:i:s', strtotime($header->date));
297      else
298        $cont = rep_specialchars_output($header->$col);
299         
300      $a_msg_cols[$col] = $cont;
301      }
302
303    $a_msg_flags['unread'] = $header->seen ? 0 : 1;
304    $a_msg_flags['replied'] = $header->answered ? 1 : 0;
305 
306    $commands .= sprintf("this.add_message_row(%s, %s, %s, %b);\n",
307                         $header->uid,
308                         array2js($a_msg_cols),
309                         array2js($a_msg_flags),
310                         preg_match("/multipart\/m/i", $header->ctype));
311    }
312
313  return $commands;
314  }
315
316
317
318function rcmail_messagecount_display($attrib)
319  {
320  global $IMAP, $OUTPUT, $JS_OBJECT_NAME;
321 
322  if (!$attrib['id'])
323    $attrib['id'] = 'rcmcountdisplay';
324
325  $OUTPUT->add_script(sprintf("%s.gui_object('countdisplay', '%s');", $JS_OBJECT_NAME, $attrib['id']));
326
327  // allow the following attributes to be added to the <span> tag
328  $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id'));
329
330 
331  $out = '<span' . $attrib_str . '>';
332  $out .= rcmail_get_messagecount_text();
333  $out .= '</span>';
334  return $out;
335  }
336
337
338
339function rcmail_get_messagecount_text()
340  {
341  global $IMAP, $MESSAGE;
342 
343  if (isset($MESSAGE['index']))
344    {
345    $a_msg_index = $IMAP->message_index();
346    return rcube_label(array('name' => 'messagenrof',
347                             'vars' => array('nr'  => $MESSAGE['index']+1,
348                                             'count' => sizeof($a_msg_index))));
349    }
350 
351  $start_msg = ($IMAP->list_page-1) * $IMAP->page_size + 1;
352  $max = $IMAP->messagecount();
353
354  if ($max==0)
355    $out = rcube_label('mailboxempty');
356  else
357    $out = rcube_label(array('name' => 'messagesfromto',
358                              'vars' => array('from'  => $start_msg,
359                                              'to'    => min($max, $start_msg + $IMAP->page_size - 1),
360                                              'count' => $max)));
361
362  return $out;
363  }
364
365
366function rcmail_print_body($part, $safe=FALSE, $plain=FALSE) // $body, $ctype_primary='text', $ctype_secondary='plain', $encoding='7bit', $safe=FALSE, $plain=FALSE)
367  {
368  global $IMAP, $REMOTE_OBJECTS, $JS_OBJECT_NAME;
369
370  // extract part properties: body, ctype_primary, ctype_secondary, encoding, parameters
371  extract($part);
372 
373  $block = $plain ? '%s' : '%s'; //'<div style="display:block;">%s</div>';
374  $body = $IMAP->mime_decode($body, $encoding);
375  $body = $IMAP->charset_decode($body, $parameters);
376
377
378  // text/html
379  if ($ctype_secondary=='html')
380    {
381    if (!$safe)  // remove remote images and scripts
382      {
383      $remote_patterns = array('/(src|background)=(["\']?)([hftps]{3,5}:\/{2}[^"\'\s]+)(\2|\s|>)/Ui',
384                           //  '/(src|background)=(["\']?)([\.\/]+[^"\'\s]+)(\2|\s|>)/Ui',
385                               '/(<base.*href=["\']?)([hftps]{3,5}:\/{2}[^"\'\s]+)([^<]*>)/i',
386                               '/(<link.*href=["\']?)([hftps]{3,5}:\/{2}[^"\'\s]+)([^<]*>)/i',
387                               '/url\s*\(["\']?([hftps]{3,5}:\/{2}[^"\'\s]+)["\']?\)/i',
388                               '/url\s*\(["\']?([\.\/]+[^"\'\s]+)["\']?\)/i',
389                               '/<script.+<\/script>/Umis');
390
391      $remote_replaces = array('\\1=\\2#\\4',
392                            // '\\1=\\2#\\4',
393                               '',
394                               '\\1#\\3',
395                               'none',
396                               'none',
397                               '');
398     
399      // set flag if message containes remote obejcts that where blocked
400      foreach ($remote_patterns as $pattern)
401        {
402        if (preg_match($pattern, $body))
403          {
404          $REMOTE_OBJECTS = TRUE;
405          break;
406          }
407        }
408
409      $body = preg_replace($remote_patterns, $remote_replaces, $body);
410      }
411
412    return sprintf($block, rep_specialchars_output($body, 'html', '', FALSE));
413    }
414
415  // text/enriched
416  if ($ctype_secondary=='enriched')
417    {
418    $body = enriched_to_html($body);
419    return sprintf($block, rep_specialchars_output($body, 'html'));
420    }
421  else
422    {
423    // make links and email-addresses clickable
424    $convert_patterns = $convert_replaces = $replace_strings = array();
425   
426    $url_chars = 'a-z0-9_\-\+\*\$\/&%=@#';
427    $url_chars_within = '\?\.~,!';
428
429    $convert_patterns[] = "/([\w]+):\/\/([a-z0-9\-\.]+[a-z]{2,4}([$url_chars$url_chars_within]*[$url_chars])?)/ie";
430    $convert_replaces[] = "rcmail_str_replacement('<a href=\"\\1://\\2\" target=\"_blank\">\\1://\\2</a>', \$replace_strings)";
431
432    $convert_patterns[] = "/([^\/:]|\s)(www\.)([a-z0-9\-]{2,}[a-z]{2,4}([$url_chars$url_chars_within]*[$url_chars])?)/ie";
433    $convert_replaces[] = "rcmail_str_replacement('\\1<a href=\"http://\\2\\3\" target=\"_blank\">\\2\\3</a>', \$replace_strings)";
434   
435    $convert_patterns[] = '/([a-z0-9][a-z0-9\-\.\+\_]*@[a-z0-9]([a-z0-9\-][.]?)*[a-z0-9]\\.[a-z]{2,5})/ie';
436    $convert_replaces[] = "rcmail_str_replacement('<a href=\"mailto:\\1\" onclick=\"return $JS_OBJECT_NAME.command(\'compose\',\'\\1\',this)\">\\1</a>', \$replace_strings)";
437
438    $body = wordwrap(trim($body), 80);
439    $body = preg_replace($convert_patterns, $convert_replaces, $body);
440
441    // split body into single lines
442    $a_lines = preg_split('/\r?\n/', $body);
443
444    // colorize quoted parts
445    for($n=0; $n<sizeof($a_lines); $n++)
446      {
447      $line = $a_lines[$n];
448
449      if ($line{2}=='>')
450        $color = 'red';
451      else if ($line{1}=='>')
452        $color = 'green';
453      else if ($line{0}=='>')
454        $color = 'blue';
455      else
456        $color = FALSE;
457
458      $line = rep_specialchars_output($line, 'html', 'replace', FALSE);
459       
460      if ($color)
461        $a_lines[$n] = sprintf('<font color="%s">%s</font>', $color, $line);
462      else
463        $a_lines[$n] = $line;
464      }
465
466    // insert the links for urls and mailtos
467    $body = preg_replace("/##string_replacement\{([0-9]+)\}##/e", "\$replace_strings[\\1]", join("\n", $a_lines));
468   
469    return sprintf($block, "<pre>\n".$body."\n</pre>");
470    }
471  }
472
473
474
475// add a string to the replacement array and return a replacement string
476function rcmail_str_replacement($str, &$rep)
477  {
478  static $count = 0;
479  $rep[$count] = stripslashes($str);
480  return "##string_replacement{".($count++)."}##";
481  }
482
483
484function rcmail_parse_message($structure, $arg=array(), $recursive=FALSE)
485  {
486  global $IMAP;
487  static $sa_inline_objects = array();
488
489  // arguments are: (bool)$prefer_html, (string)$get_url
490  extract($arg);
491
492  $a_attachments = array();
493  $a_return_parts = array();
494  $out = '';
495
496  $message_ctype_primary = strtolower($structure->ctype_primary);
497  $message_ctype_secondary = strtolower($structure->ctype_secondary);
498
499  // show message headers
500  if ($recursive && is_array($structure->headers) && isset($structure->headers['subject']))
501    $a_return_parts[] = array('type' => 'headers',
502                              'headers' => $structure->headers);
503
504  // print body if message doesn't have multiple parts
505  if ($message_ctype_primary=='text')
506    {
507    $a_return_parts[] = array('type' => 'content',
508                              'body' => $structure->body,
509                              'ctype_primary' => $message_ctype_primary,
510                              'ctype_secondary' => $message_ctype_secondary,
511                              'encoding' => $structure->headers['content-transfer-encoding']);
512    }
513
514  // message contains alternative parts
515  else if ($message_ctype_primary=='multipart' && $message_ctype_secondary=='alternative' && is_array($structure->parts))
516    {
517    // get html/plaintext parts
518    $plain_part = $html_part = $print_part = $related_part = NULL;
519   
520    foreach ($structure->parts as $p => $sub_part)
521      {
522      $sub_ctype_primary = strtolower($sub_part->ctype_primary);
523      $sub_ctype_secondary = strtolower($sub_part->ctype_secondary);
524
525      // check if sub part is
526      if ($sub_ctype_primary=='text' && $sub_ctype_secondary=='plain')
527        $plain_part = $p;
528      else if ($sub_ctype_primary=='text' && $sub_ctype_secondary=='html')
529        $html_part = $p;
530      else if ($sub_ctype_primary=='text' && $sub_ctype_secondary=='enriched')
531        $enriched_part = $p;
532      else if ($sub_ctype_primary=='multipart' && $sub_ctype_secondary=='related')
533        $related_part = $p;
534      }
535
536    // parse related part (alternative part could be in here)
537    if ($related_part!==NULL && $prefer_html)
538      {
539      list($parts, $attachmnts) = rcmail_parse_message($structure->parts[$related_part], $arg, TRUE);
540      $a_return_parts = array_merge($a_return_parts, $parts);
541      $a_attachments = array_merge($a_attachments, $attachmnts);
542      }
543
544    // print html/plain part
545    else if ($html_part!==NULL && $prefer_html)
546      $print_part = $structure->parts[$html_part];
547    else if ($enriched_part!==NULL)
548      $print_part = $structure->parts[$enriched_part];
549    else if ($plain_part!==NULL)
550      $print_part = $structure->parts[$plain_part];
551
552    // show message body
553    if (is_object($print_part))
554      $a_return_parts[] = array('type' => 'content',
555                                'body' => $print_part->body,
556                                'ctype_primary' => strtolower($print_part->ctype_primary),
557                                'ctype_secondary' => strtolower($print_part->ctype_secondary),
558                                'parameters' => $print_part->ctype_parameters,
559                                'encoding' => $print_part->headers['content-transfer-encoding']);
560    // show plaintext warning
561    else if ($html_part!==NULL)
562      $a_return_parts[] = array('type' => 'content',
563                                'body' => rcube_label('htmlmessage'),
564                                'ctype_primary' => 'text',
565                                'ctype_secondary' => 'plain');
566                               
567    // add html part as attachment
568    if ($html_part!==NULL && $structure->parts[$html_part]!==$print_part)
569      {
570      $html_part = $structure->parts[$html_part];
571      $a_attachments[] = array('filename' => rcube_label('htmlmessage'),
572                               'encoding' => $html_part->headers['content-transfer-encoding'],
573                               'mimetype' => 'text/html',
574                               'part_id'  => $html_part->mime_id,
575                               'size'     => strlen($IMAP->mime_decode($html_part->body, $html_part->headers['content-transfer-encoding'])));
576      }
577    }
578
579  // message contains multiple parts
580  else if ($message_ctype_primary=='multipart' && is_array($structure->parts))
581    {
582    foreach ($structure->parts as $mail_part)
583      {
584      $primary_type = strtolower($mail_part->ctype_primary);
585      $secondary_type = strtolower($mail_part->ctype_secondary);
586
587      // multipart/alternative
588      if ($primary_type=='multipart') // && ($secondary_type=='alternative' || $secondary_type=='mixed' || $secondary_type=='related'))
589        {
590        list($parts, $attachmnts) = rcmail_parse_message($mail_part, $arg, TRUE);
591
592        $a_return_parts = array_merge($a_return_parts, $parts);
593        $a_attachments = array_merge($a_attachments, $attachmnts);
594        }
595
596      // part text/[plain|html] OR message/delivery-status
597      else if (($primary_type=='text' && ($secondary_type=='plain' || $secondary_type=='html')) ||
598               ($primary_type=='message' && $secondary_type=='delivery-status'))
599        {
600        $a_return_parts[] = array('type' => 'content',
601                                  'body' => $mail_part->body,
602                                  'ctype_primary' => $primary_type,
603                                  'ctype_secondary' => $secondary_type,
604                                  'encoding' => $mail_part->headers['content-transfer-encoding']);
605        }
606
607      // part message/*
608      else if ($primary_type=='message')
609        {
610        /* don't parse headers here; they're parsed within the recursive call to rcmail_parse_message()
611        if ($mail_part->parts[0]->headers)
612          $a_return_parts[] = array('type' => 'headers',
613                                    'headers' => $mail_part->parts[0]->headers);
614        */
615                                     
616        list($parts, $attachmnts) = rcmail_parse_message($mail_part->parts[0], $arg, TRUE);
617
618        $a_return_parts = array_merge($a_return_parts, $parts);
619        $a_attachments = array_merge($a_attachments, $attachmnts);
620        }
621
622      // part is file/attachment
623      else if ($mail_part->disposition=='attachment' || $mail_part->disposition=='inline' || $mail_part->headers['content-id'])
624        {
625        if ($message_ctype_secondary=='related' && $mail_part->headers['content-id'])
626          $sa_inline_objects[] = array('filename' => $mail_part->d_parameters['filename'],
627                                       'mimetype' => strtolower("$primary_type/$secondary_type"),
628                                       'part_id'  => $mail_part->mime_id,
629                                       'content_id' => preg_replace(array('/^</', '/>$/'), '', $mail_part->headers['content-id']));
630
631        else if ($mail_part->d_parameters['filename'])
632          $a_attachments[] = array('filename' => $mail_part->d_parameters['filename'],
633                                   'encoding' => strtolower($mail_part->headers['content-transfer-encoding']),
634                                   'mimetype' => strtolower("$primary_type/$secondary_type"),
635                                   'part_id'  => $mail_part->mime_id,
636                                   'size'     => strlen($IMAP->mime_decode($mail_part->body, $mail_part->headers['content-transfer-encoding'])) /*,
637                                   'content'  => $mail_part->body */);
638                                   
639        else if ($mail_part->ctype_parameters['name'])
640          $a_attachments[] = array('filename' => $mail_part->ctype_parameters['name'],
641                                   'encoding' => strtolower($mail_part->headers['content-transfer-encoding']),
642                                   'mimetype' => strtolower("$primary_type/$secondary_type"),
643                                   'part_id'  => $mail_part->mime_id,
644                                   'size'     => strlen($IMAP->mime_decode($mail_part->body, $mail_part->headers['content-transfer-encoding'])) /*,
645                                   'content'  => $mail_part->body */);
646                                   
647                                   
648        }
649      }
650
651
652    // if this was a related part try to resolve references
653    if ($message_ctype_secondary=='related' && sizeof($sa_inline_objects))
654      {
655      $a_replace_patters = array();
656      $a_replace_strings = array();
657       
658      foreach ($sa_inline_objects as $inline_object)
659        {
660        $a_replace_patters[] = 'cid:'.$inline_object['content_id'];
661        $a_replace_strings[] = sprintf($get_url, $inline_object['part_id']);
662        }
663     
664      foreach ($a_return_parts as $i => $return_part)
665        {
666        if ($return_part['type']!='content')
667          continue;
668
669        // decode body and replace cid:...
670        $a_return_parts[$i]['body'] = str_replace($a_replace_patters, $a_replace_strings, $IMAP->mime_decode($return_part['body'], $return_part['encoding']));
671        $a_return_parts[$i]['encoding'] = '7bit';
672        }
673      }
674    }
675   
676
677  // join all parts together
678  //$out .= join($part_delimiter, $a_return_parts);
679
680  return array($a_return_parts, $a_attachments);
681  }
682
683
684
685
686// return table with message headers
687function rcmail_message_headers($attrib, $headers=NULL)
688  {
689  global $IMAP, $OUTPUT, $MESSAGE;
690  static $sa_attrib;
691 
692  // keep header table attrib
693  if (is_array($attrib) && !$sa_attrib)
694    $sa_attrib = $attrib;
695  else if (!is_array($attrib) && is_array($sa_attrib))
696    $attrib = $sa_attrib;
697 
698 
699  if (!isset($MESSAGE))
700    return FALSE;
701
702  // get associative array of headers object
703  if (!$headers)
704    $headers = is_object($MESSAGE['headers']) ? get_object_vars($MESSAGE['headers']) : $MESSAGE['headers'];
705   
706  $header_count = 0;
707 
708  // allow the following attributes to be added to the <table> tag
709  $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id', 'cellpadding', 'cellspacing', 'border', 'summary'));
710  $out = '<table' . $attrib_str . ">\n";
711
712  // show these headers
713  $standard_headers = array('subject', 'from', 'organization', 'to', 'cc', 'reply-to', 'date');
714 
715  foreach ($standard_headers as $hkey)
716    {
717    if (!$headers[$hkey])
718      continue;
719
720    if ($hkey=='date')
721      $header_value = format_date(strtotime($headers[$hkey]));
722    else if (in_array($hkey, array('from', 'to', 'cc', 'reply-to')))
723      $header_value = rep_specialchars_output(rcmail_address_string($IMAP->decode_header($headers[$hkey]), NULL, $attrib['addicon']));
724    else
725      $header_value = rep_specialchars_output($IMAP->decode_header($headers[$hkey]), '', 'all');
726
727    $out .= "\n<tr>\n";
728    $out .= '<td class="header-title">'.rcube_label($hkey).":&nbsp;</td>\n";
729    $out .= '<td class="'.$hkey.'" width="90%">'.$header_value."</td>\n</tr>";
730    $header_count++;
731    }
732
733  $out .= "\n</table>\n\n";
734
735  return $header_count ? $out : ''; 
736  }
737
738
739
740function rcmail_message_body($attrib)
741  {
742  global $CONFIG, $OUTPUT, $MESSAGE, $GET_URL, $REMOTE_OBJECTS, $JS_OBJECT_NAME;
743 
744  if (!is_array($MESSAGE['parts']) && !$MESSAGE['body'])
745    return '';
746   
747  if (!$attrib['id'])
748    $attrib['id'] = 'rcmailMsgBody';
749
750  $safe_mode = (bool)$_GET['_safe'];
751  $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id'));
752  $out = '<div '. $attrib_str . ">\n";
753 
754  $header_attrib = array();
755  foreach ($attrib as $attr => $value)
756    if (preg_match('/^headertable([a-z]+)$/i', $attr, $regs))
757      $header_attrib[$regs[1]] = $value;
758
759
760  // this is an ecrypted message
761  // -> create a plaintext body with the according message
762  if (!sizeof($MESSAGE['parts']) && $MESSAGE['headers']->ctype=='multipart/encrypted')
763    {
764    $MESSAGE['parts'][0] = array('type' => 'content',
765                                 'ctype_primary' => 'text',
766                                 'ctype_secondary' => 'plain',
767                                 'body' => rcube_label('encryptedmessage'));
768    }
769 
770  if ($MESSAGE['parts'])
771    {
772    foreach ($MESSAGE['parts'] as $i => $part)
773      {
774      if ($part['type']=='headers')
775        $out .= rcmail_message_headers(sizeof($header_attrib) ? $header_attrib : NULL, $part['headers']);
776      else if ($part['type']=='content')
777        {
778//        var_dump($part['parameters']);
779        // $body = rcmail_print_body($part['body'], $part['ctype_primary'], $part['ctype_secondary'], $part['encoding'], $safe_mode);
780        $body = rcmail_print_body($part, $safe_mode);
781        $out .= '<div class="message-part">';
782        $out .= rcmail_mod_html_body($body, $attrib['id']);
783        $out .= "</div>\n";
784        }
785      }
786    }
787  else
788    $out .= $MESSAGE['body'];
789
790
791  $ctype_primary = strtolower($MESSAGE['structure']->ctype_primary);
792  $ctype_secondary = strtolower($MESSAGE['structure']->ctype_secondary);
793 
794  // list images after mail body
795  if (get_boolean($attrib['showimages']) && $ctype_primary=='multipart' && $ctype_secondary=='mixed' &&
796      sizeof($MESSAGE['attachments']) && !strstr($message_body, '<html') && strlen($GET_URL))
797    {
798    foreach ($MESSAGE['attachments'] as $attach_prop)
799      {
800      if (strpos($attach_prop['mimetype'], 'image/')===0)
801        $out .= sprintf("\n<hr />\n<p align=\"center\"><img src=\"%s&_part=%s\" alt=\"%s\" title=\"%s\" /></p>\n",
802                        $GET_URL, $attach_prop['part_id'],
803                        $attach_prop['filename'],
804                        $attach_prop['filename']);
805      }
806    }
807 
808  // tell client that there are blocked remote objects
809  if ($REMOTE_OBJECTS && !$safe_mode)
810    $OUTPUT->add_script(sprintf("%s.set_env('blockedobjects', true);", $JS_OBJECT_NAME));
811
812  $out .= "\n</div>";
813  return $out;
814  }
815
816
817
818// modify a HTML message that it can be displayed inside a HTML page
819function rcmail_mod_html_body($body, $container_id)
820  {
821  $last_style_pos = 0;
822  $body_lc = strtolower($body);
823 
824  // find STYLE tags
825  while (($pos = strpos($body_lc, '<style', $last_style_pos)) && ($pos2 = strpos($body_lc, '</style>', $pos)))
826    {
827    $pos2 += 8;
828    $body_pre = substr($body, 0, $pos);
829    $styles = substr($body, $pos, $pos2-$pos);
830    $body_post = substr($body, $pos2, strlen($body)-$pos2);
831   
832    // replace all css definitions with #container [def]
833    $styles = rcmail_mod_css_styles($styles, $container_id);
834   
835    $body = $body_pre . $styles . $body_post;
836    $last_style_pos = $pos2;
837    }
838
839
840  // remove SCRIPT tags
841  while (($pos = strpos($body_lc, '<script')) && ($pos2 = strpos($body_lc, '</script>', $pos)))
842    {
843    $pos2 += 8;
844    $body = substr($body, 0, $pos) . substr($body, $pos2, strlen($body)-$pos2);
845    $body_lc = strtolower($body);
846    }
847 
848
849  // resolve <base href>
850  $base_reg = '/(<base.*href=["\']?)([hftps]{3,5}:\/{2}[^"\'\s]+)([^<]*>)/i';
851  if (preg_match($base_reg, $body, $regs))
852    {
853    $base_url = $regs[2];
854    $body = preg_replace('/(src|background|href)=(["\']?)([\.\/]+[^"\'\s]+)(\2|\s|>)/Uie', "'\\1=\"'.make_absolute_url('\\3', '$base_url').'\"'", $body);
855    $body = preg_replace('/(url\s*\()(["\']?)([\.\/]+[^"\'\)\s]+)(\2)\)/Uie', "'\\1\''.make_absolute_url('\\3', '$base_url').'\')'", $body);
856    $body = preg_replace($base_reg, '', $body);
857    }
858
859
860  // add comments arround html and other tags
861  $out = preg_replace(array('/(<\/?html[^>]*>)/i',
862                            '/(<\/?head[^>]*>)/i',
863                            '/(<title[^>]*>.+<\/title>)/ui',
864                            '/(<\/?meta[^>]*>)/i'),
865                      '<!--\\1-->',
866                      $body);
867                     
868  $out = preg_replace(array('/(<body[^>]*>)/i',
869                            '/(<\/body>)/i'),
870                      array('<div class="rcmBody">',
871                            '</div>'),
872                      $out);
873
874 
875  return $out;
876  }
877
878
879
880// replace all css definitions with #container [def]
881function rcmail_mod_css_styles($source, $container_id)
882  {
883  $a_css_values = array();
884  $last_pos = 0;
885 
886  // cut out all contents between { and }
887  while (($pos = strpos($source, '{', $last_pos)) && ($pos2 = strpos($source, '}', $pos)))
888    {
889    $key = sizeof($a_css_values);
890    $a_css_values[$key] = substr($source, $pos+1, $pos2-($pos+1));
891    $source = substr($source, 0, $pos+1) . "<<str_replacement[$key]>>" . substr($source, $pos2, strlen($source)-$pos2);
892    $last_pos = $pos+2;
893    }
894 
895  $styles = preg_replace('/(^\s*|,\s*)([a-z0-9\._][a-z0-9\.\-_]*)/im', "\\1#$container_id \\2", $source);
896  $styles = preg_replace('/<<str_replacement\[([0-9]+)\]>>/e', "\$a_css_values[\\1]", $styles);
897 
898  // replace body definition because we also stripped off the <body> tag
899  $styles = preg_replace("/$container_id\s+body/i", "$container_id div.rcmBody", $styles);
900 
901  return $styles;
902  }
903
904
905
906// return first text part of a message
907function rcmail_first_text_part($message_parts)
908  {
909  if (!is_array($message_parts))
910    return FALSE;
911   
912  $html_part = NULL;
913     
914  // check all message parts
915  foreach ($message_parts as $pid => $part)
916    {
917    $mimetype = strtolower($part->ctype_primary.'/'.$part->ctype_secondary);
918    if ($mimetype=='text/plain')
919      {
920      $body = rcube_imap::mime_decode($part->body, $part->headers['content-transfer-encoding']);
921      $body = rcube_imap::charset_decode($body, $part->ctype_parameters);
922      return $body;
923      }
924    else if ($mimetype=='text/html')
925      {
926      $html_part = rcube_imap::mime_decode($part->body, $part->headers['content-transfer-encoding']);
927      $html_part = rcube_imap::charset_decode($html_part, $part->ctype_parameters);
928      }
929    }
930   
931
932  // convert HTML to plain text
933  if ($html_part)
934    {   
935    // remove special chars encoding
936    $trans = array_flip(get_html_translation_table(HTML_ENTITIES));
937    $html_part = strtr($html_part, $trans);
938
939    // create instance of html2text class
940    $txt = new html2text($html_part);
941    return $txt->get_text();
942    }
943
944  return FALSE;
945  }
946
947
948// get source code of a specific message and cache it
949function rcmail_message_source($uid)
950  {
951  global $IMAP, $DB;
952
953  // get message ID if uid is given 
954  $headers = $IMAP->get_headers($uid);
955  $message_id = $headers->messageID;
956 
957  // get cached message source
958  $msg_source = rcube_read_cache($message_id);
959
960  // get message from server and cache it
961  if (!$msg_source)
962    {
963    $msg_source = $IMAP->get_raw_body($uid);
964    rcube_write_cache($message_id, $msg_source, TRUE);
965    }
966
967  return $msg_source;
968  }
969
970
971// decode address string and re-format it as HTML links
972function rcmail_address_string($input, $max=NULL, $addicon=NULL)
973  {
974  global $IMAP, $PRINT_MODE, $CONFIG, $OUTPUT, $JS_OBJECT_NAME, $EMAIL_ADDRESS_PATTERN;
975 
976  $a_parts = $IMAP->decode_address_list($input);
977
978  if (!sizeof($a_parts))
979    return $input;
980
981  $c = count($a_parts);
982  $j = 0;
983  $out = '';
984
985  foreach ($a_parts as $part)
986    {
987    $j++;
988    if ($PRINT_MODE)
989      $out .= sprintf('%s &lt;%s&gt;', htmlentities($part['name']), $part['mailto']);
990    else if (preg_match($EMAIL_ADDRESS_PATTERN, $part['mailto']))
991      {
992      $out .= sprintf('<a href="mailto:%s" onclick="return %s.command(\'compose\',\'%s\',this)" class="rcmContactAddress" title="%s">%s</a>',
993                      $part['mailto'],
994                      $JS_OBJECT_NAME,
995                      $part['mailto'],
996                      $part['mailto'],
997                      htmlentities($part['name']));
998                     
999      if ($addicon)
1000        $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>',
1001                        $JS_OBJECT_NAME,
1002                        urlencode($part['string']),
1003                        rcube_label('addtoaddressbook'),
1004                        $CONFIG['skin_path'],
1005                        $addicon);
1006      }
1007    else
1008      {
1009      if ($part['name'])
1010        $out .= htmlentities($part['name']);
1011      if ($part['mailto'])
1012        $out .= (strlen($out) ? ' ' : '') . sprintf('&lt;%s&gt;', $part['mailto']);
1013      }
1014     
1015    if ($c>$j)
1016      $out .= ','.($max ? '&nbsp;' : ' ');
1017       
1018    if ($max && $j==$max && $c>$j)
1019      {
1020      $out .= '...';
1021      break;
1022      }       
1023    }
1024   
1025  return $out;
1026  }
1027
1028
1029function rcmail_message_part_controls()
1030  {
1031  global $CONFIG, $IMAP, $MESSAGE;
1032 
1033  if (!is_array($MESSAGE) || !is_array($MESSAGE['parts']) || !($_GET['_uid'] && $_GET['_part']) || !$MESSAGE['parts'][$_GET['_part']])
1034    return '';
1035   
1036  $part = $MESSAGE['parts'][$_GET['_part']];
1037 
1038  $attrib_str = create_attrib_string($attrib, array('id', 'class', 'style', 'cellspacing', 'cellpadding', 'border', 'summary'));
1039  $out = '<table '. $attrib_str . ">\n";
1040 
1041  $filename = $part->d_parameters['filename'] ? $part->d_parameters['filename'] : $part->ctype_parameters['name'];
1042  $filesize = strlen($IMAP->mime_decode($part->body, $part->headers['content-transfer-encoding']));
1043 
1044  if ($filename)
1045    {
1046    $out .= sprintf('<tr><td class="title">%s</td><td>%s</td><td>[<a href="./?%s">%s</a>]</tr>'."\n",
1047                    rcube_label('filename'),
1048                    rep_specialchars_output($filename),
1049                    str_replace('_frame=', '_download=', $_SERVER['QUERY_STRING']),
1050                    rcube_label('download'));
1051    }
1052   
1053  if ($filesize)
1054    $out .= sprintf('<tr><td class="title">%s</td><td>%s</td></tr>'."\n",
1055                    rcube_label('filesize'),
1056                    show_bytes($filesize));
1057 
1058  $out .= "\n</table>";
1059 
1060  return $out;
1061  }
1062
1063
1064
1065function rcmail_message_part_frame($attrib)
1066  {
1067  global $MESSAGE;
1068 
1069  $part = $MESSAGE['parts'][$_GET['_part']];
1070  $ctype_primary = strtolower($part->ctype_primary);
1071
1072  $attrib['src'] = './?'.str_replace('_frame=', ($ctype_primary=='text' ? '_show=' : '_preload='), $_SERVER['QUERY_STRING']);
1073
1074  $attrib_str = create_attrib_string($attrib, array('id', 'class', 'style', 'src', 'width', 'height'));
1075  $out = '<iframe '. $attrib_str . "></ifame>";
1076   
1077  return $out;
1078  }
1079
1080
1081
1082// clear message composing settings
1083function rcmail_compose_cleanup()
1084  {
1085  if (!isset($_SESSION['compose']))
1086    return;
1087 
1088  // remove attachment files from temp dir
1089  if (is_array($_SESSION['compose']['attachments']))
1090    foreach ($_SESSION['compose']['attachments'] as $attachment)
1091      unlink($attachment['path']);
1092
1093  // kill temp dir
1094  if ($_SESSION['compose']['temp_dir'])
1095    rmdir($_SESSION['compose']['temp_dir']);
1096 
1097  unset($_SESSION['compose']);
1098  }
1099 
1100 
1101?>
Note: See TracBrowser for help on using the repository browser.