/**
  * This function cleanup_addr() has been used a large part of function
  * rcmail_email_input_format() in program/steps/mail/sendmail.inc of
  * Roundcube core at version 0.9.
  */
 function cleanup_addr($mailto)
 {
     global $RCMAIL;
     // simplified email regexp, supporting quoted local part
     $email_regexp = '(\\S+|("[^"]+"))@\\S+';
     $delim = trim($RCMAIL->config->get('recipients_separator', ','));
     $regexp = array("/[,;{$delim}]\\s*[\r\n]+/", '/[\\r\\n]+/', "/[,;{$delim}]\\s*\$/m", '/;/', '/(\\S{1})(<' . $email_regexp . '>)/U');
     $replace = array($delim . ' ', ', ', '', $delim, '\\1 \\2');
     // replace new lines and strip ending ', ', make address input more valid
     $mailto = trim(preg_replace($regexp, $replace, $mailto));
     $result = array();
     $items = rcube_explode_quoted_string($delim, $mailto);
     foreach ($items as $item) {
         $item = trim($item);
         // address in brackets without name (do nothing)
         if (preg_match('/^<' . $email_regexp . '>$/', $item)) {
             $item = rcube_idn_to_ascii(trim($item, '<>'));
             $result[] = $item;
             // address without brackets and without name (add brackets)
         } else {
             if (preg_match('/^' . $email_regexp . '$/', $item)) {
                 $item = rcube_idn_to_ascii($item);
                 $result[] = $item;
                 // address with name (handle name)
             } else {
                 if (preg_match('/<*' . $email_regexp . '>*$/', $item, $matches)) {
                     $address = $matches[0];
                     $name = trim(str_replace($address, '', $item));
                     if ($name[0] == '"' && $name[count($name) - 1] == '"') {
                         $name = substr($name, 1, -1);
                     }
                     $name = stripcslashes($name);
                     $address = rcube_idn_to_ascii(trim($address, '<>'));
                     $result[] = $address;
                     $item = $address;
                 } else {
                     if (trim($item)) {
                         continue;
                     }
                 }
             }
         }
     }
     return implode(', ', $result);
 }
Example #2
0
 /**
  * @access private
  */
 private function _parse_address_list($str, $decode = true)
 {
     // remove any newlines and carriage returns before
     $a = rcube_explode_quoted_string('[,;]', preg_replace("/[\r\n]/", " ", $str));
     $result = array();
     foreach ($a as $key => $val) {
         $val = preg_replace("/([\"\\w])</", "\$1 <", $val);
         $sub_a = rcube_explode_quoted_string(' ', $decode ? $this->decode_header($val) : $val);
         $result[$key]['name'] = '';
         foreach ($sub_a as $k => $v) {
             // use angle brackets in regexp to not handle names with @ sign
             if (preg_match('/^<\\S+@\\S+>$/', $v)) {
                 $result[$key]['address'] = trim($v, '<>');
             } else {
                 $result[$key]['name'] .= (empty($result[$key]['name']) ? '' : ' ') . str_replace("\"", '', stripslashes($v));
             }
         }
         if (empty($result[$key]['name'])) {
             $result[$key]['name'] = $result[$key]['address'];
         } elseif (empty($result[$key]['address'])) {
             $result[$key]['address'] = $result[$key]['name'];
         }
     }
     return $result;
 }
Example #3
0
 /**
  * Take a set of recipients and parse them, returning an array of
  * bare addresses (forward paths) that can be passed to sendmail
  * or an smtp server with the rcpt to: command.
  *
  * @param mixed Either a comma-seperated list of recipients
  *              (RFC822 compliant), or an array of recipients,
  *              each RFC822 valid.
  *
  * @return array An array of forward paths (bare addresses).
  * @access private
  */
 private function _parse_rfc822($recipients)
 {
     // if we're passed an array, assume addresses are valid and implode them before parsing.
     if (is_array($recipients)) {
         $recipients = implode(', ', $recipients);
     }
     $addresses = array();
     $recipients = rcube_explode_quoted_string(',', $recipients);
     reset($recipients);
     while (list($k, $recipient) = each($recipients)) {
         $a = explode(" ", $recipient);
         while (list($k2, $word) = each($a)) {
             if (strpos($word, "@") > 0 && $word[strlen($word) - 1] != '"') {
                 $word = preg_replace('/^<|>$/', '', trim($word));
                 if (in_array($word, $addresses) === false) {
                     array_push($addresses, $word);
                 }
             }
         }
     }
     return $addresses;
 }
Example #4
0
 function listSubscribed($ref, $mailbox)
 {
     if (empty($mailbox)) {
         $mailbox = '*';
     }
     if (empty($ref) && $this->rootdir) {
         $ref = $this->rootdir;
     }
     $folders = array();
     // send command
     if (!$this->putLine('lsb LSUB "' . $this->escape($ref) . '" "' . $this->escape($mailbox) . '"')) {
         $this->error = "Couldn't send LSUB command";
         return false;
     }
     $i = 0;
     // get folder list
     do {
         $line = $this->readLine(500);
         $line = $this->multLine($line, true);
         $a = explode(' ', $line);
         if ($line[0] == '*' && ($a[1] == 'LSUB' || $a[1] == 'LIST')) {
             $line = rtrim($line);
             // split one line
             $a = rcube_explode_quoted_string(' ', $line);
             // last string is folder name
             $folder = preg_replace(array('/^"/', '/"$/'), '', $this->UnEscape($a[count($a) - 1]));
             // @TODO: do we need this check???
             if (!in_array($folder, $folders)) {
                 $folders[$i] = $folder;
             }
             // second from last is delimiter
             $delim = trim($a[count($a) - 2], '"');
             // is it a container?
             $i++;
         }
     } while (!$this->startsWith($line, 'lsb', true));
     if (is_array($folders)) {
         if (!empty($ref)) {
             // if rootdir was specified, make sure it's the first element
             // some IMAP servers (i.e. Courier) won't return it
             if ($ref[strlen($ref) - 1] == $delim) {
                 $ref = substr($ref, 0, strlen($ref) - 1);
             }
             if ($folders[0] != $ref) {
                 array_unshift($folders, $ref);
             }
         }
         return $folders;
     }
     $this->error = $line;
     return false;
 }
Example #5
0
 private function rcmail_email_input_format($mailto, $count = false, $check = true)
 {
     $regexp = array('/[,;]\\s*[\\r\\n]+/', '/[\\r\\n]+/', '/[,;]\\s*$/m', '/;/', '/(\\S{1})(<\\S+@\\S+>)/U');
     $replace = array(', ', ', ', '', ',', '\\1 \\2');
     // replace new lines and strip ending ', ', make address input more valid
     $mailto = trim(preg_replace($regexp, $replace, $mailto));
     $result = array();
     $items = rcube_explode_quoted_string(',', $mailto);
     foreach ($items as $item) {
         $item = trim($item);
         // address in brackets without name (do nothing)
         if (preg_match('/^<\\S+@\\S+>$/', $item)) {
             $item = idn_to_ascii($item);
             $result[] = $item;
             // address without brackets and without name (add brackets)
         } else {
             if (preg_match('/^\\S+@\\S+$/', $item)) {
                 $item = idn_to_ascii($item);
                 $result[] = '<' . $item . '>';
                 // address with name (handle name)
             } else {
                 if (preg_match('/\\S+@\\S+>*$/', $item, $matches)) {
                     $address = $matches[0];
                     $name = str_replace($address, '', $item);
                     $name = trim($name);
                     if ($name && ($name[0] != '"' || $name[strlen($name) - 1] != '"') && preg_match('/[\\(\\)\\<\\>\\\\.\\[\\]@,;:"]/', $name)) {
                         $name = '"' . addcslashes($name, '"') . '"';
                     }
                     $address = idn_to_ascii($address);
                     if (!preg_match('/^<\\S+@\\S+>$/', $address)) {
                         $address = '<' . $address . '>';
                     }
                     $result[] = $name . ' ' . $address;
                     $item = $address;
                 } else {
                     if (trim($item)) {
                         continue;
                     }
                 }
             }
         }
         // check address format
         $item = trim($item, '<>');
         if ($item && $check && !check_email($item)) {
             $this->email_format_error = $item;
             return;
         }
     }
     if ($count) {
         $this->recipient_count += count($result);
     }
     return implode(', ', $result);
 }
Example #6
0
 /**
  * Encodes a header as per RFC2047
  *
  * @param  array $input The header data to encode
  * @param  array $params Extra build parameters
  * @return array Encoded data
  * @access private
  * @override
  */
 function _encodeHeaders($input, $params = array())
 {
     $maxlen = 73;
     $params += $this->_build_params;
     foreach ($input as $hdr_name => $hdr_value) {
         // if header contains e-mail addresses
         if (preg_match('/\\s<.+@[a-z0-9\\-\\.]+\\.[a-z]+>/U', $hdr_value)) {
             $chunks = rcube_explode_quoted_string(',', $hdr_value);
         } else {
             $chunks = array($hdr_value);
         }
         $hdr_value = '';
         $line_len = 0;
         foreach ($chunks as $i => $value) {
             $value = trim($value);
             //This header contains non ASCII chars and should be encoded.
             if (preg_match('/[\\x80-\\xFF]{1}/', $value)) {
                 $suffix = '';
                 // Don't encode e-mail address
                 if (preg_match('/(.+)\\s(<.+@[a-z0-9\\-\\.]+>)$/Ui', $value, $matches)) {
                     $value = $matches[1];
                     $suffix = ' ' . $matches[2];
                 }
                 switch ($params['head_encoding']) {
                     case 'base64':
                         // Base64 encoding has been selected.
                         $mode = 'B';
                         $encoded = base64_encode($value);
                         break;
                     case 'quoted-printable':
                     default:
                         // quoted-printable encoding has been selected
                         $mode = 'Q';
                         // replace ?, =, _ and spaces
                         $encoded = str_replace(array('=', '_', '?', ' '), array('=3D', '=5F', '=3F', '_'), $value);
                         $encoded = preg_replace('/([\\x80-\\xFF])/e', "'='.sprintf('%02X', ord('\\1'))", $encoded);
                 }
                 $value = '=?' . $params['head_charset'] . '?' . $mode . '?' . $encoded . '?=' . $suffix;
             }
             // add chunk to output string by regarding the header maxlen
             $len = strlen($value);
             if ($i == 0 || $line_len + $len < $maxlen) {
                 $hdr_value .= ($i > 0 ? ', ' : '') . $value;
                 $line_len += $len + ($i > 0 ? 2 : 0);
             } else {
                 $hdr_value .= ($i > 0 ? ', ' : '') . "\n " . $value;
                 $line_len = $len;
             }
         }
         $input[$hdr_name] = wordwrap($hdr_value, 990, "\n", true);
         // hard limit header length
     }
     return $input;
 }
Example #7
0
 function fetchHeaders($mailbox, $message_set, $uidfetch = false, $bodystr = false, $add = '')
 {
     $result = array();
     if (!$this->select($mailbox)) {
         return false;
     }
     $message_set = $this->compressMessageSet($message_set);
     if ($add) {
         $add = ' ' . trim($add);
     }
     /* FETCH uid, size, flags and headers */
     $key = $this->nextTag();
     $request = $key . ($uidfetch ? ' UID' : '') . " FETCH {$message_set} ";
     $request .= "(UID RFC822.SIZE FLAGS INTERNALDATE ";
     if ($bodystr) {
         $request .= "BODYSTRUCTURE ";
     }
     $request .= "BODY.PEEK[HEADER.FIELDS (DATE FROM TO SUBJECT CONTENT-TYPE ";
     $request .= "LIST-POST DISPOSITION-NOTIFICATION-TO" . $add . ")])";
     if (!$this->putLine($request)) {
         $this->setError(self::ERROR_COMMAND, "Unable to send command: {$request}");
         return false;
     }
     do {
         $line = $this->readLine(4096);
         $line = $this->multLine($line);
         if (!$line) {
             break;
         }
         if (preg_match('/^\\* ([0-9]+) FETCH/', $line, $m)) {
             $id = intval($m[1]);
             $result[$id] = new rcube_mail_header();
             $result[$id]->id = $id;
             $result[$id]->subject = '';
             $result[$id]->messageID = 'mid:' . $id;
             $lines = array();
             $ln = 0;
             // Sample reply line:
             // * 321 FETCH (UID 2417 RFC822.SIZE 2730 FLAGS (\Seen)
             // INTERNALDATE "16-Nov-2008 21:08:46 +0100" BODYSTRUCTURE (...)
             // BODY[HEADER.FIELDS ...
             if (preg_match('/^\\* [0-9]+ FETCH \\((.*) BODY/sU', $line, $matches)) {
                 $str = $matches[1];
                 // swap parents with quotes, then explode
                 $str = preg_replace('/[()]/', '"', $str);
                 $a = rcube_explode_quoted_string(' ', $str);
                 // did we get the right number of replies?
                 $parts_count = count($a);
                 if ($parts_count >= 6) {
                     for ($i = 0; $i < $parts_count; $i = $i + 2) {
                         if ($a[$i] == 'UID') {
                             $result[$id]->uid = intval($a[$i + 1]);
                         } else {
                             if ($a[$i] == 'RFC822.SIZE') {
                                 $result[$id]->size = intval($a[$i + 1]);
                             } else {
                                 if ($a[$i] == 'INTERNALDATE') {
                                     $time_str = $a[$i + 1];
                                 } else {
                                     if ($a[$i] == 'FLAGS') {
                                         $flags_str = $a[$i + 1];
                                     }
                                 }
                             }
                         }
                     }
                     $time_str = str_replace('"', '', $time_str);
                     // if time is gmt...
                     $time_str = str_replace('GMT', '+0000', $time_str);
                     $result[$id]->internaldate = $time_str;
                     $result[$id]->timestamp = $this->StrToTime($time_str);
                     $result[$id]->date = $time_str;
                 }
                 // BODYSTRUCTURE
                 if ($bodystr) {
                     while (!preg_match('/ BODYSTRUCTURE (.*) BODY\\[HEADER.FIELDS/sU', $line, $m)) {
                         $line2 = $this->readLine(1024);
                         $line .= $this->multLine($line2, true);
                     }
                     $result[$id]->body_structure = $m[1];
                 }
                 // the rest of the result
                 if (preg_match('/ BODY\\[HEADER.FIELDS \\(.*?\\)\\]\\s*(.*)$/s', $line, $m)) {
                     $reslines = explode("\n", trim($m[1], '"'));
                     // re-parse (see below)
                     foreach ($reslines as $resln) {
                         if (ord($resln[0]) <= 32) {
                             $lines[$ln] .= (empty($lines[$ln]) ? '' : "\n") . trim($resln);
                         } else {
                             $lines[++$ln] = trim($resln);
                         }
                     }
                 }
             }
             // Start parsing headers.  The problem is, some header "lines" take up multiple lines.
             // So, we'll read ahead, and if the one we're reading now is a valid header, we'll
             // process the previous line.  Otherwise, we'll keep adding the strings until we come
             // to the next valid header line.
             do {
                 $line = rtrim($this->readLine(300), "\r\n");
                 // The preg_match below works around communigate imap, which outputs " UID <number>)".
                 // Without this, the while statement continues on and gets the "FH0 OK completed" message.
                 // If this loop gets the ending message, then the outer loop does not receive it from radline on line 1249.
                 // This in causes the if statement on line 1278 to never be true, which causes the headers to end up missing
                 // If the if statement was changed to pick up the fh0 from this loop, then it causes the outer loop to spin
                 // An alternative might be:
                 // if (!preg_match("/:/",$line) && preg_match("/\)$/",$line)) break;
                 // however, unsure how well this would work with all imap clients.
                 if (preg_match("/^\\s*UID [0-9]+\\)\$/", $line)) {
                     break;
                 }
                 // handle FLAGS reply after headers (AOL, Zimbra?)
                 if (preg_match('/\\s+FLAGS \\((.*)\\)\\)$/', $line, $matches)) {
                     $flags_str = $matches[1];
                     break;
                 }
                 if (ord($line[0]) <= 32) {
                     $lines[$ln] .= (empty($lines[$ln]) ? '' : "\n") . trim($line);
                 } else {
                     $lines[++$ln] = trim($line);
                 }
                 // patch from "Maksim Rubis" <*****@*****.**>
             } while ($line[0] != ')' && !$this->startsWith($line, $key, true));
             if (strncmp($line, $key, strlen($key))) {
                 // process header, fill rcube_mail_header obj.
                 // initialize
                 if (is_array($headers)) {
                     reset($headers);
                     while (list($k, $bar) = each($headers)) {
                         $headers[$k] = '';
                     }
                 }
                 // create array with header field:data
                 while (list($lines_key, $str) = each($lines)) {
                     list($field, $string) = $this->splitHeaderLine($str);
                     $field = strtolower($field);
                     $string = preg_replace('/\\n\\s*/', ' ', $string);
                     switch ($field) {
                         case 'date':
                             $result[$id]->date = $string;
                             $result[$id]->timestamp = $this->strToTime($string);
                             break;
                         case 'from':
                             $result[$id]->from = $string;
                             break;
                         case 'to':
                             $result[$id]->to = preg_replace('/undisclosed-recipients:[;,]*/', '', $string);
                             break;
                         case 'subject':
                             $result[$id]->subject = $string;
                             break;
                         case 'reply-to':
                             $result[$id]->replyto = $string;
                             break;
                         case 'cc':
                             $result[$id]->cc = $string;
                             break;
                         case 'bcc':
                             $result[$id]->bcc = $string;
                             break;
                         case 'content-transfer-encoding':
                             $result[$id]->encoding = $string;
                             break;
                         case 'content-type':
                             $ctype_parts = preg_split('/[; ]/', $string);
                             $result[$id]->ctype = strtolower(array_shift($ctype_parts));
                             if (preg_match('/charset\\s*=\\s*"?([a-z0-9\\-\\.\\_]+)"?/i', $string, $regs)) {
                                 $result[$id]->charset = $regs[1];
                             }
                             break;
                         case 'in-reply-to':
                             $result[$id]->in_reply_to = str_replace(array("\n", '<', '>'), '', $string);
                             break;
                         case 'references':
                             $result[$id]->references = $string;
                             break;
                         case 'return-receipt-to':
                         case 'disposition-notification-to':
                         case 'x-confirm-reading-to':
                             $result[$id]->mdn_to = $string;
                             break;
                         case 'message-id':
                             $result[$id]->messageID = $string;
                             break;
                         case 'x-priority':
                             if (preg_match('/^(\\d+)/', $string, $matches)) {
                                 $result[$id]->priority = intval($matches[1]);
                             }
                             break;
                         default:
                             if (strlen($field) > 2) {
                                 $result[$id]->others[$field] = $string;
                             }
                             break;
                     }
                     // end switch ()
                 }
                 // end while ()
             }
             // process flags
             if (!empty($flags_str)) {
                 $flags_str = preg_replace('/[\\\\"]/', '', $flags_str);
                 $flags_a = explode(' ', $flags_str);
                 if (is_array($flags_a)) {
                     foreach ($flags_a as $flag) {
                         $flag = strtoupper($flag);
                         if ($flag == 'SEEN') {
                             $result[$id]->seen = true;
                         } else {
                             if ($flag == 'DELETED') {
                                 $result[$id]->deleted = true;
                             } else {
                                 if ($flag == 'RECENT') {
                                     $result[$id]->recent = true;
                                 } else {
                                     if ($flag == 'ANSWERED') {
                                         $result[$id]->answered = true;
                                     } else {
                                         if ($flag == '$FORWARDED') {
                                             $result[$id]->forwarded = true;
                                         } else {
                                             if ($flag == 'DRAFT') {
                                                 $result[$id]->is_draft = true;
                                             } else {
                                                 if ($flag == '$MDNSENT') {
                                                     $result[$id]->mdn_sent = true;
                                                 } else {
                                                     if ($flag == 'FLAGGED') {
                                                         $result[$id]->flagged = true;
                                                     }
                                                 }
                                             }
                                         }
                                     }
                                 }
                             }
                         }
                     }
                     $result[$id]->flags = $flags_a;
                 }
             }
         }
     } while (!$this->startsWith($line, $key, true));
     return $result;
 }