[ Index ] |
|
Code source de IMP H3 (4.1.5) |
1 <?php 2 3 require_once 'Horde/MIME.php'; 4 5 /** 6 * The virtual path to use for VFS data. 7 */ 8 define('IMP_VFS_ATTACH_PATH', '.horde/imp/compose'); 9 10 /** 11 * The virtual path to save linked attachments. 12 */ 13 define('IMP_VFS_LINK_ATTACH_PATH', '.horde/imp/attachments'); 14 15 /** 16 * The IMP_Compose:: class contains functions related to generating 17 * outgoing mail messages. 18 * 19 * $Horde: imp/lib/Compose.php,v 1.107.2.41 2007/01/02 13:54:55 jan Exp $ 20 * 21 * Copyright 2002-2007 Michael Slusarz <slusarz@bigworm.colorado.edu> 22 * 23 * See the enclosed file COPYING for license information (GPL). If you 24 * did not receive this file, see http://www.fsf.org/copyleft/gpl.html. 25 * 26 * @author Michael Slusarz <slusarz@bigworm.colorado.edu> 27 * @since IMP 4.0 28 * @package IMP 29 */ 30 class IMP_Compose { 31 32 /** 33 * The cached attachment data. 34 * 35 * @var array 36 */ 37 var $_cache = array(); 38 39 /** 40 * For findBody, the MIME ID of the "body" part. 41 * 42 * @var string 43 */ 44 var $_mimeid = null; 45 46 /** 47 * The aggregate size of all attachments (in bytes). 48 * 49 * @var integer 50 */ 51 var $_size = 0; 52 53 /** 54 * Constructor 55 * 56 * @param array $params Parameters needed. 57 */ 58 function IMP_Compose($params = array()) 59 { 60 if (isset($params['cacheID'])) { 61 $this->_retrieveMimeCache($params['cacheID']); 62 } 63 } 64 65 /** 66 * Sends a message. 67 * 68 * @param string $email The e-mail list to send to. 69 * @param IMP_Headers &$headers The IMP_Headers object holding this 70 * messages headers. 71 * @param mixed &$message Either the message text (string) or a 72 * MIME_Message object that contains the 73 * text to send. 74 * @param string $charset The charset that was used for the headers. 75 * 76 * @return mixed True on success, PEAR_Error on error. 77 */ 78 function sendMessage($email, &$headers, &$message, $charset) 79 { 80 global $conf; 81 82 require_once 'Mail.php'; 83 84 /* We don't actually want to alter the contents of the $conf['mailer'] 85 * array, so we make a copy of the current settings. We will apply our 86 * modifications (if any) to the copy, instead. */ 87 $params = $conf['mailer']['params']; 88 $driver = $conf['mailer']['type']; 89 90 /* If user specifies an SMTP server on login, force SMTP mailer. */ 91 if (!empty($conf['server']['change_smtphost'])) { 92 $driver = 'smtp'; 93 if (empty($params['mailer']['auth'])) { 94 $params['mailer']['auth'] = '1'; 95 } 96 } 97 98 /* Force the SMTP host and port value to the current SMTP server if 99 * one has been selected for this connection. */ 100 if (!empty($_SESSION['imp']['smtphost'])) { 101 $params['host'] = $_SESSION['imp']['smtphost']; 102 } 103 if (!empty($_SESSION['imp']['smtpport'])) { 104 $params['port'] = $_SESSION['imp']['smtpport']; 105 } 106 107 /* If SMTP authentication has been requested, use either the username 108 * and password provided in the configuration or populate the username 109 * and password fields based on the current values for the user. Note 110 * that we assume that the username and password values from the 111 * current IMAP / POP3 connection are valid for SMTP authentication as 112 * well. */ 113 if (!empty($params['auth']) && empty($params['username'])) { 114 $params['username'] = $_SESSION['imp']['user']; 115 $params['password'] = Secret::read(Secret::getKey('imp'), $_SESSION['imp']['pass']); 116 } 117 118 /* Add the site headers. */ 119 $headers->addSiteHeaders(); 120 121 /* If $message is a string, we need to get a MIME_Message object to 122 * encode the headers. */ 123 if (is_string($message)) { 124 $msg = $message; 125 $mime_message = &new MIME_Message($_SESSION['imp']['maildomain']); 126 $headerArray = $mime_message->encode($headers->toArray(), $charset); 127 } else { 128 $msg = $message->toString(); 129 $headerArray = $message->encode($headers->toArray(), $charset); 130 } 131 132 /* Make sure the message has a trailing newline. */ 133 if (substr($msg, -1) != "\n") { 134 $msg .= "\n"; 135 } 136 137 $mailer = Mail::factory($driver, $params); 138 if (is_a($mailer, 'PEAR_Error')) { 139 return $mailer; 140 } 141 142 /* Properly encode the addresses we're sending to. */ 143 $email = MIME::encodeAddress($email, null, $_SESSION['imp']['maildomain']); 144 if (is_a($email, 'PEAR_Error')) { 145 return $email; 146 } 147 148 /* Validate the recipient addresses. */ 149 require_once 'Mail/RFC822.php'; 150 $parser = &new Mail_RFC822(); 151 $result = $parser->parseAddressList($email, $_SESSION['imp']['maildomain'], null, true); 152 if (is_a($result, 'PEAR_Error')) { 153 return $result; 154 } 155 156 $result = $mailer->send($email, $headerArray, $msg); 157 158 if (is_a($result, 'PEAR_Error') && 159 $conf['mailer']['type'] == 'sendmail') { 160 // Interpret return values as defined in /usr/include/sysexits.h 161 switch ($result->getCode()) { 162 case 64: // EX_USAGE 163 $error = 'sendmail: ' . _("command line usage error") . ' (64)'; 164 break; 165 166 case 65: // EX_DATAERR 167 $error = 'sendmail: ' . _("data format error") . ' (65)'; 168 break; 169 170 case 66: // EX_NOINPUT 171 $error = 'sendmail: ' . _("cannot open input") . ' (66)'; 172 break; 173 174 case 67: // EX_NOUSER 175 $error = 'sendmail: ' . _("addressee unknown") . ' (67)'; 176 break; 177 178 case 68: // EX_NOHOST 179 $error = 'sendmail: ' . _("host name unknown") . ' (68)'; 180 break; 181 182 case 69: // EX_UNAVAILABLE 183 $error = 'sendmail: ' . _("service unavailable") . ' (69)'; 184 break; 185 186 case 70: // EX_SOFTWARE 187 $error = 'sendmail: ' . _("internal software error") . ' (70)'; 188 break; 189 190 case 71: // EX_OSERR 191 $error = 'sendmail: ' . _("system error") . ' (71)'; 192 break; 193 194 case 72: // EX_OSFILE 195 $error = 'sendmail: ' . _("critical system file missing") . ' (72)'; 196 break; 197 198 case 73: // EX_CANTCREAT 199 $error = 'sendmail: ' . _("cannot create output file") . ' (73)'; 200 break; 201 202 case 74: // EX_IOERR 203 $error = 'sendmail: ' . _("input/output error") . ' (74)'; 204 break; 205 206 case 75: // EX_TEMPFAIL 207 $error = 'sendmail: ' . _("temporary failure") . ' (75)'; 208 break; 209 210 case 76: // EX_PROTOCOL 211 $error = 'sendmail: ' . _("remote error in protocol") . ' (76)'; 212 break; 213 214 case 77: // EX_NOPERM 215 $error = 'sendmail: ' . _("permission denied") . ' (77)'; 216 break; 217 218 case 78: // EX_CONFIG 219 $error = 'sendmail: ' . _("configuration error") . ' (78)'; 220 break; 221 222 case 79: // EX_NOTFOUND 223 $error = 'sendmail: ' . _("entry not found") . ' (79)'; 224 break; 225 226 default: 227 $error = $result; 228 } 229 return PEAR::raiseError($error); 230 } 231 232 return $result; 233 } 234 235 /** 236 * Finds the main "body" text part (if any) in a message. 237 * 238 * @param IMP_Contents &$imp_contents An IMP_Contents object. 239 * 240 * @return string The text of the "body" part of the message. 241 * Returns an empty string if no "body" found. 242 */ 243 function findBody(&$imp_contents) 244 { 245 if (is_null($this->_mimeid)) { 246 $this->_mimeid = $imp_contents->findBody(); 247 if (is_null($this->_mimeid)) { 248 return ''; 249 } 250 } 251 252 $mime_part = &$imp_contents->getDecodedMIMEPart($this->_mimeid); 253 $body = $mime_part->getContents(); 254 255 //if ($mime_message->getType() == 'multipart/encrypted') { 256 /* TODO: Maybe someday I can figure out how to show embedded 257 * text parts here. But for now, just output this message. */ 258 // return '[' . _("Original message was encrypted") . ']'; 259 //} 260 261 if ($mime_part->getSubType() == 'html') { 262 require_once 'Horde/Text/Filter.php'; 263 return Text_Filter::filter($body, 'html2text', array('wrap' => ($mime_part->getContentTypeParameter('format') != 'flowed'), 'charset' => $mime_part->getCharset())); 264 } else { 265 return $body; 266 } 267 } 268 269 /** 270 * Returns the ID of the MIME part containing the "body". 271 * 272 * @param IMP_Contents &$imp_contents An IMP_Contents object. 273 * 274 * @return string The ID of the mime part's body. 275 */ 276 function getBodyId(&$imp_contents) 277 { 278 if (is_null($this->_mimeid)) { 279 $this->findBody($imp_contents); 280 } 281 return $this->_mimeid; 282 } 283 284 /** 285 * Determine the reply text for a message. 286 * 287 * @param IMP_Contents $imp_contents An IMP_Contents object. 288 * @param string $from The email address of the 289 * original sender. 290 * @param IMP_Headers $h The IMP_Headers object for 291 * the message. 292 * 293 * @return string The text of the body part of the message to use 294 * for the reply. 295 */ 296 function replyMessage(&$imp_contents, $from, &$h) 297 { 298 global $prefs; 299 300 if (!$prefs->getValue('reply_quote')) { 301 return ''; 302 } 303 304 $mime_message = $imp_contents->getMIMEMessage(); 305 $msg = $this->findBody($imp_contents); 306 $msg = $mime_message->replaceEOL($msg, "\n"); 307 308 if (!is_null($this->_mimeid)) { 309 require_once 'Text/Flowed.php'; 310 $old_part = $mime_message->getPart($this->_mimeid); 311 if ($old_part->getContentTypeParameter('format') == 'flowed') { 312 /* We need to convert the flowed text to fixed text before 313 * we begin working on it. */ 314 $flowed = &new Text_Flowed($msg); 315 if ((String::lower($mime_message->getContentTypeParameter('delsp')) == 'yes') && 316 method_exists($flowed, 'setDelSp')) { 317 $flowed->setDelSp(true); 318 } 319 $msg = $flowed->toFixed(false); 320 } else { 321 /* If the input is *not* in flowed format, make sure there is 322 * no padding at the end of lines. */ 323 $msg = preg_replace("/\s*\n/U", "\n", $msg); 324 } 325 326 $msg = String::convertCharset($msg, $old_part->getCharset()); 327 328 $flowed = &new Text_Flowed($msg); 329 if (method_exists($flowed, 'setDelSp')) { 330 $flowed->setDelSp(true); 331 } 332 $msg = $flowed->toFlowed(true); 333 } 334 335 if (empty($msg)) { 336 $msg = '[' . _("No message body text") . ']'; 337 } elseif ($prefs->getValue('reply_headers') && !empty($h)) { 338 $msghead = '----- '; 339 if (($from = $h->getFromAddress())) { 340 $msghead .= sprintf(_("Message from %s"), $from); 341 } else { 342 $msghead .= _("Message"); 343 } 344 345 /* Extra '-'s line up with "End Message" below. */ 346 $msghead .= " ---------\n"; 347 $msghead .= $this->_getMsgHeaders($h); 348 $msg = $msghead . "\n\n" . $msg; 349 if (!empty($from)) { 350 $msg .= "\n\n" . '----- ' . sprintf(_("End message from %s"), $from) . " -----\n"; 351 } else { 352 $msg .= "\n\n" . '----- ' . _("End message") . " -----\n"; 353 } 354 } else { 355 $msg = $this->_expandAttribution($prefs->getValue('attrib_text'), $from, $h) . "\n\n" . $msg; 356 } 357 358 return $msg . "\n"; 359 } 360 361 /** 362 * Determine the text for a forwarded message. 363 * 364 * @param IMP_Contents &$imp_contents An IMP_Contents object. 365 * @param IMP_Headers &$h The IMP_Headers object for 366 * the message. 367 * 368 * @return string The text of the body part of the message to use 369 * for the forward. 370 */ 371 function forwardMessage(&$imp_contents, &$h) 372 { 373 require_once 'Horde/Text.php'; 374 375 $msg = "\n\n\n----- "; 376 377 if (($from = $h->getFromAddress())) { 378 $msg .= sprintf(_("Forwarded message from %s"), $from); 379 } else { 380 $msg .= _("Forwarded message"); 381 } 382 383 $msg .= " -----\n"; 384 $msg .= $this->_getMsgHeaders($h); 385 $msg .= "\n"; 386 387 $fwd_msg = $this->findBody($imp_contents); 388 if (!is_null($this->_mimeid)) { 389 $mime_message = $imp_contents->getMIMEMessage(); 390 $old_part = $mime_message->getPart($this->_mimeid); 391 $fwd_msg = String::convertCharset($fwd_msg, $old_part->getCharset()); 392 } 393 394 $msg .= $fwd_msg; 395 $msg .= "\n\n----- " . _("End forwarded message") . " -----\n"; 396 397 return $msg; 398 } 399 400 /** 401 * Determine the header information to display in the forward/reply. 402 * 403 * @access private 404 * 405 * @param IMP_Headers &$h The IMP_Headers object for the message. 406 * 407 * @return string The header information for the original message. 408 */ 409 function _getMsgHeaders(&$h) 410 { 411 $text = ''; 412 413 if (($date_ob = $h->getOb('date', true))) { 414 $text .= _(" Date: ") . $date_ob . "\n"; 415 } 416 if (($from_ob = MIME::addrArray2String($h->getOb('from')))) { 417 $text .= _(" From: ") . $from_ob . "\n"; 418 } 419 if (($rep_ob = MIME::addrArray2String($h->getOb('reply_to')))) { 420 $text .= _("Reply-To: ") . $rep_ob . "\n"; 421 } 422 if (($sub_ob = $h->getOb('subject', true))) { 423 $text .= _(" Subject: ") . $sub_ob . "\n"; 424 } 425 if (($to_ob = MIME::addrArray2String($h->getOb('to')))) { 426 $text .= _(" To: ") . $to_ob . "\n"; 427 } 428 if (($cc_ob = MIME::addrArray2String($h->getOb('cc')))) { 429 $text .= _(" Cc: ") . $cc_ob . "\n"; 430 } 431 432 return $text; 433 } 434 435 /** 436 * Adds an attachment to a MIME_Part from an uploaded file. 437 * The actual attachment data is stored in a separate file - the 438 * MIME_Part information entries 'temp_filename' and 'temp_filetype' 439 * are set with this information. 440 * 441 * @param string $name The input field name from the form. 442 * @param string $disposition The disposition to use for the file. 443 * 444 * @return mixed Returns the filename on success. 445 * Returns PEAR_Error on error. 446 */ 447 function addUploadAttachment($name, $disposition) 448 { 449 global $conf; 450 451 $res = Browser::wasFileUploaded($name, _("attachment")); 452 if (is_a($res, 'PEAR_Error')) { 453 return $res; 454 } 455 456 $filename = $_FILES[$name]['name']; 457 $tempfile = $_FILES[$name]['tmp_name']; 458 459 /* Check for filesize limitations. */ 460 if (!empty($conf['compose']['attach_size_limit']) && 461 (($conf['compose']['attach_size_limit'] - $this->sizeOfAttachments() - $_FILES[$name]['size']) < 0)) { 462 return PEAR::raiseError(sprintf(_("Attached file \"%s\" exceeds the attachment size limits. File NOT attached."), $filename), 'horde.error'); 463 } 464 465 /* Store the data in a MIME_Part. Some browsers do not send the MIME 466 type so try an educated guess. */ 467 if (!empty($_FILES[$name]['type']) && 468 ($_FILES[$name]['type'] != 'application/octet-stream')) { 469 $part = &new MIME_Part($_FILES[$name]['type']); 470 } else { 471 require_once 'Horde/MIME/Magic.php'; 472 /* Try to determine the MIME type from 1) analysis of the file 473 * (if available) and, if that fails, 2) from the extension. We 474 * do it in this order here because, most likely, if a browser 475 * can't identify the type of a file, it is because the file 476 * extensions isn't available and/or recognized. */ 477 if (!($type = MIME_Magic::analyzeFile($tempfile, !empty($conf['mime']['magic_db']) ? $conf['mime']['magic_db'] : null))) { 478 $type = MIME_Magic::filenameToMIME($filename, false); 479 } 480 $part = &new MIME_Part($type); 481 } 482 $part->setCharset(NLS::getCharset()); 483 $part->setName(MIME::encode($filename)); 484 $part->setBytes($_FILES[$name]['size']); 485 if ($disposition) { 486 $part->setDisposition($disposition); 487 } 488 489 if ($conf['compose']['use_vfs']) { 490 $attachment = $tempfile; 491 } else { 492 $attachment = Horde::getTempFile('impatt', false); 493 move_uploaded_file($tempfile, $attachment); 494 } 495 496 /* Store the data. */ 497 $result = $this->_storeAttachment($part, $attachment); 498 if (is_a($result, 'PEAR_Error')) { 499 return $result; 500 } 501 502 return $filename; 503 } 504 505 /** 506 * Adds an attachment to a MIME_Part from data existing in the part. 507 * 508 * @param MIME_Part &$part The MIME_Part object that contains the 509 * attachment data. 510 * 511 * @return PEAR_Error Returns a PEAR_Error object on error. 512 */ 513 function addMIMEPartAttachment(&$part) 514 { 515 global $conf; 516 517 $type = $part->getType(); 518 $vfs = $conf['compose']['use_vfs']; 519 520 /* Decode the contents. */ 521 $part->transferDecodeContents(); 522 523 /* Try to determine the MIME type from 1) the extension and 524 * then 2) analysis of the file (if available). */ 525 if ($type == 'application/octet-stream') { 526 require_once 'Horde/MIME/Magic.php'; 527 $type = MIME_Magic::filenameToMIME($part->getName(true), false); 528 } 529 530 /* Extract the data from the currently existing MIME_Part and then 531 delete it. If this is an unknown MIME part, we must save to a 532 temporary file to run the file analysis on it. */ 533 if (!$vfs) { 534 $attachment = Horde::getTempFile('impatt', $vfs); 535 $fp = fopen($attachment, 'w'); 536 fwrite($fp, $part->getContents()); 537 fclose($fp); 538 539 if (($type == 'application/octet-stream') && 540 ($analyzetype = MIME_Magic::analyzeFile($attachment, !empty($conf['mime']['magic_db']) ? $conf['mime']['magic_db'] : null))) { 541 $type = $analyzetype; 542 } 543 } elseif (($type == 'application/octet-stream') && 544 ($analyzetype = MIME_Magic::analyzeData($part->getContents(), !empty($conf['mime']['magic_db']) ? $conf['mime']['magic_db'] : null))) { 545 $type = $analyzetype; 546 } 547 548 $part->setType($type); 549 550 /* Set the size of the Part explicitly since we delete the contents 551 later on in this function. */ 552 $part->setBytes($part->getBytes()); 553 554 /* Check for filesize limitations. */ 555 if (!empty($conf['compose']['attach_size_limit']) && 556 (($conf['compose']['attach_size_limit'] - $this->sizeOfAttachments() - $part->getBytes()) < 0)) { 557 return PEAR::raiseError(sprintf(_("Attached file \"%s\" exceeds the attachment size limits. File NOT attached."), $part->getName()), 'horde.error'); 558 } 559 560 /* Store the data. */ 561 if ($vfs) { 562 $vfs_data = $part->getContents(); 563 $part->clearContents(); 564 $this->_storeAttachment($part, $vfs_data, false); 565 } else { 566 $part->clearContents(); 567 $this->_storeAttachment($part, $attachment); 568 } 569 } 570 571 /** 572 * Stores the attachment data in its correct location. 573 * 574 * @access private 575 * 576 * @param MIME_Part &$part The MIME_Part of the attachment. 577 * @param string $data Either the filename of the attachment or, if 578 * $vfs_file is false, the attachment data. 579 * @param boolean $vfs_file If using VFS, is $data a filename? 580 */ 581 function _storeAttachment(&$part, $data, $vfs_file = true) 582 { 583 global $conf; 584 585 /* Store in VFS. */ 586 if ($conf['compose']['use_vfs']) { 587 require_once 'VFS.php'; 588 require_once 'VFS/GC.php'; 589 $vfs = &VFS::singleton($conf['vfs']['type'], Horde::getDriverConfig('vfs', $conf['vfs']['type'])); 590 VFS_GC::gc($vfs, IMP_VFS_ATTACH_PATH, 86400); 591 $cacheID = md5(mt_rand() . microtime()); 592 if ($vfs_file) { 593 $result = $vfs->write(IMP_VFS_ATTACH_PATH, $cacheID, $data, true); 594 } else { 595 $result = $vfs->writeData(IMP_VFS_ATTACH_PATH, $cacheID, $data, true); 596 } 597 if (is_a($result, 'PEAR_Error')) { 598 return $result; 599 } 600 $part->setInformation('temp_filename', $cacheID); 601 $part->setInformation('temp_filetype', 'vfs'); 602 } else { 603 chmod($data, 0600); 604 $part->setInformation('temp_filename', $data); 605 $part->setInformation('temp_filetype', 'file'); 606 } 607 608 /* Add the size information to the counter. */ 609 $this->_size += $part->getBytes(); 610 611 $this->_cache[] = $part; 612 } 613 614 /** 615 * Delete attached files. 616 * 617 * @param mixed $number Either a single integer or an array of integers 618 * corresponding to the attachment position. 619 * 620 * @return array The list of deleted filenames (MIME encoded). 621 */ 622 function deleteAttachment($number) 623 { 624 global $conf; 625 626 $names = array(); 627 628 if (!is_array($number)) { 629 $number = array($number); 630 } 631 632 foreach ($number as $val) { 633 $val--; 634 $part = &$this->_cache[$val]; 635 $filename = $part->getInformation('temp_filename'); 636 if ($part->getInformation('temp_filetype') == 'vfs') { 637 /* Delete from VFS. */ 638 require_once 'VFS.php'; 639 $vfs = &VFS::singleton($conf['vfs']['type'], Horde::getDriverConfig('vfs', $conf['vfs']['type'])); 640 $vfs->deleteFile(IMP_VFS_ATTACH_PATH, $filename); 641 } else { 642 /* Delete from filesystem. */ 643 @unlink($filename); 644 } 645 646 $part->setInformation('temp_filename', ''); 647 $part->setInformation('temp_filetype', ''); 648 649 $names[] = $part->getName(false, true); 650 651 /* Remove the size information from the counter. */ 652 $this->_size -= $part->getBytes(); 653 654 unset($this->_cache[$val]); 655 } 656 657 /* Reorder the attachments. */ 658 $this->_cache = array_values($this->_cache); 659 660 return $names; 661 } 662 663 /** 664 * Deletes all attachments. 665 */ 666 function deleteAllAttachments() 667 { 668 $numbers = array(); 669 670 for ($i = 1; $i <= $this->numberOfAttachments(); $i++) { 671 $numbers[] = $i; 672 } 673 674 $this->deleteAttachment($numbers); 675 } 676 677 /** 678 * Updates information in a specific attachment. 679 * 680 * @param integer $number The attachment to update. 681 * @param array $params An array of update information. 682 * <pre> 683 * 'disposition' -- The Content-Disposition value. 684 * 'description' -- The Content-Description value. 685 * </pre> 686 */ 687 function updateAttachment($number, $params) 688 { 689 $number--; 690 $this->_cache[$number]->setDisposition($params['disposition']); 691 $this->_cache[$number]->setDescription($params['description']); 692 } 693 694 /** 695 * Returns the list of current attachments. 696 * 697 * @return array The list of attachments. 698 */ 699 function getAttachments() 700 { 701 return $this->_cache; 702 } 703 704 /** 705 * Returns the number of attachments currently in this message. 706 * 707 * @return integer The number of attachments in this message. 708 */ 709 function numberOfAttachments() 710 { 711 return count($this->_cache); 712 } 713 714 /** 715 * Returns the size of the attachments in bytes. 716 * 717 * @return integer The size of the attachments (in bytes). 718 */ 719 function sizeOfAttachments() 720 { 721 return $this->_size; 722 } 723 724 /** 725 * Build a single attachment part with its data. 726 * 727 * @param integer $id The ID of the part to rebuild. 728 * 729 * @return MIME_Part The MIME_Part with its contents. 730 */ 731 function buildAttachment($id) 732 { 733 $part = $this->_cache[($id - 1)]; 734 $this->_buildPartData($part); 735 return $part; 736 } 737 738 /** 739 * Build the MIME_Part attachments from the temporary file data. 740 * 741 * @param MIME_Part &$base The base MIME_Part object to add the 742 * attachments to. 743 * @param string $charset The charset to use for the filename. 744 */ 745 function buildAllAttachments(&$base, $charset) 746 { 747 foreach ($this->_cache as $part) { 748 /* Store the data inside the current part. */ 749 $this->_buildPartData($part); 750 751 /* Convert the charset of the filename. */ 752 $name = String::convertCharset($part->getName(true), NLS::getCharset(), $charset); 753 $part->setName(MIME::encode($name, $charset)); 754 755 /* Add to the base part. */ 756 $base->addPart($part); 757 } 758 } 759 760 /** 761 * Takes the temporary data for a single part and puts it into the 762 * contents of that part. 763 * 764 * @access private 765 * 766 * @param MIME_Part &$part The part to rebuild data into. 767 */ 768 function _buildPartData(&$part) 769 { 770 global $conf; 771 772 $filename = $part->getInformation('temp_filename'); 773 if ($part->getInformation('temp_filetype') == 'vfs') { 774 require_once 'VFS.php'; 775 $vfs = &VFS::singleton($conf['vfs']['type'], Horde::getDriverConfig('vfs', $conf['vfs']['type'])); 776 $data = $vfs->read(IMP_VFS_ATTACH_PATH, $filename); 777 } else { 778 $data = file_get_contents($filename); 779 } 780 781 /* Set the part's contents to the raw attachment data. */ 782 $part->setContents($data); 783 } 784 785 /** 786 * Expand macros in attribution text when replying to messages. 787 * 788 * @access private 789 * 790 * @param string $line The line of attribution text. 791 * @param string $from The email address of the original 792 * sender. 793 * @param IMP_Headers &$h The IMP_Headers object for the message. 794 * 795 * @return string The attribution text. 796 */ 797 function _expandAttribution($line, $from, &$h) 798 { 799 $addressList = ''; 800 $nameList = ''; 801 802 /* First we'll get a comma seperated list of email addresses 803 and a comma seperated list of personal names out of $from 804 (there just might be more than one of each). */ 805 foreach (IMP::parseAddressList($from) as $entry) { 806 if (isset($entry->mailbox) && isset($entry->host)) { 807 if (strlen($addressList) > 0) { 808 $addressList .= ', '; 809 } 810 $addressList .= $entry->mailbox . '@' . $entry->host; 811 } elseif (isset($entry->mailbox)) { 812 if (strlen($addressList) > 0) { 813 $addressList .= ', '; 814 } 815 $addressList .= $entry->mailbox; 816 } 817 if (isset($entry->personal)) { 818 if (strlen($nameList) > 0) { 819 $nameList .= ', '; 820 } 821 $nameList .= $entry->personal; 822 } elseif (isset($entry->mailbox)) { 823 if (strlen($nameList) > 0) { 824 $nameList .= ', '; 825 } 826 $nameList .= $entry->mailbox; 827 } 828 } 829 830 /* Define the macros. */ 831 if (is_array($message_id = $h->getOb('message_id'))) { 832 $message_id = reset($message_id); 833 } 834 if (!($subject = $h->getOb('subject', true))) { 835 $subject = _("[No Subject]"); 836 } 837 $udate = strtotime($h->getOb('date', true)); 838 839 $match = array( 840 /* New line. */ 841 '/%n/' => "\n", 842 843 /* The '%' character. */ 844 '/%%/' => '%', 845 846 /* Name and email address of original sender. */ 847 '/%f/' => $from, 848 849 /* Senders email address(es). */ 850 '/%a/' => $addressList, 851 852 /* Senders name(s). */ 853 '/%p/' => $nameList, 854 855 /* RFC 822 date and time. */ 856 '/%r/' => $h->getOb('date', true), 857 858 /* Date as ddd, dd mmm yyyy. */ 859 '/%d/' => String::convertCharset(@strftime("%a, %d %b %Y", $udate), NLS::getExternalCharset()), 860 861 /* Date in locale's default. */ 862 '/%x/' => String::convertCharset(@strftime("%x", $udate), NLS::getExternalCharset()), 863 864 /* Date and time in locale's default. */ 865 '/%c/' => String::convertCharset(@strftime("%c", $udate), NLS::getExternalCharset()), 866 867 /* Message-ID. */ 868 '/%m/' => $message_id, 869 870 /* Message subject. */ 871 '/%s/' => $subject 872 ); 873 874 return (preg_replace(array_keys($match), array_values($match), $line)); 875 } 876 877 /** 878 * Obtains the cached array of MIME_Parts to be attached to this message. 879 * 880 * @access private 881 * 882 * @param string $cacheID The cacheID of the session object. 883 */ 884 function _retrieveMimeCache($cacheID) 885 { 886 if ($cacheID) { 887 require_once 'Horde/SessionObjects.php'; 888 $cacheSess = &Horde_SessionObjects::singleton(); 889 $result = $cacheSess->query($cacheID); 890 $cacheSess->setPruneFlag($cacheID, true); 891 $this->_cache = &$result['cache']; 892 $this->_size = &$result['size']; 893 } 894 } 895 896 /** 897 * Obtains the cache ID for the session object that contains the 898 * MIME_Part objects to be attached to this message. 899 * This function needs to be run at least once per pageload to save the 900 * session object. 901 * 902 * @return string The message cache ID if the object needs to be saved. 903 * Else, false is returned. 904 */ 905 function getMessageCacheId() 906 { 907 if (!empty($this->_cache)) { 908 require_once 'Horde/SessionObjects.php'; 909 $cacheSess = &Horde_SessionObjects::singleton(); 910 $store = array( 911 'cache' => $this->_cache, 912 'size' => $this->_size 913 ); 914 return $cacheSess->storeOid($store); 915 } else { 916 return false; 917 } 918 } 919 920 /** 921 * How many more attachments are allowed? 922 * 923 * @return mixed Returns true if no attachment limit. 924 * Else returns the number of additional attachments 925 * allowed. 926 */ 927 function additionalAttachmentsAllowed() 928 { 929 global $conf; 930 931 if (!empty($conf['compose']['attach_count_limit'])) { 932 return $conf['compose']['attach_count_limit'] - $this->numberOfAttachments(); 933 } else { 934 return true; 935 } 936 } 937 938 /** 939 * What is the maximum attachment size allowed? 940 * 941 * @return integer The maximum attachment size allowed (in bytes). 942 */ 943 function maxAttachmentSize() 944 { 945 global $conf, $imp; 946 947 $size = $imp['file_upload']; 948 949 if (!empty($conf['compose']['attach_size_limit'])) { 950 $size = min($size, max($conf['compose']['attach_size_limit'] - $this->sizeOfAttachments(), 0)); 951 } 952 953 return $size; 954 } 955 956 /** 957 * Adds the attachments to the message (in the case of a forward with 958 * attachments). 959 * This function MUST be called after IMP_Compose::forwardMessage(). 960 * 961 * @param IMP_Contents &$contents An IMP_Contents object. 962 * 963 * @return array An array of PEAR_Error object on error. 964 * An empty array if successful. 965 */ 966 function attachFilesFromMessage(&$contents) 967 { 968 $errors = array(); 969 970 $dl_list = $contents->getDownloadAllList(true); 971 $mime_message = $contents->getMIMEMessage(); 972 973 foreach ($dl_list as $val) { 974 if (is_null($this->_mimeid) || ($val != $this->_mimeid)) { 975 $mime = $mime_message->getPart($val); 976 if (!empty($mime)) { 977 $res = $this->addMIMEPartAttachment($mime); 978 if (is_a($res, 'PEAR_Error')) { 979 $errors[] = $res; 980 } 981 } 982 } 983 } 984 985 return $errors; 986 } 987 988 /** 989 * Convert a text/html MIME_Part message with embedded image links to 990 * a multipart/related MIME_Part with the image data embedded in the part. 991 * 992 * @param MIME_Part $mime_part The text/html MIME_Part object. 993 * 994 * @return MIME_Part The modified MIME_Part. 995 */ 996 function convertToMultipartRelated($mime_part) 997 { 998 /* Return immediately if HTTP_Request is not available. */ 999 $inc = include_once 'HTTP/Request.php'; 1000 if ($inc === false) { 1001 return $mime_part; 1002 } 1003 1004 /* Return immediately if not an HTML part. */ 1005 if ($mime_part->getType() != 'text/html') { 1006 return $mime_part; 1007 } 1008 1009 /* Scan for 'img' tags - specifically the 'src' parameter. If 1010 * none, return the original MIME_Part. */ 1011 if (!preg_match_all('/<img[^>]+src\s*\=\s*([^\s]+)\s+/iU', $mime_part->getContents(), $results)) { 1012 return $mime_part; 1013 } 1014 1015 /* Go through list of results, download the image, and create 1016 * MIME_Part objects with the data. */ 1017 $img_data = array(); 1018 $img_parts = array(); 1019 foreach ($results[1] as $url) { 1020 /* Strip any quotation marks and convert '&' to '&' (since 1021 * HTTP_Request doesn't handle the former correctly). */ 1022 $img_url = str_replace('&', '&', trim($url, '"\'')); 1023 1024 /* Attempt to download the image data. */ 1025 $request = &new HTTP_Request($img_url, array('timeout' => 5)); 1026 $request->sendRequest(); 1027 1028 if ($request->getResponseCode() == '200') { 1029 /* We need to determine the image type. Try getting 1030 * that information from the returned HTTP 1031 * content-type header. TODO: Use MIME_Magic if this 1032 * fails (?) */ 1033 $part = &new MIME_Part($request->getResponseHeader('content-type'), $request->getResponseBody(), null, 'attachment', '8bit'); 1034 $img_data[$url] = '"cid:' . $part->setContentID() . '"'; 1035 $img_parts[] = $part; 1036 } 1037 } 1038 1039 /* If we could not successfully download any data, return the 1040 * original MIME_Part now. */ 1041 if (empty($img_data)) { 1042 return $mime_part; 1043 } 1044 1045 /* Replace the URLs with with CID tags. */ 1046 $text = $mime_part->getContents(); 1047 $text = str_replace(array_keys($img_data), array_values($img_data), $text); 1048 $mime_part->setContents($text); 1049 1050 /* Create new multipart/related part. */ 1051 $related = &new MIME_Part('multipart/related'); 1052 1053 /* Get the CID for the 'root' part. Although by default the 1054 * first part is the root part (RFC 2387 [3.2]), we may as 1055 * well be explicit and put the CID in the 'start' 1056 * parameter. */ 1057 $related->setContentTypeParameter('start', $mime_part->setContentID()); 1058 1059 /* Add the root part and the various images to the multipart 1060 * object. */ 1061 $related->addPart($mime_part); 1062 foreach ($img_parts as $val) { 1063 $related->addPart($val); 1064 } 1065 1066 return $related; 1067 } 1068 1069 /** 1070 * Remove all attachments from an email message and replace with 1071 * urls to downloadable links. Should properly save all 1072 * attachments to a new folder and remove the MIME_Parts for the 1073 * attachments. 1074 * 1075 * @param string $baseurl The base URL for creating the links. 1076 * @param MIME_Part $base_part The body of the message. 1077 * @param string $auth The authorized user who owns the attachments. 1078 * 1079 * @return MIME_Part Modified part with links to attachments. Returns 1080 * PEAR_Error on error. 1081 */ 1082 function linkAttachments($baseurl, $base_part, $auth) 1083 { 1084 global $conf, $prefs; 1085 1086 if (!$conf['compose']['link_attachments']) { 1087 return PEAR::raiseError(_("Linked attachments are forbidden.")); 1088 } 1089 1090 require_once 'VFS.php'; 1091 $vfs = &VFS::singleton($conf['vfs']['type'], Horde::getDriverConfig('vfs', $conf['vfs']['type'])); 1092 1093 $ts = gmmktime(); 1094 $fullpath = sprintf('%s/%s/%d', IMP_VFS_LINK_ATTACH_PATH, $auth, $ts); 1095 1096 $trailer = String::convertCharset(_("Attachments"), NLS::getCharset(), $base_part->getCharset()); 1097 1098 if ($prefs->getValue('delete_attachments_monthly')) { 1099 /* Determine the first day of the month in which the current 1100 * attachments will be ripe for deletion, then subtract 1 second 1101 * to obtain the last day of the previous month. */ 1102 $del_time = gmmktime(0, 0, 0, date('n') + $prefs->getValue('delete_attachments_monthly_keep') + 1, 1, date('Y')) - 1; 1103 $trailer .= String::convertCharset(' (' . sprintf(_("Links will expire on %s"), strftime('%x', $del_time)) . ')', NLS::getCharset(), $base_part->getCharset()); 1104 } 1105 1106 foreach ($this->_cache as $att) { 1107 $trailer .= "\n" . Util::addParameter($baseurl, array('u' => $auth, 1108 't' => $ts, 1109 'f' => $att->getName()), 1110 null, false); 1111 if ($conf['compose']['use_vfs']) { 1112 $res = $vfs->rename(IMP_VFS_ATTACH_PATH, $att->getInformation('temp_filename'), $fullpath, escapeshellcmd($att->getName())); 1113 } else { 1114 $data = file_get_contents($att->getInformation('temp_filename')); 1115 $res = $vfs->writeData($fullpath, escapeshellcmd($att->getName()), $data, true); 1116 } 1117 if (is_a($res, 'PEAR_Error')) { 1118 return $res; 1119 } 1120 } 1121 1122 $this->deleteAllAttachments(); 1123 1124 if ($base_part->getPrimaryType() == 'multipart') { 1125 $mixed_part = &new MIME_Part('multipart/mixed'); 1126 $mixed_part->addPart($base_part); 1127 $link_part = &new MIME_Part('text/plain', $trailer, $base_part->getCharset(), 'inline', $base_part->getCurrentEncoding()); 1128 $link_part->setDescription(_("Attachment Information")); 1129 $mixed_part->addPart($link_part); 1130 return $mixed_part; 1131 } else { 1132 $base_part->appendContents("\n-----\n" . $trailer, $base_part->getCurrentEncoding()); 1133 return $base_part; 1134 } 1135 1136 } 1137 1138 }
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
Généré le : Thu Nov 29 12:30:07 2007 | par Balluche grâce à PHPXref 0.7 |
![]() |