Generic Mail Recipient (#46)

(an instance of Root Class made by Hacker)

     This can either be a mailing list or a mail folder, depending on what mood you're in...



VERB SOURCE CODE:

set_aliases:
"For changing mailing list aliases, we check to make sure that none of the aliases 
match existing mailing list aliases.  Aliases containing spaces are not used in addresses 
and so are not subject to this restriction ($mail_agent:match will not match on them, 
however, so they only match if used in the immediate room, e.g., with match_object() 
or somesuch).";
"  => E_PERM   if you don't own this";
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
    return E_PERM;
elseif (this.location != $mail_agent)
    "... we don't care...";
    return pass(@args);
else
    for a in (aliases = args[1])
        if (index(a, " "))
            "... we don't care...";
        elseif (rp = $mail_agent:reserved_pattern(a))
            player:tell("Mailing list name \"", a, "\" uses a reserved pattern: ", 
rp[1]);
            aliases = setremove(aliases, a);
        elseif (valid(p = $mail_agent:match(a, #-1)) && ((p != this) && (a in p.aliases)))
            player:tell("Mailing list name \"", a, "\" in use on ", p.name, "(", 
p, ")");
            aliases = setremove(aliases, a);
        endif
    endfor
    if (aliases)
        return pass(aliases);
    else
        return 1;
    endif
endif
.


look_self:
namelist = "*" + ((names = this:mail_names()) ? $string_utils:from_list(names, ", 
*") | tostr(this));
if (typeof(fwd = this:mail_forward()) != LIST)
    fwd = {};
endif
if (this:is_writable_by(player))
    if (player in fwd)
        read = " [Writable/Subscribed]";
    else
        read = " [Writable]";
    endif
elseif (typeof(this.readers) != LIST)
    read = tostr(" [Public", (player in fwd) ? "/Subscribed]" | "]");
elseif (player in fwd)
    read = " [Subscribed]";
elseif (this:is_readable_by(player))
    read = " [Readable]";
else
    read = "";
endif
if (this:is_usable_by($no_one))
    mod = "";
elseif (this:is_usable_by(player))
    mod = " [Approved]";
else
    mod = " [Moderated]";
endif
player:tell(namelist, "  (", this, ")", read, mod);
d = this:description();
if (typeof(d) == STR)
    d = {d};
endif
for l in (d)
    if (length(l) <= 75)
        ls = {l};
    else
        ls = $generic_editor:fill_string(l, 76);
    endif
    for line in (ls)
        player:tell("    ", line);
    endfor
endfor
.


is_writable_by:
return $perm_utils:controls(who = args[1], this) || (who in this.writers);
.


is_readable_by:
return (typeof(this.readers) != LIST) || (((who = args[1]) in this.readers) || (this:is_writable_by(who) 
|| $mail_agent:sends_to(1, this, who)));
.


is_usable_by:
who = args[1];
if (this.moderated)
    return (who in this.moderated) || (this:is_writable_by(who) || who.wizard);
else
    return this.guests_can_send_here || (!$object_utils:isa(who, $guest));
endif
.


mail_notify:
if ((args && (!this:is_usable_by(args[1]))) && (!args[1].wizard))
    return this:moderator_notify(@args);
else
    return this.(verb);
endif
.


mail_forward:
if ((args && (!this:is_usable_by(args[1]))) && (!args[1].wizard))
    return this:moderator_forward(@args);
elseif (typeof(mf = this.(verb)) == STR)
    return $string_utils:pronoun_sub(mf, @args);
else
    return mf;
endif
.


moderator_forward:
if (typeof(mf = this.(verb)) == STR)
    return $string_utils:pronoun_sub(mf, args ? args[1] | $player);
else
    return mf;
endif
.


add_forward:
":add_forward(recip[,recip...]) adds new recipients to this list.  Returns a string 
error message or a list of results (recip => success, E_PERM => not allowed, E_INVARG 
=> not a valid recipient, string => other kind of failure)";
if (caller == $mail_editor)
    perms = player;
else
    perms = caller_perms();
endif
result = {};
forward_self = (!this.mail_forward) || (this in this.mail_forward);
    for recip in (args)
        if ((!valid(recip)) || ((!is_player(recip)) && (!($mail_recipient in $object_utils:ancestors(recip)))))
            r = E_INVARG;
        elseif ($perm_utils:controls(perms, this) || ((typeof(this.readers) != LIST) 
&& $perm_utils:controls(perms, recip)))
            this.mail_forward = setadd(this.mail_forward, recip);
            r = recip;
        else
            r = E_PERM;
        endif
        result = listappend(result, r);
    endfor
    if ((length(this.mail_forward) > 1) && ($nothing in this.mail_forward))
        this.mail_forward = setremove(this.mail_forward, $nothing);
    endif
    if (forward_self)
        this.mail_forward = setadd(this.mail_forward, this);
    endif
    return result;
.


delete_forward:
":delete_forward(recip[,recip...]) removes recipients to this list.  Returns a list 
of results (E_PERM => not allowed, E_INVARG => not on list)";
if (caller == $mail_editor)
    perms = player;
else
    perms = caller_perms();
endif
result = {};
forward_self = (!this.mail_forward) || (this in this.mail_forward);
    for recip in (args)
        if (!(recip in this.mail_forward))
            r = E_INVARG;
        elseif (((!valid(recip)) || $perm_utils:controls(perms, recip)) || $perm_utils:controls(perms, 
this))
            if (recip == this)
                forward_self = 0;
                endif
                this.mail_forward = setremove(this.mail_forward, recip);
                r = recip;
            else
                r = E_PERM;
            endif
            result = listappend(result, r);
        endfor
        if (!(forward_self || this.mail_forward))
            this.mail_forward = {$nothing};
        elseif (this.mail_forward == {this})
            this.mail_forward = {};
        endif
        return result;
.


add_notify:
":add_notify(recip[,recip...]) adds new notifiees to this list.  Returns a list of 
results (recip => success, E_PERM => not allowed, E_INVARG => not a valid recipient)";
if (caller == $mail_editor)
    perms = player;
else
    perms = caller_perms();
endif
result = {};
for recip in (args)
    if ((!valid(recip)) || (recip == this))
        r = E_INVARG;
    elseif ($perm_utils:controls(perms, this) || (this:is_readable_by(perms) && $perm_utils:controls(perms, 
recip)))
        this.mail_notify = setadd(this.mail_notify, recip);
        r = recip;
    else
        r = E_PERM;
    endif
    result = listappend(result, r);
endfor
return result;
.


delete_notify:
":delete_notify(recip[,recip...]) removes notifiees from this list.  Returns a list 
of results (E_PERM => not allowed, E_INVARG => not on list)";
if (caller == $mail_editor)
    perms = player;
else
    perms = caller_perms();
endif
result = {};
rmthis = 0;
for recip in (args)
    if (!(recip in this.mail_notify))
        r = E_INVARG;
    elseif ((!valid(recip)) || ($perm_utils:controls(perms, recip) || $perm_utils:controls(perms, 
this)))
        if (recip == this)
            rmthis = 1;
        endif
        this.mail_notify = setremove(this.mail_notify, recip);
        r = recip;
    else
        r = E_PERM;
    endif
    result = listappend(result, r);
endfor
return result;
.


receive_message:
if (!this:ok_write(caller, caller_perms()))
    return E_PERM;
else
    this.messages = {@this.messages, {new = this:new_message_num(), args[1]}};
    this.last_msg_date = args[1][1];
    this.last_used_time = time();
    return new;
endif
.


ok:
":ok(caller,callerperms) => true iff caller can do read operations";
return (args[1] in {this, $mail_agent}) || (args[2].wizard || this:is_readable_by(args[2]));
.


ok_write:
":ok_write(caller,callerperms) => true iff caller can do write operations";
return (args[1] in {this, $mail_agent}) || (args[2].wizard || this:is_writable_by(args[2]));
.


parse_message_seq from_msg_seq %from_msg_seq to_msg_seq %to_msg_seq subject_msg_seq body_msg_seq kept_msg_seq unkept_msg_seq display_seq_headers display_seq_full messages_in_seq list_rmm new_message_num length_num_le length_date_le length_all_msgs exists_num_eq msg_seq_to_msg_num_list msg_seq_to_msg_num_string:
":parse_message_seq(strings,cur) => msg_seq";
"";
":from_msg_seq(olist)     => msg_seq of messages from those people";
":%from_msg_seq(strings)  => msg_seq of messages with strings in the From: line";
":to_msg_seq(olist)       => msg_seq of messages to those people";
":%to_msg_seq(strings)    => msg_seq of messages with strings in the To: line";
":subject_msg_seq(target) => msg_seq of messages with target in the Subject:";
":body_msg_seq(target)    => msg_seq of messages with target in the body";
":new_message_num()    => number that the next incoming message will receive.";
":length_num_le(num)   => number of messages in folder numbered <= num";
":length_date_le(date) => number of messages in folder dated <= date";
":length_all_msgs()    => number of messages in folder";
":exists_num_eq(num)   => index of message in folder numbered == num, or 0";
"";
":display_seq_headers(msg_seq[,cur])   display message summary lines";
":display_seq_full(msg_seq[,preamble]) display entire messages";
"            => number of final message displayed";
":list_rmm() displays contents of .messages_going.";
"            => the number of messages in .messages_going.";
"";
":messages_in_seq(msg_seq) => list of messages in msg_seq on folder";
"";
"See the corresponding routines on $mail_agent for more detail.";
return this:ok(caller, caller_perms()) ? $mail_agent:(verb)(@args) | E_PERM;
.


length_date_gt:
":length_date_le(date) => number of messages in folder dated > date";
"";
if (this:ok(caller, caller_perms()))
    date = args[1];
    return (this.last_msg_date <= date) ? 0 | $mail_agent:(verb)(date);
else
    return E_PERM;
endif
.


rm_message_seq:
":rm_message_seq(msg_seq) removes the given sequence of from folder";
"               => string giving msg numbers removed";
"See the corresponding routine on $mail_agent.";
if (this:ok_write(caller, caller_perms()))
    return $mail_agent:(verb)(@args);
elseif (this:ok(caller, caller_perms()) && (seq = this:own_messages_filter(caller_perms(), 
@args)))
    return $mail_agent:(verb)(@listset(args, seq, 1));
else
    return E_PERM;
endif
.


undo_rmm expunge_rmm renumber keep_message_seq:
":rm_message_seq(msg_seq) removes the given sequence of from folder";
"               => string giving msg numbers removed";
":list_rmm()    displays contents of .messages_going.";
"               => number of messages in .messages_going.";
":undo_rmm()    restores previously deleted messages from .messages_going.";
"               => msg_seq of restored messages";
":expunge_rmm() destroys contents of .messages_going once and for all.";
"               => number of messages in .messages_going.";
":renumber([cur])  renumbers all messages";
"               => {number of messages,new cur}.";
"";
"See the corresponding routines on $mail_agent.";
return this:ok_write(caller, caller_perms()) ? $mail_agent:(verb)(@args) | E_PERM;
.


own_messages_filter:
":own_messages_filter(who,msg_seq) => subsequence of msg_seq consisting of those 
messages that  is actually allowed to remove (on the assumption that  is 
not one of the allowed writers of this folder.";
if (!this.rmm_own_msgs)
    return E_PERM;
elseif ((typeof(seq = this:from_msg_seq({args[1]}, args[2])) != LIST) || (seq != 
args[2]))
    return {};
else
    return seq;
endif
.


messages:
"NOTE:  this routine is obsolete, use :messages_in_seq()";
":messages(num) => returns the message numbered num.";
":messages()    => returns the entire list of messages (can be SLOW).";
if (!this:ok(caller, caller_perms()))
    return E_PERM;
elseif (!args)
    return this:messages_in_seq({1, this:length_all_msgs() + 1});
elseif (!(n = this:exists_num_eq(args[1])))
    return E_RANGE;
else
    return this:messages_in_seq(n)[2];
endif
.


date_sort:
if (!this:ok_write(caller, caller_perms()))
    return E_PERM;
endif
date_seq = {};
for msg in (this.messages)
    date_seq = {@date_seq, msg[2][1]};
endfor
msg_order = $list_utils:sort($list_utils:range(n = length(msgs = this.messages)), 
date_seq);
newmsgs = {};
for i in [1..n]
    if ($command_utils:suspend_if_needed(0))
        player:tell("...", i);
    endif
    newmsgs = {@newmsgs, {i, msgs[msg_order[i]][2]}};
endfor
if (length(this.messages) != n)
    "...shit, new mail received,... start again...";
    fork (0)
        this:date_sort();
    endfork
else
    this.messages = newmsgs;
    this.last_used_time = newmsgs[length(newmsgs)][2][1];
endif
.


_fix_last_msg_date:
mlen = this:length_all_msgs();
this.last_msg_date = mlen && this:messages_in_seq(mlen)[2][1];
.


moderator_notify:
return this.(verb);
.


msg_summary_line:
return $mail_agent:msg_summary_line(@args);
.


__check:
for m in (this.messages)
    $mail_agent:__convert_new(@m[2]);
    $command_utils:suspend_if_needed(0);
endfor
.


__fix:
if (!this:ok_write(caller, caller_perms()))
    return E_PERM;
endif
msgs = {};
i = 1;
for m in (oldmsgs = this.messages)
    msgs = {@msgs, {m[1], $mail_agent:__convert_new(@m[2])}};
    if ($command_utils:running_out_of_time())
        player:notify(tostr("...", i, " ", this));
        suspend(0);
        if (oldmsgs != this.messages)
            return 0;
        endif
    endif
    i = i + 1;
endfor
this.messages = msgs;
return 1;
.


init_for_core:
if (caller_perms().wizard)
    pass();
    if (!(this in {$mail_recipient, $big_mail_recipient}))
        "...generic mail recipients stay in #-1...";
        move(this, $mail_agent);
    endif
endif
.


initialize:
if ($perm_utils:controls(caller_perms(), this))
    this.mail_forward = {};
    return pass(@args);
endif
.


mail_name_old mail_name:
return "*" + this.aliases[1];
.


mail_names:
names = {};
for a in (this.aliases)
    if (!index(a, " "))
        names = setadd(names, strsub(a, "_", "-"));
    endif
endfor
return names;
.


expire_old_messages:
if (this:ok_write(caller, caller_perms()))
    "Passed security check...";
    if (this.expire_period && (rmseq = $seq_utils:remove(this:unkept_msg_seq(), 1 
+ this:length_date_le(time() - this.expire_period))))
        "... i.e., everything not marked kept that is older than expire_period";
        this:rm_message_seq(rmseq);
        return this:expunge_rmm();
    else
        return 0;
    endif
else
    return E_PERM;
endif
.


moveto:
if (this:is_writable_by(caller_perms()) || this:is_writable_by(caller))
    pass(@args);
else
    return E_PERM;
endif
.


accept_subname:
":accept_subname(object,name,perms)";
"is  allowed to make  mail subname of this with the given ?";
object = args[1];
accept = this.accept_subname;
if (!valid(object))
    return E_INVARG;
elseif ((accept && (typeof(accept) != LIST)) || (this:is_writable_by(object.owner) 
|| this:is_writable_by(perms = args[3])))
    "...writers can do anything...";
    "... .accept_subname==1 => anyone can do anything...";
    return 1;
elseif (accept && ((object in accept) || ({object, args[2]} in accept)))
    "... object/name is explicitly sanctioned";
    return 1;
elseif (object in $mail_name_db:find_all(tostr(this, ":")))
    "...object is already a subname...";
    "...allow name change unless .accept_subname specifies a name";
    return !(accept && (a = $list_utils:assoc(object, accept)));
else
    return 0;
endif
.



mail_name_new mail_name:
return (!(names = this.names)) ? "" | (((name = names[1])[1] == #-1) ? "*" + name[2] 
| tostr(is_player(name[1]) ? name[1].name | ("*" + name[1]:(verb)()), ":", name[2]));
.


@mail-n*ame:
"@mail_name  is whatever";
if (!this:is_writable_by(player))
    player:tell(E_PERM);
    return;
endif
if (h = rindex(iobjstr, ":"))
    if ($mail_agent:match_failed(head = $mail_agent:match_new(iobjstr[1..h]), iobjstr[1..h]))
        return;
    elseif (!(name = iobjstr[h + 1..length(iobjstr)]))
        player:tell("Recipient cannot have a blank name.");
        return;
    endif
elseif (this.names && $object_utils:isa(head = this.names[1][1], $mail_recipient))
    name = iobjstr;
else
    player:tell("No names have yet been given to this recipient.");
    player:tell("You need to specify a full path.");
    return;
endif
if (e = $mail_agent:set_mail_name(this, head, name))
    player:tell("Name set to ", this:mail_name_new(), ".");
elseif (e == E_NACC)
    player:tell(head:mail_name_new(), " does not want ", this, " to be a subname.");
elseif (e == E_RECMOVE)
    player:tell(head:mail_name_new(), " is a name-descendant of ", this, ".");
elseif (e == E_INVARG)
    player:tell("Name must contain no parentheses, colons or stars.");
else
    player:tell(e);
endif
.


@rm-mail-n*ame @remove-mail-n*ame:
"@rm-mail-name whatever from ";
if (!this:is_writable_by(player))
    player:tell(E_PERM);
    return;
elseif (!this.names)
    player:tell("This recipient has no names.");
    return;
endif
if (h = rindex(dobjstr, ":"))
    if ($mail_agent:match_failed(head = $mail_agent:match_new(dobjstr[1..h]), dobjstr[1..h]))
        return;
    endif
    name = dobjstr[h + 1..length(dobjstr)];
    nlist = this.names;
    while ((i = $list_utils:iassoc(head, nlist)) && (name && (name != nlist[i][2])))
        nlist[1..i] = {};
    endwhile
    if (i)
        "...no problem...";
    elseif (name)
        player:tell("Recipient does not have that name");
        return;
    else
        player:tell("Recipient does not have any names under ", head:mail_name_new(), 
": (", head, ").");
        return;
    endif
    name = nlist[i][2];
elseif (a = $list_utils:assoc(name = dobjstr, this.names, 2))
    head = a[1];
else
    player:tell("Recipient does not have that name");
    return;
endif
if (e = $mail_agent:remove_mail_name(this, head, name))
    player:tell("Removed name ", head:mail_name_new(), ":", name, " from ", this:mail_name_new(), 
" (", this, ").");
else
    player:tell(e);
endif
.


@add-mail-n*ame:
"@add-mail-name whatever to ";
if (!this:is_writable_by(player))
    player:tell(E_PERM);
    return;
endif
if (h = rindex(dobjstr, ":"))
    if ($mail_agent:match_failed(head = $mail_agent:match_new(dobjstr[1..h]), dobjstr[1..h]))
        return;
    elseif (!(name = dobjstr[h + 1..length(dobjstr)]))
        player:tell("Recipient cannot have a blank name.");
        return;
    endif
elseif (this.names && $object_utils:isa(head = this.names[1][1], $mail_recipient))
    name = dobjstr;
else
    player:tell("No names have yet been given to this recipient.");
    player:tell("You need to specify a full path.");
    return;
endif
if (e = $mail_agent:add_mail_name(this, head, name))
    player:tell("Added name ", head:mail_name_new(), ":", name, " to ", $mail_agent:name_new(this), 
".");
elseif (e == E_NACC)
    player:tell(head:mail_name_new(), " does not want ", this, " to be a subname.");
elseif (e == E_RECMOVE)
    player:tell(head:mail_name_new(), " is a name-descendant of ", this, ".");
elseif (e == E_INVARG)
    player:tell("Name must contain no parentheses, colons or stars.");
else
    player:tell(e);
endif
.



PROPERTY DATA:
      moderator_notify
      last_msg_date
      messages_going
      moderated
      moderator_forward
      writers
      readers
      mail_notify
      mail_forward
      expire_period
      last_used_time
      messages
      rmm_own_msgs
      guests_can_send_here
      names
      accept_subname
      messages_kept

CHILDREN:
Quota-Log New-Prog-Log Generic Large-Capacity Mail Recipient Site-Locks Wizard-Mail graffiti Core-Updates Quota-Requests Text cuffs boot-log coolthingstoread I'd-Rather-Be-Smashing-the-MOO web
MAIL MESSAGES: