id = $_id; $this->pj = array(); $this->messages = array(); if (!$this->_header()) { $this->valid = false; return null; } if ($body = $banana->nntp->body($_id)) { $this->body = join("\n", $body); } else { $this->valid = false; return null; } if (isset($this->headers['content-transfer-encoding'])) { if (preg_match("/base64/", $this->headers['content-transfer-encoding'])) { $this->body = base64_decode($this->body); } elseif (preg_match("/quoted-printable/", $this->headers['content-transfer-encoding'])) { $this->body = quoted_printable_decode($this->body); } } if ($this->_split_multipart($this->headers, $this->body)) { $this->set_body_to_part(0); } else { if(isset($mpart_type)) { $this->_split_multipart($mpart_type[1], $mpart_boundary[1]); } $this->_find_uuencode(); $this->_fix_charset(); } } /** find and add uuencoded attachments */ function _find_uuencode() { if (preg_match_all('@\n(begin \d+ ([^\r\n]+)\r?(?:\n(?!end)[^\n]*)*\nend)@', $this->body, $matches, PREG_SET_ORDER)) { foreach ($matches as $match) { $mime = trim(exec('echo '.escapeshellarg($match[1]).' | uudecode -o /dev/stdout | file -bi -')); if ($mime != 'application/x-empty') { $this->body = trim(str_replace($match[0], '', $this->body)); $body = $match[1]; $header['content-type'] = $mime.'; name="'.$match[2].'"'; $header['content-transfer-encoding'] = 'x-uuencode'; $header['content-disposition'] = 'attachment; filename="'.$match[2].'"'; $this->_add_attachment(Array('headers' => $header, 'body' => $body)); } } } } /** split multipart messages * @param $type STRING multipart type description * @param $boundary STRING multipart boundary identification string */ function _split_multipart($headers, $body) { if (!isset($headers['content-type']) || !preg_match("@multipart/([^;]+);@", $headers['content-type'], $type)) { return false; } preg_match("/boundary=\"?([^ \"]+)\"?/", $headers['content-type'], $boundary); $boundary = $boundary[1]; $type = $type[1]; $parts = preg_split("@\n--$boundary(--|\n)@", $body); foreach ($parts as $part) { $part = $this->_get_part($part); $local_header = $part['headers']; $local_body = $part['body']; if (!$this->_split_multipart($local_header, $local_body)) { $is_text = isset($local_header['content-type']) && preg_match("@text/([^;]+);@", $local_header['content-type']) && (!isset($local_header['content-disposition']) || !preg_match('@attachment@', $local_header['content-disposition'])); // alternative ==> multiple formats for messages if ($type == 'alternative' && $is_text) { array_push($this->messages, $part); // !alternative ==> une body, others are attachments } else if ($is_text) { if (count($this->messages) == 0) { $this->body = $local_body; foreach (array_keys($local_header) as $key) { $this->header[$key] = $local_header[$key]; } array_push($this->messages, $part); } else { $this->_add_attachment($part); } } else { $this->_add_attachment($part); } } } return true; } /** extract new headers from the part * @param $part STRING part of a multipart message */ function _get_part($part) { global $banana; $local_headers = Array(); $lines = split("\n", $part); while (count($lines)) { $line = array_shift($lines); if ($line != "") { if (preg_match('@^[\t\r ]+@', $line) && isset($hdr)) { $local_headers[$hdr] .= ' '.trim($line); } else { list($hdr, $val) = split(":[ \t\r]*", $line, 2); $hdr = strtolower($hdr); if (in_array($hdr, $banana->parse_hdr)) { $local_headers[$hdr] = $val; } } } else { break; } } $local_body = join("\n", $lines); if (isset($local_headers['content-transfer-encoding']) && preg_match("/quoted-printable/", $local_headers['content-transfer-encoding'])) { $local_body = quoted_printable_decode($local_body); } return Array('headers' => $local_headers, 'body' => $local_body); } /** add an attachment */ function _add_attachment($part) { $local_header = $part['headers']; $local_body = $part['body']; if ((isset($local_header['content-disposition']) && preg_match('/filename="?([^"]+)"?/', $local_header['content-disposition'], $filename)) || (isset($local_header['content-type']) && preg_match('/name="?([^"]+)"?/', $local_header['content-type'], $filename))) { $filename = $filename[1]; } if (!isset($filename)) { $filename = "attachment" . count($this->pj); } if (isset($local_header['content-type']) && preg_match('/^\s*([^ ;]+);/', $local_header['content-type'], $mimetype)) { $mimetype = $mimetype[1]; } else { return false; } if (isset($local_header['content-id']) && preg_match('/^\s*<([^> ]+)>/', $local_header['content-id'], $cid)) { $cid = $cid[1]; } else { $cid = null; } array_push($this->pj, Array('MIME' => $mimetype, 'filename' => $filename, 'encoding' => strtolower($local_header['content-transfer-encoding']), 'data' => $local_body, 'cid' => $cid)); return true; } /** Fix body charset (convert body to utf8) * @return false if failed */ function _fix_charset() { if (isset($this->headers['content-type']) && preg_match('!charset="?([^;"]*)"?\s*(;|$)?!', $this->headers['content-type'], $matches)) { $body = iconv($matches[1], 'utf-8', $this->body); if (strlen($body) == 0) { return false; } $this->body = $body; } else { $this->body = utf8_encode($this->body); } return true; } /** return body in plain text (useful for messages without a text/plain part) */ function get_body() { preg_match("@text/([^;]+);@", $this->headers['content-type'], $format); if ($format[1] == 'plain') { return $this->body; } if ($format[1] == 'richtext') { return htmlToPlainText(richtextToHtml($this->body)); } else { return htmlToPlainText($this->body); } } /** return local url for the given cid * @param cid STRING */ function find_attachment($cid) { global $banana; $i = 0; foreach ($this->pj as $pj) { if ($pj['cid'] == $cid) { return htmlentities(makeLink(Array('group' => $banana->state['group'], 'artid' => $this->id, 'pj' => $i, 'action' => 'view'))); } $i++; } return 'cid:' . $cid;; } /** decode an attachment * @param pjid INT id of the attachment to decode * @param action BOOL action to execute : true=view, false=download */ function get_attachment($pjid, $action = false) { if ($pjid >= count($this->pj)) { return false; } else { $file = $this->pj[$pjid]; header('Content-Type: '.$file['MIME'].'; name="'.$file['filename'].'"'); if (!$action) { header('Content-Disposition: attachment; filename="'.$file['filename'].'"'); } else { header('Content-Disposition: inline; filename="'.$file['filename'].'"'); } if ($file['encoding'] == 'base64') { echo base64_decode($file['data']); } else if ($file['encoding'] == 'x-uuencode') { passthru('echo '.escapeshellarg($file['data']).' | uudecode -o /dev/stdout'); } else { header('Content-Transfer-Encoding: '.$file['encoding']); echo $file['data']; } return true; } } /** set body to represent the given part * @param partid INT index of the part in messages */ function set_body_to_part($partid) { global $banana; if (count($this->messages) == 0) { return false; } $local_header = $this->messages[$partid]['headers']; $this->body = $this->messages[$partid]['body']; foreach ($banana->parse_hdr as $hdr) { if (isset($local_header[$hdr])) { $this->headers[$hdr] = $local_header[$hdr]; } } $this->_fix_charset(); return true; } function _header() { global $banana; $hdrs = $banana->nntp->head($this->id); if (!$hdrs) { return false; } // parse headers foreach ($hdrs as $line) { if (preg_match("/^[\t\r ]+/", $line)) { $line = ($hdr=="X-Face"?"":" ").ltrim($line); if (in_array($hdr, $banana->parse_hdr)) { $this->headers[$hdr] .= $line; } } else { list($hdr, $val) = split(":[ \t\r]*", $line, 2); $hdr = strtolower($hdr); if (in_array($hdr, $banana->parse_hdr)) { $this->headers[$hdr] = $val; } } } // decode headers foreach ($banana->hdecode as $hdr) { if (isset($this->headers[$hdr])) { $this->headers[$hdr] = headerDecode($this->headers[$hdr]); } } $this->name = $this->headers['from']; $this->name = preg_replace('/<[^ ]*>/', '', $this->name); $this->name = trim($this->name); return true; } function checkcancel() { if (function_exists('hook_checkcancel')) { return hook_checkcancel($this->headers); } if (!isset($_SESSION)) { return false; } return ($this->headers['from'] == $_SESSION['name'] . ' <' . $_SESSION['mail']. '>'); } /** Make some links to browse the current newsgroup */ function _browser() { global $banana; $ret = '
'; $actions = Array('prevThread' => Array('prev_thread', _b_('Discussion précédente')), 'prevPost' => Array('prev', _b_('Article précédent')), 'nextPost' => Array('next', _b_('Article suivant')), 'nextThread' => Array('next_thread', _b_('Discussion suivante'))); foreach ($actions as $method=>$params) { $id = $banana->spool->$method($this->id); if (!is_null($id)) { $ret .= makeImgLink(Array('group' => $banana->state['group'], 'artid' => $id), $params[0] . '.gif', $params[1]); } } return $ret . '
'; } /** convert message to html * @param partid INT id of the multipart message that must be displaid */ function to_html($partid = -1) { global $banana; if (count($this->messages) > 1) { if ($partid != -1) { $this->set_body_to_part($partid); } else { // Select prefered text-format foreach ($banana->body_mime as $mime) { for ($id = 0 ; $id < count($this->messages) ; $id++) { if (preg_match("@$mime@", $this->messages[$id]['headers']['content-type'])) { $partid = $id; $this->set_body_to_part($partid); break; } } if ($partid != -1) { break; } } if ($partid == -1) { $partid = 0; } } } else { $partid = 0; } $res = ''; $res .= '' . ''; if (count($this->messages) > 1) { $res .= ''; } if (isset($this->headers['content-type']) && preg_match("@text/([^;]+);@", $this->headers['content-type'], $format)) { $format = $format[1]; } else { $format = 'plain'; } $res .= ''; if (count($this->pj) > 0) { $res .= ''; $res .= ''; } $ndx = $banana->spool->getndx($this->id); $res .= ''; return $res.'
' . $this->_browser() . '
' . makeImgLink(Array('group' => $banana->state['group'], 'action' => 'new'), 'post.gif', _b_('Nouveau message')) . ' ' . makeImgLink(Array('group' => $banana->state['group'], 'artid' => $this->id, 'action' => 'new'), 'reply.gif', _b_('Répondre')); if ($this->checkCancel()) { $res .= ' ' . makeImgLink(Array('group' => $banana->state['group'], 'artid' => $this->id, 'action' => 'cancel'), 'cancel.gif', _b_('Annuler')); } $res .= '
' . formatDisplayHeader('subject', $this->headers['subject']) . '
'; $xface = null; foreach ($banana->show_hdr as $hdr) { if (isset($this->headers[$hdr])) { $res2 = formatdisplayheader($hdr, $this->headers[$hdr]); if ($res2 && ($hdr != 'x-face' || !$banana->formatxface)) { $res .= '\n"; } else if ($res2) { $xface = $res2; } } } $res .= '
'.header_translate($hdr)."$res2
'; if ($xface) { $res .= $xface; } $res .= '
'; for ($i = 0 ; $i < count($this->messages) ; $i++) { if ($i != 0) { $res .= ' . '; } preg_match("@text/([^;]+);@", $this->messages[$i]['headers']['content-type'], $format); $format = textFormat_translate($format[1]); if ($i != $partid) { $res .= makeHREF(Array('group' => $banana->state['group'], 'artid' => $this->id, 'part' => $i), $format); } else { $res .= $format; } } $res .= '
]*bgcolor="?([#0-9a-f]+)"?[^>]*>@i', $this->body, $bgcolor)) { $res .= ' bgcolor="'.$bgcolor[1].'"'; } $this->body = preg_replace('/cid:([^\'" ]+)/e', "find_attachment('\\1')", $this->body); $res .= '>'.formatbody($this->body, $format); } else { $res .= '>
'.formatbody($this->body).'
'; } $res .= '
'._b_('Pièces jointes').'
'; $i = 0; foreach ($this->pj as $file) { $res .= makeImgLink(Array('group' => $banana->state['group'], 'artid' => $this->id, 'pj' => $i), 'save.gif', _b_('Télécharger')) . ' '; $res .= makeImgLink(Array('group' => $banana->state['group'], 'artid' => $this->id, 'pj' => $i, 'action'=> 'view'), 'preview.gif', _b_('Aperçu')); $res .= ' ' . $file['filename'].' ('.$file['MIME'].')'; $res .= '
'; $i++; } $res .= '
' . $banana->spool->to_html($ndx-$banana->tbefore, $ndx+$banana->tafter, $ndx) . '
'; } } /** Wrapper for Post::find_attachment */ function find_attachment($cid) { global $banana; return $banana->post->find_attachment($cid); } // vim:set et sw=4 sts=4 ts=4 ?>