lima-1.0b5/
lima-1.0b5/driver/
lima-1.0b5/driver/ChangeLog.old/
lima-1.0b5/driver/Win32/
lima-1.0b5/driver/compat/
lima-1.0b5/driver/include/
lima-1.0b5/driver/testsuite/
lima-1.0b5/driver/testsuite/clone/
lima-1.0b5/driver/testsuite/command/
lima-1.0b5/driver/testsuite/data/
lima-1.0b5/driver/testsuite/etc/
lima-1.0b5/driver/testsuite/include/
lima-1.0b5/driver/testsuite/inherit/
lima-1.0b5/driver/testsuite/inherit/master/
lima-1.0b5/driver/testsuite/log/
lima-1.0b5/driver/testsuite/single/
lima-1.0b5/driver/testsuite/single/tests/compiler/
lima-1.0b5/driver/testsuite/single/tests/efuns/
lima-1.0b5/driver/testsuite/single/tests/operators/
lima-1.0b5/driver/testsuite/u/
lima-1.0b5/driver/tmp/
lima-1.0b5/etc/
lima-1.0b5/lib/WWW/help/
lima-1.0b5/lib/cmds/
lima-1.0b5/lib/cmds/create/
lima-1.0b5/lib/cmds/player/attic/
lima-1.0b5/lib/contrib/bboard/
lima-1.0b5/lib/contrib/boards/
lima-1.0b5/lib/contrib/marriage/
lima-1.0b5/lib/contrib/roommaker/
lima-1.0b5/lib/contrib/transient_effect/
lima-1.0b5/lib/daemons/channel/
lima-1.0b5/lib/daemons/imud/
lima-1.0b5/lib/data/
lima-1.0b5/lib/data/config/
lima-1.0b5/lib/data/links/
lima-1.0b5/lib/data/news/
lima-1.0b5/lib/data/players/
lima-1.0b5/lib/data/secure/
lima-1.0b5/lib/domains/
lima-1.0b5/lib/domains/std/2.4.5/maze1/
lima-1.0b5/lib/domains/std/2.4.5/npc/
lima-1.0b5/lib/domains/std/2.4.5/post_dir/
lima-1.0b5/lib/domains/std/2.4.5/sub/
lima-1.0b5/lib/domains/std/camera/
lima-1.0b5/lib/domains/std/config/
lima-1.0b5/lib/domains/std/cult/
lima-1.0b5/lib/domains/std/effects/
lima-1.0b5/lib/domains/std/misc/
lima-1.0b5/lib/domains/std/monsters/
lima-1.0b5/lib/domains/std/recorder/
lima-1.0b5/lib/domains/std/rooms/
lima-1.0b5/lib/domains/std/rooms/beach/
lima-1.0b5/lib/domains/std/rooms/labyrinth/
lima-1.0b5/lib/domains/std/school/
lima-1.0b5/lib/domains/std/school/O/
lima-1.0b5/lib/domains/std/spells/
lima-1.0b5/lib/domains/std/spells/stock-mage/
lima-1.0b5/lib/domains/std/spells/stock-priest/
lima-1.0b5/lib/help/
lima-1.0b5/lib/help/admin/
lima-1.0b5/lib/help/hints/General_Questions/
lima-1.0b5/lib/help/hints/Pirate_Quest/
lima-1.0b5/lib/help/player/
lima-1.0b5/lib/help/player/bin/
lima-1.0b5/lib/help/player/quests/
lima-1.0b5/lib/help/wizard/
lima-1.0b5/lib/help/wizard/coding/guilds/
lima-1.0b5/lib/help/wizard/coding/rooms/
lima-1.0b5/lib/help/wizard/lib/daemons/
lima-1.0b5/lib/help/wizard/lib/lfun/
lima-1.0b5/lib/help/wizard/lib/std/
lima-1.0b5/lib/help/wizard/mudos_doc/
lima-1.0b5/lib/help/wizard/mudos_doc/applies/
lima-1.0b5/lib/help/wizard/mudos_doc/applies/interactive/
lima-1.0b5/lib/help/wizard/mudos_doc/applies/parsing/
lima-1.0b5/lib/help/wizard/mudos_doc/concepts/
lima-1.0b5/lib/help/wizard/mudos_doc/driver/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/arrays/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/buffers/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/compile/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/filesystem/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/floats/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/functions/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/general/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/mappings/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/mixed/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/numbers/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/parsing/
lima-1.0b5/lib/help/wizard/mudos_doc/lpc/constructs/
lima-1.0b5/lib/help/wizard/mudos_doc/lpc/types/
lima-1.0b5/lib/include/driver/
lima-1.0b5/lib/log/
lima-1.0b5/lib/obj/admtool/
lima-1.0b5/lib/obj/admtool/internal/
lima-1.0b5/lib/obj/admtool/mudinfo/
lima-1.0b5/lib/obj/admtool/secure/
lima-1.0b5/lib/obj/secure/
lima-1.0b5/lib/obj/secure/cmd/
lima-1.0b5/lib/obj/secure/mailers/
lima-1.0b5/lib/obj/secure/shell/
lima-1.0b5/lib/obj/secure/shell/classes/
lima-1.0b5/lib/obj/tasktool/
lima-1.0b5/lib/obj/tasktool/internal/
lima-1.0b5/lib/open/
lima-1.0b5/lib/secure/
lima-1.0b5/lib/secure/cgi/
lima-1.0b5/lib/secure/modules/
lima-1.0b5/lib/secure/simul_efun/
lima-1.0b5/lib/std/adversary/
lima-1.0b5/lib/std/adversary/advancement/
lima-1.0b5/lib/std/adversary/armor/
lima-1.0b5/lib/std/adversary/blows/
lima-1.0b5/lib/std/adversary/death/
lima-1.0b5/lib/std/adversary/formula/
lima-1.0b5/lib/std/adversary/health/
lima-1.0b5/lib/std/adversary/pulse/
lima-1.0b5/lib/std/adversary/wield/
lima-1.0b5/lib/std/classes/event_info/
lima-1.0b5/lib/std/container/
lima-1.0b5/lib/std/living/
lima-1.0b5/lib/std/modules/contrib/
lima-1.0b5/lib/std/patterns/
lima-1.0b5/lib/std/race/
lima-1.0b5/lib/std/race/restricted/
lima-1.0b5/lib/std/room/
lima-1.0b5/lib/tmp/
lima-1.0b5/lib/trans/
lima-1.0b5/lib/trans/admincmds/
lima-1.0b5/lib/trans/obj/
lima-1.0b5/lib/wiz/
/* Do not remove the headers from this file! see /USAGE for more info. */

/*
** IMAIL_D -- Handle intermud mail.  This has different functionality 
** than local mail, because muds might be down, drop off the mudlist, etc.
**
**
** Some explanation of the implementation:
** Mail messages might go to users at different muds.  
** We might deliver successfully to one mud, and not to others.
** Therefore, we assign multiple outgoing ids to each post, one for each
** mud we're sending to.  When all those ids are ack'd, we know the post 
** has been fully delivered, so we remove that from our "internal queue".
** I deemed this better than copying the message x times if the message
** was getting sent to x muds.
**
** John Viega (rust@lima.mudlib.org) July 19, 1996.
*/
#define SAVE_FILE	"/data/mail/imud_queue"
#define ONE_DAY			(60 * 60 * 24)
#define ONE_HOUR		(60 * 60)
#define WARNING_INTERVAL	ONE_DAY
#include <driver/origin.h>

inherit M_DAEMON_DATA;
inherit CLASS_MAILMSG;
inherit M_COMPLETE;


varargs string array targ_map_to_list(mapping, int);
private void process_queue();
varargs private void handle_ack(mapping, int);

class outgoing_info
{
  string 	mudname;
  int		msgId;
  int		lastWarningTime;
}
int nextOutgoingId = 1;  /* I'd like to see this change to a string. */
int nextMsgId      = 1;

// Okay, this one maps id : (mudname, users, msgId)
private mapping outgoing_queue = ([]);

// This one maps msgId : mail_msg.  
private mapping internal_queue = ([]);

private int is_currently_up(string name)
{
 return member_array(IMUD_D->canon_mudname(name), 
		     IMUD_D->query_up_muds()) != -1;
}

nomask mixed get_complete_mudname(string name)
{
  mixed 	info;
  
  info = find_best_match_or_complete(name, IMUD_D->query_mudnames());
   
  if(!arrayp(info) || !sizeof(info))
    return ({});
  
  if(sizeof(info) == 1)
    return info[0];

  return info;
}

void create()
{
    ::create();
    process_queue();
    call_out("check_queue", ONE_HOUR);
}

private void check_queue()
{
  process_queue();
  call_out("check_queue", ONE_HOUR);
}

string format_addrs(mapping m)
{
  return implode(targ_map_to_list(m), ", ");
}

private string array quote_msg(class mail_msg msg)
{
  return ({
      "",  
      "---Original message follows:",
      sprintf("FROM: %s", msg->sender),
      sprintf("TO: %s", arrayp(msg->to_list) ? implode(msg->to_list, ", ") :
	      format_addrs(msg->to_list)),
      sprintf("CC: %s", arrayp(msg->cc_list) ? implode(msg->cc_list, ", ") :
	      format_addrs(msg->cc_list)),
      sprintf("SUBJECT: %s", msg->subject)
	}) + msg->body;
}

private void send_warning (int id, string array warning)
{
  class outgoing_info	this_info;
  string 		recipient;


  if(this_info->lastWarningTime + WARNING_INTERVAL > time())
    {
      return;   // Don't send out a warning every single time we try to send.
    }

  this_info = outgoing_queue[id];
  recipient = ((class mail_msg)internal_queue[this_info->msgId])->sender;

  unguarded (1, (: MAIL_D->send_mail ("I3 Mail Service",
				   "Delivery Still Pending",
				      ({ "*** Your mail hasn't been fully "
					   "delivered yet."}) +
				   $(warning)  + 
			   quote_msg(internal_queue[$(this_info->msgId)]),
				   ({$(recipient)}),
				   ({})) :));
  
  this_info->lastWarningTime = time();
  save_me();
}

private void report_errors (int id, string array errorset)
{
  class outgoing_info	this_info;
  string 		recipient;

  this_info = outgoing_queue[id];
  recipient = ((class mail_msg)internal_queue[this_info->msgId])->sender;

  unguarded (1, (: MAIL_D->send_mail ("I3 Mail Service",
				   "Failed mail",
				   $(errorset)  + 
			      quote_msg(internal_queue[$(this_info->msgId)]),
				   ({$(recipient)}),
				   ({})) :));
}

private void error_ambiguous_or_unknown_mudnames(class mail_msg msg, 
						 mapping info)
{
  string array 	problems = ({});
  string array 	whatItCouldHaveBeen;
  string	whatYouCalledIt;

  foreach(whatYouCalledIt, whatItCouldHaveBeen in info)
    {
      if(sizeof(whatItCouldHaveBeen))
	{
	  problems += 
	    ({sprintf("Ambigous mud name, '%s'.  Could have been: %s",
		      whatYouCalledIt, implode(whatItCouldHaveBeen, ", "))});
	}
      else
	{
	  problems += ({ sprintf("Unknown mud, '%s'.", whatYouCalledIt) });
	}
    }

    unguarded (1, (: MAIL_D->send_mail ("I3 Mail Service",
				      "Delivery problems",
				   $(problems)  + 
			      quote_msg($(msg)),
				   ({$(msg->sender)}),
				   ({})) :));
}

void receive_ack(mapping ack_list)
{
  if (previous_object() != find_object(IMUD_D))
    {
      error("Illegal call to receive_ack.");
    }
  handle_ack(ack_list, 1);
}

varargs private void handle_ack(mapping ack_list, int flag)
{
  int			id;
  string array		errorset;
  class outgoing_info	info;
  class mail_msg	this_msg;

  /* Okay, if there are failures, report them. */
  foreach (id, errorset in ack_list)
    {
      info = outgoing_queue[id];
      if(arrayp(errorset) && sizeof(errorset))
	{
	  if(flag)
	    {
	      report_errors (id, ({sprintf("%s reports the following errors:",
				       info->mudname)}) + errorset);
	    }
	  else
	    {
	      report_errors (id, ({"The following delivery errors occured:"})
		+ errorset);

	    }
	}
      map_delete(outgoing_queue, id);
      this_msg = internal_queue[info->msgId];
      if(!(--this_msg->dels_pending))
	{
	  map_delete(internal_queue, info->msgId);
	}
    }
    save_me();
}


private array prep_message(int id, class outgoing_info info)
{
  class	mail_msg msg = internal_queue[info->msgId];
  return ({
    "mail",
      id,
      msg->sender,
      msg->to_list,
      msg->cc_list,
      ({}),
      msg->date,
      msg->subject,
      implode(msg->body, "\n")
  });
}

private void send_message(int id)
{
  class outgoing_info	this_info;
  array			packet;

  this_info = outgoing_queue[id];
  if (!IMUD_D->mud_exists(this_info->mudname))
    {
      handle_ack (([id : ({sprintf("Mud '%s' is not known at all by I3 "
				    "(might have dropped off the mudlist)", 
				    this_info->mudname) }) ]));
      return;
    }
  if(!IMUD_D->has_service(this_info->mudname, "mail"))
    {
      handle_ack (([id : ({sprintf("Mud '%s' doesn't support I3 mail.",
				    this_info->mudname) }) ]));
      return;
    }
  if (!is_currently_up(this_info->mudname))
    {
      send_warning(id, ({ "Couldn't deliver messgage because " + 
			    this_info->mudname + " is currently down."}));
    }
  
  packet = prep_message(id, this_info);
  IMUD_D->send_mail_message_to_mud(packet, this_info->mudname); 
}

private void process_queue()
{
  int			i;

  foreach(i in keys(outgoing_queue))
    {
      send_message(i);
    }
}


private mapping find_remote_recipients(class mail_msg msg)
{
  mapping 	remote_info = ([]);
  string	name;
  mapping	ambiguous_mudnames = ([]);
  mapping	new_to = ([]);
  mapping	new_cc = ([]);
  int		i;

  for(i = 0; i < sizeof(msg->to_list); i++)
    {
      string user, mudname; 
      mixed  mudinf;
      name = msg->to_list[i];
      if(sscanf(name, "%s@%s", user, mudname))
	{
	  mudname = lower_case(mudname);
	  if(ambiguous_mudnames[mudname])
	    {
	      continue;
	    }
	  mudinf = get_complete_mudname(mudname);
	  if(arrayp(mudinf))
	    {
	      ambiguous_mudnames[mudname] = mudinf;
	      continue;
	    }
	  else
	    {
	      mudinf = IMUD_D->canon_mudname(mudinf);
	    }
	  if(!remote_info[mudinf])
	    {
	      new_to[mudinf] 	  = ({ user });
	      remote_info[mudinf] = ({ user });
	    }
	  else
	    {
	      new_to[mudinf]      += ({ user });
	      remote_info[mudinf] += ({ user });
	    }
	}
      else
	{
	  if(new_to[mud_name()])
	    {
	      new_to[mud_name()] += ({name});
	    }
	  else
	    {
	      new_to[mud_name()] = ({name});
	    }
	}
    }
  msg->to_list = new_to;

  for(i = 0; i < sizeof(msg->cc_list); i++)
    {
      string user, mudname; 
      mixed  mudinf;
      name = msg->cc_list[i];
      if(sscanf(name, "%s@%s", user, mudname))
	{
	  mudname = lower_case(mudname);
	  if(ambiguous_mudnames[mudname])
	    {
	      continue;
	    }
	  mudinf = get_complete_mudname(mudname);
	  if(arrayp(mudinf))
	    {
	      ambiguous_mudnames[mudname] = mudinf;
	      continue;
	    }
	  else
	    {
	      mudinf = IMUD_D->canon_mudname(mudinf);
	    }
	  if(!remote_info[mudinf])
	    {
	      new_cc[mudinf] 	  = ({ user });
	      remote_info[mudinf] = ({ user });
	    }
	  else
	    {
	      if(new_cc[mudinf])
		{
		  new_cc[mudinf]      += ({ user });
		}
	      else
		{
		  new_cc[mudinf] = ({ user });
		}
	      remote_info[mudinf] += ({ user });
	    }
	}
      else
	{
	  if(new_cc[mud_name()])
	    {
	      new_cc[mud_name()] += ({name});
	    }
	  else
	    {
	      new_cc[mud_name()] = ({name});
	    }
	}
    }
  msg->cc_list = new_cc;

    if(sizeof(ambiguous_mudnames))
      {
	error_ambiguous_or_unknown_mudnames(msg, ambiguous_mudnames);
      }
    return remote_info;
}

void enqueue_message(class mail_msg msg)
{
  mapping		remote_info;
  string		mudName;
  int			myMsgId;

  if (previous_object() != find_object(MAIL_D))
    {
      error("Illegal call to enqueue_message");      
    }
  remote_info = find_remote_recipients(msg);

  if (!sizeof(remote_info))
    {
      // No imud users, ambigous or bad mud names...
      return;
    }

  myMsgId = nextMsgId++;
  msg->dels_pending = sizeof(remote_info);
  internal_queue[myMsgId] = msg;
  foreach ( mudName in keys(remote_info) )
    {
      class outgoing_info 	myInfo;

      myInfo = new(class outgoing_info);
      myInfo->mudname = mudName;
      myInfo->msgId   = myMsgId;
      myInfo->lastWarningTime = 0;
      outgoing_queue[nextOutgoingId] = myInfo;
      send_message(nextOutgoingId++);
    }
  save_me();
}

private string array get_local_recipients(mixed array mail_packet)
{
  string array	mudnames;
  string array 	local_recipients = ({});

  mudnames = filter(keys(mail_packet[3]), // TO list
		    (: mud_name() == IMUD_D->canon_mudname($1) :)); 
  if(sizeof(mudnames))
    {
      local_recipients += mail_packet[3][mudnames[0]];
    }

  mudnames = filter(keys(mail_packet[4]), //CC list
		    (: mud_name() == IMUD_D->canon_mudname($1) :)); 
  if(sizeof(mudnames))
    {
      local_recipients += mail_packet[4][mudnames[0]];
    }
  local_recipients += mail_packet[5];  // BCC list
  local_recipients = clean_array(local_recipients);

  return local_recipients;
}


// Converts a to_list or cc_list of the form:
//    ([
//        MUD-A : ({ user-1, user-2, }),
//        MUD-B : ({ user-1, user-2, }),
//    ])
//
// To one of the form: ({user-1@MUD-A, user-2@MUD-A, ... })

// If flag, I want @This Mud to be left off.
varargs string array targ_map_to_list(mapping info, int flag)
{
  string array ret = ({});

  foreach(string mudname, string array userlist in info)
    {
      if(flag && mudname == mud_name())
	{
	  ret += userlist;
	}
      else
	{
	  ret += map(userlist, (: $1 + "@" + $(mudname) :));
	}
    }

  return ret;
}

string array incoming_mail(string mudname, mixed array mail_packet)
{
  string array 	local_recipients;
  string array	errors = ({});
  int	 	i;

  if(previous_object() != find_object(IMUD_D))
    {
      error("illegal call to incoming_mail");
    }
  if(!mapp(mail_packet[3]))
    {
      mail_packet[3] = ([]);
      errors += ({"Imud implementation error: TO list should be a mapping"});
    }
  if(!mapp(mail_packet[4]))
    {
      mail_packet[4] = ([]);
      errors += ({"Imud implementation error: CC list should be a mapping"});
    }
  if(!arrayp(mail_packet[5]))
    {
      mail_packet[5] = ({});
      errors += ({"Imud implementation error: BCC list should be an array"});
    }
  // Get local recipients.
  local_recipients = get_local_recipients(mail_packet);

  // Check their validity.
  i = sizeof(local_recipients);
  while(i--)
    {
      if(!user_exists(local_recipients[i]))
	{
	  errors += ({sprintf("'%s' is not a user on %s.", local_recipients[i],
			      mud_name())});
	  local_recipients[i] = 0;
	}
    }
  local_recipients -= ({0});

  if(sizeof(local_recipients))
    {
      unguarded(1, (: MAIL_D->send_mail($(mail_packet)[2] + "@" + $(mudname), 
				    $(mail_packet)[7],  // subject
				    $(mail_packet)[8],  // body
				    targ_map_to_list($(mail_packet)[3], 1),
				    targ_map_to_list($(mail_packet)[4], 1),
				    $(mail_packet)[6])  // send time.
		:));
    }
  return errors;
}

void test()
{
  class mail_msg	test;

  test = new(class mail_msg);
  test->sender = "rust";
  test->to_list = ({"rust@zork"});
  test->cc_list = ({});
  test->subject = "Blah";
  test->date    = time();
  test->thread_id = test->date;
  test->body    = ({"This is test mail."});
  enqueue_message(test);
}