about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/command/command.c14
-rw-r--r--src/command/commands.c57
-rw-r--r--src/config/preferences.c5
-rw-r--r--src/config/preferences.h1
-rw-r--r--src/server_events.c8
-rw-r--r--src/server_events.h1
-rw-r--r--src/ui/buffer.c25
-rw-r--r--src/ui/buffer.h12
-rw-r--r--src/ui/console.c222
-rw-r--r--src/ui/core.c484
-rw-r--r--src/ui/ui.h7
-rw-r--r--src/ui/window.c200
-rw-r--r--src/ui/window.h11
-rw-r--r--src/ui/windows.c2
-rw-r--r--src/xmpp/message.c109
-rw-r--r--src/xmpp/stanza.c59
-rw-r--r--src/xmpp/stanza.h10
-rw-r--r--src/xmpp/xmpp.h4
18 files changed, 718 insertions, 513 deletions
diff --git a/src/command/command.c b/src/command/command.c
index 2186d224..0bca4212 100644
--- a/src/command/command.c
+++ b/src/command/command.c
@@ -1945,7 +1945,7 @@ _cmd_execute_default(const char * inp)
                 if (otr_is_secure(chatwin->barejid)) {
                     char *encrypted = otr_encrypt_message(chatwin->barejid, inp);
                     if (encrypted != NULL) {
-                        message_send_chat_encrypted(chatwin->barejid, encrypted);
+                        char *id = message_send_chat_encrypted(chatwin->barejid, encrypted);
                         otr_free_message(encrypted);
                         if (prefs_get_boolean(PREF_CHLOG)) {
                             const char *jid = jabber_get_fulljid();
@@ -1960,12 +1960,12 @@ _cmd_execute_default(const char * inp)
                             jid_destroy(jidp);
                         }
 
-                        ui_outgoing_chat_msg("me", chatwin->barejid, inp);
+                        ui_outgoing_chat_msg(chatwin->barejid, inp, id);
                     } else {
                         cons_show_error("Failed to send message.");
                     }
                 } else {
-                    message_send_chat(chatwin->barejid, inp);
+                    char *id = message_send_chat(chatwin->barejid, inp);
                     if (prefs_get_boolean(PREF_CHLOG)) {
                         const char *jid = jabber_get_fulljid();
                         Jid *jidp = jid_create(jid);
@@ -1973,10 +1973,10 @@ _cmd_execute_default(const char * inp)
                         jid_destroy(jidp);
                     }
 
-                    ui_outgoing_chat_msg("me", chatwin->barejid, inp);
+                    ui_outgoing_chat_msg(chatwin->barejid, inp, id);
                 }
 #else
-                message_send_chat(chatwin->barejid, inp);
+                char *id = message_send_chat(chatwin->barejid, inp);
                 if (prefs_get_boolean(PREF_CHLOG)) {
                     const char *jid = jabber_get_fulljid();
                     Jid *jidp = jid_create(jid);
@@ -1984,7 +1984,7 @@ _cmd_execute_default(const char * inp)
                     jid_destroy(jidp);
                 }
 
-                ui_outgoing_chat_msg("me", chatwin->barejid, inp);
+                ui_outgoing_chat_msg(chatwin->barejid, inp, id);
 #endif
             }
             break;
@@ -1995,7 +1995,7 @@ _cmd_execute_default(const char * inp)
             } else {
                 ProfPrivateWin *privatewin = wins_get_current_private();
                 message_send_private(privatewin->fulljid, inp);
-                ui_outgoing_private_msg("me", privatewin->fulljid, inp);
+                ui_outgoing_private_msg(privatewin->fulljid, inp);
             }
             break;
 
diff --git a/src/command/commands.c b/src/command/commands.c
index 1851a3f4..8075e2c0 100644
--- a/src/command/commands.c
+++ b/src/command/commands.c
@@ -1240,7 +1240,7 @@ cmd_msg(gchar **args, struct cmd_help_t help)
 
             if (msg != NULL) {
                 message_send_private(full_jid->str, msg);
-                ui_outgoing_private_msg("me", full_jid->str, msg);
+                ui_outgoing_private_msg(full_jid->str, msg);
             } else {
                 ui_new_private_win(full_jid->str);
             }
@@ -1265,9 +1265,9 @@ cmd_msg(gchar **args, struct cmd_help_t help)
             if (otr_is_secure(barejid)) {
                 char *encrypted = otr_encrypt_message(barejid, msg);
                 if (encrypted != NULL) {
-                    message_send_chat_encrypted(barejid, encrypted);
+                    char *id = message_send_chat_encrypted(barejid, encrypted);
                     otr_free_message(encrypted);
-                    ui_outgoing_chat_msg("me", barejid, msg);
+                    ui_outgoing_chat_msg(barejid, msg, id);
 
                     if (((win_type == WIN_CHAT) || (win_type == WIN_CONSOLE)) && prefs_get_boolean(PREF_CHLOG)) {
                         const char *jid = jabber_get_fulljid();
@@ -1286,6 +1286,7 @@ cmd_msg(gchar **args, struct cmd_help_t help)
                 }
             } else {
                 prof_otrpolicy_t policy = otr_get_policy(barejid);
+                char *id = NULL;
 
                 if (policy == PROF_OTRPOLICY_ALWAYS) {
                     cons_show_error("Failed to send message. Please check OTR policy");
@@ -1294,13 +1295,13 @@ cmd_msg(gchar **args, struct cmd_help_t help)
                     GString *otr_message = g_string_new(msg);
                     g_string_append(otr_message, OTRL_MESSAGE_TAG_BASE);
                     g_string_append(otr_message, OTRL_MESSAGE_TAG_V2);
-                    message_send_chat_encrypted(barejid, otr_message->str);
+                    id = message_send_chat_encrypted(barejid, otr_message->str);
 
                     g_string_free(otr_message, TRUE);
                 } else {
-                    message_send_chat(barejid, msg);
+                    id = message_send_chat(barejid, msg);
                 }
-                ui_outgoing_chat_msg("me", barejid, msg);
+                ui_outgoing_chat_msg(barejid, msg, id);
 
                 if (((win_type == WIN_CHAT) || (win_type == WIN_CONSOLE)) && prefs_get_boolean(PREF_CHLOG)) {
                     const char *jid = jabber_get_fulljid();
@@ -1311,8 +1312,8 @@ cmd_msg(gchar **args, struct cmd_help_t help)
             }
             return TRUE;
 #else
-            message_send_chat(barejid, msg);
-            ui_outgoing_chat_msg("me", barejid, msg);
+            char *id = message_send_chat(barejid, msg);
+            ui_outgoing_chat_msg(barejid, msg, id);
 
             if (((win_type == WIN_CHAT) || (win_type == WIN_CONSOLE)) && prefs_get_boolean(PREF_CHLOG)) {
                 const char *jid = jabber_get_fulljid();
@@ -1721,7 +1722,7 @@ cmd_status(gchar **args, struct cmd_help_t help)
                 if (occupant) {
                     win_show_occupant(window, occupant);
                 } else {
-                    win_save_vprint(window, '-', NULL, 0, 0, "", "No such participant \"%s\" in room.", usr);
+                    win_vprint(window, '-', NULL, 0, 0, "", "No such participant \"%s\" in room.", usr);
                 }
             } else {
                 ui_current_print_line("You must specify a nickname.");
@@ -1737,7 +1738,7 @@ cmd_status(gchar **args, struct cmd_help_t help)
                 if (pcontact != NULL) {
                     win_show_contact(window, pcontact);
                 } else {
-                    win_save_println(window, "Error getting contact info.");
+                    win_println(window, "Error getting contact info.");
                 }
             }
             break;
@@ -1752,7 +1753,7 @@ cmd_status(gchar **args, struct cmd_help_t help)
                 if (occupant) {
                     win_show_occupant(window, occupant);
                 } else {
-                    win_save_println(window, "Error getting contact info.");
+                    win_println(window, "Error getting contact info.");
                 }
                 jid_destroy(jid);
             }
@@ -1818,7 +1819,7 @@ cmd_info(gchar **args, struct cmd_help_t help)
                 if (pcontact != NULL) {
                     win_show_info(window, pcontact);
                 } else {
-                    win_save_println(window, "Error getting contact info.");
+                    win_println(window, "Error getting contact info.");
                 }
             }
             break;
@@ -1833,7 +1834,7 @@ cmd_info(gchar **args, struct cmd_help_t help)
                 if (occupant) {
                     win_show_occupant_info(window, jid->barejid, occupant);
                 } else {
-                    win_save_println(window, "Error getting contact info.");
+                    win_println(window, "Error getting contact info.");
                 }
                 jid_destroy(jid);
             }
@@ -2448,7 +2449,7 @@ cmd_kick(gchar **args, struct cmd_help_t help)
             char *reason = args[1];
             iq_room_kick_occupant(mucwin->roomjid, nick, reason);
         } else {
-            win_save_vprint((ProfWin*) mucwin, '!', NULL, 0, 0, "", "Occupant does not exist: %s", nick);
+            win_vprint((ProfWin*) mucwin, '!', NULL, 0, 0, "", "Occupant does not exist: %s", nick);
         }
     } else {
         cons_show("Usage: %s", help.usage);
@@ -2507,10 +2508,10 @@ cmd_subject(gchar **args, struct cmd_help_t help)
     if (args[0] == NULL) {
         char *subject = muc_subject(mucwin->roomjid);
         if (subject) {
-            win_save_vprint(window, '!', NULL, NO_EOL, THEME_ROOMINFO, "", "Room subject: ");
-            win_save_vprint(window, '!', NULL, NO_DATE, 0, "", "%s", subject);
+            win_vprint(window, '!', NULL, NO_EOL, THEME_ROOMINFO, "", "Room subject: ");
+            win_vprint(window, '!', NULL, NO_DATE, 0, "", "%s", subject);
         } else {
-            win_save_print(window, '!', NULL, 0, THEME_ROOMINFO, "", "Room has no subject");
+            win_print(window, '!', NULL, 0, THEME_ROOMINFO, "", "Room has no subject");
         }
         return TRUE;
     }
@@ -2575,7 +2576,7 @@ cmd_affiliation(gchar **args, struct cmd_help_t help)
             iq_room_affiliation_list(mucwin->roomjid, "member");
             iq_room_affiliation_list(mucwin->roomjid, "outcast");
         } else if (g_strcmp0(affiliation, "none") == 0) {
-            win_save_print((ProfWin*) mucwin, '!', NULL, 0, 0, "", "Cannot list users with no affiliation.");
+            win_print((ProfWin*) mucwin, '!', NULL, 0, 0, "", "Cannot list users with no affiliation.");
         } else {
             iq_room_affiliation_list(mucwin->roomjid, affiliation);
         }
@@ -2643,7 +2644,7 @@ cmd_role(gchar **args, struct cmd_help_t help)
             iq_room_role_list(mucwin->roomjid, "participant");
             iq_room_role_list(mucwin->roomjid, "visitor");
         } else if (g_strcmp0(role, "none") == 0) {
-            win_save_print((ProfWin*) mucwin, '!', NULL, 0, 0, "", "Cannot list users with no role.");
+            win_print((ProfWin*) mucwin, '!', NULL, 0, 0, "", "Cannot list users with no role.");
         } else {
             iq_room_role_list(mucwin->roomjid, role);
         }
@@ -2706,12 +2707,12 @@ cmd_room(gchar **args, struct cmd_help_t help)
     if (g_strcmp0(args[0], "accept") == 0) {
         gboolean requires_config = muc_requires_config(mucwin->roomjid);
         if (!requires_config) {
-            win_save_print(window, '!', NULL, 0, THEME_ROOMINFO, "", "Current room does not require configuration.");
+            win_print(window, '!', NULL, 0, THEME_ROOMINFO, "", "Current room does not require configuration.");
             return TRUE;
         } else {
             iq_confirm_instant_room(mucwin->roomjid);
             muc_set_requires_config(mucwin->roomjid, FALSE);
-            win_save_print(window, '!', NULL, 0, THEME_ROOMINFO, "", "Room unlocked.");
+            win_print(window, '!', NULL, 0, THEME_ROOMINFO, "", "Room unlocked.");
             return TRUE;
         }
     }
@@ -3073,7 +3074,7 @@ cmd_tiny(gchar **args, struct cmd_help_t help)
                 if (otr_is_secure(chatwin->barejid)) {
                     char *encrypted = otr_encrypt_message(chatwin->barejid, tiny);
                     if (encrypted != NULL) {
-                        message_send_chat_encrypted(chatwin->barejid, encrypted);
+                        char *id = message_send_chat_encrypted(chatwin->barejid, encrypted);
                         otr_free_message(encrypted);
                         if (prefs_get_boolean(PREF_CHLOG)) {
                             const char *jid = jabber_get_fulljid();
@@ -3088,12 +3089,12 @@ cmd_tiny(gchar **args, struct cmd_help_t help)
                             jid_destroy(jidp);
                         }
 
-                        ui_outgoing_chat_msg("me", chatwin->barejid, tiny);
+                        ui_outgoing_chat_msg(chatwin->barejid, tiny, id);
                     } else {
                         cons_show_error("Failed to send message.");
                     }
                 } else {
-                    message_send_chat(chatwin->barejid, tiny);
+                    char *id = message_send_chat(chatwin->barejid, tiny);
                     if (prefs_get_boolean(PREF_CHLOG)) {
                         const char *jid = jabber_get_fulljid();
                         Jid *jidp = jid_create(jid);
@@ -3101,10 +3102,10 @@ cmd_tiny(gchar **args, struct cmd_help_t help)
                         jid_destroy(jidp);
                     }
 
-                    ui_outgoing_chat_msg("me", chatwin->barejid, tiny);
+                    ui_outgoing_chat_msg(chatwin->barejid, tiny, id);
                 }
 #else
-                message_send_chat(chatwin->barejid, tiny);
+                char *id = message_send_chat(chatwin->barejid, tiny);
                 if (prefs_get_boolean(PREF_CHLOG)) {
                     const char *jid = jabber_get_fulljid();
                     Jid *jidp = jid_create(jid);
@@ -3112,12 +3113,12 @@ cmd_tiny(gchar **args, struct cmd_help_t help)
                     jid_destroy(jidp);
                 }
 
-                ui_outgoing_chat_msg("me", chatwin->barejid, tiny);
+                ui_outgoing_chat_msg(chatwin->barejid, tiny, id);
 #endif
             } else if (win_type == WIN_PRIVATE) {
                 ProfPrivateWin *privatewin = wins_get_current_private();
                 message_send_private(privatewin->fulljid, tiny);
-                ui_outgoing_private_msg("me", privatewin->fulljid, tiny);
+                ui_outgoing_private_msg(privatewin->fulljid, tiny);
             } else if (win_type == WIN_MUC) {
                 ProfMucWin *mucwin = wins_get_current_muc();
                 message_send_groupchat(mucwin->roomjid, tiny);
diff --git a/src/config/preferences.c b/src/config/preferences.c
index 0dbfe3e3..ce25ff9e 100644
--- a/src/config/preferences.c
+++ b/src/config/preferences.c
@@ -543,6 +543,7 @@ _get_group(preference_t pref)
         case PREF_CONNECT_ACCOUNT:
         case PREF_DEFAULT_ACCOUNT:
         case PREF_CARBONS:
+        case PREF_RECEIPTS:
             return PREF_GROUP_CONNECTION;
         case PREF_OTR_LOG:
         case PREF_OTR_POLICY:
@@ -579,6 +580,8 @@ _get_key(preference_t pref)
             return "history";
         case PREF_CARBONS:
             return "carbons";
+        case PREF_RECEIPTS:
+            return "receipts";
         case PREF_MOUSE:
             return "mouse";
         case PREF_OCCUPANTS:
@@ -730,4 +733,4 @@ _get_default_string(preference_t pref)
         default:
             return NULL;
     }
-}
+}
\ No newline at end of file
diff --git a/src/config/preferences.h b/src/config/preferences.h
index 7b29ae33..b031e857 100644
--- a/src/config/preferences.h
+++ b/src/config/preferences.h
@@ -60,6 +60,7 @@ typedef enum {
     PREF_INTYPE,
     PREF_HISTORY,
     PREF_CARBONS,
+    PREF_RECEIPTS,
     PREF_MOUSE,
     PREF_OCCUPANTS,
     PREF_OCCUPANTS_SIZE,
diff --git a/src/server_events.c b/src/server_events.c
index b0a9b1e3..d7504b64 100644
--- a/src/server_events.c
+++ b/src/server_events.c
@@ -310,7 +310,7 @@ handle_incoming_private_message(char *fulljid, char *message)
 
 void
 handle_carbon(char *barejid, char *message){
-    ui_outgoing_chat_msg("me", barejid, message);
+    ui_outgoing_chat_msg(barejid, message, NULL);
 }
 
 void
@@ -403,6 +403,12 @@ handle_delayed_message(char *barejid, char *message, GTimeVal tv_stamp)
 }
 
 void
+handle_message_receipt(char *barejid, char *id)
+{
+    ui_message_receipt(barejid, id);
+}
+
+void
 handle_typing(char *barejid, char *resource)
 {
     ui_contact_typing(barejid, resource);
diff --git a/src/server_events.h b/src/server_events.h
index 042eb934..403c5bb5 100644
--- a/src/server_events.h
+++ b/src/server_events.h
@@ -79,6 +79,7 @@ void handle_inactive(char *barejid, char *resource);
 void handle_activity(char *barejid, char *resource, gboolean send_states);
 void handle_gone(const char * const barejid, const char * const resource);
 void handle_subscription(const char *from, jabber_subscr_t type);
+void handle_message_receipt(char *barejid, char *id);
 void handle_contact_offline(char *contact, char *resource, char *status);
 void handle_contact_online(char *contact, Resource *resource,
     GDateTime *last_activity);
diff --git a/src/ui/buffer.c b/src/ui/buffer.c
index da505867..d16eec76 100644
--- a/src/ui/buffer.c
+++ b/src/ui/buffer.c
@@ -81,7 +81,7 @@ buffer_free(ProfBuff buffer)
 
 void
 buffer_push(ProfBuff buffer, const char show_char, GDateTime *time,
-    int flags, theme_item_t theme_item, const char * const from, const char * const message)
+    int flags, theme_item_t theme_item, const char * const from, const char * const message, DeliveryReceipt *receipt)
 {
     ProfBuffEntry *e = malloc(sizeof(struct prof_buff_entry_t));
     e->show_char = show_char;
@@ -90,6 +90,7 @@ buffer_push(ProfBuff buffer, const char show_char, GDateTime *time,
     e->time = time;
     e->from = strdup(from);
     e->message = strdup(message);
+    e->receipt = receipt;
 
     if (g_slist_length(buffer->entries) == BUFF_SIZE) {
         _free_entry(buffer->entries->data);
@@ -99,6 +100,24 @@ buffer_push(ProfBuff buffer, const char show_char, GDateTime *time,
     buffer->entries = g_slist_append(buffer->entries, e);
 }
 
+gboolean
+buffer_mark_received(ProfBuff buffer, const char * const id)
+{
+    GSList *entries = buffer->entries;
+    while (entries) {
+        ProfBuffEntry *entry = entries->data;
+        if (entry->receipt) {
+            if (!entry->receipt->received) {
+                entry->receipt->received = TRUE;
+                return TRUE;
+            }
+        }
+        entries = g_slist_next(entries);
+    }
+
+    return FALSE;
+}
+
 ProfBuffEntry*
 buffer_yield_entry(ProfBuff buffer, int entry)
 {
@@ -113,4 +132,8 @@ _free_entry(ProfBuffEntry *entry)
     free(entry->from);
     g_date_time_unref(entry->time);
     free(entry);
+    if (entry->receipt) {
+        free(entry->receipt->id);
+        free(entry->receipt);
+    }
 }
\ No newline at end of file
diff --git a/src/ui/buffer.h b/src/ui/buffer.h
index 5258b8c1..cad7eee0 100644
--- a/src/ui/buffer.h
+++ b/src/ui/buffer.h
@@ -40,6 +40,11 @@
 
 #include <glib.h>
 
+typedef struct delivery_receipt_t {
+    char *id;
+    gboolean received;
+} DeliveryReceipt;
+
 typedef struct prof_buff_entry_t {
     char show_char;
     GDateTime *time;
@@ -47,13 +52,18 @@ typedef struct prof_buff_entry_t {
     theme_item_t theme_item;
     char *from;
     char *message;
+    DeliveryReceipt *receipt;
 } ProfBuffEntry;
 
 typedef struct prof_buff_t *ProfBuff;
 
 ProfBuff buffer_create();
 void buffer_free(ProfBuff buffer);
-void buffer_push(ProfBuff buffer, const char show_char, GDateTime *time, int flags, theme_item_t theme_item, const char * const from, const char * const message);
+void buffer_push(ProfBuff buffer, const char show_char, GDateTime *time, int flags, theme_item_t theme_item,
+    const char * const from, const char * const message, DeliveryReceipt *receipt);
 int buffer_size(ProfBuff buffer);
 ProfBuffEntry* buffer_yield_entry(ProfBuff buffer, int entry);
+gboolean buffer_mark_received(ProfBuff buffer, const char * const id);
+
+
 #endif
diff --git a/src/ui/console.c b/src/ui/console.c
index dc8a3c7b..46cb00fb 100644
--- a/src/ui/console.c
+++ b/src/ui/console.c
@@ -67,14 +67,14 @@ void
 cons_show_time(void)
 {
     ProfWin *console = wins_get_console();
-    win_save_print(console, '-', NULL, NO_EOL, 0, "", "");
+    win_print(console, '-', NULL, NO_EOL, 0, "", "");
 }
 
 void
 cons_show_word(const char * const word)
 {
     ProfWin *console = wins_get_console();
-    win_save_print(console, '-', NULL, NO_DATE | NO_EOL, 0, "", word);
+    win_print(console, '-', NULL, NO_DATE | NO_EOL, 0, "", word);
 }
 
 void
@@ -86,7 +86,7 @@ cons_debug(const char * const msg, ...)
         va_start(arg, msg);
         GString *fmt_msg = g_string_new(NULL);
         g_string_vprintf(fmt_msg, msg, arg);
-        win_save_println(console, fmt_msg->str);
+        win_println(console, fmt_msg->str);
         g_string_free(fmt_msg, TRUE);
         va_end(arg);
     }
@@ -100,7 +100,7 @@ cons_show(const char * const msg, ...)
     va_start(arg, msg);
     GString *fmt_msg = g_string_new(NULL);
     g_string_vprintf(fmt_msg, msg, arg);
-    win_save_println(console, fmt_msg->str);
+    win_println(console, fmt_msg->str);
     g_string_free(fmt_msg, TRUE);
     va_end(arg);
 }
@@ -113,7 +113,7 @@ cons_show_error(const char * const msg, ...)
     va_start(arg, msg);
     GString *fmt_msg = g_string_new(NULL);
     g_string_vprintf(fmt_msg, msg, arg);
-    win_save_print(console, '-', NULL, 0, THEME_ERROR, "", fmt_msg->str);
+    win_print(console, '-', NULL, 0, THEME_ERROR, "", fmt_msg->str);
     g_string_free(fmt_msg, TRUE);
     va_end(arg);
 
@@ -136,7 +136,7 @@ cons_show_typing(const char * const barejid)
         display_usr = barejid;
     }
 
-    win_save_vprint(console, '-', NULL, 0, THEME_TYPING, "", "!! %s is typing a message...", display_usr);
+    win_vprint(console, '-', NULL, 0, THEME_TYPING, "", "!! %s is typing a message...", display_usr);
     cons_alert();
 }
 
@@ -149,7 +149,7 @@ cons_show_incoming_message(const char * const short_from, const int win_index)
     if (ui_index == 10) {
         ui_index = 0;
     }
-    win_save_vprint(console, '-', NULL, 0, THEME_INCOMING, "", "<< incoming from %s (%d)", short_from, ui_index);
+    win_vprint(console, '-', NULL, 0, THEME_INCOMING, "", "<< incoming from %s (%d)", short_from, ui_index);
 
     cons_alert();
 }
@@ -167,23 +167,23 @@ cons_about(void)
 
         if (strcmp(PACKAGE_STATUS, "development") == 0) {
 #ifdef HAVE_GIT_VERSION
-            win_save_vprint(console, '-', NULL, 0, 0, "", "Welcome to Profanity, version %sdev.%s.%s", PACKAGE_VERSION, PROF_GIT_BRANCH, PROF_GIT_REVISION);
+            win_vprint(console, '-', NULL, 0, 0, "", "Welcome to Profanity, version %sdev.%s.%s", PACKAGE_VERSION, PROF_GIT_BRANCH, PROF_GIT_REVISION);
 #else
-            win_save_vprint(console, '-', NULL, 0, 0, "", "Welcome to Profanity, version %sdev", PACKAGE_VERSION);
+            win_vprint(console, '-', NULL, 0, 0, "", "Welcome to Profanity, version %sdev", PACKAGE_VERSION);
 #endif
         } else {
-            win_save_vprint(console, '-', NULL, 0, 0, "", "Welcome to Profanity, version %s", PACKAGE_VERSION);
+            win_vprint(console, '-', NULL, 0, 0, "", "Welcome to Profanity, version %s", PACKAGE_VERSION);
         }
     }
 
-    win_save_vprint(console, '-', NULL, 0, 0, "", "Copyright (C) 2012 - 2015 James Booth <%s>.", PACKAGE_BUGREPORT);
-    win_save_println(console, "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>");
-    win_save_println(console, "");
-    win_save_println(console, "This is free software; you are free to change and redistribute it.");
-    win_save_println(console, "There is NO WARRANTY, to the extent permitted by law.");
-    win_save_println(console, "");
-    win_save_println(console, "Type '/help' to show complete help.");
-    win_save_println(console, "");
+    win_vprint(console, '-', NULL, 0, 0, "", "Copyright (C) 2012 - 2015 James Booth <%s>.", PACKAGE_BUGREPORT);
+    win_println(console, "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>");
+    win_println(console, "");
+    win_println(console, "This is free software; you are free to change and redistribute it.");
+    win_println(console, "There is NO WARRANTY, to the extent permitted by law.");
+    win_println(console, "");
+    win_println(console, "Type '/help' to show complete help.");
+    win_println(console, "");
 
     if (prefs_get_boolean(PREF_VERCHECK)) {
         cons_check_version(FALSE);
@@ -205,13 +205,13 @@ cons_check_version(gboolean not_available_msg)
 
         if (relase_valid) {
             if (release_is_new(latest_release)) {
-                win_save_vprint(console, '-', NULL, 0, 0, "", "A new version of Profanity is available: %s", latest_release);
-                win_save_println(console, "Check <http://www.profanity.im> for details.");
-                win_save_println(console, "");
+                win_vprint(console, '-', NULL, 0, 0, "", "A new version of Profanity is available: %s", latest_release);
+                win_println(console, "Check <http://www.profanity.im> for details.");
+                win_println(console, "");
             } else {
                 if (not_available_msg) {
-                    win_save_println(console, "No new version available.");
-                    win_save_println(console, "");
+                    win_println(console, "No new version available.");
+                    win_println(console, "");
                 }
             }
 
@@ -225,16 +225,16 @@ void
 cons_show_login_success(ProfAccount *account)
 {
     ProfWin *console = wins_get_console();
-    win_save_vprint(console, '-', NULL, NO_EOL, 0, "", "%s logged in successfully, ", account->jid);
+    win_vprint(console, '-', NULL, NO_EOL, 0, "", "%s logged in successfully, ", account->jid);
 
     resource_presence_t presence = accounts_get_login_presence(account->name);
     const char *presence_str = string_from_resource_presence(presence);
 
     theme_item_t presence_colour = theme_main_presence_attrs(presence_str);
-    win_save_vprint(console, '-', NULL, NO_DATE | NO_EOL, presence_colour, "", "%s", presence_str);
-    win_save_vprint(console, '-', NULL, NO_DATE | NO_EOL, 0, "", " (priority %d)",
+    win_vprint(console, '-', NULL, NO_DATE | NO_EOL, presence_colour, "", "%s", presence_str);
+    win_vprint(console, '-', NULL, NO_DATE | NO_EOL, 0, "", " (priority %d)",
         accounts_get_priority_for_presence_type(account->name, presence));
-    win_save_print(console, '-', NULL, NO_DATE, 0, "", ".");
+    win_print(console, '-', NULL, NO_DATE, 0, "", ".");
     cons_alert();
 }
 
@@ -248,7 +248,7 @@ cons_show_wins(void)
 
     GSList *curr = window_strings;
     while (curr != NULL) {
-        win_save_println(console, curr->data);
+        win_println(console, curr->data);
         curr = g_slist_next(curr);
     }
     g_slist_free_full(window_strings, free);
@@ -294,53 +294,53 @@ cons_show_caps(const char * const fulljid, resource_presence_t presence)
         const char *resource_presence = string_from_resource_presence(presence);
 
         theme_item_t presence_colour = theme_main_presence_attrs(resource_presence);
-        win_save_vprint(console, '-', NULL, NO_EOL, presence_colour, "", "%s", fulljid);
-        win_save_print(console, '-', NULL, NO_DATE, 0, "", ":");
+        win_vprint(console, '-', NULL, NO_EOL, presence_colour, "", "%s", fulljid);
+        win_print(console, '-', NULL, NO_DATE, 0, "", ":");
 
         // show identity
         if ((caps->category != NULL) || (caps->type != NULL) || (caps->name != NULL)) {
-            win_save_print(console, '-', NULL, NO_EOL, 0, "", "Identity: ");
+            win_print(console, '-', NULL, NO_EOL, 0, "", "Identity: ");
             if (caps->name != NULL) {
-                win_save_print(console, '-', NULL, NO_DATE | NO_EOL, 0, "", caps->name);
+                win_print(console, '-', NULL, NO_DATE | NO_EOL, 0, "", caps->name);
                 if ((caps->category != NULL) || (caps->type != NULL)) {
-                    win_save_print(console, '-', NULL, NO_DATE | NO_EOL, 0, "", " ");
+                    win_print(console, '-', NULL, NO_DATE | NO_EOL, 0, "", " ");
                 }
             }
             if (caps->type != NULL) {
-                win_save_print(console, '-', NULL, NO_DATE | NO_EOL, 0, "", caps->type);
+                win_print(console, '-', NULL, NO_DATE | NO_EOL, 0, "", caps->type);
                 if (caps->category != NULL) {
-                    win_save_print(console, '-', NULL, NO_DATE | NO_EOL, 0, "", " ");
+                    win_print(console, '-', NULL, NO_DATE | NO_EOL, 0, "", " ");
                 }
             }
             if (caps->category != NULL) {
-                win_save_print(console, '-', NULL, NO_DATE | NO_EOL, 0, "", caps->category);
+                win_print(console, '-', NULL, NO_DATE | NO_EOL, 0, "", caps->category);
             }
-            win_save_newline(console);
+            win_newline(console);
         }
         if (caps->software != NULL) {
-            win_save_vprint(console, '-', NULL, NO_EOL, 0, "", "Software: %s", caps->software);
+            win_vprint(console, '-', NULL, NO_EOL, 0, "", "Software: %s", caps->software);
         }
         if (caps->software_version != NULL) {
-            win_save_vprint(console, '-', NULL, NO_DATE | NO_EOL, 0, "", ", %s", caps->software_version);
+            win_vprint(console, '-', NULL, NO_DATE | NO_EOL, 0, "", ", %s", caps->software_version);
         }
         if ((caps->software != NULL) || (caps->software_version != NULL)) {
-            win_save_newline(console);
+            win_newline(console);
         }
         if (caps->os != NULL) {
-            win_save_vprint(console, '-', NULL, NO_EOL, 0, "", "OS: %s", caps->os);
+            win_vprint(console, '-', NULL, NO_EOL, 0, "", "OS: %s", caps->os);
         }
         if (caps->os_version != NULL) {
-            win_save_vprint(console, '-', NULL, NO_DATE | NO_EOL, 0, "", ", %s", caps->os_version);
+            win_vprint(console, '-', NULL, NO_DATE | NO_EOL, 0, "", ", %s", caps->os_version);
         }
         if ((caps->os != NULL) || (caps->os_version != NULL)) {
-            win_save_newline(console);
+            win_newline(console);
         }
 
         if (caps->features != NULL) {
-            win_save_println(console, "Features:");
+            win_println(console, "Features:");
             GSList *feature = caps->features;
             while (feature != NULL) {
-                win_save_vprint(console, '-', NULL, 0, 0, "", " %s", feature->data);
+                win_vprint(console, '-', NULL, 0, 0, "", " %s", feature->data);
                 feature = g_slist_next(feature);
             }
         }
@@ -361,8 +361,8 @@ cons_show_software_version(const char * const jid, const char * const  presence,
     if ((name != NULL) || (version != NULL) || (os != NULL)) {
         cons_show("");
         theme_item_t presence_colour = theme_main_presence_attrs(presence);
-        win_save_vprint(console, '-', NULL, NO_EOL, presence_colour, "", "%s", jid);
-        win_save_print(console, '-', NULL, NO_DATE, 0, "", ":");
+        win_vprint(console, '-', NULL, NO_EOL, presence_colour, "", "%s", jid);
+        win_print(console, '-', NULL, NO_DATE, 0, "", ":");
     }
     if (name != NULL) {
         cons_show("Name    : %s", name);
@@ -426,11 +426,11 @@ cons_show_room_list(GSList *rooms, const char * const conference_node)
         cons_show("Chat rooms at %s:", conference_node);
         while (rooms != NULL) {
             DiscoItem *room = rooms->data;
-            win_save_vprint(console, '-', NULL, NO_EOL, 0, "", "  %s", room->jid);
+            win_vprint(console, '-', NULL, NO_EOL, 0, "", "  %s", room->jid);
             if (room->name != NULL) {
-                win_save_vprint(console, '-', NULL, NO_DATE | NO_EOL, 0, "", ", (%s)", room->name);
+                win_vprint(console, '-', NULL, NO_DATE | NO_EOL, 0, "", ", (%s)", room->name);
             }
-            win_save_newline(console);
+            win_newline(console);
             rooms = g_slist_next(rooms);
         }
     } else {
@@ -460,24 +460,24 @@ cons_show_bookmarks(const GList *list)
             if (muc_active(item->jid)) {
                 presence_colour = THEME_ONLINE;
             }
-            win_save_vprint(console, '-', NULL, NO_EOL, presence_colour, "", "  %s", item->jid);
+            win_vprint(console, '-', NULL, NO_EOL, presence_colour, "", "  %s", item->jid);
             if (item->nick != NULL) {
-                win_save_vprint(console, '-', NULL, NO_DATE | NO_EOL, presence_colour, "", "/%s", item->nick);
+                win_vprint(console, '-', NULL, NO_DATE | NO_EOL, presence_colour, "", "/%s", item->nick);
             }
             if (item->autojoin) {
-                win_save_print(console, '-', NULL, NO_DATE | NO_EOL, presence_colour, "", " (autojoin)");
+                win_print(console, '-', NULL, NO_DATE | NO_EOL, presence_colour, "", " (autojoin)");
             }
             if (item->password != NULL) {
-                win_save_print(console, '-', NULL, NO_DATE | NO_EOL, presence_colour, "", " (private)");
+                win_print(console, '-', NULL, NO_DATE | NO_EOL, presence_colour, "", " (private)");
             }
             if (muc_active(item->jid)) {
                 ProfWin *roomwin = (ProfWin*)wins_get_muc(item->jid);
                 if (roomwin != NULL) {
                     int num = wins_get_num(roomwin);
-                    win_save_vprint(console, '-', NULL, NO_DATE | NO_EOL, presence_colour, "", " (%d)", num);
+                    win_vprint(console, '-', NULL, NO_DATE | NO_EOL, presence_colour, "", " (%d)", num);
                 }
             }
-            win_save_newline(console);
+            win_newline(console);
             list = g_list_next(list);
         }
     }
@@ -535,11 +535,11 @@ cons_show_disco_items(GSList *items, const char * const jid)
         cons_show("Service discovery items for %s:", jid);
         while (items != NULL) {
             DiscoItem *item = items->data;
-            win_save_vprint(console, '-', NULL, NO_EOL, 0, "", "  %s", item->jid);
+            win_vprint(console, '-', NULL, NO_EOL, 0, "", "  %s", item->jid);
             if (item->name != NULL) {
-                win_save_vprint(console, '-', NULL, NO_DATE | NO_EOL, 0, "", ", (%s)", item->name);
+                win_vprint(console, '-', NULL, NO_DATE | NO_EOL, 0, "", ", (%s)", item->name);
             }
-            win_save_vprint(console, '-', NULL, NO_DATE, 0, "", "");
+            win_vprint(console, '-', NULL, NO_DATE, 0, "", "");
             items = g_slist_next(items);
         }
     } else {
@@ -614,7 +614,7 @@ cons_show_account_list(gchar **accounts)
                     (g_strcmp0(jabber_get_account_name(), accounts[i]) == 0)) {
                 resource_presence_t presence = accounts_get_last_presence(accounts[i]);
                 theme_item_t presence_colour = theme_main_presence_attrs(string_from_resource_presence(presence));
-                win_save_vprint(console, '-', NULL, 0, presence_colour, "", "%s", accounts[i]);
+                win_vprint(console, '-', NULL, 0, presence_colour, "", "%s", accounts[i]);
             } else {
                 cons_show(accounts[i]);
             }
@@ -721,7 +721,7 @@ cons_show_account(ProfAccount *account)
 
         GList *curr = resources;
         if (curr != NULL) {
-            win_save_println(console, "Resources:");
+            win_println(console, "Resources:");
 
             // sort in order of availability
             while (curr != NULL) {
@@ -739,12 +739,12 @@ cons_show_account(ProfAccount *account)
             Resource *resource = curr->data;
             const char *resource_presence = string_from_resource_presence(resource->presence);
             theme_item_t presence_colour = theme_main_presence_attrs(resource_presence);
-            win_save_vprint(console, '-', NULL, NO_EOL, presence_colour, "", "  %s (%d), %s", resource->name, resource->priority, resource_presence);
+            win_vprint(console, '-', NULL, NO_EOL, presence_colour, "", "  %s (%d), %s", resource->name, resource->priority, resource_presence);
 
             if (resource->status != NULL) {
-                win_save_vprint(console, '-', NULL, NO_DATE | NO_EOL, presence_colour, "", ", \"%s\"", resource->status);
+                win_vprint(console, '-', NULL, NO_DATE | NO_EOL, presence_colour, "", ", \"%s\"", resource->status);
             }
-            win_save_vprint(console, '-', NULL, NO_DATE, 0, "", "");
+            win_vprint(console, '-', NULL, NO_DATE, 0, "", "");
             Jid *jidp = jid_create_from_bare_and_resource(account->jid, resource->name);
             Capabilities *caps = caps_lookup(jidp->fulljid);
             jid_destroy(jidp);
@@ -752,41 +752,41 @@ cons_show_account(ProfAccount *account)
             if (caps != NULL) {
                 // show identity
                 if ((caps->category != NULL) || (caps->type != NULL) || (caps->name != NULL)) {
-                    win_save_print(console, '-', NULL, NO_EOL, 0, "", "    Identity: ");
+                    win_print(console, '-', NULL, NO_EOL, 0, "", "    Identity: ");
                     if (caps->name != NULL) {
-                        win_save_print(console, '-', NULL, NO_DATE | NO_EOL, 0, "", caps->name);
+                        win_print(console, '-', NULL, NO_DATE | NO_EOL, 0, "", caps->name);
                         if ((caps->category != NULL) || (caps->type != NULL)) {
-                            win_save_print(console, '-', NULL, NO_DATE | NO_EOL, 0, "", " ");
+                            win_print(console, '-', NULL, NO_DATE | NO_EOL, 0, "", " ");
                         }
                     }
                     if (caps->type != NULL) {
-                        win_save_print(console, '-', NULL, NO_DATE | NO_EOL, 0, "", caps->type);
+                        win_print(console, '-', NULL, NO_DATE | NO_EOL, 0, "", caps->type);
                         if (caps->category != NULL) {
-                            win_save_print(console, '-', NULL, NO_DATE | NO_EOL, 0, "", " ");
+                            win_print(console, '-', NULL, NO_DATE | NO_EOL, 0, "", " ");
                         }
                     }
                     if (caps->category != NULL) {
-                        win_save_print(console, '-', NULL, NO_DATE | NO_EOL, 0, "", caps->category);
+                        win_print(console, '-', NULL, NO_DATE | NO_EOL, 0, "", caps->category);
                     }
-                    win_save_newline(console);
+                    win_newline(console);
                 }
                 if (caps->software != NULL) {
-                    win_save_vprint(console, '-', NULL, NO_EOL, 0, "", "    Software: %s", caps->software);
+                    win_vprint(console, '-', NULL, NO_EOL, 0, "", "    Software: %s", caps->software);
                 }
                 if (caps->software_version != NULL) {
-                    win_save_vprint(console, '-', NULL, NO_DATE | NO_EOL, 0, "", ", %s", caps->software_version);
+                    win_vprint(console, '-', NULL, NO_DATE | NO_EOL, 0, "", ", %s", caps->software_version);
                 }
                 if ((caps->software != NULL) || (caps->software_version != NULL)) {
-                    win_save_newline(console);
+                    win_newline(console);
                 }
                 if (caps->os != NULL) {
-                    win_save_vprint(console, '-', NULL, NO_EOL, 0, "", "    OS: %s", caps->os);
+                    win_vprint(console, '-', NULL, NO_EOL, 0, "", "    OS: %s", caps->os);
                 }
                 if (caps->os_version != NULL) {
-                    win_save_vprint(console, '-', NULL, NO_DATE | NO_EOL, 0, "", ", %s", caps->os_version);
+                    win_vprint(console, '-', NULL, NO_DATE | NO_EOL, 0, "", ", %s", caps->os_version);
                 }
                 if ((caps->os != NULL) || (caps->os_version != NULL)) {
-                    win_save_newline(console);
+                    win_newline(console);
                 }
                 caps_destroy(caps);
             }
@@ -1551,22 +1551,22 @@ cons_theme_colours(void)
 
     ProfWin *console = wins_get_console();
     cons_show("Theme colours:");
-    win_save_print(console, '-', NULL, NO_EOL, THEME_WHITE, "",         " white   ");
-    win_save_print(console, '-', NULL, NO_DATE, THEME_WHITE_BOLD, "",   " bold_white");
-    win_save_print(console, '-', NULL, NO_EOL, THEME_GREEN, "",         " green   ");
-    win_save_print(console, '-', NULL, NO_DATE, THEME_GREEN_BOLD, "",   " bold_green");
-    win_save_print(console, '-', NULL, NO_EOL, THEME_RED, "",           " red     ");
-    win_save_print(console, '-', NULL, NO_DATE, THEME_RED_BOLD, "",     " bold_red");
-    win_save_print(console, '-', NULL, NO_EOL, THEME_YELLOW, "",        " yellow  ");
-    win_save_print(console, '-', NULL, NO_DATE, THEME_YELLOW_BOLD, "",  " bold_yellow");
-    win_save_print(console, '-', NULL, NO_EOL, THEME_BLUE, "",          " blue    ");
-    win_save_print(console, '-', NULL, NO_DATE, THEME_BLUE_BOLD, "",    " bold_blue");
-    win_save_print(console, '-', NULL, NO_EOL, THEME_CYAN, "",          " cyan    ");
-    win_save_print(console, '-', NULL, NO_DATE, THEME_CYAN_BOLD, "",    " bold_cyan");
-    win_save_print(console, '-', NULL, NO_EOL, THEME_MAGENTA, "",       " magenta ");
-    win_save_print(console, '-', NULL, NO_DATE, THEME_MAGENTA_BOLD, "", " bold_magenta");
-    win_save_print(console, '-', NULL, NO_EOL, THEME_BLACK, "",         " black   ");
-    win_save_print(console, '-', NULL, NO_DATE, THEME_BLACK_BOLD, "",   " bold_black");
+    win_print(console, '-', NULL, NO_EOL, THEME_WHITE, "",         " white   ");
+    win_print(console, '-', NULL, NO_DATE, THEME_WHITE_BOLD, "",   " bold_white");
+    win_print(console, '-', NULL, NO_EOL, THEME_GREEN, "",         " green   ");
+    win_print(console, '-', NULL, NO_DATE, THEME_GREEN_BOLD, "",   " bold_green");
+    win_print(console, '-', NULL, NO_EOL, THEME_RED, "",           " red     ");
+    win_print(console, '-', NULL, NO_DATE, THEME_RED_BOLD, "",     " bold_red");
+    win_print(console, '-', NULL, NO_EOL, THEME_YELLOW, "",        " yellow  ");
+    win_print(console, '-', NULL, NO_DATE, THEME_YELLOW_BOLD, "",  " bold_yellow");
+    win_print(console, '-', NULL, NO_EOL, THEME_BLUE, "",          " blue    ");
+    win_print(console, '-', NULL, NO_DATE, THEME_BLUE_BOLD, "",    " bold_blue");
+    win_print(console, '-', NULL, NO_EOL, THEME_CYAN, "",          " cyan    ");
+    win_print(console, '-', NULL, NO_DATE, THEME_CYAN_BOLD, "",    " bold_cyan");
+    win_print(console, '-', NULL, NO_EOL, THEME_MAGENTA, "",       " magenta ");
+    win_print(console, '-', NULL, NO_DATE, THEME_MAGENTA_BOLD, "", " bold_magenta");
+    win_print(console, '-', NULL, NO_EOL, THEME_BLACK, "",         " black   ");
+    win_print(console, '-', NULL, NO_DATE, THEME_BLACK_BOLD, "",   " bold_black");
     cons_show("");
 }
 
@@ -1574,25 +1574,25 @@ static void
 _cons_splash_logo(void)
 {
     ProfWin *console = wins_get_console();
-    win_save_println(console, "Welcome to");
+    win_println(console, "Welcome to");
 
-    win_save_print(console, '-', NULL, 0, THEME_SPLASH, "", "                   ___            _           ");
-    win_save_print(console, '-', NULL, 0, THEME_SPLASH, "", "                  / __)          (_)_         ");
-    win_save_print(console, '-', NULL, 0, THEME_SPLASH, "", " ____   ____ ___ | |__ ____ ____  _| |_ _   _ ");
-    win_save_print(console, '-', NULL, 0, THEME_SPLASH, "", "|  _ \\ / ___) _ \\|  __) _  |  _ \\| |  _) | | |");
-    win_save_print(console, '-', NULL, 0, THEME_SPLASH, "", "| | | | |  | |_| | | ( ( | | | | | | |_| |_| |");
-    win_save_print(console, '-', NULL, 0, THEME_SPLASH, "", "| ||_/|_|   \\___/|_|  \\_||_|_| |_|_|\\___)__  |");
-    win_save_print(console, '-', NULL, 0, THEME_SPLASH, "", "|_|                                    (____/ ");
-    win_save_print(console, '-', NULL, 0, THEME_SPLASH, "", "");
+    win_print(console, '-', NULL, 0, THEME_SPLASH, "", "                   ___            _           ");
+    win_print(console, '-', NULL, 0, THEME_SPLASH, "", "                  / __)          (_)_         ");
+    win_print(console, '-', NULL, 0, THEME_SPLASH, "", " ____   ____ ___ | |__ ____ ____  _| |_ _   _ ");
+    win_print(console, '-', NULL, 0, THEME_SPLASH, "", "|  _ \\ / ___) _ \\|  __) _  |  _ \\| |  _) | | |");
+    win_print(console, '-', NULL, 0, THEME_SPLASH, "", "| | | | |  | |_| | | ( ( | | | | | | |_| |_| |");
+    win_print(console, '-', NULL, 0, THEME_SPLASH, "", "| ||_/|_|   \\___/|_|  \\_||_|_| |_|_|\\___)__  |");
+    win_print(console, '-', NULL, 0, THEME_SPLASH, "", "|_|                                    (____/ ");
+    win_print(console, '-', NULL, 0, THEME_SPLASH, "", "");
 
     if (strcmp(PACKAGE_STATUS, "development") == 0) {
 #ifdef HAVE_GIT_VERSION
-        win_save_vprint(console, '-', NULL, 0, 0, "", "Version %sdev.%s.%s", PACKAGE_VERSION, PROF_GIT_BRANCH, PROF_GIT_REVISION);
+        win_vprint(console, '-', NULL, 0, 0, "", "Version %sdev.%s.%s", PACKAGE_VERSION, PROF_GIT_BRANCH, PROF_GIT_REVISION);
 #else
-        win_save_vprint(console, '-', NULL, 0, 0, "", "Version %sdev", PACKAGE_VERSION);
+        win_vprint(console, '-', NULL, 0, 0, "", "Version %sdev", PACKAGE_VERSION);
 #endif
     } else {
-        win_save_vprint(console, '-', NULL, 0, 0, "", "Version %s", PACKAGE_VERSION);
+        win_vprint(console, '-', NULL, 0, 0, "", "Version %s", PACKAGE_VERSION);
     }
 }
 
@@ -1619,11 +1619,11 @@ _show_roster_contacts(GSList *list, gboolean show_groups)
         } else {
             presence_colour = theme_main_presence_attrs("offline");
         }
-        win_save_vprint(console, '-', NULL, NO_EOL, presence_colour, "", title->str);
+        win_vprint(console, '-', NULL, NO_EOL, presence_colour, "", title->str);
 
         g_string_free(title, TRUE);
 
-        win_save_print(console, '-', NULL, NO_DATE | NO_EOL, 0, "", " - ");
+        win_print(console, '-', NULL, NO_DATE | NO_EOL, 0, "", " - ");
         GString *sub = g_string_new("");
         sub = g_string_append(sub, p_contact_subscription(contact));
         if (p_contact_pending_out(contact)) {
@@ -1639,9 +1639,9 @@ _show_roster_contacts(GSList *list, gboolean show_groups)
         }
 
         if (show_groups) {
-            win_save_vprint(console, '-', NULL, NO_DATE | NO_EOL, presence_colour, "", "%s", sub->str);
+            win_vprint(console, '-', NULL, NO_DATE | NO_EOL, presence_colour, "", "%s", sub->str);
         } else {
-            win_save_vprint(console, '-', NULL, NO_DATE, presence_colour, "", "%s", sub->str);
+            win_vprint(console, '-', NULL, NO_DATE, presence_colour, "", "%s", sub->str);
         }
 
         g_string_free(sub, TRUE);
@@ -1657,10 +1657,10 @@ _show_roster_contacts(GSList *list, gboolean show_groups)
                     }
                     groups = g_slist_next(groups);
                 }
-                win_save_vprint(console, '-', NULL, NO_DATE, 0, "", "%s", groups_str->str);
+                win_vprint(console, '-', NULL, NO_DATE, 0, "", "%s", groups_str->str);
                 g_string_free(groups_str, TRUE);
             } else {
-                 win_save_print(console, '-', NULL, NO_DATE, 0, "", " ");
+                 win_print(console, '-', NULL, NO_DATE, 0, "", " ");
             }
         }
 
diff --git a/src/ui/core.c b/src/ui/core.c
index b9f526cc..aa77e32e 100644
--- a/src/ui/core.c
+++ b/src/ui/core.c
@@ -307,13 +307,13 @@ ui_handle_stanza(const char * const msg)
         ProfWin *window = (ProfWin*) xmlconsole;
 
         if (g_str_has_prefix(msg, "SENT:")) {
-            win_save_print(window, '-', NULL, 0, 0, "", "SENT:");
-            win_save_print(window, '-', NULL, 0, THEME_ONLINE, "", &msg[6]);
-            win_save_print(window, '-', NULL, 0, THEME_ONLINE, "", "");
+            win_print(window, '-', NULL, 0, 0, "", "SENT:");
+            win_print(window, '-', NULL, 0, THEME_ONLINE, "", &msg[6]);
+            win_print(window, '-', NULL, 0, THEME_ONLINE, "", "");
         } else if (g_str_has_prefix(msg, "RECV:")) {
-            win_save_print(window, '-', NULL, 0, 0, "", "RECV:");
-            win_save_print(window, '-', NULL, 0, THEME_AWAY, "", &msg[6]);
-            win_save_print(window, '-', NULL, 0, THEME_AWAY, "", "");
+            win_print(window, '-', NULL, 0, 0, "", "RECV:");
+            win_print(window, '-', NULL, 0, THEME_AWAY, "", &msg[6]);
+            win_print(window, '-', NULL, 0, THEME_AWAY, "", "");
         }
     }
 }
@@ -382,6 +382,16 @@ ui_get_current_chat(void)
 }
 
 void
+ui_message_receipt(const char * const barejid, const char * const id)
+{
+    ProfChatWin *chatwin = wins_get_chat(barejid);
+    if (chatwin) {
+        ProfWin *win = (ProfWin*) chatwin;
+        win_mark_received(win, id);
+    }
+}
+
+void
 ui_incoming_msg(const char * const barejid, const char * const resource, const char * const message, GTimeVal *tv_stamp)
 {
     gboolean win_created = FALSE;
@@ -649,7 +659,7 @@ ui_handle_recipient_not_found(const char * const recipient, const char * const e
     ProfMucWin *mucwin = wins_get_muc(recipient);
     if (mucwin) {
         cons_show_error("Room %s not found: %s", recipient, err_msg);
-        win_save_vprint((ProfWin*) mucwin, '!', NULL, 0, THEME_ERROR, "", "Room %s not found: %s", recipient, err_msg);
+        win_vprint((ProfWin*) mucwin, '!', NULL, 0, THEME_ERROR, "", "Room %s not found: %s", recipient, err_msg);
         return;
     }
 }
@@ -662,19 +672,19 @@ ui_handle_recipient_error(const char * const recipient, const char * const err_m
 
     ProfChatWin *chatwin = wins_get_chat(recipient);
     if (chatwin) {
-        win_save_vprint((ProfWin*)chatwin, '!', NULL, 0, THEME_ERROR, "", "Error from %s: %s", recipient, err_msg);
+        win_vprint((ProfWin*)chatwin, '!', NULL, 0, THEME_ERROR, "", "Error from %s: %s", recipient, err_msg);
         return;
     }
 
     ProfMucWin *mucwin = wins_get_muc(recipient);
     if (mucwin) {
-        win_save_vprint((ProfWin*)mucwin, '!', NULL, 0, THEME_ERROR, "", "Error from %s: %s", recipient, err_msg);
+        win_vprint((ProfWin*)mucwin, '!', NULL, 0, THEME_ERROR, "", "Error from %s: %s", recipient, err_msg);
         return;
     }
 
     ProfPrivateWin *privatewin = wins_get_private(recipient);
     if (privatewin) {
-        win_save_vprint((ProfWin*)privatewin, '!', NULL, 0, THEME_ERROR, "", "Error from %s: %s", recipient, err_msg);
+        win_vprint((ProfWin*)privatewin, '!', NULL, 0, THEME_ERROR, "", "Error from %s: %s", recipient, err_msg);
         return;
     }
 }
@@ -979,9 +989,9 @@ ui_gone_secure(const char * const barejid, gboolean trusted)
     chatwin->is_otr = TRUE;
     chatwin->is_trusted = trusted;
     if (trusted) {
-        win_save_print(window, '!', NULL, 0, THEME_OTR_STARTED_TRUSTED, "", "OTR session started (trusted).");
+        win_print(window, '!', NULL, 0, THEME_OTR_STARTED_TRUSTED, "", "OTR session started (trusted).");
     } else {
-        win_save_print(window, '!', NULL, 0, THEME_OTR_STARTED_UNTRUSTED, "", "OTR session started (untrusted).");
+        win_print(window, '!', NULL, 0, THEME_OTR_STARTED_UNTRUSTED, "", "OTR session started (untrusted).");
     }
 
     if (wins_is_current(window)) {
@@ -1008,7 +1018,7 @@ ui_gone_insecure(const char * const barejid)
         chatwin->is_trusted = FALSE;
 
         ProfWin *window = (ProfWin*)chatwin;
-        win_save_print(window, '!', NULL, 0, THEME_OTR_ENDED, "", "OTR session ended.");
+        win_print(window, '!', NULL, 0, THEME_OTR_ENDED, "", "OTR session ended.");
         if (wins_is_current(window)) {
             title_bar_switch();
         }
@@ -1020,7 +1030,7 @@ ui_smp_recipient_initiated(const char * const barejid)
 {
     ProfChatWin *chatwin = wins_get_chat(barejid);
     if (chatwin) {
-        win_save_vprint((ProfWin*)chatwin, '!', NULL, 0, 0, "", "%s wants to authenticate your identity, use '/otr secret <secret>'.", barejid);
+        win_vprint((ProfWin*)chatwin, '!', NULL, 0, 0, "", "%s wants to authenticate your identity, use '/otr secret <secret>'.", barejid);
     }
 }
 
@@ -1029,9 +1039,9 @@ ui_smp_recipient_initiated_q(const char * const barejid, const char *question)
 {
     ProfChatWin *chatwin = wins_get_chat(barejid);
     if (chatwin) {
-        win_save_vprint((ProfWin*)chatwin, '!', NULL, 0, 0, "", "%s wants to authenticate your identity with the following question:", barejid);
-        win_save_vprint((ProfWin*)chatwin, '!', NULL, 0, 0, "", "  %s", question);
-        win_save_print((ProfWin*)chatwin, '!', NULL, 0, 0, "", "use '/otr answer <answer>'.");
+        win_vprint((ProfWin*)chatwin, '!', NULL, 0, 0, "", "%s wants to authenticate your identity with the following question:", barejid);
+        win_vprint((ProfWin*)chatwin, '!', NULL, 0, 0, "", "  %s", question);
+        win_print((ProfWin*)chatwin, '!', NULL, 0, 0, "", "use '/otr answer <answer>'.");
     }
 }
 
@@ -1040,7 +1050,7 @@ ui_smp_unsuccessful_sender(const char * const barejid)
 {
     ProfChatWin *chatwin = wins_get_chat(barejid);
     if (chatwin) {
-        win_save_vprint((ProfWin*)chatwin, '!', NULL, 0, 0, "", "Authentication failed, the secret you entered does not match the secret entered by %s.", barejid);
+        win_vprint((ProfWin*)chatwin, '!', NULL, 0, 0, "", "Authentication failed, the secret you entered does not match the secret entered by %s.", barejid);
     }
 }
 
@@ -1049,7 +1059,7 @@ ui_smp_unsuccessful_receiver(const char * const barejid)
 {
     ProfChatWin *chatwin = wins_get_chat(barejid);
     if (chatwin) {
-        win_save_vprint((ProfWin*)chatwin, '!', NULL, 0, 0, "", "Authentication failed, the secret entered by %s does not match yours.", barejid);
+        win_vprint((ProfWin*)chatwin, '!', NULL, 0, 0, "", "Authentication failed, the secret entered by %s does not match yours.", barejid);
     }
 }
 
@@ -1058,7 +1068,7 @@ ui_smp_aborted(const char * const barejid)
 {
     ProfChatWin *chatwin = wins_get_chat(barejid);
     if (chatwin) {
-        win_save_print((ProfWin*)chatwin, '!', NULL, 0, 0, "", "SMP session aborted.");
+        win_print((ProfWin*)chatwin, '!', NULL, 0, 0, "", "SMP session aborted.");
     }
 }
 
@@ -1067,7 +1077,7 @@ ui_smp_successful(const char * const barejid)
 {
     ProfChatWin *chatwin = wins_get_chat(barejid);
     if (chatwin) {
-        win_save_print((ProfWin*)chatwin, '!', NULL, 0, 0, "", "Authentication successful.");
+        win_print((ProfWin*)chatwin, '!', NULL, 0, 0, "", "Authentication successful.");
     }
 }
 
@@ -1076,7 +1086,7 @@ ui_smp_answer_success(const char * const barejid)
 {
     ProfChatWin *chatwin = wins_get_chat(barejid);
     if (chatwin) {
-        win_save_vprint((ProfWin*)chatwin, '!', NULL, 0, 0, "", "%s successfully authenticated you.", barejid);
+        win_vprint((ProfWin*)chatwin, '!', NULL, 0, 0, "", "%s successfully authenticated you.", barejid);
     }
 }
 
@@ -1085,7 +1095,7 @@ ui_smp_answer_failure(const char * const barejid)
 {
     ProfChatWin *chatwin = wins_get_chat(barejid);
     if (chatwin) {
-        win_save_vprint((ProfWin*)chatwin, '!', NULL, 0, 0, "", "%s failed to authenticate you.", barejid);
+        win_vprint((ProfWin*)chatwin, '!', NULL, 0, 0, "", "%s failed to authenticate you.", barejid);
     }
 }
 
@@ -1094,7 +1104,7 @@ ui_otr_authenticating(const char * const barejid)
 {
     ProfChatWin *chatwin = wins_get_chat(barejid);
     if (chatwin) {
-        win_save_vprint((ProfWin*)chatwin, '!', NULL, 0, 0, "", "Authenticating %s...", barejid);
+        win_vprint((ProfWin*)chatwin, '!', NULL, 0, 0, "", "Authenticating %s...", barejid);
     }
 }
 
@@ -1103,7 +1113,7 @@ ui_otr_authetication_waiting(const char * const barejid)
 {
     ProfChatWin *chatwin = wins_get_chat(barejid);
     if (chatwin) {
-        win_save_vprint((ProfWin*)chatwin, '!', NULL, 0, 0, "", "Awaiting authentication from %s...", barejid);
+        win_vprint((ProfWin*)chatwin, '!', NULL, 0, 0, "", "Awaiting authentication from %s...", barejid);
     }
 }
 
@@ -1116,7 +1126,7 @@ ui_trust(const char * const barejid)
         chatwin->is_trusted = TRUE;
 
         ProfWin *window = (ProfWin*)chatwin;
-        win_save_print(window, '!', NULL, 0, THEME_OTR_TRUSTED, "", "OTR session trusted.");
+        win_print(window, '!', NULL, 0, THEME_OTR_TRUSTED, "", "OTR session trusted.");
         if (wins_is_current(window)) {
             title_bar_switch();
         }
@@ -1132,7 +1142,7 @@ ui_untrust(const char * const barejid)
         chatwin->is_trusted = FALSE;
 
         ProfWin *window = (ProfWin*)chatwin;
-        win_save_print(window, '!', NULL, 0, THEME_OTR_UNTRUSTED, "", "OTR session untrusted.");
+        win_print(window, '!', NULL, 0, THEME_OTR_UNTRUSTED, "", "OTR session untrusted.");
         if (wins_is_current(window)) {
             title_bar_switch();
         }
@@ -1260,7 +1270,7 @@ ui_current_print_line(const char * const msg, ...)
     va_start(arg, msg);
     GString *fmt_msg = g_string_new(NULL);
     g_string_vprintf(fmt_msg, msg, arg);
-    win_save_println(window, fmt_msg->str);
+    win_println(window, fmt_msg->str);
     va_end(arg);
     g_string_free(fmt_msg, TRUE);
 }
@@ -1273,7 +1283,7 @@ ui_current_print_formatted_line(const char show_char, int attrs, const char * co
     va_start(arg, msg);
     GString *fmt_msg = g_string_new(NULL);
     g_string_vprintf(fmt_msg, msg, arg);
-    win_save_print(current, show_char, NULL, 0, attrs, "", fmt_msg->str);
+    win_print(current, show_char, NULL, 0, attrs, "", fmt_msg->str);
     va_end(arg);
     g_string_free(fmt_msg, TRUE);
 }
@@ -1282,7 +1292,7 @@ void
 ui_current_error_line(const char * const msg)
 {
     ProfWin *current = wins_get_current();
-    win_save_print(current, '-', NULL, 0, THEME_ERROR, "", msg);
+    win_print(current, '-', NULL, 0, THEME_ERROR, "", msg);
 }
 
 void
@@ -1305,7 +1315,7 @@ ui_print_system_msg_from_recipient(const char * const barejid, const char *messa
         }
     }
 
-    win_save_vprint(window, '-', NULL, 0, 0, "", "*%s %s", barejid, message);
+    win_vprint(window, '-', NULL, 0, 0, "", "*%s %s", barejid, message);
 }
 
 void
@@ -1337,7 +1347,7 @@ ui_recipient_gone(const char * const barejid, const char * const resource)
                 display_usr = barejid;
             }
 
-            win_save_vprint((ProfWin*)chatwin, '!', NULL, 0, THEME_GONE, "", "<- %s has left the conversation.", display_usr);
+            win_vprint((ProfWin*)chatwin, '!', NULL, 0, THEME_GONE, "", "<- %s has left the conversation.", display_usr);
         }
     }
 }
@@ -1410,8 +1420,7 @@ ui_open_xmlconsole_win(void)
 }
 
 void
-ui_outgoing_chat_msg(const char * const from, const char * const barejid,
-    const char * const message)
+ui_outgoing_chat_msg(const char * const barejid, const char * const message, char *id)
 {
     PContact contact = roster_get_contact(barejid);
     ProfWin *window = (ProfWin*)wins_get_chat(barejid);
@@ -1447,13 +1456,16 @@ ui_outgoing_chat_msg(const char * const from, const char * const barejid,
     ProfChatWin *chatwin = (ProfChatWin*)window;
     chat_state_active(chatwin->state);
 
-    win_save_print(window, '-', NULL, 0, THEME_TEXT_ME, from, message);
+    if (prefs_get_boolean(PREF_RECEIPTS) && id) {
+        win_print_with_receipt(window, '-', NULL, 0, THEME_TEXT_ME, "me", message, id);
+    } else {
+        win_print(window, '-', NULL, 0, THEME_TEXT_ME, "me", message);
+    }
     ui_switch_win(num);
 }
 
 void
-ui_outgoing_private_msg(const char * const from, const char * const fulljid,
-    const char * const message)
+ui_outgoing_private_msg(const char * const fulljid, const char * const message)
 {
     ProfWin *window = (ProfWin*)wins_get_private(fulljid);
     int num = 0;
@@ -1468,7 +1480,7 @@ ui_outgoing_private_msg(const char * const from, const char * const fulljid,
         num = wins_get_num(window);
     }
 
-    win_save_print(window, '-', NULL, 0, THEME_TEXT_ME, from, message);
+    win_print(window, '-', NULL, 0, THEME_TEXT_ME, "me", message);
     ui_switch_win(num);
 }
 
@@ -1484,18 +1496,18 @@ ui_room_join(const char * const roomjid, gboolean focus)
     }
 
     char *nick = muc_nick(roomjid);
-    win_save_vprint(window, '!', NULL, NO_EOL, THEME_ROOMINFO, "", "-> You have joined the room as %s", nick);
+    win_vprint(window, '!', NULL, NO_EOL, THEME_ROOMINFO, "", "-> You have joined the room as %s", nick);
     if (prefs_get_boolean(PREF_MUC_PRIVILEGES)) {
         char *role = muc_role_str(roomjid);
         char *affiliation = muc_affiliation_str(roomjid);
         if (role) {
-            win_save_vprint(window, '!', NULL, NO_DATE | NO_EOL, THEME_ROOMINFO, "", ", role: %s", role);
+            win_vprint(window, '!', NULL, NO_DATE | NO_EOL, THEME_ROOMINFO, "", ", role: %s", role);
         }
         if (affiliation) {
-            win_save_vprint(window, '!', NULL, NO_DATE | NO_EOL, THEME_ROOMINFO, "", ", affiliation: %s", affiliation);
+            win_vprint(window, '!', NULL, NO_DATE | NO_EOL, THEME_ROOMINFO, "", ", affiliation: %s", affiliation);
         }
     }
-    win_save_print(window, '!', NULL, NO_DATE, THEME_ROOMINFO, "", "");
+    win_print(window, '!', NULL, NO_DATE, THEME_ROOMINFO, "", "");
 
     num = wins_get_num(window);
 
@@ -1505,7 +1517,7 @@ ui_room_join(const char * const roomjid, gboolean focus)
         status_bar_active(num);
         ProfWin *console = wins_get_console();
         char *nick = muc_nick(roomjid);
-        win_save_vprint(console, '!', NULL, 0, THEME_TYPING, "", "-> Autojoined %s as %s (%d).", roomjid, nick, num);
+        win_vprint(console, '!', NULL, 0, THEME_TYPING, "", "-> Autojoined %s as %s (%d).", roomjid, nick, num);
     }
 }
 
@@ -1523,14 +1535,14 @@ ui_room_role_change(const char * const roomjid, const char * const role, const c
     const char * const reason)
 {
     ProfWin *window = (ProfWin*)wins_get_muc(roomjid);
-    win_save_vprint(window, '!', NULL, NO_EOL, THEME_ROOMINFO, "", "Your role has been changed to: %s", role);
+    win_vprint(window, '!', NULL, NO_EOL, THEME_ROOMINFO, "", "Your role has been changed to: %s", role);
     if (actor) {
-        win_save_vprint(window, '!', NULL, NO_DATE | NO_EOL, THEME_ROOMINFO, "", ", by: %s", actor);
+        win_vprint(window, '!', NULL, NO_DATE | NO_EOL, THEME_ROOMINFO, "", ", by: %s", actor);
     }
     if (reason) {
-        win_save_vprint(window, '!', NULL, NO_DATE | NO_EOL, THEME_ROOMINFO, "", ", reason: %s", reason);
+        win_vprint(window, '!', NULL, NO_DATE | NO_EOL, THEME_ROOMINFO, "", ", reason: %s", reason);
     }
-    win_save_print(window, '!', NULL, NO_DATE, THEME_ROOMINFO, "", "");
+    win_print(window, '!', NULL, NO_DATE, THEME_ROOMINFO, "", "");
 }
 
 void
@@ -1538,14 +1550,14 @@ ui_room_affiliation_change(const char * const roomjid, const char * const affili
     const char * const reason)
 {
     ProfWin *window = (ProfWin*)wins_get_muc(roomjid);
-    win_save_vprint(window, '!', NULL, NO_EOL, THEME_ROOMINFO, "", "Your affiliation has been changed to: %s", affiliation);
+    win_vprint(window, '!', NULL, NO_EOL, THEME_ROOMINFO, "", "Your affiliation has been changed to: %s", affiliation);
     if (actor) {
-        win_save_vprint(window, '!', NULL, NO_DATE | NO_EOL, THEME_ROOMINFO, "", ", by: %s", actor);
+        win_vprint(window, '!', NULL, NO_DATE | NO_EOL, THEME_ROOMINFO, "", ", by: %s", actor);
     }
     if (reason) {
-        win_save_vprint(window, '!', NULL, NO_DATE | NO_EOL, THEME_ROOMINFO, "", ", reason: %s", reason);
+        win_vprint(window, '!', NULL, NO_DATE | NO_EOL, THEME_ROOMINFO, "", ", reason: %s", reason);
     }
-    win_save_print(window, '!', NULL, NO_DATE, THEME_ROOMINFO, "", "");
+    win_print(window, '!', NULL, NO_DATE, THEME_ROOMINFO, "", "");
 }
 
 void
@@ -1553,14 +1565,14 @@ ui_room_role_and_affiliation_change(const char * const roomjid, const char * con
     const char * const actor, const char * const reason)
 {
     ProfWin *window = (ProfWin*)wins_get_muc(roomjid);
-    win_save_vprint(window, '!', NULL, NO_EOL, THEME_ROOMINFO, "", "Your role and affiliation have been changed, role: %s, affiliation: %s", role, affiliation);
+    win_vprint(window, '!', NULL, NO_EOL, THEME_ROOMINFO, "", "Your role and affiliation have been changed, role: %s, affiliation: %s", role, affiliation);
     if (actor) {
-        win_save_vprint(window, '!', NULL, NO_DATE | NO_EOL, THEME_ROOMINFO, "", ", by: %s", actor);
+        win_vprint(window, '!', NULL, NO_DATE | NO_EOL, THEME_ROOMINFO, "", ", by: %s", actor);
     }
     if (reason) {
-        win_save_vprint(window, '!', NULL, NO_DATE | NO_EOL, THEME_ROOMINFO, "", ", reason: %s", reason);
+        win_vprint(window, '!', NULL, NO_DATE | NO_EOL, THEME_ROOMINFO, "", ", reason: %s", reason);
     }
-    win_save_print(window, '!', NULL, NO_DATE, THEME_ROOMINFO, "", "");
+    win_print(window, '!', NULL, NO_DATE, THEME_ROOMINFO, "", "");
 }
 
 
@@ -1569,14 +1581,14 @@ ui_room_occupant_role_change(const char * const roomjid, const char * const nick
     const char * const actor, const char * const reason)
 {
     ProfWin *window = (ProfWin*)wins_get_muc(roomjid);
-    win_save_vprint(window, '!', NULL, NO_EOL, THEME_ROOMINFO, "", "%s's role has been changed to: %s", nick, role);
+    win_vprint(window, '!', NULL, NO_EOL, THEME_ROOMINFO, "", "%s's role has been changed to: %s", nick, role);
     if (actor) {
-        win_save_vprint(window, '!', NULL, NO_DATE | NO_EOL, THEME_ROOMINFO, "", ", by: %s", actor);
+        win_vprint(window, '!', NULL, NO_DATE | NO_EOL, THEME_ROOMINFO, "", ", by: %s", actor);
     }
     if (reason) {
-        win_save_vprint(window, '!', NULL, NO_DATE | NO_EOL, THEME_ROOMINFO, "", ", reason: %s", reason);
+        win_vprint(window, '!', NULL, NO_DATE | NO_EOL, THEME_ROOMINFO, "", ", reason: %s", reason);
     }
-    win_save_print(window, '!', NULL, NO_DATE, THEME_ROOMINFO, "", "");
+    win_print(window, '!', NULL, NO_DATE, THEME_ROOMINFO, "", "");
 }
 
 void
@@ -1584,14 +1596,14 @@ ui_room_occupant_affiliation_change(const char * const roomjid, const char * con
     const char * const actor, const char * const reason)
 {
     ProfWin *window = (ProfWin*)wins_get_muc(roomjid);
-    win_save_vprint(window, '!', NULL, NO_EOL, THEME_ROOMINFO, "", "%s's affiliation has been changed to: %s", nick, affiliation);
+    win_vprint(window, '!', NULL, NO_EOL, THEME_ROOMINFO, "", "%s's affiliation has been changed to: %s", nick, affiliation);
     if (actor) {
-        win_save_vprint(window, '!', NULL, NO_DATE | NO_EOL, THEME_ROOMINFO, "", ", by: %s", actor);
+        win_vprint(window, '!', NULL, NO_DATE | NO_EOL, THEME_ROOMINFO, "", ", by: %s", actor);
     }
     if (reason) {
-        win_save_vprint(window, '!', NULL, NO_DATE | NO_EOL, THEME_ROOMINFO, "", ", reason: %s", reason);
+        win_vprint(window, '!', NULL, NO_DATE | NO_EOL, THEME_ROOMINFO, "", ", reason: %s", reason);
     }
-    win_save_print(window, '!', NULL, NO_DATE, THEME_ROOMINFO, "", "");
+    win_print(window, '!', NULL, NO_DATE, THEME_ROOMINFO, "", "");
 }
 
 void
@@ -1599,14 +1611,14 @@ ui_room_occupant_role_and_affiliation_change(const char * const roomjid, const c
     const char * const affiliation, const char * const actor, const char * const reason)
 {
     ProfWin *window = (ProfWin*)wins_get_muc(roomjid);
-    win_save_vprint(window, '!', NULL, NO_EOL, THEME_ROOMINFO, "", "%s's role and affiliation have been changed, role: %s, affiliation: %s", nick, role, affiliation);
+    win_vprint(window, '!', NULL, NO_EOL, THEME_ROOMINFO, "", "%s's role and affiliation have been changed, role: %s, affiliation: %s", nick, role, affiliation);
     if (actor) {
-        win_save_vprint(window, '!', NULL, NO_DATE | NO_EOL, THEME_ROOMINFO, "", ", by: %s", actor);
+        win_vprint(window, '!', NULL, NO_DATE | NO_EOL, THEME_ROOMINFO, "", ", by: %s", actor);
     }
     if (reason) {
-        win_save_vprint(window, '!', NULL, NO_DATE | NO_EOL, THEME_ROOMINFO, "", ", reason: %s", reason);
+        win_vprint(window, '!', NULL, NO_DATE | NO_EOL, THEME_ROOMINFO, "", ", reason: %s", reason);
     }
-    win_save_print(window, '!', NULL, NO_DATE, THEME_ROOMINFO, "", "");
+    win_print(window, '!', NULL, NO_DATE, THEME_ROOMINFO, "", "");
 }
 
 void
@@ -1614,8 +1626,8 @@ ui_handle_room_info_error(const char * const roomjid, const char * const error)
 {
     ProfWin *window = (ProfWin*)wins_get_muc(roomjid);
     if (window) {
-        win_save_vprint(window, '!', NULL, 0, 0, "", "Room info request failed: %s", error);
-        win_save_print(window, '-', NULL, 0, 0, "", "");
+        win_vprint(window, '!', NULL, 0, 0, "", "Room info request failed: %s", error);
+        win_print(window, '-', NULL, 0, 0, "", "");
     }
 }
 
@@ -1627,7 +1639,7 @@ ui_show_room_disco_info(const char * const roomjid, GSList *identities, GSList *
         if (((identities != NULL) && (g_slist_length(identities) > 0)) ||
             ((features != NULL) && (g_slist_length(features) > 0))) {
             if (identities != NULL) {
-                win_save_print(window, '!', NULL, 0, 0, "", "Identities:");
+                win_print(window, '!', NULL, 0, 0, "", "Identities:");
             }
             while (identities != NULL) {
                 DiscoIdentity *identity = identities->data;  // anme trpe, cat
@@ -1643,19 +1655,19 @@ ui_show_room_disco_info(const char * const roomjid, GSList *identities, GSList *
                 if (identity->category != NULL) {
                     identity_str = g_string_append(identity_str, identity->category);
                 }
-                win_save_print(window, '!', NULL, 0, 0, "", identity_str->str);
+                win_print(window, '!', NULL, 0, 0, "", identity_str->str);
                 g_string_free(identity_str, TRUE);
                 identities = g_slist_next(identities);
             }
 
             if (features != NULL) {
-                win_save_print(window, '!', NULL, 0, 0, "", "Features:");
+                win_print(window, '!', NULL, 0, 0, "", "Features:");
             }
             while (features != NULL) {
-                win_save_vprint(window, '!', NULL, 0, 0, "", "  %s", features->data);
+                win_vprint(window, '!', NULL, 0, 0, "", "  %s", features->data);
                 features = g_slist_next(features);
             }
-            win_save_print(window, '-', NULL, 0, 0, "", "");
+            win_print(window, '-', NULL, 0, 0, "", "");
         }
     }
 }
@@ -1669,16 +1681,16 @@ ui_room_roster(const char * const roomjid, GList *roster, const char * const pre
     } else {
         if ((roster == NULL) || (g_list_length(roster) == 0)) {
             if (presence == NULL) {
-                win_save_print(window, '!', NULL, 0, THEME_ROOMINFO, "", "Room is empty.");
+                win_print(window, '!', NULL, 0, THEME_ROOMINFO, "", "Room is empty.");
             } else {
-                win_save_vprint(window, '!', NULL, 0, THEME_ROOMINFO, "", "No occupants %s.", presence);
+                win_vprint(window, '!', NULL, 0, THEME_ROOMINFO, "", "No occupants %s.", presence);
             }
         } else {
             int length = g_list_length(roster);
             if (presence == NULL) {
-                win_save_vprint(window, '!', NULL, NO_EOL, THEME_ROOMINFO, "", "%d occupants: ", length);
+                win_vprint(window, '!', NULL, NO_EOL, THEME_ROOMINFO, "", "%d occupants: ", length);
             } else {
-                win_save_vprint(window, '!', NULL, NO_EOL, THEME_ROOMINFO, "", "%d %s: ", length, presence);
+                win_vprint(window, '!', NULL, NO_EOL, THEME_ROOMINFO, "", "%d %s: ", length, presence);
             }
 
             while (roster != NULL) {
@@ -1686,15 +1698,15 @@ ui_room_roster(const char * const roomjid, GList *roster, const char * const pre
                 const char *presence_str = string_from_resource_presence(occupant->presence);
 
                 theme_item_t presence_colour = theme_main_presence_attrs(presence_str);
-                win_save_vprint(window, '!', NULL, NO_DATE | NO_EOL, presence_colour, "", "%s", occupant->nick);
+                win_vprint(window, '!', NULL, NO_DATE | NO_EOL, presence_colour, "", "%s", occupant->nick);
 
                 if (roster->next != NULL) {
-                    win_save_print(window, '!', NULL, NO_DATE | NO_EOL, 0, "", ", ");
+                    win_print(window, '!', NULL, NO_DATE | NO_EOL, 0, "", ", ");
                 }
 
                 roster = g_list_next(roster);
             }
-            win_save_print(window, '!', NULL, NO_DATE, THEME_ONLINE, "", "");
+            win_print(window, '!', NULL, NO_DATE, THEME_ONLINE, "", "");
 
         }
     }
@@ -1713,7 +1725,7 @@ ui_room_member_offline(const char * const roomjid, const char * const nick)
     if (window == NULL) {
         log_error("Received offline presence for room participant %s, but no window open for %s.", nick, roomjid);
     } else {
-        win_save_vprint(window, '!', NULL, 0, THEME_OFFLINE, "", "<- %s has left the room.", nick);
+        win_vprint(window, '!', NULL, 0, THEME_OFFLINE, "", "<- %s has left the room.", nick);
     }
 }
 
@@ -1736,7 +1748,7 @@ ui_room_member_kicked(const char * const roomjid, const char * const nick, const
             g_string_append(message, reason);
         }
 
-        win_save_vprint(window, '!', NULL, 0, THEME_OFFLINE, "", "<- %s", message->str);
+        win_vprint(window, '!', NULL, 0, THEME_OFFLINE, "", "<- %s", message->str);
         g_string_free(message, TRUE);
     }
 }
@@ -1760,7 +1772,7 @@ ui_room_member_banned(const char * const roomjid, const char * const nick, const
             g_string_append(message, reason);
         }
 
-        win_save_vprint(window, '!', NULL, 0, THEME_OFFLINE, "", "<- %s", message->str);
+        win_vprint(window, '!', NULL, 0, THEME_OFFLINE, "", "<- %s", message->str);
         g_string_free(message, TRUE);
     }
 }
@@ -1773,16 +1785,16 @@ ui_room_member_online(const char * const roomjid, const char * const nick, const
     if (window == NULL) {
         log_error("Received online presence for room participant %s, but no window open for %s.", nick, roomjid);
     } else {
-        win_save_vprint(window, '!', NULL, NO_EOL, THEME_ONLINE, "", "-> %s has joined the room", nick);
+        win_vprint(window, '!', NULL, NO_EOL, THEME_ONLINE, "", "-> %s has joined the room", nick);
         if (prefs_get_boolean(PREF_MUC_PRIVILEGES)) {
             if (role) {
-                win_save_vprint(window, '!', NULL, NO_DATE | NO_EOL, THEME_ONLINE, "", ", role: %s", role);
+                win_vprint(window, '!', NULL, NO_DATE | NO_EOL, THEME_ONLINE, "", ", role: %s", role);
             }
             if (affiliation) {
-                win_save_vprint(window, '!', NULL, NO_DATE | NO_EOL, THEME_ONLINE, "", ", affiliation: %s", affiliation);
+                win_vprint(window, '!', NULL, NO_DATE | NO_EOL, THEME_ONLINE, "", ", affiliation: %s", affiliation);
             }
         }
-        win_save_print(window, '!', NULL, NO_DATE, THEME_ROOMINFO, "", "");
+        win_print(window, '!', NULL, NO_DATE, THEME_ROOMINFO, "", "");
     }
 }
 
@@ -1806,7 +1818,7 @@ ui_room_member_nick_change(const char * const roomjid,
     if (window == NULL) {
         log_error("Received nick change for room participant %s, but no window open for %s.", old_nick, roomjid);
     } else {
-        win_save_vprint(window, '!', NULL, 0, THEME_THEM, "", "** %s is now known as %s", old_nick, nick);
+        win_vprint(window, '!', NULL, 0, THEME_THEM, "", "** %s is now known as %s", old_nick, nick);
     }
 }
 
@@ -1817,7 +1829,7 @@ ui_room_nick_change(const char * const roomjid, const char * const nick)
     if (window == NULL) {
         log_error("Received self nick change %s, but no window open for %s.", nick, roomjid);
     } else {
-        win_save_vprint(window, '!', NULL, 0, THEME_ME, "", "** You are now known as %s", nick);
+        win_vprint(window, '!', NULL, 0, THEME_ME, "", "** You are now known as %s", nick);
     }
 }
 
@@ -1842,7 +1854,7 @@ ui_room_history(const char * const roomjid, const char * const nick,
             g_string_append(line, message);
         }
 
-        win_save_print(window, '-', &tv_stamp, NO_COLOUR_DATE, 0, "", line->str);
+        win_print(window, '-', &tv_stamp, NO_COLOUR_DATE, 0, "", line->str);
         g_string_free(line, TRUE);
     }
 }
@@ -1861,12 +1873,12 @@ ui_room_message(const char * const roomjid, const char * const nick,
 
         if (g_strcmp0(nick, my_nick) != 0) {
             if (g_strrstr(message, my_nick) != NULL) {
-                win_save_print(window, '-', NULL, NO_ME, THEME_ROOMMENTION, nick, message);
+                win_print(window, '-', NULL, NO_ME, THEME_ROOMMENTION, nick, message);
             } else {
-                win_save_print(window, '-', NULL, NO_ME, THEME_TEXT_THEM, nick, message);
+                win_print(window, '-', NULL, NO_ME, THEME_TEXT_THEM, nick, message);
             }
         } else {
-            win_save_print(window, '-', NULL, 0, THEME_TEXT_ME, nick, message);
+            win_print(window, '-', NULL, 0, THEME_TEXT_ME, nick, message);
         }
 
         // currently in groupchat window
@@ -1942,16 +1954,16 @@ ui_room_requires_config(const char * const roomjid)
             ui_index = 0;
         }
 
-        win_save_print(window, '-', NULL, 0, 0, "", "");
-        win_save_vprint(window, '!', NULL, 0, THEME_ROOMINFO, "",
+        win_print(window, '-', NULL, 0, 0, "", "");
+        win_vprint(window, '!', NULL, 0, THEME_ROOMINFO, "",
             "Room locked, requires configuration.");
-        win_save_vprint(window, '!', NULL, 0, THEME_ROOMINFO, "",
+        win_vprint(window, '!', NULL, 0, THEME_ROOMINFO, "",
             "Use '/room accept' to accept the defaults");
-        win_save_vprint(window, '!', NULL, 0, THEME_ROOMINFO, "",
+        win_vprint(window, '!', NULL, 0, THEME_ROOMINFO, "",
             "Use '/room destroy' to cancel and destroy the room");
-        win_save_vprint(window, '!', NULL, 0, THEME_ROOMINFO, "",
+        win_vprint(window, '!', NULL, 0, THEME_ROOMINFO, "",
             "Use '/room config' to edit the room configuration");
-        win_save_print(window, '-', NULL, 0, 0, "", "");
+        win_print(window, '-', NULL, 0, 0, "", "");
 
         // currently in groupchat window
         if (wins_is_current(window)) {
@@ -2000,16 +2012,16 @@ ui_room_destroyed(const char * const roomjid, const char * const reason, const c
         ProfWin *console = wins_get_console();
 
         if (reason) {
-            win_save_vprint(console, '!', NULL, 0, THEME_TYPING, "", "<- Room destroyed: %s, reason: %s", roomjid, reason);
+            win_vprint(console, '!', NULL, 0, THEME_TYPING, "", "<- Room destroyed: %s, reason: %s", roomjid, reason);
         } else {
-            win_save_vprint(console, '!', NULL, 0, THEME_TYPING, "", "<- Room destroyed: %s", roomjid);
+            win_vprint(console, '!', NULL, 0, THEME_TYPING, "", "<- Room destroyed: %s", roomjid);
         }
 
         if (new_jid) {
             if (password) {
-                win_save_vprint(console, '!', NULL, 0, THEME_TYPING, "", "Replacement room: %s, password: %s", new_jid, password);
+                win_vprint(console, '!', NULL, 0, THEME_TYPING, "", "Replacement room: %s, password: %s", new_jid, password);
             } else {
-                win_save_vprint(console, '!', NULL, 0, THEME_TYPING, "", "Replacement room: %s", new_jid);
+                win_vprint(console, '!', NULL, 0, THEME_TYPING, "", "Replacement room: %s", new_jid);
             }
         }
     }
@@ -2037,7 +2049,7 @@ ui_room_kicked(const char * const roomjid, const char * const actor, const char
         }
 
         ProfWin *console = wins_get_console();
-        win_save_vprint(console, '!', NULL, 0, THEME_TYPING, "", "<- %s", message->str);
+        win_vprint(console, '!', NULL, 0, THEME_TYPING, "", "<- %s", message->str);
         g_string_free(message, TRUE);
     }
 }
@@ -2064,7 +2076,7 @@ ui_room_banned(const char * const roomjid, const char * const actor, const char
         }
 
         ProfWin *console = wins_get_console();
-        win_save_vprint(console, '!', NULL, 0, THEME_TYPING, "", "<- %s", message->str);
+        win_vprint(console, '!', NULL, 0, THEME_TYPING, "", "<- %s", message->str);
         g_string_free(message, TRUE);
     }
 }
@@ -2080,17 +2092,17 @@ ui_room_subject(const char * const roomjid, const char * const nick, const char
 
         if (subject) {
             if (nick) {
-                win_save_vprint(window, '!', NULL, NO_EOL, THEME_ROOMINFO, "", "*%s has set the room subject: ", nick);
-                win_save_vprint(window, '!', NULL, NO_DATE, 0, "", "%s", subject);
+                win_vprint(window, '!', NULL, NO_EOL, THEME_ROOMINFO, "", "*%s has set the room subject: ", nick);
+                win_vprint(window, '!', NULL, NO_DATE, 0, "", "%s", subject);
             } else {
-                win_save_vprint(window, '!', NULL, NO_EOL, THEME_ROOMINFO, "", "Room subject: ");
-                win_save_vprint(window, '!', NULL, NO_DATE, 0, "", "%s", subject);
+                win_vprint(window, '!', NULL, NO_EOL, THEME_ROOMINFO, "", "Room subject: ");
+                win_vprint(window, '!', NULL, NO_DATE, 0, "", "%s", subject);
             }
         } else {
             if (nick) {
-                win_save_vprint(window, '!', NULL, 0, THEME_ROOMINFO, "", "*%s has cleared the room subject: ", nick);
+                win_vprint(window, '!', NULL, 0, THEME_ROOMINFO, "", "*%s has cleared the room subject: ", nick);
             } else {
-                win_save_vprint(window, '!', NULL, 0, THEME_ROOMINFO, "", "Room subject cleared");
+                win_vprint(window, '!', NULL, 0, THEME_ROOMINFO, "", "Room subject cleared");
             }
         }
 
@@ -2112,7 +2124,7 @@ ui_handle_room_kick_error(const char * const roomjid, const char * const nick, c
     if (window == NULL) {
         log_error("Kick error received for %s, but no window open for %s.", nick, roomjid);
     } else {
-        win_save_vprint(window, '!', NULL, 0, THEME_ERROR, "", "Error kicking %s: %s", nick, error);
+        win_vprint(window, '!', NULL, 0, THEME_ERROR, "", "Error kicking %s: %s", nick, error);
     }
 }
 
@@ -2125,8 +2137,8 @@ ui_room_broadcast(const char * const roomjid, const char * const message)
     } else {
         int num = wins_get_num(window);
 
-        win_save_vprint(window, '!', NULL, NO_EOL, THEME_ROOMINFO, "", "Room message: ");
-        win_save_vprint(window, '!', NULL, NO_DATE, 0, "", "%s", message);
+        win_vprint(window, '!', NULL, NO_EOL, THEME_ROOMINFO, "", "Room message: ");
+        win_vprint(window, '!', NULL, NO_DATE, 0, "", "%s", message);
 
         // currently in groupchat window
         if (wins_is_current(window)) {
@@ -2145,7 +2157,7 @@ ui_handle_room_affiliation_list_error(const char * const roomjid, const char * c
 {
     ProfWin *window = (ProfWin*)wins_get_muc(roomjid);
     if (window) {
-        win_save_vprint(window, '!', NULL, 0, THEME_ERROR, "", "Error retrieving %s list: %s", affiliation, error);
+        win_vprint(window, '!', NULL, 0, THEME_ERROR, "", "Error retrieving %s list: %s", affiliation, error);
     }
 }
 
@@ -2155,17 +2167,17 @@ ui_handle_room_affiliation_list(const char * const roomjid, const char * const a
     ProfWin *window = (ProfWin*)wins_get_muc(roomjid);
     if (window) {
         if (jids) {
-            win_save_vprint(window, '!', NULL, 0, 0, "", "Affiliation: %s", affiliation);
+            win_vprint(window, '!', NULL, 0, 0, "", "Affiliation: %s", affiliation);
             GSList *curr_jid = jids;
             while (curr_jid) {
                 char *jid = curr_jid->data;
-                win_save_vprint(window, '!', NULL, 0, 0, "", "  %s", jid);
+                win_vprint(window, '!', NULL, 0, 0, "", "  %s", jid);
                 curr_jid = g_slist_next(curr_jid);
             }
-            win_save_print(window, '!', NULL, 0, 0, "", "");
+            win_print(window, '!', NULL, 0, 0, "", "");
         } else {
-            win_save_vprint(window, '!', NULL, 0, 0, "", "No users found with affiliation: %s", affiliation);
-            win_save_print(window, '!', NULL, 0, 0, "", "");
+            win_vprint(window, '!', NULL, 0, 0, "", "No users found with affiliation: %s", affiliation);
+            win_print(window, '!', NULL, 0, 0, "", "");
         }
     }
 }
@@ -2175,7 +2187,7 @@ ui_handle_room_role_list_error(const char * const roomjid, const char * const ro
 {
     ProfWin *window = (ProfWin*)wins_get_muc(roomjid);
     if (window) {
-        win_save_vprint(window, '!', NULL, 0, THEME_ERROR, "", "Error retrieving %s list: %s", role, error);
+        win_vprint(window, '!', NULL, 0, THEME_ERROR, "", "Error retrieving %s list: %s", role, error);
     }
 }
 
@@ -2185,26 +2197,26 @@ ui_handle_room_role_list(const char * const roomjid, const char * const role, GS
     ProfWin *window = (ProfWin*)wins_get_muc(roomjid);
     if (window) {
         if (nicks) {
-            win_save_vprint(window, '!', NULL, 0, 0, "", "Role: %s", role);
+            win_vprint(window, '!', NULL, 0, 0, "", "Role: %s", role);
             GSList *curr_nick = nicks;
             while (curr_nick) {
                 char *nick = curr_nick->data;
                 Occupant *occupant = muc_roster_item(roomjid, nick);
                 if (occupant) {
                     if (occupant->jid) {
-                        win_save_vprint(window, '!', NULL, 0, 0, "", "  %s (%s)", nick, occupant->jid);
+                        win_vprint(window, '!', NULL, 0, 0, "", "  %s (%s)", nick, occupant->jid);
                     } else {
-                        win_save_vprint(window, '!', NULL, 0, 0, "", "  %s", nick);
+                        win_vprint(window, '!', NULL, 0, 0, "", "  %s", nick);
                     }
                 } else {
-                    win_save_vprint(window, '!', NULL, 0, 0, "", "  %s", nick);
+                    win_vprint(window, '!', NULL, 0, 0, "", "  %s", nick);
                 }
                 curr_nick = g_slist_next(curr_nick);
             }
-            win_save_print(window, '!', NULL, 0, 0, "", "");
+            win_print(window, '!', NULL, 0, 0, "", "");
         } else {
-            win_save_vprint(window, '!', NULL, 0, 0, "", "No occupants found with role: %s", role);
-            win_save_print(window, '!', NULL, 0, 0, "", "");
+            win_vprint(window, '!', NULL, 0, 0, "", "No occupants found with role: %s", role);
+            win_print(window, '!', NULL, 0, 0, "", "");
         }
     }
 }
@@ -2215,7 +2227,7 @@ ui_handle_room_affiliation_set_error(const char * const roomjid, const char * co
 {
     ProfWin *window = (ProfWin*)wins_get_muc(roomjid);
     if (window) {
-        win_save_vprint(window, '!', NULL, 0, THEME_ERROR, "", "Error setting %s affiliation for %s: %s", affiliation, jid, error);
+        win_vprint(window, '!', NULL, 0, THEME_ERROR, "", "Error setting %s affiliation for %s: %s", affiliation, jid, error);
     }
 }
 
@@ -2225,7 +2237,7 @@ ui_handle_room_role_set_error(const char * const roomjid, const char * const nic
 {
     ProfWin *window = (ProfWin*)wins_get_muc(roomjid);
     if (window) {
-        win_save_vprint(window, '!', NULL, 0, THEME_ERROR, "", "Error setting %s role for %s: %s", role, nick, error);
+        win_vprint(window, '!', NULL, 0, THEME_ERROR, "", "Error setting %s role for %s: %s", role, nick, error);
     }
 }
 
@@ -2388,10 +2400,10 @@ ui_show_room_info(ProfMucWin *mucwin)
     char *affiliation = muc_affiliation_str(mucwin->roomjid);
 
     ProfWin *window = (ProfWin*) mucwin;
-    win_save_vprint(window, '!', NULL, 0, 0, "", "Room: %s", mucwin->roomjid);
-    win_save_vprint(window, '!', NULL, 0, 0, "", "Affiliation: %s", affiliation);
-    win_save_vprint(window, '!', NULL, 0, 0, "", "Role: %s", role);
-    win_save_print(window, '-', NULL, 0, 0, "", "");
+    win_vprint(window, '!', NULL, 0, 0, "", "Room: %s", mucwin->roomjid);
+    win_vprint(window, '!', NULL, 0, 0, "", "Affiliation: %s", affiliation);
+    win_vprint(window, '!', NULL, 0, 0, "", "Role: %s", role);
+    win_print(window, '-', NULL, 0, 0, "", "");
 }
 
 void
@@ -2403,28 +2415,28 @@ ui_show_room_role_list(ProfMucWin *mucwin, muc_role_t role)
     if (!occupants) {
         switch (role) {
             case MUC_ROLE_MODERATOR:
-                win_save_print(window, '!', NULL, 0, 0, "", "No moderators found.");
+                win_print(window, '!', NULL, 0, 0, "", "No moderators found.");
                 break;
             case MUC_ROLE_PARTICIPANT:
-                win_save_print(window, '!', NULL, 0, 0, "", "No participants found.");
+                win_print(window, '!', NULL, 0, 0, "", "No participants found.");
                 break;
             case MUC_ROLE_VISITOR:
-                win_save_print(window, '!', NULL, 0, 0, "", "No visitors found.");
+                win_print(window, '!', NULL, 0, 0, "", "No visitors found.");
                 break;
             default:
                 break;
         }
-        win_save_print(window, '-', NULL, 0, 0, "", "");
+        win_print(window, '-', NULL, 0, 0, "", "");
     } else {
         switch (role) {
             case MUC_ROLE_MODERATOR:
-                win_save_print(window, '!', NULL, 0, 0, "", "Moderators:");
+                win_print(window, '!', NULL, 0, 0, "", "Moderators:");
                 break;
             case MUC_ROLE_PARTICIPANT:
-                win_save_print(window, '!', NULL, 0, 0, "", "Participants:");
+                win_print(window, '!', NULL, 0, 0, "", "Participants:");
                 break;
             case MUC_ROLE_VISITOR:
-                win_save_print(window, '!', NULL, 0, 0, "", "Visitors:");
+                win_print(window, '!', NULL, 0, 0, "", "Visitors:");
                 break;
             default:
                 break;
@@ -2435,16 +2447,16 @@ ui_show_room_role_list(ProfMucWin *mucwin, muc_role_t role)
             Occupant *occupant = curr_occupant->data;
             if (occupant->role == role) {
                 if (occupant->jid) {
-                    win_save_vprint(window, '!', NULL, 0, 0, "", "  %s (%s)", occupant->nick, occupant->jid);
+                    win_vprint(window, '!', NULL, 0, 0, "", "  %s (%s)", occupant->nick, occupant->jid);
                 } else {
-                    win_save_vprint(window, '!', NULL, 0, 0, "", "  %s", occupant->nick);
+                    win_vprint(window, '!', NULL, 0, 0, "", "  %s", occupant->nick);
                 }
             }
 
             curr_occupant = g_slist_next(curr_occupant);
         }
 
-        win_save_print(window, '-', NULL, 0, 0, "", "");
+        win_print(window, '-', NULL, 0, 0, "", "");
     }
 }
 
@@ -2457,34 +2469,34 @@ ui_show_room_affiliation_list(ProfMucWin *mucwin, muc_affiliation_t affiliation)
     if (!occupants) {
         switch (affiliation) {
             case MUC_AFFILIATION_OWNER:
-                win_save_print(window, '!', NULL, 0, 0, "", "No owners found.");
+                win_print(window, '!', NULL, 0, 0, "", "No owners found.");
                 break;
             case MUC_AFFILIATION_ADMIN:
-                win_save_print(window, '!', NULL, 0, 0, "", "No admins found.");
+                win_print(window, '!', NULL, 0, 0, "", "No admins found.");
                 break;
             case MUC_AFFILIATION_MEMBER:
-                win_save_print(window, '!', NULL, 0, 0, "", "No members found.");
+                win_print(window, '!', NULL, 0, 0, "", "No members found.");
                 break;
             case MUC_AFFILIATION_OUTCAST:
-                win_save_print(window, '!', NULL, 0, 0, "", "No outcasts found.");
+                win_print(window, '!', NULL, 0, 0, "", "No outcasts found.");
                 break;
             default:
                 break;
         }
-        win_save_print(window, '-', NULL, 0, 0, "", "");
+        win_print(window, '-', NULL, 0, 0, "", "");
     } else {
         switch (affiliation) {
             case MUC_AFFILIATION_OWNER:
-                win_save_print(window, '!', NULL, 0, 0, "", "Owners:");
+                win_print(window, '!', NULL, 0, 0, "", "Owners:");
                 break;
             case MUC_AFFILIATION_ADMIN:
-                win_save_print(window, '!', NULL, 0, 0, "", "Admins:");
+                win_print(window, '!', NULL, 0, 0, "", "Admins:");
                 break;
             case MUC_AFFILIATION_MEMBER:
-                win_save_print(window, '!', NULL, 0, 0, "", "Members:");
+                win_print(window, '!', NULL, 0, 0, "", "Members:");
                 break;
             case MUC_AFFILIATION_OUTCAST:
-                win_save_print(window, '!', NULL, 0, 0, "", "Outcasts:");
+                win_print(window, '!', NULL, 0, 0, "", "Outcasts:");
                 break;
             default:
                 break;
@@ -2495,28 +2507,28 @@ ui_show_room_affiliation_list(ProfMucWin *mucwin, muc_affiliation_t affiliation)
             Occupant *occupant = curr_occupant->data;
             if (occupant->affiliation == affiliation) {
                 if (occupant->jid) {
-                    win_save_vprint(window, '!', NULL, 0, 0, "", "  %s (%s)", occupant->nick, occupant->jid);
+                    win_vprint(window, '!', NULL, 0, 0, "", "  %s (%s)", occupant->nick, occupant->jid);
                 } else {
-                    win_save_vprint(window, '!', NULL, 0, 0, "", "  %s", occupant->nick);
+                    win_vprint(window, '!', NULL, 0, 0, "", "  %s", occupant->nick);
                 }
             }
 
             curr_occupant = g_slist_next(curr_occupant);
         }
 
-        win_save_print(window, '-', NULL, 0, 0, "", "");
+        win_print(window, '-', NULL, 0, 0, "", "");
     }
 }
 
 static void
 _ui_handle_form_field(ProfWin *window, char *tag, FormField *field)
 {
-    win_save_vprint(window, '-', NULL, NO_EOL, THEME_AWAY, "", "[%s] ", tag);
-    win_save_vprint(window, '-', NULL, NO_EOL | NO_DATE, 0, "", "%s", field->label);
+    win_vprint(window, '-', NULL, NO_EOL, THEME_AWAY, "", "[%s] ", tag);
+    win_vprint(window, '-', NULL, NO_EOL | NO_DATE, 0, "", "%s", field->label);
     if (field->required) {
-        win_save_print(window, '-', NULL, NO_DATE | NO_EOL, 0, "", " (required): ");
+        win_print(window, '-', NULL, NO_DATE | NO_EOL, 0, "", " (required): ");
     } else {
-        win_save_print(window, '-', NULL, NO_DATE | NO_EOL, 0, "", ": ");
+        win_print(window, '-', NULL, NO_DATE | NO_EOL, 0, "", ": ");
     }
 
     GSList *values = field->values;
@@ -2530,63 +2542,63 @@ _ui_handle_form_field(ProfWin *window, char *tag, FormField *field)
             char *value = curr_value->data;
             if (value != NULL) {
                 if (g_strcmp0(field->var, "muc#roomconfig_roomsecret") == 0) {
-                    win_save_print(window, '-', NULL, NO_DATE | NO_EOL, THEME_ONLINE, "", "[hidden]");
+                    win_print(window, '-', NULL, NO_DATE | NO_EOL, THEME_ONLINE, "", "[hidden]");
                 } else {
-                    win_save_print(window, '-', NULL, NO_DATE | NO_EOL, THEME_ONLINE, "", value);
+                    win_print(window, '-', NULL, NO_DATE | NO_EOL, THEME_ONLINE, "", value);
                 }
             }
         }
-        win_save_newline(window);
+        win_newline(window);
         break;
     case FIELD_TEXT_PRIVATE:
         if (curr_value != NULL) {
             char *value = curr_value->data;
             if (value != NULL) {
-                win_save_print(window, '-', NULL, NO_DATE | NO_EOL, THEME_ONLINE, "", "[hidden]");
+                win_print(window, '-', NULL, NO_DATE | NO_EOL, THEME_ONLINE, "", "[hidden]");
             }
         }
-        win_save_newline(window);
+        win_newline(window);
         break;
     case FIELD_TEXT_MULTI:
-        win_save_newline(window);
+        win_newline(window);
         int index = 1;
         while (curr_value != NULL) {
             char *value = curr_value->data;
             GString *val_tag = g_string_new("");
             g_string_printf(val_tag, "val%d", index++);
-            win_save_vprint(window, '-', NULL, 0, THEME_ONLINE, "", "  [%s] %s", val_tag->str, value);
+            win_vprint(window, '-', NULL, 0, THEME_ONLINE, "", "  [%s] %s", val_tag->str, value);
             g_string_free(val_tag, TRUE);
             curr_value = g_slist_next(curr_value);
         }
         break;
     case FIELD_BOOLEAN:
         if (curr_value == NULL) {
-            win_save_print(window, '-', NULL, NO_DATE, THEME_OFFLINE, "", "FALSE");
+            win_print(window, '-', NULL, NO_DATE, THEME_OFFLINE, "", "FALSE");
         } else {
             char *value = curr_value->data;
             if (value == NULL) {
-                win_save_print(window, '-', NULL, NO_DATE, THEME_OFFLINE, "", "FALSE");
+                win_print(window, '-', NULL, NO_DATE, THEME_OFFLINE, "", "FALSE");
             } else {
                 if (g_strcmp0(value, "0") == 0) {
-                    win_save_print(window, '-', NULL, NO_DATE, THEME_OFFLINE, "", "FALSE");
+                    win_print(window, '-', NULL, NO_DATE, THEME_OFFLINE, "", "FALSE");
                 } else {
-                    win_save_print(window, '-', NULL, NO_DATE, THEME_ONLINE, "", "TRUE");
+                    win_print(window, '-', NULL, NO_DATE, THEME_ONLINE, "", "TRUE");
                 }
             }
         }
         break;
     case FIELD_LIST_SINGLE:
         if (curr_value != NULL) {
-            win_save_newline(window);
+            win_newline(window);
             char *value = curr_value->data;
             GSList *options = field->options;
             GSList *curr_option = options;
             while (curr_option != NULL) {
                 FormOption *option = curr_option->data;
                 if (g_strcmp0(option->value, value) == 0) {
-                    win_save_vprint(window, '-', NULL, 0, THEME_ONLINE, "", "  [%s] %s", option->value, option->label);
+                    win_vprint(window, '-', NULL, 0, THEME_ONLINE, "", "  [%s] %s", option->value, option->label);
                 } else {
-                    win_save_vprint(window, '-', NULL, 0, THEME_OFFLINE, "", "  [%s] %s", option->value, option->label);
+                    win_vprint(window, '-', NULL, 0, THEME_OFFLINE, "", "  [%s] %s", option->value, option->label);
                 }
                 curr_option = g_slist_next(curr_option);
             }
@@ -2594,15 +2606,15 @@ _ui_handle_form_field(ProfWin *window, char *tag, FormField *field)
         break;
     case FIELD_LIST_MULTI:
         if (curr_value != NULL) {
-            win_save_newline(window);
+            win_newline(window);
             GSList *options = field->options;
             GSList *curr_option = options;
             while (curr_option != NULL) {
                 FormOption *option = curr_option->data;
                 if (g_slist_find_custom(curr_value, option->value, (GCompareFunc)g_strcmp0) != NULL) {
-                    win_save_vprint(window, '-', NULL, 0, THEME_ONLINE, "", "  [%s] %s", option->value, option->label);
+                    win_vprint(window, '-', NULL, 0, THEME_ONLINE, "", "  [%s] %s", option->value, option->label);
                 } else {
-                    win_save_vprint(window, '-', NULL, 0, THEME_OFFLINE, "", "  [%s] %s", option->value, option->label);
+                    win_vprint(window, '-', NULL, 0, THEME_OFFLINE, "", "  [%s] %s", option->value, option->label);
                 }
                 curr_option = g_slist_next(curr_option);
             }
@@ -2612,16 +2624,16 @@ _ui_handle_form_field(ProfWin *window, char *tag, FormField *field)
         if (curr_value != NULL) {
             char *value = curr_value->data;
             if (value != NULL) {
-                win_save_print(window, '-', NULL, NO_DATE | NO_EOL, THEME_ONLINE, "", value);
+                win_print(window, '-', NULL, NO_DATE | NO_EOL, THEME_ONLINE, "", value);
             }
         }
-        win_save_newline(window);
+        win_newline(window);
         break;
     case FIELD_JID_MULTI:
-        win_save_newline(window);
+        win_newline(window);
         while (curr_value != NULL) {
             char *value = curr_value->data;
-            win_save_vprint(window, '-', NULL, 0, THEME_ONLINE, "", "  %s", value);
+            win_vprint(window, '-', NULL, 0, THEME_ONLINE, "", "  %s", value);
             curr_value = g_slist_next(curr_value);
         }
         break;
@@ -2629,10 +2641,10 @@ _ui_handle_form_field(ProfWin *window, char *tag, FormField *field)
         if (curr_value != NULL) {
             char *value = curr_value->data;
             if (value != NULL) {
-                win_save_print(window, '-', NULL, NO_DATE | NO_EOL, 0, "", value);
+                win_print(window, '-', NULL, NO_DATE | NO_EOL, 0, "", value);
             }
         }
-        win_save_newline(window);
+        win_newline(window);
         break;
     default:
         break;
@@ -2644,12 +2656,12 @@ ui_show_form(ProfMucConfWin *confwin)
 {
     ProfWin *window = (ProfWin*) confwin;
     if (confwin->form->title != NULL) {
-        win_save_print(window, '-', NULL, NO_EOL, 0, "", "Form title: ");
-        win_save_print(window, '-', NULL, NO_DATE, 0, "", confwin->form->title);
+        win_print(window, '-', NULL, NO_EOL, 0, "", "Form title: ");
+        win_print(window, '-', NULL, NO_DATE, 0, "", confwin->form->title);
     } else {
-        win_save_vprint(window, '-', NULL, 0, 0, "", "Configuration for room %s.", confwin->roomjid);
+        win_vprint(window, '-', NULL, 0, 0, "", "Configuration for room %s.", confwin->roomjid);
     }
-    win_save_print(window, '-', NULL, 0, 0, "", "");
+    win_print(window, '-', NULL, 0, 0, "", "");
 
     ui_show_form_help(confwin);
 
@@ -2661,7 +2673,7 @@ ui_show_form(ProfMucConfWin *confwin)
         if ((g_strcmp0(field->type, "fixed") == 0) && field->values) {
             if (field->values) {
                 char *value = field->values->data;
-                win_save_print(window, '-', NULL, 0, 0, "", value);
+                win_print(window, '-', NULL, 0, 0, "", value);
             }
         } else if (g_strcmp0(field->type, "hidden") != 0 && field->var) {
             char *tag = g_hash_table_lookup(confwin->form->var_to_tag, field->var);
@@ -2677,7 +2689,7 @@ ui_show_form_field(ProfWin *window, DataForm *form, char *tag)
 {
     FormField *field = form_get_field_by_tag(form, tag);
     _ui_handle_form_field(window, tag, field);
-    win_save_println(window, "");
+    win_println(window, "");
 }
 
 void
@@ -2692,11 +2704,11 @@ ui_handle_room_configuration(const char * const roomjid, DataForm *form)
 
     ui_show_form(confwin);
 
-    win_save_print(window, '-', NULL, 0, 0, "", "");
-    win_save_print(window, '-', NULL, 0, 0, "", "Use '/form submit' to save changes.");
-    win_save_print(window, '-', NULL, 0, 0, "", "Use '/form cancel' to cancel changes.");
-    win_save_print(window, '-', NULL, 0, 0, "", "See '/form help' for more information.");
-    win_save_print(window, '-', NULL, 0, 0, "", "");
+    win_print(window, '-', NULL, 0, 0, "", "");
+    win_print(window, '-', NULL, 0, 0, "", "Use '/form submit' to save changes.");
+    win_print(window, '-', NULL, 0, 0, "", "Use '/form cancel' to cancel changes.");
+    win_print(window, '-', NULL, 0, 0, "", "See '/form help' for more information.");
+    win_print(window, '-', NULL, 0, 0, "", "");
 }
 
 void
@@ -2718,7 +2730,7 @@ ui_handle_room_configuration_form_error(const char * const roomjid, const char *
         g_string_append(message_str, message);
     }
 
-    win_save_print(window, '-', NULL, 0, THEME_ERROR, "", message_str->str);
+    win_print(window, '-', NULL, 0, THEME_ERROR, "", message_str->str);
 
     g_string_free(message_str, TRUE);
 }
@@ -2746,7 +2758,7 @@ ui_handle_room_config_submit_result(const char * const roomjid)
         if (muc_window) {
             int num = wins_get_num(muc_window);
             ui_switch_win(num);
-            win_save_print(muc_window, '!', NULL, 0, THEME_ROOMINFO, "", "Room configuration successful");
+            win_print(muc_window, '!', NULL, 0, THEME_ROOMINFO, "", "Room configuration successful");
         } else {
             ui_switch_win(1);
             cons_show("Room configuration successful: %s", roomjid);
@@ -2773,25 +2785,25 @@ ui_handle_room_config_submit_result_error(const char * const roomjid, const char
 
         if (form_window) {
             if (message) {
-                win_save_vprint(form_window, '!', NULL, 0, THEME_ERROR, "", "Configuration error: %s", message);
+                win_vprint(form_window, '!', NULL, 0, THEME_ERROR, "", "Configuration error: %s", message);
             } else {
-                win_save_print(form_window, '!', NULL, 0, THEME_ERROR, "", "Configuration error");
+                win_print(form_window, '!', NULL, 0, THEME_ERROR, "", "Configuration error");
             }
         } else if (muc_window) {
             if (message) {
-                win_save_vprint(muc_window, '!', NULL, 0, THEME_ERROR, "", "Configuration error: %s", message);
+                win_vprint(muc_window, '!', NULL, 0, THEME_ERROR, "", "Configuration error: %s", message);
             } else {
-                win_save_print(muc_window, '!', NULL, 0, THEME_ERROR, "", "Configuration error");
+                win_print(muc_window, '!', NULL, 0, THEME_ERROR, "", "Configuration error");
             }
         } else {
             if (message) {
-                win_save_vprint(console, '!', NULL, 0, THEME_ERROR, "", "Configuration error for %s: %s", roomjid, message);
+                win_vprint(console, '!', NULL, 0, THEME_ERROR, "", "Configuration error for %s: %s", roomjid, message);
             } else {
-                win_save_vprint(console, '!', NULL, 0, THEME_ERROR, "", "Configuration error for %s", roomjid);
+                win_vprint(console, '!', NULL, 0, THEME_ERROR, "", "Configuration error for %s", roomjid);
             }
         }
     } else {
-        win_save_print(console, '!', NULL, 0, THEME_ERROR, "", "Configuration error");
+        win_print(console, '!', NULL, 0, THEME_ERROR, "", "Configuration error");
     }
 }
 
@@ -2801,16 +2813,16 @@ ui_show_form_field_help(ProfMucConfWin *confwin, char *tag)
     ProfWin *window = (ProfWin*) confwin;
     FormField *field = form_get_field_by_tag(confwin->form, tag);
     if (field != NULL) {
-        win_save_print(window, '-', NULL, NO_EOL, 0, "", field->label);
+        win_print(window, '-', NULL, NO_EOL, 0, "", field->label);
         if (field->required) {
-            win_save_print(window, '-', NULL, NO_DATE, 0, "", " (Required):");
+            win_print(window, '-', NULL, NO_DATE, 0, "", " (Required):");
         } else {
-            win_save_print(window, '-', NULL, NO_DATE, 0, "", ":");
+            win_print(window, '-', NULL, NO_DATE, 0, "", ":");
         }
         if (field->description != NULL) {
-            win_save_vprint(window, '-', NULL, 0, 0, "", "  Description : %s", field->description);
+            win_vprint(window, '-', NULL, 0, 0, "", "  Description : %s", field->description);
         }
-        win_save_vprint(window, '-', NULL, 0, 0, "", "  Type        : %s", field->type);
+        win_vprint(window, '-', NULL, 0, 0, "", "  Type        : %s", field->type);
 
         int num_values = 0;
         GSList *curr_option = NULL;
@@ -2819,51 +2831,51 @@ ui_show_form_field_help(ProfMucConfWin *confwin, char *tag)
         switch (field->type_t) {
         case FIELD_TEXT_SINGLE:
         case FIELD_TEXT_PRIVATE:
-            win_save_vprint(window, '-', NULL, 0, 0, "", "  Set         : /%s <value>", tag);
-            win_save_print(window, '-', NULL, 0, 0, "", "  Where       : <value> is any text");
+            win_vprint(window, '-', NULL, 0, 0, "", "  Set         : /%s <value>", tag);
+            win_print(window, '-', NULL, 0, 0, "", "  Where       : <value> is any text");
             break;
         case FIELD_TEXT_MULTI:
             num_values = form_get_value_count(confwin->form, tag);
-            win_save_vprint(window, '-', NULL, 0, 0, "", "  Add         : /%s add <value>", tag);
-            win_save_print(window, '-', NULL, 0, 0, "", "  Where       : <value> is any text");
+            win_vprint(window, '-', NULL, 0, 0, "", "  Add         : /%s add <value>", tag);
+            win_print(window, '-', NULL, 0, 0, "", "  Where       : <value> is any text");
             if (num_values > 0) {
-                win_save_vprint(window, '-', NULL, 0, 0, "", "  Remove      : /%s remove <value>", tag);
-                win_save_vprint(window, '-', NULL, 0, 0, "", "  Where       : <value> between 'val1' and 'val%d'", num_values);
+                win_vprint(window, '-', NULL, 0, 0, "", "  Remove      : /%s remove <value>", tag);
+                win_vprint(window, '-', NULL, 0, 0, "", "  Where       : <value> between 'val1' and 'val%d'", num_values);
             }
             break;
         case FIELD_BOOLEAN:
-            win_save_vprint(window, '-', NULL, 0, 0, "", "  Set         : /%s <value>", tag);
-            win_save_print(window, '-', NULL, 0, 0, "", "  Where       : <value> is either 'on' or 'off'");
+            win_vprint(window, '-', NULL, 0, 0, "", "  Set         : /%s <value>", tag);
+            win_print(window, '-', NULL, 0, 0, "", "  Where       : <value> is either 'on' or 'off'");
             break;
         case FIELD_LIST_SINGLE:
-            win_save_vprint(window, '-', NULL, 0, 0, "", "  Set         : /%s <value>", tag);
-            win_save_print(window, '-', NULL, 0, 0, "", "  Where       : <value> is one of");
+            win_vprint(window, '-', NULL, 0, 0, "", "  Set         : /%s <value>", tag);
+            win_print(window, '-', NULL, 0, 0, "", "  Where       : <value> is one of");
             curr_option = field->options;
             while (curr_option != NULL) {
                 option = curr_option->data;
-                win_save_vprint(window, '-', NULL, 0, 0, "", "                  %s", option->value);
+                win_vprint(window, '-', NULL, 0, 0, "", "                  %s", option->value);
                 curr_option = g_slist_next(curr_option);
             }
             break;
         case FIELD_LIST_MULTI:
-            win_save_vprint(window, '-', NULL, 0, 0, "", "  Add         : /%s add <value>", tag);
-            win_save_vprint(window, '-', NULL, 0, 0, "", "  Remove      : /%s remove <value>", tag);
-            win_save_print(window, '-', NULL, 0, 0, "", "  Where       : <value> is one of");
+            win_vprint(window, '-', NULL, 0, 0, "", "  Add         : /%s add <value>", tag);
+            win_vprint(window, '-', NULL, 0, 0, "", "  Remove      : /%s remove <value>", tag);
+            win_print(window, '-', NULL, 0, 0, "", "  Where       : <value> is one of");
             curr_option = field->options;
             while (curr_option != NULL) {
                 option = curr_option->data;
-                win_save_vprint(window, '-', NULL, 0, 0, "", "                  %s", option->value);
+                win_vprint(window, '-', NULL, 0, 0, "", "                  %s", option->value);
                 curr_option = g_slist_next(curr_option);
             }
             break;
         case FIELD_JID_SINGLE:
-            win_save_vprint(window, '-', NULL, 0, 0, "", "  Set         : /%s <value>", tag);
-            win_save_print(window, '-', NULL, 0, 0, "", "  Where       : <value> is a valid Jabber ID");
+            win_vprint(window, '-', NULL, 0, 0, "", "  Set         : /%s <value>", tag);
+            win_print(window, '-', NULL, 0, 0, "", "  Where       : <value> is a valid Jabber ID");
             break;
         case FIELD_JID_MULTI:
-            win_save_vprint(window, '-', NULL, 0, 0, "", "  Add         : /%s add <value>", tag);
-            win_save_vprint(window, '-', NULL, 0, 0, "", "  Remove      : /%s remove <value>", tag);
-            win_save_print(window, '-', NULL, 0, 0, "", "  Where       : <value> is a valid Jabber ID");
+            win_vprint(window, '-', NULL, 0, 0, "", "  Add         : /%s add <value>", tag);
+            win_vprint(window, '-', NULL, 0, 0, "", "  Remove      : /%s remove <value>", tag);
+            win_print(window, '-', NULL, 0, 0, "", "  Where       : <value> is a valid Jabber ID");
             break;
         case FIELD_FIXED:
         case FIELD_UNKNOWN:
@@ -2872,7 +2884,7 @@ ui_show_form_field_help(ProfMucConfWin *confwin, char *tag)
             break;
         }
     } else {
-        win_save_vprint(window, '-', NULL, 0, 0, "", "No such field %s", tag);
+        win_vprint(window, '-', NULL, 0, 0, "", "No such field %s", tag);
     }
 }
 
@@ -2881,9 +2893,9 @@ ui_show_form_help(ProfMucConfWin *confwin)
 {
     if (confwin->form->instructions != NULL) {
         ProfWin *window = (ProfWin*) confwin;
-        win_save_print(window, '-', NULL, 0, 0, "", "Supplied instructions:");
-        win_save_print(window, '-', NULL, 0, 0, "", confwin->form->instructions);
-        win_save_print(window, '-', NULL, 0, 0, "", "");
+        win_print(window, '-', NULL, 0, 0, "", "Supplied instructions:");
+        win_print(window, '-', NULL, 0, 0, "", confwin->form->instructions);
+        win_print(window, '-', NULL, 0, 0, "", "");
     }
 }
 
@@ -2893,7 +2905,7 @@ ui_show_lines(ProfWin *window, const gchar** lines)
     if (lines != NULL) {
         int i;
         for (i = 0; lines[i] != NULL; i++) {
-            win_save_print(window, '-', NULL, 0, 0, "", lines[i]);
+            win_print(window, '-', NULL, 0, 0, "", lines[i]);
         }
     }
 }
@@ -2984,11 +2996,11 @@ _win_show_history(int win_index, const char * const contact)
                     GDateTime *time = g_date_time_new_local(2000, 1, 1, ihh, imm, iss);
                     GTimeVal tv;
                     g_date_time_to_timeval(time, &tv);
-                    win_save_print(window, '-', &tv, NO_COLOUR_DATE, 0, "", curr->data+11);
+                    win_print(window, '-', &tv, NO_COLOUR_DATE, 0, "", curr->data+11);
                     g_date_time_unref(time);
                 // header
                 } else {
-                    win_save_print(window, '-', NULL, 0, 0, "", curr->data);
+                    win_print(window, '-', NULL, 0, 0, "", curr->data);
                 }
                 curr = g_slist_next(curr);
             }
diff --git a/src/ui/ui.h b/src/ui/ui.h
index a0408503..9c8b9b4d 100644
--- a/src/ui/ui.h
+++ b/src/ui/ui.h
@@ -116,14 +116,13 @@ void ui_handle_stanza(const char * const msg);
 void ui_contact_typing(const char * const barejid, const char * const resource);
 void ui_incoming_msg(const char * const from, const char * const resource,  const char * const message, GTimeVal *tv_stamp);
 void ui_incoming_private_msg(const char * const fulljid, const char * const message, GTimeVal *tv_stamp);
+void ui_message_receipt(const char * const barejid, const char * const id);
 
 void ui_disconnected(void);
 void ui_recipient_gone(const char * const barejid, const char * const resource);
 
-void ui_outgoing_chat_msg(const char * const from, const char * const barejid,
-    const char * const message);
-void ui_outgoing_private_msg(const char * const from, const char * const fulljid,
-    const char * const message);
+void ui_outgoing_chat_msg(const char * const barejid, const char * const message, char *id);
+void ui_outgoing_private_msg(const char * const fulljid, const char * const message);
 
 void ui_room_join(const char * const roomjid, gboolean focus);
 void ui_switch_to_room(const char * const roomjid);
diff --git a/src/ui/window.c b/src/ui/window.c
index 96bfd546..81408403 100644
--- a/src/ui/window.c
+++ b/src/ui/window.c
@@ -59,7 +59,7 @@
 #define CEILING(X) (X-(int)(X) > 0 ? (int)(X+1) : (int)(X))
 
 static void _win_print(ProfWin *window, const char show_char, GDateTime *time,
-    int flags, theme_item_t theme_item, const char * const from, const char * const message);
+    int flags, theme_item_t theme_item, const char * const from, const char * const message, DeliveryReceipt *receipt);
 static void _win_print_wrapped(WINDOW *win, const char * const message);
 
 int
@@ -547,14 +547,14 @@ win_show_occupant(ProfWin *window, Occupant *occupant)
 
     theme_item_t presence_colour = theme_main_presence_attrs(presence_str);
 
-    win_save_print(window, '-', NULL, NO_EOL, presence_colour, "", occupant->nick);
-    win_save_vprint(window, '-', NULL, NO_DATE | NO_EOL, presence_colour, "", " is %s", presence_str);
+    win_print(window, '-', NULL, NO_EOL, presence_colour, "", occupant->nick);
+    win_vprint(window, '-', NULL, NO_DATE | NO_EOL, presence_colour, "", " is %s", presence_str);
 
     if (occupant->status) {
-        win_save_vprint(window, '-', NULL, NO_DATE | NO_EOL, presence_colour, "", ", \"%s\"", occupant->status);
+        win_vprint(window, '-', NULL, NO_DATE | NO_EOL, presence_colour, "", ", \"%s\"", occupant->status);
     }
 
-    win_save_print(window, '-', NULL, NO_DATE, presence_colour, "", "");
+    win_print(window, '-', NULL, NO_DATE, presence_colour, "", "");
 }
 
 void
@@ -569,12 +569,12 @@ win_show_contact(ProfWin *window, PContact contact)
     theme_item_t presence_colour = theme_main_presence_attrs(presence);
 
     if (name != NULL) {
-        win_save_print(window, '-', NULL, NO_EOL, presence_colour, "", name);
+        win_print(window, '-', NULL, NO_EOL, presence_colour, "", name);
     } else {
-        win_save_print(window, '-', NULL, NO_EOL, presence_colour, "", barejid);
+        win_print(window, '-', NULL, NO_EOL, presence_colour, "", barejid);
     }
 
-    win_save_vprint(window, '-', NULL, NO_DATE | NO_EOL, presence_colour, "", " is %s", presence);
+    win_vprint(window, '-', NULL, NO_DATE | NO_EOL, presence_colour, "", " is %s", presence);
 
     if (last_activity != NULL) {
         GDateTime *now = g_date_time_new_now_local();
@@ -587,18 +587,18 @@ win_show_contact(ProfWin *window, PContact contact)
         int seconds = span / G_TIME_SPAN_SECOND;
 
         if (hours > 0) {
-          win_save_vprint(window, '-', NULL, NO_DATE | NO_EOL, presence_colour, "", ", idle %dh%dm%ds", hours, minutes, seconds);
+          win_vprint(window, '-', NULL, NO_DATE | NO_EOL, presence_colour, "", ", idle %dh%dm%ds", hours, minutes, seconds);
         }
         else {
-          win_save_vprint(window, '-', NULL, NO_DATE | NO_EOL, presence_colour, "", ", idle %dm%ds", minutes, seconds);
+          win_vprint(window, '-', NULL, NO_DATE | NO_EOL, presence_colour, "", ", idle %dm%ds", minutes, seconds);
         }
     }
 
     if (status != NULL) {
-        win_save_vprint(window, '-', NULL, NO_DATE | NO_EOL, presence_colour, "", ", \"%s\"", p_contact_status(contact));
+        win_vprint(window, '-', NULL, NO_DATE | NO_EOL, presence_colour, "", ", \"%s\"", p_contact_status(contact));
     }
 
-    win_save_print(window, '-', NULL, NO_DATE, presence_colour, "", "");
+    win_print(window, '-', NULL, NO_DATE, presence_colour, "", "");
 }
 
 void
@@ -610,21 +610,21 @@ win_show_occupant_info(ProfWin *window, const char * const room, Occupant *occup
 
     theme_item_t presence_colour = theme_main_presence_attrs(presence_str);
 
-    win_save_print(window, '!', NULL, NO_EOL, presence_colour, "", occupant->nick);
-    win_save_vprint(window, '!', NULL, NO_DATE | NO_EOL, presence_colour, "", " is %s", presence_str);
+    win_print(window, '!', NULL, NO_EOL, presence_colour, "", occupant->nick);
+    win_vprint(window, '!', NULL, NO_DATE | NO_EOL, presence_colour, "", " is %s", presence_str);
 
     if (occupant->status) {
-        win_save_vprint(window, '!', NULL, NO_DATE | NO_EOL, presence_colour, "", ", \"%s\"", occupant->status);
+        win_vprint(window, '!', NULL, NO_DATE | NO_EOL, presence_colour, "", ", \"%s\"", occupant->status);
     }
 
-    win_save_newline(window);
+    win_newline(window);
 
     if (occupant->jid) {
-        win_save_vprint(window, '!', NULL, 0, 0, "", "  Jid: %s", occupant->jid);
+        win_vprint(window, '!', NULL, 0, 0, "", "  Jid: %s", occupant->jid);
     }
 
-    win_save_vprint(window, '!', NULL, 0, 0, "", "  Affiliation: %s", occupant_affiliation);
-    win_save_vprint(window, '!', NULL, 0, 0, "", "  Role: %s", occupant_role);
+    win_vprint(window, '!', NULL, 0, 0, "", "  Affiliation: %s", occupant_affiliation);
+    win_vprint(window, '!', NULL, 0, 0, "", "  Role: %s", occupant_role);
 
     Jid *jidp = jid_create_from_bare_and_resource(room, occupant->nick);
     Capabilities *caps = caps_lookup(jidp->fulljid);
@@ -633,46 +633,46 @@ win_show_occupant_info(ProfWin *window, const char * const room, Occupant *occup
     if (caps) {
         // show identity
         if ((caps->category != NULL) || (caps->type != NULL) || (caps->name != NULL)) {
-            win_save_print(window, '!', NULL, NO_EOL, 0, "", "  Identity: ");
+            win_print(window, '!', NULL, NO_EOL, 0, "", "  Identity: ");
             if (caps->name != NULL) {
-                win_save_print(window, '!', NULL, NO_DATE | NO_EOL, 0, "", caps->name);
+                win_print(window, '!', NULL, NO_DATE | NO_EOL, 0, "", caps->name);
                 if ((caps->category != NULL) || (caps->type != NULL)) {
-                    win_save_print(window, '-', NULL, NO_DATE | NO_EOL, 0, "", " ");
+                    win_print(window, '-', NULL, NO_DATE | NO_EOL, 0, "", " ");
                 }
             }
             if (caps->type != NULL) {
-                win_save_print(window, '!', NULL, NO_DATE | NO_EOL, 0, "", caps->type);
+                win_print(window, '!', NULL, NO_DATE | NO_EOL, 0, "", caps->type);
                 if (caps->category != NULL) {
-                    win_save_print(window, '!', NULL, NO_DATE | NO_EOL, 0, "", " ");
+                    win_print(window, '!', NULL, NO_DATE | NO_EOL, 0, "", " ");
                 }
             }
             if (caps->category != NULL) {
-                win_save_print(window, '!', NULL, NO_DATE | NO_EOL, 0, "", caps->category);
+                win_print(window, '!', NULL, NO_DATE | NO_EOL, 0, "", caps->category);
             }
-            win_save_newline(window);
+            win_newline(window);
         }
         if (caps->software != NULL) {
-            win_save_vprint(window, '!', NULL, NO_EOL, 0, "", "  Software: %s", caps->software);
+            win_vprint(window, '!', NULL, NO_EOL, 0, "", "  Software: %s", caps->software);
         }
         if (caps->software_version != NULL) {
-            win_save_vprint(window, '!', NULL, NO_DATE | NO_EOL, 0, "", ", %s", caps->software_version);
+            win_vprint(window, '!', NULL, NO_DATE | NO_EOL, 0, "", ", %s", caps->software_version);
         }
         if ((caps->software != NULL) || (caps->software_version != NULL)) {
-            win_save_newline(window);
+            win_newline(window);
         }
         if (caps->os != NULL) {
-            win_save_vprint(window, '!', NULL, NO_EOL, 0, "", "  OS: %s", caps->os);
+            win_vprint(window, '!', NULL, NO_EOL, 0, "", "  OS: %s", caps->os);
         }
         if (caps->os_version != NULL) {
-            win_save_vprint(window, '!', NULL, NO_DATE | NO_EOL, 0, "", ", %s", caps->os_version);
+            win_vprint(window, '!', NULL, NO_DATE | NO_EOL, 0, "", ", %s", caps->os_version);
         }
         if ((caps->os != NULL) || (caps->os_version != NULL)) {
-            win_save_newline(window);
+            win_newline(window);
         }
         caps_destroy(caps);
     }
 
-    win_save_print(window, '-', NULL, 0, 0, "", "");
+    win_print(window, '-', NULL, 0, 0, "", "");
 }
 
 void
@@ -686,15 +686,15 @@ win_show_info(ProfWin *window, PContact contact)
 
     theme_item_t presence_colour = theme_main_presence_attrs(presence);
 
-    win_save_print(window, '-', NULL, 0, 0, "", "");
-    win_save_print(window, '-', NULL, NO_EOL, presence_colour, "", barejid);
+    win_print(window, '-', NULL, 0, 0, "", "");
+    win_print(window, '-', NULL, NO_EOL, presence_colour, "", barejid);
     if (name != NULL) {
-        win_save_vprint(window, '-', NULL, NO_DATE | NO_EOL, presence_colour, "", " (%s)", name);
+        win_vprint(window, '-', NULL, NO_DATE | NO_EOL, presence_colour, "", " (%s)", name);
     }
-    win_save_print(window, '-', NULL, NO_DATE, 0, "", ":");
+    win_print(window, '-', NULL, NO_DATE, 0, "", ":");
 
     if (sub != NULL) {
-        win_save_vprint(window, '-', NULL, 0, 0, "", "Subscription: %s", sub);
+        win_vprint(window, '-', NULL, 0, 0, "", "Subscription: %s", sub);
     }
 
     if (last_activity != NULL) {
@@ -708,10 +708,10 @@ win_show_info(ProfWin *window, PContact contact)
         int seconds = span / G_TIME_SPAN_SECOND;
 
         if (hours > 0) {
-          win_save_vprint(window, '-', NULL, 0, 0, "", "Last activity: %dh%dm%ds", hours, minutes, seconds);
+          win_vprint(window, '-', NULL, 0, 0, "", "Last activity: %dh%dm%ds", hours, minutes, seconds);
         }
         else {
-          win_save_vprint(window, '-', NULL, 0, 0, "", "Last activity: %dm%ds", minutes, seconds);
+          win_vprint(window, '-', NULL, 0, 0, "", "Last activity: %dm%ds", minutes, seconds);
         }
 
         g_date_time_unref(now);
@@ -720,7 +720,7 @@ win_show_info(ProfWin *window, PContact contact)
     GList *resources = p_contact_get_available_resources(contact);
     GList *ordered_resources = NULL;
     if (resources != NULL) {
-        win_save_print(window, '-', NULL, 0, 0, "", "Resources:");
+        win_print(window, '-', NULL, 0, 0, "", "Resources:");
 
         // sort in order of availability
         GList *curr = resources;
@@ -738,11 +738,11 @@ win_show_info(ProfWin *window, PContact contact)
         Resource *resource = curr->data;
         const char *resource_presence = string_from_resource_presence(resource->presence);
         theme_item_t presence_colour = theme_main_presence_attrs(resource_presence);
-        win_save_vprint(window, '-', NULL, NO_EOL, presence_colour, "", "  %s (%d), %s", resource->name, resource->priority, resource_presence);
+        win_vprint(window, '-', NULL, NO_EOL, presence_colour, "", "  %s (%d), %s", resource->name, resource->priority, resource_presence);
         if (resource->status != NULL) {
-            win_save_vprint(window, '-', NULL, NO_DATE | NO_EOL, presence_colour, "", ", \"%s\"", resource->status);
+            win_vprint(window, '-', NULL, NO_DATE | NO_EOL, presence_colour, "", ", \"%s\"", resource->status);
         }
-        win_save_newline(window);
+        win_newline(window);
 
         Jid *jidp = jid_create_from_bare_and_resource(barejid, resource->name);
         Capabilities *caps = caps_lookup(jidp->fulljid);
@@ -751,41 +751,41 @@ win_show_info(ProfWin *window, PContact contact)
         if (caps) {
             // show identity
             if ((caps->category != NULL) || (caps->type != NULL) || (caps->name != NULL)) {
-                win_save_print(window, '-', NULL, NO_EOL, 0, "", "    Identity: ");
+                win_print(window, '-', NULL, NO_EOL, 0, "", "    Identity: ");
                 if (caps->name != NULL) {
-                    win_save_print(window, '-', NULL, NO_DATE | NO_EOL, 0, "", caps->name);
+                    win_print(window, '-', NULL, NO_DATE | NO_EOL, 0, "", caps->name);
                     if ((caps->category != NULL) || (caps->type != NULL)) {
-                        win_save_print(window, '-', NULL, NO_DATE | NO_EOL, 0, "", " ");
+                        win_print(window, '-', NULL, NO_DATE | NO_EOL, 0, "", " ");
                     }
                 }
                 if (caps->type != NULL) {
-                    win_save_print(window, '-', NULL, NO_DATE | NO_EOL, 0, "", caps->type);
+                    win_print(window, '-', NULL, NO_DATE | NO_EOL, 0, "", caps->type);
                     if (caps->category != NULL) {
-                        win_save_print(window, '-', NULL, NO_DATE | NO_EOL, 0, "", " ");
+                        win_print(window, '-', NULL, NO_DATE | NO_EOL, 0, "", " ");
                     }
                 }
                 if (caps->category != NULL) {
-                    win_save_print(window, '-', NULL, NO_DATE | NO_EOL, 0, "", caps->category);
+                    win_print(window, '-', NULL, NO_DATE | NO_EOL, 0, "", caps->category);
                 }
-                win_save_newline(window);
+                win_newline(window);
             }
             if (caps->software != NULL) {
-                win_save_vprint(window, '-', NULL, NO_EOL, 0, "", "    Software: %s", caps->software);
+                win_vprint(window, '-', NULL, NO_EOL, 0, "", "    Software: %s", caps->software);
             }
             if (caps->software_version != NULL) {
-                win_save_vprint(window, '-', NULL, NO_DATE | NO_EOL, 0, "", ", %s", caps->software_version);
+                win_vprint(window, '-', NULL, NO_DATE | NO_EOL, 0, "", ", %s", caps->software_version);
             }
             if ((caps->software != NULL) || (caps->software_version != NULL)) {
-                win_save_newline(window);
+                win_newline(window);
             }
             if (caps->os != NULL) {
-                win_save_vprint(window, '-', NULL, NO_EOL, 0, "", "    OS: %s", caps->os);
+                win_vprint(window, '-', NULL, NO_EOL, 0, "", "    OS: %s", caps->os);
             }
             if (caps->os_version != NULL) {
-                win_save_vprint(window, '-', NULL, NO_DATE | NO_EOL, 0, "", ", %s", caps->os_version);
+                win_vprint(window, '-', NULL, NO_DATE | NO_EOL, 0, "", ", %s", caps->os_version);
             }
             if ((caps->os != NULL) || (caps->os_version != NULL)) {
-                win_save_newline(window);
+                win_newline(window);
             }
             caps_destroy(caps);
         }
@@ -812,12 +812,12 @@ win_show_status_string(ProfWin *window, const char * const from,
     }
 
 
-    win_save_vprint(window, '-', NULL, NO_EOL, presence_colour, "", "%s %s", pre, from);
+    win_vprint(window, '-', NULL, NO_EOL, presence_colour, "", "%s %s", pre, from);
 
     if (show != NULL)
-        win_save_vprint(window, '-', NULL, NO_DATE | NO_EOL, presence_colour, "", " is %s", show);
+        win_vprint(window, '-', NULL, NO_DATE | NO_EOL, presence_colour, "", " is %s", show);
     else
-        win_save_vprint(window, '-', NULL, NO_DATE | NO_EOL, presence_colour, "", " is %s", default_show);
+        win_vprint(window, '-', NULL, NO_DATE | NO_EOL, presence_colour, "", " is %s", default_show);
 
     if (last_activity != NULL) {
         GDateTime *now = g_date_time_new_now_local();
@@ -831,17 +831,17 @@ win_show_status_string(ProfWin *window, const char * const from,
         int seconds = span / G_TIME_SPAN_SECOND;
 
         if (hours > 0) {
-          win_save_vprint(window, '-', NULL, NO_DATE | NO_EOL, presence_colour, "", ", idle %dh%dm%ds", hours, minutes, seconds);
+          win_vprint(window, '-', NULL, NO_DATE | NO_EOL, presence_colour, "", ", idle %dh%dm%ds", hours, minutes, seconds);
         }
         else {
-          win_save_vprint(window, '-', NULL, NO_DATE | NO_EOL, presence_colour, "", ", idle %dm%ds", minutes, seconds);
+          win_vprint(window, '-', NULL, NO_DATE | NO_EOL, presence_colour, "", ", idle %dm%ds", minutes, seconds);
         }
     }
 
     if (status != NULL)
-        win_save_vprint(window, '-', NULL, NO_DATE | NO_EOL, presence_colour, "", ", \"%s\"", status);
+        win_vprint(window, '-', NULL, NO_DATE | NO_EOL, presence_colour, "", ", \"%s\"", status);
 
-    win_save_print(window, '-', NULL, NO_DATE, presence_colour, "", "");
+    win_print(window, '-', NULL, NO_DATE, presence_colour, "", "");
 
 }
 
@@ -853,7 +853,7 @@ win_print_incoming_message(ProfWin *window, GTimeVal *tv_stamp,
     {
         case WIN_CHAT:
         case WIN_PRIVATE:
-            win_save_print(window, '-', tv_stamp, NO_ME, THEME_TEXT_THEM, from, message);
+            win_print(window, '-', tv_stamp, NO_ME, THEME_TEXT_THEM, from, message);
             break;
         default:
             assert(FALSE);
@@ -862,19 +862,19 @@ win_print_incoming_message(ProfWin *window, GTimeVal *tv_stamp,
 }
 
 void
-win_save_vprint(ProfWin *window, const char show_char, GTimeVal *tstamp,
+win_vprint(ProfWin *window, const char show_char, GTimeVal *tstamp,
     int flags, theme_item_t theme_item, const char * const from, const char * const message, ...)
 {
     va_list arg;
     va_start(arg, message);
     GString *fmt_msg = g_string_new(NULL);
     g_string_vprintf(fmt_msg, message, arg);
-    win_save_print(window, show_char, tstamp, flags, theme_item, from, fmt_msg->str);
+    win_print(window, show_char, tstamp, flags, theme_item, from, fmt_msg->str);
     g_string_free(fmt_msg, TRUE);
 }
 
 void
-win_save_print(ProfWin *window, const char show_char, GTimeVal *tstamp,
+win_print(ProfWin *window, const char show_char, GTimeVal *tstamp,
     int flags, theme_item_t theme_item, const char * const from, const char * const message)
 {
     GDateTime *time;
@@ -885,27 +885,59 @@ win_save_print(ProfWin *window, const char show_char, GTimeVal *tstamp,
         time = g_date_time_new_from_timeval_utc(tstamp);
     }
 
-    buffer_push(window->layout->buffer, show_char, time, flags, theme_item, from, message);
-    _win_print(window, show_char, time, flags, theme_item, from, message);
+    buffer_push(window->layout->buffer, show_char, time, flags, theme_item, from, message, NULL);
+    _win_print(window, show_char, time, flags, theme_item, from, message, NULL);
     // TODO: cross-reference.. this should be replaced by a real event-based system
     ui_input_nonblocking(TRUE);
 }
 
 void
-win_save_println(ProfWin *window, const char * const message)
+win_print_with_receipt(ProfWin *window, const char show_char, GTimeVal *tstamp,
+    int flags, theme_item_t theme_item, const char * const from, const char * const message, char *id)
 {
-    win_save_print(window, '-', NULL, 0, 0, "", message);
+    GDateTime *time;
+
+    if (tstamp == NULL) {
+        time = g_date_time_new_now_local();
+    } else {
+        time = g_date_time_new_from_timeval_utc(tstamp);
+    }
+
+    DeliveryReceipt *receipt = malloc(sizeof(struct delivery_receipt_t));
+    receipt->id = strdup(id);
+    receipt->received = FALSE;
+    free(id);
+
+    buffer_push(window->layout->buffer, show_char, time, flags, theme_item, from, message, receipt);
+    _win_print(window, show_char, time, flags, theme_item, from, message, receipt);
+    // TODO: cross-reference.. this should be replaced by a real event-based system
+    ui_input_nonblocking(TRUE);
+}
+
+void
+win_mark_received(ProfWin *window, const char * const id)
+{
+    gboolean received = buffer_mark_received(window->layout->buffer, id);
+    if (received) {
+        win_redraw(window);
+    }
+}
+
+void
+win_println(ProfWin *window, const char * const message)
+{
+    win_print(window, '-', NULL, 0, 0, "", message);
 }
 
 void
-win_save_newline(ProfWin *window)
+win_newline(ProfWin *window)
 {
-    win_save_print(window, '-', NULL, NO_DATE, 0, "", "");
+    win_print(window, '-', NULL, NO_DATE, 0, "", "");
 }
 
 static void
 _win_print(ProfWin *window, const char show_char, GDateTime *time,
-    int flags, theme_item_t theme_item, const char * const from, const char * const message)
+    int flags, theme_item_t theme_item, const char * const from, const char * const message, DeliveryReceipt *receipt)
 {
     // flags : 1st bit =  0/1 - me/not me
     //         2nd bit =  0/1 - date/no date
@@ -947,6 +979,10 @@ _win_print(ProfWin *window, const char show_char, GDateTime *time,
             colour = 0;
         }
 
+        if (receipt && !receipt->received) {
+            colour = theme_attrs(THEME_BLACK_BOLD);
+        }
+
         wattron(window->layout->win, colour);
         if (strncmp(message, "/me ", 4) == 0) {
             wprintw(window->layout->win, "*%s ", from);
@@ -959,7 +995,11 @@ _win_print(ProfWin *window, const char show_char, GDateTime *time,
     }
 
     if (!me_message) {
-        wattron(window->layout->win, theme_attrs(theme_item));
+        if (receipt && !receipt->received) {
+            wattron(window->layout->win, theme_attrs(THEME_BLACK_BOLD));
+        } else {
+            wattron(window->layout->win, theme_attrs(theme_item));
+        }
     }
 
     if (prefs_get_boolean(PREF_WRAP)) {
@@ -975,7 +1015,11 @@ _win_print(ProfWin *window, const char show_char, GDateTime *time,
     if (me_message) {
         wattroff(window->layout->win, colour);
     } else {
-        wattroff(window->layout->win, theme_attrs(theme_item));
+        if (receipt && !receipt->received) {
+            wattroff(window->layout->win, theme_attrs(THEME_BLACK_BOLD));
+        } else {
+            wattroff(window->layout->win, theme_attrs(theme_item));
+        }
     }
 }
 
@@ -1074,7 +1118,7 @@ win_redraw(ProfWin *window)
 
     for (i = 0; i < size; i++) {
         ProfBuffEntry *e = buffer_yield_entry(window->layout->buffer, i);
-        _win_print(window, e->show_char, e->time, e->flags, e->theme_item, e->from, e->message);
+        _win_print(window, e->show_char, e->time, e->flags, e->theme_item, e->from, e->message, e->receipt);
     }
 }
 
diff --git a/src/ui/window.h b/src/ui/window.h
index d6e82340..f525e5fa 100644
--- a/src/ui/window.h
+++ b/src/ui/window.h
@@ -168,10 +168,12 @@ void win_print_incoming_message(ProfWin *window, GTimeVal *tv_stamp,
     const char * const from, const char * const message);
 void win_show_info(ProfWin *window, PContact contact);
 void win_show_occupant_info(ProfWin *window, const char * const room, Occupant *occupant);
-void win_save_vprint(ProfWin *window, const char show_char, GTimeVal *tstamp, int flags, theme_item_t theme_item, const char * const from, const char * const message, ...);
-void win_save_print(ProfWin *window, const char show_char, GTimeVal *tstamp, int flags, theme_item_t theme_item, const char * const from, const char * const message);
-void win_save_println(ProfWin *window, const char * const message);
-void win_save_newline(ProfWin *window);
+void win_vprint(ProfWin *window, const char show_char, GTimeVal *tstamp, int flags, theme_item_t theme_item, const char * const from, const char * const message, ...);
+void win_print(ProfWin *window, const char show_char, GTimeVal *tstamp, int flags, theme_item_t theme_item, const char * const from, const char * const message);
+void win_print_with_receipt(ProfWin *window, const char show_char, GTimeVal *tstamp, int flags,
+    theme_item_t theme_item, const char * const from, const char * const message, char *id);
+void win_println(ProfWin *window, const char * const message);
+void win_newline(ProfWin *window);
 void win_redraw(ProfWin *window);
 void win_hide_subwin(ProfWin *window);
 void win_show_subwin(ProfWin *window);
@@ -179,6 +181,7 @@ int win_roster_cols(void);
 int win_occpuants_cols(void);
 void win_printline_nowrap(WINDOW *win, char *msg);
 void win_mouse(ProfWin *current, const wint_t ch, const int result);
+void win_mark_received(ProfWin *window, const char * const id);
 
 int win_unread(ProfWin *window);
 gboolean win_has_active_subwin(ProfWin *window);
diff --git a/src/ui/windows.c b/src/ui/windows.c
index b534e973..c9496ef6 100644
--- a/src/ui/windows.c
+++ b/src/ui/windows.c
@@ -622,7 +622,7 @@ wins_lost_connection(void)
     while (curr != NULL) {
         ProfWin *window = curr->data;
         if (window->type != WIN_CONSOLE) {
-            win_save_print(window, '-', NULL, 0, THEME_ERROR, "", "Lost connection.");
+            win_print(window, '-', NULL, 0, THEME_ERROR, "", "Lost connection.");
 
             // if current win, set current_win_dirty
             if (wins_is_current(window)) {
diff --git a/src/xmpp/message.c b/src/xmpp/message.c
index a3da01ee..5663dbfc 100644
--- a/src/xmpp/message.c
+++ b/src/xmpp/message.c
@@ -79,60 +79,86 @@ message_add_handlers(void)
     HANDLE(STANZA_NS_CAPTCHA,    NULL,                   _captcha_handler);
 }
 
-void
+char *
 message_send_chat(const char * const barejid, const char * const msg)
 {
-    xmpp_stanza_t *message;
     xmpp_conn_t * const conn = connection_get_conn();
     xmpp_ctx_t * const ctx = connection_get_ctx();
 
     ChatSession *session = chat_session_get(barejid);
+    char *state = NULL;
+    char *jid = NULL;
     if (session) {
-        char *state = NULL;
         if (prefs_get_boolean(PREF_STATES) && session->send_states) {
             state = STANZA_NAME_ACTIVE;
         }
         Jid *jidp = jid_create_from_bare_and_resource(session->barejid, session->resource);
-        message = stanza_create_message(ctx, jidp->fulljid, STANZA_TYPE_CHAT, msg, state, false);
+        jid = strdup(jidp->fulljid);
         jid_destroy(jidp);
+
     } else {
-        char *state = NULL;
         if (prefs_get_boolean(PREF_STATES)) {
             state = STANZA_NAME_ACTIVE;
         }
-        message = stanza_create_message(ctx, barejid, STANZA_TYPE_CHAT, msg, state, false);
+        jid = strdup(barejid);
+    }
+
+    char *id = create_unique_id("msg");
+    xmpp_stanza_t *message = stanza_create_message(ctx, id, jid, STANZA_TYPE_CHAT, msg);
+    free(jid);
+
+    if (state) {
+        stanza_attach_state(ctx, message, state);
+    }
+    if (prefs_get_boolean(PREF_RECEIPTS)) {
+        stanza_attach_receipt_request(ctx, message);
     }
 
     xmpp_send(conn, message);
     xmpp_stanza_release(message);
+
+    return id;
 }
 
-void
+char *
 message_send_chat_encrypted(const char * const barejid, const char * const msg)
 {
-    xmpp_stanza_t *message;
     xmpp_conn_t * const conn = connection_get_conn();
     xmpp_ctx_t * const ctx = connection_get_ctx();
 
     ChatSession *session = chat_session_get(barejid);
+    char *state = NULL;
+    char *jid = NULL;
     if (session) {
-        char *state = NULL;
         if (prefs_get_boolean(PREF_STATES) && session->send_states) {
             state = STANZA_NAME_ACTIVE;
         }
         Jid *jidp = jid_create_from_bare_and_resource(session->barejid, session->resource);
-        message = stanza_create_message(ctx, jidp->fulljid, STANZA_TYPE_CHAT, msg, state, true);
+        jid = strdup(jidp->fulljid);
         jid_destroy(jidp);
     } else {
-        char *state = NULL;
         if (prefs_get_boolean(PREF_STATES)) {
             state = STANZA_NAME_ACTIVE;
         }
-        message = stanza_create_message(ctx, barejid, STANZA_TYPE_CHAT, msg, state, true);
+        jid = strdup(barejid);
+    }
+
+    char *id = create_unique_id("msg");
+    xmpp_stanza_t *message = stanza_create_message(ctx, id, barejid, STANZA_TYPE_CHAT, msg);
+    free(jid);
+
+    if (state) {
+        stanza_attach_state(ctx, message, state);
+    }
+    stanza_attach_carbons_private(ctx, message);
+    if (prefs_get_boolean(PREF_RECEIPTS)) {
+        stanza_attach_receipt_request(ctx, message);
     }
 
     xmpp_send(conn, message);
     xmpp_stanza_release(message);
+
+    return id;
 }
 
 void
@@ -140,7 +166,9 @@ message_send_private(const char * const fulljid, const char * const msg)
 {
     xmpp_conn_t * const conn = connection_get_conn();
     xmpp_ctx_t * const ctx = connection_get_ctx();
-    xmpp_stanza_t *message = stanza_create_message(ctx, fulljid, STANZA_TYPE_CHAT, msg, NULL, false);
+    char *id = create_unique_id("prv");
+    xmpp_stanza_t *message = stanza_create_message(ctx, id, fulljid, STANZA_TYPE_CHAT, msg);
+    free(id);
 
     xmpp_send(conn, message);
     xmpp_stanza_release(message);
@@ -151,7 +179,9 @@ message_send_groupchat(const char * const roomjid, const char * const msg)
 {
     xmpp_conn_t * const conn = connection_get_conn();
     xmpp_ctx_t * const ctx = connection_get_ctx();
-    xmpp_stanza_t *message = stanza_create_message(ctx, roomjid, STANZA_TYPE_GROUPCHAT, msg, NULL, false);
+    char *id = create_unique_id("muc");
+    xmpp_stanza_t *message = stanza_create_message(ctx, id, roomjid, STANZA_TYPE_GROUPCHAT, msg);
+    free(id);
 
     xmpp_send(conn, message);
     xmpp_stanza_release(message);
@@ -441,6 +471,29 @@ _groupchat_handler(xmpp_conn_t * const conn,
     return 1;
 }
 
+void
+_message_send_receipt(const char * const fulljid, const char * const message_id)
+{
+    xmpp_conn_t * const conn = connection_get_conn();
+    xmpp_ctx_t * const ctx = connection_get_ctx();
+    xmpp_stanza_t *message = xmpp_stanza_new(ctx);
+    char *id = create_unique_id("receipt");
+    xmpp_stanza_set_name(message, STANZA_NAME_MESSAGE);
+    xmpp_stanza_set_id(message, id);
+    xmpp_stanza_set_attribute(message, STANZA_ATTR_TO, fulljid);
+
+    xmpp_stanza_t *receipt = xmpp_stanza_new(ctx);
+    xmpp_stanza_set_name(receipt, "received");
+    xmpp_stanza_set_ns(receipt, STANZA_NS_RECEIPTS);
+    xmpp_stanza_set_attribute(receipt, STANZA_ATTR_ID, message_id);
+
+    xmpp_stanza_add_child(message, receipt);
+    xmpp_stanza_release(receipt);
+
+    xmpp_send(conn, message);
+    xmpp_stanza_release(message);
+}
+
 static int
 _chat_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
     void * const userdata)
@@ -451,6 +504,24 @@ _chat_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
         return 1;
     }
 
+    // check if message receipt
+    xmpp_stanza_t *receipt = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_RECEIPTS);
+    if (receipt) {
+        char *name = xmpp_stanza_get_name(receipt);
+        if (g_strcmp0(name, "received") == 0) {
+            char *id = xmpp_stanza_get_attribute(receipt, STANZA_ATTR_ID);
+            if (id) {
+                char *fulljid = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM);
+                if (fulljid) {
+                    Jid *jidp = jid_create(fulljid);
+                    handle_message_receipt(jidp->barejid, id);
+                    jid_destroy(jidp);
+                }
+
+            }
+        }
+    }
+
     // check if carbon message
     xmpp_stanza_t *carbons = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_CARBONS);
     if(carbons) {
@@ -538,6 +609,7 @@ _chat_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
         // determine if the notifications happened whilst offline
         GTimeVal tv_stamp;
         gboolean delayed = stanza_get_delay(stanza, &tv_stamp);
+        char *id = xmpp_stanza_get_id(stanza);
 
         // check for and deal with message
         xmpp_stanza_t *body = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_BODY);
@@ -549,6 +621,15 @@ _chat_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
                 } else {
                     handle_incoming_message(jid->barejid, jid->resourcepart, message);
                 }
+                if (id) {
+                    xmpp_stanza_t *receipts = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_RECEIPTS);
+                    if (receipts) {
+                        char *receipts_name = xmpp_stanza_get_name(receipts);
+                        if (g_strcmp0(receipts_name, "request") == 0) {
+                            _message_send_receipt(jid->fulljid, id);
+                        }
+                    }
+                }
                 xmpp_free(ctx, message);
             }
         }
diff --git a/src/xmpp/stanza.c b/src/xmpp/stanza.c
index e849ac97..0b291d2e 100644
--- a/src/xmpp/stanza.c
+++ b/src/xmpp/stanza.c
@@ -282,9 +282,44 @@ stanza_create_room_subject_message(xmpp_ctx_t *ctx, const char * const room, con
 }
 
 xmpp_stanza_t *
-stanza_create_message(xmpp_ctx_t *ctx, const char * const recipient,
-    const char * const type, const char * const message,
-    const char * const state, gboolean encrypted)
+stanza_attach_state(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza, const char * const state)
+{
+    xmpp_stanza_t *chat_state = xmpp_stanza_new(ctx);
+    xmpp_stanza_set_name(chat_state, state);
+    xmpp_stanza_set_ns(chat_state, STANZA_NS_CHATSTATES);
+    xmpp_stanza_add_child(stanza, chat_state);
+    xmpp_stanza_release(chat_state);
+
+    return stanza;
+}
+
+xmpp_stanza_t *
+stanza_attach_carbons_private(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza)
+{
+    xmpp_stanza_t *private_carbon = xmpp_stanza_new(ctx);
+    xmpp_stanza_set_name(private_carbon, "private");
+    xmpp_stanza_set_ns(private_carbon, STANZA_NS_CARBONS);
+    xmpp_stanza_add_child(stanza, private_carbon);
+    xmpp_stanza_release(private_carbon);
+
+    return stanza;
+}
+
+xmpp_stanza_t *
+stanza_attach_receipt_request(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza)
+{
+    xmpp_stanza_t *receipet_request = xmpp_stanza_new(ctx);
+    xmpp_stanza_set_name(receipet_request, "request");
+    xmpp_stanza_set_ns(receipet_request, STANZA_NS_RECEIPTS);
+    xmpp_stanza_add_child(stanza, receipet_request);
+    xmpp_stanza_release(receipet_request);
+
+    return stanza;
+}
+
+xmpp_stanza_t *
+stanza_create_message(xmpp_ctx_t *ctx, char *id, const char * const recipient,
+    const char * const type, const char * const message)
 {
     xmpp_stanza_t *msg, *body, *text;
 
@@ -292,9 +327,7 @@ stanza_create_message(xmpp_ctx_t *ctx, const char * const recipient,
     xmpp_stanza_set_name(msg, STANZA_NAME_MESSAGE);
     xmpp_stanza_set_type(msg, type);
     xmpp_stanza_set_attribute(msg, STANZA_ATTR_TO, recipient);
-    char *id = create_unique_id(NULL);
     xmpp_stanza_set_id(msg, id);
-    free(id);
 
     body = xmpp_stanza_new(ctx);
     xmpp_stanza_set_name(body, STANZA_NAME_BODY);
@@ -306,22 +339,6 @@ stanza_create_message(xmpp_ctx_t *ctx, const char * const recipient,
     xmpp_stanza_add_child(msg, body);
     xmpp_stanza_release(body);
 
-    if (state != NULL) {
-        xmpp_stanza_t *chat_state = xmpp_stanza_new(ctx);
-        xmpp_stanza_set_name(chat_state, state);
-        xmpp_stanza_set_ns(chat_state, STANZA_NS_CHATSTATES);
-        xmpp_stanza_add_child(msg, chat_state);
-        xmpp_stanza_release(chat_state);
-    }
-
-    if (encrypted) {
-        xmpp_stanza_t *private_carbon = xmpp_stanza_new(ctx);
-        xmpp_stanza_set_name(private_carbon, "private");
-        xmpp_stanza_set_ns(private_carbon, STANZA_NS_CARBONS);
-        xmpp_stanza_add_child(msg, private_carbon);
-        xmpp_stanza_release(private_carbon);
-    }
-
     return msg;
 }
 
diff --git a/src/xmpp/stanza.h b/src/xmpp/stanza.h
index 365bed86..50f3dbd0 100644
--- a/src/xmpp/stanza.h
+++ b/src/xmpp/stanza.h
@@ -158,6 +158,7 @@
 #define STANZA_NS_PUBSUB "http://jabber.org/protocol/pubsub"
 #define STANZA_NS_CARBONS "urn:xmpp:carbons:2"
 #define STANZA_NS_FORWARD "urn:xmpp:forward:0"
+#define STANZA_NS_RECEIPTS "urn:xmpp:receipts"
 
 #define STANZA_DATAFORM_SOFTWARE "urn:xmpp:dataforms:softwareinfo"
 
@@ -189,9 +190,12 @@ xmpp_stanza_t * stanza_disable_carbons(xmpp_ctx_t *ctx);
 xmpp_stanza_t* stanza_create_chat_state(xmpp_ctx_t *ctx,
     const char * const fulljid, const char * const state);
 
-xmpp_stanza_t* stanza_create_message(xmpp_ctx_t *ctx,
-    const char * const recipient, const char * const type,
-    const char * const message, const char * const state, gboolean encrypted);
+xmpp_stanza_t * stanza_attach_state(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza, const char * const state);
+xmpp_stanza_t * stanza_attach_carbons_private(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza);
+xmpp_stanza_t * stanza_attach_receipt_request(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza);
+
+xmpp_stanza_t* stanza_create_message(xmpp_ctx_t *ctx, char *id,
+    const char * const recipient, const char * const type, const char * const message);
 
 xmpp_stanza_t* stanza_create_room_join_presence(xmpp_ctx_t * const ctx,
     const char * const full_room_jid, const char * const passwd);
diff --git a/src/xmpp/xmpp.h b/src/xmpp/xmpp.h
index e8e79ee9..7deb71db 100644
--- a/src/xmpp/xmpp.h
+++ b/src/xmpp/xmpp.h
@@ -145,8 +145,8 @@ char* jabber_get_account_name(void);
 GList * jabber_get_available_resources(void);
 
 // message functions
-void message_send_chat(const char * const barejid, const char * const msg);
-void message_send_chat_encrypted(const char * const barejid, const char * const msg);
+char* message_send_chat(const char * const barejid, const char * const msg);
+char* message_send_chat_encrypted(const char * const barejid, const char * const msg);
 void message_send_private(const char * const fulljid, const char * const msg);
 void message_send_groupchat(const char * const roomjid, const char * const msg);
 void message_send_groupchat_subject(const char * const roomjid, const char * const subject);