<?php

/**
 * dmarc-srg - A php parser, viewer and summary report generator for incoming DMARC reports.
 * Copyright (C) 2020-2024 Aleksey Andreev (liuch)
 *
 * Available at:
 * https://github.com/liuch/dmarc-srg
 *
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation, either version 3 of the License.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of  MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * =========================
 *
 * This script deletes old DMARC report email messages in mailboxes.
 * The mailbox parameters and conditions for removal must be specified
 * in the configuration file. The mailbox accessibility can be checked
 * on the administration page in the web interface.
 * The best place to use it is cron.
 *
 * @category Utilities
 * @package  DmarcSrg
 * @author   Aleksey Andreev (liuch)
 * @license  https://www.gnu.org/licenses/gpl-3.0.html GNU/GPLv3
 */

namespace Liuch\DmarcSrg;

use Liuch\DmarcSrg\Mail\MailBox;
use Liuch\DmarcSrg\Mail\MailBoxes;
use Liuch\DmarcSrg\Sources\SourceAction;
use Liuch\DmarcSrg\Exception\RuntimeException;

require realpath(__DIR__ . '/..') . '/init.php';

if (Core::isWEB()) {
    echo 'Forbidden';
    exit(1);
}

$core = Core::instance();
$core->setCurrentUser('admin');

$days = $core->config('cleaner/mailboxes/days_old', -1);
if (gettype($days) !== 'integer' || $days < 0) {
    exit(0);
}
$days_date = (new DateTime())->sub(new \DateInterval("P{$days}D"));

$maximum = $core->config('cleaner/mailboxes/delete_maximum', 0);
if (gettype($maximum) !== 'integer' || $maximum < 0) {
    exit(0);
}

$leave = $core->config('cleaner/mailboxes/leave_minimum', 0);
if (gettype($leave) !== 'integer' || $leave < 0) {
    exit(0);
}

// Get a list of the configured email boxes
$mb_list = new MailBoxes();
$mb_cnt  = $mb_list->count();
if ($mb_cnt === 0) {
    // There are no configured mailboxes
    exit(0);
}

// Get the names of mailboxes where processed messages are moved to.
$dirs = [];
foreach ([ [ 'done', '', 1 ], [ 'failed', 'failed', 0 ] ] as $it) {
    $opt_nm  = $it[0];
    $def_opt = $it[1];
    $def_cri = $it[2];
    $actions = SourceAction::fromSetting($core->config("fetcher/mailboxes/when_{$opt_nm}", ''), 0, '');
    if (count($actions) === 0) {
        $dir = $def_opt;
    } else {
        $dir = null;
        foreach ($actions as $act) {
            if ($act->type === SourceAction::ACTION_MOVE) {
                $dir = $act->param;
                break;
            } elseif ($act->type === SourceAction::ACTION_SEEN) {
                $dir = $def_opt;
            }
        }
        if (is_null($dir)) {
            continue;
        }
    }
    switch (strtolower($core->config("cleaner/mailboxes/{$opt_nm}", ''))) {
        case 'any':
            $cri = 2;
            break;
        case 'seen':
            $cri = 1;
            break;
        case '':
            $cri = $def_cri;
            break;
        default:
            $cri = 0;
            break;
    }
    if (empty($dir) && $cri > 1) {
        $cri = 1;
    }
    $dirs[$dir] = min(($dirs[$dir] ?? $cri), $cri);
}

try {
    for ($mb_idx = 1; $mb_idx <= $mb_cnt; ++$mb_idx) {
        foreach ($dirs as $dir_name => $i_criteria) {
            if ($i_criteria > 0) {
                $criteria = $i_criteria === 2 ? MailBox::SEARCH_ALL : MailBox::SEARCH_SEEN;
                $mbox = $mb_list->mailbox($mb_idx);
                if (!empty($dir_name)) {
                    if (!($mbox = $mbox->childMailbox($dir_name))) {
                        continue;
                    }
                }
                $s_res = $mbox->messages($criteria, MailBox::ORDER_ASCENT);
                $max = $maximum > 0 ? $maximum : -1;
                $lv = $leave === 0 ? count($s_res) : count($s_res) - $leave;
                foreach ($s_res as $msg) {
                    if ($lv-- <= 0) {
                        break;
                    }
                    $mo = $msg->overview();
                    if (isset($mo['date'])) {
                        if ($mo['date'] > $days_date) {
                            break;
                        }
                        $msg->delete();
                        if ($max === 1) {
                            break;
                        }
                        if ($max > 0) {
                            --$max;
                        }
                    }
                }
            }
        }
    }
} catch (RuntimeException $e) {
    echo ErrorHandler::exceptionText($e);
    exit(1);
}

exit(0);
