Index: ChangeLog
===================================================================
--- ChangeLog	(revision 8c9284854a3fc6c3ac6cd959706f5674878cd849)
+++ ChangeLog	(revision 37eab7f7cd2720c3f612c28783d596910005f4bf)
@@ -6,4 +6,6 @@
 	More AIM logout detection
 	Don't proclaim "interfaces changed" on first build.
+	Added the 'loopback' message type
+	Added the 'loopwrite' command
 	
 2.0.10
Index: commands.c
===================================================================
--- commands.c	(revision 8c9284854a3fc6c3ac6cd959706f5674878cd849)
+++ commands.c	(revision 37eab7f7cd2720c3f612c28783d596910005f4bf)
@@ -134,4 +134,9 @@
 	      "aimzwrite <user>",
 	      "Send an aim message to a user.\n"),
+
+  OWLCMD_ARGS("loopwrite", owl_command_loopwrite, OWL_CTX_INTERACTIVE,
+	      "send a loopback message",
+	      "loopwrite",
+	      "Send a local message.\n"),
 
   OWLCMD_ARGS("zcrypt", owl_command_zcrypt, OWL_CTX_INTERACTIVE,
@@ -1725,4 +1730,10 @@
   owl_function_aimwrite_setup(newbuff);
   owl_free(newbuff);
+  return(NULL);
+}
+
+char *owl_command_loopwrite(int argc, char **argv, char *buff)
+{
+  owl_function_loopwrite_setup();
   return(NULL);
 }
Index: filter.c
===================================================================
--- filter.c	(revision 49d467c9419ca1be59dd7d5de3fd564d3089cf4a)
+++ filter.c	(revision 37eab7f7cd2720c3f612c28783d596910005f4bf)
@@ -162,5 +162,5 @@
       match=owl_message_get_realm(m);
     } else if (!strcasecmp(field, "type")) {
-      match=owl_message_type_to_string(m);
+      match=owl_message_get_type(m);
     } else if (!strcasecmp(field, "hostname")) {
       match=owl_message_get_hostname(m);
Index: functions.c
===================================================================
--- functions.c	(revision 8c9284854a3fc6c3ac6cd959706f5674878cd849)
+++ functions.c	(revision 37eab7f7cd2720c3f612c28783d596910005f4bf)
@@ -208,4 +208,32 @@
 }
 
+int owl_function_make_outgoing_loopback(char *body)
+{
+  owl_message *m;
+  int followlast;
+
+  followlast=owl_global_should_followlast(&g);
+
+  /* create the message */
+  m=owl_malloc(sizeof(owl_message));
+  owl_message_create_loopback(m, body);
+  owl_message_set_direction_out(m);
+
+  /* add it to the global list and current view */
+  owl_messagelist_append_element(owl_global_get_msglist(&g), m);
+  owl_view_consider_message(owl_global_get_current_view(&g), m);
+
+  if (followlast) owl_function_lastmsg_noredisplay();
+
+  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
+  if (owl_popwin_is_active(owl_global_get_popwin(&g))) {
+    owl_popwin_refresh(owl_global_get_popwin(&g));
+  }
+  
+  wnoutrefresh(owl_global_get_curs_recwin(&g));
+  owl_global_set_needrefresh(&g);
+  return(0);
+}
+
 void owl_function_zwrite_setup(char *line)
 {
@@ -280,4 +308,28 @@
 
   owl_global_set_buffercommand(&g, line);
+}
+
+void owl_function_loopwrite_setup()
+{
+  owl_editwin *e;
+
+  /* create and setup the editwin */
+  e=owl_global_get_typwin(&g);
+  owl_editwin_new_style(e, OWL_EDITWIN_STYLE_MULTILINE, owl_global_get_msg_history(&g));
+
+  if (!owl_global_get_lockout_ctrld(&g)) {
+    owl_function_makemsg("Type your message below.  End with ^D or a dot on a line by itself.  ^C will quit.");
+  } else {
+    owl_function_makemsg("Type your message below.  End with a dot on a line by itself.  ^C will quit.");
+  }
+
+  owl_editwin_clear(e);
+  owl_editwin_set_dotsend(e);
+  owl_editwin_set_locktext(e, "----> loopwrite\n");
+
+  /* make it active */
+  owl_global_set_typwin_active(&g);
+
+  owl_global_set_buffercommand(&g, "loopwrite");
 }
 
@@ -397,4 +449,29 @@
   if (owl_global_is_logging(&g)) {
     owl_log_outgoing_aim(to, owl_editwin_get_text(owl_global_get_typwin(&g)));
+  }
+}
+
+void owl_function_loopwrite()
+{
+  owl_message *m;
+
+  /* create a message and put it on the message queue.  This simulates
+   * an incoming message */
+  m=owl_malloc(sizeof(owl_message));
+  owl_message_create_loopback(m, owl_editwin_get_text(owl_global_get_typwin(&g)));
+  owl_message_set_direction_out(m);
+  owl_global_messagequeue_addmsg(&g, m);
+
+  /* display the message as an outgoing message in the receive window */
+  if (owl_global_is_displayoutgoing(&g)) {
+    owl_function_make_outgoing_loopback(owl_editwin_get_text(owl_global_get_typwin(&g)));
+  }
+
+  /* fake a makemsg */
+  owl_function_makemsg("loopback message sent");
+
+  /* log it if we have logging turned on */
+  if (owl_global_is_logging(&g)) {
+    owl_log_outgoing_loopback(owl_editwin_get_text(owl_global_get_typwin(&g)));
   }
 }
@@ -1079,8 +1156,12 @@
   } else if (!strncmp(buff, "aimwrite ", 9)) {
     owl_function_aimwrite(buff+9);
+  } else if (!strncmp(buff, "loopwrite", 9) || !strncmp(buff, "loopwrite ", 10)) {
+    owl_function_loopwrite();
   } else if (!strncmp(buff, "aimlogin ", 9)) {
     ptr=owl_sprintf("%s %s", buff, owl_global_get_response(&g));
     owl_function_command(ptr);
     owl_free(ptr);
+  } else {
+    owl_function_error("Internal error: invalid buffercommand %s", buff);
   }
 }
@@ -1282,5 +1363,5 @@
 
   owl_fmtext_append_normal(&fm, "  Type      : ");
-  owl_fmtext_append_bold(&fm, owl_message_type_to_string(m));
+  owl_fmtext_append_bold(&fm, owl_message_get_type(m));
   owl_fmtext_append_normal(&fm, "\n");
 
@@ -1946,5 +2027,4 @@
   owl_context_set_editline(owl_global_get_context(&g), tw);
   owl_function_activate_keymap("editline");
-    
 }
 
Index: logging.c
===================================================================
--- logging.c	(revision 15283bb262a596e38cf9c59b498670211458c33d)
+++ logging.c	(revision 37eab7f7cd2720c3f612c28783d596910005f4bf)
@@ -55,5 +55,6 @@
 }
 
-void owl_log_outgoing_aim(char *to, char *text) {
+void owl_log_outgoing_aim(char *to, char *text)
+{
   FILE *file;
   char filename[MAXPATHLEN], *logpath;
@@ -61,4 +62,45 @@
 
   tobuff=owl_sprintf("aim:%s", to);
+
+  /* expand ~ in path names */
+  logpath = owl_util_substitute(owl_global_get_logpath(&g), "~", 
+				owl_global_get_homedir(&g));
+
+  snprintf(filename, MAXPATHLEN, "%s/%s", logpath, tobuff);
+  file=fopen(filename, "a");
+  if (!file) {
+    owl_function_error("Unable to open file for outgoing logging");
+    owl_free(logpath);
+    return;
+  }
+  fprintf(file, "OUTGOING (owl): %s\n%s\n", tobuff, text);
+  if (text[strlen(text)-1]!='\n') {
+    fprintf(file, "\n");
+  }
+  fclose(file);
+
+  snprintf(filename, MAXPATHLEN, "%s/all", logpath);
+  owl_free(logpath);
+  file=fopen(filename, "a");
+  if (!file) {
+    owl_function_error("Unable to open file for outgoing logging");
+    return;
+  }
+  fprintf(file, "OUTGOING (owl): %s\n%s\n", tobuff, text);
+  if (text[strlen(text)-1]!='\n') {
+    fprintf(file, "\n");
+  }
+  fclose(file);
+
+  owl_free(tobuff);
+}
+
+void owl_log_outgoing_loopback(char *text)
+{
+  FILE *file;
+  char filename[MAXPATHLEN], *logpath;
+  char *tobuff;
+
+  tobuff=owl_sprintf("loopback:%s", "loppback");
 
   /* expand ~ in path names */
@@ -142,4 +184,6 @@
     /* we do not yet handle chat rooms */
     from=frombuff=owl_sprintf("aim:%s", owl_message_get_sender(m));
+  } else if (owl_message_is_type_loopback(m)) {
+    from=frombuff=owl_strdup("loopback");
   } else {
     from=frombuff=owl_strdup("unknown");
@@ -220,5 +264,10 @@
     if (owl_message_is_login(m)) fprintf(file, "LOGIN\n\n");
     if (owl_message_is_logout(m)) fprintf(file, "LOGOUT\n\n");
-  }
+  } else {
+    fprintf(file, "From: <%s> To: <%s>\n", owl_message_get_sender(m), owl_message_get_recipient(m));
+    fprintf(file, "Time: %s\n\n", owl_message_get_timestr(m));
+    fprintf(file, "%s\n\n", owl_message_get_body(m));
+  }
+
   fclose(file);
 
@@ -245,4 +294,8 @@
       if (owl_message_is_login(m)) fprintf(allfile, "LOGIN\n\n");
       if (owl_message_is_logout(m)) fprintf(allfile, "LOGOUT\n\n");
+    } else {
+      fprintf(file, "From: <%s> To: <%s>\n", owl_message_get_sender(m), owl_message_get_recipient(m));
+      fprintf(file, "Time: %s\n\n", owl_message_get_timestr(m));
+      fprintf(file, "%s\n\n", owl_message_get_body(m));
     }
     fclose(allfile);
Index: mainwin.c
===================================================================
--- mainwin.c	(revision f2f9314aa68fe613c75b3d869a1085511708b968)
+++ mainwin.c	(revision 37eab7f7cd2720c3f612c28783d596910005f4bf)
@@ -3,10 +3,12 @@
 static const char fileIdent[] = "$Id$";
 
-void owl_mainwin_init(owl_mainwin *mw) {
+void owl_mainwin_init(owl_mainwin *mw)
+{
   mw->curtruncated=0;
   mw->lastdisplayed=-1;
 }
 
-void owl_mainwin_redisplay(owl_mainwin *mw) {
+void owl_mainwin_redisplay(owl_mainwin *mw)
+{
   owl_message *m;
   int i, p, q, lines, isfull, viewsize;
@@ -136,15 +138,18 @@
 
 
-int owl_mainwin_is_curmsg_truncated(owl_mainwin *mw) {
+int owl_mainwin_is_curmsg_truncated(owl_mainwin *mw)
+{
   if (mw->curtruncated) return(1);
   return(0);
 }
 
-int owl_mainwin_is_last_msg_truncated(owl_mainwin *mw) {
+int owl_mainwin_is_last_msg_truncated(owl_mainwin *mw)
+{
   if (mw->lasttruncated) return(1);
   return(0);
 }
 
-int owl_mainwin_get_last_msg(owl_mainwin *mw) {
+int owl_mainwin_get_last_msg(owl_mainwin *mw)
+{
   /* return the number of the last message displayed. -1 if none */
   return(mw->lastdisplayed);
Index: message.c
===================================================================
--- message.c	(revision f4d0975bed0618ba963347663b4b5de5825c0a22)
+++ message.c	(revision 37eab7f7cd2720c3f612c28783d596910005f4bf)
@@ -313,4 +313,9 @@
 }
 
+void owl_message_set_type_loopback(owl_message *m)
+{
+  m->type=OWL_MESSAGE_TYPE_LOOPBACK;
+}
+
 void owl_message_set_type_zephyr(owl_message *m)
 {
@@ -329,4 +334,10 @@
 }
 
+int owl_message_is_type_loopback(owl_message *m)
+{
+  if (m->type==OWL_MESSAGE_TYPE_LOOPBACK) return(1);
+  return(0);
+}
+
 int owl_message_is_type_zephyr(owl_message *m)
 {
@@ -345,16 +356,4 @@
   if (m->type==OWL_MESSAGE_TYPE_GENERIC) return(1);
   return(0);
-}
-
-char *owl_message_type_to_string(owl_message *m)
-{
-  if (m->type==OWL_MESSAGE_TYPE_ADMIN) return("admin");
-  if (m->type==OWL_MESSAGE_TYPE_GENERIC) return("generic");
-  if (m->type==OWL_MESSAGE_TYPE_ZEPHYR) return("zephyr");
-  if (m->type==OWL_MESSAGE_TYPE_AIM) return("aim");
-  if (m->type==OWL_MESSAGE_TYPE_JABBER) return("jabber");
-  if (m->type==OWL_MESSAGE_TYPE_ICQ) return("icq");
-  if (m->type==OWL_MESSAGE_TYPE_MSN) return("msn");
-  return("unknown");
 }
 
@@ -449,5 +448,4 @@
   return(m->hostname);
 }
-
 
 void owl_message_curs_waddstr(owl_message *m, WINDOW *win, int aline, int bline, int acol, int bcol, int color)
@@ -597,4 +595,6 @@
   case OWL_MESSAGE_TYPE_MSN:
     return("msn");
+  case OWL_MESSAGE_TYPE_LOOPBACK:
+    return("loopback");
   default:
     return("unknown");
@@ -677,4 +677,15 @@
   owl_message_set_body(m, text);
   owl_message_set_attribute(m, "adminheader", header); /* just a hack for now */
+}
+
+/* caller should set the direction */
+void owl_message_create_loopback(owl_message *m, char *text)
+{
+  owl_message_init(m);
+  owl_message_set_type_loopback(m);
+  owl_message_set_body(m, text);
+  owl_message_set_sender(m, "loopback-sender");
+  owl_message_set_recipient(m, "loopback-recipient");
+  owl_message_set_isprivate(m);
 }
 
@@ -817,5 +828,4 @@
 }
 
-
 void owl_message_pretty_zsig(owl_message *m, char *buff)
 {
Index: owl.h
===================================================================
--- owl.h	(revision 15283bb262a596e38cf9c59b498670211458c33d)
+++ owl.h	(revision 37eab7f7cd2720c3f612c28783d596910005f4bf)
@@ -65,4 +65,5 @@
 #define OWL_MESSAGE_TYPE_YAHOO      6
 #define OWL_MESSAGE_TYPE_MSN        7
+#define OWL_MESSAGE_TYPE_LOOPBACK   8
 
 #define OWL_MESSAGE_DIRECTION_NONE  0
Index: stylefunc.c
===================================================================
--- stylefunc.c	(revision ec6ff526f80f4544b68f01e84ccd88c783e54326)
+++ stylefunc.c	(revision 37eab7f7cd2720c3f612c28783d596910005f4bf)
@@ -199,4 +199,25 @@
     
     owl_free(indent);
+  } else {
+    char *text, *header, *indent;
+    
+    text=owl_message_get_body(m);
+    header=owl_sprintf("%s from: %s to: %s",
+		       owl_message_get_type(m),
+		       owl_message_get_sender(m),
+		       owl_message_get_recipient(m));
+    
+    indent=owl_malloc(strlen(text)+owl_text_num_lines(text)*OWL_MSGTAB+10);
+    owl_text_indent(indent, text, OWL_MSGTAB);
+    owl_fmtext_append_normal(fm, OWL_TABSTR);
+    owl_fmtext_append_normal(fm, header);
+    owl_fmtext_append_normal(fm, "\n");
+    owl_fmtext_append_normal(fm, indent);
+    if (text[strlen(text)-1]!='\n') {
+      owl_fmtext_append_normal(fm, "\n");
+    }
+    
+    owl_free(indent);
+    owl_free(header);
   }
 }
@@ -395,4 +416,25 @@
     
     owl_free(indent);
+  } else {
+    char *text, *header, *indent;
+    
+    text=owl_message_get_body(m);
+    header=owl_sprintf("%s from: %s to: %s",
+		       owl_message_get_type(m),
+		       owl_message_get_sender(m),
+		       owl_message_get_recipient(m));
+    
+    indent=owl_malloc(strlen(text)+owl_text_num_lines(text)*OWL_MSGTAB+10);
+    owl_text_indent(indent, text, OWL_MSGTAB);
+    owl_fmtext_append_normal(fm, OWL_TABSTR);
+    owl_fmtext_append_normal(fm, header);
+    owl_fmtext_append_normal(fm, "\n");
+    owl_fmtext_append_normal(fm, indent);
+    if (text[strlen(text)-1]!='\n') {
+      owl_fmtext_append_normal(fm, "\n");
+    }
+    
+    owl_free(indent);
+    owl_free(header);
   }
 }
@@ -526,5 +568,14 @@
     owl_fmtext_append_normal(fm, "\n");
     if (tmp) owl_free(tmp);
-  }
+  } else {
+    owl_fmtext_append_spaces(fm, OWL_TAB);
+    owl_fmtext_append_normal(fm, "< LOOPBACK                                     ");
+    
+    tmp=owl_strdup(owl_message_get_body(m));
+    owl_util_tr(tmp, '\n', ' ');
+    owl_fmtext_append_normal(fm, tmp);
+    owl_fmtext_append_normal(fm, "\n");
+    if (tmp) owl_free(tmp);
+  }    
 
 }
@@ -736,4 +787,6 @@
     
     owl_free(indent);
+  } else {
+
   }
 }
