* function mail - send the message parts to the SMTP stream
*
* @param Message $message Message object to send
- * @param resource $stream Handle to the SMTP stream
+ * NOTE that this is passed by
+ * reference and will be modified
+ * upon return with updated
+ * fields such as Message ID, References,
+ * In-Reply-To and Date headers.
+ * @param resource $stream Handle to the outgoing stream
* (when FALSE, nothing will be
* written to the stream; this can
* be used to determine the actual
* can be passed in here and used in
* an overloaded version of this method
* if needed.
+ * @param resource $imap_stream If there is an open IMAP stream in
+ * the caller's context, it should be
+ * passed in here. This is OPTIONAL,
+ * as one will be created if not given,
+ * but as some IMAP servers may baulk
+ * at opening more than one connection
+ * at a time, the caller should always
+ * abide if possible. Currently, this
+ * stream is only used when $reply_id
+ * is also non-zero, but that is subject
+ * to change.
+ *
+ * @return integer The number of bytes written (or that would have been
+ * written) to the output stream.
*
- * @return integer $raw_length The number of bytes written (or that would
- * have been written) to the output stream
*/
- function mail($message, $stream=false, $reply_id=0, $reply_ent_id=0,
- $extra=NULL) {
+ function mail(&$message, $stream=false, $reply_id=0, $reply_ent_id=0,
+ $extra=NULL, $imap_stream=NULL) {
+
+ $rfc822_header = &$message->rfc822_header;
- $rfc822_header = $message->rfc822_header;
if (count($message->entities)) {
$boundary = $this->mimeBoundary();
$rfc822_header->content_type->properties['boundary']='"'.$boundary.'"';
global $imapConnection, $username, $imapServerAddress,
$imapPort, $mailbox;
- if (!is_resource($imapConnection))
- $imapConnection = sqimap_login($username, FALSE,
+ // try our best to use an existing IMAP handle
+ //
+ $close_imap_stream = FALSE;
+ if (is_resource($imap_stream)) {
+ $my_imap_stream = $imap_stream;
+
+ } else if (is_resource($imapConnection)) {
+ $my_imap_stream = $imapConnection;
+
+ } else {
+ $close_imap_stream = TRUE;
+ $my_imap_stream = sqimap_login($username, FALSE,
$imapServerAddress, $imapPort, 0);
+ }
- sqimap_mailbox_select($imapConnection, $mailbox);
- $reply_message = sqimap_get_message($imapConnection, $reply_id, $mailbox);
+ sqimap_mailbox_select($my_imap_stream, $mailbox);
+ $reply_message = sqimap_get_message($my_imap_stream, $reply_id, $mailbox);
+
+ if ($close_imap_stream) {
+ sqimap_logout($my_imap_stream);
+ }
if ($reply_ent_id) {
/* redefine the messsage in case of message/rfc822 */
} else {
$orig_header = $reply_message->rfc822_header;
}
+ $message->reply_rfc822_header = $orig_header;
}
- $message->reply_rfc822_header = $orig_header;
$reply_rfc822_header = (isset($message->reply_rfc822_header)
} else {
if ($mime_header->type0 == 'text' || $mime_header->type0 == 'message') {
$header[] = 'Content-Transfer-Encoding: 8bit' . $rn;
+ } else if ($mime_header->type0 == 'multipart' || $mime_header->type0 == 'alternative') {
+ /* no-op; no encoding needed */
} else {
$header[] = 'Content-Transfer-Encoding: base64' . $rn;
}
*
* @return string $header
*/
- function prepareRFC822_Header($rfc822_header, $reply_rfc822_header, &$raw_length) {
+ function prepareRFC822_Header(&$rfc822_header, $reply_rfc822_header, &$raw_length) {
global $domain, $username, $encode_header_key,
$edit_identity, $hide_auth_header;
- /* if server var SERVER_NAME not available, use $domain */
- if(!sqGetGlobalVar('SERVER_NAME', $SERVER_NAME, SQ_SERVER)) {
+ /* if server var SERVER_NAME not available, or contains
+ ":" (e.g. IPv6) which is illegal in a Message-ID, use $domain */
+ if(!sqGetGlobalVar('SERVER_NAME', $SERVER_NAME, SQ_SERVER) ||
+ strpos($SERVER_NAME,':') !== FALSE) {
$SERVER_NAME = $domain;
}
/* This creates an RFC 822 date */
$date = date('D, j M Y H:i:s ', time()) . $this->timezone();
+
/* Create a message-id */
- $message_id = '<' . (!empty($REMOTE_PORT) ? $REMOTE_PORT . '.' : '');
-//FIXME: if $REMOTE_ADDR is missing, should we skip this if/else block? or perhaps try to generate it with some different kind of info?
- if (isset($encode_header_key) && trim($encode_header_key)!='') {
- // use encrypted form of remote address
- $message_id.= OneTimePadEncrypt($this->ip2hex($REMOTE_ADDR),base64_encode($encode_header_key));
- } else {
- $message_id.= $REMOTE_ADDR;
+ $message_id = 'MESSAGE ID GENERATION ERROR! PLEASE CONTACT SQUIRRELMAIL DEVELOPERS';
+ if (empty($rfc822_header->message_id)) {
+ $message_id = '<';
+ /* user-specifc data to decrease collision chance */
+ $seed_data = $username . '.';
+ $seed_data .= (!empty($REMOTE_PORT) ? $REMOTE_PORT . '.' : '');
+ $seed_data .= (!empty($REMOTE_ADDR) ? $REMOTE_ADDR . '.' : '');
+ /* add the current time in milliseconds and randomness */
+ $seed_data .= uniqid(mt_rand(),true);
+ /* put it through one-way hash and add it to the ID */
+ $message_id .= md5($seed_data) . '.squirrel@' . $SERVER_NAME .'>';
}
- $message_id .= '.' . time() . '.squirrel@' . $SERVER_NAME .'>';
+
/* Make an RFC822 Received: line */
if (isset($REMOTE_HOST)) {
$received_from = "$REMOTE_HOST ([$REMOTE_ADDR])";
*/
$show_sm_header = ( defined('hide_squirrelmail_header') ? ! hide_squirrelmail_header : 1 );
+ // FIXME: The following headers may generate slightly differently between the message sent to the destination and that stored in the Sent folder because this code will be called before both actions. This is not necessarily a big problem, but other headers such as Message-ID and Date are preserved between both actions
if ( $show_sm_header ) {
if (isset($encode_header_key) &&
trim($encode_header_key)!='') {
}
/* Insert the rest of the header fields */
- $header[] = 'Message-ID: '. $message_id . $rn;
+
+ if (!empty($rfc822_header->message_id)) {
+ $header[] = 'Message-ID: '. $rfc822_header->message_id . $rn;
+ } else {
+ $header[] = 'Message-ID: '. $message_id . $rn;
+ $rfc822_header->message_id = $message_id;
+ }
+
if (is_object($reply_rfc822_header) &&
isset($reply_rfc822_header->message_id) &&
$reply_rfc822_header->message_id) {
$rep_message_id = $reply_rfc822_header->message_id;
- // $this->strip_crlf($message_id);
$header[] = 'In-Reply-To: '.$rep_message_id . $rn;
+ $rfc822_header->in_reply_to = $rep_message_id;
$references = $this->calculate_references($reply_rfc822_header);
$header[] = 'References: '.$references . $rn;
+ $rfc822_header->references = $references;
}
- $header[] = "Date: $date" . $rn;
+
+ if (!empty($rfc822_header->date) && $rfc822_header->date != -1) {
+ $header[] = 'Date: '. $rfc822_header->date . $rn;
+ } else {
+ $header[] = "Date: $date" . $rn;
+ $rfc822_header->date = $date;
+ }
+
$header[] = 'Subject: '.encodeHeader($rfc822_header->subject) . $rn;
$header[] = 'From: '. $rfc822_header->getAddr_s('from',",$rn ",true) . $rn;