#! /usr/bin/env php

<?php
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2008 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
//
// BOINC 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with BOINC.  If not, see <http://www.gnu.org/licenses/>.

// remind.php [--lapsed | --failed] [--show_email] [--userid ID]
//
// --lapsed
//      Send emails to lapsed user (see below)
// --failed
//      Send emails to failed user (see below)
// --userid
//      Send both "lapsed" and "failed" emails to the given user,
//      regardless of whether they are due to be sent.
//      The --lapsed and --failed options are ignored.
//      (for testing)
// --show_email
//      Show the text that would be mailed
// --explain
//      Show which users would be sent email and why
// --send
//      Actually send emails (this is an option to encourage
//      you to do thorough testing before using it)
// --count N
//      By default send to all users that qualify, but if count is
//      set, only send to N users at a time
//
// This program sends "reminder" emails to
// - failed users: those who
//   1) were created at least $start_interval seconds ago,
//   2) have zero total credit
//   3) haven't been sent an email in at least $email_interval seconds.
//   These people typically either had a technical glitch,
//   or their prefs didn't allow sending them work,
//   or the app crashed on their host.
//   The email should direct them to a web page that helps
//   them fix the startup problem.
//
//   Set $start_interval according to your project's delay bounds
//   e.g. (1 or 2 weeks).
///  $email_interval should be roughly 1 month -
//   we don't want to bother people too often.
//
// - lapsed users: those who
//   1) have positive total credit,
//   2) haven't done a scheduler RPC within the past
//      $lapsed_interval seconds, and
//   3) haven't been sent an email in at least $email_interval seconds.
//   The email should gently prod them to start running the project again.
//

$globals->start_interval = 14*86400;
$globals->email_interval = 200*86400;
$globals->lapsed_interval = 60*86400;
$globals->do_failed = false;
$globals->do_lapsed = false;
$globals->show_email = false;
$globals->send = false;
$globals->explain = false;
$globals->userid = 0;
$globals->count = -1;

for ($i=1; $i<$argc; $i++) {
    if ($argv[$i] == "--failed") {
        $globals->do_failed = true;
    } elseif ($argv[$i] == "--lapsed") {
        $globals->do_lapsed = true;
    } elseif ($argv[$i] == "--show_email") {
        $globals->show_email = true;
    } elseif ($argv[$i] == "--explain") {
        $globals->explain = true;
    } elseif ($argv[$i] == "--send") {
        $globals->send = true;
    } elseif ($argv[$i] == "--userid") {
        $i++;
        $globals->userid = $argv[$i];
    } elseif ($argv[$i] == "--count") {
        $i++;
        $globals->count = $argv[$i];
    } else {
        echo "unrecognized option $argv[$i]\n";
        echo "usage: remind.php [--failed ] [--lapsed] [--userid N] [--show_mail] [--explain] [--send] [--count N]\n";
        exit (1);
    }
}

require_once('../project/project.inc');
require_once("../inc/db.inc");
require_once("../inc/email.inc");

db_init();
set_time_limit(0);

// File names for the various mail types.
// Change these here if needed.
//
$dir = "remind_email";
$failed_html = "$dir/reminder_failed_html";
$failed_text = "$dir/reminder_failed_text";
$failed_subject = "$dir/reminder_failed_subject";
$lapsed_html = "$dir/reminder_lapsed_html";
$lapsed_text = "$dir/reminder_lapsed_text";
$lapsed_subject = "$dir/reminder_lapsed_subject";

// return time of last scheduler RPC from this user,
// or zero if they're never done one
//
function last_rpc_time($user) {
    $x = 0;
    $result = mysql_query("select rpc_time from host where userid=$user->id");
    while ($host = mysql_fetch_object($result)) {
        if ($host->rpc_time > $x) $x = $host->rpc_time;
    }
    mysql_free_result($result);
    return $x;
}

function read_files(&$item) {
    $item['html'] = @file_get_contents($item['html_file']);
    if (!$item['html']) {
        //$x = $item['html_file'];
        //echo "file missing: $x\n";
        //exit();
    }
    $item['text'] = @file_get_contents($item['text_file']);
    if (!$item['text']) {
        $x = $item['text_file'];
        echo "file missing: $x\n";
        exit();
    }
    $item['subject'] = @file_get_contents($item['subject']);
    if (!$item['subject']) {
        $x = $item['subject'];
        echo "file missing: $x\n";
        exit();
    }
}

function read_email_files() {
    global $failed_html;
    global $failed_text;
    global $failed_subject;
    global $lapsed_html;
    global $lapsed_text;
    global $lapsed_subject;

    $failed['html_file'] = $failed_html;
    $failed['text_file'] = $failed_text;
    $failed['subject'] = $failed_subject;
    $lapsed['html_file'] = $lapsed_html;
    $lapsed['text_file'] = $lapsed_text;
    $lapsed['subject'] = $lapsed_subject;
    read_files($failed);
    read_files($lapsed);
    $email_files['failed'] = $failed;
    $email_files['lapsed'] = $lapsed;
    return $email_files;
}

function replace($user, $template) {
    $pat = array(
        '/<name\/>/',
        '/<email\/>/',
        '/<create_time\/>/',
        '/<total_credit\/>/',
        '/<opt_out_url\/>/',
        '/<user_id\/>/',
        '/<lapsed_interval\/>/',
    );
    $rep = array(
        $user->name,
        $user->email_addr,
        gmdate('d F Y', $user->create_time),
        number_format($user->total_credit, 0),
        URL_BASE."opt_out.php?code=".salted_key($user->authenticator)."&userid=$user->id",
        $user->id,
        floor ((time() - last_rpc_time($user)) / 86400),
    );
    return preg_replace($pat, $rep, $template);
}

function mail_type($user, $type) {
    global $globals;
    global $email_files;

    $email_file = $email_files[$type];
    if ($email_file['html']) {
        $html = replace($user, $email_file['html']);
    } else {
        $html = null;
    }
    $text = replace($user, $email_file['text']);
    if ($globals->show_email) {
        echo "------- SUBJECT ----------\n";
        echo $email_file['subject'];
        echo "\n------- HTML ----------\n";
        echo $html;
        echo "\n------- TEXT ----------\n";
        echo $text;
    }
    if ($globals->send) {
        echo "sending to $user->email_addr\n";
        echo send_email(
            $user,
            $email_file['subject'],
            $text,
            $html
        );
        $now = time();
        $ntype = 0;
        if ($type == 'lapsed') $ntype = 2;
        if ($type == 'failed') $ntype = 3;
        $query = "insert into sent_email values($user->id, $now, $ntype)";
        mysql_query($query);
    }
    $globals->count--;
    if ($globals->count == 0) { 
        echo "reached limit set by --count - exiting...\n";
        exit();
    }
}

function last_reminder_time($user) {
    $query = "select * from sent_email where userid=$user->id";
    $result = mysql_query($query);
    $t = 0;
    while ($r = mysql_fetch_object($result)) {
        if ($r->email_type !=2 && $r->email_type != 3) continue;
        if ($r->time_sent > $t) $t = $r->time_sent;

    }
    mysql_free_result($result);
    return $t;
}

function handle_user($user, $do_type) {
    global $globals;
    global $email_interval;

    if ($user->send_email == 0) {
        if ($globals->explain) {
            echo "user: $user->id send_email = 0\n";
        }
        return;
    }
    $max_email_time = time() - $globals->email_interval;
    if (last_reminder_time($user) > $max_email_time) {
        if ($globals->explain) {
            echo "user: $user->id sent too recently\n";
        }
        return;
    }
    if ($globals->explain) {
        $x = (time() - $user->create_time)/86400;
        $t = last_rpc_time($user);
        $show_lapsed_interval = (time()-$t)/86400;
        echo "user $user->id ($user->email_addr) was created $x days ago\n";
        echo "  total_credit: $user->total_credit; last RPC $show_lapsed_interval days ago\n";
        echo "  sending $do_type email\n";
    }
    mail_type($user, $do_type);
}

function do_failed() {
    global $globals;

    $max_create_time = time() - $globals->start_interval;
    $result = mysql_query(
        "select * from user where send_email<>0 and create_time<$max_create_time and total_credit = 0;"
    );
    while ($user = mysql_fetch_object($result)) {
        handle_user($user, 'failed');
    }
    mysql_free_result($result);
}

function do_lapsed() {
    global $globals;
    $max_last_rpc_time = time() - $globals->lapsed_interval;

    // the following is an efficient way of getting the list of
    // users for which no host has done an RPC recently
    //
    $result = mysql_query(
        "select userid from host group by userid having max(rpc_time)<$max_last_rpc_time;"
    );
    while ($host = mysql_fetch_object($result)) {
        $uresult = mysql_query("select * from user where id = $host->userid;");
        $user = mysql_fetch_object($uresult);
        mysql_free_result($uresult);
        if (!$user) {
            echo "Can't find user $host->userid\n";
            continue;
        }
        handle_user($user, 'lapsed');
    }
    mysql_free_result($result);
}

if (!$USE_PHPMAILER) {
    echo "You must use PHPMailer (http://phpmailer.sourceforge.net)\n";
    exit();
}

$email_files = read_email_files();

if ($globals->userid) {
    $user = lookup_user_id($globals->userid);
    if (!$user) {
        echo "No such user: $globals->userid\n";
        exit();
    }
    $user->last_rpc_time = last_rpc_time($user);
    mail_type($user, 'failed');
    mail_type($user, 'lapsed');
} else {
    if ($globals->do_failed) {
        do_failed();
    }
    if ($globals->do_lapsed) {
        do_lapsed();
    }
}

?>
