#!/usr/bin/env php
<?php
/**
 * Query/manage the contents of a user's cached IMAP data.
 *
 * Copyright 2010-2014 Horde LLC (http://www.horde.org/)
 *
 * See the enclosed file COPYING for license information (GPL). If you
 * did not receive this file, see http://www.horde.org/licenses/gpl.
 *
 * @author    Michael Slusarz <slusarz@horde.org>
 * @category  Horde
 * @copyright 2010-2014 Horde LLC
 * @license   http://www.horde.org/licenses/gpl GPL
 * @package   IMP
 */

$baseFile = __DIR__ . '/../lib/Application.php';
if (file_exists($baseFile)) {
    require_once $baseFile;
} else {
    require_once 'PEAR/Config.php';
    require_once PEAR_Config::singleton()
        ->get('horde_dir', null, 'pear.horde.org') . '/imp/lib/Application.php';
}
Horde_Registry::appInit('imp', array('cli' => true));

$parser = new Horde_Argv_Parser();
$parser->addOption('-l', '--lifetime', array(
    'dest' => 'lifetime',
    'help' => 'Purge lifetime (in seconds; default: ALL entries purged)',
    'type' => 'int'
));
$parser->addOption('-p', '--pass', array(
    'dest' => 'pass',
    'help' => 'Password (otherwise, is prompted; query mode)'
));
$parser->addOption('-P', '--purge', array(
    'action' => 'store_const',
    'const' => 1,
    'dest' => 'purge',
    'help' => 'Purge cache'
));
$parser->addOption('-s', '--server', array(
    'dest' => 'server',
    'help' => 'Server key (otherwise, is prompted)'
));
$parser->addOption('-u', '--user', array(
    'dest' => 'user',
    'help' => 'Username (otherwise, is prompted; query mode)'
));
list($values,) = $parser->parseArgs();

$imp_imap = $injector->getInstance('IMP_Factory_Imap')->create();

/* Set first entry to 1, not 0. */
$sconfig = $slookup = array('');
$i = 1;

foreach ($imp_imap->loadServerConfig() as $key => $val) {
    $sconfig[$i] = $val->name . ' [' . $key . ']';
    $slookup[$i++] = $key;
}
unset($sconfig[0]);

$server = $values->server;
while (is_null($server) || !in_array($server, $slookup)) {
    $tmp = $cli->prompt('Server:', $sconfig);
    if (isset($slookup[$tmp])) {
        $server = $slookup[$tmp];
    }
}
$cli->message('Server: ' . $server);

/* Purge cache? */
$action = $values->purge;
if (is_null($action)) {
    $opts = array(
        1 => 'Purge Cache',
        2 => 'Query Cache'
    );
    while (is_null($action)) {
        $action = $cli->prompt('Action:', $opts);
    }
}

if ($action == 1) {
    $config = $imp_imap->loadServerConfig($server)->cache_params;
    if (isset($config['backend'])) {
        $config['backend']->clear($values->lifetime);
        $cli->message('Cache cleared.');
    }
    exit;
}

$user = $values->user;
if (is_null($user)) {
    while (is_null($user)) {
        $user = $cli->prompt('Username:');
        if (!strlen($user)) {
            $user = null;
        }
    }
} else {
    $cli->message('Username: ' . $user);
}

$pass = $values->pass;
while (is_null($pass)) {
    $pass = $cli->passwordPrompt('Password:');
    if (!strlen($pass)) {
        $pass = null;
    }
}

$ob = $imp_imap->createBaseImapObject($user, $pass, $server);
if (!$ob) {
    $cli->fatal('Could not create Imap Client object.');
}

/* Make sure IMAP cache exists in the IMAP driver. */
$cache = $ob->getCache();
if (is_null($cache)) {
    $cli->fatal('Could not get cache object from IMAP driver.');
}

try {
    $ob->login();
    $cli->message('Successfully logged in to IMAP server.');

    $mboxes = $ob->listMailboxes('*', Horde_Imap_Client::MBOX_ALL, array('flat' => true, 'sort' => true));
    $cli->message('User mailbox count: ' . count($mboxes));
} catch (Horde_Imap_Client_Exception $e) {
    $cli->fatal('IMAP error: ' . $e);
}

$opts = array(
    1 => 'Summary Statistics (All Mailboxes)',
    2 => 'Detailed Statistics (All Mailboxes)',
    3 => 'Detailed Statistics (Single Mailbox)',
    4 => 'Summary Statistics (Single UID)',
    5 => 'Detailed Statistics (Single UID)',
    6 => 'Expire All Mailboxes',
    7 => 'Expire Mailbox',
    8 => 'Expire specific UIDs',
    0 => 'Exit'
);

while (true) {
    $cli->writeln();

    $action = $cli->prompt('Action:', $opts);
    switch ($action) {
    case 0:
        exit;

    case 1:
        $mbox_list = array();
        $msg_cnt = $search_cnt = 0;

        foreach (array_map('strval', $mboxes) as $val) {
            if ($res = $cache->get($val)) {
                $mbox_list[$val] = array(
                    'msgs' => count($res)
                );
                $msg_cnt += $mbox_list[$val]['msgs'];

                if ($res = $cache->getMetaData($val, null, array(Horde_Imap_Client_Base::CACHE_SEARCH))) {
                    $mbox_list[$val]['search'] = count($res[Horde_Imap_Client_Base::CACHE_SEARCH]);
                    $search_cnt += $mbox_list[$val]['search'];
                }
            }
        }

        $cli->writeln();
        $cli->message($cli->bold('Cached mailboxes:') . ' ' . count($mbox_list));
        $cli->message($cli->bold('Cached messages:') . ' ' . $msg_cnt);
        $cli->message($cli->bold('Cached searches:') . ' ' . $search_cnt);
        break;

    case 2:
    case 3:
        if ($action == 3) {
            $prompt = $cli->prompt('Mailbox:');
            if (!strlen($prompt)) {
                break;
            }
            $mbox_list = array($prompt);
        } else {
            $mbox_list = array_map('strval', $mboxes);
        }

        foreach ($mbox_list as $mbox) {
            if ($res = $cache->get($mbox)) {
                $cli->writeln();

                $indices = new IMP_Indices($mbox, $res);
                $uids = $indices->getSingle(true);

                $cli->message('Mailbox: ' . $cli->green($mbox));
                $cli->message('Cached messages: ' . count($res) . ' [' . $imp_imap->getIdsOb($uids[1])->tostring_sort . ']');

                $total_size = 0;
                foreach ($cache->get($mbox, $res, null) as $val) {
                    $data = serialize($val);
                    $total_size += strlen($data);
                }

                $cli->message('Approximate size: ' . IMP::sizeFormat($total_size));

                if ($res = $cache->getMetaData($mbox)) {
                    try {
                        $status = $ob->status($mbox, Horde_Imap_Client::STATUS_UIDVALIDITY | Horde_Imap_Client::STATUS_HIGHESTMODSEQ);
                    } catch (Horde_Imap_Client_Exception $e) {
                        $cli->writeln();
                        $cli->message('IMAP error: ' . $e, 'cli.error');
                    }
                    $cli->message(
                        'UIDVALIDITY: ' . $res['uidvalid'] .
                        (($status['uidvalidity'] != $res['uidvalid'])
                            ? ' [Server value: ' . $cli->red($status['uidvalidity']) . ']'
                            : '')
                    );
                    if (isset($res[Horde_Imap_Client_Base::CACHE_MODSEQ])) {
                        $cli->message(
                            'Highest MODSEQ seen: ' . $res[Horde_Imap_Client_Base::CACHE_MODSEQ] .
                            (($status['highestmodseq'] != $res[Horde_Imap_Client_Base::CACHE_MODSEQ])
                                ? ' [Server value: ' . $cli->red($status['highestmodseq']) . ']'
                                : '')
                        );
                    }
                    if (isset($res[Horde_Imap_Client_Base::CACHE_SEARCH])) {
                        $cli->message('Cached searches: ' . count($res[Horde_Imap_Client_Base::CACHE_SEARCH]));
                    }
                }
            } elseif ($action == 3) {
                $cli->writeln();
                $cli->message(sprintf('No cache information found for "%s".', $mbox), 'cli.error');
            }
        }
        break;

    case 4:
    case 5:
        $mbox = $cli->prompt('Mailbox:');
        if (!strlen($mbox)) {
            break;
        }
        $uid = $cli->prompt('UID:');
        if (!strlen($uid)) {
            break;
        }
        if ($res = $cache->get($mbox, array($uid), null)) {
            $cli->writeln();
            $cli->message(sprintf('Message information [%s:%d]', $mbox, $uid));
            $cli->message('Cached fields: ' . implode(', ', array_keys($res[$uid])));

            $data = serialize($res[$uid]);
            $msg_size = strlen($data);
            $cli->message('Approximate size: ' . IMP::sizeFormat($msg_size));

            if ($action == 5) {
                $cli->writeln();
                $cli->writeln(print_r($res[$uid], true));
            }
        } else {
            $cli->writeln();
            $cli->message(sprintf('No cache information found for "%s:%d".', $mbox, $uid), 'cli.error');
        }
        break;

    case 6:
    case 7:
        if ($action == 7) {
            $prompt = $cli->prompt('Mailbox:');
            if (!strlen($prompt)) {
                break;
            }
            $mbox_list = array($prompt);
        } else {
            $mbox_list = array_map('strval', $mboxes);
        }

        if ($cli->prompt('Delete mailbox cache(s)?', array('1' => 'No', '2' => 'Yes'), 1) == 2) {
            $cli->writeln();
            foreach ($mbox_list as $val) {
                $cache->deleteMailbox($val);
                $cli->message('Deleted cache: ' . $val, 'cli.success');
            }
        }
        break;

    case 8:
        $mbox = $cli->prompt('Mailbox:');
        if (!strlen($mbox)) {
            break;
        }
        $uids = $cli->prompt('UIDs (IMAP sequence string format):');
        if (!strlen($uids)) {
            break;
        }
        $uids = new IMP_Indices($uids);
        if (!count($uids)) {
            $cli->writeln();
            $cli->message('No UIDs found', 'cli.error');
            break;
        }

        $cli->writeln();

        try {
            $cache->deleteMsgs($mbox, $uids->getSingle(true));
            $cli->message(sprintf('Deleted %d UIDs from cache.', count($uids)), 'cli.success');
        } catch (Horde_Imap_Client_Exception $e) {
            $cli->writeln();
            $cli->message('Failed deleting UIDs. Error: ' . $e, 'cli.error');
        }
        break;
    }
}
