/** * Displays error messages, if there are errors in responses to * commands issues by sqimap_append() and sqimap_append_done() functions. * @param string $response * @param string $sMailbox * @return bool $bDone * @since 1.5.1 and 1.4.5 */ function sqimap_append_checkresponse($response, $sMailbox, $sid = '', $query = '') { // static vars to keep them available when sqimap_append_done calls this function. static $imapquery, $imapsid; $bDone = false; if ($query) { $imapquery = $query; } if ($sid) { $imapsid = $sid; } if ($response[0] == '+') { // continuation request triggerd by sqimap_append() $bDone = true; } else { $i = strpos($response, ' '); $sRsp = substr($response, 0, $i); $sMsg = substr($response, $i + 1); $aExtra = array('MAILBOX' => $sMailbox); switch ($sRsp) { case '*': //untagged response $i = strpos($sMsg, ' '); $sRsp = strtoupper(substr($sMsg, 0, $i)); $sMsg = substr($sMsg, $i + 1); if ($sRsp == 'NO' || $sRsp == 'BAD') { // for the moment disabled. Enable after 1.5.1 release. // Notices could give valueable information about the mailbox // Update: seems this was forgotten, but now it is finally enabled sqm_trigger_imap_error('SQM_IMAP_APPEND_NOTICE', $imapquery, $sRsp, $sMsg); } $bDone = false; case $imapsid: // A001 OK message // $imapsid<space>$sRsp<space>$sMsg $bDone = true; $i = strpos($sMsg, ' '); $sRsp = strtoupper(substr($sMsg, 0, $i)); $sMsg = substr($sMsg, $i + 1); switch ($sRsp) { case 'NO': if (preg_match("/(.*)(quota)(.*)\$/i", $sMsg, $aMatch)) { sqm_trigger_imap_error('SQM_IMAP_APPEND_QUOTA_ERROR', $imapquery, $sRsp, $sMsg, $aExtra); } else { sqm_trigger_imap_error('SQM_IMAP_APPEND_ERROR', $imapquery, $sRsp, $sMsg, $aExtra); } break; case 'BAD': sqm_trigger_imap_error('SQM_IMAP_ERROR', $imapquery, $sRsp, $sMsg, $aExtra); break; case 'BYE': sqm_trigger_imap_error('SQM_IMAP_BYE', $imapquery, $sRsp, $sMsg, $aExtra); break; case 'OK': break; default: break; } break; default: // should be false because of the unexpected response but i'm not sure if // that will cause an endless loop in sqimap_append_done $bDone = true; } } return $bDone; }
/** * Returns an array with each element as a string representing one * message-thread as returned by the IMAP server. * @param resource $imap_stream IMAP socket connection * @param string $search optional search string * @return array * @link http://www.ietf.org/internet-drafts/draft-ietf-imapext-sort-13.txt */ function get_thread_sort($imap_stream, $search = 'ALL') { global $sort_by_ref, $default_charset; if ($sort_by_ref == 1) { $sort_type = 'REFERENCES'; } else { $sort_type = 'ORDEREDSUBJECT'; } $query = "THREAD {$sort_type} " . strtoupper($default_charset) . " {$search}"; // TODO use sqimap_run_command_list as we do in get_server_sort() $sRead = sqimap_run_command($imap_stream, $query, false, $response, $message, TRUE); /* fallback to default charset */ if ($response == 'NO') { if (strpos($message, 'BADCHARSET') !== false || strpos($message, 'character') !== false) { sqm_trigger_imap_error('SQM_IMAP_BADCHARSET', $query, $response, $message); $query = "THREAD {$sort_type} US-ASCII {$search}"; $sRead = sqimap_run_command($imap_stream, $query, true, $response, $message, TRUE); } else { sqm_trigger_imap_error('SQM_IMAP_ERROR', $query, $response, $message); } } elseif ($response == 'BAD') { sqm_trigger_imap_error('SQM_IMAP_NO_THREAD', $query, $response, $message); } $sThreadResponse = ''; if (isset($sRead[0])) { for ($i = 0, $iCnt = count($sRead); $i < $iCnt; ++$i) { if (preg_match("/^\\* THREAD (.+)\$/", $sRead[$i], $aMatch)) { $sThreadResponse = trim($aMatch[1]); break; } } } unset($sRead); if ($response !== 'OK') { return false; } /* Example response * S: * THREAD (2)(3 6 (4 23)(44 7 96)) * -- 2 * * -- 3 * \-- 6 * |-- 4 * | \-- 23 * | * \-- 44 * \-- 7 * \-- 96 */ /* * Notes for future work: * indent_array should contain: indent_level, parent and flags, * sibling nodes .. * To achieve that we need to define the following flags: * 0: hasnochildren * 1: haschildren * 2: is first * 4: is last * a node has sibling nodes if it's not the last node * a node has no sibling nodes if it's the last node * By using binary comparations we can store the flag in one var * * example: * -1 par = 0, level = 0, flag = 1 + 2 + 4 = 7 (haschildren, isfirst, islast) * \-2 par = 1, level = 1, flag = 0 + 2 = 2 (hasnochildren, isfirst) * |-3 par = 1, level = 1, flag = 1 + 4 = 5 (haschildren, islast) * \-4 par = 3, level = 2, flag = 1 + 2 + 4 = 7 (haschildren, isfirst, islast) * \-5 par = 4, level = 3, flag = 0 + 2 + 4 = 6 (hasnochildren, isfirst, islast) */ $j = 0; $k = 0; $l = 0; $aUidThread = array(); $aIndent = array(); $aUidSubThread = array(); $aDepthStack = array(); $sUid = ''; if ($sThreadResponse) { for ($i = 0, $iCnt = strlen($sThreadResponse); $i < $iCnt; ++$i) { $cChar = $sThreadResponse[$i]; switch ($cChar) { case '(': // new sub thread // correction for a subthread of a thread with no parents in thread if (!count($aUidSubThread) && $j > 0) { --$l; } $aDepthStack[$j] = $l; ++$j; break; case ')': // close sub thread if ($sUid !== '') { $aUidSubThread[] = $sUid; $aIndent[$sUid] = $j + $l - 1; ++$l; $sUid = ''; } --$j; if ($j === 0) { // show message that starts the thread first. $aUidSubThread = array_reverse($aUidSubThread); // do not use array_merge because it's extremely slow and is causing timeouts foreach ($aUidSubThread as $iUid) { $aUidThread[] = $iUid; } $aUidSubThread = array(); $l = 0; $aDepthStack = array(); } else { $l = $aDepthStack[$j]; } break; case ' ': // new child if ($sUid !== '') { $aUidSubThread[] = $sUid; $aIndent[$sUid] = $j + $l - 1; ++$l; $sUid = ''; } break; default: // part of UID $sUid .= $cChar; break; } } } unset($sThreadResponse); // show newest threads first $aUidThread = array_reverse($aUidThread); return array($aUidThread, $aIndent); }
/** * Creates and runs the IMAP command to filter messages * @param string $imap_stream TODO: Document this parameter * @param string $where Which part of the message to search (TO, CC, SUBJECT, etc...) * @param string $what String to search for * @param string $where_to Folder it will move to * @param string $user_scan Whether to search all or just unseen * @param string $should_expunge * @access private */ function filter_search_and_delete($imap_stream, $where, $what, $where_to, $user_scan, $should_expunge) { global $languages, $squirrelmail_language, $allow_charset_search, $imap_server_type; //TODO: make use of new mailbox cache. See mailbox_display.phpinfo if (strtolower($where_to) == 'inbox') { return array(); } if ($user_scan == 'new') { $category = 'UNSEEN'; } else { $category = 'ALL'; } $category .= ' UNDELETED'; if ($allow_charset_search && isset($languages[$squirrelmail_language]['CHARSET']) && $languages[$squirrelmail_language]['CHARSET']) { $search_str = 'SEARCH CHARSET ' . strtoupper($languages[$squirrelmail_language]['CHARSET']) . ' ' . $category; } else { $search_str = 'SEARCH CHARSET US-ASCII ' . $category; } if ($where == 'Header') { $what = explode(':', $what); $where = strtoupper($where); $where = trim($where . ' ' . $what[0]); $what = addslashes(trim($what[1])); } // see comments in squirrelmail sqimap_search function if ($imap_server_type == 'macosx' || $imap_server_type == 'hmailserver') { $search_str .= ' ' . $where . ' ' . $what; /* read data back from IMAP */ $read = sqimap_run_command($imap_stream, $search_str, true, $response, $message, TRUE); } else { $search_str .= ' ' . $where . ' {' . strlen($what) . "}"; $sid = sqimap_session_id(true); fputs($imap_stream, $sid . ' ' . $search_str . "\r\n"); $read2 = sqimap_fgets($imap_stream); # server should respond with Ready for argument, then we will send search text #echo "RR2 $read2<br>"; fputs($imap_stream, "{$what}\r\n"); #echo "SS $what<br>"; $read2 = sqimap_fgets($imap_stream); #echo "RR2 $read2<br>"; $read[] = $read2; $read3 = sqimap_fgets($imap_stream); #echo "RR3 $read3<br>"; list($rtag, $response, $message) = explode(' ', $read3, 3); ## $read2 = sqimap_retrieve_imap_response($imap_stream, $sid, true, ## $response, $message, $search_str, false, true, false); #echo "RR2 $read2 / RESPONSE $response<br>"; } if (isset($read[0])) { $ids = array(); for ($i = 0, $iCnt = count($read); $i < $iCnt; ++$i) { if (preg_match("/^\\* SEARCH (.+)\$/", $read[$i], $regs)) { $ids += explode(' ', trim($regs[1])); } } if ($response == 'OK' && count($ids)) { if (sqimap_mailbox_exists($imap_stream, $where_to)) { if (!sqimap_msgs_list_move($imap_stream, $ids, $where_to, false)) { // if errors occurred, don't try to filter again during this session sqsession_register(TRUE, 'filters_error'); global $color; error_box(_("A problem occurred filtering messages. Check filter settings and account quota if applicable. Filtering is disabled for the remainder of this login session."), $color); } // expunge even in the case of errors, in case some // messages were filtered before the error happened $should_expunge = true; } } elseif ($response != 'OK') { $query = $search_str . "\r\n" . $what . "\r\n"; if ($response == 'NO') { if (strpos($message, 'BADCHARSET') !== false || strpos($message, 'character') !== false) { sqm_trigger_imap_error('SQM_IMAP_BADCHARSET', $query, $response, $message); } else { sqm_trigger_imap_error('SQM_IMAP_ERROR', $query, $response, $message); } } else { sqm_trigger_imap_error('SQM_IMAP_ERROR', $query, $response, $message); } } } return $should_expunge; }