| // MorgenGrauen MUDlib |
| // |
| // mailer.c |
| // |
| // $Id: mailer.c 9547 2016-04-17 19:27:47Z Zesstra $ |
| |
| /* |
| *------------------------------------------------------------ |
| * The mail demon. Receives mail from users and delivers it into |
| * the mail directory. |
| * |
| * Deepthought, Nightfall, 25-May-92 |
| * Remove-Functions : Jof, 29-June-92 |
| * Caching, DeleteUnreadFolder, small changes: Loco, 08-Feb-97 |
| * General clean-up and speed-up: Tiamak, 18-Jan-2000 |
| * DON'T USE restore_object any more, use GetFolders instead! |
| *------------------------------------------------------------ |
| * |
| * Save file format (sort of formal notation): |
| * |
| * mixed *folders = ({ |
| * ({ string name1; string name2; ... string nameN; }) |
| * ({ mixed *msgs1; mixed *msgs2; ... mixed *msgsN; }) |
| * }) |
| * |
| * Each msgs field is an array of messages: |
| * |
| * mixed *msgs = ({ mixed *message1; ... mixed *messageM }) |
| * |
| * A message is represented as an array with the following fields: |
| * |
| * mixed *message = ({ |
| * string from; |
| * string sender; |
| * string recipient; |
| * string *cc; |
| * string *bcc; |
| * string subject; |
| * string date; |
| * string id; |
| * string body; |
| * }) |
| * |
| * The mailer demon (/secure/mailer) provides the following functions: |
| * |
| * string *DeliverMail(mixed *message) |
| * Hand a mail message over to the mailer demon. The mailer |
| * demon extracts recipients from the recipient, cc and bcc |
| * fields and removes the bcc information. It then deposits |
| * the message to the mail files of all recipients. A valid |
| * message is shown above. Returns a list of successfully |
| * delivered recipients. |
| * |
| * int FingerMail(string user) |
| * Gives the number of unread messages a user has. |
| *------------------------------------------------------------ |
| */ |
| #pragma strict_types |
| #pragma no_clone |
| #pragma no_shadow |
| #pragma no_inherit |
| #pragma verbose_errors |
| #pragma pedantic |
| #pragma warn_deprecated |
| |
| #include <config.h> |
| #include <mail.h> |
| #include <wizlevels.h> |
| |
| // debugging |
| #define DEBUG(msg) if ( find_player("zesstra") ) \ |
| tell_object( find_player("zesstra"), "MAILER: "+msg ) |
| #undef DEBUG |
| #define DEBUG(x) |
| |
| // write out a message to the recipient |
| #define NOTIFY_RECIPIENT |
| // who gets undeliverable mail? |
| #define BOUNCE_ADDR "mud@mg.mud.de" |
| #define SECURITY(x) (geteuid(x) == ROOTID || geteuid(x) == MAILID) |
| // flag for _DeliverMail |
| #define MAIL_DELAYED 4096 |
| |
| // prototypes |
| protected void create(); |
| static int GetFolders( string user ); |
| static string _unify( string str ); |
| static string *unify( string *str ); |
| static string *expand( string *addr, int expa ); |
| static string _filter_addr( string addr ); |
| public string *DeliverMail( mixed msg, int expa ); |
| public int FingerMail( string user ); |
| static void save_msg( mixed msg, string user ); |
| public int RemoveMsg( int msg, int folder, string user ); |
| public int MoveMsg( int msg, int folder, string newfolder, string user ); |
| public int DeleteUnreadFolder( string user ); |
| public int RemoveFolder( string folder, string user ); |
| public int MakeFolder( string folder, string user ); |
| public int query_recipient_ok( string name ); |
| public void deliver_mail( string recipient, string from, string subject, |
| string mail_body ); |
| |
| |
| mixed *folders; /* used for save and restore of mail files */ |
| static mapping alias; |
| static string cachedname; /* whose folder is still in memory? */ |
| |
| |
| protected void create() |
| { |
| mixed tmp; |
| int i; |
| string s1, s2; |
| |
| seteuid(ROOTID); |
| alias=([]); |
| |
| if ( tmp = read_file("/mail/system.mailrc") ){ |
| tmp = explode( tmp, "\n" ); |
| |
| for ( i = sizeof(tmp); i--; ) |
| if ( sscanf( tmp[i], "%s %s", s1, s2 ) == 2 ) |
| alias[s1] = s2; |
| } |
| } |
| |
| |
| // GetFolders laedt einen folder, wenn er nicht im cache ist, und gibt |
| // 0 zurueck, wenn der folder nicht vorhanden oder evtl auch leer ist. |
| // Sinn: Vor allem bei Listenargumenten im mailer kann es leicht vorkommen, |
| // dass dasselbe mailfile einige Male hintereinander gebraucht wird. |
| |
| static int GetFolders( string user ) |
| { |
| if ( user == cachedname ){ |
| DEBUG( "Using cached folder for " + user + "\n" ); |
| return sizeof(folders[1]); |
| } |
| |
| cachedname = user; |
| |
| if ( !restore_object( MAILPATH + "/" + user[0..0] + "/" + user ) ){ |
| DEBUG( "Loading folder: " + user + " (empty)\n" ); |
| folders = ({ ({}), ({}) }); |
| return 0; |
| } |
| |
| DEBUG( "Loading folder:" + user + "\n" ); |
| return 1; |
| } |
| |
| |
| static string _unify( string str ) |
| { |
| return str[0] == '\\' ? str[1..] : str; |
| } |
| |
| |
| static string *unify( string *str ) |
| { |
| if ( !pointerp(str) ) |
| return ({}); |
| |
| str = map( filter( str, #'stringp/*'*/ ), #'lower_case/*'*/ ); |
| str = map( str, "_unify", this_object() ); |
| |
| return m_indices( mkmapping(str) ); |
| } |
| |
| |
| #define MG_NAMES ({ MUDNAME, "mg", "morgengrauen", "mud", "mg.mud.de" }) |
| |
| string expandSystemRecursive(string addr,int maxrec){ |
| string *list,*tlist; |
| string ret,tmp; |
| int i,size; |
| |
| if(maxrec>8 || !addr){ |
| return addr; |
| } |
| maxrec++; |
| |
| tlist=({}); |
| ret=""; |
| |
| list=explode(addr,","); |
| list-=({""}); |
| size=sizeof(list); |
| for(i=0;i<size;i++){ |
| if(tmp=alias[list[i]]){ |
| tlist+=explode(tmp,","); |
| } |
| else{ |
| tlist+=({list[i]}); |
| } |
| } |
| tlist-=({""}); |
| ret=implode(tlist,","); |
| |
| if((ret!=addr)!=0){ |
| ret=expandSystemRecursive(ret,maxrec); |
| } |
| return ret; |
| } |
| |
| // expa: also do alias and forward-expansion? (for inbound external mail) |
| // expa == 0 means full expansion, known flags are NO_SYSTEM_ALIASES |
| // and NO_USER_ALIASES |
| static string *expand( string *addr, int expa ) |
| { |
| string tmp, *new, *ret; |
| int i; |
| closure qf; |
| |
| ret = ({}); |
| addr -= ({""}); |
| qf = symbol_function( "QueryForward", FWSERV ); |
| |
| for ( i = sizeof(addr); i--; ){ |
| addr[i] = lower_case( addr[i] ); |
| // @morgengrauen-namen werden lokal zugestellt. |
| if ( sizeof(new = explode( addr[i], "@" )) == 2 && |
| member( MG_NAMES, new[1] ) != -1 ) |
| addr[i] = new[0]; |
| |
| if ( !(expa & NO_SYSTEM_ALIASES) && tmp = expandSystemRecursive(addr[i],0) ){ |
| ret += explode( tmp, "," ); |
| } |
| else |
| ret += ({ addr[i] }); |
| } |
| |
| for ( i = sizeof(ret); i--; ){ |
| if ( ret[i][0] == '\\' ) |
| ret[i] = ret[i][1..]; |
| else if ( !(expa & NO_USER_ALIASES) ) |
| ret = ret - ({ ret[i] }) + |
| explode( funcall( qf, ret[i] ), "," ); |
| } |
| |
| return ret; |
| } |
| |
| |
| static string _filter_addr( string addr ) |
| { |
| addr = regreplace( addr, "[^<]*<(.*)>[^>]*", "\\1", 0); |
| return regreplace( addr, " *([^ ][^ ]*).*", "\\1", 0); |
| } |
| |
| #ifdef INTERNET_MAIL_ENABLED |
| #define FOOTER \ |
| "\n*****************************************************************\n" \ |
| "* MorgenGrauen MailRelay v1.0 - Processed %s, %s *\n" \ |
| "* MorgenGrauen - mg.mud.de 23 - 87.79.24.60 23 *\n" \ |
| "*****************************************************************" |
| #endif |
| |
| public string *DeliverMail( mixed msg, int expa ) |
| { |
| string sender, *recipients, *recok, t, *tmp; |
| mixed *newmsg; |
| int i; |
| #ifdef INTERNET_MAIL_ENABLED |
| int ext; |
| #endif |
| |
| if ( !pointerp(msg) || sizeof(msg) != 9 ) |
| return 0; |
| |
| DEBUG( sprintf( "DeliverMail: %O %O\n", msg[0..4] +({0})+ msg[6..7], expa ) ); |
| t = ctime(time()); |
| |
| // Ohne Empfaenger wird abgebrochen |
| if (!stringp(msg[MSG_RECIPIENT])) |
| return 0; |
| |
| if ( !(expa & MAIL_DELAYED) ){ |
| /* determine the real sender */ |
| if ( extern_call() && object_name(previous_object())[0..7] != "/secure/" ) |
| sender = getuid( this_interactive() || previous_object() ); |
| else |
| sender = msg[MSG_SENDER]; |
| |
| /* make a list of all recipients */ |
| recipients = ({ msg[MSG_RECIPIENT] }); |
| |
| if ( !(expa & NO_CARBON_COPIES) ){ |
| if ( pointerp(msg[MSG_CC]) ) |
| recipients += msg[MSG_CC]; |
| |
| if ( pointerp(msg[MSG_BCC]) ) |
| recipients += msg[MSG_BCC]; |
| } |
| |
| // Mail-Aliase ersetzen |
| recipients = expand( recipients, expa ); |
| |
| // doppelte Adressen loeschen (nebenbei: auf Kleinschreibung wandeln) |
| recipients = unify( recipients ); |
| |
| // Realnamen und Kommentare entfernen |
| recipients = map( recipients, "_filter_addr", this_object() ); |
| |
| // auf ungueltige Zeichen checken |
| if ( sizeof(tmp = regexp( recipients, "^[-_.@a-z0-9]*$" )) |
| != sizeof(recipients) ){ |
| tmp = recipients - tmp; |
| |
| for ( i = sizeof(tmp); i--; ) |
| log_file( "MAIL_INVALID", sprintf( "%s: Mail von %O (%O) an " |
| "'%O'.\n", dtime(time()), |
| msg[MSG_FROM], |
| sender, tmp[i] ) ); |
| |
| recipients -= tmp; |
| } |
| |
| // check for valid Subject and Body |
| if (!stringp(msg[MSG_SUBJECT])) |
| msg[MSG_SUBJECT] = "(no subject given)"; |
| if (!stringp(msg[MSG_BODY])) |
| msg[MSG_BODY] = |
| "\n\nSorry - This mail was delivered without a mail body\n\n"; |
| |
| DEBUG( sprintf( "NEED TO DELIVER TO %O\n", recipients ) ); |
| |
| /* build the new message */ |
| newmsg = ({ msg[MSG_FROM], sender, msg[MSG_RECIPIENT], |
| msg[MSG_CC], "", msg[MSG_SUBJECT], |
| dtime(time()), MUDNAME + ":" + time(), |
| msg[MSG_BODY] }); |
| } |
| |
| /* Send it off ... */ |
| recok = ({ }); |
| |
| // ACHTUNG: durch expand() geaenderte Adressen werden zugestellt, |
| // aber durch /mail/mailer.c zusaetzlich als unzustellbar genannt! |
| |
| for ( i = sizeof(recipients); i-- /*&& get_eval_cost() > 500000*/; ){ |
| DEBUG( sprintf( "Begin delivering to %s. Evalcosts left: %d.\n", |
| recipients[i], get_eval_cost() ) ); |
| if ( member( recipients[i], '@' ) > 0 && |
| strstr( recipients[i], "daemon" ) < 0 ) { |
| string rec, mud; |
| |
| tmp = explode( recipients[i], "@" ); |
| mud = tmp[1]; |
| rec = tmp[0]; |
| sender = regreplace( sender, "@", "%", 1 ); |
| // Zustellung via Intermud-Mail an andere Muds. |
| if ( member( mud, '.' ) == -1 ) { |
| "/secure/udp_mail"->deliver_mail( rec, mud, sender, |
| msg[MSG_SUBJECT], |
| msg[MSG_BODY] ); |
| recok += ({ recipients[i] }); |
| } |
| #ifdef INTERNET_MAIL_ENABLED |
| // Zustellung in den Rest des Internets. |
| else { |
| ext = 1; |
| sender = explode( sender, "%" )[0]; |
| rec = explode( regreplace( rec, "@", "%", 1 ), "%" )[0]; |
| mud = explode( regreplace( mud, "@", "%", 1 ), "%" )[0]; |
| |
| write_file( sprintf( "/mail/outbound/%s.%d-%d-%d", |
| sender, time(), i, random(123456) ), |
| sprintf( "%s\n%s@%s\n" |
| "Subject: %s\n" |
| "X-MUD-From: %s\n" |
| "X-MUD-To: %s\n" |
| "X-MUD-Cc: %s\n" |
| "X-MU-Subject: %s\n\n", |
| sender, rec, mud, |
| msg[MSG_SUBJECT], |
| sender, recipients[0], |
| pointerp(msg[MSG_CC]) ? |
| implode( msg[MSG_CC], "," ) : "", |
| msg[MSG_SUBJECT] ) + msg[MSG_BODY] + |
| sprintf( FOOTER, t[4..10] + t[20..], t[11..18] ) |
| + "\n" ); |
| recok += ({ recipients[i] }); |
| } |
| #endif // INTERNET_MAIL_ENABLED |
| |
| } |
| else |
| if ( file_size( SAVEPATH + recipients[i][0..0] + "/" + |
| recipients[i] + ".o" ) >=0 ){ |
| save_msg( newmsg, recipients[i] ); |
| recok += ({ recipients[i] }); |
| } |
| else { |
| string *tmpmsg = copy(newmsg); |
| tmpmsg[MSG_BODY] = "--- Text der Mail geloescht. ---\n"; |
| write_file( sprintf( "/mail/outbound/postmaster.%d-%d", |
| time(), random(time()) ), |
| sprintf( "postmaster\n" + BOUNCE_ADDR + |
| "\nSubject: Undeliverable Mail\n%O\n", |
| tmpmsg) ); |
| } |
| DEBUG( sprintf( "End delivering to %s. Evalcosts left: %d.\n", |
| recipients[i], get_eval_cost() ) ); |
| } |
| #ifdef INTERNET_MAIL_ENABLED |
| if ( ext ) |
| send_udp( UDPSERV, 4123, "DELIVERMAIL" ); |
| #endif |
| return recok; |
| } |
| |
| |
| public int FingerMail( string user ) |
| { |
| int newfolder, i; |
| |
| //Zugriff beschraenken, Zahl der gelesenen Mails ist Privatsphaere |
| if (!objectp(this_interactive()) || !stringp(user) || !sizeof(user)) |
| return(-1); |
| if ((getuid(this_interactive())!=user) && |
| (process_call() || !ARCH_SECURITY)) return(-1); |
| |
| if ( !GetFolders(user) ) |
| return 0; |
| |
| if ( (newfolder = member(folders[0],"unread")) != -1 ) |
| return sizeof(folders[1][newfolder]); |
| |
| return 0; |
| } |
| |
| |
| static void save_msg( mixed msg, string user ) |
| { |
| int newfolder; |
| object p; |
| |
| GetFolders( user ); |
| |
| /* if folder 'unread' doesn't exist, create it */ |
| newfolder = member( folders[0], "unread"); |
| |
| if ( newfolder == -1 ){ |
| folders[0] += ({ "unread" }); |
| folders[1] += ({ ({ }) }); |
| newfolder = member( folders[0], "unread"); |
| } |
| |
| folders[1][newfolder] += ({ msg }); |
| save_object( MAILPATH + user[0..0] + "/" + user ); |
| #ifdef NOTIFY_RECIPIENT |
| if ( p = find_player(user) ) |
| tell_object( p, "Ein Postreiter ruft Dir aus einiger Entfernung zu, " |
| "dass Du neue Post hast!\n" ); |
| #endif |
| } |
| |
| |
| /* Remove a message from a folder */ |
| public int RemoveMsg( int msg, int folder, string user ) |
| { |
| if ( !SECURITY(previous_object()) ) |
| return -2; |
| |
| if ( !GetFolders(user) ) |
| return -1; /* No such folder */ |
| |
| if ( !pointerp(folders) || !pointerp(folders[0]) || |
| folder >= sizeof(folders[0]) ) |
| return -1; |
| |
| if ( msg < 0 || sizeof(folders[1][folder]) <= msg ) |
| return 0; /* No such msg */ |
| |
| folders[1][folder][msg..msg] = ({}); |
| |
| save_object( MAILPATH + user[0..0] + "/" + user ); |
| return 1; /* Success */ |
| } |
| |
| |
| /* Move message into another folder */ |
| public int MoveMsg( int msg, int folder, string newfolder, string user ) |
| { |
| int target; |
| |
| if ( !SECURITY(previous_object()) ) |
| return -2; |
| |
| if ( !GetFolders(user) ) |
| return -1; /* Source folder not found */ |
| |
| if ( !pointerp(folders) || !pointerp(folders[0]) || |
| folder >= sizeof(folders[0]) ) |
| return -1; |
| |
| if ( msg < 0 || sizeof(folders[1][folder]) <= msg ) |
| return 0; /* No such msg */ |
| |
| if ( (target = member(folders[0], newfolder)) == -1 ) |
| return -3; |
| |
| if ( target == folder ) |
| return 1; |
| |
| if ( !pointerp(folders[1][target]) ) |
| folders[1][target] = ({ folders[1][folder][msg] }); |
| else |
| folders[1][target] += ({ folders[1][folder][msg] }); |
| |
| return RemoveMsg( msg, folder, user ); |
| } |
| |
| |
| public int DeleteUnreadFolder( string user ) |
| { |
| int unread, newmail; |
| |
| if ( !SECURITY(previous_object()) ) |
| return -2; |
| |
| if ( !GetFolders(user) ) |
| return -1; /* Source folder not found */ |
| |
| if ( (unread = member( folders[0], "unread")) == -1 ) |
| return 0; |
| |
| if ( (newmail = member( folders[0], "newmail")) == -1 ){ |
| folders[0] += ({ "newmail" }); |
| folders[1] += ({({})}); |
| newmail = sizeof(folders[1]) - 1; |
| } |
| |
| if ( !pointerp(folders[1][newmail]) ) |
| folders[1][newmail] = ({}); |
| |
| if ( pointerp(folders[1][unread]) ) |
| folders[1][newmail] += folders[1][unread]; |
| |
| folders[0][unread..unread] = ({}); |
| folders[1][unread..unread] = ({}); |
| |
| save_object( MAILPATH + user[0..0] + "/" + user ); |
| return 0; |
| } |
| |
| |
| public int RemoveFolder( string folder, string user ) |
| { |
| int i; |
| |
| if ( !SECURITY(previous_object()) ) |
| return -2; |
| |
| if ( !GetFolders(user) ) |
| return -1; /* No such folder */ |
| |
| if ( (i = member( folders[0], folder)) == -1 ) |
| return -1; /* No such folder */ |
| |
| if ( sizeof(folders[1][i]) > 0 ) |
| return 0; /* Folder not empty */ |
| |
| folders[0][i..i] = ({}); |
| folders[1][i..i] = ({}); |
| |
| save_object( MAILPATH + user[0..0] + "/" + user ); |
| return 1; |
| } |
| |
| |
| public int MakeFolder( string folder, string user ) |
| { |
| if ( !SECURITY(previous_object()) ) |
| return -2; |
| |
| if ( !folder || !stringp(folder) ) |
| return -1; /* Huh ? */ |
| |
| if ( folder == "unread" ) |
| return 0; /* Folder exists virtually :) */ |
| |
| GetFolders( user ); |
| |
| if ( member( folders[0], folder) != -1 ) |
| return 0; /* Folder exists */ |
| |
| folders[0] = folders[0] + ({ folder }); |
| folders[1] = folders[1] + ({ ({}) }); |
| |
| save_object( MAILPATH + user[0..0] + "/" + user ); |
| return 1; |
| } |
| |
| |
| public int query_recipient_ok( string name ) |
| { |
| #if INTERNET_MAIL_ENABLED |
| return (file_size( "secure/save/" + name[0..0] + "/" + name + ".o" ) > 0 |
| || member( name, '%' ) > 0 || member( name, '@' ) > 0 ); |
| #else |
| // es darf zwar ein @ in der Adresse vorkommen, dahinter aber kein . mehr |
| // (dann ist es ne Mail via Intermud-Mail, nicht ins Internet). |
| string *tmp; |
| return (file_size( "secure/save/" + name[0..0] + "/" + name + ".o" ) > 0 |
| || member( name, '%' ) > 0 |
| || (sizeof(tmp=explode(name,"@")) == 2 && strstr(tmp[1],".") == -1)); |
| #endif |
| } |
| |
| |
| public void deliver_mail( |
| string recipient, /* the local players real name*/ |
| string from, /* A string depicting the sender */ |
| string subject, /* The mail subject/header */ |
| string mail_body /* The actual mail message */ ) |
| { |
| DEBUG( sprintf("DELIVER %O\n", |
| ({ from, from, recipient, ({}), ({}), subject, time() })) ); |
| |
| // Geloggt wird, wenn ein aufrufendes Objekt nicht sicher ist. |
| if (object_name(previous_object())[0..7]!="/secure/") |
| write_file("/secure/ARCH/DELIVER_MAIL", |
| sprintf("%s : Aufruf von /secure/mailer->deliver_mail()\n" |
| " Sender: %O Empfaenger: %O\n PO: %O TI: %O TP:%O\n\n", |
| ctime(time()),from, recipient, |
| previous_object(), this_interactive(), this_player())); |
| |
| DeliverMail( ({ from, from, recipient, ({}), ({}), subject, time(), |
| "EXTERNAL", mail_body }), 0 ); |
| } |