[3754] | 1 | <?php |
---|
| 2 | /*********************************************** |
---|
| 3 | * File : imap.php |
---|
| 4 | * Project : Z-Push |
---|
| 5 | * Descr : This backend is based on |
---|
| 6 | * 'BackendDiff' and implements an |
---|
| 7 | * IMAP interface |
---|
| 8 | * |
---|
| 9 | * Created : 10.10.2007 |
---|
| 10 | * |
---|
| 11 | * ᅵ Zarafa Deutschland GmbH, www.zarafaserver.de |
---|
| 12 | * This file is distributed under GPL v2. |
---|
| 13 | * Consult LICENSE file for details |
---|
| 14 | ************************************************/ |
---|
| 15 | |
---|
| 16 | include_once('diffbackend.php'); |
---|
| 17 | |
---|
| 18 | // The is an improved version of mimeDecode from PEAR that correctly |
---|
| 19 | // handles charsets and charset conversion |
---|
| 20 | include_once('mimeDecode.php'); |
---|
| 21 | require_once('z_RFC822.php'); |
---|
| 22 | |
---|
| 23 | class BackendIMAP extends BackendDiff { |
---|
| 24 | /* Called to logon a user. These are the three authentication strings that you must |
---|
| 25 | * specify in ActiveSync on the PDA. Normally you would do some kind of password |
---|
| 26 | * check here. Alternatively, you could ignore the password here and have Apache |
---|
| 27 | * do authentication via mod_auth_* |
---|
| 28 | */ |
---|
| 29 | function Logon($username, $domain, $password) { |
---|
| 30 | $this->_wasteID = false; |
---|
| 31 | $this->_sentID = false; |
---|
| 32 | $this->_server = "{" . IMAP_SERVER . ":" . IMAP_PORT . "/imap" . IMAP_OPTIONS . "}"; |
---|
| 33 | |
---|
| 34 | if (!function_exists("imap_open")) |
---|
| 35 | debugLog("ERROR BackendIMAP : PHP-IMAP module not installed!!!!!"); |
---|
| 36 | |
---|
| 37 | // open the IMAP-mailbox |
---|
| 38 | $this->_mbox = @imap_open($this->_server , $username, $password, OP_HALFOPEN); |
---|
| 39 | $this->_mboxFolder = ""; |
---|
| 40 | |
---|
| 41 | if ($this->_mbox) { |
---|
| 42 | debugLog("IMAP connection opened sucessfully "); |
---|
| 43 | $this->_username = $username; |
---|
| 44 | $this->_domain = $domain; |
---|
| 45 | // set serverdelimiter |
---|
| 46 | $this->_serverdelimiter = $this->getServerDelimiter(); |
---|
| 47 | return true; |
---|
| 48 | } |
---|
| 49 | else { |
---|
| 50 | debugLog("IMAP can't connect: " . imap_last_error()); |
---|
| 51 | return false; |
---|
| 52 | } |
---|
| 53 | } |
---|
| 54 | |
---|
| 55 | /* Called before shutting down the request to close the IMAP connection |
---|
| 56 | */ |
---|
| 57 | function Logoff() { |
---|
| 58 | if ($this->_mbox) { |
---|
| 59 | // list all errors |
---|
| 60 | $errors = imap_errors(); |
---|
| 61 | if (is_array($errors)) { |
---|
| 62 | foreach ($errors as $e) debugLog("IMAP-errors: $e"); |
---|
| 63 | } |
---|
| 64 | @imap_close($this->_mbox); |
---|
| 65 | debugLog("IMAP connection closed"); |
---|
| 66 | } |
---|
| 67 | } |
---|
| 68 | |
---|
| 69 | /* Called directly after the logon. This specifies the client's protocol version |
---|
| 70 | * and device id. The device ID can be used for various things, including saving |
---|
| 71 | * per-device state information. |
---|
| 72 | * The $user parameter here is normally equal to the $username parameter from the |
---|
| 73 | * Logon() call. In theory though, you could log on a 'foo', and then sync the emails |
---|
| 74 | * of user 'bar'. The $user here is the username specified in the request URL, while the |
---|
| 75 | * $username in the Logon() call is the username which was sent as a part of the HTTP |
---|
| 76 | * authentication. |
---|
| 77 | */ |
---|
| 78 | function Setup($user, $devid, $protocolversion) { |
---|
| 79 | $this->_user = $user; |
---|
| 80 | $this->_devid = $devid; |
---|
| 81 | $this->_protocolversion = $protocolversion; |
---|
| 82 | |
---|
| 83 | return true; |
---|
| 84 | } |
---|
| 85 | |
---|
| 86 | /* Sends a message which is passed as rfc822. You basically can do two things |
---|
| 87 | * 1) Send the message to an SMTP server as-is |
---|
| 88 | * 2) Parse the message yourself, and send it some other way |
---|
| 89 | * It is up to you whether you want to put the message in the sent items folder. If you |
---|
| 90 | * want it in 'sent items', then the next sync on the 'sent items' folder should return |
---|
| 91 | * the new message as any other new message in a folder. |
---|
| 92 | */ |
---|
[4613] | 93 | function SendMail($rfc822, $forward = false, $reply = false, $parent = false) { |
---|
| 94 | debugLog("IMAP-SendMail: for: $forward reply: $reply parent: $parent RFC822: \n". $rfc822 ); |
---|
[3754] | 95 | |
---|
[4613] | 96 | $mobj = new Mail_mimeDecode($rfc822); |
---|
| 97 | $message = $mobj->decode(array('decode_headers' => false, 'decode_bodies' => true, 'include_bodies' => true, 'charset' => 'utf-8')); |
---|
| 98 | |
---|
| 99 | $Mail_RFC822 = new Mail_RFC822(); |
---|
| 100 | $toaddr = $ccaddr = $bccaddr = ""; |
---|
| 101 | if(isset($message->headers["to"])) |
---|
| 102 | $toaddr = $this->parseAddr($Mail_RFC822->parseAddressList($message->headers["to"])); |
---|
| 103 | if(isset($message->headers["cc"])) |
---|
| 104 | $ccaddr = $this->parseAddr($Mail_RFC822->parseAddressList($message->headers["cc"])); |
---|
| 105 | if(isset($message->headers["bcc"])) |
---|
| 106 | $bccaddr = $this->parseAddr($Mail_RFC822->parseAddressList($message->headers["bcc"])); |
---|
[3754] | 107 | |
---|
[4613] | 108 | // save some headers when forwarding mails (content type & transfer-encoding) |
---|
| 109 | $headers = ""; |
---|
| 110 | $forward_h_ct = ""; |
---|
| 111 | $forward_h_cte = ""; |
---|
| 112 | $envelopefrom = ""; |
---|
[3754] | 113 | |
---|
[4613] | 114 | $use_orgbody = false; |
---|
| 115 | |
---|
| 116 | // clean up the transmitted headers |
---|
| 117 | // remove default headers because we are using imap_mail |
---|
| 118 | $changedfrom = false; |
---|
| 119 | $returnPathSet = false; |
---|
| 120 | $body_base64 = false; |
---|
| 121 | $org_charset = ""; |
---|
| 122 | $org_boundary = false; |
---|
| 123 | $multipartmixed = false; |
---|
| 124 | foreach($message->headers as $k => $v) { |
---|
| 125 | if ($k == "subject" || $k == "to" || $k == "cc" || $k == "bcc") |
---|
| 126 | continue; |
---|
[3754] | 127 | |
---|
[4613] | 128 | if ($k == "content-type") { |
---|
| 129 | // if the message is a multipart message, then we should use the sent body |
---|
| 130 | if (preg_match("/multipart/i", $v)) { |
---|
| 131 | $use_orgbody = true; |
---|
| 132 | $org_boundary = $message->ctype_parameters["boundary"]; |
---|
| 133 | } |
---|
[3754] | 134 | |
---|
[4613] | 135 | // save the original content-type header for the body part when forwarding |
---|
| 136 | if ($forward && !$use_orgbody) { |
---|
| 137 | $forward_h_ct = $v; |
---|
| 138 | continue; |
---|
| 139 | } |
---|
[3754] | 140 | |
---|
[4613] | 141 | // set charset always to utf-8 |
---|
| 142 | $org_charset = $v; |
---|
| 143 | $v = preg_replace("/charset=([A-Za-z0-9-\"']+)/", "charset=\"utf-8\"", $v); |
---|
| 144 | } |
---|
[3754] | 145 | |
---|
[4613] | 146 | if ($k == "content-transfer-encoding") { |
---|
| 147 | // if the content was base64 encoded, encode the body again when sending |
---|
| 148 | if (trim($v) == "base64") $body_base64 = true; |
---|
[3754] | 149 | |
---|
[4613] | 150 | // save the original encoding header for the body part when forwarding |
---|
| 151 | if ($forward) { |
---|
| 152 | $forward_h_cte = $v; |
---|
| 153 | continue; |
---|
| 154 | } |
---|
| 155 | } |
---|
[3754] | 156 | |
---|
[4613] | 157 | // check if "from"-header is set, do nothing if it's set |
---|
| 158 | // else set it to IMAP_DEFAULTFROM |
---|
| 159 | if ($k == "from") { |
---|
| 160 | if (trim($v)) { |
---|
| 161 | $changedfrom = true; |
---|
| 162 | } elseif (! trim($v) && IMAP_DEFAULTFROM) { |
---|
| 163 | $changedfrom = true; |
---|
| 164 | if (IMAP_DEFAULTFROM == 'username') $v = $this->_username; |
---|
| 165 | else if (IMAP_DEFAULTFROM == 'domain') $v = $this->_domain; |
---|
| 166 | else $v = $this->_username . IMAP_DEFAULTFROM; |
---|
| 167 | $envelopefrom = "-f$v"; |
---|
| 168 | } |
---|
| 169 | } |
---|
[3754] | 170 | |
---|
[4613] | 171 | // check if "Return-Path"-header is set |
---|
| 172 | if ($k == "return-path") { |
---|
| 173 | $returnPathSet = true; |
---|
| 174 | if (! trim($v) && IMAP_DEFAULTFROM) { |
---|
| 175 | if (IMAP_DEFAULTFROM == 'username') $v = $this->_username; |
---|
| 176 | else if (IMAP_DEFAULTFROM == 'domain') $v = $this->_domain; |
---|
| 177 | else $v = $this->_username . IMAP_DEFAULTFROM; |
---|
| 178 | } |
---|
| 179 | } |
---|
[3754] | 180 | |
---|
[4613] | 181 | // all other headers stay |
---|
| 182 | if ($headers) $headers .= "\n"; |
---|
| 183 | $headers .= ucfirst($k) . ": ". $v; |
---|
| 184 | } |
---|
[3754] | 185 | |
---|
[4613] | 186 | // set "From" header if not set on the device |
---|
| 187 | if(IMAP_DEFAULTFROM && !$changedfrom){ |
---|
| 188 | if (IMAP_DEFAULTFROM == 'username') $v = $this->_username; |
---|
| 189 | else if (IMAP_DEFAULTFROM == 'domain') $v = $this->_domain; |
---|
| 190 | else $v = $this->_username . IMAP_DEFAULTFROM; |
---|
| 191 | if ($headers) $headers .= "\n"; |
---|
| 192 | $headers .= 'From: '.$v; |
---|
| 193 | $envelopefrom = "-f$v"; |
---|
| 194 | } |
---|
[3754] | 195 | |
---|
[4613] | 196 | // set "Return-Path" header if not set on the device |
---|
| 197 | if(IMAP_DEFAULTFROM && !$returnPathSet){ |
---|
| 198 | if (IMAP_DEFAULTFROM == 'username') $v = $this->_username; |
---|
| 199 | else if (IMAP_DEFAULTFROM == 'domain') $v = $this->_domain; |
---|
| 200 | else $v = $this->_username . IMAP_DEFAULTFROM; |
---|
| 201 | if ($headers) $headers .= "\n"; |
---|
| 202 | $headers .= 'Return-Path: '.$v; |
---|
| 203 | } |
---|
[3754] | 204 | |
---|
[4613] | 205 | // if this is a multipart message with a boundary, we must use the original body |
---|
| 206 | if ($use_orgbody) { |
---|
[4617] | 207 | list(,$body) = $mobj->_splitBodyHeader($rfc822); |
---|
| 208 | $repl_body = $this->getBody($message); |
---|
| 209 | if ($message->parts[0]->headers["content-transfer-encoding"] == "base64") $multipart_text_cte_base64 = true; |
---|
| 210 | else $multipart_text_cte_base64 = false; |
---|
[4613] | 211 | } |
---|
| 212 | else |
---|
| 213 | $body = $this->getBody($message); |
---|
[3754] | 214 | |
---|
[4613] | 215 | // reply |
---|
| 216 | if ($reply && $parent) { |
---|
| 217 | $this->imap_reopenFolder($parent); |
---|
| 218 | // receive entire mail (header + body) to decode body correctly |
---|
| 219 | $origmail = @imap_fetchheader($this->_mbox, $reply, FT_UID) . @imap_body($this->_mbox, $reply, FT_PEEK | FT_UID); |
---|
[4617] | 220 | |
---|
[4613] | 221 | $mobj2 = new Mail_mimeDecode($origmail); |
---|
| 222 | // receive only body |
---|
| 223 | $body .= $this->getBody($mobj2->decode(array('decode_headers' => false, 'decode_bodies' => true, 'include_bodies' => true, 'charset' => 'utf-8'))); |
---|
| 224 | // unset mimedecoder & origmail - free memory |
---|
| 225 | unset($mobj2); |
---|
| 226 | unset($origmail); |
---|
| 227 | } |
---|
[3754] | 228 | |
---|
[4613] | 229 | // encode the body to base64 if it was sent originally in base64 by the pda |
---|
| 230 | // contrib - chunk base64 encoded body |
---|
| 231 | if ($body_base64 && !$forward) $body = chunk_split(base64_encode($body)); |
---|
[3754] | 232 | |
---|
| 233 | |
---|
[4613] | 234 | // forward |
---|
| 235 | if ($forward && $parent) { |
---|
| 236 | $this->imap_reopenFolder($parent); |
---|
| 237 | // receive entire mail (header + body) |
---|
| 238 | $origmail = @imap_fetchheader($this->_mbox, $forward, FT_UID) . @imap_body($this->_mbox, $forward, FT_PEEK | FT_UID); |
---|
[3754] | 239 | |
---|
[4613] | 240 | //if (!defined('IMAP_INLINE_FORWARD') || IMAP_INLINE_FORWARD === false) { |
---|
| 241 | if (defined('IMAP_INLINE_FORWARD') && IMAP_INLINE_FORWARD === false) { |
---|
| 242 | // contrib - chunk base64 encoded body |
---|
| 243 | if ($body_base64) $body = chunk_split(base64_encode($body)); |
---|
| 244 | //use original boundary if it's set |
---|
| 245 | $boundary = ($org_boundary) ? $org_boundary : false; |
---|
| 246 | // build a new mime message, forward entire old mail as file |
---|
| 247 | list($aheader, $body) = $this->mail_attach("forwarded_message.eml",strlen($origmail),$origmail, $body, $forward_h_ct, $forward_h_cte,$boundary); |
---|
| 248 | // add boundary headers |
---|
| 249 | $headers .= "\n" . $aheader; |
---|
[3754] | 250 | |
---|
[4613] | 251 | } |
---|
| 252 | else { |
---|
| 253 | $mobj2 = new Mail_mimeDecode($origmail); |
---|
| 254 | $mess2 = $mobj2->decode(array('decode_headers' => true, 'decode_bodies' => true, 'include_bodies' => true, 'charset' => 'utf-8')); |
---|
[3754] | 255 | |
---|
[4613] | 256 | if (!$use_orgbody) |
---|
| 257 | $nbody = $body; |
---|
| 258 | else |
---|
| 259 | $nbody = $repl_body; |
---|
[3754] | 260 | |
---|
[4613] | 261 | $nbody .= "\r\n\r\n"; |
---|
| 262 | $nbody .= "-----Original Message-----\r\n"; |
---|
| 263 | if(isset($mess2->headers['from'])) |
---|
| 264 | $nbody .= "From: " . $mess2->headers['from'] . "\r\n"; |
---|
| 265 | if(isset($mess2->headers['to']) && strlen($mess2->headers['to']) > 0) |
---|
| 266 | $nbody .= "To: " . $mess2->headers['to'] . "\r\n"; |
---|
| 267 | if(isset($mess2->headers['cc']) && strlen($mess2->headers['cc']) > 0) |
---|
| 268 | $nbody .= "Cc: " . $mess2->headers['cc'] . "\r\n"; |
---|
| 269 | if(isset($mess2->headers['date'])) |
---|
| 270 | $nbody .= "Sent: " . $mess2->headers['date'] . "\r\n"; |
---|
| 271 | if(isset($mess2->headers['subject'])) |
---|
| 272 | $nbody .= "Subject: " . $mess2->headers['subject'] . "\r\n"; |
---|
| 273 | $nbody .= "\r\n"; |
---|
| 274 | $nbody .= $this->getBody($mess2); |
---|
[3754] | 275 | |
---|
[4613] | 276 | if ($body_base64) { |
---|
| 277 | // contrib - chunk base64 encoded body |
---|
| 278 | $nbody = chunk_split(base64_encode($nbody)); |
---|
| 279 | if ($use_orgbody) |
---|
| 280 | // contrib - chunk base64 encoded body |
---|
| 281 | $repl_body = chunk_split(base64_encode($repl_body)); |
---|
| 282 | } |
---|
[3754] | 283 | |
---|
[4613] | 284 | if ($use_orgbody) { |
---|
| 285 | debugLog("-------------------"); |
---|
| 286 | debugLog("old:\n'$repl_body'\nnew:\n'$nbody'\nund der body:\n'$body'"); |
---|
| 287 | //$body is quoted-printable encoded while $repl_body and $nbody are plain text, |
---|
| 288 | //so we need to decode $body in order replace to take place |
---|
[4617] | 289 | if (!$multipart_text_cte_base64) { |
---|
| 290 | $body = str_replace($repl_body, $nbody, quoted_printable_decode($body)); |
---|
| 291 | } else { |
---|
| 292 | $body = str_replace(base64_encode($repl_body), base64_encode($nbody), $body); } |
---|
[4613] | 293 | } |
---|
| 294 | else |
---|
| 295 | $body = $nbody; |
---|
[3754] | 296 | |
---|
| 297 | |
---|
[4613] | 298 | if(isset($mess2->parts)) { |
---|
| 299 | $attached = false; |
---|
[3754] | 300 | |
---|
[4613] | 301 | if ($org_boundary) { |
---|
| 302 | $att_boundary = $org_boundary; |
---|
| 303 | // cut end boundary from body |
---|
| 304 | $body = substr($body, 0, strrpos($body, "--$att_boundary--")); |
---|
| 305 | } |
---|
| 306 | else { |
---|
| 307 | $att_boundary = strtoupper(md5(uniqid(time()))); |
---|
| 308 | // add boundary headers |
---|
| 309 | $headers .= "\n" . "Content-Type: multipart/mixed; boundary=$att_boundary"; |
---|
| 310 | $multipartmixed = true; |
---|
| 311 | } |
---|
[3754] | 312 | |
---|
[4613] | 313 | foreach($mess2->parts as $part) { |
---|
| 314 | if(isset($part->disposition) && ($part->disposition == "attachment" || $part->disposition == "inline")) { |
---|
[3754] | 315 | |
---|
[4613] | 316 | if(isset($part->d_parameters['filename'])) |
---|
| 317 | $attname = $part->d_parameters['filename']; |
---|
| 318 | else if(isset($part->ctype_parameters['name'])) |
---|
| 319 | $attname = $part->ctype_parameters['name']; |
---|
| 320 | else if(isset($part->headers['content-description'])) |
---|
| 321 | $attname = $part->headers['content-description']; |
---|
| 322 | else $attname = "unknown attachment"; |
---|
[3754] | 323 | |
---|
[4613] | 324 | // ignore html content |
---|
| 325 | if ($part->ctype_primary == "text" && $part->ctype_secondary == "html") { |
---|
| 326 | continue; |
---|
| 327 | } |
---|
| 328 | // |
---|
| 329 | if ($use_orgbody || $attached) { |
---|
| 330 | $body .= $this->enc_attach_file($att_boundary, $attname, strlen($part->body),$part->body, $part->ctype_primary ."/". $part->ctype_secondary); |
---|
| 331 | } |
---|
| 332 | // first attachment |
---|
| 333 | else { |
---|
| 334 | $encmail = $body; |
---|
| 335 | $attached = true; |
---|
| 336 | $body = $this->enc_multipart($att_boundary, $body, $forward_h_ct, $forward_h_cte); |
---|
| 337 | $body .= $this->enc_attach_file($att_boundary, $attname, strlen($part->body),$part->body, $part->ctype_primary ."/". $part->ctype_secondary); |
---|
| 338 | } |
---|
| 339 | } |
---|
| 340 | } |
---|
| 341 | if ($multipartmixed) { |
---|
| 342 | //this happens if a multipart/alternative message is forwarded |
---|
| 343 | //then it's a multipart/mixed message which consists of: |
---|
| 344 | //1. text/plain part which was written on the mobile |
---|
| 345 | //2. multipart/alternative part which is the original message |
---|
| 346 | //$body = "This is a message with multiple parts in MIME format.\n--". |
---|
| 347 | // $att_boundary. |
---|
| 348 | // "\nContent-Type: $forward_h_ct\nContent-Transfer-Encoding: $forward_h_cte\n\n". |
---|
| 349 | // (($body_base64) ? chunk_split(base64_encode($message->body)) : rtrim($message->body)). |
---|
| 350 | // "\n--".$att_boundary. |
---|
| 351 | // "\nContent-Type: {$mess2->headers['content-type']}\n\n". |
---|
| 352 | // @imap_body($this->_mbox, $forward, FT_PEEK | FT_UID)."\n\n"; |
---|
| 353 | $body = "\n--". |
---|
| 354 | $att_boundary. |
---|
| 355 | "\nContent-Type: $forward_h_ct\nContent-Transfer-Encoding: $forward_h_cte\n\n". |
---|
| 356 | $body; |
---|
| 357 | } |
---|
| 358 | |
---|
| 359 | $body .= "--$att_boundary--\n\n"; |
---|
| 360 | } |
---|
| 361 | |
---|
| 362 | unset($mobj2); |
---|
| 363 | } |
---|
| 364 | |
---|
| 365 | // unset origmail - free memory |
---|
| 366 | unset($origmail); |
---|
| 367 | |
---|
| 368 | } |
---|
| 369 | |
---|
| 370 | // remove carriage-returns from body |
---|
| 371 | $body = str_replace("\r\n", "\n", $body); |
---|
| 372 | |
---|
| 373 | if (!$multipartmixed) { |
---|
| 374 | if (!empty($forward_h_ct)) $headers .= "\nContent-Type: $forward_h_ct"; |
---|
| 375 | if (!empty($forward_h_cte)) $headers .= "\nContent-Transfer-Encoding: $forward_h_cte"; |
---|
| 376 | } |
---|
| 377 | //advanced debugging |
---|
| 378 | debugLog("IMAP-SendMail: parsed message: ". print_r($message,1)); |
---|
| 379 | debugLog("IMAP-SendMail: headers: $headers"); |
---|
| 380 | debugLog("IMAP-SendMail: subject: {$message->headers["subject"]}"); |
---|
| 381 | debugLog("IMAP-SendMail: body: $body"); |
---|
| 382 | |
---|
| 383 | if (!defined('IMAP_USE_IMAPMAIL') || IMAP_USE_IMAPMAIL == true) { |
---|
| 384 | $send = @imap_mail ( $toaddr, $message->headers["subject"], $body, $headers, $ccaddr, $bccaddr); |
---|
| 385 | } |
---|
| 386 | else { |
---|
| 387 | if (!empty($ccaddr)) $headers .= "\nCc: $ccaddr"; |
---|
| 388 | if (!empty($bccaddr)) $headers .= "\nBcc: $bccaddr"; |
---|
| 389 | $send = @mail ( $toaddr, $message->headers["subject"], $body, $headers, $envelopefrom ); |
---|
| 390 | } |
---|
| 391 | |
---|
| 392 | // email sent? |
---|
| 393 | if (!$send) { |
---|
| 394 | debugLog("The email could not be sent. Last-IMAP-error: ". imap_last_error()); |
---|
| 395 | } |
---|
| 396 | |
---|
| 397 | // add message to the sent folder |
---|
| 398 | // build complete headers |
---|
| 399 | $headers .= "\nTo: $toaddr"; |
---|
| 400 | $headers .= "\nSubject: " . $message->headers["subject"]; |
---|
| 401 | |
---|
| 402 | if (!defined('IMAP_USE_IMAPMAIL') || IMAP_USE_IMAPMAIL == true) { |
---|
| 403 | if (!empty($ccaddr)) $headers .= "\nCc: $ccaddr"; |
---|
| 404 | if (!empty($bccaddr)) $headers .= "\nBcc: $bccaddr"; |
---|
| 405 | } |
---|
| 406 | debugLog("IMAP-SendMail: complete headers: $headers"); |
---|
| 407 | |
---|
| 408 | $asf = false; |
---|
| 409 | if ($this->_sentID) { |
---|
| 410 | $asf = $this->addSentMessage($this->_sentID, $headers, $body); |
---|
| 411 | } |
---|
| 412 | else if (IMAP_SENTFOLDER) { |
---|
| 413 | $asf = $this->addSentMessage(IMAP_SENTFOLDER, $headers, $body); |
---|
| 414 | debugLog("IMAP-SendMail: Outgoing mail saved in configured 'Sent' folder '".IMAP_SENTFOLDER."': ". (($asf)?"success":"failed")); |
---|
| 415 | } |
---|
| 416 | // No Sent folder set, try defaults |
---|
| 417 | else { |
---|
| 418 | debugLog("IMAP-SendMail: No Sent mailbox set"); |
---|
| 419 | if($this->addSentMessage("INBOX.Sent", $headers, $body)) { |
---|
| 420 | debugLog("IMAP-SendMail: Outgoing mail saved in 'INBOX.Sent'"); |
---|
| 421 | $asf = true; |
---|
| 422 | } |
---|
| 423 | else if ($this->addSentMessage("Sent", $headers, $body)) { |
---|
| 424 | debugLog("IMAP-SendMail: Outgoing mail saved in 'Sent'"); |
---|
| 425 | $asf = true; |
---|
| 426 | } |
---|
| 427 | else if ($this->addSentMessage("Sent Items", $headers, $body)) { |
---|
| 428 | debugLog("IMAP-SendMail: Outgoing mail saved in 'Sent Items'"); |
---|
| 429 | $asf = true; |
---|
| 430 | } |
---|
| 431 | } |
---|
| 432 | |
---|
| 433 | // unset mimedecoder - free memory |
---|
| 434 | unset($mobj); |
---|
| 435 | return ($send && $asf); |
---|
| 436 | } |
---|
| 437 | |
---|
| 438 | |
---|
[3754] | 439 | /* Should return a wastebasket folder if there is one. This is used when deleting |
---|
| 440 | * items; if this function returns a valid folder ID, then all deletes are handled |
---|
| 441 | * as moves and are sent to your backend as a move. If it returns FALSE, then deletes |
---|
| 442 | * are always handled as real deletes and will be sent to your importer as a DELETE |
---|
| 443 | */ |
---|
| 444 | function GetWasteBasket() { |
---|
| 445 | return $this->_wasteID; |
---|
| 446 | } |
---|
| 447 | |
---|
| 448 | /* Should return a list (array) of messages, each entry being an associative array |
---|
| 449 | * with the same entries as StatMessage(). This function should return stable information; ie |
---|
| 450 | * if nothing has changed, the items in the array must be exactly the same. The order of |
---|
| 451 | * the items within the array is not important though. |
---|
| 452 | * |
---|
| 453 | * The cutoffdate is a date in the past, representing the date since which items should be shown. |
---|
| 454 | * This cutoffdate is determined by the user's setting of getting 'Last 3 days' of e-mail, etc. If |
---|
| 455 | * you ignore the cutoffdate, the user will not be able to select their own cutoffdate, but all |
---|
| 456 | * will work OK apart from that. |
---|
| 457 | */ |
---|
| 458 | |
---|
| 459 | function GetMessageList($folderid, $cutoffdate) { |
---|
| 460 | debugLog("IMAP-GetMessageList: (fid: '$folderid' cutdate: '$cutoffdate' )"); |
---|
| 461 | |
---|
| 462 | $messages = array(); |
---|
| 463 | $this->imap_reopenFolder($folderid, true); |
---|
| 464 | |
---|
| 465 | $sequence = "1:*"; |
---|
| 466 | if ($cutoffdate > 0) { |
---|
| 467 | $search = @imap_search($this->_mbox, "SINCE ". date("d-M-Y", $cutoffdate)); |
---|
| 468 | if ($search !== false) |
---|
| 469 | $sequence = implode(",", $search); |
---|
[4219] | 470 | if (TRACE_UID !== false and (TRACE_TYPE == 'IMAP' or TRACE_TYPE == 'ALL')) traceLog('IMAP::GetMessageList-> A função @imap_search leu as seguintes SEQUÊNCIAS do servidor IMAP com base no filtro de data: '.$sequence); |
---|
[3754] | 471 | } |
---|
[4219] | 472 | |
---|
[3754] | 473 | $overviews = @imap_fetch_overview($this->_mbox, $sequence); |
---|
| 474 | |
---|
| 475 | if (!$overviews) { |
---|
| 476 | debugLog("IMAP-GetMessageList: Failed to retrieve overview"); |
---|
| 477 | } else { |
---|
| 478 | foreach($overviews as $overview) { |
---|
| 479 | $date = ""; |
---|
| 480 | $vars = get_object_vars($overview); |
---|
[4219] | 481 | if (TRACE_UID !== false and (TRACE_TYPE == 'IMAP' or TRACE_TYPE == 'ALL')) traceLog('IMAP::GetMessageList-> A função @imap_fetch_overview leu mais detalhes da mensagem: '. print_r($overview,1)); |
---|
[3754] | 482 | if (array_key_exists( "date", $vars)) { |
---|
| 483 | // message is out of range for cutoffdate, ignore it |
---|
[4219] | 484 | if(strtotime(preg_replace("/\(.*\)/", "", $overview->date)) < $cutoffdate) { // emerson-faria.nobre@serpro.gov.br - 07/feb/2011 |
---|
| 485 | if (TRACE_UID !== false and (TRACE_TYPE == 'IMAP' or TRACE_TYPE == 'ALL')) traceLog('IMAP::GetMessageList-> O overview->date: '.$overview->date.' (overview->date_timestamp: '.strtotime(preg_replace("/\(.*\)/", "", $overview->date)).') é menor que o $cutoffdate: '.date("d/m/Y G:i:s",$cutoffdate).' ($cutoffdate_timestamp: '.$cutoffdate.') ou a função "strtotime" gerou um ERRO porque não conseguiu retornar um valor para o overview->date_timestamp. A mensagem será descartada da sincronização.'); |
---|
| 486 | continue; |
---|
| 487 | } |
---|
[3754] | 488 | //if(strtotime($overview->date) < $cutoffdate) continue; |
---|
| 489 | $date = $overview->date; |
---|
[4219] | 490 | } else if (TRACE_UID !== false and (TRACE_TYPE == 'IMAP' or TRACE_TYPE == 'ALL')) traceLog('IMAP::GetMessageList-> ERRO: O campo date não existe no overview da mensagem.'); |
---|
[3754] | 491 | |
---|
| 492 | // cut of deleted messages |
---|
[4219] | 493 | if (array_key_exists( "deleted", $vars) && $overview->deleted) { |
---|
| 494 | if (TRACE_UID !== false and (TRACE_TYPE == 'IMAP' or TRACE_TYPE == 'ALL')) traceLog('IMAP::GetMessageList-> A mensagem está com o flag deleted ativo e será descartada da sincronização'); |
---|
| 495 | continue; |
---|
| 496 | } |
---|
[3754] | 497 | |
---|
| 498 | if (array_key_exists( "uid", $vars)) { |
---|
| 499 | $message = array(); |
---|
| 500 | $message["mod"] = $date; |
---|
| 501 | $message["id"] = $overview->uid; |
---|
| 502 | // 'seen' aka 'read' is the only flag we want to know about |
---|
| 503 | $message["flags"] = 0; |
---|
| 504 | |
---|
| 505 | if(array_key_exists( "seen", $vars) && $overview->seen) |
---|
| 506 | $message["flags"] = 1; |
---|
| 507 | array_push($messages, $message); |
---|
[4219] | 508 | } else if (TRACE_UID !== false and (TRACE_TYPE == 'IMAP' or TRACE_TYPE == 'ALL')) traceLog('IMAP::GetMessageList-> ERRO: O campo uid não existe no overview da mensagem.'); |
---|
[3754] | 509 | } |
---|
| 510 | } |
---|
| 511 | return $messages; |
---|
| 512 | } |
---|
| 513 | |
---|
| 514 | /* This function is analogous to GetMessageList. |
---|
| 515 | * |
---|
| 516 | */ |
---|
| 517 | function GetFolderList() { |
---|
| 518 | $folders = array(); |
---|
| 519 | |
---|
| 520 | $list = @imap_getmailboxes($this->_mbox, $this->_server, "*"); |
---|
| 521 | if (is_array($list)) { |
---|
| 522 | // reverse list to obtain folders in right order |
---|
| 523 | $list = array_reverse($list); |
---|
| 524 | foreach ($list as $val) { |
---|
[4219] | 525 | if (TRACE_UID !== false and (TRACE_TYPE == 'IMAP' or TRACE_TYPE == 'ALL')) traceLog('IMAP::GetFolderList-> Pasta lida usando a função @imap_getmailboxes: '.print_r($val,1)); |
---|
[3754] | 526 | $box = array(); |
---|
| 527 | |
---|
| 528 | // cut off serverstring |
---|
| 529 | $box["id"] = imap_utf7_decode(substr($val->name, strlen($this->_server))); |
---|
| 530 | |
---|
| 531 | // always use "." as folder delimiter |
---|
| 532 | $box["id"] = imap_utf7_encode(str_replace($val->delimiter, ".", $box["id"])); |
---|
| 533 | |
---|
| 534 | // explode hierarchies |
---|
| 535 | $fhir = explode(".", $box["id"]); |
---|
| 536 | if (count($fhir) > 1) { |
---|
| 537 | $box["mod"] = imap_utf7_encode(array_pop($fhir)); // mod is last part of path |
---|
| 538 | $box["parent"] = imap_utf7_encode(implode(".", $fhir)); // parent is all previous parts of path |
---|
| 539 | } |
---|
| 540 | else { |
---|
| 541 | $box["mod"] = imap_utf7_encode($box["id"]); |
---|
| 542 | $box["parent"] = "0"; |
---|
| 543 | } |
---|
[4219] | 544 | if (TRACE_UID !== false and (TRACE_TYPE == 'IMAP' or TRACE_TYPE == 'ALL')) traceLog('IMAP::GetFolderList-> Parâmetros da pasta após decodificação: '.print_r($box,1)); |
---|
[3754] | 545 | $folders[]=$box; |
---|
| 546 | } |
---|
| 547 | } |
---|
| 548 | else { |
---|
| 549 | debugLog("GetFolderList: imap_list failed: " . imap_last_error()); |
---|
| 550 | } |
---|
| 551 | |
---|
| 552 | return $folders; |
---|
| 553 | } |
---|
| 554 | |
---|
| 555 | /* GetFolder should return an actual SyncFolder object with all the properties set. Folders |
---|
| 556 | * are pretty simple really, having only a type, a name, a parent and a server ID. |
---|
| 557 | */ |
---|
| 558 | |
---|
| 559 | function GetFolder($id) { |
---|
| 560 | $folder = new SyncFolder(); |
---|
| 561 | $folder->serverid = $id; |
---|
| 562 | |
---|
| 563 | // explode hierarchy |
---|
| 564 | $fhir = explode(".", $id); |
---|
| 565 | |
---|
| 566 | // compare on lowercase strings |
---|
| 567 | $lid = strtolower($id); |
---|
| 568 | |
---|
| 569 | if($lid == "inbox") { |
---|
| 570 | $folder->parentid = "0"; // Root |
---|
| 571 | $folder->displayname = "Inbox"; |
---|
| 572 | $folder->type = SYNC_FOLDER_TYPE_INBOX; |
---|
| 573 | } |
---|
| 574 | // Zarafa IMAP-Gateway outputs |
---|
| 575 | else if($lid == "drafts") { |
---|
| 576 | $folder->parentid = "0"; |
---|
| 577 | $folder->displayname = "Drafts"; |
---|
| 578 | $folder->type = SYNC_FOLDER_TYPE_DRAFTS; |
---|
| 579 | } |
---|
| 580 | else if($lid == "trash") { |
---|
| 581 | $folder->parentid = "0"; |
---|
| 582 | $folder->displayname = "Trash"; |
---|
| 583 | $folder->type = SYNC_FOLDER_TYPE_WASTEBASKET; |
---|
| 584 | $this->_wasteID = $id; |
---|
| 585 | } |
---|
| 586 | else if($lid == "sent" || $lid == "sent items" || $lid == IMAP_SENTFOLDER) { |
---|
| 587 | $folder->parentid = "0"; |
---|
| 588 | $folder->displayname = "Sent"; |
---|
| 589 | $folder->type = SYNC_FOLDER_TYPE_SENTMAIL; |
---|
| 590 | $this->_sentID = $id; |
---|
| 591 | } |
---|
| 592 | // courier-imap outputs |
---|
| 593 | else if($lid == "inbox.drafts") { |
---|
| 594 | $folder->parentid = $fhir[0]; |
---|
| 595 | $folder->displayname = "Drafts"; |
---|
| 596 | $folder->type = SYNC_FOLDER_TYPE_DRAFTS; |
---|
| 597 | } |
---|
| 598 | else if($lid == "inbox.trash") { |
---|
| 599 | $folder->parentid = $fhir[0]; |
---|
| 600 | $folder->displayname = "Trash"; |
---|
| 601 | $folder->type = SYNC_FOLDER_TYPE_WASTEBASKET; |
---|
| 602 | $this->_wasteID = $id; |
---|
| 603 | } |
---|
| 604 | else if($lid == "inbox.sent") { |
---|
| 605 | $folder->parentid = $fhir[0]; |
---|
| 606 | $folder->displayname = "Sent"; |
---|
| 607 | $folder->type = SYNC_FOLDER_TYPE_SENTMAIL; |
---|
| 608 | $this->_sentID = $id; |
---|
| 609 | } |
---|
| 610 | |
---|
| 611 | // define the rest as other-folders |
---|
| 612 | else { |
---|
| 613 | if (count($fhir) > 1) { |
---|
| 614 | $folder->displayname = windows1252_to_utf8(imap_utf7_decode(array_pop($fhir))); |
---|
| 615 | $folder->parentid = implode(".", $fhir); |
---|
| 616 | } |
---|
| 617 | else { |
---|
| 618 | $folder->displayname = windows1252_to_utf8(imap_utf7_decode($id)); |
---|
| 619 | $folder->parentid = "0"; |
---|
| 620 | } |
---|
| 621 | $folder->type = SYNC_FOLDER_TYPE_OTHER; |
---|
| 622 | } |
---|
| 623 | |
---|
| 624 | //advanced debugging |
---|
| 625 | //debugLog("IMAP-GetFolder(id: '$id') -> " . print_r($folder, 1)); |
---|
[4219] | 626 | if (TRACE_UID !== false and (TRACE_TYPE == 'IMAP' or TRACE_TYPE == 'ALL')) traceLog('IMAP::GetFolder(id: '.$id.'): '.print_r($folder,1)); |
---|
[3754] | 627 | |
---|
| 628 | return $folder; |
---|
| 629 | } |
---|
| 630 | |
---|
| 631 | /* Return folder stats. This means you must return an associative array with the |
---|
| 632 | * following properties: |
---|
| 633 | * "id" => The server ID that will be used to identify the folder. It must be unique, and not too long |
---|
| 634 | * How long exactly is not known, but try keeping it under 20 chars or so. It must be a string. |
---|
| 635 | * "parent" => The server ID of the parent of the folder. Same restrictions as 'id' apply. |
---|
| 636 | * "mod" => This is the modification signature. It is any arbitrary string which is constant as long as |
---|
| 637 | * the folder has not changed. In practice this means that 'mod' can be equal to the folder name |
---|
| 638 | * as this is the only thing that ever changes in folders. (the type is normally constant) |
---|
| 639 | */ |
---|
| 640 | function StatFolder($id) { |
---|
| 641 | $folder = $this->GetFolder($id); |
---|
| 642 | |
---|
| 643 | $stat = array(); |
---|
| 644 | $stat["id"] = $id; |
---|
| 645 | $stat["parent"] = $folder->parentid; |
---|
| 646 | $stat["mod"] = $folder->displayname; |
---|
[4219] | 647 | if (TRACE_UID !== false and (TRACE_TYPE == 'IMAP' or TRACE_TYPE == 'ALL')) traceLog('IMAP::StatFolder(id: '.$id.'): '.print_r($stat,1)); |
---|
[3754] | 648 | return $stat; |
---|
| 649 | } |
---|
| 650 | |
---|
| 651 | /* Creates or modifies a folder |
---|
| 652 | * "folderid" => id of the parent folder |
---|
| 653 | * "oldid" => if empty -> new folder created, else folder is to be renamed |
---|
| 654 | * "displayname" => new folder name (to be created, or to be renamed to) |
---|
| 655 | * "type" => folder type, ignored in IMAP |
---|
| 656 | * |
---|
| 657 | */ |
---|
| 658 | function ChangeFolder($folderid, $oldid, $displayname, $type){ |
---|
| 659 | debugLog("ChangeFolder: (parent: '$folderid' oldid: '$oldid' displayname: '$displayname' type: '$type')"); |
---|
| 660 | |
---|
| 661 | // go to parent mailbox |
---|
| 662 | $this->imap_reopenFolder($folderid); |
---|
| 663 | |
---|
| 664 | // build name for new mailbox |
---|
| 665 | $newname = $this->_server . str_replace(".", $this->_serverdelimiter, $folderid) . $this->_serverdelimiter . $displayname; |
---|
| 666 | |
---|
| 667 | $csts = false; |
---|
| 668 | // if $id is set => rename mailbox, otherwise create |
---|
| 669 | if ($oldid) { |
---|
| 670 | // rename doesn't work properly with IMAP |
---|
| 671 | // the activesync client doesn't support a 'changing ID' |
---|
| 672 | //$csts = imap_renamemailbox($this->_mbox, $this->_server . imap_utf7_encode(str_replace(".", $this->_serverdelimiter, $oldid)), $newname); |
---|
| 673 | } |
---|
| 674 | else { |
---|
| 675 | $csts = @imap_createmailbox($this->_mbox, $newname); |
---|
| 676 | } |
---|
| 677 | if ($csts) { |
---|
| 678 | return $this->StatFolder($folderid . "." . $displayname); |
---|
| 679 | } |
---|
| 680 | else |
---|
| 681 | return false; |
---|
| 682 | } |
---|
| 683 | |
---|
| 684 | /* Should return attachment data for the specified attachment. The passed attachment identifier is |
---|
| 685 | * the exact string that is returned in the 'AttName' property of an SyncAttachment. So, you should |
---|
| 686 | * encode any information you need to find the attachment in that 'attname' property. |
---|
| 687 | */ |
---|
| 688 | function GetAttachmentData($attname) { |
---|
| 689 | debugLog("getAttachmentDate: (attname: '$attname')"); |
---|
| 690 | |
---|
| 691 | list($folderid, $id, $part) = explode(":", $attname); |
---|
| 692 | |
---|
| 693 | $this->imap_reopenFolder($folderid); |
---|
| 694 | $mail = @imap_fetchheader($this->_mbox, $id, FT_PREFETCHTEXT | FT_UID) . @imap_body($this->_mbox, $id, FT_PEEK | FT_UID); |
---|
| 695 | |
---|
| 696 | $mobj = new Mail_mimeDecode($mail); |
---|
| 697 | $message = $mobj->decode(array('decode_headers' => true, 'decode_bodies' => true, 'include_bodies' => true, 'input' => $mail, 'crlf' => "\n", 'charset' => 'utf-8')); |
---|
| 698 | |
---|
| 699 | if (isset($message->parts[$part]->body)) |
---|
| 700 | print $message->parts[$part]->body; |
---|
[4219] | 701 | |
---|
[3754] | 702 | // unset mimedecoder & mail |
---|
| 703 | unset($mobj); |
---|
| 704 | unset($mail); |
---|
| 705 | return true; |
---|
| 706 | } |
---|
| 707 | |
---|
| 708 | /* StatMessage should return message stats, analogous to the folder stats (StatFolder). Entries are: |
---|
| 709 | * 'id' => Server unique identifier for the message. Again, try to keep this short (under 20 chars) |
---|
| 710 | * 'flags' => simply '0' for unread, '1' for read |
---|
| 711 | * 'mod' => modification signature. As soon as this signature changes, the item is assumed to be completely |
---|
| 712 | * changed, and will be sent to the PDA as a whole. Normally you can use something like the modification |
---|
| 713 | * time for this field, which will change as soon as the contents have changed. |
---|
| 714 | */ |
---|
| 715 | |
---|
| 716 | function StatMessage($folderid, $id) { |
---|
| 717 | debugLog("IMAP-StatMessage: (fid: '$folderid' id: '$id' )"); |
---|
| 718 | |
---|
| 719 | $this->imap_reopenFolder($folderid); |
---|
| 720 | $overview = @imap_fetch_overview( $this->_mbox , $id , FT_UID); |
---|
| 721 | |
---|
| 722 | if (!$overview) { |
---|
| 723 | debugLog("IMAP-StatMessage: Failed to retrieve overview: ". imap_last_error()); |
---|
| 724 | return false; |
---|
| 725 | } |
---|
| 726 | |
---|
| 727 | else { |
---|
| 728 | // check if variables for this overview object are available |
---|
| 729 | $vars = get_object_vars($overview[0]); |
---|
| 730 | |
---|
[4219] | 731 | if (TRACE_UID !== false and (TRACE_TYPE == 'IMAP' or TRACE_TYPE == 'ALL') and (! array_key_exists( "uid", $vars))) debugLog('IMAP::StatMessage-> ERRO: A mensagem será desconsiderada porque não tem o campo "uid"'); |
---|
[3754] | 732 | // without uid it's not a valid message |
---|
| 733 | if (! array_key_exists( "uid", $vars)) return false; |
---|
| 734 | |
---|
| 735 | |
---|
| 736 | $entry = array(); |
---|
| 737 | $entry["mod"] = (array_key_exists( "date", $vars)) ? $overview[0]->date : ""; |
---|
| 738 | $entry["id"] = $overview[0]->uid; |
---|
| 739 | // 'seen' aka 'read' is the only flag we want to know about |
---|
| 740 | $entry["flags"] = 0; |
---|
| 741 | |
---|
| 742 | if(array_key_exists( "seen", $vars) && $overview[0]->seen) |
---|
| 743 | $entry["flags"] = 1; |
---|
| 744 | |
---|
[4219] | 745 | if (TRACE_UID !== false and (TRACE_TYPE == 'IMAP' or TRACE_TYPE == 'ALL')) traceLog('IMAP::StatMessage: '.print_r($entry,1)); |
---|
| 746 | |
---|
[3754] | 747 | //advanced debugging |
---|
| 748 | //debugLog("IMAP-StatMessage-parsed: ". print_r($entry,1)); |
---|
| 749 | |
---|
| 750 | return $entry; |
---|
| 751 | } |
---|
| 752 | } |
---|
| 753 | |
---|
| 754 | /* GetMessage should return the actual SyncXXX object type. You may or may not use the '$folderid' parent folder |
---|
| 755 | * identifier here. |
---|
| 756 | * Note that mixing item types is illegal and will be blocked by the engine; ie returning an Email object in a |
---|
| 757 | * Tasks folder will not do anything. The SyncXXX objects should be filled with as much information as possible, |
---|
| 758 | * but at least the subject, body, to, from, etc. |
---|
| 759 | */ |
---|
| 760 | function GetMessage($folderid, $id, $truncsize, $mimesupport = 0) { |
---|
| 761 | debugLog("IMAP-GetMessage: (fid: '$folderid' id: '$id' truncsize: $truncsize)"); |
---|
| 762 | |
---|
| 763 | // Get flags, etc |
---|
| 764 | $stat = $this->StatMessage($folderid, $id); |
---|
| 765 | |
---|
| 766 | if ($stat) { |
---|
| 767 | $this->imap_reopenFolder($folderid); |
---|
| 768 | $mail = @imap_fetchheader($this->_mbox, $id, FT_PREFETCHTEXT | FT_UID) . @imap_body($this->_mbox, $id, FT_PEEK | FT_UID); |
---|
| 769 | |
---|
[4219] | 770 | if (TRACE_UID !== false and (TRACE_TYPE == 'IMAP' or TRACE_TYPE == 'ALL')) traceLog("IMAP-GetMessage: Mensagem lida do servidor IMAP através da função @imap_fetchheader: ". print_r($mail,1)); |
---|
| 771 | |
---|
[3754] | 772 | $mobj = new Mail_mimeDecode($mail); |
---|
| 773 | $message = $mobj->decode(array('decode_headers' => true, 'decode_bodies' => true, 'include_bodies' => true, 'input' => $mail, 'crlf' => "\n", 'charset' => 'utf-8')); |
---|
| 774 | |
---|
[4219] | 775 | if (TRACE_UID !== false and (TRACE_TYPE == 'IMAP' or TRACE_TYPE == 'ALL')) traceLog("IMAP-GetMessage: Mensagem mime decodificada: ". print_r($message,1)); |
---|
| 776 | |
---|
[3754] | 777 | $output = new SyncMail(); |
---|
| 778 | |
---|
[4617] | 779 | $body = $this->getBody($message, true); // true - Truncate body due to celular memory limit |
---|
[3754] | 780 | // truncate body, if requested |
---|
| 781 | if(strlen($body) > $truncsize) { |
---|
| 782 | $body = utf8_truncate($body, $truncsize); |
---|
| 783 | $output->bodytruncated = 1; |
---|
| 784 | } else { |
---|
| 785 | $body = $body; |
---|
| 786 | $output->bodytruncated = 0; |
---|
| 787 | } |
---|
| 788 | $body = str_replace("\n","\r\n", str_replace("\r","",$body)); |
---|
| 789 | |
---|
| 790 | $output->bodysize = strlen($body); |
---|
| 791 | $output->body = $body; |
---|
| 792 | //$output->datereceived = isset($message->headers["date"]) ? strtotime($message->headers["date"]) : null; |
---|
| 793 | $output->datereceived = isset($message->headers["date"]) ? strtotime(preg_replace("/\(.*\)/", "", $message->headers["date"])) : null; // emerson-faria.nobre@serpro.gov.br |
---|
| 794 | $output->displayto = isset($message->headers["to"]) ? $message->headers["to"] : null; |
---|
| 795 | $output->importance = isset($message->headers["x-priority"]) ? preg_replace("/\D+/", "", $message->headers["x-priority"]) : null; |
---|
| 796 | $output->messageclass = "IPM.Note"; |
---|
| 797 | $output->subject = isset($message->headers["subject"]) ? $message->headers["subject"] : ""; |
---|
| 798 | $output->read = $stat["flags"]; |
---|
| 799 | $output->to = isset($message->headers["to"]) ? $message->headers["to"] : null; |
---|
| 800 | $output->cc = isset($message->headers["cc"]) ? $message->headers["cc"] : null; |
---|
| 801 | $output->from = isset($message->headers["from"]) ? $message->headers["from"] : null; |
---|
| 802 | $output->reply_to = isset($message->headers["reply-to"]) ? $message->headers["reply-to"] : null; |
---|
| 803 | |
---|
| 804 | // Attachments are only searched in the top-level part |
---|
| 805 | $n = 0; |
---|
| 806 | if(isset($message->parts)) { |
---|
| 807 | foreach($message->parts as $part) { |
---|
| 808 | if(isset($part->disposition) && ($part->disposition == "attachment" || $part->disposition == "inline")) { |
---|
| 809 | $attachment = new SyncAttachment(); |
---|
| 810 | |
---|
| 811 | if (isset($part->body)) |
---|
| 812 | $attachment->attsize = strlen($part->body); |
---|
| 813 | |
---|
| 814 | if(isset($part->d_parameters['filename'])) |
---|
| 815 | $attname = $part->d_parameters['filename']; |
---|
| 816 | else if(isset($part->ctype_parameters['name'])) |
---|
| 817 | $attname = $part->ctype_parameters['name']; |
---|
| 818 | else if(isset($part->headers['content-description'])) |
---|
| 819 | $attname = $part->headers['content-description']; |
---|
| 820 | else $attname = "unknown attachment"; |
---|
| 821 | |
---|
| 822 | $attachment->displayname = $attname; |
---|
| 823 | $attachment->attname = $folderid . ":" . $id . ":" . $n; |
---|
| 824 | $attachment->attmethod = 1; |
---|
| 825 | $attachment->attoid = isset($part->headers['content-id']) ? $part->headers['content-id'] : ""; |
---|
| 826 | array_push($output->attachments, $attachment); |
---|
| 827 | } |
---|
| 828 | $n++; |
---|
| 829 | } |
---|
| 830 | } |
---|
| 831 | // unset mimedecoder & mail |
---|
| 832 | unset($mobj); |
---|
| 833 | unset($mail); |
---|
[4219] | 834 | if (TRACE_UID !== false and (TRACE_TYPE == 'IMAP' or TRACE_TYPE == 'ALL')) traceLog("IMAP-GetMessage: Mensagem formatada para envio ao celular: ". print_r($output,1)); |
---|
[3754] | 835 | return $output; |
---|
| 836 | } |
---|
| 837 | return false; |
---|
| 838 | } |
---|
| 839 | |
---|
| 840 | /* This function is called when the user has requested to delete (really delete) a message. Usually |
---|
| 841 | * this means just unlinking the file its in or somesuch. After this call has succeeded, a call to |
---|
| 842 | * GetMessageList() should no longer list the message. If it does, the message will be re-sent to the PDA |
---|
| 843 | * as it will be seen as a 'new' item. This means that if you don't implement this function, you will |
---|
| 844 | * be able to delete messages on the PDA, but as soon as you sync, you'll get the item back |
---|
| 845 | */ |
---|
| 846 | function DeleteMessage($folderid, $id) { |
---|
| 847 | debugLog("IMAP-DeleteMessage: (fid: '$folderid' id: '$id' )"); |
---|
| 848 | |
---|
| 849 | $this->imap_reopenFolder($folderid); |
---|
| 850 | $s1 = @imap_delete ($this->_mbox, $id, FT_UID); |
---|
| 851 | $s11 = @imap_setflag_full($this->_mbox, $id, "\\Deleted", FT_UID); |
---|
| 852 | $s2 = @imap_expunge($this->_mbox); |
---|
| 853 | |
---|
| 854 | debugLog("IMAP-DeleteMessage: s-delete: $s1 s-expunge: $s2 setflag: $s11"); |
---|
| 855 | |
---|
| 856 | return ($s1 && $s2 && $s11); |
---|
| 857 | } |
---|
| 858 | |
---|
| 859 | /* This should change the 'read' flag of a message on disk. The $flags |
---|
| 860 | * parameter can only be '1' (read) or '0' (unread). After a call to |
---|
| 861 | * SetReadFlag(), GetMessageList() should return the message with the |
---|
| 862 | * new 'flags' but should not modify the 'mod' parameter. If you do |
---|
| 863 | * change 'mod', simply setting the message to 'read' on the PDA will trigger |
---|
| 864 | * a full resync of the item from the server |
---|
| 865 | */ |
---|
| 866 | function SetReadFlag($folderid, $id, $flags) { |
---|
| 867 | debugLog("IMAP-SetReadFlag: (fid: '$folderid' id: '$id' flags: '$flags' )"); |
---|
| 868 | |
---|
| 869 | $this->imap_reopenFolder($folderid); |
---|
| 870 | |
---|
| 871 | if ($flags == 0) { |
---|
| 872 | // set as "Unseen" (unread) |
---|
| 873 | $status = @imap_clearflag_full ( $this->_mbox, $id, "\\Seen", ST_UID); |
---|
| 874 | } else { |
---|
| 875 | // set as "Seen" (read) |
---|
| 876 | $status = @imap_setflag_full($this->_mbox, $id, "\\Seen",ST_UID); |
---|
| 877 | } |
---|
| 878 | |
---|
| 879 | debugLog("IMAP-SetReadFlag -> set as " . (($flags) ? "read" : "unread") . "-->". $status); |
---|
| 880 | |
---|
| 881 | return $status; |
---|
| 882 | } |
---|
| 883 | |
---|
| 884 | /* This function is called when a message has been changed on the PDA. You should parse the new |
---|
| 885 | * message here and save the changes to disk. The return value must be whatever would be returned |
---|
| 886 | * from StatMessage() after the message has been saved. This means that both the 'flags' and the 'mod' |
---|
| 887 | * properties of the StatMessage() item may change via ChangeMessage(). |
---|
| 888 | * Note that this function will never be called on E-mail items as you can't change e-mail items, you |
---|
| 889 | * can only set them as 'read'. |
---|
| 890 | */ |
---|
| 891 | function ChangeMessage($folderid, $id, $message) { |
---|
| 892 | return false; |
---|
| 893 | } |
---|
| 894 | |
---|
| 895 | /* This function is called when the user moves an item on the PDA. You should do whatever is needed |
---|
| 896 | * to move the message on disk. After this call, StatMessage() and GetMessageList() should show the items |
---|
| 897 | * to have a new parent. This means that it will disappear from GetMessageList() will not return the item |
---|
| 898 | * at all on the source folder, and the destination folder will show the new message |
---|
| 899 | * |
---|
| 900 | */ |
---|
| 901 | function MoveMessage($folderid, $id, $newfolderid) { |
---|
| 902 | debugLog("IMAP-MoveMessage: (sfid: '$folderid' id: '$id' dfid: '$newfolderid' )"); |
---|
| 903 | |
---|
| 904 | $this->imap_reopenFolder($folderid); |
---|
| 905 | |
---|
| 906 | // read message flags |
---|
| 907 | $overview = @imap_fetch_overview ( $this->_mbox , $id, FT_UID); |
---|
| 908 | |
---|
| 909 | if (!$overview) { |
---|
| 910 | debugLog("IMAP-MoveMessage: Failed to retrieve overview"); |
---|
| 911 | return false; |
---|
| 912 | } |
---|
| 913 | else { |
---|
| 914 | // move message |
---|
| 915 | $s1 = imap_mail_move($this->_mbox, $id, str_replace(".", $this->_serverdelimiter, $newfolderid), FT_UID); |
---|
| 916 | |
---|
| 917 | // delete message in from-folder |
---|
| 918 | $s2 = imap_expunge($this->_mbox); |
---|
| 919 | |
---|
| 920 | // open new folder |
---|
| 921 | $this->imap_reopenFolder($newfolderid); |
---|
| 922 | |
---|
| 923 | // remove all flags |
---|
| 924 | $s3 = @imap_clearflag_full ($this->_mbox, $id, "\\Seen \\Answered \\Flagged \\Deleted \\Draft", FT_UID); |
---|
| 925 | $newflags = ""; |
---|
| 926 | if ($overview[0]->seen) $newflags .= "\\Seen"; |
---|
| 927 | if ($overview[0]->flagged) $newflags .= " \\Flagged"; |
---|
| 928 | if ($overview[0]->answered) $newflags .= " \\Answered"; |
---|
| 929 | $s4 = @imap_setflag_full ($this->_mbox, $id, $newflags, FT_UID); |
---|
| 930 | |
---|
| 931 | debugLog("MoveMessage: (" . $folderid . "->" . $newfolderid . ") s-move: $s1 s-expunge: $s2 unset-Flags: $s3 set-Flags: $s4"); |
---|
| 932 | |
---|
| 933 | return ($s1 && $s2 && $s3 && $s4); |
---|
| 934 | } |
---|
| 935 | } |
---|
| 936 | |
---|
| 937 | // new ping mechanism for the IMAP-Backend |
---|
| 938 | function AlterPing() { |
---|
| 939 | return true; |
---|
| 940 | } |
---|
| 941 | |
---|
| 942 | // returns a changes array using imap_status |
---|
| 943 | // if changes occurr default diff engine computes the actual changes |
---|
| 944 | function AlterPingChanges($folderid, &$syncstate) { |
---|
| 945 | debugLog("AlterPingChanges on $folderid stat: ". $syncstate); |
---|
| 946 | $this->imap_reopenFolder($folderid); |
---|
| 947 | |
---|
| 948 | // courier-imap only cleares the status cache after checking |
---|
| 949 | @imap_check($this->_mbox); |
---|
| 950 | |
---|
| 951 | $status = imap_status($this->_mbox, $this->_server . str_replace(".", $this->_serverdelimiter, $folderid), SA_ALL); |
---|
| 952 | if (!$status) { |
---|
| 953 | debugLog("AlterPingChanges: could not stat folder $folderid : ". imap_last_error()); |
---|
| 954 | return false; |
---|
| 955 | } |
---|
| 956 | else { |
---|
| 957 | $newstate = "M:". $status->messages ."-R:". $status->recent ."-U:". $status->unseen; |
---|
| 958 | |
---|
| 959 | // message number is different - change occured |
---|
| 960 | if ($syncstate != $newstate) { |
---|
| 961 | $syncstate = $newstate; |
---|
| 962 | debugLog("AlterPingChanges: Change FOUND!"); |
---|
| 963 | // build a dummy change |
---|
| 964 | return array(array("type" => "fakeChange")); |
---|
| 965 | } |
---|
| 966 | } |
---|
| 967 | |
---|
| 968 | return array(); |
---|
| 969 | } |
---|
| 970 | |
---|
| 971 | // ---------------------------------------- |
---|
| 972 | // imap-specific internals |
---|
| 973 | |
---|
| 974 | /* Parse the message and return only the plaintext body |
---|
| 975 | */ |
---|
[4617] | 976 | function getBody($message, $trunc = false) { |
---|
[3754] | 977 | $body = ""; |
---|
| 978 | $htmlbody = ""; |
---|
| 979 | |
---|
| 980 | $this->getBodyRecursive($message, "plain", $body); |
---|
[4619] | 981 | if($trunc == true and isset($body) and strlen($body) > 102400) { |
---|
| 982 | $body = substr($body, 0, 102400); |
---|
| 983 | $body .= "\n\n\n A MENSAGEM FOI TRUNCADA NO CELULAR(MENSAGEM MUITO GRANDE)."; |
---|
| 984 | } |
---|
[3754] | 985 | |
---|
[4219] | 986 | if(!isset($body) or $body === '') { |
---|
[3754] | 987 | $this->getBodyRecursive($message, "html", $body); |
---|
[4617] | 988 | |
---|
[4618] | 989 | if($trunc == true and isset($body) and strlen($body) > 209715) { |
---|
| 990 | $body = substr($body, 0, 209715); |
---|
| 991 | $body .= "<BR><BR><BR> A MENSAGEM FOI TRUNCADA NO CELULAR(MENSAGEM MUITO GRANDE)."; |
---|
| 992 | } |
---|
| 993 | |
---|
[3754] | 994 | // remove css-style tags |
---|
| 995 | $body = preg_replace("/<style.*?<\/style>/is", "a", $body); |
---|
| 996 | // remove all other html |
---|
| 997 | //$body = strip_tags($body); |
---|
| 998 | |
---|
| 999 | // Remove the HTML tags using the 'html2text' - emerson-faria.nobre@serpro.gov.br |
---|
| 1000 | // The 'html2text' (http://www.mbayer.de/html2text) must be installed in Z-Push server. |
---|
[4219] | 1001 | |
---|
[3754] | 1002 | // Advanced debug |
---|
| 1003 | // debugLog("IMAP-getBody: subject: " . $message->headers["subject"]); |
---|
[4219] | 1004 | $body = utf8_encode($body); |
---|
[3754] | 1005 | libxml_use_internal_errors(true); |
---|
| 1006 | try { |
---|
| 1007 | $doc = new DOMDocument('1.0', 'UTF-8'); |
---|
| 1008 | @$doc->loadHTML($body); |
---|
| 1009 | $tables = $doc->getElementsByTagName('table'); |
---|
| 1010 | foreach ($tables as $table) |
---|
| 1011 | { |
---|
| 1012 | $tds = $table->getElementsByTagName('td'); |
---|
| 1013 | foreach ($tds as $td) |
---|
| 1014 | { |
---|
| 1015 | foreach ($td->childNodes as $td_child) { |
---|
| 1016 | if ($td_child->nodeName == 'br') $td->removeChild($td_child); |
---|
| 1017 | } |
---|
| 1018 | } |
---|
| 1019 | } |
---|
| 1020 | $links = $doc->getElementsByTagName('a'); |
---|
| 1021 | foreach ($links as $link) |
---|
| 1022 | { |
---|
| 1023 | if ($link->hasAttributes()){ |
---|
| 1024 | $novoNo = $doc->createTextNode(' (' . $link->getAttribute('href') . ')'); |
---|
| 1025 | $link->parentNode->insertBefore($novoNo, $link->nextSibling); |
---|
| 1026 | } |
---|
| 1027 | } |
---|
| 1028 | $body = $doc->saveHTML(); |
---|
| 1029 | } catch (Exception $e) { |
---|
| 1030 | debugLog("IMAP-getBody: Alert - DOMDocument cannot parse HTML."); |
---|
| 1031 | } |
---|
| 1032 | $filename = "./state/" . $this->_user . "-" . $this->_devid . ".html"; |
---|
| 1033 | $fp = fopen($filename, 'w') or die("can't open file"); |
---|
| 1034 | fwrite($fp, $body); |
---|
| 1035 | $body_aux = shell_exec('html2text -nobs -style compact "' . $filename . '"'); |
---|
| 1036 | if (trim($body_aux) != '') $body = $body_aux; |
---|
| 1037 | unset($body_aux); |
---|
| 1038 | fclose($fp); |
---|
| 1039 | unlink($filename); |
---|
| 1040 | $body = utf8_decode($body); |
---|
| 1041 | $body = str_replace('_','',$body); |
---|
| 1042 | // End change block - Remove the HTML tags using the 'html2text' Linux application |
---|
| 1043 | } |
---|
[4617] | 1044 | |
---|
[3754] | 1045 | return $body; |
---|
| 1046 | } |
---|
| 1047 | |
---|
| 1048 | // Get all parts in the message with specified type and concatenate them together, unless the |
---|
| 1049 | // Content-Disposition is 'attachment', in which case the text is apparently an attachment |
---|
| 1050 | function getBodyRecursive($message, $subtype, &$body) { |
---|
| 1051 | if(!isset($message->ctype_primary)) return; |
---|
| 1052 | if(strcasecmp($message->ctype_primary,"text")==0 && strcasecmp($message->ctype_secondary,$subtype)==0 && isset($message->body)) |
---|
| 1053 | $body .= $message->body; |
---|
| 1054 | |
---|
| 1055 | if(strcasecmp($message->ctype_primary,"multipart")==0 && isset($message->parts) && is_array($message->parts)) { |
---|
[4617] | 1056 | foreach($message->parts as $part) { |
---|
[3754] | 1057 | if(!isset($part->disposition) || strcasecmp($part->disposition,"attachment")) { |
---|
| 1058 | $this->getBodyRecursive($part, $subtype, $body); |
---|
| 1059 | } |
---|
| 1060 | } |
---|
| 1061 | } |
---|
| 1062 | } |
---|
| 1063 | |
---|
| 1064 | // save the serverdelimiter for later folder (un)parsing |
---|
| 1065 | function getServerDelimiter() { |
---|
| 1066 | $list = @imap_getmailboxes($this->_mbox, $this->_server, "*"); |
---|
| 1067 | if (is_array($list)) { |
---|
| 1068 | $val = $list[0]; |
---|
| 1069 | |
---|
| 1070 | return $val->delimiter; |
---|
| 1071 | } |
---|
| 1072 | return "."; // default "." |
---|
| 1073 | } |
---|
| 1074 | |
---|
| 1075 | // speed things up |
---|
| 1076 | // remember what folder is currently open and only change if necessary |
---|
| 1077 | function imap_reopenFolder($folderid, $force = false) { |
---|
| 1078 | // to see changes, the folder has to be reopened! |
---|
| 1079 | if ($this->_mboxFolder != $folderid || $force) { |
---|
| 1080 | $s = @imap_reopen($this->_mbox, $this->_server . str_replace(".", $this->_serverdelimiter, $folderid)); |
---|
| 1081 | if (!$s) debugLog("failed to change folder: ". implode(", ", imap_errors())); |
---|
| 1082 | $this->_mboxFolder = $folderid; |
---|
| 1083 | } |
---|
| 1084 | } |
---|
| 1085 | |
---|
| 1086 | |
---|
| 1087 | // build a multipart email, embedding body and one file (for attachments) |
---|
[4613] | 1088 | function mail_attach($filenm,$filesize,$file_cont,$body, $body_ct, $body_cte, $boundary = false) { |
---|
| 1089 | if (!$boundary) $boundary = strtoupper(md5(uniqid(time()))); |
---|
[3754] | 1090 | |
---|
[4613] | 1091 | //remove the ending boundary because we will add it at the end |
---|
| 1092 | $body = str_replace("--$boundary--", "", $body); |
---|
[3754] | 1093 | |
---|
[4613] | 1094 | $mail_header = "Content-Type: multipart/mixed; boundary=$boundary\n"; |
---|
[3754] | 1095 | |
---|
[4613] | 1096 | // build main body with the sumitted type & encoding from the pda |
---|
| 1097 | $mail_body = $this->enc_multipart($boundary, $body, $body_ct, $body_cte); |
---|
| 1098 | $mail_body .= $this->enc_attach_file($boundary, $filenm, $filesize, $file_cont); |
---|
[3754] | 1099 | |
---|
[4613] | 1100 | $mail_body .= "--$boundary--\n\n"; |
---|
| 1101 | return array($mail_header, $mail_body); |
---|
| 1102 | } |
---|
[3754] | 1103 | |
---|
[4613] | 1104 | function enc_multipart($boundary, $body, $body_ct, $body_cte) { |
---|
| 1105 | // $mail_body = "This is a multi-part message in MIME format\n\n"; |
---|
| 1106 | // $mail_body .= "--$boundary\n"; |
---|
| 1107 | // $mail_body .= "Content-Type: $body_ct\n"; |
---|
| 1108 | // $mail_body .= "Content-Transfer-Encoding: $body_cte\n\n"; |
---|
| 1109 | $mail_body = "$body\n\n"; |
---|
[3754] | 1110 | |
---|
[4613] | 1111 | return $mail_body; |
---|
| 1112 | } |
---|
| 1113 | |
---|
| 1114 | function enc_attach_file($boundary, $filenm, $filesize, $file_cont, $content_type = "") { |
---|
| 1115 | if (!$content_type) $content_type = "text/plain"; |
---|
| 1116 | $mail_body = "--$boundary\n"; |
---|
| 1117 | $mail_body .= "Content-Type: $content_type; name=\"$filenm\"\n"; |
---|
| 1118 | $mail_body .= "Content-Transfer-Encoding: base64\n"; |
---|
| 1119 | $mail_body .= "Content-Disposition: attachment; filename=\"$filenm\"\n"; |
---|
| 1120 | $mail_body .= "Content-Description: $filenm\n\n"; |
---|
| 1121 | //contrib - chunk base64 encoded attachments |
---|
| 1122 | $mail_body .= chunk_split(base64_encode($file_cont)) . "\n\n"; |
---|
[3754] | 1123 | |
---|
[4613] | 1124 | return $mail_body; |
---|
| 1125 | } |
---|
| 1126 | |
---|
[3754] | 1127 | // adds a message as seen to a specified folder (used for saving sent mails) |
---|
| 1128 | function addSentMessage($folderid, $header, $body) { |
---|
| 1129 | $header_body = str_replace("\n", "\r\n", str_replace("\r", "", $header . "\n\n" . $body)); |
---|
| 1130 | |
---|
| 1131 | return @imap_append($this->_mbox, $this->_server . $folderid, $header_body, "\\Seen"); |
---|
| 1132 | } |
---|
| 1133 | |
---|
| 1134 | |
---|
| 1135 | // parses address objects back to a simple "," separated string |
---|
| 1136 | function parseAddr($ad) { |
---|
| 1137 | $addr_string = ""; |
---|
| 1138 | if (isset($ad) && is_array($ad)) { |
---|
| 1139 | foreach($ad as $addr) { |
---|
| 1140 | if ($addr_string) $addr_string .= ","; |
---|
| 1141 | $addr_string .= $addr->mailbox . "@" . $addr->host; |
---|
| 1142 | } |
---|
| 1143 | } |
---|
| 1144 | return $addr_string; |
---|
| 1145 | } |
---|
| 1146 | |
---|
| 1147 | }; |
---|
| 1148 | |
---|
| 1149 | ?> |
---|