/* mailer 2 */ /* This mailer has three parts, an editor, and a pager (which are used by the bulletin board). The mailer allows text copy/paste, forwarding, replying. Allows the mailer to mark mail as private. Private mail cannot be forwarded or pasted to the clipboard. I have also added the route string on the basis that if intermud ever gains a mail system, then this can easily be adapted. */ #include <mudlib.h> #include <player.cfg> #include <ansi.h> #define REAL (string)this_player()->query_name(1) #define TIME ctime(time())[4..15] #undef PASTE #define PASTE "/"+ PASTE_DIR +"/"+ REAL #define SENDER 0 #define HEADER 1 #define ROUTE 2 #define MESSAGE 3 #define NEW 4 #define PRIVATE 5 #define DATE_TIME 6 #ifdef INTERMUD /* no such thing yet, just in case */ #define MUDNAME INTERMUD_NAME #define UDP_MAIL UDP_CMD_DIR +"mail" #endif /* stored mail */ mixed *mail; /* ({ ({ sender,header,route,message,new,private}),}) */ /* default mailing fields */ static string *aspirant; static string *apprentice; static string *creator; static string *sage; static string *lord; static string *senior; static string *elder; static string *arch; static string *admin; /* new send mail stuff */ static string recipient; static string title; static string route; static string *cc; static string message; static int reply; static status private_mail; /* Not clipable, forwardable */ /* read mail stuff */ static int current_message; /* fn prototypes */ void reset(status arg); /* set some variables */ void init(); static status test_player(string who); /* test if player exists */ static status headers(); /* writes list of mail */ int new_mail(string who); /* used by finger */ static status pmail(string str); /* private mail */ static status mail(string str); /* mail someone */ static status reply(int i); /* reply to existing mail */ static status forward(string str); /* forward existing mail */ static void mail_editor(string str); /* clone mail editor */ void end_editor(string str); /* editor calls on save quit */ static void get_cc(string str); /* get list of other recipients */ static send_mail(); /* prepare to send mail */ static send_mail_to(string who); /* send mail to recipient */ static status delete(string str); /* delete existing mail */ void load_mail(); /* load new mail in recipients' mailer */ void refresh_mail(); /* refresh recipients mailer before send new mail */ static status read(string str); /* read mail|start controller */ static void prompt(); /* prompt for mail controller */ static void mail_control(string str); /* main mail controls */ static void do_read(int i); /* clone mail reader(pager) */ void editor_quit(string arg); /* editor calls on quit */ void pager_quit(string arg); /* pager calls this on quit */ static status valid_name(string arg); void reset_field(); /* resets/loads default mailing fields */ string *set_field(string field_name, string *field); string *get_field(string field_name); static void parse_cc(string str); /***********************************************************************/ #ifdef NATIVE_MODE varargs void move(mixed dest) { if(!dest) dest = previous_object(); move_object(this_object(), dest); } void create() { #else void reset(status arg) { if(arg) return; #endif /* native */ title = ""; cc = ({}); mail = ({}); current_message = 1; reset_field(); } status drop(status quit) { if(quit) destruct(this_object()); return 1; } void init() { if(this_player()->query_npc()) { destruct(this_object()); return; } if(environment() != this_player()) return; restore_object(MAIL_DIR+REAL); add_action("mail", "mail"); add_action("pmail", "pmail"); add_action("headers", "from"); add_action("reply", "reply"); add_action("forward", "forward"); add_action("delete", "delete"); add_action("read", "read"); } status id(string str) { return str == "mailer"; } status get() { return 1; } /***********************************************************************/ /* cc parser */ void reset_field() { aspirant = ({}); apprentice = ({}); creator = ({}); sage = ({}); lord = ({}); senior = ({}); elder = ({}); arch = ({}); admin = ({}); #ifdef MAIL_FIELD_LOADER MAIL_FIELD_LOADER->load_fields(this_object()); #endif } string *set_field(string field_name, string *field) { switch(field_name) { case "aspirant": aspirant = (field) ? field : ({}); break; case "apprentice": return apprentice = (field) ? field : ({}); break; case "creator": return creator = (field) ? field : ({}); break; case "sage": return sage = (field) ? field : ({}); break; case "lord": return lord = (field) ? field : ({}); break; case "senior": return senior = (field) ? field : ({}); break; case "elder": return elder = (field) ? field : ({}); break; case "arch": return arch = (field) ? field : ({}); break; case "admin": return admin = (field) ? field : ({}); break; } return ({}); } string *get_field(string field_name) { switch(field_name) { case "aspirant": return aspirant; break; case "apprentice": return apprentice; break; case "creator": return creator; break; case "sage": return sage; break; case "lord": return lord; break; case "senior": return senior; break; case "elder": return elder; break; case "arch": return arch; break; case "admin": return admin; break; } return ({}); } static void parse_cc(string str) { string next_word, rest; string *operators, *fields; string next_operator, current_operator; int i; status stop_flag; if(!str) return; while(sscanf(str,"%s %s", str, rest)) str += rest; operators = ({ ">", "<", "+", ",", "-", }); fields = ({ "aspirant", "apprentice", "creator", "sage", "lord", "senior", "elder", "arch", "admin", }); for(next_operator = "+"; !stop_flag; ) { stop_flag = 1; current_operator = next_operator; for(i = 0, next_word = str; i < sizeof(operators); i++) { if(sscanf(next_word,"%s"+ operators[i] +"%s", next_word, rest)) { next_operator = operators[i]; stop_flag = 0; } } sscanf(str,next_word + next_operator +"%s", str); if(next_word == "") continue; if((i = member_array(next_word, fields)) != -1) { switch(current_operator) { case "<": for(i += 1; i--; ) { if(!sizeof(get_field(fields[i]))) continue; cc += get_field(fields[i]); if(sizeof(cc) && sizeof(cc) != sizeof(get_field(fields[i]))) { route += ", "; } route += fields[i]; } break; case ">": for( ; i < sizeof(fields); i++) { if(!sizeof(get_field(fields[i]))) continue; cc += get_field(fields[i]); if(sizeof(cc) && sizeof(cc) != sizeof(get_field(fields[i]))) { route += ", "; } route += fields[i]; } break; case "+": case ",": if(!sizeof(get_field(fields[i]))) break; cc += get_field(fields[i]); if(sizeof(cc) && sizeof(cc) != sizeof(get_field(fields[i]))) { route += ", "; } route += fields[i]; break; case "-": #ifdef 312MASTER write("Cannot subtract a field.\n"); #else if(!sizeof(get_field(fields[i]))) break; if(sizeof(cc)) route += ", "; cc -= get_field(fields[i]); route += "!"+ fields[i]; #endif break; } } else { switch(current_operator) { case "<": case "-": next_word = capitalize(next_word); if((i = member_array(next_word, cc)) == -1) { write("Mail is not routed to '"+ next_word +"'.\n"); } else { if(sizeof(cc)) route += ", "; route += "!"+ next_word; cc = cc[0..(i-1)] + cc[(i+1)..(sizeof(cc)-1)]; } break; case ">": case "+": case ",": next_word = capitalize(next_word); if(!valid_name(next_word)) { write("Invalid name, '"+ next_word +"'.\n"); } else if(test_player(next_word)) { if(sizeof(cc)) route += ", "; route += next_word; cc += ({ next_word, }); } else { write("No such player, "+ next_word +".\n"); } break; } } } } /*********************************************************************/ /* test for existing player character */ static status test_player(string who) { #ifdef INTERMUD string mud; if(sscanf(who,"%s@%s", who, mud)) return 1; /* let receiver do it */ #endif who = lower_case(who); return restore_object(SAVE_NO_BANISH+who) || restore_object(SAVE_WIZARD+who) || restore_object(SAVE_PLAYER+who) || restore_object(SAVE_FIRST +who); } /****************************************************************/ /* headers - indicate new mail */ static status headers() { int i; if(!sizeof(mail)) { notify_fail("No Mail.\n"); return 0; } for(i = 0; i < sizeof(mail); i++) { if(i < 9) write(" "); write((i+1)+". Subject: "+ mail[i][HEADER]); if(mail[i][NEW]) write(" (NEW"+ ((mail[i][PRIVATE]) ? " -CONFIDENTIAL-" : "") +")\n"); else write(" (READ"+ ((mail[i][PRIVATE]) ? " -CONFIDENTIAL-" : "") +")\n"); } if(query_verb() != "from") prompt(); return 1; } /*****************************************************************/ /* new mail - used by finger */ int new_mail(string who) { int i; int size; /* why? (Tamsyn) if(environment()) return 0; */ if(!who) return 0; who = capitalize(who); if(!test_player(who)) return 0; if(!restore_object(MAIL_DIR+lower_case(who))) return 0; size = sizeof(mail); for(i = size; i-- ; ) { if(mail[i][NEW]) { restore_object(MAIL_DIR + REAL); return (i == size -1 ? 1 : -1); /* -1 for unread */ } } return 0; } /*****************************************************************/ /* private mail */ static status pmail(string str) { private_mail = 1; mail(str); return 1; } /*****************************************************************/ /* mail */ static status mail(string str) { if(!str) { notify_fail("mail who?\n"); private_mail = 0; return 0; } str = capitalize(str); if(!test_player(str)) { notify_fail("No such player, "+ str +".\n"); private_mail = 0; return 0; } recipient = str; if(reply) write("Press <return> for reply.\n"); write("Subject: "); input_to("mail_editor"); return 1; } /********************************************************************/ /* reply */ static status reply(string str) { int i; if(!sizeof(mail)) { notify_fail("No Mail.\n"); return 0; } if(!str || sscanf(str,"%d",i) != 1) { notify_fail("reply #num\n"); return 0; } if(i < 1 || i > sizeof(mail)) { notify_fail("You have only "+ sizeof(mail) + " message"+((sizeof(mail)==1)?"":"s")+".\n"); return 0; } reply = i; mail(mail[i-1][SENDER]); return 1; } /**********************************************************************/ /* forward */ static status forward(string str) { int i; string *mark; string tmp; if(!sizeof(mail)) { notify_fail("No Mail.\n"); return 0; } if(!str || sscanf(str,"%d %s", i, str) != 2) { notify_fail("forward #num who?\n"); return 0; } if(i < 1 || i > sizeof(mail)) { notify_fail("You have only "+ sizeof(mail) + " message"+((sizeof(mail)==1)?"":"s")+".\n"); return 0; } if(mail[i-1][PRIVATE]) { write("Sorry, you cannot forward mail marked as Confidential.\n"); return 1; } str = capitalize(str); if(!test_player(str)) { notify_fail("No such player, "+ str +".\n"); return 0; } i -= 1; recipient = str; title = mail[i][HEADER]; sscanf(title,"%sFwd: %s",tmp, title); title = "Fwd: "+ title; route = mail[i][ROUTE]; route += "Forwarded by: "+ capitalize(REAL) #ifdef INTERMUD +"@"+ MUDNAME #endif +", Sent to: "+ recipient +"\n"; message = mail[i][MESSAGE]; mark = explode(message,"\n"); message = ">"+ implode(mark,"\n>"); write("CC: "); input_to("get_cc"); return 1; } /**********************************************************************/ /* invoke editor */ static void mail_editor(string str) { string tmp, reply_msg, *mark; if(!str || str == "") { if(reply) { title = mail[reply-1][HEADER]; sscanf(title,"%sRE: %s",tmp, title); title = "RE: "+ title; write("Reply: "+ title +"\n"); route = mail[reply-1][ROUTE]; route += "Reply by: "+ capitalize(REAL) #ifdef INTERMUD +"@"+ MUDNAME #endif +", Sent to: "+ recipient +"\n"; reply_msg = mail[reply-1][MESSAGE]; mark = explode(reply_msg,"\n"); reply_msg = ">"+ implode(mark,"\n>"); private_mail = mail[reply-1][PRIVATE]; reply = 0; /* reset reply flag */ } else { title = "("+ capitalize(REAL) +")"; route = "Author: "+ capitalize(REAL) #ifdef INTERMUD +"@"+ MUDNAME #endif +", Sent to: "+ recipient +"\n"; } } else { str = (string)this_player()->filter_ansi(str); title = str +" ("+ capitalize(REAL) +")"; route = "Author: "+ capitalize(REAL) #ifdef INTERMUD +"@"+ MUDNAME #endif +", Sent to: "+ recipient +"\n"; } clone_object(EDITOR)->edit("end_editor", reply_msg, 1); } /* end of using editor */ void end_editor(string str) { int i; if(!sscanf(file_name(previous_object()),EDITOR+"#%d",i)) return; message = str; route += "CC: "; #ifdef MAIL_FIELD_LOADER write("Valid CC fields: admin, arch, elder, senior, lord, sage,\n"+ " creator, apprentice, aspirant.\n"+ "Valid CC operators: +, -, >, <\n"+ "CC Example: >arch+lord will send mail to arches and above,"+ " and lords.\n"); #endif /* MAIL_FIELD_LOADER */ write("CC: "); input_to("get_cc"); } /* get other recipients */ static status valid_name(string str) { int i; str = lower_case(str); for(i = 0; i < strlen(str); i++) { if((str[i] < 'a' || str[i] > 'z') && str[i] != '@') return 0; } return 1; } static void get_cc(string str) { if(!str || str == "") { if(!sizeof(cc)) route += "none"; route += ".\n"; send_mail(); return; } parse_cc(str); write("CC: "); input_to("get_cc"); } /* prepare to send all mail */ static send_mail() { int i; save_object(MAIL_DIR+REAL); send_mail_to(recipient); for(i = 0; i < sizeof(cc); i++) { if(member_array(cc[i], cc) == i && lower_case(cc[i]) != lower_case(recipient) && lower_case(cc[i]) != (string)this_player()->query_name(1)) { send_mail_to(cc[i]); } } recipient = 0; title = 0; route = 0; cc = ({}); message = 0; reply = 0; private_mail = 0; if(sizeof(mail)) prompt(); } /* send mail to recipient */ static send_mail_to(string who) { object ob; string mud; who = lower_case(who); #ifdef INTERMUD /* the object UDP_MAIL is not code yet, just an idea */ if(sscanf(who,"%s@%s", who, mud)) { call_other(UDP_MAIL,"mail",who, /* who are we mailing */ mud, /* at which mud */ title, /* mail header */ route, /* route of mail */ message); /* mail message */ return; } #endif if(ob = find_player(who)) { tell_object(ob,"You have New mail from "+capitalize(REAL)+".\n"); ob = present("mailer", ob); if(ob) ob->refresh_mail(); } mail = ({}); restore_object(MAIL_DIR+who); mail += ({ ({ REAL, title, route, message, 1, private_mail, TIME, }), }); save_object(MAIL_DIR+who); if(ob) ob->load_mail(); restore_object(MAIL_DIR+REAL); write("Mail sent to "+ capitalize(who) +".\n"); } /**********************************************************************/ /* delete */ static status delete(string str) { int from, to; if(!str || !sscanf(str,"%d,%d", from,to) || !sscanf(str,"%d",from)) { write("delete #1,#2?\n"); return 1; } if(!to) to = from; if(from < 1 || to < from || to > sizeof(mail)) { write("You have only "+ sizeof(mail) +" message"); write(((sizeof(mail) == 1) ? "" : "s") +".\n"); return 1; } write("You delete message "); write(((from == to) ? from +"" : from +" to "+ to) +".\n"); mail = mail[0..(from-2)]+mail[to..(sizeof(mail)-1)]; if(current_message > sizeof(mail)) current_message = sizeof(mail); if(current_message < 1) current_message = 1; if(sizeof(mail)) { prompt(); } else { save_object(MAIL_DIR+REAL); } return 1; } /**********************************************************************/ /* reload mail object of recipient with new mail */ void load_mail() { int i; if(previous_object()) { if(!(sscanf(file_name(previous_object()), MAILER+"#%d",i) #ifdef INTERMUD || file_name(previous_object()) == UDP_MAIL #endif )) { return; } } restore_object(MAIL_DIR+(string)environment()->query_name(1)); } /***********************************************************************/ /* refresh mail object of recipient before sending new mail */ void refresh_mail() { int i; if(previous_object()) { if(!(sscanf(file_name(previous_object()), MAILER+"#%d", i) #ifdef INTERMUD || file_name(previous_object()) == UDP_MAIL #endif )) { return; } } save_object(MAIL_DIR+(string)environment()->query_name(1)); restore_object(MAIL_DIR+(string)environment()->query_name(1)); } /*************************************************************************/ /*************************************************************************/ /* read mail */ static status read(string str) { int i; if(!sizeof(mail)) { notify_fail("No Mail.\n"); return 0; } if(str && sscanf(str,"%d",i) == 1) { if(i < 1 || i > sizeof(mail)) { notify_fail("You only have "+ sizeof(mail) + " message"+((sizeof(mail)==1)?"":"s")+".\n"); return 0; } current_message = i - 1; do_read(current_message); return 1; } prompt(); return 1; } static void prompt() { write("\nNext Subject: "+mail[current_message-1][HEADER]); write(" ("+ ((mail[current_message-1][NEW]) ? "NEW" : "READ")); if(mail[current_message-1][PRIVATE]) write(", -CONFIDENTIAL-"); write(")\n(" + current_message +"/"+ sizeof(mail) +") [? - help]: "); input_to("mail_control"); } static void mail_control(string str) { string who; int num; int from, to; if(!str || str == "") { /* read current message */ do_read(current_message-1); return; } else if(str[0] == '?') { /* help */ write(" -=[ Mail Help ]=-\n\n"+ " ? this help\n"+ " f forward mail\n"+ " r reply mail\n"+ " h headers\n"+ " m mail\n"+ " p mail, and mark it as confidential (private)\n"+ " d delete\n"+ " #1 read mail number #1\n"+ " +/- move current message number up or down\n"+ " <cr> read current message\n"+ " c clip mail to clipboard\n"+ " x,q quit\n"+ " oops restore a mail deletion (maybe)\n"); if(this_player()->query_security_level()) { write(" l log mail to mailbox\n"); } } else if(str[0] == 'l') { write_file("/log/mailbox/"+ REAL +".mbox", "\n"+ mail[current_message-1][HEADER] +"\n"+ mail[current_message-1][ROUTE] + "Message:\n"+ mail[current_message-1][MESSAGE]); write("Message "+ current_message +" has been put into mailbox.\n"); } else if(str[0] == 'h') { headers(); return; } else if(str[0] == 'f') { /* forward */ if(sscanf(str,"f %d %s",num,who) || sscanf(str,"f %s",who)) { if(!num) num = current_message; forward(num +" "+ who); return; } else { write("f who? or f #num who?\n"); } } else if(str[0] == 'r') { /* reply */ if(str == "r") str = "r "+ current_message; if(sscanf(str,"r %d", num)) { if(!num) num = current_message; if(num < 1 || num > sizeof(mail)) { write("You have only "+ sizeof(mail) +" message"); write(((sizeof(mail) == 1) ? "" : "s") +".\n"); } else { reply(num +""); return; } } else { write("r #num?\n"); } } else if(str[0] == 'm') { /* mail */ if(sscanf(str,"m %s",who)) { mail(who); return; } else { write("m who?\n"); } } else if(str[0] == 'p') { /* private mail */ if(sscanf(str,"p %s",who)) { pmail(who); return; } else { write("p who?\n"); } } else if(str[0] == 'd') { if(sscanf(str,"d %s",str)) str = "d"+ str; if(str == "d") str = "d"+ current_message; if(sscanf(str,"d%d,%d",from,to) || sscanf(str,"d%d",from)) { if(!to) to = from; if(from < 1 || to < from || to > sizeof(mail)) { write("You have only "+ sizeof(mail) + " message"+((sizeof(mail)==1)?"":"s")+".\n"); } delete(from +","+ to); return; } else { write("d #1? or d #1,#2?\n"); } } else if(str[0] == '+') { /* move current message up */ sscanf(str,"+%d",num); current_message += (num) ? num : 1; if(current_message > sizeof(mail)) current_message = sizeof(mail); } else if(str[0] == '-') { /* move current message down */ sscanf(str,"-%d",num); current_message -= (num) ? num : 1; if(current_message < 1) current_message = 1; } else if(sscanf(str,"%d",num)) { /* read num */ if(num < 1 || num > sizeof(mail)) { write("You have only "+ sizeof(mail) + " message"+((sizeof(mail)==1)?"":"s")+".\n"); } else { current_message = num - 1; do_read(num-1); return; } } else if(str[0] == 'c') { /* clip to clipboard */ if(!sscanf(str,"c %d",num)) num = current_message; if(num < 1 || num > sizeof(mail)) { write("You have only "+ sizeof(mail) + " message"+((sizeof(mail)==1)?"":"s")+".\n"); } else { if(mail[num-1][PRIVATE]) { rm(PASTE); write_file(PASTE,mail[num-1][MESSAGE]); write("Copied message to clipboard.\n"); } else { write("sorry, you cannot 'clip' Confidential mail.\n"); } } } else if(str[0] == 'x' || str[0] == 'q') { write("Ok.\n"); save_object(MAIL_DIR+REAL); return; } else if(str == "oops") { write("Restoring...\n"); restore_object(MAIL_DIR+REAL); } prompt(); } static void do_read(int i) { object reader; string str; write("\nMessage Number "+ (i+1) +".\n"); write("Title: "+ mail[i][HEADER] +"\n"); write(mail[i][ROUTE]); write("Date: "+ mail[i][DATE_TIME] +"\n"); if(mail[i][PRIVATE]) write("\t\t-CONFIDENTIAL-\n"); write("\nMessage:\n"); reader = clone_object(PAGER); #ifdef NATIVE_MODE reader->move(this_player()); #else move_object(reader, this_player()); #endif /* NATIVE_MODE */ mail[i][NEW] = 0; str = mail[i][MESSAGE]; if(this_player()->ansi_on()) { str += OFF; } else { str = (string)this_player()->filter_ansi(str); } reader->page(str); } void editor_quit(string arg) { int i; if(!sscanf(file_name(previous_object()),EDITOR +"#%d",i)) return; recipient = 0; title = 0; route = 0; cc = ({}); message = 0; reply = 0; private_mail = 0; if(sizeof(mail)) prompt(); } /********************************************************************/ /* pager quit */ void pager_quit(string arg) { int i; if(!sscanf(file_name(previous_object()),PAGER +"#%d",i)) return; current_message += (current_message == sizeof(mail)) ? 0 : 1; prompt(); }