📄 maildir.php
字号:
}
}
/**
* set flags for message
*
* NOTE: this method can't set the recent flag.
*
* @param int $id number of message
* @param array $flags new flags for message
* @throws Zend_Mail_Storage_Exception
*/
public function setFlags($id, $flags)
{
$info = $this->_getInfoString($flags);
$filedata = $this->_getFileData($id);
// TODO: move file from new to cur
$new_filename = dirname($filedata['filename']) . DIRECTORY_SEPARATOR . "$filedata[uniq]$info";
if (!@rename($filedata['filename'], $new_filename)) {
/**
* @see Zend_Mail_Storage_Exception
*/
require_once 'Zend/Mail/Storage/Exception.php';
throw new Zend_Mail_Storage_Exception('cannot rename file');
}
$filedata['flags'] = $flags;
$filedata['filename'] = $new_filename;
$this->_files[$id - 1] = $filedata;
}
/**
* stub for not supported message deletion
*
* @return null
* @throws Zend_Mail_Storage_Exception
*/
public function removeMessage($id)
{
$filename = $this->_getFileData($id, 'filename');
if ($this->_quota) {
$size = filesize($filename);
}
if (!@unlink($filename)) {
/**
* @see Zend_Mail_Storage_Exception
*/
require_once 'Zend/Mail/Storage/Exception.php';
throw new Zend_Mail_Storage_Exception('cannot remove message');
}
unset($this->_files[$id - 1]);
// remove the gap
$this->_files = array_values($this->_files);
if ($this->_quota) {
$this->_addQuotaEntry(0 - (int)$size, -1);
}
}
/**
* enable/disable quota and set a quota value if wanted or needed
*
* You can enable/disable quota with true/false. If you don't have
* a MDA or want to enforce a quota value you can also set this value
* here. Use array('size' => SIZE_QUOTA, 'count' => MAX_MESSAGE) do
* define your quota. Order of these fields does matter!
*
* @param bool|array $value new quota value
* @return null
*/
public function setQuota($value) {
$this->_quota = $value;
}
/**
* get currently set quota
*
* @see Zend_Mail_Storage_Writable_Maildir::setQuota()
*
* @return bool|array
*/
public function getQuota($fromStorage = false) {
if ($fromStorage) {
$fh = @fopen($this->_rootdir . 'maildirsize', 'r');
if (!$fh) {
/**
* @see Zend_Mail_Storage_Exception
*/
require_once 'Zend/Mail/Storage/Exception.php';
throw new Zend_Mail_Storage_Exception('cannot open maildirsize');
}
$definition = fgets($fh);
fclose($fh);
$definition = explode(',', trim($definition));
$quota = array();
foreach ($definition as $member) {
$key = $member[strlen($member) - 1];
if ($key == 'S' || $key == 'C') {
$key = $key == 'C' ? 'count' : 'size';
}
$quota[$key] = substr($member, 0, -1);
}
return $quota;
}
return $this->_quota;
}
/**
* @see http://www.inter7.com/courierimap/README.maildirquota.html "Calculating maildirsize"
*/
protected function _calculateMaildirsize() {
$timestamps = array();
$messages = 0;
$total_size = 0;
if (is_array($this->_quota)) {
$quota = $this->_quota;
} else {
try {
$quota = $this->getQuota(true);
} catch (Zend_Mail_Storage_Exception $e) {
throw new Zend_Mail_Storage_Exception('no quota defintion found');
}
}
$folders = new RecursiveIteratorIterator($this->getFolders(), RecursiveIteratorIterator::SELF_FIRST);
foreach ($folders as $folder) {
$subdir = $folder->getGlobalName();
if ($subdir == 'INBOX') {
$subdir = '';
} else {
$subdir = '.' . $subdir;
}
if ($subdir == 'Trash') {
continue;
}
foreach (array('cur', 'new') as $subsubdir) {
$dirname = $this->_rootdir . $subdir . DIRECTORY_SEPARATOR . $subsubdir . DIRECTORY_SEPARATOR;
if (!file_exists($dirname)) {
continue;
}
// NOTE: we are using mtime instead of "the latest timestamp". The latest would be atime
// and as we are accessing the directory it would make the whole calculation useless.
$timestamps[$dirname] = filemtime($dirname);
$dh = opendir($dirname);
// NOTE: Should have been checked in constructor. Not throwing an exception here, quotas will
// therefore not be fully enforeced, but next request will fail anyway, if problem persists.
if (!$dh) {
continue;
}
while (($entry = readdir()) !== false) {
if ($entry[0] == '.' || !is_file($dirname . $entry)) {
continue;
}
if (strpos($entry, ',S=')) {
strtok($entry, '=');
$filesize = strtok(':');
if (is_numeric($filesize)) {
$total_size += $filesize;
++$messages;
continue;
}
}
$size = filesize($dirname . $entry);
if ($size === false) {
// ignore, as we assume file got removed
continue;
}
$total_size += $size;
++$messages;
}
}
}
$tmp = $this->_createTmpFile();
$fh = $tmp['handle'];
$definition = array();
foreach ($quota as $type => $value) {
if ($type == 'size' || $type == 'count') {
$type = $type == 'count' ? 'C' : 'S';
}
$definition[] = $value . $type;
}
$definition = implode(',', $definition);
fputs($fh, "$definition\n");
fputs($fh, "$total_size $messages\n");
fclose($fh);
rename($tmp['filename'], $this->_rootdir . 'maildirsize');
foreach ($timestamps as $dir => $timestamp) {
if ($timestamp < filemtime($dir)) {
unlink($this->_rootdir . 'maildirsize');
break;
}
}
return array('size' => $total_size, 'count' => $messages, 'quota' => $quota);
}
/**
* @see http://www.inter7.com/courierimap/README.maildirquota.html "Calculating the quota for a Maildir++"
*/
protected function _calculateQuota($forceRecalc = false) {
$fh = null;
$total_size = 0;
$messages = 0;
$maildirsize = '';
if (!$forceRecalc && file_exists($this->_rootdir . 'maildirsize') && filesize($this->_rootdir . 'maildirsize') < 5120) {
$fh = fopen($this->_rootdir . 'maildirsize', 'r');
}
if ($fh) {
$maildirsize = fread($fh, 5120);
if (strlen($maildirsize) >= 5120) {
fclose($fh);
$fh = null;
$maildirsize = '';
}
}
if (!$fh) {
$result = $this->_calculateMaildirsize();
$total_size = $result['size'];
$messages = $result['count'];
$quota = $result['quota'];
} else {
$maildirsize = explode("\n", $maildirsize);
if (is_array($this->_quota)) {
$quota = $this->_quota;
} else {
$definition = explode(',', $maildirsize[0]);
$quota = array();
foreach ($definition as $member) {
$key = $member[strlen($member) - 1];
if ($key == 'S' || $key == 'C') {
$key = $key == 'C' ? 'count' : 'size';
}
$quota[$key] = substr($member, 0, -1);
}
}
unset($maildirsize[0]);
foreach ($maildirsize as $line) {
list($size, $count) = explode(' ', trim($line));
$total_size += $size;
$messages += $count;
}
}
$over_quota = false;
$over_quota = $over_quota || (isset($quota['size']) && $total_size > $quota['size']);
$over_quota = $over_quota || (isset($quota['count']) && $messages > $quota['count']);
// NOTE: $maildirsize equals false if it wasn't set (AKA we recalculated) or it's only
// one line, because $maildirsize[0] gets unsetted.
// Also we're using local time to calculate the 15 minute offset. Touching a file just for known the
// local time of the file storage isn't worth the hassle.
if ($over_quota && ($maildirsize || filemtime($this->_rootdir . 'maildirsize') > time() - 900)) {
$result = $this->_calculateMaildirsize();
$total_size = $result['size'];
$messages = $result['count'];
$quota = $result['quota'];
$over_quota = false;
$over_quota = $over_quota || (isset($quota['size']) && $total_size > $quota['size']);
$over_quota = $over_quota || (isset($quota['count']) && $messages > $quota['count']);
}
if ($fh) {
// TODO is there a safe way to keep the handle open for writing?
fclose($fh);
}
return array('size' => $total_size, 'count' => $messages, 'quota' => $quota, 'over_quota' => $over_quota);
}
protected function _addQuotaEntry($size, $count = 1) {
if (!file_exists($this->_rootdir . 'maildirsize')) {
// TODO: should get file handler from _calculateQuota
}
$size = (int)$size;
$count = (int)$count;
file_put_contents($this->_rootdir . 'maildirsize', "$size $count\n", FILE_APPEND);
}
/**
* check if storage is currently over quota
*
* @param bool $detailedResponse return known data of quota and current size and message count @see _calculateQuota()
* @return bool|array over quota state or detailed response
*/
public function checkQuota($detailedResponse = false, $forceRecalc = false) {
$result = $this->_calculateQuota($forceRecalc);
return $detailedResponse ? $result : $result['over_quota'];
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -