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) + ".";
};