/* Do not remove the headers from this file! see /USAGE for more info. */ /* ** Belboz on July 16 for Zorkmud, something that any mailer can ** interface with, so Beek and I can have our input_to()less mailer. ** ** 950710, Deathblade: Added mailboxes, removed mail queue. ** 950910, Deathblade: revamped some more: doc, trim api, cleaning ** 960119, Deathblade: moved mailbox functionality to MAILBOX_D ** ** Public API: ** ** send_mail(): send a piece of mail ** delete_mail(): delete a piece of mail for a user ** get_one_message(): get a message ** ** rebuild_mailboxes(): rebuild all mailboxes from actual message data */ #include <mudlib.h> #include <classes.h> #include <log.h> inherit M_ACCESS; inherit CLASS_MAILMSG; #define MAIL_PATH "/data/M/" private class mail_msg saved_msg; private nosave mapping mailboxes = ([ ]); private nomask void create() { object mailbox; set_privilege(1); foreach ( mailbox in children(MAILBOX) ) { string owner; if ( mailbox && (owner = mailbox->query_owner()) ) mailboxes[owner] = mailbox; } } private nomask string get_fname(int message_key) { //### eventually move this to /data/mail/N/N/NNNNNNNN.o return sprintf(MAIL_PATH "%d", message_key); } private nomask void save_msg(int message_key, class mail_msg msg) { saved_msg = msg; unguarded(1, (: save_object, get_fname(message_key) :)); saved_msg = 0; } private nomask class mail_msg restore_msg(int message_key) { class mail_msg msg; if ( !unguarded(1, (: restore_object, get_fname(message_key), 1 :)) ) return 0; msg = saved_msg; /* don't leave the message sitting around in our variables */ saved_msg = 0; return msg; } private nomask int get_message_key() { int message_key; message_key = time(); while ( unguarded(1, (: is_file, get_fname(message_key) + __SAVE_EXTENSION__ :)) ) message_key--; return message_key; } private nomask void deliver_mail(int message_key, string who) { MAILBOX_D->get_mailbox(who)->receive_new_message(message_key); } nomask string * process_mail_list(string * list) { /* convert groups to real names, then clean the lists: remove dups, ensure lower-cased, ensure valid */ list = GROUP_D->process_list(list); list = clean_array(list); list = map_array(list, (: lower_case :)); list = filter_array(list, (: user_exists :)); return list; } private nomask mixed clean_addresses(string array list) { string user, mudname; mixed mudinf; string array local_recips = ({}); string array recips = ({}); string listitem; foreach(listitem in list) { listitem = lower_case( listitem ); if(!sscanf(listitem, "%s@%s", user, mudname)) { if(user_exists(listitem)) { local_recips += ({listitem}); recips += ({listitem}); } continue; } mudinf = IMAIL_D->get_complete_mudname(mudname); if(arrayp(mudinf)) { recips += ({listitem}); continue; } if(mudinf == mud_name()) { recips += ({user}); local_recips += ({user}); continue; } recips += ({user + "@" + mudinf}); } return ({local_recips, recips}); } string array ungroup( string array list ) { mixed to; foreach( string addressee in list ) { to = GROUP_D->get_group( addressee); if( !arrayp( to )) to = ({ to }); list -= ({ addressee }); list += to; } return list; } varargs nomask string * send_mail(string Sender, string Subject, mixed Body, string* To_list, string* Cc_list, // Send time should only be provided by // the imud mail stuff... int send_time) { class mail_msg msg; int message_key; string* recip_list; string* local_recip_list; mixed recip_lists; // No mail forgeries, except by system stuff =-) // (this is so that system objects can send mail as Root or whatever) if ( !check_previous_privilege(1) && ( !this_user() || this_user()->query_userid() != Sender ) ) error("insufficient priviledge to send mail as " + Sender + "\n"); if ( stringp(Body) ) Body = explode(Body, "\n"); else if ( !arrayp(Body) ) Body = ({ "**Blank message!**" }); msg = new(class mail_msg); msg->to_list = To_list; msg->cc_list = Cc_list; msg->sender = Sender; msg->subject = Subject; msg->body = Body; if ( !arrayp(msg->to_list) || !arrayp(msg->cc_list) ) error("send mail: invalid list of recipients"); /* ** Pass it to the IMAIL_D for a second, let it see ** if any of the addresses are imud addresses. */ /* Convert groups to real names */ To_list = ungroup( To_list ); Cc_list = ungroup( Cc_list ); // this is the mail pointer as well as within seconds of time() message_key = get_message_key(); msg->date = msg->thread_id = message_key; if ( send_time ) { msg->date = send_time; } // A list of target recipients with no names duplicated Cc_list -= To_list; recip_lists = clean_addresses( To_list + Cc_list); local_recip_list = recip_lists[0]; recip_list = recip_lists[1]; local_recip_list = process_mail_list(local_recip_list); msg->to_list = recip_list; // deliver the mail (the message keys) to all recipients map_array(local_recip_list, (: deliver_mail, message_key :)); // we need to keep track of who needs to delete this message before // we erase this message to prevent people from making a call // to delete a mail into oblivion. msg->dels_pending = local_recip_list; // If there are no active copies, no one on this mud // is receiving this message, so why save it? if ( sizeof(local_recip_list) ) save_msg(message_key, msg); // close the mailboxes of people who are not online now MAILBOX_D->close_mailboxes(); if ( previous_object() != find_object(IMAIL_D) && sizeof(recip_list) != sizeof(local_recip_list) ) { IMAIL_D->enqueue_message(msg); } // Let the mailer know who successfully received the message. return recip_list; } nomask mixed get_one_message(int message_key) { class mail_msg msg; if ( base_name(previous_object()) != MAILBOX ) error("security violation: illegal attempt to read mail\n"); if ( !(msg = restore_msg(message_key)) ) { LOG_D->log(LOG_MAIL_ERROR, sprintf("%s lost message #%d", this_user()->query_userid(), message_key)); return 0; } if ( member_array(this_user()->query_userid(), msg->dels_pending) == -1 ) error("security violation: illegal attempt to read mail\n"); return msg; } nomask void delete_mail(int message_key, string user) { class mail_msg msg; // Do I have permission to delete mail as user? // Should only fail when I try to delete mail in someone elses box if ( base_name(previous_object()) != MAILBOX || previous_object()->query_owner() != user ) error("security violation: illegal attempt to delete mail\n"); if ( !(msg = restore_msg(message_key)) ) error("lost the message\n"); msg->dels_pending -= ({ user }); if( !sizeof(msg->dels_pending) ) unguarded(1, (: rm, get_fname(message_key) + __SAVE_EXTENSION__ :)); else save_msg(message_key, msg); } private nomask void process_message(string fname) { int message_key; class mail_msg msg; message_key = to_int(fname[0..<3]); /* ### dependent on SAVE_EXTENSION */ msg = restore_msg(message_key); map(msg->dels_pending, (: MAILBOX_D->get_mailbox($1)->receive_new_message($(message_key)) :)); } nomask void rebuild_mailboxes() { string * messages = unguarded(1, (: get_dir, MAIL_PATH "*.o" :)); map_array(messages, (: process_message :)); }