/
ColdCore-3.0a8/
ColdCore-3.0a8/src/
new object $mail_ui: $mail_list, $user_interfaces;

var $has_commands local = \
	#[["@sub?scribed", [["@sub?scribed", "*", "@sub?scribed <any>", 'subscribe_cmd, #[[1, ['any, []]]]]]],\
  ["@unsub?scribed",\
    [["@unsub?scribed", "*", "@unsub?scribed <any>", 'unsubscribe_cmd, #[[1, ['any, []]]]]]],\
  ["@mail-list?s",\
    [["@mail-list?s", "", "@mail-list?s", 'mail_lists_cmd, #[]]]], ["@read", [["@read", "*", "@read <any>", 'mail_read_cmd, #[[1, ['any, []]]]]]],\
  ["@remove-m?ail|@rmm?ail",\
    [["@remove-m?ail|@rmm?ail", "*", "@remove-m?ail|@rmm?ail <any>", 'mail_remove_cmd, #[[1, ['any, []]]]]]],\
  ["@nn|@next-new",\
    [["@nn|@next-new", "*", "@nn|@next-new <any>", 'next_new_cmd, #[[1, ['any, []]]]]]],\
  ["@mail",\
    [["@mail", "*", "@mail <any>", 'mail_on_cmd, #[[1, ['any, []]]]]]],\
  ["@reply",\
    [["@reply", "*", "@reply <any>", 'reply_cmd, #[[1, ['any, []]]]]]],\
  ["@send",\
    [["@send", "*", "@send <any: +e?dit>", 'send_to_cmd, #[[1, ['any_opt, ["e?dit"]]]]]]]];
var $has_commands shortcuts = #[];
var $has_name name = ['prop, "Mail User Interface", "Mail User Interface"];
var $mail_list last_letter = 0;
var $mail_list letters = #[];
var $mail_list letters_index = #[];
var $mail_list mail = [];
var $mail_list notify = [$mail_ui];
var $mail_list readers = [];
var $mail_list senders = 1;
var $mail_ui current = 0;
var $mail_ui subscribed = #[];
var $root created_on = 796268969;
var $root defined_settings = #[["@send-editor", #[['parse, ['is_boolean]], ['format, ['format_boolean]]]]];
var $root flags = ['methods, 'code, 'core, 'variables];
var $root help_node = $help_mail;
var $root inited = 1;
var $root managed = [$mail_ui];
var $root manager = $mail_ui;
var $root settings = #[["@send-editor", 1]];

root method .init_mail_ui() {
    current = #[['list, this()]];
    .subscribe(this());
    .new_list(this());
    (| .subscribe($mail_list_news) |);
    
    // now change the 'news' subscription so it notifies them NOW
    subscribed = subscribed.add($mail_list_news, [0, 0]);
};

protected method .mail_lists_cmd() {
    arg @args;
    var l, output;
    
    if (caller() != $mail_ui)
        (> .perms(caller(), 'command) <);
    output = map l in (($mail_db.database()).values()) to ($cml_lib.format_tr_tag($cml_lib.format_td_tag(l.mail_name()), $cml_lib.format_td_tag((l.list_is_sendable_by(this())) ? "[Sendable]" : " "), $cml_lib.format_td_tag((l.list_is_readable_by(this())) ? "[Readable]" : " ")));
    return $ctext_frob.new_with(["All Available Mail Lists:", $cml_lib.format_table_tag("70%,15%,15%", @output), $cml_lib.format_sep_tag()]);
};

protected method .mail_on_cmd() {
    arg cmdstr, cmd, str;
    var args, lmail, mail, rng, start, end, line, list, len, out, m, rows, o, from;
    
    (> .perms(caller(), 'command) <);
    if ((args = match_template(str, "* on *"))) {
        rng = args[1];
        list = args[3];
    } else if (match_template(str, "* to *")) {
        return (> .send_to_cmd(cmdstr, cmd, str) <);
    } else {
        rng = "";
        list = str;
    }
    if (!list) {
        list = current['list];
    } else {
        catch ~listnf, ~perm {
            list = (> $mail_lib.match_mail_recipient(list) <);
            (> .new_list(list) <);
        } with {
            return (traceback()[1])[2];
        }
    }
    if (!rng) {
        mail = list.mail();
        end = mail.length();
    
        // minus two for the head and tail
        rows = (.get_rows()) - 2;
        if (end > rows) {
            start = end - rows;
            mail = sublist(mail, start);
        } else {
            start = 1;
        }
        rng = (start + "-") + end;
    } else {
        catch ~range
            mail = $mail_lib.range_to_actual($parse_lib.range(rng), current);
        with
            return (traceback()[1])[2];
    }
    if (!mail)
        return "No mail on " + (list.mail_name());
    len = (.linelen()) - 36;
    out = [((("Mail from " + rng) + " on ") + (list.mail_name())) + ":"];
    lmail = list.mail();
    o = [];
    for m in (mail) {
        if (!valid(m)) {
            list.notify_bad_mail(m);
        } else {
            from = $object_lib.get_name(m.from(), 'name);
            if (from && (((from[1]) == "<") && ("@" in from)))
                from = (explode(from, "@")[1]) + ">";
            o += [$cml_lib.format_tr_tag($cml_lib.format_td_tag((m == (| (subscribed[list])[2] |)) ? "=>" : " "), $cml_lib.format_td_tag(m in lmail), $cml_lib.format_td_tag((m.has_read(this())) ? " " : "!"), $cml_lib.format_td_tag(m.subject()), $cml_lib.format_td_tag(from), $cml_lib.format_td_tag(m.lines()), $cml_lib.format_td_tag($time.format("%d-%h-%Y", m.time())))];
        }
    }
    return $ctext_frob.new_with((out + [$cml_lib.format_table_tag("5%,5%,2%,50%,17%,5%,15%", o)]) + [$cml_lib.format_sep_tag()]);
};

protected method .mail_read_cmd() {
    arg cmdstr, cmd, str;
    var mail, m, args, opts, lname, list, rng, args, meta;
    
    (> .perms(caller(), 'command) <);
    [args, opts] = $parse_lib.opt(str, "m?eta");
    str = args.join(" ");
    meta = [];
    if ((m = "m?eta" in (opts.slice(1)))) {
        if ((opts[m])[3])
            meta = [1];
    }
    if ((args = match_template(str, "* on *"))) {
        rng = args[1];
        catch ~listnf
            list = (> $mail_lib.match_mail_recipient(args[3]) <);
        with
            return (traceback()[1])[2];
    } else {
        rng = str;
        list = current['list];
    }
    catch ~perm
        (> .new_list(list) <);
    with
        return (traceback()[1])[2];
    if (((list.mail()).length()) == 0)
        return ("There is no mail on " + (list.mail_name())) + ".";
    if (!rng)
        return ("You must specify a message to read on " + (list.mail_name())) + ".";
    catch ~range {
        if (rng == "next") {
            .read_mail((> list.mail_list_next((subscribed[list])[2]) <), list, @meta);
        } else if (rng == "prev") {
            .read_mail((> list.mail_list_prev((subscribed[list])[2]) <), list, @meta);
        } else {
            for m in ($mail_lib.range_to_actual($parse_lib.range(rng), current))
                .read_mail(m, list, @meta);
        }
    } with {
        return ((("Mail message " + rng) + " does not exist on ") + (list.mail_name())) + ".";
    }
};

protected method .mail_remove_cmd() {
    arg cmdstr, cmd, str;
    var mail, args, lmail, list, rng, m, x, name, lname, offset, ans;
    
    (> .perms(caller(), 'command) <);
    if ((args = match_template(str, "* on|from *"))) {
        rng = args[1];
        list = args[3];
    } else if (!str) {
        return "You must specify a message and list.";
    } else {
        // grr, be hacky
        args = explode(str);
        if ((args.length()) > 1) {
            list = args.last();
            if (!(| $mail_lib.match_mail_recipient(list) |)) {
                rng = str;
                list = "";
            } else {
                rng = (args.delete(args.length())).join();
            }
        } else {
            rng = str;
            list = "";
        }
    }
    if (!rng)
        return ("You must specify a message to remove from " + lname) + ".";
    if (!list) {
        list = current['list];
        ans = .prompt(((("Remove mail " + rng) + " from ") + (list.mail_name())) + "? ");
        if (type(ans) == 'symbol)
            return;
        if (match_regexp(ans, "no|n"))
            return "Ok, aborting..";
    } else {
        catch ~listnf
            list = (> $mail_lib.match_mail_recipient(list) <);
        with
            return ("The list \"" + list) + "\" is invalid.";
    }
    lname = list.mail_name();
    catch any
        (> .new_list(list) <);
    with
        return (traceback()[1])[2];
    if (rng && ((rng[1]) == "$")) {
        catch ~namenf
            mail = [(> $object_lib.to_dbref(rng) <)];
        with
            return (traceback()[1])[2];
    } else {
        catch ~range
            mail = (> $mail_lib.range_to_actual($parse_lib.range(rng), current) <);
        with
            return (traceback()[1])[2];
    }
    lmail = list.mail();
    if ((mail[1]) != (| lmail[1] |)) {
        offset = (mail[1]) in lmail;
        lmail = sublist(lmail, offset);
        offset--;
    }
    for m in (mail) {
        catch ~perm {
            x = (m in lmail) + offset;
            name = ((((("#" + x) + " \"") + (m.subject())) + "\" (") + m) + ")";
            list.del_mail(m);
            .tell(((("Removed message " + name) + " from ") + (list.mail_name())) + ".");
        } with {
            .tell((traceback()[1])[2]);
        }
    }
};

protected method .new_list() {
    arg list;
    
    // check here so we can assume later that this error will not bite us
    if (!(subscribed.contains(list))) {
        if (!(list.list_is_readable_by(this())))
            throw(~perm, "You cannot read mail on " + (list.mail_name()));
    }
    
    // set the current list
    if (list != (current['list]))
        current = current.add('list, list);
};

protected method .next_new_cmd() {
    arg cmdstr, cmd, str;
    var mail, list, keys, start;
    
    (> .perms(caller(), 'command) <);
    if (str) {
        catch any
            list = (> $mail_lib.match_mail_recipient(str) <);
        with
            return (traceback()[1])[2];
        .new_list(list);
        mail = (| list.mail_list_next((subscribed[list])[2]) |);
        while (mail && (mail.has_read(this()))) {
            refresh();
            mail = (| list.mail_list_next(mail) |);
        }
        if (!mail)
            return ("No new mail on " + ($mail_lib.mail_name(list))) + ".";
        .read_mail(mail, list);
    } else {
        keys = dict_keys(subscribed);
        if (!keys)
            return "You are not subscribed to any lists.";
        if (!((current['list]) in keys))
            current = current.add('list, keys[1]);
        start = (list = current['list]);
        while (1) {
            // anything left on this list?
            mail = (| list.mail_list_next((subscribed[list])[2]) |);
            while (mail && (mail.has_read(this()))) {
                refresh();
                mail = (| list.mail_list_next(mail) |);
            }
            if (mail)
                break;
    
            // pick a new list
            catch any
                list = (> keys[(list in keys) + 1] <);
            with
                list = (| keys[1] |);
    
            // die?
            if ((!list) || (list == start))
                return "No new mail.";
        }
        .new_list(list);
        .read_mail(mail, list);
    }
};

public method .old_mail_lists_cmd() {
    arg @args;
    var l, line;
    
    if (caller() != $mail_ui)
        (> .perms(caller(), 'command) <);
    for l in (($mail_db.database()).values()) {
        line = "";
        if (l.list_is_readable_by(this()))
            line = "[Readable]";
        if (l.list_is_sendable_by(this()))
            line = ("[Sendable]" + (line ? " " : "")) + line;
        .tell((((l.mail_name()).pad(((.linelen()) - (line.length())) - 1)) + " ") + line);
    }
};

public method .old_mail_on_cmd() {
    arg cmdstr, cmd, str;
    var args, lmail, mail, rng, start, end, line, list, len, out, m, rows;
    
    (> .perms(caller(), 'command) <);
    if ((args = match_template(str, "* on *"))) {
        rng = args[1];
        list = args[3];
    } else if (match_template(str, "* to *")) {
        return (> .send_to_cmd(cmdstr, cmd, str) <);
    } else {
        rng = "";
        list = str;
    }
    if (!list) {
        list = current['list];
    } else {
        catch ~listnf, ~perm {
            list = (> $mail_lib.match_mail_recipient(list) <);
            (> .new_list(list) <);
        } with {
            return (traceback()[1])[2];
        }
    }
    if (!rng) {
        mail = list.mail();
        end = mail.length();
    
        // minus two for the head and tail
        rows = (.get_rows()) - 2;
        if (end > rows) {
            start = end - rows;
            mail = sublist(mail, start);
        } else {
            start = 1;
        }
        rng = (start + "-") + end;
    } else {
        catch ~range
            mail = $mail_lib.range_to_actual($parse_lib.range(rng), current);
        with
            return (traceback()[1])[2];
    }
    if (!mail)
        return "No mail on " + (list.mail_name());
    len = (.linelen()) - 36;
    out = [((("Mail from " + rng) + " on ") + (list.mail_name())) + ":"];
    lmail = list.mail();
    for m in (mail) {
        if (!valid(m))
            list.notify_bad_mail(m);
        else
            out += [strfmt("%s%3r:%s%*L %12L%5l%11l", (m == (| (subscribed[list])[2] |)) ? "=>" : "  ", m in lmail, (m.has_read(this())) ? " " : "!", len, m.subject(), $object_lib.get_name(m.from(), 'name), m.lines(), $time.format("%d-%h-%Y", m.time()))];
    }
    .tell(out + ["------"]);
};

public method .old_read_mail() {
    arg mail, list;
    var loc;
    
    loc = mail in (list.mail());
    .tell(strfmt("Message %l (%l) on %s:", loc, mail.name(), list.mail_name()));
    .tell(mail.format());
    mail.did_read();
    subscribed = subscribed.add(list, [time(), mail]);
};

protected method .read_mail() {
    arg mail, list, @metainfo;
    var loc, out;
    
    loc = mail in (list.mail());
    out = $ctext_frob.new_with([strfmt("Message %l (%l) on %s:", loc, mail.name(), list.mail_name())]);
    .tell(out.append(mail.format(@metainfo)));
    mail.did_read();
    subscribed = subscribed.add(list, [time(), mail]);
};

protected method .reply_cmd() {
    arg cmdstr, cmd, str;
    var list, msg, args, i, subj, text, recip;
    
    (> .perms(caller(), 'command) <);
    if ((args = match_template(str, "to *")))
        str = args[2];
    if ((args = match_template(str, "* on *")))
        [msg, str, list] = args;
    else
        msg = str;
    if (!list) {
        list = current['list];
    } else {
        catch ~listnf, ~perm {
            list = (> $mail_lib.match_mail_recipient(list) <);
            (> .new_list(list) <);
        } with {
            return (traceback()[1])[2];
        }
    }
    if (!msg) {
        if (!(msg = (| (subscribed[list])[2] |)))
            return "No current message to reply to.";
    } else {
        catch ~range
            msg = $mail_lib.range_to_actual($parse_lib.range(msg), current);
        with
            return (traceback()[1])[2];
        msg = msg[1];
    }
    i = msg in (list.mail());
    if (list.is($user))
        recip = msg.from();
    else
        recip = list;
    if (!(recip.is($mail_list)))
        return (list.namef('ref)) + " is not a valid mail recipient.";
    .tell(((("Replying to message " + i) + " on ") + (list.mail_name())) + ".");
    text = msg.body();
    if (text == ["", "(no message)", ""])
        text = [];
    text = $mail_lib.indent_reply(text);
    text = [((("On " + ($time.format("%d-%b-%Y", msg.time()))) + " ") + ((msg.from()).namef('ref))) + " said: "] + text;
    if (.active_editor()) {
        .tell("Storing active editor..");
        .store_editor();
    }
    subj = msg.subject();
    if (!(subj.match_begin("Re:")))
        subj = "Re: " + subj;
    (> .invoke_editor(this(), '_edit_mail_callback, text, ['mail, [recip], subj, i, msg]) <);
    if (.active_editor()) {
        .tell(("Editor invoked with " + ((.active_editor()).session_name())) + ".");
        .tell("Type 'help' for available commands.");
    }
};

protected method .send_to_cmd() {
    arg cmdstr, cmd, str;
    var subj, lists, note, list, x, mail, text, args, edit;
    
    (> .perms(caller(), 'command) <);
    if ((args = match_template((str[1]).join(" "), "* to *"))) {
        note = args[1];
        args = args[3];
    } else {
        note = "";
        args = (str[1]).join(" ");
    }
    if (args) {
        lists = [];
        for list in (args.explode_english_list()) {
            catch ~listnf {
                list = (> $mail_lib.match_mail_recipient(list) <);
                lists += [list];
            } with {
                .tell(("The list \"" + list) + "\" is invalid.");
            }
        }
        if (!lists)
            return "No lists specified.";
    } else {
        lists = [current['list]];
        .tell(("No recipient specified, using current recipient (" + ((lists[1]).mail_name())) + ")");
    }
    edit = "e?dit" in ((str[2]).slice(1));
    edit = edit ? (((str[2])[edit])[3]) : (.get_setting("@send-editor", $mail_ui));
    
    // get the text of the message
    if ((!note) && edit) {
        if (.active_editor()) {
            .tell("Storing active editor..");
            .store_editor();
        }
        subj = "";
        text = [];
        (> .invoke_editor(this(), '_edit_mail_callback, text, ['mail, lists, subj, 0, 0]) <);
        if (.active_editor()) {
            .tell(("Editor invoked with " + ((.active_editor()).session_name())) + ".");
            .tell("Type 'help' for available commands.");
        }
    } else {
        if (note) {
            text = (> (.match_env_nice(note)).get_raw_text() <);
        } else {
            text = .read("-- Enter text for mail message, \".\" when done or \"@abort\" to abort --");
            if (text == 'aborted)
                return;
            if (text == 'engaged)
                return "** Already reading - mail send aborted **";
        }
        subj = .prompt("Subject: ");
        if (subj == "@abort")
            return "** Aborted mail send **";
        if (subj == 'engaged)
            return "** Already reading - mail send aborted **";
        mail = $mail_message.new_mail();
        mail.set_subject(subj);
        mail.set_text(text);
        catch any
            mail.send(@lists);
        with
            return (traceback()[1])[2];
        return "Mail sent.";
    }
};

protected method .subscribe() {
    arg list;
    
    if (!subscribed)
        subscribed = #[];
    subscribed = subscribed.add(list, [time(), 0]);
    (| list.add_sender_to_notification() |);
};

protected method .subscribe_cmd() {
    arg cmdstr, cmd, str;
    var list, mname, l, args, line, len, out;
    
    (> .perms(caller(), 'command) <);
    
    // this is ugly bad bad
    args = $parse_lib.opt(str, "n?ew");
    if (!(args[1])) {
        if ("n?ew" in ((args[2]).slice(1))) {
            out = [];
            for l in ((subscribed.keys()).setremove(this())) {
                if ((l.last_received_on()) > ((subscribed[l])[1]))
                    out += ["  " + (l.mail_name())];
            }
            if (out)
                .tell(["New mail on:"] + out);
            else
                .tell(["No new mail on the lists you are subscribed to."]);
            return;
        }
        .tell("Currently Subscribed Lists:");
        len = (.linelen()) / 3;
        for l in ((subscribed.keys()).setremove(this())) {
            line = "  " + (l.mail_name());
            if ((l.last_received_on()) > ((subscribed[l])[1]))
                line += " (new mail)";
            .tell(line);
        }
        .tell("---");
        return;
    }
    list = $mail_lib.match_mail_recipient(str);
    mname = $mail_lib.mail_name(list);
    if (list in (subscribed.keys()))
        return .tell(("You are already subscribed to " + mname) + ".");
    if (!(list.list_is_readable_by(this())))
        return .tell(mname + " is not subscribeable by you.");
    .subscribe(list);
    .tell(("Successfully subscribed to " + mname) + ".");
};

protected method .subscribed() {
    return subscribed;
};

root method .uninit_mail_ui() {
    var l;
    
    for l in ((subscribed || #[]).keys())
        (| .unsubscribe(l) |);
};

protected method .unsubscribe() {
    arg list;
    
    subscribed = subscribed.del(list);
    (| list.del_sender_from_notification() |);
};

protected method .unsubscribe_cmd() {
    arg cmdstr, cmd, str;
    var list, line, mname;
    
    (> .perms(caller(), 'command) <);
    if (!str)
        return .mail_lists_cmd();
    list = $mail_lib.match_mail_recipient(str);
    if (list == this())
        return "You cannot unsubscribe yourself.";
    mname = list.mail_name();
    if (!(list in (subscribed.keys())))
        return "You are not subscribed to " + mname;
    .unsubscribe(list);
    return ("Successfully unsubscribed from " + mname) + ".";
};