| 1 | <?php |
| 2 | /******************************************************************************** |
| 3 | * include/posts.inc.php : class for posts |
| 4 | * ----------------------- |
| 5 | * |
| 6 | * This file is part of the banana distribution |
| 7 | * Copyright: See COPYING files that comes with this distribution |
| 8 | ********************************************************************************/ |
| 9 | |
| 10 | /** class for posts |
| 11 | */ |
| 12 | |
| 13 | class BananaPost |
| 14 | { |
| 15 | var $id; |
| 16 | /** headers */ |
| 17 | var $headers; |
| 18 | /** body */ |
| 19 | var $body; |
| 20 | /** attachment */ |
| 21 | var $pj; |
| 22 | /** poster name */ |
| 23 | var $name; |
| 24 | |
| 25 | /** constructor |
| 26 | * @param $_id STRING MSGNUM or MSGID (a group should be selected in this case) |
| 27 | */ |
| 28 | function BananaPost($_id) |
| 29 | { |
| 30 | global $banana; |
| 31 | $this->id = $_id; |
| 32 | $this->pj = array(); |
| 33 | $this->_header(); |
| 34 | |
| 35 | if ($body = $banana->nntp->body($_id)) { |
| 36 | $this->body = join("\n", $body); |
| 37 | } else { |
| 38 | return ($this = null); |
| 39 | } |
| 40 | |
| 41 | if (isset($this->headers['content-transfer-encoding'])) { |
| 42 | if (preg_match("/base64/", $this->headers['content-transfer-encoding'])) { |
| 43 | $this->body = base64_decode($this->body); |
| 44 | } elseif (preg_match("/quoted-printable/", $this->headers['content-transfer-encoding'])) { |
| 45 | $this->body = quoted_printable_decode($this->body); |
| 46 | } |
| 47 | } |
| 48 | |
| 49 | if (preg_match("@multipart/([^;]+);@", $this->headers['content-type'], $mpart_type)) { |
| 50 | preg_match("/boundary=\"?([^ \"]+)\"?/", $this->headers['content-type'], $mpart_boundary); |
| 51 | $this->split_multipart($mpart_type[1], $mpart_boundary[1]); |
| 52 | } |
| 53 | |
| 54 | if (preg_match('!charset=([^;]*)\s*(;|$)!', $this->headers['content-type'], $matches)) { |
| 55 | $this->body = iconv($matches[1], 'utf-8', $this->body); |
| 56 | } else { |
| 57 | $this->body = utf8_encode($this->body); |
| 58 | } |
| 59 | } |
| 60 | |
| 61 | /** split multipart messages |
| 62 | * @param $type STRING multipart type description |
| 63 | * @param $boundary STRING multipart boundary identification string |
| 64 | */ |
| 65 | function split_multipart($type, $boundary) |
| 66 | { |
| 67 | global $banana; |
| 68 | |
| 69 | $parts = preg_split("/\n--$boundary(--|\n)/", $this->body); |
| 70 | foreach ($parts as $part) { |
| 71 | $part = $this->get_part($part); |
| 72 | $local_header = $part['headers']; |
| 73 | $local_body = $part['body']; |
| 74 | if (isset($local_header['content-disposition']) && preg_match("/attachment/", $local_header['content-disposition'])) { |
| 75 | $this->add_attachment($part); |
| 76 | } else if (isset($local_header['content-type']) && preg_match("@text/@", $local_header['content-type'])) { |
| 77 | $this->body = $local_body; |
| 78 | foreach ($banana->parse_hdr as $hdr) { |
| 79 | if (isset($local_header[$hdr])) { |
| 80 | $this->headers[$hdr] = $local_header[$hdr]; |
| 81 | } |
| 82 | } |
| 83 | } |
| 84 | } |
| 85 | } |
| 86 | |
| 87 | /** extract new headers from the part |
| 88 | * @param $part STRING part of a multipart message |
| 89 | */ |
| 90 | function get_part($part) { |
| 91 | global $banana; |
| 92 | |
| 93 | $lines = split("\n", $part); |
| 94 | while (count($lines)) { |
| 95 | $line = array_shift($lines); |
| 96 | if ($line != "") { |
| 97 | list($hdr, $val) = split(":[ \t\r]*", $line, 2); |
| 98 | $hdr = strtolower($hdr); |
| 99 | if (in_array($hdr, $banana->parse_hdr)) { |
| 100 | $local_headers[$hdr] = $val; |
| 101 | } |
| 102 | } else { |
| 103 | break; |
| 104 | } |
| 105 | } |
| 106 | # echo join("\n", $lines)."<br/>------------------------------------<br/>"; |
| 107 | return Array('headers' => $local_headers, 'body' => join("\n", $lines)); |
| 108 | } |
| 109 | |
| 110 | function add_attachment($part) { |
| 111 | $local_header = $part['headers']; |
| 112 | $local_body = $part['body']; |
| 113 | |
| 114 | if (!isset($local_header['content-transfer-encoding'])) { |
| 115 | return; |
| 116 | } |
| 117 | |
| 118 | if (isset($local_header['content-disposition'])) { |
| 119 | if (preg_match("/attachment/", $local_header['content-disposition'])) { |
| 120 | preg_match("/filename=\"?([^\"]+)\"?/", $local_header['content-disposition'], $filename); |
| 121 | $filename = $filename[1]; |
| 122 | } |
| 123 | } |
| 124 | if (!isset($filename)) { |
| 125 | $filename = "attachment".count($pj); |
| 126 | } |
| 127 | |
| 128 | if (isset($local_header['content-type'])) { |
| 129 | if (preg_match("/^\\s*([^ ;]+);/", $local_header['content-type'], $mimetype)) { |
| 130 | $mimetype = $mimetype[1]; |
| 131 | } |
| 132 | } |
| 133 | if (!isset($mimetype)) { |
| 134 | return; |
| 135 | } |
| 136 | |
| 137 | array_push($this->pj, Array('MIME' => $mimetype, |
| 138 | 'filename' => $filename, |
| 139 | 'encoding' => strtolower($local_header['content-transfer-encoding']), |
| 140 | 'data' => $local_body)); |
| 141 | } |
| 142 | |
| 143 | /** decode an attachment |
| 144 | * @param pjid INT id of the attachment to decode |
| 145 | * @param action BOOL action to execute : true=view, false=download |
| 146 | */ |
| 147 | function get_attachment($pjid, $action = false) { |
| 148 | if ($pjid >= count($this->pj)) { |
| 149 | return false; |
| 150 | } else { |
| 151 | $file = $this->pj[$pjid]; |
| 152 | header('Content-Type: '.$file['MIME']); |
| 153 | if (!$action) { |
| 154 | header('Content-Disposition: attachment; filename="'.$file['filename'].'"'); |
| 155 | } |
| 156 | if ($file['encoding'] == 'base64') { |
| 157 | echo base64_decode($file['data']); |
| 158 | } else { |
| 159 | header('Content-Transfer-Encoding: '.$file['encoding']); |
| 160 | echo $file['data']; |
| 161 | } |
| 162 | return true; |
| 163 | } |
| 164 | } |
| 165 | |
| 166 | function _header() |
| 167 | { |
| 168 | global $banana; |
| 169 | $hdrs = $banana->nntp->head($this->id); |
| 170 | if (!$hdrs) { |
| 171 | $this = null; |
| 172 | return false; |
| 173 | } |
| 174 | |
| 175 | // parse headers |
| 176 | foreach ($hdrs as $line) { |
| 177 | if (preg_match("/^[\t\r ]+/", $line)) { |
| 178 | $line = ($hdr=="X-Face"?"":" ").ltrim($line); |
| 179 | if (in_array($hdr, $banana->parse_hdr)) { |
| 180 | $this->headers[$hdr] .= $line; |
| 181 | } |
| 182 | } else { |
| 183 | list($hdr, $val) = split(":[ \t\r]*", $line, 2); |
| 184 | $hdr = strtolower($hdr); |
| 185 | if (in_array($hdr, $banana->parse_hdr)) { |
| 186 | $this->headers[$hdr] = $val; |
| 187 | } |
| 188 | } |
| 189 | } |
| 190 | // decode headers |
| 191 | foreach ($banana->hdecode as $hdr) { |
| 192 | if (isset($this->headers[$hdr])) { |
| 193 | $this->headers[$hdr] = headerDecode($this->headers[$hdr]); |
| 194 | } |
| 195 | } |
| 196 | |
| 197 | $this->name = $this->headers['from']; |
| 198 | $this->name = preg_replace('/<[^ ]*>/', '', $this->name); |
| 199 | $this->name = trim($this->name); |
| 200 | } |
| 201 | |
| 202 | function checkcancel() |
| 203 | { |
| 204 | if (function_exists('hook_checkcancel')) { |
| 205 | return hook_checkcancel($this->headers); |
| 206 | } |
| 207 | return ($this->headers['from'] == $_SESSION['name']." <".$_SESSION['mail'].">"); |
| 208 | } |
| 209 | |
| 210 | function to_html() |
| 211 | { |
| 212 | global $banana; |
| 213 | |
| 214 | $res = '<table class="bicol banana_msg" cellpadding="0" cellspacing="0">'; |
| 215 | $res .= '<tr><th colspan="2">'._b_('En-têtes').'</th></tr>'; |
| 216 | |
| 217 | foreach ($banana->show_hdr as $hdr) { |
| 218 | if (isset($this->headers[$hdr])) { |
| 219 | $res2 = formatdisplayheader($hdr, $this->headers[$hdr]); |
| 220 | if ($res2) { |
| 221 | $res .= '<tr><td class="hdr">'.header_translate($hdr)."</td><td class='val'>$res2</td></tr>\n"; |
| 222 | } |
| 223 | } |
| 224 | } |
| 225 | |
| 226 | $res .= '<tr><th colspan="2">'._b_('Corps').'</th></tr>'; |
| 227 | $res .= '<tr><td colspan="2"><pre>'.formatbody($this->body).'</pre></td></tr>'; |
| 228 | |
| 229 | if (count($this->pj) > 0) { |
| 230 | $res .= '<tr><th colspan="2">'._b_('Pièces jointes').'</th></tr>'; |
| 231 | $res .= '<tr><td colspan="2">'; |
| 232 | $i = 0; |
| 233 | foreach ($this->pj as $file) { |
| 234 | $res .= $file['filename'].' ('.$file['MIME'].') : '; |
| 235 | $res .= '<a href="pj.php?group='.$banana->state['group'].'&artid='.$this->id.'&pj='.$i.'">télécharger</a>'; |
| 236 | if (preg_match("@(image|text)/@", $file['MIME'])) { |
| 237 | $res .= ' . <a href="pj.php?group='.$banana->state['group'].'&artid='.$this->id.'&pj='.$i.'&action=view">aperçu</a>'; |
| 238 | } |
| 239 | $res .= '<br/>'; |
| 240 | $i++; |
| 241 | } |
| 242 | $res .= '</td></tr>'; |
| 243 | } |
| 244 | |
| 245 | $res .= '<tr><th colspan="2">'._b_('apercu').'</th></tr>'; |
| 246 | $ndx = $banana->spool->getndx($this->id); |
| 247 | $res .= '<tr><td class="thrd" colspan="2">'.$banana->spool->to_html($ndx-$banana->tbefore, $ndx+$banana->tafter, $ndx).'</td></tr>'; |
| 248 | |
| 249 | return $res.'</table>'; |
| 250 | } |
| 251 | } |
| 252 | |
| 253 | ?> |