about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--.gitignore91
-rw-r--r--.travis.yml13
-rw-r--r--CHANGELOG1
-rw-r--r--Makefile.am110
-rwxr-xr-xconfigure-debug2
-rw-r--r--configure.ac43
-rwxr-xr-xinstall-all.sh2
-rw-r--r--src/command/command.c313
-rw-r--r--src/command/command.h9
-rw-r--r--src/command/commands.c994
-rw-r--r--src/command/commands.h167
-rw-r--r--src/common.c21
-rw-r--r--src/common.h8
-rw-r--r--src/config/account.c9
-rw-r--r--src/config/account.h3
-rw-r--r--src/config/accounts.c28
-rw-r--r--src/config/accounts.h2
-rw-r--r--src/config/preferences.c50
-rw-r--r--src/config/preferences.h11
-rw-r--r--src/config/theme.c8
-rw-r--r--src/config/theme.h5
-rw-r--r--src/event/client_events.c84
-rw-r--r--src/event/server_events.c156
-rw-r--r--src/event/server_events.h14
-rw-r--r--src/event/ui_events.c4
-rw-r--r--src/jid.c3
-rw-r--r--src/log.c157
-rw-r--r--src/log.h8
-rw-r--r--src/main.c6
-rw-r--r--src/muc.c13
-rw-r--r--src/muc.h3
-rw-r--r--src/otr/otr.c65
-rw-r--r--src/otr/otr.h6
-rw-r--r--src/pgp/gpg.c531
-rw-r--r--src/pgp/gpg.h59
-rw-r--r--src/profanity.c34
-rw-r--r--src/ui/buffer.c4
-rw-r--r--src/ui/console.c58
-rw-r--r--src/ui/core.c112
-rw-r--r--src/ui/inputwin.c26
-rw-r--r--src/ui/notifier.c2
-rw-r--r--src/ui/occupantswin.c4
-rw-r--r--src/ui/rosterwin.c4
-rw-r--r--src/ui/statusbar.c3
-rw-r--r--src/ui/statusbar.h6
-rw-r--r--src/ui/titlebar.c100
-rw-r--r--src/ui/ui.h70
-rw-r--r--src/ui/win_types.h146
-rw-r--r--src/ui/window.c115
-rw-r--r--src/ui/window.h135
-rw-r--r--src/window_list.c (renamed from src/ui/windows.c)142
-rw-r--r--src/window_list.h (renamed from src/ui/windows.h)13
-rw-r--r--src/xmpp/connection.c10
-rw-r--r--src/xmpp/message.c152
-rw-r--r--src/xmpp/presence.c28
-rw-r--r--src/xmpp/stanza.c21
-rw-r--r--src/xmpp/stanza.h4
-rw-r--r--src/xmpp/xmpp.h8
-rw-r--r--tests/functionaltests/functionaltests.c119
-rw-r--r--tests/functionaltests/proftest.c238
-rw-r--r--tests/functionaltests/proftest.h17
-rwxr-xr-xtests/functionaltests/start_profanity.sh2
-rw-r--r--tests/functionaltests/test_chat_session.c207
-rw-r--r--tests/functionaltests/test_chat_session.h7
-rw-r--r--tests/functionaltests/test_connect.c95
-rw-r--r--tests/functionaltests/test_connect.h6
-rw-r--r--tests/functionaltests/test_message.c42
-rw-r--r--tests/functionaltests/test_message.h2
-rw-r--r--tests/functionaltests/test_ping.c57
-rw-r--r--tests/functionaltests/test_ping.h2
-rw-r--r--tests/functionaltests/test_presence.c243
-rw-r--r--tests/functionaltests/test_presence.h13
-rw-r--r--tests/functionaltests/test_rooms.c38
-rw-r--r--tests/functionaltests/test_rooms.h2
-rw-r--r--tests/unittests/config/stub_accounts.c (renamed from tests/config/stub_accounts.c)2
-rw-r--r--tests/unittests/helpers.c (renamed from tests/helpers.c)0
-rw-r--r--tests/unittests/helpers.h (renamed from tests/helpers.h)0
-rw-r--r--tests/unittests/log/stub_log.c (renamed from tests/log/stub_log.c)8
-rw-r--r--tests/unittests/otr/stub_otr.c (renamed from tests/otr/stub_otr.c)10
-rw-r--r--tests/unittests/pgp/stub_gpg.c49
-rw-r--r--tests/unittests/test_autocomplete.c (renamed from tests/test_autocomplete.c)0
-rw-r--r--tests/unittests/test_autocomplete.h (renamed from tests/test_autocomplete.h)0
-rw-r--r--tests/unittests/test_chat_session.c (renamed from tests/test_chat_session.c)0
-rw-r--r--tests/unittests/test_chat_session.h (renamed from tests/test_chat_session.h)0
-rw-r--r--tests/unittests/test_cmd_account.c (renamed from tests/test_cmd_account.c)160
-rw-r--r--tests/unittests/test_cmd_account.h (renamed from tests/test_cmd_account.h)1
-rw-r--r--tests/unittests/test_cmd_alias.c (renamed from tests/test_cmd_alias.c)18
-rw-r--r--tests/unittests/test_cmd_alias.h (renamed from tests/test_cmd_alias.h)0
-rw-r--r--tests/unittests/test_cmd_bookmark.c (renamed from tests/test_cmd_bookmark.c)47
-rw-r--r--tests/unittests/test_cmd_bookmark.h (renamed from tests/test_cmd_bookmark.h)0
-rw-r--r--tests/unittests/test_cmd_connect.c (renamed from tests/test_cmd_connect.c)50
-rw-r--r--tests/unittests/test_cmd_connect.h (renamed from tests/test_cmd_connect.h)0
-rw-r--r--tests/unittests/test_cmd_disconnect.c (renamed from tests/test_cmd_disconnect.c)4
-rw-r--r--tests/unittests/test_cmd_disconnect.h (renamed from tests/test_cmd_disconnect.h)0
-rw-r--r--tests/unittests/test_cmd_join.c (renamed from tests/test_cmd_join.c)20
-rw-r--r--tests/unittests/test_cmd_join.h (renamed from tests/test_cmd_join.h)0
-rw-r--r--tests/unittests/test_cmd_otr.c (renamed from tests/test_cmd_otr.c)185
-rw-r--r--tests/unittests/test_cmd_otr.h (renamed from tests/test_cmd_otr.h)4
-rw-r--r--tests/unittests/test_cmd_pgp.c129
-rw-r--r--tests/unittests/test_cmd_pgp.h17
-rw-r--r--tests/unittests/test_cmd_rooms.c (renamed from tests/test_cmd_rooms.c)6
-rw-r--r--tests/unittests/test_cmd_rooms.h (renamed from tests/test_cmd_rooms.h)0
-rw-r--r--tests/unittests/test_cmd_roster.c (renamed from tests/test_cmd_roster.c)27
-rw-r--r--tests/unittests/test_cmd_roster.h (renamed from tests/test_cmd_roster.h)0
-rw-r--r--tests/unittests/test_cmd_statuses.c (renamed from tests/test_cmd_statuses.c)26
-rw-r--r--tests/unittests/test_cmd_statuses.h (renamed from tests/test_cmd_statuses.h)0
-rw-r--r--tests/unittests/test_cmd_sub.c (renamed from tests/test_cmd_sub.c)4
-rw-r--r--tests/unittests/test_cmd_sub.h (renamed from tests/test_cmd_sub.h)0
-rw-r--r--tests/unittests/test_common.c (renamed from tests/test_common.c)0
-rw-r--r--tests/unittests/test_common.h (renamed from tests/test_common.h)0
-rw-r--r--tests/unittests/test_contact.c (renamed from tests/test_contact.c)0
-rw-r--r--tests/unittests/test_contact.h (renamed from tests/test_contact.h)0
-rw-r--r--tests/unittests/test_form.c (renamed from tests/test_form.c)0
-rw-r--r--tests/unittests/test_form.h (renamed from tests/test_form.h)0
-rw-r--r--tests/unittests/test_jid.c (renamed from tests/test_jid.c)0
-rw-r--r--tests/unittests/test_jid.h (renamed from tests/test_jid.h)0
-rw-r--r--tests/unittests/test_keyhandlers.c (renamed from tests/test_keyhandlers.c)0
-rw-r--r--tests/unittests/test_keyhandlers.h (renamed from tests/test_keyhandlers.h)0
-rw-r--r--tests/unittests/test_muc.c (renamed from tests/test_muc.c)0
-rw-r--r--tests/unittests/test_muc.h (renamed from tests/test_muc.h)0
-rw-r--r--tests/unittests/test_parser.c (renamed from tests/test_parser.c)0
-rw-r--r--tests/unittests/test_parser.h (renamed from tests/test_parser.h)0
-rw-r--r--tests/unittests/test_preferences.c (renamed from tests/test_preferences.c)0
-rw-r--r--tests/unittests/test_preferences.h (renamed from tests/test_preferences.h)0
-rw-r--r--tests/unittests/test_roster_list.c (renamed from tests/test_roster_list.c)0
-rw-r--r--tests/unittests/test_roster_list.h (renamed from tests/test_roster_list.h)0
-rw-r--r--tests/unittests/test_server_events.c (renamed from tests/test_server_events.c)6
-rw-r--r--tests/unittests/test_server_events.h (renamed from tests/test_server_events.h)0
-rw-r--r--tests/unittests/ui/stub_ui.c (renamed from tests/ui/stub_ui.c)87
-rw-r--r--tests/unittests/ui/stub_ui.h (renamed from tests/ui/stub_ui.h)0
-rw-r--r--tests/unittests/unittests.c (renamed from tests/testsuite.c)26
-rw-r--r--tests/unittests/xmpp/stub_xmpp.c (renamed from tests/xmpp/stub_xmpp.c)12
-rw-r--r--theme_template3
-rw-r--r--themes/boothj55
-rw-r--r--themes/complex2
-rw-r--r--themes/simple3
136 files changed, 4556 insertions, 1641 deletions
diff --git a/.gitignore b/.gitignore
index ba909849..fbf0a3fb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,68 +4,55 @@
 # exclude patterns (uncomment them if you want to use them):
 # *.[oa]
 # *~
-profanity
-profanity.sh
-*.o
-*.log
-*.swp
-testsuite
-tags
-cscope.out
-.deps/
-build-aux/
+
+# IDE
+.codelite/
+profanity.mk
+profanity.project
+profanity.workspace
+compile_commands.json
+
+# autotools
+.libs/
 Makefile
 Makefile.in
+_configs.sed
 aclocal.m4
 autom4te.cache/
-src/config.h
-src/config.h.in
+build-aux/
+config.log
 config.status
 configure
-configure.scan
-stamp-h1
-*~
-*dirstamp
-valgrind*.out*
-core
-bugs/
-TODO
-plugins/
-*_key.txt
-*_fingerprints.txt
-src/gitversion.h
-src/gitversion.h.in
-*_key.txt
-*_fingerprints.txt
-TAGS
-.libs/
-_configs.sed
 libprofanity.la
 libtool
-runvalgrind.sh
-src/prof_config.h
-clean.sh
-valgrind.out.old
-tests/testsuite.trs
-push-all.sh
-.DS_Store
-.codelite/
-Profanity.project
-Profanity.workspace
-profanity.project
-profanity.workspace
-pageing.txt
-cppcheck.out
-gitpushall.sh
-Debug/
-compile_commands.json
-profanity.mk
-profanity.project
-profanity.workspace
 m4/
-test.sh
+**/.deps/
+**.dirstamp
+src/config.h
+src/config.h.in
+src/config.h.in~
+src/gitversion.h
+src/gitversion.h.in
+src/stamp-h1
+
+# binaries
+profanity
+**/*.o
+
+# test output
+tests/functionaltests/functionaltests
+tests/functionaltests/functionaltests.log
+tests/functionaltests/functionaltests.trs
+tests/unittests/unittests
+tests/unittests/unittests.log
+tests/unittests/unittests.trs
+test-suite.log
+
+# local scripts
 clean-test.sh
-callgrind.out.*
 gen_docs.sh
+gitpushall.sh
+
+# website files
 main_fragment.html
 toc_fragment.html
diff --git a/.travis.yml b/.travis.yml
index e546d8b3..ccc67e6b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,9 @@
 language: c
 install:
+    - lsb_release -a
+    - uname -a
     - sudo apt-get update
-    - sudo apt-get -y install libssl-dev libexpat1-dev libncursesw5-dev libglib2.0-dev libnotify-dev libcurl3-dev libxss-dev libotr2-dev uuid-dev
+    - sudo apt-get -y install libssl-dev libexpat1-dev libncursesw5-dev libglib2.0-dev libnotify-dev libcurl3-dev libxss-dev libotr2-dev libgpgme11-dev uuid-dev expect-dev tcl-dev
     - git clone git://github.com/strophe/libstrophe.git
     - cd libstrophe
     - mkdir m4
@@ -21,5 +23,14 @@ install:
     - sudo make install
     - cd ../..
     - rm -rf cmocka-1.0.0
+    - sudo apt-get install libmicrohttpd-dev
+    - git clone git://github.com/boothj5/stabber.git
+    - cd stabber
+    - ./bootstrap.sh
+    - ./configure --prefix=/usr
+    - make
+    - sudo make install
+    - cd ..
+    - rm -rf stabber
     - ./bootstrap.sh
 script: ./configure && make && make check
diff --git a/CHANGELOG b/CHANGELOG
index 519660db..0548c172 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -2,6 +2,7 @@
 =====
 
 - GNU Readline
+- OpenPGP support
 - Message Carbons (xep-0280)
 - Message Delivery Receipts (xep-0184)
 - MUC Mediated Invitation support
diff --git a/Makefile.am b/Makefile.am
index 372b60f3..0486982d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -20,7 +20,8 @@ core_sources = \
 	src/ui/titlebar.c src/ui/statusbar.c src/ui/inputwin.c \
 	src/ui/titlebar.h src/ui/statusbar.h src/ui/inputwin.h \
 	src/ui/console.c src/ui/notifier.c \
-	src/ui/windows.c src/ui/windows.h \
+	src/ui/win_types.h \
+	src/window_list.c src/window_list.h \
 	src/ui/rosterwin.c src/ui/occupantswin.c \
 	src/ui/buffer.c src/ui/buffer.h \
 	src/command/command.h src/command/command.c \
@@ -35,7 +36,7 @@ core_sources = \
 	src/config/preferences.c src/config/preferences.h \
 	src/config/theme.c src/config/theme.h
 
-tests_sources = \
+unittest_sources = \
 	src/contact.c src/contact.h src/common.c \
 	src/log.h src/profanity.c src/common.h \
 	src/profanity.h src/chat_session.c \
@@ -46,6 +47,7 @@ tests_sources = \
 	src/xmpp/xmpp.h src/xmpp/form.c \
 	src/ui/ui.h \
 	src/otr/otr.h \
+	src/pgp/gpg.h \
 	src/command/command.h src/command/command.c \
 	src/command/commands.h src/command/commands.c \
 	src/tools/parser.c \
@@ -57,55 +59,68 @@ tests_sources = \
 	src/config/account.c src/config/account.h \
 	src/config/preferences.c src/config/preferences.h \
 	src/config/theme.c src/config/theme.h \
-	src/ui/windows.c src/ui/windows.h \
-	src/ui/window.c src/ui/window.h \
-	src/ui/buffer.c \
-	src/ui/titlebar.c src/ui/statusbar.c src/ui/inputwin.c \
-	src/ui/titlebar.h src/ui/statusbar.h src/ui/inputwin.h \
+	src/window_list.c src/window_list.h \
 	src/event/server_events.c src/event/server_events.h \
 	src/event/client_events.c src/event/client_events.h \
 	src/event/ui_events.c src/event/ui_events.h \
-	tests/xmpp/stub_xmpp.c \
-	tests/ui/stub_ui.c \
-	tests/log/stub_log.c \
-	tests/config/stub_accounts.c \
-	tests/helpers.c tests/helpers.h \
-	tests/test_cmd_account.c tests/test_cmd_account.h \
-	tests/test_cmd_alias.c tests/test_cmd_alias.h \
-	tests/test_cmd_bookmark.c tests/test_cmd_bookmark.h \
-	tests/test_cmd_connect.c tests/test_cmd_connect.h \
-	tests/test_cmd_join.c tests/test_cmd_join.h \
-	tests/test_cmd_otr.c tests/test_cmd_otr.h \
-	tests/test_cmd_rooms.c tests/test_cmd_rooms.h \
-	tests/test_cmd_roster.c tests/test_cmd_roster.h \
-	tests/test_cmd_statuses.c tests/test_cmd_statuses.h \
-	tests/test_cmd_sub.c tests/test_cmd_sub.h \
-	tests/test_cmd_disconnect.c tests/test_cmd_disconnect.h \
-	tests/test_common.c tests/test_common.h \
-	tests/test_contact.c tests/test_contact.h \
-	tests/test_form.c tests/test_form.h \
-	tests/test_jid.c tests/test_jid.h \
-	tests/test_muc.c tests/test_muc.h \
-	tests/test_parser.c tests/test_parser.h \
-	tests/test_preferences.c tests/test_preferences.h \
-	tests/test_roster_list.c tests/test_roster_list.h \
-	tests/test_server_events.c tests/test_server_events.h \
-	tests/test_autocomplete.c tests/test_autocomplete.h \
-	tests/test_chat_session.c tests/test_chat_session.h \
-	tests/testsuite.c
+	tests/unittests/xmpp/stub_xmpp.c \
+	tests/unittests/ui/stub_ui.c \
+	tests/unittests/log/stub_log.c \
+	tests/unittests/config/stub_accounts.c \
+	tests/unittests/helpers.c tests/unittests/helpers.h \
+	tests/unittests/test_cmd_account.c tests/unittests/test_cmd_account.h \
+	tests/unittests/test_cmd_alias.c tests/unittests/test_cmd_alias.h \
+	tests/unittests/test_cmd_bookmark.c tests/unittests/test_cmd_bookmark.h \
+	tests/unittests/test_cmd_connect.c tests/unittests/test_cmd_connect.h \
+	tests/unittests/test_cmd_join.c tests/unittests/test_cmd_join.h \
+	tests/unittests/test_cmd_otr.c tests/unittests/test_cmd_otr.h \
+	tests/unittests/test_cmd_pgp.c tests/unittests/test_cmd_pgp.h \
+	tests/unittests/test_cmd_rooms.c tests/unittests/test_cmd_rooms.h \
+	tests/unittests/test_cmd_roster.c tests/unittests/test_cmd_roster.h \
+	tests/unittests/test_cmd_statuses.c tests/unittests/test_cmd_statuses.h \
+	tests/unittests/test_cmd_sub.c tests/unittests/test_cmd_sub.h \
+	tests/unittests/test_cmd_disconnect.c tests/unittests/test_cmd_disconnect.h \
+	tests/unittests/test_common.c tests/unittests/test_common.h \
+	tests/unittests/test_contact.c tests/unittests/test_contact.h \
+	tests/unittests/test_form.c tests/unittests/test_form.h \
+	tests/unittests/test_jid.c tests/unittests/test_jid.h \
+	tests/unittests/test_muc.c tests/unittests/test_muc.h \
+	tests/unittests/test_parser.c tests/unittests/test_parser.h \
+	tests/unittests/test_preferences.c tests/unittests/test_preferences.h \
+	tests/unittests/test_roster_list.c tests/unittests/test_roster_list.h \
+	tests/unittests/test_server_events.c tests/unittests/test_server_events.h \
+	tests/unittests/test_autocomplete.c tests/unittests/test_autocomplete.h \
+	tests/unittests/test_chat_session.c tests/unittests/test_chat_session.h \
+	tests/unittests/unittests.c
+
+functionaltest_sources = \
+	tests/functionaltests/proftest.c tests/functionaltests/proftest.h \
+	tests/functionaltests/test_connect.c tests/functionaltests/test_connect.h \
+	tests/functionaltests/test_ping.c tests/functionaltests/test_ping.h \
+	tests/functionaltests/test_rooms.c tests/functionaltests/test_rooms.h \
+	tests/functionaltests/test_presence.c tests/functionaltests/test_presence.h \
+	tests/functionaltests/test_message.c tests/functionaltests/test_message.h \
+	tests/functionaltests/test_chat_session.c tests/functionaltests/test_chat_session.h \
+	tests/functionaltests/functionaltests.c
 
 main_source = src/main.c
 
 git_include = src/gitversion.h
 
+pgp_sources = \
+	src/pgp/gpg.h src/pgp/gpg.c
+
+pgp_unittest_sources = \
+	tests/unittests/pgp/stub_gpg.c
+
 otr3_sources = \
 	src/otr/otrlib.h src/otr/otrlibv3.c src/otr/otr.h src/otr/otr.c
 
 otr4_sources = \
 	src/otr/otrlib.h src/otr/otrlibv4.c src/otr/otr.h src/otr/otr.c
 
-otr_test_sources = \
-	tests/otr/stub_otr.c
+otr_unittest_sources = \
+	tests/unittests/otr/stub_otr.c
 
 themes_sources = themes/*
 
@@ -113,8 +128,13 @@ script_sources = bootstrap.sh configure-debug install-all.sh
 
 man_sources = docs/profanity.1
 
+if BUILD_PGP
+core_sources += $(pgp_sources)
+unittest_sources += $(pgp_unittest_sources)
+endif
+
 if BUILD_OTR
-tests_sources += $(otr_test_sources)
+unittest_sources += $(otr_unittest_sources)
 if BUILD_OTR3
 core_sources += $(otr3_sources)
 endif
@@ -133,10 +153,13 @@ if INCLUDE_GIT_VERSION
 BUILT_SOURCES = $(git_include)
 endif
 
-TESTS = tests/testsuite
-check_PROGRAMS = tests/testsuite
-tests_testsuite_SOURCES = $(tests_sources)
-tests_testsuite_LDADD = -lcmocka
+TESTS = tests/unittests/unittests tests/functionaltests/functionaltests
+check_PROGRAMS = tests/unittests/unittests tests/functionaltests/functionaltests
+tests_unittests_unittests_SOURCES = $(unittest_sources)
+tests_unittests_unittests_LDADD = -lcmocka
+tests_functionaltests_functionaltests_SOURCES = $(functionaltest_sources)
+tests_functionaltests_functionaltests_CFLAGS = -I/usr/include/tcl8.6 -I/usr/include/tcl8.5
+tests_functionaltests_functionaltests_LDADD = -lcmocka -lstabber -lexpect -ltcl
 
 man_MANS = $(man_sources)
 
@@ -165,3 +188,6 @@ $(git_include): $(git_include).in
 clean-local:
 	rm -f $(git_include) $(git_include).in
 endif
+
+check-unit: tests/unittests/unittests
+	tests/unittests/unittests
diff --git a/configure-debug b/configure-debug
index 85bfd817..c61a0f50 100755
--- a/configure-debug
+++ b/configure-debug
@@ -1,3 +1,3 @@
 #!/bin/sh
 
-./configure CFLAGS='-g3 -O0' CXXFLAGS='-g3 -O0'
+./configure CFLAGS='-g3 -O0' CXXFLAGS='-g3 -O0' $@
diff --git a/configure.ac b/configure.ac
index 1e2c6173..1bf05a92 100644
--- a/configure.ac
+++ b/configure.ac
@@ -11,6 +11,9 @@ AM_INIT_AUTOMAKE([foreign subdir-objects])
 ### Checks for programs.
 AC_PROG_CC
 
+## Check for LFS
+AC_SYS_LARGEFILE
+
 ### Get canonical host
 AC_CANONICAL_HOST
 PLATFORM="unknown"
@@ -42,6 +45,8 @@ AC_ARG_ENABLE([notifications],
     [AS_HELP_STRING([--enable-notifications], [enable desktop notifications])])
 AC_ARG_ENABLE([otr],
     [AS_HELP_STRING([--enable-otr], [enable otr encryption])])
+AC_ARG_ENABLE([pgp],
+    [AS_HELP_STRING([--enable-pgp], [enable pgp])])
 AC_ARG_WITH([libxml2],
     [AS_HELP_STRING([--with-libxml2], [link with libxml2 instead of expat])])
 AC_ARG_WITH([xscreensaver],
@@ -186,10 +191,24 @@ elif test "x$with_xscreensaver" = x; then
         [AC_MSG_NOTICE([libX11 not found, falling back to profanity auto-away])])
 fi
 
+AM_CONDITIONAL([BUILD_PGP], [false])
+if test "x$enable_pgp" != xno; then
+    AC_CHECK_LIB([gpgme], [main],
+        [AM_CONDITIONAL([BUILD_PGP], [true])
+         AC_DEFINE([HAVE_LIBGPGME], [1], [Have libgpgme])
+         AC_PATH_PROG([GPGME_CONFIG], [gpgme-config], ["failed"])
+         AS_IF([test "x$GPGME_CONFIG" = xfailed],
+            [LIBS="-lgpgme $LIBS"],
+            [LIBS="`$GPGME_CONFIG --libs` $LIBS" AM_CPPFLAGS="`$GPGME_CONFIG --cflags` $AM_CPPFLAGS"])],
+        [AS_IF([test "x$enable_pgp" = xyes],
+            [AC_MSG_ERROR([libgpgme is required for pgp support])],
+            [AC_MSG_NOTICE([libgpgme not found, pgp support not enabled])])])
+fi
+
 AM_CONDITIONAL([BUILD_OTR], [false])
 AM_CONDITIONAL([BUILD_OTR3], [false])
 AM_CONDITIONAL([BUILD_OTR4], [false])
-if test "x$enable_otr" = xyes; then
+if test "x$enable_otr" != xno; then
     AC_SEARCH_LIBS([otrl_init], [otr],
         [AC_COMPILE_IFELSE(
             [AC_LANG_PROGRAM([[
@@ -203,22 +222,9 @@ if test "x$enable_otr" = xyes; then
             ]])],
             [AM_CONDITIONAL([BUILD_OTR], [true]) AM_CONDITIONAL([BUILD_OTR4], [true]) AC_DEFINE([HAVE_LIBOTR], [1], [Have libotr])],
             [AM_CONDITIONAL([BUILD_OTR], [true]) AM_CONDITIONAL([BUILD_OTR3], [true]) AC_DEFINE([HAVE_LIBOTR], [1], [Have libotr])])],
-        [AC_MSG_ERROR([libotr is required for otr encryption support])])
-elif test "x$enable_otr" = x; then
-    AC_SEARCH_LIBS([otrl_init], [otr],
-        [AC_COMPILE_IFELSE(
-            [AC_LANG_PROGRAM([[
-            #include <libotr/version.h>
-            ]],[[
-            #if OTRL_VERSION_MAJOR == 4
-            // OK
-            #else
-            # assume version 3
-            #endif
-            ]])],
-            [AM_CONDITIONAL([BUILD_OTR], [true]) AM_CONDITIONAL([BUILD_OTR4], [true]) AC_DEFINE([HAVE_LIBOTR], [1], [Have libotr])],
-            [AM_CONDITIONAL([BUILD_OTR], [true]) AM_CONDITIONAL([BUILD_OTR3], [true]) AC_DEFINE([HAVE_LIBOTR], [1], [Have libotr])])],
-        [AC_MSG_NOTICE([libotr not found, otr encryption support not enabled])])
+        [AS_IF([test "x$enable_otr" = xyes],
+            [AC_MSG_ERROR([libotr is required for otr encryption support])],
+            [AC_MSG_NOTICE([libotr not found, otr encryption support not enabled])])])
 fi
 
 AS_IF([test "x$with_themes" = xno],
@@ -235,6 +241,9 @@ AM_CONDITIONAL([THEMES_INSTALL], "$THEMES_INSTALL")
 PKG_CHECK_MODULES([cmocka], [cmocka], [],
     [AC_MSG_NOTICE([cmocka is not found, will not be able to run tests])])
 
+AC_CHECK_LIB([stabber], [stbbr_start], [LIBS="$LIBS"],
+    [AC_MSG_NOTICE([stabber not found, will not be able to run functional tests])])
+
 ### Check for ncursesw/ncurses.h first, Arch linux uses ncurses.h for ncursesw
 AC_CHECK_HEADERS([ncursesw/ncurses.h], [], [])
 AC_CHECK_HEADERS([ncurses.h], [], [])
diff --git a/install-all.sh b/install-all.sh
index 4ffc5999..0b1719a4 100755
--- a/install-all.sh
+++ b/install-all.sh
@@ -24,7 +24,7 @@ debian_prepare()
     echo
     echo Profanity installer... installing dependencies
     echo
-    sudo apt-get -y install git automake autoconf libssl-dev libexpat1-dev libncursesw5-dev libglib2.0-dev libnotify-dev libcurl3-dev libxss-dev libotr5-dev libreadline-dev libtool uuid-dev
+    sudo apt-get -y install git automake autoconf libssl-dev libexpat1-dev libncursesw5-dev libglib2.0-dev libnotify-dev libcurl3-dev libxss-dev libotr5-dev libreadline-dev libtool libgpgme11-dev uuid-dev
 
 }
 
diff --git a/src/command/command.c b/src/command/command.c
index 4086278c..105e41e5 100644
--- a/src/command/command.c
+++ b/src/command/command.c
@@ -66,42 +66,41 @@
 #include "xmpp/xmpp.h"
 #include "xmpp/bookmark.h"
 #include "ui/ui.h"
-#include "ui/windows.h"
-
-typedef char*(*autocompleter)(char*, int*);
-
-static gboolean _cmd_execute(const char * const command, const char * const inp);
-
-static char * _cmd_complete_parameters(const char * const input);
-
-static char * _sub_autocomplete(const char * const input);
-static char * _notify_autocomplete(const char * const input);
-static char * _theme_autocomplete(const char * const input);
-static char * _autoaway_autocomplete(const char * const input);
-static char * _autoconnect_autocomplete(const char * const input);
-static char * _account_autocomplete(const char * const input);
-static char * _who_autocomplete(const char * const input);
-static char * _roster_autocomplete(const char * const input);
-static char * _group_autocomplete(const char * const input);
-static char * _bookmark_autocomplete(const char * const input);
-static char * _otr_autocomplete(const char * const input);
-static char * _connect_autocomplete(const char * const input);
-static char * _statuses_autocomplete(const char * const input);
-static char * _alias_autocomplete(const char * const input);
-static char * _join_autocomplete(const char * const input);
-static char * _log_autocomplete(const char * const input);
-static char * _form_autocomplete(const char * const input);
-static char * _form_field_autocomplete(const char * const input);
-static char * _occupants_autocomplete(const char * const input);
-static char * _kick_autocomplete(const char * const input);
-static char * _ban_autocomplete(const char * const input);
-static char * _affiliation_autocomplete(const char * const input);
-static char * _role_autocomplete(const char * const input);
-static char * _resource_autocomplete(const char * const input);
-static char * _titlebar_autocomplete(const char * const input);
-static char * _inpblock_autocomplete(const char * const input);
-static char * _time_autocomplete(const char * const input);
-static char * _receipts_autocomplete(const char * const input);
+#include "window_list.h"
+
+static gboolean _cmd_execute(ProfWin *window, const char * const command, const char * const inp);
+
+static char * _cmd_complete_parameters(ProfWin *window, const char * const input);
+
+static char * _sub_autocomplete(ProfWin *window, const char * const input);
+static char * _notify_autocomplete(ProfWin *window, const char * const input);
+static char * _theme_autocomplete(ProfWin *window, const char * const input);
+static char * _autoaway_autocomplete(ProfWin *window, const char * const input);
+static char * _autoconnect_autocomplete(ProfWin *window, const char * const input);
+static char * _account_autocomplete(ProfWin *window, const char * const input);
+static char * _who_autocomplete(ProfWin *window, const char * const input);
+static char * _roster_autocomplete(ProfWin *window, const char * const input);
+static char * _group_autocomplete(ProfWin *window, const char * const input);
+static char * _bookmark_autocomplete(ProfWin *window, const char * const input);
+static char * _otr_autocomplete(ProfWin *window, const char * const input);
+static char * _pgp_autocomplete(ProfWin *window, const char * const input);
+static char * _connect_autocomplete(ProfWin *window, const char * const input);
+static char * _statuses_autocomplete(ProfWin *window, const char * const input);
+static char * _alias_autocomplete(ProfWin *window, const char * const input);
+static char * _join_autocomplete(ProfWin *window, const char * const input);
+static char * _log_autocomplete(ProfWin *window, const char * const input);
+static char * _form_autocomplete(ProfWin *window, const char * const input);
+static char * _form_field_autocomplete(ProfWin *window, const char * const input);
+static char * _occupants_autocomplete(ProfWin *window, const char * const input);
+static char * _kick_autocomplete(ProfWin *window, const char * const input);
+static char * _ban_autocomplete(ProfWin *window, const char * const input);
+static char * _affiliation_autocomplete(ProfWin *window, const char * const input);
+static char * _role_autocomplete(ProfWin *window, const char * const input);
+static char * _resource_autocomplete(ProfWin *window, const char * const input);
+static char * _titlebar_autocomplete(ProfWin *window, const char * const input);
+static char * _inpblock_autocomplete(ProfWin *window, const char * const input);
+static char * _time_autocomplete(ProfWin *window, const char * const input);
+static char * _receipts_autocomplete(ProfWin *window, const char * const input);
 
 GHashTable *commands = NULL;
 
@@ -206,6 +205,7 @@ static struct cmd_t command_defs[] =
           "size           : Percentage of the screen taken up by the roster (1-99).",
           "add jid [nick] : Add a new item to the roster.",
           "remove jid     : Removes an item from the roster.",
+          "empty          : Remove all items from roster."
           "nick jid nick  : Change a contacts nickname.",
           "clearnick jid  : Removes the current nickname.",
           "",
@@ -668,6 +668,14 @@ static struct cmd_t command_defs[] =
           "If the terminal does not support sounds, it may attempt to flash the screen instead.",
           NULL } } },
 
+    { "/encwarn",
+        cmd_encwarn, parse_args, 1, 1, &cons_encwarn_setting,
+        { "/encwarn on|off", "Titlebar encryption warning.",
+        { "/encwarn on|off",
+          "---------------",
+          "Enabled or disable the unencrypted warning message in the titlebar.",
+          NULL } } },
+
     { "/presence",
         cmd_presence, parse_args, 1, 1, &cons_presence_setting,
         { "/presence on|off", "Show the contacts presence in the titlebar.",
@@ -684,6 +692,14 @@ static struct cmd_t command_defs[] =
           "Enable or disable word wrapping in the main window.",
           NULL } } },
 
+    { "/winstidy",
+        cmd_winstidy, parse_args, 1, 1, &cons_winstidy_setting,
+        { "/winstidy on|off", "Auto tidy windows.",
+        { "/winstidy on|off",
+          "----------------",
+          "Enable or disable auto window tidy.",
+          NULL } } },
+
     { "/time",
         cmd_time, parse_args, 1, 3, &cons_time_setting,
         { "/time main|statusbar set|off [format]", "Time display.",
@@ -855,6 +871,22 @@ static struct cmd_t command_defs[] =
           "Send chat state notifications during chat sessions.",
           NULL } } },
 
+    { "/pgp",
+        cmd_pgp, parse_args, 1, 3, NULL,
+        { "/pgp command [args..]", "Open PGP commands.",
+        { "/pgp command [args..]",
+          "---------------------",
+          "Open PGP commands.",
+          "",
+          "keys                 : List all keys.",
+          "libver               : Show which version of the libgpgme library is being used.",
+          "fps                  : Show known fingerprints.",
+          "setkey contact keyid : Manually associate a key ID with a JID.",
+          "start [contact]      : Start PGP encrypted chat, current contact will be used if not specified.",
+          "end                  : End PGP encrypted chat with the current recipient.",
+          "log on|off|redact    : PGP message logging, default: redact.",
+          NULL } } },
+
     { "/otr",
         cmd_otr, parse_args, 1, 3, NULL,
         { "/otr command [args..]", "Off The Record encryption commands.",
@@ -1210,6 +1242,8 @@ static Autocomplete time_format_ac;
 static Autocomplete resource_ac;
 static Autocomplete inpblock_ac;
 static Autocomplete receipts_ac;
+static Autocomplete pgp_ac;
+static Autocomplete pgp_log_ac;
 
 /*
  * Initialise command autocompleter and history
@@ -1269,6 +1303,7 @@ cmd_init(void)
     autocomplete_add(prefs_ac, "conn");
     autocomplete_add(prefs_ac, "presence");
     autocomplete_add(prefs_ac, "otr");
+    autocomplete_add(prefs_ac, "pgp");
 
     notify_ac = autocomplete_new();
     autocomplete_add(notify_ac, "message");
@@ -1366,6 +1401,7 @@ cmd_init(void)
     autocomplete_add(account_set_ac, "muc");
     autocomplete_add(account_set_ac, "nick");
     autocomplete_add(account_set_ac, "otr");
+    autocomplete_add(account_set_ac, "pgpkeyid");
 
     account_clear_ac = autocomplete_new();
     autocomplete_add(account_clear_ac, "password");
@@ -1373,6 +1409,7 @@ cmd_init(void)
     autocomplete_add(account_clear_ac, "server");
     autocomplete_add(account_clear_ac, "port");
     autocomplete_add(account_clear_ac, "otr");
+    autocomplete_add(account_clear_ac, "pgpkeyid");
 
     account_default_ac = autocomplete_new();
     autocomplete_add(account_default_ac, "set");
@@ -1393,6 +1430,7 @@ cmd_init(void)
     autocomplete_add(roster_ac, "nick");
     autocomplete_add(roster_ac, "clearnick");
     autocomplete_add(roster_ac, "remove");
+    autocomplete_add(roster_ac, "empty");
     autocomplete_add(roster_ac, "show");
     autocomplete_add(roster_ac, "hide");
     autocomplete_add(roster_ac, "by");
@@ -1462,7 +1500,6 @@ cmd_init(void)
     autocomplete_add(otr_ac, "untrust");
     autocomplete_add(otr_ac, "secret");
     autocomplete_add(otr_ac, "log");
-    autocomplete_add(otr_ac, "warn");
     autocomplete_add(otr_ac, "libver");
     autocomplete_add(otr_ac, "policy");
     autocomplete_add(otr_ac, "question");
@@ -1571,6 +1608,20 @@ cmd_init(void)
     receipts_ac = autocomplete_new();
     autocomplete_add(receipts_ac, "send");
     autocomplete_add(receipts_ac, "request");
+
+    pgp_ac = autocomplete_new();
+    autocomplete_add(pgp_ac, "keys");
+    autocomplete_add(pgp_ac, "fps");
+    autocomplete_add(pgp_ac, "setkey");
+    autocomplete_add(pgp_ac, "libver");
+    autocomplete_add(pgp_ac, "start");
+    autocomplete_add(pgp_ac, "end");
+    autocomplete_add(pgp_ac, "log");
+
+    pgp_log_ac = autocomplete_new();
+    autocomplete_add(pgp_log_ac, "on");
+    autocomplete_add(pgp_log_ac, "off");
+    autocomplete_add(pgp_log_ac, "redact");
 }
 
 void
@@ -1630,6 +1681,8 @@ cmd_uninit(void)
     autocomplete_free(resource_ac);
     autocomplete_free(inpblock_ac);
     autocomplete_free(receipts_ac);
+    autocomplete_free(pgp_ac);
+    autocomplete_free(pgp_log_ac);
 }
 
 gboolean
@@ -1714,7 +1767,7 @@ cmd_alias_remove(char *value)
 
 // Command autocompletion functions
 char*
-cmd_autocomplete(const char * const input)
+cmd_autocomplete(ProfWin *window, const char * const input)
 {
     // autocomplete command
     if ((strncmp(input, "/", 1) == 0) && (!str_contains(input, strlen(input), ' '))) {
@@ -1726,7 +1779,7 @@ cmd_autocomplete(const char * const input)
 
     // autocomplete parameters
     } else {
-        char *found = _cmd_complete_parameters(input);
+        char *found = _cmd_complete_parameters(window, input);
         if (found) {
             return found;
         }
@@ -1736,7 +1789,7 @@ cmd_autocomplete(const char * const input)
 }
 
 void
-cmd_reset_autocomplete()
+cmd_reset_autocomplete(ProfWin *window)
 {
     roster_reset_search_attempts();
     muc_invites_reset_ac();
@@ -1802,23 +1855,28 @@ cmd_reset_autocomplete()
     autocomplete_reset(resource_ac);
     autocomplete_reset(inpblock_ac);
     autocomplete_reset(receipts_ac);
+    autocomplete_reset(pgp_ac);
+    autocomplete_reset(pgp_log_ac);
 
-    if (ui_current_win_type() == WIN_CHAT) {
-        ProfChatWin *chatwin = wins_get_current_chat();
+    if (window->type == WIN_CHAT) {
+        ProfChatWin *chatwin = (ProfChatWin*)window;
+        assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
         PContact contact = roster_get_contact(chatwin->barejid);
         if (contact) {
             p_contact_resource_ac_reset(contact);
         }
     }
 
-    if (ui_current_win_type() == WIN_MUC) {
-        ProfMucWin *mucwin = wins_get_current_muc();
+    if (window->type == WIN_MUC) {
+        ProfMucWin *mucwin = (ProfMucWin*)window;
+        assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
         muc_autocomplete_reset(mucwin->roomjid);
         muc_jid_autocomplete_reset(mucwin->roomjid);
     }
 
-    if (ui_current_win_type() == WIN_MUC_CONFIG) {
-        ProfMucConfWin *confwin = wins_get_current_muc_conf();
+    if (window->type == WIN_MUC_CONFIG) {
+        ProfMucConfWin *confwin = (ProfMucConfWin*)window;
+        assert(confwin->memcheck == PROFCONFWIN_MEMCHECK);
         if (confwin->form) {
             form_reset_autocompleters(confwin->form);
         }
@@ -1832,11 +1890,11 @@ cmd_reset_autocomplete()
  * continue, FALSE otherwise
  */
 gboolean
-cmd_process_input(char *inp)
+cmd_process_input(ProfWin *window, char *inp)
 {
     log_debug("Input received: %s", inp);
     gboolean result = FALSE;
-    g_strstrip(inp);
+    g_strchomp(inp);
 
     // just carry on if no input
     if (strlen(inp) == 0) {
@@ -1846,12 +1904,12 @@ cmd_process_input(char *inp)
     } else if (inp[0] == '/') {
         char *inp_cpy = strdup(inp);
         char *command = strtok(inp_cpy, " ");
-        result = _cmd_execute(command, inp);
+        result = _cmd_execute(window, command, inp);
         free(inp_cpy);
 
     // call a default handler if input didn't start with '/'
     } else {
-        result = cmd_execute_default(inp);
+        result = cmd_execute_default(window, inp);
     }
 
     return result;
@@ -1860,18 +1918,18 @@ cmd_process_input(char *inp)
 // Command execution
 
 void
-cmd_execute_connect(const char * const account)
+cmd_execute_connect(ProfWin *window, const char * const account)
 {
     GString *command = g_string_new("/connect ");
     g_string_append(command, account);
-    cmd_process_input(command->str);
+    cmd_process_input(window, command->str);
     g_string_free(command, TRUE);
 }
 
 static gboolean
-_cmd_execute(const char * const command, const char * const inp)
+_cmd_execute(ProfWin *window, const char * const command, const char * const inp)
 {
-    if (g_str_has_prefix(command, "/field") && ui_current_win_type() == WIN_MUC_CONFIG) {
+    if (g_str_has_prefix(command, "/field") && window->type == WIN_MUC_CONFIG) {
         gboolean result = FALSE;
         gchar **args = parse_args_with_freetext(inp, 1, 2, &result);
         if (!result) {
@@ -1880,7 +1938,7 @@ _cmd_execute(const char * const command, const char * const inp)
         } else {
             gchar **tokens = g_strsplit(inp, " ", 2);
             char *field = tokens[0] + 1;
-            result = cmd_form_field(field, args);
+            result = cmd_form_field(window, field, args);
             g_strfreev(tokens);
         }
 
@@ -1897,15 +1955,15 @@ _cmd_execute(const char * const command, const char * const inp)
             ui_invalid_command_usage(cmd->help.usage, cmd->setting_func);
             return TRUE;
         } else {
-            gboolean result = cmd->func(args, cmd->help);
+            gboolean result = cmd->func(window, args, cmd->help);
             g_strfreev(args);
             return result;
         }
     } else {
         gboolean ran_alias = FALSE;
-        gboolean alias_result = cmd_execute_alias(inp, &ran_alias);
+        gboolean alias_result = cmd_execute_alias(window, inp, &ran_alias);
         if (!ran_alias) {
-            return cmd_execute_default(inp);
+            return cmd_execute_default(window, inp);
         } else {
             return alias_result;
         }
@@ -1913,7 +1971,7 @@ _cmd_execute(const char * const command, const char * const inp)
 }
 
 static char *
-_cmd_complete_parameters(const char * const input)
+_cmd_complete_parameters(ProfWin *window, const char * const input)
 {
     int i;
     char *result = NULL;
@@ -1921,7 +1979,7 @@ _cmd_complete_parameters(const char * const input)
     // autocomplete boolean settings
     gchar *boolean_choices[] = { "/beep", "/intype", "/states", "/outtype",
         "/flash", "/splash", "/chlog", "/grlog", "/mouse", "/history",
-        "/vercheck", "/privileges", "/presence", "/wrap", "/carbons" };
+        "/vercheck", "/privileges", "/presence", "/wrap", "/winstidy", "/carbons", "/encwarn" };
 
     for (i = 0; i < ARRAY_SIZE(boolean_choices); i++) {
         result = autocomplete_param_with_func(input, boolean_choices[i], prefs_autocomplete_boolean_choice);
@@ -1931,8 +1989,9 @@ _cmd_complete_parameters(const char * const input)
     }
 
     // autocomplete nickname in chat rooms
-    if (ui_current_win_type() == WIN_MUC) {
-        ProfMucWin *mucwin = wins_get_current_muc();
+    if (window->type == WIN_MUC) {
+        ProfMucWin *mucwin = (ProfMucWin*)window;
+        assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
         Autocomplete nick_ac = muc_roster_ac(mucwin->roomjid);
         if (nick_ac) {
             gchar *nick_choices[] = { "/msg", "/info", "/caps", "/status", "/software" } ;
@@ -2008,6 +2067,7 @@ _cmd_complete_parameters(const char * const input)
     g_hash_table_insert(ac_funcs, "/bookmark",      _bookmark_autocomplete);
     g_hash_table_insert(ac_funcs, "/autoconnect",   _autoconnect_autocomplete);
     g_hash_table_insert(ac_funcs, "/otr",           _otr_autocomplete);
+    g_hash_table_insert(ac_funcs, "/pgp",           _pgp_autocomplete);
     g_hash_table_insert(ac_funcs, "/connect",       _connect_autocomplete);
     g_hash_table_insert(ac_funcs, "/statuses",      _statuses_autocomplete);
     g_hash_table_insert(ac_funcs, "/alias",         _alias_autocomplete);
@@ -2037,9 +2097,9 @@ _cmd_complete_parameters(const char * const input)
     }
     parsed[i] = '\0';
 
-    char * (*ac_func)(const char * const) = g_hash_table_lookup(ac_funcs, parsed);
+    char * (*ac_func)(ProfWin*, const char * const) = g_hash_table_lookup(ac_funcs, parsed);
     if (ac_func) {
-        result = ac_func(input);
+        result = ac_func(window, input);
         if (result) {
             g_hash_table_destroy(ac_funcs);
             return result;
@@ -2048,7 +2108,7 @@ _cmd_complete_parameters(const char * const input)
     g_hash_table_destroy(ac_funcs);
 
     if (g_str_has_prefix(input, "/field")) {
-        result = _form_field_autocomplete(input);
+        result = _form_field_autocomplete(window, input);
         if (result) {
             return result;
         }
@@ -2058,7 +2118,7 @@ _cmd_complete_parameters(const char * const input)
 }
 
 static char *
-_sub_autocomplete(const char * const input)
+_sub_autocomplete(ProfWin *window, const char * const input)
 {
     char *result = NULL;
     result = autocomplete_param_with_func(input, "/sub allow", presence_sub_request_find);
@@ -2078,12 +2138,11 @@ _sub_autocomplete(const char * const input)
 }
 
 static char *
-_who_autocomplete(const char * const input)
+_who_autocomplete(ProfWin *window, const char * const input)
 {
     char *result = NULL;
-    win_type_t win_type = ui_current_win_type();
 
-    if (win_type == WIN_MUC) {
+    if (window->type == WIN_MUC) {
         result = autocomplete_param_with_ac(input, "/who", who_room_ac, TRUE);
         if (result) {
             return result;
@@ -2111,7 +2170,7 @@ _who_autocomplete(const char * const input)
 }
 
 static char *
-_roster_autocomplete(const char * const input)
+_roster_autocomplete(ProfWin *window, const char * const input)
 {
     char *result = NULL;
     result = autocomplete_param_with_func(input, "/roster nick", roster_barejid_autocomplete);
@@ -2147,7 +2206,7 @@ _roster_autocomplete(const char * const input)
 }
 
 static char *
-_group_autocomplete(const char * const input)
+_group_autocomplete(ProfWin *window, const char * const input)
 {
     char *result = NULL;
     result = autocomplete_param_with_func(input, "/group show", roster_group_autocomplete);
@@ -2180,7 +2239,7 @@ _group_autocomplete(const char * const input)
 }
 
 static char *
-_bookmark_autocomplete(const char * const input)
+_bookmark_autocomplete(ProfWin *window, const char * const input)
 {
     char *found = NULL;
 
@@ -2259,7 +2318,7 @@ _bookmark_autocomplete(const char * const input)
 }
 
 static char *
-_notify_autocomplete(const char * const input)
+_notify_autocomplete(ProfWin *window, const char * const input)
 {
     int i = 0;
     char *result = NULL;
@@ -2322,7 +2381,7 @@ _notify_autocomplete(const char * const input)
 }
 
 static char *
-_autoaway_autocomplete(const char * const input)
+_autoaway_autocomplete(ProfWin *window, const char * const input)
 {
     char *result = NULL;
 
@@ -2344,7 +2403,7 @@ _autoaway_autocomplete(const char * const input)
 }
 
 static char *
-_log_autocomplete(const char * const input)
+_log_autocomplete(ProfWin *window, const char * const input)
 {
     char *result = NULL;
 
@@ -2367,7 +2426,7 @@ _log_autocomplete(const char * const input)
 }
 
 static char *
-_autoconnect_autocomplete(const char * const input)
+_autoconnect_autocomplete(ProfWin *window, const char * const input)
 {
     char *result = NULL;
 
@@ -2385,7 +2444,7 @@ _autoconnect_autocomplete(const char * const input)
 }
 
 static char *
-_otr_autocomplete(const char * const input)
+_otr_autocomplete(ProfWin *window, const char * const input)
 {
     char *found = NULL;
 
@@ -2423,13 +2482,35 @@ _otr_autocomplete(const char * const input)
         return found;
     }
 
-    found = autocomplete_param_with_func(input, "/otr warn",
-        prefs_autocomplete_boolean_choice);
+    found = autocomplete_param_with_ac(input, "/otr", otr_ac, TRUE);
     if (found) {
         return found;
     }
 
-    found = autocomplete_param_with_ac(input, "/otr", otr_ac, TRUE);
+    return NULL;
+}
+
+static char *
+_pgp_autocomplete(ProfWin *window, const char * const input)
+{
+    char *found = NULL;
+
+    found = autocomplete_param_with_func(input, "/pgp start", roster_contact_autocomplete);
+    if (found) {
+        return found;
+    }
+
+    found = autocomplete_param_with_ac(input, "/pgp log", pgp_log_ac, TRUE);
+    if (found) {
+        return found;
+    }
+
+    found = autocomplete_param_with_func(input, "/pgp setkey", roster_barejid_autocomplete);
+    if (found) {
+        return found;
+    }
+
+    found = autocomplete_param_with_ac(input, "/pgp", pgp_ac, TRUE);
     if (found) {
         return found;
     }
@@ -2438,7 +2519,7 @@ _otr_autocomplete(const char * const input)
 }
 
 static char *
-_theme_autocomplete(const char * const input)
+_theme_autocomplete(ProfWin *window, const char * const input)
 {
     char *result = NULL;
     if ((strncmp(input, "/theme load ", 12) == 0) && (strlen(input) > 12)) {
@@ -2467,13 +2548,13 @@ _theme_autocomplete(const char * const input)
 }
 
 static char *
-_resource_autocomplete(const char * const input)
+_resource_autocomplete(ProfWin *window, const char * const input)
 {
     char *found = NULL;
 
-    ProfWin *current = wins_get_current();
-    if (current && current->type == WIN_CHAT) {
-        ProfChatWin *chatwin = wins_get_current_chat();
+    if (window->type == WIN_CHAT) {
+        ProfChatWin *chatwin = (ProfChatWin*)window;
+        assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
         PContact contact = roster_get_contact(chatwin->barejid);
         if (contact) {
             Autocomplete ac = p_contact_resource_ac(contact);
@@ -2503,7 +2584,7 @@ _resource_autocomplete(const char * const input)
 }
 
 static char *
-_titlebar_autocomplete(const char * const input)
+_titlebar_autocomplete(ProfWin *window, const char * const input)
 {
     char *found = NULL;
 
@@ -2526,7 +2607,7 @@ _titlebar_autocomplete(const char * const input)
 }
 
 static char *
-_inpblock_autocomplete(const char * const input)
+_inpblock_autocomplete(ProfWin *window, const char * const input)
 {
     char *found = NULL;
 
@@ -2544,16 +2625,15 @@ _inpblock_autocomplete(const char * const input)
 }
 
 static char *
-_form_autocomplete(const char * const input)
+_form_autocomplete(ProfWin *window, const char * const input)
 {
-    ProfWin *current = wins_get_current();
-    if (current->type != WIN_MUC_CONFIG) {
+    if (window->type != WIN_MUC_CONFIG) {
         return NULL;
     }
 
     char *found = NULL;
 
-    ProfMucConfWin *confwin = (ProfMucConfWin*)current;
+    ProfMucConfWin *confwin = (ProfMucConfWin*)window;
     DataForm *form = confwin->form;
     if (form) {
         found = autocomplete_param_with_ac(input, "/form help", form->tag_ac, TRUE);
@@ -2571,16 +2651,15 @@ _form_autocomplete(const char * const input)
 }
 
 static char *
-_form_field_autocomplete(const char * const input)
+_form_field_autocomplete(ProfWin *window, const char * const input)
 {
-    ProfWin *current = wins_get_current();
-    if (current->type != WIN_MUC_CONFIG) {
+    if (window->type != WIN_MUC_CONFIG) {
         return NULL;
     }
 
     char *found = NULL;
 
-    ProfMucConfWin *confwin = (ProfMucConfWin*)current;
+    ProfMucConfWin *confwin = (ProfMucConfWin*)window;
     DataForm *form = confwin->form;
     if (form == NULL) {
         return NULL;
@@ -2642,7 +2721,7 @@ _form_field_autocomplete(const char * const input)
 }
 
 static char *
-_occupants_autocomplete(const char * const input)
+_occupants_autocomplete(ProfWin *window, const char * const input)
 {
     char *found = NULL;
 
@@ -2680,7 +2759,7 @@ _occupants_autocomplete(const char * const input)
 }
 
 static char *
-_time_autocomplete(const char * const input)
+_time_autocomplete(ProfWin *window, const char * const input)
 {
     char *found = NULL;
 
@@ -2703,12 +2782,13 @@ _time_autocomplete(const char * const input)
 }
 
 static char *
-_kick_autocomplete(const char * const input)
+_kick_autocomplete(ProfWin *window, const char * const input)
 {
     char *result = NULL;
 
-    if (ui_current_win_type() == WIN_MUC) {
-        ProfMucWin *mucwin = wins_get_current_muc();
+    if (window->type == WIN_MUC) {
+        ProfMucWin *mucwin = (ProfMucWin*)window;
+        assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
         Autocomplete nick_ac = muc_roster_ac(mucwin->roomjid);
 
         if (nick_ac) {
@@ -2723,12 +2803,13 @@ _kick_autocomplete(const char * const input)
 }
 
 static char *
-_ban_autocomplete(const char * const input)
+_ban_autocomplete(ProfWin *window, const char * const input)
 {
     char *result = NULL;
 
-    if (ui_current_win_type() == WIN_MUC) {
-        ProfMucWin *mucwin = wins_get_current_muc();
+    if (window->type == WIN_MUC) {
+        ProfMucWin *mucwin = (ProfMucWin*)window;
+        assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
         Autocomplete jid_ac = muc_roster_jid_ac(mucwin->roomjid);
 
         if (jid_ac) {
@@ -2743,12 +2824,13 @@ _ban_autocomplete(const char * const input)
 }
 
 static char *
-_affiliation_autocomplete(const char * const input)
+_affiliation_autocomplete(ProfWin *window, const char * const input)
 {
     char *result = NULL;
 
-    if (ui_current_win_type() == WIN_MUC) {
-        ProfMucWin *mucwin = wins_get_current_muc();
+    if (window->type == WIN_MUC) {
+        ProfMucWin *mucwin = (ProfMucWin*)window;
+        assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
         gboolean parse_result;
         Autocomplete jid_ac = muc_roster_jid_ac(mucwin->roomjid);
 
@@ -2790,12 +2872,13 @@ _affiliation_autocomplete(const char * const input)
 }
 
 static char *
-_role_autocomplete(const char * const input)
+_role_autocomplete(ProfWin *window, const char * const input)
 {
     char *result = NULL;
 
-    if (ui_current_win_type() == WIN_MUC) {
-        ProfMucWin *mucwin = wins_get_current_muc();
+    if (window->type == WIN_MUC) {
+        ProfMucWin *mucwin = (ProfMucWin*)window;
+        assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
         gboolean parse_result;
         Autocomplete nick_ac = muc_roster_ac(mucwin->roomjid);
 
@@ -2837,7 +2920,7 @@ _role_autocomplete(const char * const input)
 }
 
 static char *
-_statuses_autocomplete(const char * const input)
+_statuses_autocomplete(ProfWin *window, const char * const input)
 {
     char *result = NULL;
 
@@ -2865,7 +2948,7 @@ _statuses_autocomplete(const char * const input)
 }
 
 static char *
-_receipts_autocomplete(const char * const input)
+_receipts_autocomplete(ProfWin *window, const char * const input)
 {
     char *result = NULL;
 
@@ -2888,7 +2971,7 @@ _receipts_autocomplete(const char * const input)
 }
 
 static char *
-_alias_autocomplete(const char * const input)
+_alias_autocomplete(ProfWin *window, const char * const input)
 {
     char *result = NULL;
 
@@ -2906,7 +2989,7 @@ _alias_autocomplete(const char * const input)
 }
 
 static char *
-_connect_autocomplete(const char * const input)
+_connect_autocomplete(ProfWin *window, const char * const input)
 {
     char *found = NULL;
     gboolean result = FALSE;
@@ -2941,7 +3024,7 @@ _connect_autocomplete(const char * const input)
 }
 
 static char *
-_join_autocomplete(const char * const input)
+_join_autocomplete(ProfWin *window, const char * const input)
 {
     char *found = NULL;
     gboolean result = FALSE;
@@ -2976,7 +3059,7 @@ _join_autocomplete(const char * const input)
 }
 
 static char *
-_account_autocomplete(const char * const input)
+_account_autocomplete(ProfWin *window, const char * const input)
 {
     char *found = NULL;
     gboolean result = FALSE;
diff --git a/src/command/command.h b/src/command/command.h
index b500404b..e6fc7ead 100644
--- a/src/command/command.h
+++ b/src/command/command.h
@@ -38,14 +38,15 @@
 #include <glib.h>
 
 #include "xmpp/form.h"
+#include "ui/ui.h"
 
 GHashTable *commands;
 
 void cmd_init(void);
 void cmd_uninit(void);
 
-char* cmd_autocomplete(const char * const input);
-void cmd_reset_autocomplete(void);
+char* cmd_autocomplete(ProfWin *window, const char * const input);
+void cmd_reset_autocomplete(ProfWin *window);
 void cmd_autocomplete_add(char *value);
 void cmd_autocomplete_remove(char *value);
 void cmd_autocomplete_add_form_fields(DataForm *form);
@@ -53,8 +54,8 @@ void cmd_autocomplete_remove_form_fields(DataForm *form);
 void cmd_alias_add(char *value);
 void cmd_alias_remove(char *value);
 
-gboolean cmd_process_input(char *inp);
-void cmd_execute_connect(const char * const account);
+gboolean cmd_process_input(ProfWin *window, char *inp);
+void cmd_execute_connect(ProfWin *window, const char * const account);
 
 gboolean cmd_exists(char *cmd);
 
diff --git a/src/command/commands.c b/src/command/commands.c
index c5350519..7f13d5f5 100644
--- a/src/command/commands.c
+++ b/src/command/commands.c
@@ -57,6 +57,9 @@
 #ifdef HAVE_LIBOTR
 #include "otr/otr.h"
 #endif
+#ifdef HAVE_LIBGPGME
+#include "pgp/gpg.h"
+#endif
 #include "profanity.h"
 #include "tools/autocomplete.h"
 #include "tools/parser.h"
@@ -64,7 +67,7 @@
 #include "xmpp/xmpp.h"
 #include "xmpp/bookmark.h"
 #include "ui/ui.h"
-#include "ui/windows.h"
+#include "window_list.h"
 #include "event/client_events.h"
 #include "event/ui_events.h"
 
@@ -74,13 +77,13 @@ static gboolean _cmd_set_boolean_preference(gchar *arg, struct cmd_help_t help,
     const char * const display, preference_t pref);
 static void _cmd_show_filtered_help(char *heading, gchar *cmd_filter[], int filter_size);
 static gint _compare_commands(Command *a, Command *b);
-static void _who_room(gchar **args, struct cmd_help_t help);
-static void _who_roster(gchar **args, struct cmd_help_t help);
+static void _who_room(ProfWin *window, gchar **args, struct cmd_help_t help);
+static void _who_roster(ProfWin *window, gchar **args, struct cmd_help_t help);
 
 extern GHashTable *commands;
 
 gboolean
-cmd_execute_default(const char * inp)
+cmd_execute_default(ProfWin *window, const char * inp)
 {
     // handle escaped commands - treat as normal message
     if (g_str_has_prefix(inp, "//")) {
@@ -94,8 +97,7 @@ cmd_execute_default(const char * inp)
     }
 
     // handle non commands in non chat windows
-    ProfWin *current = wins_get_current();
-    if (current->type != WIN_CHAT && current->type != WIN_MUC && current->type != WIN_PRIVATE) {
+    if (window->type != WIN_CHAT && window->type != WIN_MUC && window->type != WIN_PRIVATE) {
         cons_show("Unknown command: %s", inp);
         return TRUE;
     }
@@ -106,22 +108,25 @@ cmd_execute_default(const char * inp)
         return TRUE;
     }
 
-    switch (current->type) {
+    switch (window->type) {
     case WIN_CHAT:
     {
-        ProfChatWin *chatwin = wins_get_current_chat();
+        ProfChatWin *chatwin = (ProfChatWin*)window;
+        assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
         cl_ev_send_msg(chatwin, inp);
         break;
     }
     case WIN_PRIVATE:
     {
-        ProfPrivateWin *privatewin = wins_get_current_private();
+        ProfPrivateWin *privatewin = (ProfPrivateWin*)window;
+        assert(privatewin->memcheck == PROFPRIVATEWIN_MEMCHECK);
         cl_ev_send_priv_msg(privatewin, inp);
         break;
     }
     case WIN_MUC:
     {
-        ProfMucWin *mucwin = wins_get_current_muc();
+        ProfMucWin *mucwin = (ProfMucWin*)window;
+        assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
         cl_ev_send_muc_msg(mucwin, inp);
         break;
     }
@@ -133,7 +138,7 @@ cmd_execute_default(const char * inp)
 }
 
 gboolean
-cmd_execute_alias(const char * const inp, gboolean *ran)
+cmd_execute_alias(ProfWin *window, const char * const inp, gboolean *ran)
 {
     if (inp[0] != '/') {
         ran = FALSE;
@@ -145,7 +150,7 @@ cmd_execute_alias(const char * const inp, gboolean *ran)
     free(alias);
     if (value) {
         *ran = TRUE;
-        return cmd_process_input(value);
+        return cmd_process_input(window, value);
     }
 
     *ran = FALSE;
@@ -153,7 +158,7 @@ cmd_execute_alias(const char * const inp, gboolean *ran)
 }
 
 gboolean
-cmd_connect(gchar **args, struct cmd_help_t help)
+cmd_connect(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     jabber_conn_status_t conn_status = jabber_get_connection_status();
     if ((conn_status != JABBER_DISCONNECTED) && (conn_status != JABBER_STARTED)) {
@@ -221,6 +226,7 @@ cmd_connect(gchar **args, struct cmd_help_t help)
             } else {
                 cons_show("Error evaluating password, see logs for details.");
                 g_free(lower);
+                account_free(account);
                 return TRUE;
             }
 
@@ -233,6 +239,7 @@ cmd_connect(gchar **args, struct cmd_help_t help)
         }
 
         jid = account_create_full_jid(account);
+        account_free(account);
 
     // connect with JID
     } else {
@@ -255,7 +262,7 @@ cmd_connect(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_account(gchar **args, struct cmd_help_t help)
+cmd_account(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     char *command = args[0];
 
@@ -431,7 +438,11 @@ cmd_account(gchar **args, struct cmd_help_t help)
                     }
                 } else if (strcmp(property, "resource") == 0) {
                     accounts_set_resource(account_name, value);
-                    cons_show("Updated resource for account %s: %s", account_name, value);
+                    if (jabber_get_connection_status() == JABBER_CONNECTED) {
+                        cons_show("Updated resource for account %s: %s, you will need to reconnect to pick up the change.", account_name, value);
+                    } else {
+                        cons_show("Updated resource for account %s: %s", account_name, value);
+                    }
                     cons_show("");
                 } else if (strcmp(property, "password") == 0) {
                     if(accounts_get_account(account_name)->eval_password) {
@@ -475,6 +486,10 @@ cmd_account(gchar **args, struct cmd_help_t help)
                         cons_show("Updated login status for account %s: %s", account_name, value);
                     }
                     cons_show("");
+                } else if (strcmp(property, "pgpkeyid") == 0) {
+                    accounts_set_pgp_keyid(account_name, value);
+                    cons_show("Updated PGP key ID for account %s: %s", account_name, value);
+                    cons_show("");
                 } else if (valid_resource_presence_string(property)) {
                     int intval;
                     char *err_msg = NULL;
@@ -553,6 +568,10 @@ cmd_account(gchar **args, struct cmd_help_t help)
                     accounts_clear_otr(account_name);
                     cons_show("OTR policy removed for account %s", account_name);
                     cons_show("");
+                } else if (strcmp(property, "pgpkeyid") == 0) {
+                    accounts_clear_pgp_keyid(account_name);
+                    cons_show("Removed PGP key ID for account %s", account_name);
+                    cons_show("");
                 } else {
                     cons_show("Invalid property: %s", property);
                     cons_show("");
@@ -567,7 +586,7 @@ cmd_account(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_sub(gchar **args, struct cmd_help_t help)
+cmd_sub(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     jabber_conn_status_t conn_status = jabber_get_connection_status();
 
@@ -595,14 +614,14 @@ cmd_sub(gchar **args, struct cmd_help_t help)
         return TRUE;
     }
 
-    win_type_t win_type = ui_current_win_type();
-    if ((win_type != WIN_CHAT) && (jid == NULL)) {
+    if ((window->type != WIN_CHAT) && (jid == NULL)) {
         cons_show("You must specify a contact.");
         return TRUE;
     }
 
     if (jid == NULL) {
-        ProfChatWin *chatwin = wins_get_current_chat();
+        ProfChatWin *chatwin = (ProfChatWin*)window;
+        assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
         jid = chatwin->barejid;
     }
 
@@ -623,13 +642,13 @@ cmd_sub(gchar **args, struct cmd_help_t help)
     } else if (strcmp(subcmd, "show") == 0) {
         PContact contact = roster_get_contact(jidp->barejid);
         if ((contact == NULL) || (p_contact_subscription(contact) == NULL)) {
-            if (win_type == WIN_CHAT) {
+            if (window->type == WIN_CHAT) {
                 ui_current_print_line("No subscription information for %s.", jidp->barejid);
             } else {
                 cons_show("No subscription information for %s.", jidp->barejid);
             }
         } else {
-            if (win_type == WIN_CHAT) {
+            if (window->type == WIN_CHAT) {
                 if (p_contact_pending_out(contact)) {
                     ui_current_print_line("%s subscription status: %s, request pending.",
                         jidp->barejid, p_contact_subscription(contact));
@@ -657,7 +676,7 @@ cmd_sub(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_disconnect(gchar **args, struct cmd_help_t help)
+cmd_disconnect(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     if (jabber_get_connection_status() == JABBER_CONNECTED) {
         char *jid = strdup(jabber_get_fulljid());
@@ -667,6 +686,9 @@ cmd_disconnect(gchar **args, struct cmd_help_t help)
         muc_invites_clear();
         chat_sessions_clear();
         ui_disconnected();
+#ifdef HAVE_LIBGPGME
+        p_gpg_on_disconnect();
+#endif
         free(jid);
     } else {
         cons_show("You are not currently connected.");
@@ -676,7 +698,7 @@ cmd_disconnect(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_quit(gchar **args, struct cmd_help_t help)
+cmd_quit(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     log_info("Profanity is shutting down...");
     exit(0);
@@ -684,12 +706,16 @@ cmd_quit(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_wins(gchar **args, struct cmd_help_t help)
+cmd_wins(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     if (args[0] == NULL) {
         cons_show_wins();
     } else if (strcmp(args[0], "tidy") == 0) {
-        ui_tidy_wins();
+        if (ui_tidy_wins()) {
+            cons_show("Windows tidied.");
+        } else {
+            cons_show("No tidy needed.");
+        }
     } else if (strcmp(args[0], "prune") == 0) {
         ui_prune_wins();
     } else if (strcmp(args[0], "swap") == 0) {
@@ -719,22 +745,34 @@ cmd_wins(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_win(gchar **args, struct cmd_help_t help)
+cmd_winstidy(ProfWin *window, gchar **args, struct cmd_help_t help)
+{
+    gboolean result = _cmd_set_boolean_preference(args[0], help, "Wins Auto Tidy", PREF_WINS_AUTO_TIDY);
+
+    if (result && g_strcmp0(args[0], "on") == 0) {
+        ui_tidy_wins();
+    }
+
+    return result;
+}
+
+gboolean
+cmd_win(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     int num = atoi(args[0]);
 
-    ProfWin *window = wins_get_by_num(num);
-    if (!window) {
+    ProfWin *focuswin = wins_get_by_num(num);
+    if (!focuswin) {
         cons_show("Window %d does not exist.", num);
     } else {
-        ui_ev_focus_win(window);
+        ui_ev_focus_win(focuswin);
     }
 
     return TRUE;
 }
 
 gboolean
-cmd_help(gchar **args, struct cmd_help_t help)
+cmd_help(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     int num_args = g_strv_length(args);
     if (num_args == 0) {
@@ -804,7 +842,7 @@ cmd_help(gchar **args, struct cmd_help_t help)
             "/carbons", "/chlog", "/flash", "/gone", "/grlog", "/history", "/intype",
             "/log", "/mouse", "/notify", "/outtype", "/prefs", "/priority",
             "/reconnect", "/roster", "/splash", "/states", "/statuses", "/theme",
-            "/titlebar", "/vercheck", "/privileges", "/occupants", "/presence", "/wrap" };
+            "/titlebar", "/vercheck", "/privileges", "/occupants", "/presence", "/wrap", "/winstidy" };
         _cmd_show_filtered_help("Settings commands", filter, ARRAY_SIZE(filter));
 
     } else if (strcmp(args[0], "navigation") == 0) {
@@ -836,14 +874,14 @@ cmd_help(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_about(gchar **args, struct cmd_help_t help)
+cmd_about(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     ui_about();
     return TRUE;
 }
 
 gboolean
-cmd_prefs(gchar **args, struct cmd_help_t help)
+cmd_prefs(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     if (args[0] == NULL) {
         cons_prefs();
@@ -876,6 +914,10 @@ cmd_prefs(gchar **args, struct cmd_help_t help)
         cons_show("");
         cons_show_otr_prefs();
         cons_show("");
+    } else if (strcmp(args[0], "pgp") == 0) {
+        cons_show("");
+        cons_show_pgp_prefs();
+        cons_show("");
     } else {
         cons_show("Usage: %s", help.usage);
     }
@@ -884,7 +926,7 @@ cmd_prefs(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_theme(gchar **args, struct cmd_help_t help)
+cmd_theme(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     // list themes
     if (g_strcmp0(args[0], "list") == 0) {
@@ -926,7 +968,7 @@ cmd_theme(gchar **args, struct cmd_help_t help)
 }
 
 static void
-_who_room(gchar **args, struct cmd_help_t help)
+_who_room(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     if ((g_strv_length(args) == 2) && args[1]) {
         cons_show("Argument group is not applicable to chat rooms.");
@@ -954,7 +996,8 @@ _who_room(gchar **args, struct cmd_help_t help)
         return;
     }
 
-    ProfMucWin *mucwin = wins_get_current_muc();
+    ProfMucWin *mucwin = (ProfMucWin*)window;
+    assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
 
     // presence filter
     if (args[0] == NULL ||
@@ -1055,7 +1098,7 @@ _who_room(gchar **args, struct cmd_help_t help)
 }
 
 static void
-_who_roster(gchar **args, struct cmd_help_t help)
+_who_roster(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     char *presence = args[0];
 
@@ -1268,20 +1311,19 @@ _who_roster(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_who(gchar **args, struct cmd_help_t help)
+cmd_who(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     jabber_conn_status_t conn_status = jabber_get_connection_status();
-    win_type_t win_type = ui_current_win_type();
 
     if (conn_status != JABBER_CONNECTED) {
         cons_show("You are not currently connected.");
-    } else if (win_type == WIN_MUC) {
-        _who_room(args, help);
+    } else if (window->type == WIN_MUC) {
+        _who_room(window, args, help);
     } else {
-        _who_roster(args, help);
+        _who_roster(window, args, help);
     }
 
-    if (win_type != WIN_CONSOLE && win_type != WIN_MUC) {
+    if (window->type != WIN_CONSOLE && window->type != WIN_MUC) {
         ui_statusbar_new(1);
     }
 
@@ -1289,13 +1331,12 @@ cmd_who(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_msg(gchar **args, struct cmd_help_t help)
+cmd_msg(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     char *usr = args[0];
     char *msg = args[1];
 
     jabber_conn_status_t conn_status = jabber_get_connection_status();
-    win_type_t win_type = ui_current_win_type();
 
     if (conn_status != JABBER_CONNECTED) {
         cons_show("You are not currently connected.");
@@ -1303,8 +1344,9 @@ cmd_msg(gchar **args, struct cmd_help_t help)
     }
 
     // send private message when in MUC room
-    if (win_type == WIN_MUC) {
-        ProfMucWin *mucwin = wins_get_current_muc();
+    if (window->type == WIN_MUC) {
+        ProfMucWin *mucwin = (ProfMucWin*)window;
+        assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
         if (muc_roster_contains_nick(mucwin->roomjid, usr)) {
             GString *full_jid = g_string_new(mucwin->roomjid);
             g_string_append(full_jid, "/");
@@ -1356,7 +1398,7 @@ cmd_msg(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_group(gchar **args, struct cmd_help_t help)
+cmd_group(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     jabber_conn_status_t conn_status = jabber_get_connection_status();
 
@@ -1463,17 +1505,17 @@ cmd_group(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_roster(gchar **args, struct cmd_help_t help)
+cmd_roster(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     jabber_conn_status_t conn_status = jabber_get_connection_status();
 
-    if (conn_status != JABBER_CONNECTED) {
-        cons_show("You are not currently connected.");
-        return TRUE;
-    }
-
     // show roster
     if (args[0] == NULL) {
+        if (conn_status != JABBER_CONNECTED) {
+            cons_show("You are not currently connected.");
+            return TRUE;
+        }
+
         GSList *list = roster_get_contacts();
         cons_show_roster(list);
         g_slist_free(list);
@@ -1481,6 +1523,11 @@ cmd_roster(gchar **args, struct cmd_help_t help)
 
     // show roster, only online contacts
     } else if(g_strcmp0(args[0], "online") == 0){
+        if (conn_status != JABBER_CONNECTED) {
+            cons_show("You are not currently connected.");
+            return TRUE;
+        }
+
         GSList *list = roster_get_contacts_online();
         cons_show_roster(list);
         g_slist_free(list);
@@ -1498,7 +1545,7 @@ cmd_roster(gchar **args, struct cmd_help_t help)
         if (res) {
             prefs_set_roster_size(intval);
             cons_show("Roster screen size set to: %d%%", intval);
-            if (prefs_get_boolean(PREF_ROSTER)) {
+            if (conn_status == JABBER_CONNECTED && prefs_get_boolean(PREF_ROSTER)) {
                 wins_resize_all();
             }
             return TRUE;
@@ -1513,17 +1560,23 @@ cmd_roster(gchar **args, struct cmd_help_t help)
         if (args[1] == NULL) {
             cons_show("Roster enabled.");
             prefs_set_boolean(PREF_ROSTER, TRUE);
-            ui_show_roster();
+            if (conn_status == JABBER_CONNECTED) {
+                ui_show_roster();
+            }
             return TRUE;
         } else if (g_strcmp0(args[1], "offline") == 0) {
             cons_show("Roster offline enabled");
             prefs_set_boolean(PREF_ROSTER_OFFLINE, TRUE);
-            rosterwin_roster();
+            if (conn_status == JABBER_CONNECTED) {
+                rosterwin_roster();
+            }
             return TRUE;
         } else if (g_strcmp0(args[1], "resource") == 0) {
             cons_show("Roster resource enabled");
             prefs_set_boolean(PREF_ROSTER_RESOURCE, TRUE);
-            rosterwin_roster();
+            if (conn_status == JABBER_CONNECTED) {
+                rosterwin_roster();
+            }
             return TRUE;
         } else {
             cons_show("Usage: %s", help.usage);
@@ -1533,17 +1586,23 @@ cmd_roster(gchar **args, struct cmd_help_t help)
         if (args[1] == NULL) {
             cons_show("Roster disabled.");
             prefs_set_boolean(PREF_ROSTER, FALSE);
-            ui_hide_roster();
+            if (conn_status == JABBER_CONNECTED) {
+                ui_hide_roster();
+            }
             return TRUE;
         } else if (g_strcmp0(args[1], "offline") == 0) {
             cons_show("Roster offline disabled");
             prefs_set_boolean(PREF_ROSTER_OFFLINE, FALSE);
-            rosterwin_roster();
+            if (conn_status == JABBER_CONNECTED) {
+                rosterwin_roster();
+            }
             return TRUE;
         } else if (g_strcmp0(args[1], "resource") == 0) {
             cons_show("Roster resource disabled");
             prefs_set_boolean(PREF_ROSTER_RESOURCE, FALSE);
-            rosterwin_roster();
+            if (conn_status == JABBER_CONNECTED) {
+                rosterwin_roster();
+            }
             return TRUE;
         } else {
             cons_show("Usage: %s", help.usage);
@@ -1554,17 +1613,23 @@ cmd_roster(gchar **args, struct cmd_help_t help)
         if (g_strcmp0(args[1], "group") == 0) {
             cons_show("Grouping roster by roster group");
             prefs_set_string(PREF_ROSTER_BY, "group");
-            rosterwin_roster();
+            if (conn_status == JABBER_CONNECTED) {
+                rosterwin_roster();
+            }
             return TRUE;
         } else if (g_strcmp0(args[1], "presence") == 0) {
             cons_show("Grouping roster by presence");
             prefs_set_string(PREF_ROSTER_BY, "presence");
-            rosterwin_roster();
+            if (conn_status == JABBER_CONNECTED) {
+                rosterwin_roster();
+            }
             return TRUE;
         } else if (g_strcmp0(args[1], "none") == 0) {
             cons_show("Roster grouping disabled");
             prefs_set_string(PREF_ROSTER_BY, "none");
-            rosterwin_roster();
+            if (conn_status == JABBER_CONNECTED) {
+                rosterwin_roster();
+            }
             return TRUE;
         } else {
             cons_show("Usage: %s", help.usage);
@@ -1572,6 +1637,10 @@ cmd_roster(gchar **args, struct cmd_help_t help)
         }
     // add contact
     } else if (strcmp(args[0], "add") == 0) {
+        if (conn_status != JABBER_CONNECTED) {
+            cons_show("You are not currently connected.");
+            return TRUE;
+        }
         char *jid = args[1];
         if (jid == NULL) {
             cons_show("Usage: %s", help.usage);
@@ -1583,6 +1652,10 @@ cmd_roster(gchar **args, struct cmd_help_t help)
 
     // remove contact
     } else if (strcmp(args[0], "remove") == 0) {
+        if (conn_status != JABBER_CONNECTED) {
+            cons_show("You are not currently connected.");
+            return TRUE;
+        }
         char *jid = args[1];
         if (jid == NULL) {
             cons_show("Usage: %s", help.usage);
@@ -1591,8 +1664,29 @@ cmd_roster(gchar **args, struct cmd_help_t help)
         }
         return TRUE;
 
+    } else if (strcmp(args[0], "empty") == 0) {
+        if (conn_status != JABBER_CONNECTED) {
+            cons_show("You are not currently connected.");
+            return TRUE;
+        }
+
+        GSList *all = roster_get_contacts();
+        GSList *curr = all;
+        while (curr) {
+            PContact contact = curr->data;
+            roster_send_remove(p_contact_barejid(contact));
+            curr = g_slist_next(curr);
+        }
+
+        g_slist_free(all);
+        return TRUE;
+
     // change nickname
     } else if (strcmp(args[0], "nick") == 0) {
+        if (conn_status != JABBER_CONNECTED) {
+            cons_show("You are not currently connected.");
+            return TRUE;
+        }
         char *jid = args[1];
         if (jid == NULL) {
             cons_show("Usage: %s", help.usage);
@@ -1623,6 +1717,10 @@ cmd_roster(gchar **args, struct cmd_help_t help)
 
     // remove nickname
     } else if (strcmp(args[0], "clearnick") == 0) {
+        if (conn_status != JABBER_CONNECTED) {
+            cons_show("You are not currently connected.");
+            return TRUE;
+        }
         char *jid = args[1];
         if (jid == NULL) {
             cons_show("Usage: %s", help.usage);
@@ -1651,7 +1749,7 @@ cmd_roster(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_resource(gchar **args, struct cmd_help_t help)
+cmd_resource(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     char *cmd = args[0];
     char *setting = NULL;
@@ -1673,12 +1771,11 @@ cmd_resource(gchar **args, struct cmd_help_t help)
         }
     }
 
-    ProfWin *current = wins_get_current();
-    if (current->type != WIN_CHAT) {
+    if (window->type != WIN_CHAT) {
         cons_show("Resource can only be changed in chat windows.");
         return TRUE;
     }
-    ProfChatWin *chatwin = (ProfChatWin*)current;
+    ProfChatWin *chatwin = (ProfChatWin*)window;
 
     if (g_strcmp0(cmd, "set") == 0) {
         char *resource = args[1];
@@ -1724,24 +1821,23 @@ cmd_resource(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_status(gchar **args, struct cmd_help_t help)
+cmd_status(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     char *usr = args[0];
 
     jabber_conn_status_t conn_status = jabber_get_connection_status();
-    win_type_t win_type = ui_current_win_type();
 
     if (conn_status != JABBER_CONNECTED) {
         cons_show("You are not currently connected.");
         return TRUE;
     }
 
-    switch (win_type)
+    switch (window->type)
     {
         case WIN_MUC:
             if (usr) {
-                ProfMucWin *mucwin = wins_get_current_muc();
-                ProfWin *window = (ProfWin*) mucwin;
+                ProfMucWin *mucwin = (ProfMucWin*)window;
+                assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
                 Occupant *occupant = muc_roster_item(mucwin->roomjid, usr);
                 if (occupant) {
                     win_show_occupant(window, occupant);
@@ -1756,8 +1852,8 @@ cmd_status(gchar **args, struct cmd_help_t help)
             if (usr) {
                 ui_current_print_line("No parameter required when in chat.");
             } else {
-                ProfChatWin *chatwin = wins_get_current_chat();
-                ProfWin *window = (ProfWin*) chatwin;
+                ProfChatWin *chatwin = (ProfChatWin*)window;
+                assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
                 PContact pcontact = roster_get_contact(chatwin->barejid);
                 if (pcontact) {
                     win_show_contact(window, pcontact);
@@ -1770,8 +1866,8 @@ cmd_status(gchar **args, struct cmd_help_t help)
             if (usr) {
                 ui_current_print_line("No parameter required when in chat.");
             } else {
-                ProfPrivateWin *privatewin = wins_get_current_private();
-                ProfWin *window = (ProfWin*) privatewin;
+                ProfPrivateWin *privatewin = (ProfPrivateWin*)window;
+                assert(privatewin->memcheck == PROFPRIVATEWIN_MEMCHECK);
                 Jid *jid = jid_create(privatewin->fulljid);
                 Occupant *occupant = muc_roster_item(jid->barejid, jid->resourcepart);
                 if (occupant) {
@@ -1801,12 +1897,11 @@ cmd_status(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_info(gchar **args, struct cmd_help_t help)
+cmd_info(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     char *usr = args[0];
 
     jabber_conn_status_t conn_status = jabber_get_connection_status();
-    win_type_t win_type = ui_current_win_type();
     PContact pcontact = NULL;
 
     if (conn_status != JABBER_CONNECTED) {
@@ -1814,20 +1909,21 @@ cmd_info(gchar **args, struct cmd_help_t help)
         return TRUE;
     }
 
-    switch (win_type)
+    switch (window->type)
     {
         case WIN_MUC:
             if (usr) {
-                ProfMucWin *mucwin = wins_get_current_muc();
+                ProfMucWin *mucwin = (ProfMucWin*)window;
+                assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
                 Occupant *occupant = muc_roster_item(mucwin->roomjid, usr);
                 if (occupant) {
-                    ProfWin *current = wins_get_current();
-                    win_show_occupant_info(current, mucwin->roomjid, occupant);
+                    win_show_occupant_info(window, mucwin->roomjid, occupant);
                 } else {
                     ui_current_print_line("No such occupant \"%s\" in room.", usr);
                 }
             } else {
-                ProfMucWin *mucwin = wins_get_current_muc();
+                ProfMucWin *mucwin = (ProfMucWin*)window;
+                assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
                 iq_room_info_request(mucwin->roomjid, TRUE);
                 ui_show_room_info(mucwin);
                 return TRUE;
@@ -1837,8 +1933,8 @@ cmd_info(gchar **args, struct cmd_help_t help)
             if (usr) {
                 ui_current_print_line("No parameter required when in chat.");
             } else {
-                ProfChatWin *chatwin = wins_get_current_chat();
-                ProfWin *window = (ProfWin*) chatwin;
+                ProfChatWin *chatwin = (ProfChatWin*)window;
+                assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
                 PContact pcontact = roster_get_contact(chatwin->barejid);
                 if (pcontact) {
                     win_show_info(window, pcontact);
@@ -1851,8 +1947,8 @@ cmd_info(gchar **args, struct cmd_help_t help)
             if (usr) {
                 ui_current_print_line("No parameter required when in chat.");
             } else {
-                ProfPrivateWin *privatewin = wins_get_current_private();
-                ProfWin *window = (ProfWin*) privatewin;
+                ProfPrivateWin *privatewin = (ProfPrivateWin*)window;
+                assert(privatewin->memcheck == PROFPRIVATEWIN_MEMCHECK);
                 Jid *jid = jid_create(privatewin->fulljid);
                 Occupant *occupant = muc_roster_item(jid->barejid, jid->resourcepart);
                 if (occupant) {
@@ -1887,10 +1983,9 @@ cmd_info(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_caps(gchar **args, struct cmd_help_t help)
+cmd_caps(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     jabber_conn_status_t conn_status = jabber_get_connection_status();
-    win_type_t win_type = ui_current_win_type();
     PContact pcontact = NULL;
     Occupant *occupant = NULL;
 
@@ -1899,11 +1994,12 @@ cmd_caps(gchar **args, struct cmd_help_t help)
         return TRUE;
     }
 
-    switch (win_type)
+    switch (window->type)
     {
         case WIN_MUC:
             if (args[0]) {
-                ProfMucWin *mucwin = wins_get_current_muc();
+                ProfMucWin *mucwin = (ProfMucWin*)window;
+                assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
                 occupant = muc_roster_item(mucwin->roomjid, args[0]);
                 if (occupant) {
                     Jid *jidp = jid_create_from_bare_and_resource(mucwin->roomjid, args[0]);
@@ -1945,7 +2041,8 @@ cmd_caps(gchar **args, struct cmd_help_t help)
             if (args[0]) {
                 cons_show("No parameter needed to /caps when in private chat.");
             } else {
-                ProfPrivateWin *privatewin = wins_get_current_private();
+                ProfPrivateWin *privatewin = (ProfPrivateWin*)window;
+                assert(privatewin->memcheck == PROFPRIVATEWIN_MEMCHECK);
                 Jid *jid = jid_create(privatewin->fulljid);
                 if (jid) {
                     occupant = muc_roster_item(jid->barejid, jid->resourcepart);
@@ -1963,10 +2060,9 @@ cmd_caps(gchar **args, struct cmd_help_t help)
 
 
 gboolean
-cmd_software(gchar **args, struct cmd_help_t help)
+cmd_software(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     jabber_conn_status_t conn_status = jabber_get_connection_status();
-    win_type_t win_type = ui_current_win_type();
     Occupant *occupant = NULL;
 
     if (conn_status != JABBER_CONNECTED) {
@@ -1974,11 +2070,12 @@ cmd_software(gchar **args, struct cmd_help_t help)
         return TRUE;
     }
 
-    switch (win_type)
+    switch (window->type)
     {
         case WIN_MUC:
             if (args[0]) {
-                ProfMucWin *mucwin = wins_get_current_muc();
+                ProfMucWin *mucwin = (ProfMucWin*)window;
+                assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
                 occupant = muc_roster_item(mucwin->roomjid, args[0]);
                 if (occupant) {
                     Jid *jid = jid_create_from_bare_and_resource(mucwin->roomjid, args[0]);
@@ -2010,7 +2107,8 @@ cmd_software(gchar **args, struct cmd_help_t help)
             if (args[0]) {
                 cons_show("No parameter needed to /software when in private chat.");
             } else {
-                ProfPrivateWin *privatewin = wins_get_current_private();
+                ProfPrivateWin *privatewin = (ProfPrivateWin*)window;
+                assert(privatewin->memcheck == PROFPRIVATEWIN_MEMCHECK);
                 iq_send_software_version(privatewin->fulljid);
             }
             break;
@@ -2022,7 +2120,7 @@ cmd_software(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_join(gchar **args, struct cmd_help_t help)
+cmd_join(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     jabber_conn_status_t conn_status = jabber_get_connection_status();
     if (conn_status != JABBER_CONNECTED) {
@@ -2086,6 +2184,7 @@ cmd_join(gchar **args, struct cmd_help_t help)
     if (!parsed) {
         cons_show("Usage: %s", help.usage);
         cons_show("");
+        jid_destroy(room_arg);
         return TRUE;
     }
 
@@ -2119,7 +2218,7 @@ cmd_join(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_invite(gchar **args, struct cmd_help_t help)
+cmd_invite(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     char *contact = args[0];
     char *reason = args[1];
@@ -2130,7 +2229,7 @@ cmd_invite(gchar **args, struct cmd_help_t help)
         return TRUE;
     }
 
-    if (ui_current_win_type() != WIN_MUC) {
+    if (window->type != WIN_MUC) {
         cons_show("You must be in a chat room to send an invite.");
         return TRUE;
     }
@@ -2140,7 +2239,8 @@ cmd_invite(gchar **args, struct cmd_help_t help)
         usr_jid = contact;
     }
 
-    ProfMucWin *mucwin = wins_get_current_muc();
+    ProfMucWin *mucwin = (ProfMucWin*)window;
+    assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
     message_send_invite(mucwin->roomjid, usr_jid, reason);
     if (reason) {
         cons_show("Room invite sent, contact: %s, room: %s, reason: \"%s\".",
@@ -2154,7 +2254,7 @@ cmd_invite(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_invites(gchar **args, struct cmd_help_t help)
+cmd_invites(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     GSList *invites = muc_invites();
     cons_show_room_invites(invites);
@@ -2163,7 +2263,7 @@ cmd_invites(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_decline(gchar **args, struct cmd_help_t help)
+cmd_decline(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     if (!muc_invites_contain(args[0])) {
         cons_show("No such invite exists.");
@@ -2176,14 +2276,13 @@ cmd_decline(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_form_field(char *tag, gchar **args)
+cmd_form_field(ProfWin *window, char *tag, gchar **args)
 {
-    ProfWin *current = wins_get_current();
-    if (current->type != WIN_MUC_CONFIG) {
+    if (window->type != WIN_MUC_CONFIG) {
         return TRUE;
     }
 
-    ProfMucConfWin *confwin = (ProfMucConfWin*)current;
+    ProfMucConfWin *confwin = (ProfMucConfWin*)window;
     DataForm *form = confwin->form;
     if (form) {
         if (!form_tag_exists(form, tag)) {
@@ -2204,11 +2303,11 @@ cmd_form_field(char *tag, gchar **args)
             if (g_strcmp0(value, "on") == 0) {
                 form_set_value(form, tag, "1");
                 ui_current_print_line("Field updated...");
-                ui_show_form_field(current, form, tag);
+                ui_show_form_field(window, form, tag);
             } else if (g_strcmp0(value, "off") == 0) {
                 form_set_value(form, tag, "0");
                 ui_current_print_line("Field updated...");
-                ui_show_form_field(current, form, tag);
+                ui_show_form_field(window, form, tag);
             } else {
                 ui_current_print_line("Invalid command, usage:");
                 ui_show_form_field_help(confwin, tag);
@@ -2227,7 +2326,7 @@ cmd_form_field(char *tag, gchar **args)
             } else {
                 form_set_value(form, tag, value);
                 ui_current_print_line("Field updated...");
-                ui_show_form_field(current, form, tag);
+                ui_show_form_field(window, form, tag);
             }
             break;
         case FIELD_LIST_SINGLE:
@@ -2239,7 +2338,7 @@ cmd_form_field(char *tag, gchar **args)
             } else {
                 form_set_value(form, tag, value);
                 ui_current_print_line("Field updated...");
-                ui_show_form_field(current, form, tag);
+                ui_show_form_field(window, form, tag);
             }
             break;
 
@@ -2263,7 +2362,7 @@ cmd_form_field(char *tag, gchar **args)
             if (g_strcmp0(cmd, "add") == 0) {
                 form_add_value(form, tag, value);
                 ui_current_print_line("Field updated...");
-                ui_show_form_field(current, form, tag);
+                ui_show_form_field(window, form, tag);
                 break;
             }
             if (g_strcmp0(args[0], "remove") == 0) {
@@ -2291,7 +2390,7 @@ cmd_form_field(char *tag, gchar **args)
                 removed = form_remove_text_multi_value(form, tag, index);
                 if (removed) {
                     ui_current_print_line("Field updated...");
-                    ui_show_form_field(current, form, tag);
+                    ui_show_form_field(window, form, tag);
                 } else {
                     ui_current_print_line("Could not remove %s from %s", value, tag);
                 }
@@ -2320,7 +2419,7 @@ cmd_form_field(char *tag, gchar **args)
                     added = form_add_unique_value(form, tag, value);
                     if (added) {
                         ui_current_print_line("Field updated...");
-                        ui_show_form_field(current, form, tag);
+                        ui_show_form_field(window, form, tag);
                     } else {
                         ui_current_print_line("Value %s already selected for %s", value, tag);
                     }
@@ -2337,7 +2436,7 @@ cmd_form_field(char *tag, gchar **args)
                     removed = form_remove_value(form, tag, value);
                     if (removed) {
                         ui_current_print_line("Field updated...");
-                        ui_show_form_field(current, form, tag);
+                        ui_show_form_field(window, form, tag);
                     } else {
                         ui_current_print_line("Value %s is not currently set for %s", value, tag);
                     }
@@ -2369,7 +2468,7 @@ cmd_form_field(char *tag, gchar **args)
                 added = form_add_unique_value(form, tag, value);
                 if (added) {
                     ui_current_print_line("Field updated...");
-                    ui_show_form_field(current, form, tag);
+                    ui_show_form_field(window, form, tag);
                 } else {
                     ui_current_print_line("JID %s already exists in %s", value, tag);
                 }
@@ -2379,7 +2478,7 @@ cmd_form_field(char *tag, gchar **args)
                 removed = form_remove_value(form, tag, value);
                 if (removed) {
                     ui_current_print_line("Field updated...");
-                    ui_show_form_field(current, form, tag);
+                    ui_show_form_field(window, form, tag);
                 } else {
                     ui_current_print_line("Field %s does not contain %s", tag, value);
                 }
@@ -2395,7 +2494,7 @@ cmd_form_field(char *tag, gchar **args)
 }
 
 gboolean
-cmd_form(gchar **args, struct cmd_help_t help)
+cmd_form(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     jabber_conn_status_t conn_status = jabber_get_connection_status();
 
@@ -2404,8 +2503,7 @@ cmd_form(gchar **args, struct cmd_help_t help)
         return TRUE;
     }
 
-    win_type_t win_type = ui_current_win_type();
-    if (win_type != WIN_MUC_CONFIG) {
+    if (window->type != WIN_MUC_CONFIG) {
         cons_show("Command '/form' does not apply to this window.");
         return TRUE;
     }
@@ -2418,7 +2516,8 @@ cmd_form(gchar **args, struct cmd_help_t help)
         return TRUE;
     }
 
-    ProfMucConfWin *confwin = wins_get_current_muc_conf();
+    ProfMucConfWin *confwin = (ProfMucConfWin*)window;
+    assert(confwin->memcheck == PROFCONFWIN_MEMCHECK);
 
     if (g_strcmp0(args[0], "show") == 0) {
         ui_show_form(confwin);
@@ -2447,7 +2546,6 @@ cmd_form(gchar **args, struct cmd_help_t help)
 
     if (g_strcmp0(args[0], "submit") == 0) {
         iq_submit_room_config(confwin->roomjid, confwin->form);
-
     }
 
     if (g_strcmp0(args[0], "cancel") == 0) {
@@ -2470,7 +2568,7 @@ cmd_form(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_kick(gchar **args, struct cmd_help_t help)
+cmd_kick(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     jabber_conn_status_t conn_status = jabber_get_connection_status();
 
@@ -2479,13 +2577,13 @@ cmd_kick(gchar **args, struct cmd_help_t help)
         return TRUE;
     }
 
-    win_type_t win_type = ui_current_win_type();
-    if (win_type != WIN_MUC) {
+    if (window->type != WIN_MUC) {
         cons_show("Command '/kick' only applies in chat rooms.");
         return TRUE;
     }
 
-    ProfMucWin *mucwin = wins_get_current_muc();
+    ProfMucWin *mucwin = (ProfMucWin*)window;
+    assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
 
     char *nick = args[0];
     if (nick) {
@@ -2503,7 +2601,7 @@ cmd_kick(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_ban(gchar **args, struct cmd_help_t help)
+cmd_ban(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     jabber_conn_status_t conn_status = jabber_get_connection_status();
 
@@ -2512,13 +2610,13 @@ cmd_ban(gchar **args, struct cmd_help_t help)
         return TRUE;
     }
 
-    win_type_t win_type = ui_current_win_type();
-    if (win_type != WIN_MUC) {
+    if (window->type != WIN_MUC) {
         cons_show("Command '/ban' only applies in chat rooms.");
         return TRUE;
     }
 
-    ProfMucWin *mucwin = wins_get_current_muc();
+    ProfMucWin *mucwin = (ProfMucWin*)window;
+    assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
 
     char *jid = args[0];
     if (jid) {
@@ -2531,7 +2629,7 @@ cmd_ban(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_subject(gchar **args, struct cmd_help_t help)
+cmd_subject(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     jabber_conn_status_t conn_status = jabber_get_connection_status();
 
@@ -2540,14 +2638,13 @@ cmd_subject(gchar **args, struct cmd_help_t help)
         return TRUE;
     }
 
-    win_type_t win_type = ui_current_win_type();
-    if (win_type != WIN_MUC) {
+    if (window->type != WIN_MUC) {
         cons_show("Command '/room' does not apply to this window.");
         return TRUE;
     }
 
-    ProfMucWin *mucwin = wins_get_current_muc();
-    ProfWin *window = (ProfWin*) mucwin;
+    ProfMucWin *mucwin = (ProfMucWin*)window;
+    assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
 
     if (args[0] == NULL) {
         char *subject = muc_subject(mucwin->roomjid);
@@ -2579,7 +2676,7 @@ cmd_subject(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_affiliation(gchar **args, struct cmd_help_t help)
+cmd_affiliation(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     jabber_conn_status_t conn_status = jabber_get_connection_status();
 
@@ -2588,8 +2685,7 @@ cmd_affiliation(gchar **args, struct cmd_help_t help)
         return TRUE;
     }
 
-    win_type_t win_type = ui_current_win_type();
-    if (win_type != WIN_MUC) {
+    if (window->type != WIN_MUC) {
         cons_show("Command '/affiliation' does not apply to this window.");
         return TRUE;
     }
@@ -2611,7 +2707,8 @@ cmd_affiliation(gchar **args, struct cmd_help_t help)
         return TRUE;
     }
 
-    ProfMucWin *mucwin = wins_get_current_muc();
+    ProfMucWin *mucwin = (ProfMucWin*)window;
+    assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
 
     if (g_strcmp0(cmd, "list") == 0) {
         if (!affiliation) {
@@ -2649,7 +2746,7 @@ cmd_affiliation(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_role(gchar **args, struct cmd_help_t help)
+cmd_role(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     jabber_conn_status_t conn_status = jabber_get_connection_status();
 
@@ -2658,8 +2755,7 @@ cmd_role(gchar **args, struct cmd_help_t help)
         return TRUE;
     }
 
-    win_type_t win_type = ui_current_win_type();
-    if (win_type != WIN_MUC) {
+    if (window->type != WIN_MUC) {
         cons_show("Command '/role' does not apply to this window.");
         return TRUE;
     }
@@ -2680,7 +2776,8 @@ cmd_role(gchar **args, struct cmd_help_t help)
         return TRUE;
     }
 
-    ProfMucWin *mucwin = wins_get_current_muc();
+    ProfMucWin *mucwin = (ProfMucWin*)window;
+    assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
 
     if (g_strcmp0(cmd, "list") == 0) {
         if (!role) {
@@ -2717,7 +2814,7 @@ cmd_role(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_room(gchar **args, struct cmd_help_t help)
+cmd_room(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     jabber_conn_status_t conn_status = jabber_get_connection_status();
 
@@ -2726,8 +2823,7 @@ cmd_room(gchar **args, struct cmd_help_t help)
         return TRUE;
     }
 
-    win_type_t win_type = ui_current_win_type();
-    if (win_type != WIN_MUC) {
+    if (window->type != WIN_MUC) {
         cons_show("Command '/room' does not apply to this window.");
         return TRUE;
     }
@@ -2739,8 +2835,8 @@ cmd_room(gchar **args, struct cmd_help_t help)
         return TRUE;
     }
 
-    ProfMucWin *mucwin = wins_get_current_muc();
-    ProfWin *window = (ProfWin*) mucwin;
+    ProfMucWin *mucwin = (ProfMucWin*)window;
+    assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
     int num = wins_get_num(window);
 
     int ui_index = num;
@@ -2781,7 +2877,7 @@ cmd_room(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_occupants(gchar **args, struct cmd_help_t help)
+cmd_occupants(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     jabber_conn_status_t conn_status = jabber_get_connection_status();
 
@@ -2836,13 +2932,13 @@ cmd_occupants(gchar **args, struct cmd_help_t help)
         }
     }
 
-    win_type_t win_type = ui_current_win_type();
-    if (win_type != WIN_MUC) {
+    if (window->type != WIN_MUC) {
         cons_show("Cannot apply setting when not in chat room.");
         return TRUE;
     }
 
-    ProfMucWin *mucwin = wins_get_current_muc();
+    ProfMucWin *mucwin = (ProfMucWin*)window;
+    assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
 
     if (g_strcmp0(args[0], "show") == 0) {
         if (g_strcmp0(args[1], "jid") == 0) {
@@ -2866,7 +2962,7 @@ cmd_occupants(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_rooms(gchar **args, struct cmd_help_t help)
+cmd_rooms(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     jabber_conn_status_t conn_status = jabber_get_connection_status();
 
@@ -2887,7 +2983,7 @@ cmd_rooms(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_bookmark(gchar **args, struct cmd_help_t help)
+cmd_bookmark(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     jabber_conn_status_t conn_status = jabber_get_connection_status();
 
@@ -2896,12 +2992,11 @@ cmd_bookmark(gchar **args, struct cmd_help_t help)
         return TRUE;
     }
 
-    win_type_t win_type = ui_current_win_type();
-
     gchar *cmd = args[0];
-    if (win_type == WIN_MUC && cmd == NULL) {
+    if (window->type == WIN_MUC && cmd == NULL) {
         // default to current nickname, password, and autojoin "on"
-        ProfMucWin *mucwin = wins_get_current_muc();
+        ProfMucWin *mucwin = (ProfMucWin*)window;
+        assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
         char *nick = muc_nick(mucwin->roomjid);
         char *password = muc_password(mucwin->roomjid);
         gboolean added = bookmark_add(mucwin->roomjid, nick, password, "on");
@@ -2999,7 +3094,7 @@ cmd_bookmark(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_disco(gchar **args, struct cmd_help_t help)
+cmd_disco(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     jabber_conn_status_t conn_status = jabber_get_connection_status();
 
@@ -3029,7 +3124,7 @@ cmd_disco(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_nick(gchar **args, struct cmd_help_t help)
+cmd_nick(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     jabber_conn_status_t conn_status = jabber_get_connection_status();
 
@@ -3037,12 +3132,13 @@ cmd_nick(gchar **args, struct cmd_help_t help)
         cons_show("You are not currently connected.");
         return TRUE;
     }
-    if (ui_current_win_type() != WIN_MUC) {
+    if (window->type != WIN_MUC) {
         cons_show("You can only change your nickname in a chat room window.");
         return TRUE;
     }
 
-    ProfMucWin *mucwin = wins_get_current_muc();
+    ProfMucWin *mucwin = (ProfMucWin*)window;
+    assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
     char *nick = args[0];
     presence_change_room_nick(mucwin->roomjid, nick);
 
@@ -3050,7 +3146,7 @@ cmd_nick(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_alias(gchar **args, struct cmd_help_t help)
+cmd_alias(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     char *subcmd = args[0];
 
@@ -3122,43 +3218,45 @@ cmd_alias(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_tiny(gchar **args, struct cmd_help_t help)
+cmd_tiny(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     char *url = args[0];
-    ProfWin *current = wins_get_current();
 
-    if (current->type != WIN_CHAT && current->type != WIN_MUC && current->type != WIN_PRIVATE) {
+    if (window->type != WIN_CHAT && window->type != WIN_MUC && window->type != WIN_PRIVATE) {
         cons_show("/tiny can only be used in chat windows");
         return TRUE;
     }
 
     if (!tinyurl_valid(url)) {
-        win_vprint(current, '-', NULL, 0, THEME_ERROR, "", "/tiny, badly formed URL: %s", url);
+        win_vprint(window, '-', NULL, 0, THEME_ERROR, "", "/tiny, badly formed URL: %s", url);
         return TRUE;
     }
 
     char *tiny = tinyurl_get(url);
     if (!tiny) {
-        win_print(current, '-', NULL, 0, THEME_ERROR, "", "Couldn't create tinyurl.");
+        win_print(window, '-', NULL, 0, THEME_ERROR, "", "Couldn't create tinyurl.");
         return TRUE;
     }
 
-    switch (current->type){
+    switch (window->type){
     case WIN_CHAT:
     {
-        ProfChatWin *chatwin = wins_get_current_chat();
+        ProfChatWin *chatwin = (ProfChatWin*)window;
+        assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
         cl_ev_send_msg(chatwin, tiny);
         break;
     }
     case WIN_PRIVATE:
     {
-        ProfPrivateWin *privatewin = wins_get_current_private();
+        ProfPrivateWin *privatewin = (ProfPrivateWin*)window;
+        assert(privatewin->memcheck == PROFPRIVATEWIN_MEMCHECK);
         cl_ev_send_priv_msg(privatewin, tiny);
         break;
     }
     case WIN_MUC:
     {
-        ProfMucWin *mucwin = wins_get_current_muc();
+        ProfMucWin *mucwin = (ProfMucWin*)window;
+        assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
         cl_ev_send_muc_msg(mucwin, tiny);
         break;
     }
@@ -3172,14 +3270,14 @@ cmd_tiny(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_clear(gchar **args, struct cmd_help_t help)
+cmd_clear(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
-    ui_clear_current();
+    ui_clear_win(window);
     return TRUE;
 }
 
 gboolean
-cmd_close(gchar **args, struct cmd_help_t help)
+cmd_close(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     jabber_conn_status_t conn_status = jabber_get_connection_status();
     int index = 0;
@@ -3221,8 +3319,8 @@ cmd_close(gchar **args, struct cmd_help_t help)
         return TRUE;
     }
 
-    ProfWin *window = wins_get_by_num(index);
-    if (!window) {
+    ProfWin *toclose = wins_get_by_num(index);
+    if (!toclose) {
         cons_show("Window is not open.");
         return TRUE;
     }
@@ -3242,17 +3340,21 @@ cmd_close(gchar **args, struct cmd_help_t help)
     ui_close_win(index);
     cons_show("Closed window %d", index);
 
+    // Tidy up the window list.
+    if (prefs_get_boolean(PREF_WINS_AUTO_TIDY)) {
+        ui_tidy_wins();
+    }
+
     return TRUE;
 }
 
 gboolean
-cmd_leave(gchar **args, struct cmd_help_t help)
+cmd_leave(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     jabber_conn_status_t conn_status = jabber_get_connection_status();
-    win_type_t win_type = ui_current_win_type();
     int index = wins_get_current_num();
 
-    if (win_type != WIN_MUC) {
+    if (window->type != WIN_MUC) {
         cons_show("You can only use the /leave command in a chat room.");
         cons_alert();
         return TRUE;
@@ -3270,7 +3372,7 @@ cmd_leave(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_privileges(gchar **args, struct cmd_help_t help)
+cmd_privileges(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     gboolean result = _cmd_set_boolean_preference(args[0], help, "MUC privileges", PREF_MUC_PRIVILEGES);
 
@@ -3280,19 +3382,19 @@ cmd_privileges(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_beep(gchar **args, struct cmd_help_t help)
+cmd_beep(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     return _cmd_set_boolean_preference(args[0], help, "Sound", PREF_BEEP);
 }
 
 gboolean
-cmd_presence(gchar **args, struct cmd_help_t help)
+cmd_presence(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     return _cmd_set_boolean_preference(args[0], help, "Contact presence", PREF_PRESENCE);
 }
 
 gboolean
-cmd_wrap(gchar **args, struct cmd_help_t help)
+cmd_wrap(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     gboolean result = _cmd_set_boolean_preference(args[0], help, "Word wrap", PREF_WRAP);
 
@@ -3302,7 +3404,7 @@ cmd_wrap(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_time(gchar **args, struct cmd_help_t help)
+cmd_time(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     if (g_strcmp0(args[0], "statusbar") == 0) {
         if (args[1] == NULL) {
@@ -3347,7 +3449,7 @@ cmd_time(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_states(gchar **args, struct cmd_help_t help)
+cmd_states(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     gboolean result = _cmd_set_boolean_preference(args[0], help, "Sending chat states",
         PREF_STATES);
@@ -3362,7 +3464,7 @@ cmd_states(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_titlebar(gchar **args, struct cmd_help_t help)
+cmd_titlebar(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     if (g_strcmp0(args[0], "show") != 0 && g_strcmp0(args[0], "goodbye") != 0) {
         cons_show("Usage: %s", help.usage);
@@ -3379,7 +3481,7 @@ cmd_titlebar(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_outtype(gchar **args, struct cmd_help_t help)
+cmd_outtype(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     gboolean result = _cmd_set_boolean_preference(args[0], help,
         "Sending typing notifications", PREF_OUTTYPE);
@@ -3393,7 +3495,7 @@ cmd_outtype(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_gone(gchar **args, struct cmd_help_t help)
+cmd_gone(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     char *value = args[0];
 
@@ -3417,7 +3519,7 @@ cmd_gone(gchar **args, struct cmd_help_t help)
 
 
 gboolean
-cmd_notify(gchar **args, struct cmd_help_t help)
+cmd_notify(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     char *kind = args[0];
 
@@ -3560,7 +3662,7 @@ cmd_notify(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_inpblock(gchar **args, struct cmd_help_t help)
+cmd_inpblock(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     char *subcmd = args[0];
     char *value = args[1];
@@ -3606,7 +3708,7 @@ cmd_inpblock(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_log(gchar **args, struct cmd_help_t help)
+cmd_log(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     char *subcmd = args[0];
     char *value = args[1];
@@ -3662,7 +3764,7 @@ cmd_log(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_reconnect(gchar **args, struct cmd_help_t help)
+cmd_reconnect(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     char *value = args[0];
 
@@ -3686,7 +3788,7 @@ cmd_reconnect(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_autoping(gchar **args, struct cmd_help_t help)
+cmd_autoping(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     char *value = args[0];
 
@@ -3711,7 +3813,7 @@ cmd_autoping(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_ping(gchar **args, struct cmd_help_t help)
+cmd_ping(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     jabber_conn_status_t conn_status = jabber_get_connection_status();
 
@@ -3731,7 +3833,7 @@ cmd_ping(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_autoaway(gchar **args, struct cmd_help_t help)
+cmd_autoaway(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     char *setting = args[0];
     char *value = args[1];
@@ -3790,7 +3892,7 @@ cmd_autoaway(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_priority(gchar **args, struct cmd_help_t help)
+cmd_priority(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     jabber_conn_status_t conn_status = jabber_get_connection_status();
 
@@ -3818,7 +3920,7 @@ cmd_priority(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_statuses(gchar **args, struct cmd_help_t help)
+cmd_statuses(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     if (strcmp(args[0], "console") != 0 &&
             strcmp(args[0], "chat") != 0 &&
@@ -3871,7 +3973,7 @@ cmd_statuses(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_vercheck(gchar **args, struct cmd_help_t help)
+cmd_vercheck(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     int num_args = g_strv_length(args);
 
@@ -3885,7 +3987,7 @@ cmd_vercheck(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_xmlconsole(gchar **args, struct cmd_help_t help)
+cmd_xmlconsole(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     if (!ui_xmlconsole_exists()) {
         ui_create_xmlconsole_win();
@@ -3897,28 +3999,28 @@ cmd_xmlconsole(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_flash(gchar **args, struct cmd_help_t help)
+cmd_flash(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     return _cmd_set_boolean_preference(args[0], help,
         "Screen flash", PREF_FLASH);
 }
 
 gboolean
-cmd_intype(gchar **args, struct cmd_help_t help)
+cmd_intype(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     return _cmd_set_boolean_preference(args[0], help,
         "Show contact typing", PREF_INTYPE);
 }
 
 gboolean
-cmd_splash(gchar **args, struct cmd_help_t help)
+cmd_splash(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     return _cmd_set_boolean_preference(args[0], help,
         "Splash screen", PREF_SPLASH);
 }
 
 gboolean
-cmd_autoconnect(gchar **args, struct cmd_help_t help)
+cmd_autoconnect(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     if (strcmp(args[0], "off") == 0) {
         prefs_set_string(PREF_CONNECT_ACCOUNT, NULL);
@@ -3933,7 +4035,7 @@ cmd_autoconnect(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_chlog(gchar **args, struct cmd_help_t help)
+cmd_chlog(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     gboolean result = _cmd_set_boolean_preference(args[0], help,
         "Chat logging", PREF_CHLOG);
@@ -3947,7 +4049,7 @@ cmd_chlog(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_grlog(gchar **args, struct cmd_help_t help)
+cmd_grlog(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     gboolean result = _cmd_set_boolean_preference(args[0], help,
         "Groupchat logging", PREF_GRLOG);
@@ -3956,14 +4058,14 @@ cmd_grlog(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_mouse(gchar **args, struct cmd_help_t help)
+cmd_mouse(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     return _cmd_set_boolean_preference(args[0], help,
         "Mouse handling", PREF_MOUSE);
 }
 
 gboolean
-cmd_history(gchar **args, struct cmd_help_t help)
+cmd_history(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     gboolean result = _cmd_set_boolean_preference(args[0], help,
         "Chat history", PREF_HISTORY);
@@ -3977,7 +4079,7 @@ cmd_history(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_carbons(gchar **args, struct cmd_help_t help)
+cmd_carbons(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     gboolean result = _cmd_set_boolean_preference(args[0], help,
         "Message carbons preference", PREF_CARBONS);
@@ -3998,7 +4100,7 @@ cmd_carbons(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_receipts(gchar **args, struct cmd_help_t help)
+cmd_receipts(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     if (g_strcmp0(args[0], "send") == 0) {
         return _cmd_set_boolean_preference(args[1], help,
@@ -4013,42 +4115,254 @@ cmd_receipts(gchar **args, struct cmd_help_t help)
 }
 
 gboolean
-cmd_away(gchar **args, struct cmd_help_t help)
+cmd_away(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     _update_presence(RESOURCE_AWAY, "away", args);
     return TRUE;
 }
 
 gboolean
-cmd_online(gchar **args, struct cmd_help_t help)
+cmd_online(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     _update_presence(RESOURCE_ONLINE, "online", args);
     return TRUE;
 }
 
 gboolean
-cmd_dnd(gchar **args, struct cmd_help_t help)
+cmd_dnd(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     _update_presence(RESOURCE_DND, "dnd", args);
     return TRUE;
 }
 
 gboolean
-cmd_chat(gchar **args, struct cmd_help_t help)
+cmd_chat(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     _update_presence(RESOURCE_CHAT, "chat", args);
     return TRUE;
 }
 
 gboolean
-cmd_xa(gchar **args, struct cmd_help_t help)
+cmd_xa(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
     _update_presence(RESOURCE_XA, "xa", args);
     return TRUE;
 }
 
 gboolean
-cmd_otr(gchar **args, struct cmd_help_t help)
+cmd_pgp(ProfWin *window, gchar **args, struct cmd_help_t help)
+{
+#ifdef HAVE_LIBGPGME
+    if (args[0] == NULL) {
+        cons_show("Usage: %s", help.usage);
+        return TRUE;
+    }
+
+    if (g_strcmp0(args[0], "log") == 0) {
+        char *choice = args[1];
+        if (g_strcmp0(choice, "on") == 0) {
+            prefs_set_string(PREF_PGP_LOG, "on");
+            cons_show("PGP messages will be logged as plaintext.");
+            if (!prefs_get_boolean(PREF_CHLOG)) {
+                cons_show("Chat logging is currently disabled, use '/chlog on' to enable.");
+            }
+        } else if (g_strcmp0(choice, "off") == 0) {
+            prefs_set_string(PREF_PGP_LOG, "off");
+            cons_show("PGP message logging disabled.");
+        } else if (g_strcmp0(choice, "redact") == 0) {
+            prefs_set_string(PREF_PGP_LOG, "redact");
+            cons_show("PGP messages will be logged as '[redacted]'.");
+            if (!prefs_get_boolean(PREF_CHLOG)) {
+                cons_show("Chat logging is currently disabled, use '/chlog on' to enable.");
+            }
+        } else {
+            cons_show("Usage: %s", help.usage);
+        }
+        return TRUE;
+    }
+
+    if (g_strcmp0(args[0], "keys") == 0) {
+        GSList *keys = p_gpg_list_keys();
+        if (!keys) {
+            cons_show("No keys found");
+            return TRUE;
+        }
+
+        cons_show("PGP keys:");
+        GSList *curr = keys;
+        while (curr) {
+            ProfPGPKey *key = curr->data;
+            cons_show("  %s", key->name);
+            cons_show("    ID          : %s", key->id);
+            cons_show("    Fingerprint : %s", key->fp);
+            curr = g_slist_next(curr);
+        }
+        g_slist_free_full(keys, (GDestroyNotify)p_gpg_free_key);
+        return TRUE;
+    }
+
+    if (g_strcmp0(args[0], "setkey") == 0) {
+        jabber_conn_status_t conn_status = jabber_get_connection_status();
+        if (conn_status != JABBER_CONNECTED) {
+            cons_show("You are not currently connected.");
+            return TRUE;
+        }
+
+        char *jid = args[1];
+        if (!args[1]) {
+            cons_show("Usage: %s", help.usage);
+            return TRUE;
+        }
+
+        char *keyid = args[2];
+        if (!args[2]) {
+            cons_show("Usage: %s", help.usage);
+            return TRUE;
+        }
+
+        gboolean res = p_gpg_addkey(jid, keyid);
+        if (!res) {
+            cons_show("Key ID not found.");
+        } else {
+            cons_show("Key %s set for %s.", keyid, jid);
+        }
+
+        return TRUE;
+    }
+
+    if (g_strcmp0(args[0], "fps") == 0) {
+        jabber_conn_status_t conn_status = jabber_get_connection_status();
+        if (conn_status != JABBER_CONNECTED) {
+            cons_show("You are not currently connected.");
+            return TRUE;
+        }
+        GHashTable *fingerprints = p_gpg_fingerprints();
+        GList *jids = g_hash_table_get_keys(fingerprints);
+        if (!jids) {
+            cons_show("No PGP fingerprints available.");
+            return TRUE;
+        }
+
+        cons_show("Known PGP fingerprints:");
+        GList *curr = jids;
+        while (curr) {
+            char *jid = curr->data;
+            char *fingerprint = g_hash_table_lookup(fingerprints, jid);
+            cons_show("  %s: %s", jid, fingerprint);
+            curr = g_list_next(curr);
+        }
+        g_list_free(jids);
+        return TRUE;
+    }
+
+    if (g_strcmp0(args[0], "libver") == 0) {
+        const char *libver = p_gpg_libver();
+        if (!libver) {
+            cons_show("Could not get libgpgme version");
+            return TRUE;
+        }
+
+        GString *fullstr = g_string_new("Using libgpgme version ");
+        g_string_append(fullstr, libver);
+        cons_show("%s", fullstr->str);
+        g_string_free(fullstr, TRUE);
+
+        return TRUE;
+    }
+
+    if (g_strcmp0(args[0], "start") == 0) {
+        jabber_conn_status_t conn_status = jabber_get_connection_status();
+        if (conn_status != JABBER_CONNECTED) {
+            cons_show("You must be connected to start PGP encrpytion.");
+            return TRUE;
+        }
+
+        if (window->type != WIN_CHAT && args[1] == NULL) {
+            cons_show("You must be in a regular chat window to start PGP encrpytion.");
+            return TRUE;
+        }
+
+        ProfChatWin *chatwin = NULL;
+
+        if (args[1]) {
+            char *contact = args[1];
+            char *barejid = roster_barejid_from_name(contact);
+            if (barejid == NULL) {
+                barejid = contact;
+            }
+
+            chatwin = wins_get_chat(barejid);
+            if (!chatwin) {
+                chatwin = ui_ev_new_chat_win(barejid);
+            }
+            ui_ev_focus_win((ProfWin*)chatwin);
+        } else {
+            chatwin = (ProfChatWin*)window;
+            assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
+        }
+
+        if (chatwin->enc_mode == PROF_ENC_OTR) {
+            ui_current_print_formatted_line('!', 0, "You must end the OTR session to start PGP encryption.");
+            return TRUE;
+        }
+
+        if (chatwin->enc_mode == PROF_ENC_PGP) {
+            ui_current_print_formatted_line('!', 0, "You have already started PGP encryption.");
+            return TRUE;
+        }
+
+        ProfAccount *account = accounts_get_account(jabber_get_account_name());
+        if (!account->pgp_keyid) {
+            ui_current_print_formatted_line('!', 0, "You must specify a PGP key ID for this account to start PGP encryption.");
+            account_free(account);
+            return TRUE;
+        }
+        account_free(account);
+
+        if (!p_gpg_available(chatwin->barejid)) {
+            ui_current_print_formatted_line('!', 0, "No PGP key found for %s.", chatwin->barejid);
+            return TRUE;
+        }
+
+        chatwin->enc_mode = PROF_ENC_PGP;
+        ui_current_print_formatted_line('!', 0, "PGP encyption enabled.");
+        return TRUE;
+    }
+
+    if (g_strcmp0(args[0], "end") == 0) {
+        jabber_conn_status_t conn_status = jabber_get_connection_status();
+        if (conn_status != JABBER_CONNECTED) {
+            cons_show("You are not currently connected.");
+            return TRUE;
+        }
+
+        if (window->type != WIN_CHAT) {
+            cons_show("You must be in a regular chat window to end PGP encrpytion.");
+            return TRUE;
+        }
+
+        ProfChatWin *chatwin = (ProfChatWin*)window;
+        if (chatwin->enc_mode != PROF_ENC_PGP) {
+            ui_current_print_formatted_line('!', 0, "PGP encryption is not currently enabled.");
+            return TRUE;
+        }
+
+        chatwin->enc_mode = PROF_ENC_NONE;
+        ui_current_print_formatted_line('!', 0, "PGP encyption disabled.");
+        return TRUE;
+    }
+
+    cons_show("Usage: %s", help.usage);
+    return TRUE;
+#else
+    cons_show("This version of Profanity has not been built with PGP support enabled");
+    return TRUE;
+#endif
+
+}
+
+gboolean
+cmd_otr(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
 #ifdef HAVE_LIBOTR
     if (args[0] == NULL) {
@@ -4078,11 +4392,6 @@ cmd_otr(gchar **args, struct cmd_help_t help)
         }
         return TRUE;
 
-    } else if (strcmp(args[0], "warn") == 0) {
-        gboolean result =  _cmd_set_boolean_preference(args[1], help,
-            "OTR warning message", PREF_OTR_WARN);
-        return result;
-
     } else if (strcmp(args[0], "libver") == 0) {
         char *version = otr_libotr_version();
         cons_show("Using libotr version %s", version);
@@ -4138,29 +4447,34 @@ cmd_otr(gchar **args, struct cmd_help_t help)
     } else if (strcmp(args[0], "myfp") == 0) {
         if (!otr_key_loaded()) {
             ui_current_print_formatted_line('!', 0, "You have not generated or loaded a private key, use '/otr gen'");
-        } else {
-            char *fingerprint = otr_get_my_fingerprint();
-            ui_current_print_formatted_line('!', 0, "Your OTR fingerprint: %s", fingerprint);
-            free(fingerprint);
+            return TRUE;
         }
+
+        char *fingerprint = otr_get_my_fingerprint();
+        ui_current_print_formatted_line('!', 0, "Your OTR fingerprint: %s", fingerprint);
+        free(fingerprint);
         return TRUE;
 
     } else if (strcmp(args[0], "theirfp") == 0) {
-        win_type_t win_type = ui_current_win_type();
-
-        if (win_type != WIN_CHAT) {
+        if (window->type != WIN_CHAT) {
             ui_current_print_line("You must be in a regular chat window to view a recipient's fingerprint.");
-        } else if (!ui_current_win_is_otr()) {
+            return TRUE;
+        }
+
+        ProfChatWin *chatwin = (ProfChatWin*)window;
+        assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
+        if (chatwin->enc_mode != PROF_ENC_OTR) {
             ui_current_print_formatted_line('!', 0, "You are not currently in an OTR session.");
-        } else {
-            ProfChatWin *chatwin = ui_get_current_chat();
-            char *fingerprint = otr_get_their_fingerprint(chatwin->barejid);
-            ui_current_print_formatted_line('!', 0, "%s's OTR fingerprint: %s", chatwin->barejid, fingerprint);
-            free(fingerprint);
+            return TRUE;
         }
+
+        char *fingerprint = otr_get_their_fingerprint(chatwin->barejid);
+        ui_current_print_formatted_line('!', 0, "%s's OTR fingerprint: %s", chatwin->barejid, fingerprint);
+        free(fingerprint);
         return TRUE;
 
     } else if (strcmp(args[0], "start") == 0) {
+        // recipient supplied
         if (args[1]) {
             char *contact = args[1];
             char *barejid = roster_barejid_from_name(contact);
@@ -4174,131 +4488,175 @@ cmd_otr(gchar **args, struct cmd_help_t help)
             }
             ui_ev_focus_win((ProfWin*)chatwin);
 
-            if (ui_current_win_is_otr()) {
+            if (chatwin->enc_mode == PROF_ENC_PGP) {
+                ui_current_print_formatted_line('!', 0, "You must disable PGP encryption before starting an OTR session.");
+                return TRUE;
+            }
+
+            if (chatwin->enc_mode == PROF_ENC_OTR) {
                 ui_current_print_formatted_line('!', 0, "You are already in an OTR session.");
-            } else {
-                if (!otr_key_loaded()) {
-                    ui_current_print_formatted_line('!', 0, "You have not generated or loaded a private key, use '/otr gen'");
-                } else if (!otr_is_secure(barejid)) {
-                    char *otr_query_message = otr_start_query();
-                    message_send_chat_encrypted(barejid, otr_query_message);
-                } else {
-                    ui_gone_secure(barejid, otr_is_trusted(barejid));
-                }
+                return TRUE;
             }
-        } else {
-            win_type_t win_type = ui_current_win_type();
 
-            if (win_type != WIN_CHAT) {
+            if (!otr_key_loaded()) {
+                ui_current_print_formatted_line('!', 0, "You have not generated or loaded a private key, use '/otr gen'");
+                return TRUE;
+            }
+
+            if (!otr_is_secure(barejid)) {
+                char *otr_query_message = otr_start_query();
+                message_send_chat_otr(barejid, otr_query_message);
+                return TRUE;
+            }
+
+            ui_gone_secure(barejid, otr_is_trusted(barejid));
+            return TRUE;
+
+        // no recipient, use current chat
+        } else {
+            if (window->type != WIN_CHAT) {
                 ui_current_print_line("You must be in a regular chat window to start an OTR session.");
-            } else if (ui_current_win_is_otr()) {
+                return TRUE;
+            }
+
+            ProfChatWin *chatwin = (ProfChatWin*)window;
+            assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
+            if (chatwin->enc_mode == PROF_ENC_PGP) {
+                ui_current_print_formatted_line('!', 0, "You must disable PGP encryption before starting an OTR session.");
+                return TRUE;
+            }
+
+            if (chatwin->enc_mode == PROF_ENC_OTR) {
                 ui_current_print_formatted_line('!', 0, "You are already in an OTR session.");
-            } else {
-                if (!otr_key_loaded()) {
-                    ui_current_print_formatted_line('!', 0, "You have not generated or loaded a private key, use '/otr gen'");
-                } else {
-                    ProfChatWin *chatwin = ui_get_current_chat();
-                    char *otr_query_message = otr_start_query();
-                    message_send_chat_encrypted(chatwin->barejid, otr_query_message);
-                }
+                return TRUE;
             }
+
+            if (!otr_key_loaded()) {
+                ui_current_print_formatted_line('!', 0, "You have not generated or loaded a private key, use '/otr gen'");
+                return TRUE;
+            }
+
+            char *otr_query_message = otr_start_query();
+            message_send_chat_otr(chatwin->barejid, otr_query_message);
+            return TRUE;
         }
-        return TRUE;
 
     } else if (strcmp(args[0], "end") == 0) {
-        win_type_t win_type = ui_current_win_type();
-
-        if (win_type != WIN_CHAT) {
+        if (window->type != WIN_CHAT) {
             ui_current_print_line("You must be in a regular chat window to use OTR.");
-        } else if (!ui_current_win_is_otr()) {
+            return TRUE;
+        }
+
+        ProfChatWin *chatwin = (ProfChatWin*)window;
+        assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
+        if (chatwin->enc_mode != PROF_ENC_OTR) {
             ui_current_print_formatted_line('!', 0, "You are not currently in an OTR session.");
-        } else {
-            ProfChatWin *chatwin = wins_get_current_chat();
-            ui_gone_insecure(chatwin->barejid);
-            otr_end_session(chatwin->barejid);
+            return TRUE;
         }
+
+        ui_gone_insecure(chatwin->barejid);
+        otr_end_session(chatwin->barejid);
         return TRUE;
 
     } else if (strcmp(args[0], "trust") == 0) {
-        win_type_t win_type = ui_current_win_type();
-
-        if (win_type != WIN_CHAT) {
+        if (window->type != WIN_CHAT) {
             ui_current_print_line("You must be in an OTR session to trust a recipient.");
-        } else if (!ui_current_win_is_otr()) {
+            return TRUE;
+        }
+
+        ProfChatWin *chatwin = (ProfChatWin*)window;
+        assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
+        if (chatwin->enc_mode != PROF_ENC_OTR) {
             ui_current_print_formatted_line('!', 0, "You are not currently in an OTR session.");
-        } else {
-            ProfChatWin *chatwin = wins_get_current_chat();
-            ui_trust(chatwin->barejid);
-            otr_trust(chatwin->barejid);
+            return TRUE;
         }
+
+        ui_trust(chatwin->barejid);
+        otr_trust(chatwin->barejid);
         return TRUE;
 
     } else if (strcmp(args[0], "untrust") == 0) {
-        win_type_t win_type = ui_current_win_type();
-
-        if (win_type != WIN_CHAT) {
+        if (window->type != WIN_CHAT) {
             ui_current_print_line("You must be in an OTR session to untrust a recipient.");
-        } else if (!ui_current_win_is_otr()) {
+            return TRUE;
+        }
+
+        ProfChatWin *chatwin = (ProfChatWin*)window;
+        assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
+        if (chatwin->enc_mode != PROF_ENC_OTR) {
             ui_current_print_formatted_line('!', 0, "You are not currently in an OTR session.");
-        } else {
-            ProfChatWin *chatwin = wins_get_current_chat();
-            ui_untrust(chatwin->barejid);
-            otr_untrust(chatwin->barejid);
+            return TRUE;
         }
+
+        ui_untrust(chatwin->barejid);
+        otr_untrust(chatwin->barejid);
         return TRUE;
 
     } else if (strcmp(args[0], "secret") == 0) {
-        win_type_t win_type = ui_current_win_type();
-        if (win_type != WIN_CHAT) {
+        if (window->type != WIN_CHAT) {
             ui_current_print_line("You must be in an OTR session to trust a recipient.");
-        } else if (!ui_current_win_is_otr()) {
+            return TRUE;
+        }
+
+        ProfChatWin *chatwin = (ProfChatWin*)window;
+        assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
+        if (chatwin->enc_mode != PROF_ENC_OTR) {
             ui_current_print_formatted_line('!', 0, "You are not currently in an OTR session.");
-        } else {
-            char *secret = args[1];
-            if (secret == NULL) {
-                cons_show("Usage: %s", help.usage);
-            } else {
-                ProfChatWin *chatwin = wins_get_current_chat();
-                otr_smp_secret(chatwin->barejid, secret);
-            }
+            return TRUE;
+        }
+
+        char *secret = args[1];
+        if (secret == NULL) {
+            cons_show("Usage: %s", help.usage);
+            return TRUE;
         }
+
+        otr_smp_secret(chatwin->barejid, secret);
         return TRUE;
 
     } else if (strcmp(args[0], "question") == 0) {
         char *question = args[1];
         char *answer = args[2];
-
         if (question == NULL || answer == NULL) {
             cons_show("Usage: %s", help.usage);
             return TRUE;
-        } else {
-            win_type_t win_type = ui_current_win_type();
-            if (win_type != WIN_CHAT) {
-                ui_current_print_line("You must be in an OTR session to trust a recipient.");
-            } else if (!ui_current_win_is_otr()) {
-                ui_current_print_formatted_line('!', 0, "You are not currently in an OTR session.");
-            } else {
-                ProfChatWin *chatwin = wins_get_current_chat();
-                otr_smp_question(chatwin->barejid, question, answer);
-            }
+        }
+
+        if (window->type != WIN_CHAT) {
+            ui_current_print_line("You must be in an OTR session to trust a recipient.");
+            return TRUE;
+        }
+
+        ProfChatWin *chatwin = (ProfChatWin*)window;
+        assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
+        if (chatwin->enc_mode != PROF_ENC_OTR) {
+            ui_current_print_formatted_line('!', 0, "You are not currently in an OTR session.");
             return TRUE;
         }
 
+        otr_smp_question(chatwin->barejid, question, answer);
+        return TRUE;
+
     } else if (strcmp(args[0], "answer") == 0) {
-        win_type_t win_type = ui_current_win_type();
-        if (win_type != WIN_CHAT) {
+        if (window->type != WIN_CHAT) {
             ui_current_print_line("You must be in an OTR session to trust a recipient.");
-        } else if (!ui_current_win_is_otr()) {
+            return TRUE;
+        }
+
+        ProfChatWin *chatwin = (ProfChatWin*)window;
+        assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
+        if (chatwin->enc_mode != PROF_ENC_OTR) {
             ui_current_print_formatted_line('!', 0, "You are not currently in an OTR session.");
-        } else {
-            char *answer = args[1];
-            if (answer == NULL) {
-                cons_show("Usage: %s", help.usage);
-            } else {
-                ProfChatWin *chatwin = wins_get_current_chat();
-                otr_smp_answer(chatwin->barejid, answer);
-            }
+            return TRUE;
         }
+
+        char *answer = args[1];
+        if (answer == NULL) {
+            cons_show("Usage: %s", help.usage);
+            return TRUE;
+        }
+
+        otr_smp_answer(chatwin->barejid, answer);
         return TRUE;
 
     } else {
@@ -4311,6 +4669,12 @@ cmd_otr(gchar **args, struct cmd_help_t help)
 #endif
 }
 
+gboolean
+cmd_encwarn(ProfWin *window, gchar **args, struct cmd_help_t help)
+{
+    return _cmd_set_boolean_preference(args[0], help, "Encryption warning message", PREF_ENC_WARN);
+}
+
 // helper function for status change commands
 static void
 _update_presence(const resource_presence_t resource_presence,
diff --git a/src/command/commands.h b/src/command/commands.h
index 7b7e7c93..6f2bada2 100644
--- a/src/command/commands.h
+++ b/src/command/commands.h
@@ -35,6 +35,8 @@
 #ifndef COMMANDS_H
 #define COMMANDS_H
 
+#include "ui/ui.h"
+
 // Command help strings
 typedef struct cmd_help_t {
     const gchar *usage;
@@ -54,7 +56,7 @@ typedef struct cmd_help_t {
  */
 typedef struct cmd_t {
     gchar *cmd;
-    gboolean (*func)(gchar **args, struct cmd_help_t help);
+    gboolean (*func)(ProfWin *window, gchar **args, struct cmd_help_t help);
     gchar** (*parser)(const char * const inp, int min, int max, gboolean *result);
     int min_args;
     int max_args;
@@ -62,87 +64,90 @@ typedef struct cmd_t {
     CommandHelp help;
 } Command;
 
-gboolean cmd_execute_alias(const char * const inp, gboolean *ran);
-gboolean cmd_execute_default(const char * inp);
+gboolean cmd_execute_alias(ProfWin *window, const char * const inp, gboolean *ran);
+gboolean cmd_execute_default(ProfWin *window, const char * inp);
 
-gboolean cmd_about(gchar **args, struct cmd_help_t help);
-gboolean cmd_account(gchar **args, struct cmd_help_t help);
-gboolean cmd_autoaway(gchar **args, struct cmd_help_t help);
-gboolean cmd_autoconnect(gchar **args, struct cmd_help_t help);
-gboolean cmd_autoping(gchar **args, struct cmd_help_t help);
-gboolean cmd_away(gchar **args, struct cmd_help_t help);
-gboolean cmd_beep(gchar **args, struct cmd_help_t help);
-gboolean cmd_caps(gchar **args, struct cmd_help_t help);
-gboolean cmd_chat(gchar **args, struct cmd_help_t help);
-gboolean cmd_chlog(gchar **args, struct cmd_help_t help);
-gboolean cmd_clear(gchar **args, struct cmd_help_t help);
-gboolean cmd_close(gchar **args, struct cmd_help_t help);
-gboolean cmd_connect(gchar **args, struct cmd_help_t help);
-gboolean cmd_decline(gchar **args, struct cmd_help_t help);
-gboolean cmd_disco(gchar **args, struct cmd_help_t help);
-gboolean cmd_disconnect(gchar **args, struct cmd_help_t help);
-gboolean cmd_dnd(gchar **args, struct cmd_help_t help);
-gboolean cmd_flash(gchar **args, struct cmd_help_t help);
-gboolean cmd_gone(gchar **args, struct cmd_help_t help);
-gboolean cmd_grlog(gchar **args, struct cmd_help_t help);
-gboolean cmd_group(gchar **args, struct cmd_help_t help);
-gboolean cmd_help(gchar **args, struct cmd_help_t help);
-gboolean cmd_history(gchar **args, struct cmd_help_t help);
-gboolean cmd_carbons(gchar **args, struct cmd_help_t help);
-gboolean cmd_receipts(gchar **args, struct cmd_help_t help);
-gboolean cmd_info(gchar **args, struct cmd_help_t help);
-gboolean cmd_intype(gchar **args, struct cmd_help_t help);
-gboolean cmd_invite(gchar **args, struct cmd_help_t help);
-gboolean cmd_invites(gchar **args, struct cmd_help_t help);
-gboolean cmd_join(gchar **args, struct cmd_help_t help);
-gboolean cmd_leave(gchar **args, struct cmd_help_t help);
-gboolean cmd_log(gchar **args, struct cmd_help_t help);
-gboolean cmd_mouse(gchar **args, struct cmd_help_t help);
-gboolean cmd_msg(gchar **args, struct cmd_help_t help);
-gboolean cmd_nick(gchar **args, struct cmd_help_t help);
-gboolean cmd_notify(gchar **args, struct cmd_help_t help);
-gboolean cmd_online(gchar **args, struct cmd_help_t help);
-gboolean cmd_otr(gchar **args, struct cmd_help_t help);
-gboolean cmd_outtype(gchar **args, struct cmd_help_t help);
-gboolean cmd_prefs(gchar **args, struct cmd_help_t help);
-gboolean cmd_priority(gchar **args, struct cmd_help_t help);
-gboolean cmd_quit(gchar **args, struct cmd_help_t help);
-gboolean cmd_reconnect(gchar **args, struct cmd_help_t help);
-gboolean cmd_room(gchar **args, struct cmd_help_t help);
-gboolean cmd_rooms(gchar **args, struct cmd_help_t help);
-gboolean cmd_bookmark(gchar **args, struct cmd_help_t help);
-gboolean cmd_roster(gchar **args, struct cmd_help_t help);
-gboolean cmd_software(gchar **args, struct cmd_help_t help);
-gboolean cmd_splash(gchar **args, struct cmd_help_t help);
-gboolean cmd_states(gchar **args, struct cmd_help_t help);
-gboolean cmd_status(gchar **args, struct cmd_help_t help);
-gboolean cmd_statuses(gchar **args, struct cmd_help_t help);
-gboolean cmd_sub(gchar **args, struct cmd_help_t help);
-gboolean cmd_theme(gchar **args, struct cmd_help_t help);
-gboolean cmd_tiny(gchar **args, struct cmd_help_t help);
-gboolean cmd_titlebar(gchar **args, struct cmd_help_t help);
-gboolean cmd_vercheck(gchar **args, struct cmd_help_t help);
-gboolean cmd_who(gchar **args, struct cmd_help_t help);
-gboolean cmd_win(gchar **args, struct cmd_help_t help);
-gboolean cmd_wins(gchar **args, struct cmd_help_t help);
-gboolean cmd_xa(gchar **args, struct cmd_help_t help);
-gboolean cmd_alias(gchar **args, struct cmd_help_t help);
-gboolean cmd_xmlconsole(gchar **args, struct cmd_help_t help);
-gboolean cmd_ping(gchar **args, struct cmd_help_t help);
-gboolean cmd_form(gchar **args, struct cmd_help_t help);
-gboolean cmd_occupants(gchar **args, struct cmd_help_t help);
-gboolean cmd_kick(gchar **args, struct cmd_help_t help);
-gboolean cmd_ban(gchar **args, struct cmd_help_t help);
-gboolean cmd_subject(gchar **args, struct cmd_help_t help);
-gboolean cmd_affiliation(gchar **args, struct cmd_help_t help);
-gboolean cmd_role(gchar **args, struct cmd_help_t help);
-gboolean cmd_privileges(gchar **args, struct cmd_help_t help);
-gboolean cmd_presence(gchar **args, struct cmd_help_t help);
-gboolean cmd_wrap(gchar **args, struct cmd_help_t help);
-gboolean cmd_time(gchar **args, struct cmd_help_t help);
-gboolean cmd_resource(gchar **args, struct cmd_help_t help);
-gboolean cmd_inpblock(gchar **args, struct cmd_help_t help);
+gboolean cmd_about(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_account(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_autoaway(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_autoconnect(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_autoping(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_away(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_beep(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_caps(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_chat(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_chlog(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_clear(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_close(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_connect(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_decline(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_disco(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_disconnect(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_dnd(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_flash(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_gone(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_grlog(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_group(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_help(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_history(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_carbons(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_receipts(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_info(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_intype(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_invite(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_invites(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_join(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_leave(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_log(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_mouse(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_msg(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_nick(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_notify(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_online(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_otr(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_pgp(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_outtype(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_prefs(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_priority(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_quit(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_reconnect(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_room(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_rooms(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_bookmark(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_roster(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_software(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_splash(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_states(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_status(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_statuses(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_sub(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_theme(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_tiny(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_titlebar(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_vercheck(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_who(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_win(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_wins(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_winstidy(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_xa(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_alias(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_xmlconsole(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_ping(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_form(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_occupants(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_kick(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_ban(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_subject(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_affiliation(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_role(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_privileges(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_presence(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_wrap(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_time(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_resource(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_inpblock(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_encwarn(ProfWin *window, gchar **args, struct cmd_help_t help);
 
-gboolean cmd_form_field(char *tag, gchar **args);
+gboolean cmd_form_field(ProfWin *window, char *tag, gchar **args);
 
 #endif
diff --git a/src/common.c b/src/common.c
index 772e24d3..832e85dd 100644
--- a/src/common.c
+++ b/src/common.c
@@ -57,6 +57,8 @@ struct curl_data_t
     size_t size;
 };
 
+static unsigned long unique_id = 0;
+
 static size_t _data_callback(void *ptr, size_t size, size_t nmemb, void *data);
 
 // taken from glib 2.30.3
@@ -251,18 +253,6 @@ utf8_display_len(const char * const str)
     return len;
 }
 
-gboolean
-utf8_is_printable(const wint_t ch)
-{
-    char bytes[MB_CUR_MAX+1];
-    size_t utf_len = wcrtomb(bytes, ch, NULL);
-    bytes[utf_len] = '\0';
-
-    gunichar unichar = g_utf8_get_char(bytes);
-
-    return g_unichar_isprint(unichar) && (ch != KEY_MOUSE);
-}
-
 char *
 prof_getline(FILE *stream)
 {
@@ -469,7 +459,6 @@ xdg_get_data_home(void)
 char *
 create_unique_id(char *prefix)
 {
-    static unsigned long unique_id;
     char *result = NULL;
     GString *result_str = g_string_new("");
 
@@ -485,6 +474,12 @@ create_unique_id(char *prefix)
     return result;
 }
 
+void
+reset_unique_id(void)
+{
+    unique_id = 0;
+}
+
 char *
 p_sha1_hash(char *str)
 {
diff --git a/src/common.h b/src/common.h
index 9521a701..9da0c974 100644
--- a/src/common.h
+++ b/src/common.h
@@ -38,12 +38,6 @@
 #include <stdio.h>
 #include <wchar.h>
 
-#ifdef HAVE_NCURSESW_NCURSES_H
-#include <ncursesw/ncurses.h>
-#elif HAVE_NCURSES_H
-#include <ncurses.h>
-#endif
-
 #include <glib.h>
 
 #if !GLIB_CHECK_VERSION(2,28,0)
@@ -113,7 +107,6 @@ char * str_replace(const char *string, const char *substr,
 int str_contains(const char str[], int size, char ch);
 gboolean strtoi_range(char *str, int *saveptr, int min, int max, char **err_msg);
 int utf8_display_len(const char * const str);
-gboolean utf8_is_printable(const wint_t ch);
 char * prof_getline(FILE *stream);
 char* release_get_latest(void);
 gboolean release_is_new(char *found_version);
@@ -127,6 +120,7 @@ contact_presence_t contact_presence_from_resource_presence(resource_presence_t r
 
 char * p_sha1_hash(char *str);
 char * create_unique_id(char *prefix);
+void reset_unique_id(void);
 
 int cmp_win_num(gconstpointer a, gconstpointer b);
 int get_next_available_win_num(GList *used);
diff --git a/src/config/account.c b/src/config/account.c
index 857d049b..de48ba02 100644
--- a/src/config/account.c
+++ b/src/config/account.c
@@ -51,7 +51,7 @@ account_new(const gchar * const name, const gchar * const jid,
     int priority_away, int priority_xa, int priority_dnd,
     const gchar * const muc_service, const gchar * const muc_nick,
     const gchar * const otr_policy, GList *otr_manual, GList *otr_opportunistic,
-    GList *otr_always)
+    GList *otr_always, const gchar * const pgp_keyid)
 {
     ProfAccount *new_account = malloc(sizeof(ProfAccount));
 
@@ -144,6 +144,12 @@ account_new(const gchar * const name, const gchar * const jid,
     new_account->otr_opportunistic = otr_opportunistic;
     new_account->otr_always = otr_always;
 
+    if (pgp_keyid != NULL) {
+        new_account->pgp_keyid = strdup(pgp_keyid);
+    } else {
+        new_account->pgp_keyid = NULL;
+    }
+
     return new_account;
 }
 
@@ -210,6 +216,7 @@ account_free(ProfAccount *account)
         free(account->muc_service);
         free(account->muc_nick);
         free(account->otr_policy);
+        free(account->pgp_keyid);
         g_list_free_full(account->otr_manual, g_free);
         g_list_free_full(account->otr_opportunistic, g_free);
         g_list_free_full(account->otr_always, g_free);
diff --git a/src/config/account.h b/src/config/account.h
index 218f8ce7..22c29161 100644
--- a/src/config/account.h
+++ b/src/config/account.h
@@ -59,6 +59,7 @@ typedef struct prof_account_t {
     GList *otr_manual;
     GList *otr_opportunistic;
     GList *otr_always;
+    gchar *pgp_keyid;
 } ProfAccount;
 
 ProfAccount* account_new(const gchar * const name, const gchar * const jid,
@@ -68,7 +69,7 @@ ProfAccount* account_new(const gchar * const name, const gchar * const jid,
     int priority_away, int priority_xa, int priority_dnd,
     const gchar * const muc_service, const gchar * const muc_nick,
     const gchar * const otr_policy, GList *otr_manual, GList *otr_opportunistic,
-    GList *otr_always);
+    GList *otr_always, const gchar * const pgp_keyid);
 char* account_create_full_jid(ProfAccount *account);
 gboolean account_eval_password(ProfAccount *account);
 void account_free(ProfAccount *account);
diff --git a/src/config/accounts.c b/src/config/accounts.c
index d68f3a55..a827392b 100644
--- a/src/config/accounts.c
+++ b/src/config/accounts.c
@@ -280,11 +280,16 @@ accounts_get_account(const char * const name)
             g_strfreev(always);
         }
 
+        gchar *pgp_keyid = NULL;
+        if (g_key_file_has_key(accounts, name, "pgp.keyid", NULL)) {
+            pgp_keyid = g_key_file_get_string(accounts, name, "pgp.keyid", NULL);
+        }
+
         ProfAccount *new_account = account_new(name, jid, password, eval_password, enabled,
             server, port, resource, last_presence, login_presence,
             priority_online, priority_chat, priority_away, priority_xa,
             priority_dnd, muc_service, muc_nick, otr_policy, otr_manual,
-            otr_opportunistic, otr_always);
+            otr_opportunistic, otr_always, pgp_keyid);
 
         g_free(jid);
         g_free(password);
@@ -296,6 +301,7 @@ accounts_get_account(const char * const name)
         g_free(muc_service);
         g_free(muc_nick);
         g_free(otr_policy);
+        g_free(pgp_keyid);
 
         return new_account;
     }
@@ -405,6 +411,8 @@ accounts_set_jid(const char * const account_name, const char * const value)
 
             _save_accounts();
         }
+
+        jid_destroy(jid);
     }
 }
 
@@ -454,6 +462,15 @@ accounts_set_eval_password(const char * const account_name, const char * const v
 }
 
 void
+accounts_set_pgp_keyid(const char * const account_name, const char * const value)
+{
+    if (accounts_account_exists(account_name)) {
+        g_key_file_set_string(accounts, account_name, "pgp.keyid", value);
+        _save_accounts();
+    }
+}
+
+void
 accounts_clear_password(const char * const account_name)
 {
     if (accounts_account_exists(account_name)) {
@@ -490,6 +507,15 @@ accounts_clear_port(const char * const account_name)
 }
 
 void
+accounts_clear_pgp_keyid(const char * const account_name)
+{
+    if (accounts_account_exists(account_name)) {
+        g_key_file_remove_key(accounts, account_name, "pgp.keyid", NULL);
+        _save_accounts();
+    }
+}
+
+void
 accounts_clear_otr(const char * const account_name)
 {
     if (accounts_account_exists(account_name)) {
diff --git a/src/config/accounts.h b/src/config/accounts.h
index 50307b5b..eb981cb8 100644
--- a/src/config/accounts.h
+++ b/src/config/accounts.h
@@ -77,11 +77,13 @@ void accounts_set_priority_dnd(const char * const account_name, const gint value
 void accounts_set_priority_all(const char * const account_name, const gint value);
 gint accounts_get_priority_for_presence_type(const char * const account_name,
     resource_presence_t presence_type);
+void accounts_set_pgp_keyid(const char * const account_name, const char * const value);
 void accounts_clear_password(const char * const account_name);
 void accounts_clear_eval_password(const char * const account_name);
 void accounts_clear_server(const char * const account_name);
 void accounts_clear_port(const char * const account_name);
 void accounts_clear_otr(const char * const account_name);
+void accounts_clear_pgp_keyid(const char * const account_name);
 void accounts_add_otr_policy(const char * const account_name, const char * const contact_jid, const char * const policy);
 
 #endif
diff --git a/src/config/preferences.c b/src/config/preferences.c
index c0d6f6e5..7153d62b 100644
--- a/src/config/preferences.c
+++ b/src/config/preferences.c
@@ -41,12 +41,6 @@
 #include <glib.h>
 #include <glib/gstdio.h>
 
-#ifdef HAVE_NCURSESW_NCURSES_H
-#include <ncursesw/ncurses.h>
-#elif HAVE_NCURSES_H
-#include <ncurses.h>
-#endif
-
 #include "common.h"
 #include "log.h"
 #include "preferences.h"
@@ -61,6 +55,7 @@
 #define PREF_GROUP_CONNECTION "connection"
 #define PREF_GROUP_ALIAS "alias"
 #define PREF_GROUP_OTR "otr"
+#define PREF_GROUP_PGP "pgp"
 
 #define INPBLOCK_DEFAULT 1000
 
@@ -81,8 +76,6 @@ void
 prefs_load(void)
 {
     GError *err;
-
-    log_info("Loading preferences");
     prefs_loc = _get_preferences_file();
 
     if (g_file_test(prefs_loc, G_FILE_TEST_EXISTS)) {
@@ -100,22 +93,12 @@ prefs_load(void)
         g_error_free(err);
     }
 
-    // move pre 0.4.6 OTR warn preferences to [ui] group
-    err = NULL;
-    gboolean otr_warn = g_key_file_get_boolean(prefs, PREF_GROUP_OTR, "warn", &err);
-    if (err == NULL) {
-        g_key_file_set_boolean(prefs, PREF_GROUP_UI, _get_key(PREF_OTR_WARN), otr_warn);
-        g_key_file_remove_key(prefs, PREF_GROUP_OTR, "warn", NULL);
-    } else {
-        g_error_free(err);
-    }
-
-    // move pre 0.4.6 titlebar preference
+    // move pre 0.4.7 otr.warn to enc.warn
     err = NULL;
-    gchar *old_titlebar = g_key_file_get_string(prefs, PREF_GROUP_UI, "titlebar", &err);
+    gboolean otr_warn = g_key_file_get_boolean(prefs, PREF_GROUP_UI, "otr.warn", &err);
     if (err == NULL) {
-        g_key_file_set_string(prefs, PREF_GROUP_UI, _get_key(PREF_TITLEBAR_SHOW), old_titlebar);
-        g_key_file_remove_key(prefs, PREF_GROUP_UI, "titlebar", NULL);
+        g_key_file_set_boolean(prefs, PREF_GROUP_UI, _get_key(PREF_ENC_WARN), otr_warn);
+        g_key_file_remove_key(prefs, PREF_GROUP_UI, "otr.warn", NULL);
     } else {
         g_error_free(err);
     }
@@ -181,7 +164,7 @@ prefs_get_string(preference_t pref)
 
     if (result == NULL) {
         if (def) {
-            return strdup(def);
+            return g_strdup(def);
         } else {
             return NULL;
         }
@@ -194,7 +177,7 @@ void
 prefs_free_string(char *pref)
 {
     if (pref) {
-        free(pref);
+        g_free(pref);
     }
     pref = NULL;
 }
@@ -507,6 +490,7 @@ _get_group(preference_t pref)
         case PREF_MUC_PRIVILEGES:
         case PREF_PRESENCE:
         case PREF_WRAP:
+        case PREF_WINS_AUTO_TIDY:
         case PREF_TIME:
         case PREF_TIME_STATUSBAR:
         case PREF_ROSTER:
@@ -515,7 +499,7 @@ _get_group(preference_t pref)
         case PREF_ROSTER_BY:
         case PREF_RESOURCE_TITLE:
         case PREF_RESOURCE_MESSAGE:
-        case PREF_OTR_WARN:
+        case PREF_ENC_WARN:
         case PREF_INPBLOCK_DYNAMIC:
             return PREF_GROUP_UI;
         case PREF_STATES:
@@ -550,6 +534,8 @@ _get_group(preference_t pref)
         case PREF_OTR_LOG:
         case PREF_OTR_POLICY:
             return PREF_GROUP_OTR;
+        case PREF_PGP_LOG:
+            return PREF_GROUP_PGP;
         default:
             return NULL;
     }
@@ -642,8 +628,6 @@ _get_key(preference_t pref)
             return "defaccount";
         case PREF_OTR_LOG:
             return "log";
-        case PREF_OTR_WARN:
-            return "otr.warn";
         case PREF_OTR_POLICY:
             return "policy";
         case PREF_LOG_ROTATE:
@@ -654,6 +638,8 @@ _get_key(preference_t pref)
             return "presence";
         case PREF_WRAP:
             return "wrap";
+        case PREF_WINS_AUTO_TIDY:
+            return "wins.autotidy";
         case PREF_TIME:
             return "time";
         case PREF_TIME_STATUSBAR:
@@ -672,6 +658,10 @@ _get_key(preference_t pref)
             return "resource.message";
         case PREF_INPBLOCK_DYNAMIC:
             return "inpblock.dynamic";
+        case PREF_ENC_WARN:
+            return "enc.warn";
+        case PREF_PGP_LOG:
+            return "log";
         default:
             return NULL;
     }
@@ -684,7 +674,7 @@ _get_default_boolean(preference_t pref)
 {
     switch (pref)
     {
-        case PREF_OTR_WARN:
+        case PREF_ENC_WARN:
         case PREF_AUTOAWAY_CHECK:
         case PREF_LOG_ROTATE:
         case PREF_LOG_SHARED:
@@ -700,6 +690,7 @@ _get_default_boolean(preference_t pref)
         case PREF_MUC_PRIVILEGES:
         case PREF_PRESENCE:
         case PREF_WRAP:
+        case PREF_WINS_AUTO_TIDY:
         case PREF_INPBLOCK_DYNAMIC:
         case PREF_RESOURCE_TITLE:
         case PREF_RESOURCE_MESSAGE:
@@ -720,6 +711,7 @@ _get_default_string(preference_t pref)
     switch (pref)
     {
         case PREF_AUTOAWAY_MODE:
+            return "off";
         case PREF_NOTIFY_ROOM:
             return "on";
         case PREF_OTR_LOG:
@@ -736,6 +728,8 @@ _get_default_string(preference_t pref)
             return "%H:%M:%S";
         case PREF_TIME_STATUSBAR:
             return "%H:%M";
+        case PREF_PGP_LOG:
+            return "redact";
         default:
             return NULL;
     }
diff --git a/src/config/preferences.h b/src/config/preferences.h
index 4455eca1..9e8d2898 100644
--- a/src/config/preferences.h
+++ b/src/config/preferences.h
@@ -38,11 +38,6 @@
 #include "config.h"
 
 #include <glib.h>
-#ifdef HAVE_NCURSESW_NCURSES_H
-#include <ncursesw/ncurses.h>
-#elif HAVE_NCURSES_H
-#include <ncurses.h>
-#endif
 
 #define PREFS_MIN_LOG_SIZE 64
 #define PREFS_MAX_LOG_SIZE 1048580
@@ -74,6 +69,7 @@ typedef enum {
     PREF_MUC_PRIVILEGES,
     PREF_PRESENCE,
     PREF_WRAP,
+    PREF_WINS_AUTO_TIDY,
     PREF_TIME,
     PREF_TIME_STATUSBAR,
     PREF_STATUSES,
@@ -102,11 +98,12 @@ typedef enum {
     PREF_LOG_ROTATE,
     PREF_LOG_SHARED,
     PREF_OTR_LOG,
-    PREF_OTR_WARN,
     PREF_OTR_POLICY,
     PREF_RESOURCE_TITLE,
     PREF_RESOURCE_MESSAGE,
-    PREF_INPBLOCK_DYNAMIC
+    PREF_INPBLOCK_DYNAMIC,
+    PREF_ENC_WARN,
+    PREF_PGP_LOG
 } preference_t;
 
 typedef struct prof_alias_t {
diff --git a/src/config/theme.c b/src/config/theme.c
index f73dee19..d870b371 100644
--- a/src/config/theme.c
+++ b/src/config/theme.c
@@ -207,12 +207,15 @@ theme_close(void)
 {
     if (theme) {
         g_key_file_free(theme);
+        theme = NULL;
     }
     if (theme_loc) {
         g_string_free(theme_loc, TRUE);
+        theme_loc = NULL;
     }
     if (bold_items) {
         g_hash_table_destroy(bold_items);
+        bold_items = NULL;
     }
 }
 
@@ -428,6 +431,7 @@ _load_preferences(void)
     _set_boolean_preference("flash", PREF_FLASH);
     _set_boolean_preference("splash", PREF_SPLASH);
     _set_boolean_preference("wrap", PREF_WRAP);
+    _set_boolean_preference("wins.autotidy", PREF_WINS_AUTO_TIDY);
     _set_string_preference("time", PREF_TIME);
     _set_string_preference("time.statusbar", PREF_TIME_STATUSBAR);
 
@@ -459,7 +463,7 @@ _load_preferences(void)
     _set_boolean_preference("presence", PREF_PRESENCE);
     _set_boolean_preference("intype", PREF_INTYPE);
 
-    _set_boolean_preference("otr.warn", PREF_OTR_WARN);
+    _set_boolean_preference("enc.warn", PREF_ENC_WARN);
 }
 
 static gchar *
@@ -613,4 +617,4 @@ theme_attrs(theme_item_t attrs)
         return result;
 
     }
-}
\ No newline at end of file
+}
diff --git a/src/config/theme.h b/src/config/theme.h
index 13099eb4..2ddbb17b 100644
--- a/src/config/theme.h
+++ b/src/config/theme.h
@@ -38,11 +38,6 @@
 #include "config.h"
 
 #include <glib.h>
-#ifdef HAVE_NCURSESW_NCURSES_H
-#include <ncursesw/ncurses.h>
-#elif HAVE_NCURSES_H
-#include <ncurses.h>
-#endif
 
 typedef enum {
     THEME_TEXT,
diff --git a/src/event/client_events.c b/src/event/client_events.c
index f0f763a6..2bf48234 100644
--- a/src/event/client_events.c
+++ b/src/event/client_events.c
@@ -32,16 +32,20 @@
  *
  */
 
+#include <stdlib.h>
 #include <glib.h>
 
 #include "config.h"
 #include "log.h"
 #include "ui/ui.h"
-#include "ui/windows.h"
+#include "window_list.h"
 #include "xmpp/xmpp.h"
 #ifdef HAVE_LIBOTR
 #include "otr/otr.h"
 #endif
+#ifdef HAVE_LIBGPGME
+#include "pgp/gpg.h"
+#endif
 
 jabber_conn_status_t
 cl_ev_connect_jid(const char * const jid, const char * const passwd, const char * const altdomain, const int port)
@@ -63,7 +67,20 @@ cl_ev_connect_account(ProfAccount *account)
 void
 cl_ev_presence_send(const resource_presence_t presence_type, const char * const msg, const int idle)
 {
-    presence_send(presence_type, msg, idle);
+    char *signed_status = NULL;
+
+#ifdef HAVE_LIBGPGME
+    char *account_name = jabber_get_account_name();
+    ProfAccount *account = accounts_get_account(account_name);
+    if (account->pgp_keyid) {
+        signed_status = p_gpg_sign(msg, account->pgp_keyid);
+    }
+    account_free(account);
+#endif
+
+    presence_send(presence_type, msg, idle, signed_status);
+
+    free(signed_status);
 }
 
 void
@@ -71,13 +88,70 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char * const msg)
 {
     chat_state_active(chatwin->state);
 
+// OTR suported, PGP supported
+#ifdef HAVE_LIBOTR
+#ifdef HAVE_LIBGPGME
+    prof_enc_t enc_mode = chatwin->enc_mode;
+    if (enc_mode == PROF_ENC_NONE || enc_mode == PROF_ENC_OTR) {
+        gboolean handled = otr_on_message_send(chatwin, msg);
+        if (!handled) {
+            char *id = message_send_chat(chatwin->barejid, msg);
+            chat_log_msg_out(chatwin->barejid, msg);
+            ui_outgoing_chat_msg(chatwin, msg, id);
+            free(id);
+        }
+    } else { // enc_mode = PROF_ENC_PGP
+        char *id = message_send_chat_pgp(chatwin->barejid, msg);
+        chat_log_pgp_msg_out(chatwin->barejid, msg);
+        ui_outgoing_chat_msg(chatwin, msg, id);
+        free(id);
+    }
+    return;
+#endif
+#endif
+
+// OTR supported, PGP unsupported
 #ifdef HAVE_LIBOTR
-    otr_on_message_send(chatwin, msg);
-#else
+#ifndef HAVE_LIBGPGME
+    gboolean handled = otr_on_message_send(chatwin, msg);
+    if (!handled) {
+        char *id = message_send_chat(chatwin->barejid, msg);
+        chat_log_msg_out(chatwin->barejid, msg);
+        ui_outgoing_chat_msg(chatwin, msg, id);
+        free(id);
+    }
+    return;
+#endif
+#endif
+
+// OTR unsupported, PGP supported
+#ifndef HAVE_LIBOTR
+#ifdef HAVE_LIBGPGME
+    prof_enc_t enc_mode = chatwin->enc_mode;
+    if (enc_mode == PROF_ENC_NONE) {
+        char *id = message_send_chat(chatwin->barejid, msg);
+        chat_log_msg_out(chatwin->barejid, msg);
+        ui_outgoing_chat_msg(chatwin, msg, id);
+        free(id);
+    } else if (enc_mode == PROF_ENC_PGP) {
+        char *id = message_send_chat_pgp(chatwin->barejid, msg);
+        chat_log_pgp_msg_out(chatwin->barejid, msg);
+        ui_outgoing_chat_msg(chatwin, msg, id);
+        free(id);
+    }
+    return;
+#endif
+#endif
+
+// OTR unsupported, PGP unsupported
+#ifndef HAVE_LIBOTR
+#ifndef HAVE_LIBGPGME
     char *id = message_send_chat(chatwin->barejid, msg);
     chat_log_msg_out(chatwin->barejid, msg);
     ui_outgoing_chat_msg(chatwin, msg, id);
     free(id);
+    return;
+#endif
 #endif
 }
 
@@ -92,4 +166,4 @@ cl_ev_send_priv_msg(ProfPrivateWin *privwin, const char * const msg)
 {
     message_send_private(privwin->fulljid, msg);
     ui_outgoing_private_msg(privwin, msg);
-}
\ No newline at end of file
+}
diff --git a/src/event/server_events.c b/src/event/server_events.c
index e2e910a3..df7a4418 100644
--- a/src/event/server_events.c
+++ b/src/event/server_events.c
@@ -43,10 +43,14 @@
 #include "config/preferences.h"
 #include "config/account.h"
 #include "roster_list.h"
+#include "window_list.h"
 
 #ifdef HAVE_LIBOTR
 #include "otr/otr.h"
 #endif
+#ifdef HAVE_LIBGPGME
+#include "pgp/gpg.h"
+#endif
 
 #include "ui/ui.h"
 
@@ -59,6 +63,10 @@ sv_ev_login_account_success(char *account_name)
     otr_on_connect(account);
 #endif
 
+#ifdef HAVE_LIBGPGME
+    p_gpg_on_connect(account->jid);
+#endif
+
     ui_handle_login_account_success(account);
 
     // attempt to rejoin rooms with passwords
@@ -93,6 +101,9 @@ sv_ev_lost_connection(void)
     muc_invites_clear();
     chat_sessions_clear();
     ui_disconnected();
+#ifdef HAVE_LIBGPGME
+    p_gpg_on_disconnect();
+#endif
 }
 
 void
@@ -135,9 +146,9 @@ sv_ev_room_subject(const char * const room, const char * const nick, const char
 
 void
 sv_ev_room_history(const char * const room_jid, const char * const nick,
-    GTimeVal tv_stamp, const char * const message)
+    GDateTime *timestamp, const char * const message)
 {
-    ui_room_history(room_jid, nick, tv_stamp, message);
+    ui_room_history(room_jid, nick, timestamp, message);
 }
 
 void
@@ -160,33 +171,146 @@ sv_ev_incoming_private_message(const char * const fulljid, char *message)
 }
 
 void
-sv_ev_carbon(char *barejid, char *message)
+sv_ev_outgoing_carbon(char *barejid, char *message)
 {
     ui_outgoing_chat_msg_carbon(barejid, message);
 }
 
 void
-sv_ev_incoming_message(char *barejid, char *resource, char *message)
+sv_ev_incoming_carbon(char *barejid, char *resource, char *message)
+{
+    gboolean new_win = FALSE;
+    ProfChatWin *chatwin = wins_get_chat(barejid);
+    if (!chatwin) {
+        ProfWin *window = wins_new_chat(barejid);
+        chatwin = (ProfChatWin*)window;
+        new_win = TRUE;
+    }
+
+    ui_incoming_msg(chatwin, resource, message, NULL, new_win);
+    chat_log_msg_in(barejid, message);
+}
+
+void
+sv_ev_incoming_message(char *barejid, char *resource, char *message, char *enc_message)
 {
+    gboolean new_win = FALSE;
+    ProfChatWin *chatwin = wins_get_chat(barejid);
+    if (!chatwin) {
+        ProfWin *window = wins_new_chat(barejid);
+        chatwin = (ProfChatWin*)window;
+        new_win = TRUE;
+    }
+
+// OTR suported, PGP supported
 #ifdef HAVE_LIBOTR
-    otr_on_message_recv(barejid, resource, message);
-#else
-    ui_incoming_msg(barejid, resource, message, NULL);
+#ifdef HAVE_LIBGPGME
+    prof_enc_t enc_mode = chatwin->enc_mode;
+    if (enc_message) {
+        if (enc_mode == PROF_ENC_OTR) {
+            win_println((ProfWin*)chatwin, "PGP encrypted message received whilst in OTR session.");
+        } else { // PROF_ENC_NONE, PROF_ENC_PGP
+            char *decrypted = p_gpg_decrypt(barejid, enc_message);
+            if (decrypted) {
+                if (enc_mode == PROF_ENC_NONE) {
+                    win_println((ProfWin*)chatwin, "PGP encryption enabled.");
+                }
+                ui_incoming_msg(chatwin, resource, decrypted, NULL, new_win);
+                chat_log_pgp_msg_in(barejid, decrypted);
+                chatwin->enc_mode = PROF_ENC_PGP;
+            } else {
+                ui_incoming_msg(chatwin, resource, message, NULL, new_win);
+                chat_log_msg_in(barejid, message);
+                chatwin->enc_mode = PROF_ENC_NONE;
+            }
+        }
+    } else {
+        if (enc_mode == PROF_ENC_PGP) {
+            win_println((ProfWin*)chatwin, "PGP encryption disabled.");
+            ui_incoming_msg(chatwin, resource, message, NULL, new_win);
+            chat_log_msg_in(barejid, message);
+            chatwin->enc_mode = PROF_ENC_NONE;
+        } else {
+            gboolean decrypted = FALSE;
+            char *otr_res = otr_on_message_recv(barejid, resource, message, &decrypted);
+            if (otr_res) {
+                ui_incoming_msg(chatwin, resource, otr_res, NULL, new_win);
+                chat_log_otr_msg_in(barejid, otr_res, decrypted);
+                otr_free_message(otr_res);
+            }
+        }
+    }
+    return;
+#endif
+#endif
+
+// OTR supported, PGP unsupported
+#ifdef HAVE_LIBOTR
+#ifndef HAVE_LIBGPGME
+    gboolean decrypted = FALSE;
+    char *otr_res = otr_on_message_recv(barejid, resource, message, &decrypted);
+    if (otr_res) {
+        ui_incoming_msg(chatwin, resource, otr_res, NULL, new_win);
+        chat_log_otr_msg_in(barejid, otr_res, decrypted);
+        otr_free_message(otr_res);
+    }
+    return;
+#endif
+#endif
+
+// OTR unsupported, PGP supported
+#ifndef HAVE_LIBOTR
+#ifdef HAVE_LIBGPGME
+    if (enc_message) {
+        char *decrypted = p_gpg_decrypt(barejid, enc_message);
+        if (decrypted) {
+            ui_incoming_msg(chatwin, resource, decrypted, NULL, new_win);
+            chat_log_pgp_msg_in(barejid, decrypted);
+            chatwin->enc_mode = PROF_ENC_PGP;
+        } else {
+            ui_incoming_msg(chatwin, resource, message, NULL, new_win);
+            chat_log_msg_in(barejid, message);
+            chatwin->enc_mode = PROF_ENC_NONE;
+        }
+    } else {
+        ui_incoming_msg(chatwin, resource, message, NULL, new_win);
+        chat_log_msg_in(barejid, message);
+        chatwin->enc_mode = PROF_ENC_NONE;
+    }
+    return;
+#endif
+#endif
+
+// OTR unsupported, PGP unsupported
+#ifndef HAVE_LIBOTR
+#ifndef HAVE_LIBGPGME
+    ui_incoming_msg(chatwin, resource, message, NULL, new_win);
     chat_log_msg_in(barejid, message);
+    chatwin->enc_mode = PROF_ENC_NONE;
+    return;
+#endif
 #endif
 }
 
 void
-sv_ev_delayed_private_message(const char * const fulljid, char *message, GTimeVal tv_stamp)
+sv_ev_delayed_private_message(const char * const fulljid, char *message, GDateTime *timestamp)
 {
-    ui_incoming_private_msg(fulljid, message, &tv_stamp);
+    ui_incoming_private_msg(fulljid, message, timestamp);
 }
 
 void
-sv_ev_delayed_message(char *barejid, char *message, GTimeVal tv_stamp)
+sv_ev_delayed_message(char *barejid, char *message, GDateTime *timestamp)
 {
-    ui_incoming_msg(barejid, NULL, message, &tv_stamp);
-    chat_log_msg_in_delayed(barejid, message, &tv_stamp);
+    gboolean new_win = FALSE;
+    ProfChatWin *chatwin = wins_get_chat(barejid);
+    if (!chatwin) {
+        ProfWin *window = wins_new_chat(barejid);
+        chatwin = (ProfChatWin*)window;
+        new_win = TRUE;
+    }
+
+    ui_incoming_msg(chatwin, NULL, message, timestamp, new_win);
+    chat_log_msg_in_delayed(barejid, message, timestamp);
 }
 
 void
@@ -280,7 +404,7 @@ sv_ev_contact_offline(char *barejid, char *resource, char *status)
 }
 
 void
-sv_ev_contact_online(char *barejid, Resource *resource, GDateTime *last_activity)
+sv_ev_contact_online(char *barejid, Resource *resource, GDateTime *last_activity, char *pgpsig)
 {
     gboolean updated = roster_update_presence(barejid, resource, last_activity);
 
@@ -288,6 +412,12 @@ sv_ev_contact_online(char *barejid, Resource *resource, GDateTime *last_activity
         ui_contact_online(barejid, resource, last_activity);
     }
 
+#ifdef HAVE_LIBGPGME
+    if (pgpsig) {
+        p_gpg_verify(barejid, pgpsig);
+    }
+#endif
+
     rosterwin_roster();
     chat_session_remove(barejid);
 }
diff --git a/src/event/server_events.h b/src/event/server_events.h
index 46d485da..4a3f86c2 100644
--- a/src/event/server_events.h
+++ b/src/event/server_events.h
@@ -47,13 +47,13 @@ void sv_ev_room_broadcast(const char *const room_jid,
     const char * const message);
 void sv_ev_room_subject(const char * const room, const char * const nick, const char * const subject);
 void sv_ev_room_history(const char * const room_jid, const char * const nick,
-    GTimeVal tv_stamp, const char * const message);
+    GDateTime *timestamp, const char * const message);
 void sv_ev_room_message(const char * const room_jid, const char * const nick,
     const char * const message);
-void sv_ev_incoming_message(char *barejid, char *resource, char *message);
+void sv_ev_incoming_message(char *barejid, char *resource, char *message, char *enc_message);
 void sv_ev_incoming_private_message(const char * const fulljid, char *message);
-void sv_ev_delayed_message(char *fulljid, char *message, GTimeVal tv_stamp);
-void sv_ev_delayed_private_message(const char * const fulljid, char *message, GTimeVal tv_stamp);
+void sv_ev_delayed_message(char *fulljid, char *message, GDateTime *timestamp);
+void sv_ev_delayed_private_message(const char * const fulljid, char *message, GDateTime *timestamp);
 void sv_ev_typing(char *barejid, char *resource);
 void sv_ev_paused(char *barejid, char *resource);
 void sv_ev_inactive(char *barejid, char *resource);
@@ -62,8 +62,7 @@ void sv_ev_gone(const char * const barejid, const char * const resource);
 void sv_ev_subscription(const char *from, jabber_subscr_t type);
 void sv_ev_message_receipt(char *barejid, char *id);
 void sv_ev_contact_offline(char *contact, char *resource, char *status);
-void sv_ev_contact_online(char *contact, Resource *resource,
-    GDateTime *last_activity);
+void sv_ev_contact_online(char *contact, Resource *resource, GDateTime *last_activity, char *pgpkey);
 void sv_ev_leave_room(const char * const room);
 void sv_ev_room_destroy(const char * const room);
 void sv_ev_room_occupant_offline(const char * const room, const char * const nick,
@@ -76,7 +75,8 @@ void sv_ev_room_occupent_kicked(const char * const room, const char * const nick
 void sv_ev_room_banned(const char * const room, const char * const actor, const char * const reason);
 void sv_ev_room_occupent_banned(const char * const room, const char * const nick, const char * const actor,
     const char * const reason);
-void sv_ev_carbon(char *barejid, char *message);
+void sv_ev_outgoing_carbon(char *barejid, char *message);
+void sv_ev_incoming_carbon(char *barejid, char *resource, char *message);
 void sv_ev_xmpp_stanza(const char * const msg);
 void sv_ev_muc_self_online(const char * const room, const char * const nick, gboolean config_required,
     const char * const role, const char * const affiliation, const char * const actor, const char * const reason,
diff --git a/src/event/ui_events.c b/src/event/ui_events.c
index ff1d7273..11296739 100644
--- a/src/event/ui_events.c
+++ b/src/event/ui_events.c
@@ -33,7 +33,7 @@
  */
 
 #include "ui/ui.h"
-#include "ui/windows.h"
+#include "window_list.h"
 
 void
 ui_ev_focus_win(ProfWin *win)
@@ -53,4 +53,4 @@ ProfPrivateWin*
 ui_ev_new_private_win(const char * const fulljid)
 {
     return ui_new_private_win(fulljid);
-}
\ No newline at end of file
+}
diff --git a/src/jid.c b/src/jid.c
index baeeb279..4eb05e87 100644
--- a/src/jid.c
+++ b/src/jid.c
@@ -91,6 +91,7 @@ jid_create(const gchar * const str)
         char *barejidraw = g_utf8_substring(trimmed, 0, g_utf8_pointer_to_offset(trimmed, slashp));
         result->barejid = g_utf8_strdown(barejidraw, -1);
         result->fulljid = g_strdup(trimmed);
+        g_free(barejidraw);
     } else {
         result->domainpart = g_strdup(domain_start);
         result->barejid = g_utf8_strdown(trimmed, -1);
@@ -192,4 +193,4 @@ jid_fulljid_or_barejid(Jid *jid)
     } else {
         return jid->barejid;
     }
-}
\ No newline at end of file
+}
diff --git a/src/log.c b/src/log.c
index a7727e8b..e4c7f305 100644
--- a/src/log.c
+++ b/src/log.c
@@ -34,6 +34,7 @@
 
 #include <assert.h>
 #include <errno.h>
+#include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -61,6 +62,16 @@ static GHashTable *logs;
 static GHashTable *groupchat_logs;
 static GDateTime *session_started;
 
+enum {
+    STDERR_BUFSIZE = 4000,
+    STDERR_RETRY_NR = 5,
+};
+static int stderr_inited;
+static log_level_t stderr_level;
+static int stderr_pipe[2];
+static char *stderr_buf;
+static GString *stderr_msg;
+
 struct dated_chat_log {
     gchar *filename;
     GDateTime *date;
@@ -80,7 +91,7 @@ static gchar * _get_main_log_file(void);
 static void _rotate_log_file(void);
 static char* _log_string_from_level(log_level_t level);
 static void _chat_log_chat(const char * const login, const char * const other,
-    const gchar * const msg, chat_log_direction_t direction, GTimeVal *tv_stamp);
+    const gchar * const msg, chat_log_direction_t direction, GDateTime *timestamp);
 
 void
 log_debug(const char * const msg, ...)
@@ -280,6 +291,23 @@ chat_log_otr_msg_out(const char * const barejid, const char * const msg)
 }
 
 void
+chat_log_pgp_msg_out(const char * const barejid, const char * const msg)
+{
+    if (prefs_get_boolean(PREF_CHLOG)) {
+        const char *jid = jabber_get_fulljid();
+        Jid *jidp = jid_create(jid);
+        char *pref_pgp_log = prefs_get_string(PREF_PGP_LOG);
+        if (strcmp(pref_pgp_log, "on") == 0) {
+            _chat_log_chat(jidp->barejid, barejid, msg, PROF_OUT_LOG, NULL);
+        } else if (strcmp(pref_pgp_log, "redact") == 0) {
+            _chat_log_chat(jidp->barejid, barejid, "[redacted]", PROF_OUT_LOG, NULL);
+        }
+        prefs_free_string(pref_pgp_log);
+        jid_destroy(jidp);
+    }
+}
+
+void
 chat_log_otr_msg_in(const char * const barejid, const char * const msg, gboolean was_decrypted)
 {
     if (prefs_get_boolean(PREF_CHLOG)) {
@@ -297,6 +325,23 @@ chat_log_otr_msg_in(const char * const barejid, const char * const msg, gboolean
 }
 
 void
+chat_log_pgp_msg_in(const char * const barejid, const char * const msg)
+{
+    if (prefs_get_boolean(PREF_CHLOG)) {
+        const char *jid = jabber_get_fulljid();
+        Jid *jidp = jid_create(jid);
+        char *pref_pgp_log = prefs_get_string(PREF_PGP_LOG);
+        if (strcmp(pref_pgp_log, "on") == 0) {
+            _chat_log_chat(jidp->barejid, barejid, msg, PROF_IN_LOG, NULL);
+        } else if (strcmp(pref_pgp_log, "redact") == 0) {
+            _chat_log_chat(jidp->barejid, barejid, "[redacted]", PROF_IN_LOG, NULL);
+        }
+        prefs_free_string(pref_pgp_log);
+        jid_destroy(jidp);
+    }
+}
+
+void
 chat_log_msg_in(const char * const barejid, const char * const msg)
 {
     if (prefs_get_boolean(PREF_CHLOG)) {
@@ -308,19 +353,19 @@ chat_log_msg_in(const char * const barejid, const char * const msg)
 }
 
 void
-chat_log_msg_in_delayed(const char * const barejid, const char * msg, GTimeVal *tv_stamp)
+chat_log_msg_in_delayed(const char * const barejid, const char * msg, GDateTime *timestamp)
 {
     if (prefs_get_boolean(PREF_CHLOG)) {
         const char *jid = jabber_get_fulljid();
         Jid *jidp = jid_create(jid);
-        _chat_log_chat(jidp->barejid, barejid, msg, PROF_IN_LOG, tv_stamp);
+        _chat_log_chat(jidp->barejid, barejid, msg, PROF_IN_LOG, timestamp);
         jid_destroy(jidp);
     }
 }
 
 static void
 _chat_log_chat(const char * const login, const char * const other,
-    const char * const msg, chat_log_direction_t direction, GTimeVal *tv_stamp)
+    const char * const msg, chat_log_direction_t direction, GDateTime *timestamp)
 {
     struct dated_chat_log *dated_log = g_hash_table_lookup(logs, other);
 
@@ -335,16 +380,9 @@ _chat_log_chat(const char * const login, const char * const other,
         g_hash_table_replace(logs, strdup(other), dated_log);
     }
 
-    gchar *date_fmt = NULL;
-    GDateTime *dt = NULL;
-    if (tv_stamp == NULL) {
-        dt = g_date_time_new_now_local();
-    } else {
-        dt = g_date_time_new_from_timeval_utc(tv_stamp);
-    }
-
-    date_fmt = g_date_time_format(dt, "%H:%M:%S");
+    if (timestamp == NULL) timestamp = g_date_time_new_now_local();
 
+    gchar *date_fmt = g_date_time_format(timestamp, "%H:%M:%S");
     FILE *logp = fopen(dated_log->filename, "a");
     g_chmod(dated_log->filename, S_IRUSR | S_IWUSR);
     if (logp) {
@@ -369,7 +407,6 @@ _chat_log_chat(const char * const login, const char * const other,
     }
 
     g_free(date_fmt);
-    g_date_time_unref(dt);
 }
 
 void
@@ -656,3 +693,95 @@ _log_string_from_level(log_level_t level)
             return "LOG";
     }
 }
+
+void
+log_stderr_handler(void)
+{
+    GString * const s = stderr_msg;
+    char * const buf = stderr_buf;
+    ssize_t size;
+    int retry = 0;
+    int i;
+
+    if (!stderr_inited)
+        return;
+
+    do {
+        size = read(stderr_pipe[0], buf, STDERR_BUFSIZE);
+        if (size == -1 && errno == EINTR && retry++ < STDERR_RETRY_NR)
+            continue;
+        if (size <= 0 || retry++ >= STDERR_RETRY_NR)
+            break;
+
+        for (i = 0; i < size; ++i) {
+            if (buf[i] == '\n') {
+                log_msg(stderr_level, "stderr", s->str);
+                g_string_assign(s, "");
+            } else
+                g_string_append_c(s, buf[i]);
+        }
+    } while (1);
+
+    if (s->len > 0 && s->str[0] != '\0') {
+        log_msg(stderr_level, "stderr", s->str);
+        g_string_assign(s, "");
+    }
+}
+
+void
+log_stderr_init(log_level_t level)
+{
+    int rc;
+    int flags;
+
+    rc = pipe(stderr_pipe);
+    if (rc != 0)
+        goto err;
+
+    flags = fcntl(stderr_pipe[0], F_GETFL);
+    rc = fcntl(stderr_pipe[0], F_SETFL, flags | O_NONBLOCK);
+    if (rc != 0)
+        goto err_close;
+
+    close(STDERR_FILENO);
+    rc = dup2(stderr_pipe[1], STDERR_FILENO);
+    if (rc < 0)
+        goto err_close;
+
+    stderr_buf = malloc(STDERR_BUFSIZE);
+    stderr_msg = g_string_sized_new(STDERR_BUFSIZE);
+    stderr_level = level;
+    stderr_inited = 1;
+
+    if (stderr_buf == NULL || stderr_msg == NULL) {
+        errno = ENOMEM;
+        goto err_free;
+    }
+    return;
+
+err_free:
+    if (stderr_msg != NULL)
+        g_string_free(stderr_msg, TRUE);
+    free(stderr_buf);
+err_close:
+    close(stderr_pipe[0]);
+    close(stderr_pipe[1]);
+err:
+    stderr_inited = 0;
+    log_error("Unable to init stderr log handler: %s", strerror(errno));
+}
+
+void
+log_stderr_close(void)
+{
+    if (!stderr_inited)
+        return;
+
+    /* handle remaining logs before close */
+    log_stderr_handler();
+    stderr_inited = 0;
+    free(stderr_buf);
+    g_string_free(stderr_msg, TRUE);
+    close(stderr_pipe[0]);
+    close(stderr_pipe[1]);
+}
diff --git a/src/log.h b/src/log.h
index 0689e2e6..99975670 100644
--- a/src/log.h
+++ b/src/log.h
@@ -63,14 +63,20 @@ void log_msg(log_level_t level, const char * const area,
     const char * const msg);
 log_level_t log_level_from_string(char *log_level);
 
+void log_stderr_init(log_level_t level);
+void log_stderr_close(void);
+void log_stderr_handler(void);
+
 void chat_log_init(void);
 
 void chat_log_msg_out(const char * const barejid, const char * const msg);
 void chat_log_otr_msg_out(const char * const barejid, const char * const msg);
+void chat_log_pgp_msg_out(const char * const barejid, const char * const msg);
 
 void chat_log_msg_in(const char * const barejid, const char * const msg);
-void chat_log_msg_in_delayed(const char * const barejid, const char * msg, GTimeVal *tv_stamp);
+void chat_log_msg_in_delayed(const char * const barejid, const char * msg, GDateTime *timestamp);
 void chat_log_otr_msg_in(const char * const barejid, const char * const msg, gboolean was_decrypted);
+void chat_log_pgp_msg_in(const char * const barejid, const char * const msg);
 
 void chat_log_close(void);
 GSList * chat_log_get_previous(const gchar * const login,
diff --git a/src/main.c b/src/main.c
index 3bb7eeb6..ea8f0cea 100644
--- a/src/main.c
+++ b/src/main.c
@@ -121,6 +121,12 @@ main(int argc, char **argv)
         g_print("OTR support: Disabled\n");
 #endif
 
+#ifdef HAVE_LIBGPGME
+        g_print("PGP support: Enabled\n");
+#else
+        g_print("PGP support: Disabled\n");
+#endif
+
         return 0;
     }
 
diff --git a/src/muc.c b/src/muc.c
index d283b55e..4474976d 100644
--- a/src/muc.c
+++ b/src/muc.c
@@ -34,6 +34,7 @@
 
 #include <stdlib.h>
 #include <string.h>
+#include <assert.h>
 
 #include <glib.h>
 
@@ -42,7 +43,7 @@
 #include "jid.h"
 #include "tools/autocomplete.h"
 #include "ui/ui.h"
-#include "ui/windows.h"
+#include "window_list.h"
 #include "muc.h"
 
 typedef struct _muc_room_t {
@@ -663,11 +664,11 @@ muc_roster_nick_change_complete(const char * const room,
 }
 
 char *
-muc_autocomplete(const char * const input)
+muc_autocomplete(ProfWin *window, const char * const input)
 {
-    win_type_t wintype = ui_current_win_type();
-    if (wintype == WIN_MUC) {
-        ProfMucWin *mucwin = wins_get_current_muc();
+    if (window->type == WIN_MUC) {
+        ProfMucWin *mucwin = (ProfMucWin*)window;
+        assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
         ChatRoom *chat_room = g_hash_table_lookup(rooms, mucwin->roomjid);
 
         if (chat_room && chat_room->nick_ac) {
@@ -978,4 +979,4 @@ _occupant_free(Occupant *occupant)
         free(occupant);
         occupant = NULL;
     }
-}
\ No newline at end of file
+}
diff --git a/src/muc.h b/src/muc.h
index ad96f3d9..eb636aff 100644
--- a/src/muc.h
+++ b/src/muc.h
@@ -40,6 +40,7 @@
 #include "contact.h"
 #include "jid.h"
 #include "tools/autocomplete.h"
+#include "ui/win_types.h"
 
 typedef enum {
     MUC_ROLE_NONE,
@@ -133,7 +134,7 @@ char* muc_subject(const char * const room);
 void muc_pending_broadcasts_add(const char * const room, const char * const message);
 GList * muc_pending_broadcasts(const char * const room);
 
-char* muc_autocomplete(const char * const input);
+char* muc_autocomplete(ProfWin *window, const char * const input);
 void muc_autocomplete_reset(const char * const room);
 
 gboolean muc_requires_config(const char * const room);
diff --git a/src/otr/otr.c b/src/otr/otr.c
index e568af56..e61a0e47 100644
--- a/src/otr/otr.c
+++ b/src/otr/otr.c
@@ -110,7 +110,7 @@ static void
 cb_inject_message(void *opdata, const char *accountname,
     const char *protocol, const char *recipient, const char *message)
 {
-    message_send_chat_encrypted(recipient, message);
+    message_send_chat_otr(recipient, message);
 }
 
 static void
@@ -163,6 +163,8 @@ otr_init(void)
     log_info("Initialising OTR");
     OTRL_INIT;
 
+    jid = NULL;
+
     ops.policy = cb_policy;
     ops.is_logged_in = cb_is_logged_in;
     ops.inject_message = cb_inject_message;
@@ -181,6 +183,7 @@ otr_shutdown(void)
 {
     if (jid) {
         free(jid);
+        jid = NULL;
     }
 }
 
@@ -269,12 +272,9 @@ otr_on_connect(ProfAccount *account)
     return;
 }
 
-void
-otr_on_message_recv(const char * const barejid, const char * const resource, const char * const message)
+char*
+otr_on_message_recv(const char * const barejid, const char * const resource, const char * const message, gboolean *was_decrypted)
 {
-    gboolean was_decrypted = FALSE;
-    char *decrypted;
-
     prof_otrpolicy_t policy = otr_get_policy(barejid);
     char *whitespace_base = strstr(message, OTRL_MESSAGE_TAG_BASE);
 
@@ -291,65 +291,65 @@ otr_on_message_recv(const char * const barejid, const char * const resource, con
                 memmove(whitespace_base, whitespace_base+tag_length, tag_length);
                 char *otr_query_message = otr_start_query();
                 cons_show("OTR Whitespace pattern detected. Attempting to start OTR session...");
-                message_send_chat_encrypted(barejid, otr_query_message);
+                message_send_chat_otr(barejid, otr_query_message);
             }
         }
     }
-    decrypted = otr_decrypt_message(barejid, message, &was_decrypted);
 
-    // internal OTR message
-    if (decrypted == NULL) {
-        return;
+    char *decrypted = otr_decrypt_message(barejid, message, was_decrypted);
+    if (!decrypted) { // internal OTR message
+        return NULL;
     }
 
-    if (policy == PROF_OTRPOLICY_ALWAYS && !was_decrypted && !whitespace_base) {
+    if (policy == PROF_OTRPOLICY_ALWAYS && *was_decrypted == FALSE && !whitespace_base) {
         char *otr_query_message = otr_start_query();
         cons_show("Attempting to start OTR session...");
-        message_send_chat_encrypted(barejid, otr_query_message);
+        message_send_chat_otr(barejid, otr_query_message);
     }
 
-    ui_incoming_msg(barejid, resource, decrypted, NULL);
-    chat_log_otr_msg_in(barejid, decrypted, was_decrypted);
-    otr_free_message(decrypted);
+    return decrypted;
 }
 
-void
+gboolean
 otr_on_message_send(ProfChatWin *chatwin, const char * const message)
 {
     char *id = NULL;
-
     prof_otrpolicy_t policy = otr_get_policy(chatwin->barejid);
 
+    // Send encrypted message
     if (otr_is_secure(chatwin->barejid)) {
         char *encrypted = otr_encrypt_message(chatwin->barejid, message);
         if (encrypted) {
-            id = message_send_chat_encrypted(chatwin->barejid, encrypted);
+            id = message_send_chat_otr(chatwin->barejid, encrypted);
             chat_log_otr_msg_out(chatwin->barejid, message);
             ui_outgoing_chat_msg(chatwin, message, id);
             otr_free_message(encrypted);
+            free(id);
+            return TRUE;
         } else {
             ui_win_error_line((ProfWin*)chatwin, "Failed to encrypt and send message.");
-            return;
+            return TRUE;
         }
+    }
 
-    } else if (policy == PROF_OTRPOLICY_ALWAYS) {
+    // show error if not secure and policy always
+    if (policy == PROF_OTRPOLICY_ALWAYS) {
         ui_win_error_line((ProfWin*)chatwin, "Failed to send message. OTR policy set to: always");
-        return;
+        return TRUE;
+    }
 
-    } else if (policy == PROF_OTRPOLICY_OPPORTUNISTIC) {
+    // tag and send for policy opportunistic
+    if (policy == PROF_OTRPOLICY_OPPORTUNISTIC) {
         char *otr_tagged_msg = otr_tag_message(message);
-        id = message_send_chat_encrypted(chatwin->barejid, otr_tagged_msg);
+        id = message_send_chat_otr(chatwin->barejid, otr_tagged_msg);
         ui_outgoing_chat_msg(chatwin, message, id);
         chat_log_msg_out(chatwin->barejid, message);
         free(otr_tagged_msg);
-
-    } else {
-        id = message_send_chat(chatwin->barejid, message);
-        ui_outgoing_chat_msg(chatwin, message, id);
-        chat_log_msg_out(chatwin->barejid, message);
+        free(id);
+        return TRUE;
     }
 
-    free(id);
+    return FALSE;
 }
 
 void
@@ -642,7 +642,8 @@ otr_get_their_fingerprint(const char * const recipient)
 prof_otrpolicy_t
 otr_get_policy(const char * const recipient)
 {
-    ProfAccount *account = accounts_get_account(jabber_get_account_name());
+    char *account_name = jabber_get_account_name();
+    ProfAccount *account = accounts_get_account(account_name);
     // check contact specific setting
     if (g_list_find_custom(account->otr_manual, recipient, (GCompareFunc)g_strcmp0)) {
         account_free(account);
@@ -747,4 +748,4 @@ void
 otr_free_message(char *message)
 {
     otrl_message_free(message);
-}
\ No newline at end of file
+}
diff --git a/src/otr/otr.h b/src/otr/otr.h
index e020c0c8..45abdc20 100644
--- a/src/otr/otr.h
+++ b/src/otr/otr.h
@@ -39,7 +39,7 @@
 #include <libotr/message.h>
 
 #include "config/accounts.h"
-#include "ui/window.h"
+#include "ui/ui.h"
 
 typedef enum {
     PROF_OTRPOLICY_MANUAL,
@@ -58,8 +58,8 @@ char* otr_start_query(void);
 void otr_poll(void);
 void otr_on_connect(ProfAccount *account);
 
-void otr_on_message_recv(const char * const barejid, const char * const resource, const char * const message);
-void otr_on_message_send(ProfChatWin *chatwin, const char * const message);
+char* otr_on_message_recv(const char * const barejid, const char * const resource, const char * const message, gboolean *was_decrypted);
+gboolean otr_on_message_send(ProfChatWin *chatwin, const char * const message);
 
 void otr_keygen(ProfAccount *account);
 
diff --git a/src/pgp/gpg.c b/src/pgp/gpg.c
new file mode 100644
index 00000000..724f1e9b
--- /dev/null
+++ b/src/pgp/gpg.c
@@ -0,0 +1,531 @@
+/*
+ * gpg.c
+ *
+ * Copyright (C) 2012 - 2015 James Booth <boothj5@gmail.com>
+ *
+ * This file is part of Profanity.
+ *
+ * Profanity is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Profanity is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Profanity.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * In addition, as a special exception, the copyright holders give permission to
+ * link the code of portions of this program with the OpenSSL library under
+ * certain conditions as described in each individual source file, and
+ * distribute linked combinations including the two.
+ *
+ * You must obey the GNU General Public License in all respects for all of the
+ * code used other than OpenSSL. If you modify file(s) with this exception, you
+ * may extend this exception to your version of the file(s), but you are not
+ * obligated to do so. If you do not wish to do so, delete this exception
+ * statement from your version. If you delete this exception statement from all
+ * source files in the program, then also delete it here.
+ *
+ */
+
+#include "config.h"
+
+#include <locale.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <gpgme.h>
+
+#include "pgp/gpg.h"
+#include "log.h"
+#include "common.h"
+
+#define PGP_SIGNATURE_HEADER "-----BEGIN PGP SIGNATURE-----"
+#define PGP_SIGNATURE_FOOTER "-----END PGP SIGNATURE-----"
+#define PGP_MESSAGE_HEADER "-----BEGIN PGP MESSAGE-----"
+#define PGP_MESSAGE_FOOTER "-----END PGP MESSAGE-----"
+
+static const char *libversion;
+static GHashTable *fingerprints;
+
+static gchar *fpsloc;
+static GKeyFile *fpskeyfile;
+
+static char* _remove_header_footer(char *str, const char * const footer);
+static char* _add_header_footer(const char * const str, const char * const header, const char * const footer);
+static void _save_fps(void);
+
+void
+p_gpg_init(void)
+{
+    libversion = gpgme_check_version(NULL);
+    log_debug("GPG: Found gpgme version: %s", libversion);
+    gpgme_set_locale(NULL, LC_CTYPE, setlocale(LC_CTYPE, NULL));
+
+    fingerprints = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+}
+
+void
+p_gpg_close(void)
+{
+    if (fingerprints) {
+        g_hash_table_destroy(fingerprints);
+        fingerprints = NULL;
+    }
+
+    if (fpskeyfile) {
+        g_key_file_free(fpskeyfile);
+        fpskeyfile = NULL;
+    }
+
+    free(fpsloc);
+    fpsloc = NULL;
+}
+
+void
+p_gpg_on_connect(const char * const barejid)
+{
+    gchar *data_home = xdg_get_data_home();
+    GString *fpsfile = g_string_new(data_home);
+    free(data_home);
+
+    gchar *account_dir = str_replace(barejid, "@", "_at_");
+    g_string_append(fpsfile, "/profanity/pgp/");
+    g_string_append(fpsfile, account_dir);
+    free(account_dir);
+
+    // mkdir if doesn't exist for account
+    errno = 0;
+    int res = g_mkdir_with_parents(fpsfile->str, S_IRWXU);
+    if (res == -1) {
+        char *errmsg = strerror(errno);
+        if (errmsg) {
+            log_error("Error creating directory: %s, %s", fpsfile->str, errmsg);
+        } else {
+            log_error("Error creating directory: %s", fpsfile->str);
+        }
+    }
+
+    // create or read fingerprints keyfile
+    g_string_append(fpsfile, "/fingerprints");
+    fpsloc = fpsfile->str;
+    g_string_free(fpsfile, FALSE);
+
+    if (g_file_test(fpsloc, G_FILE_TEST_EXISTS)) {
+        g_chmod(fpsloc, S_IRUSR | S_IWUSR);
+    }
+
+    fpskeyfile = g_key_file_new();
+    g_key_file_load_from_file(fpskeyfile, fpsloc, G_KEY_FILE_KEEP_COMMENTS, NULL);
+
+    // load each keyid
+    gsize len = 0;
+    gchar **jids = g_key_file_get_groups(fpskeyfile, &len);
+
+    gpgme_ctx_t ctx;
+    gpgme_error_t error = gpgme_new(&ctx);
+    if (error) {
+        log_error("GPG: Failed to create gpgme context. %s %s", gpgme_strsource(error), gpgme_strerror(error));
+        g_strfreev(jids);
+        return;
+    }
+
+    int i = 0;
+    for (i = 0; i < len; i++) {
+        GError *gerr = NULL;
+        gchar *jid = jids[i];
+        gchar *keyid = g_key_file_get_string(fpskeyfile, jid, "keyid", &gerr);
+        if (gerr) {
+            log_error("Error loading PGP key id for %s", jid);
+            g_error_free(gerr);
+        } else {
+            gpgme_key_t key = NULL;
+            error = gpgme_get_key(ctx, keyid, &key, 1);
+            if (error || key == NULL) {
+                log_error("GPG: Failed to get key. %s %s", gpgme_strsource(error), gpgme_strerror(error));
+                continue;
+            }
+
+            g_hash_table_replace(fingerprints, strdup(jid), strdup(key->subkeys->fpr));
+            gpgme_key_release(key);
+        }
+    }
+
+    gpgme_release(ctx);
+    g_strfreev(jids);
+
+    _save_fps();
+}
+
+void
+p_gpg_on_disconnect(void)
+{
+    if (fingerprints) {
+        g_hash_table_destroy(fingerprints);
+        fingerprints = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+    }
+
+    if (fpskeyfile) {
+        g_key_file_free(fpskeyfile);
+        fpskeyfile = NULL;
+    }
+
+    free(fpsloc);
+    fpsloc = NULL;
+}
+
+gboolean
+p_gpg_addkey(const char * const jid, const char * const keyid)
+{
+    gpgme_ctx_t ctx;
+    gpgme_error_t error = gpgme_new(&ctx);
+    if (error) {
+        log_error("GPG: Failed to create gpgme context. %s %s", gpgme_strsource(error), gpgme_strerror(error));
+        return FALSE;
+    }
+
+    gpgme_key_t key = NULL;
+    error = gpgme_get_key(ctx, keyid, &key, 1);
+    if (error || key == NULL) {
+        log_error("GPG: Failed to get key. %s %s", gpgme_strsource(error), gpgme_strerror(error));
+        return FALSE;
+    }
+
+    // save to ID keyfile
+    g_key_file_set_string(fpskeyfile, jid, "keyid", keyid);
+    _save_fps();
+
+    // update in memory fingerprint list
+    g_hash_table_replace(fingerprints, strdup(jid), strdup(key->subkeys->fpr));
+    gpgme_key_release(key);
+
+    return TRUE;
+}
+
+GSList *
+p_gpg_list_keys(void)
+{
+    gpgme_error_t error;
+    gpgme_ctx_t ctx;
+    gpgme_key_t key;
+    GSList *result = NULL;
+
+    error = gpgme_new(&ctx);
+    if (error) {
+        log_error("GPG: Could not list keys. %s %s", gpgme_strsource(error), gpgme_strerror(error));
+        return NULL;
+    }
+
+    error = gpgme_op_keylist_start(ctx, NULL, 1);
+    if (error == GPG_ERR_NO_ERROR) {
+        while (!error) {
+            error = gpgme_op_keylist_next(ctx, &key);
+            if (error) {
+                break;
+            }
+
+            ProfPGPKey *p_pgpkey = malloc(sizeof(ProfPGPKey));
+            p_pgpkey->id = strdup(key->subkeys->keyid);
+            p_pgpkey->name = strdup(key->uids->uid);
+            p_pgpkey->fp = strdup(key->subkeys->fpr);
+
+            result = g_slist_append(result, p_pgpkey);
+
+            gpgme_key_release(key);
+        }
+    } else {
+        log_error("GPG: Could not list keys. %s %s", gpgme_strsource(error), gpgme_strerror(error));
+    }
+
+    gpgme_release(ctx);
+
+    return result;
+}
+
+GHashTable *
+p_gpg_fingerprints(void)
+{
+    return fingerprints;
+}
+
+const char*
+p_gpg_libver(void)
+{
+    return libversion;
+}
+
+void
+p_gpg_free_key(ProfPGPKey *key)
+{
+    if (key) {
+        free(key->id);
+        free(key->name);
+        free(key->fp);
+        free(key);
+    }
+}
+
+gboolean
+p_gpg_available(const char * const barejid)
+{
+    char *fp = g_hash_table_lookup(fingerprints, barejid);
+    return (fp != NULL);
+}
+
+void
+p_gpg_verify(const char * const barejid, const char *const sign)
+{
+    if (!sign) {
+        return;
+    }
+
+    gpgme_ctx_t ctx;
+    gpgme_error_t error = gpgme_new(&ctx);
+    if (error) {
+        log_error("GPG: Failed to create gpgme context. %s %s", gpgme_strsource(error), gpgme_strerror(error));
+        return;
+    }
+
+    gpgme_data_t sign_data;
+    gpgme_data_t plain_data;
+    char *sign_with_header_footer = _add_header_footer(sign, PGP_SIGNATURE_HEADER, PGP_SIGNATURE_FOOTER);
+    gpgme_data_new_from_mem(&sign_data, sign_with_header_footer, strlen(sign_with_header_footer), 1);
+    gpgme_data_new(&plain_data);
+
+    error = gpgme_op_verify(ctx, sign_data, NULL, plain_data);
+    if (error) {
+        log_error("GPG: Failed to verify. %s %s", gpgme_strsource(error), gpgme_strerror(error));
+        gpgme_release(ctx);
+        return;
+    }
+
+    gpgme_verify_result_t result = gpgme_op_verify_result(ctx);
+    if (result) {
+        if (result->signatures) {
+            log_debug("Fingerprint found for %s: %s ", barejid, result->signatures->fpr);
+            g_hash_table_replace(fingerprints, strdup(barejid), strdup(result->signatures->fpr));
+        }
+    }
+
+    gpgme_data_release(sign_data);
+    gpgme_data_release(plain_data);
+}
+
+char*
+p_gpg_sign(const char * const str, const char * const fp)
+{
+    gpgme_ctx_t ctx;
+    gpgme_error_t error = gpgme_new(&ctx);
+    if (error) {
+        log_error("GPG: Failed to create gpgme context. %s %s", gpgme_strsource(error), gpgme_strerror(error));
+        return NULL;
+    }
+
+    gpgme_key_t key = NULL;
+    error = gpgme_get_key(ctx, fp, &key, 1);
+    if (error || key == NULL) {
+        log_error("GPG: Failed to get key. %s %s", gpgme_strsource(error), gpgme_strerror(error));
+        gpgme_release (ctx);
+        return NULL;
+    }
+
+    gpgme_signers_clear(ctx);
+    error = gpgme_signers_add(ctx, key);
+    if (error) {
+        log_error("GPG: Failed to load signer. %s %s", gpgme_strsource(error), gpgme_strerror(error));
+        gpgme_release(ctx);
+        return NULL;
+    }
+
+    gpgme_data_t str_data;
+    gpgme_data_t signed_data;
+    char *str_or_empty = NULL;
+    if (str) {
+        str_or_empty = strdup(str);
+    } else {
+        str_or_empty = strdup("");
+    }
+    gpgme_data_new_from_mem(&str_data, str_or_empty, strlen(str_or_empty), 1);
+    gpgme_data_new(&signed_data);
+
+    gpgme_set_armor(ctx,1);
+    error = gpgme_op_sign(ctx,str_data,signed_data,GPGME_SIG_MODE_DETACH);
+    if (error) {
+        log_error("GPG: Failed to sign string. %s %s", gpgme_strsource(error), gpgme_strerror(error));
+        gpgme_release(ctx);
+        return NULL;
+    }
+
+    char *result = NULL;
+    gpgme_data_release(str_data);
+
+    size_t len = 0;
+    char *signed_str = gpgme_data_release_and_get_mem(signed_data, &len);
+    if (signed_str) {
+        signed_str[len] = 0;
+        result = _remove_header_footer(signed_str, PGP_SIGNATURE_FOOTER);
+    }
+    gpgme_free(signed_str);
+    gpgme_release(ctx);
+    free(str_or_empty);
+
+    return result;
+}
+
+char *
+p_gpg_encrypt(const char * const barejid, const char * const message)
+{
+    char *fp = g_hash_table_lookup(fingerprints, barejid);
+
+    if (!fp) {
+        return NULL;
+    }
+
+    gpgme_key_t keys[2];
+
+    keys[0] = NULL;
+    keys[1] = NULL;
+
+    gpgme_ctx_t ctx;
+    gpgme_error_t error = gpgme_new(&ctx);
+    if (error) {
+        log_error("GPG: Failed to create gpgme context. %s %s", gpgme_strsource(error), gpgme_strerror(error));
+        return NULL;
+    }
+
+    gpgme_key_t key;
+    error = gpgme_get_key(ctx, fp, &key, 0);
+    if (error || key == NULL) {
+        log_error("GPG: Failed to get key. %s %s", gpgme_strsource(error), gpgme_strerror(error));
+        gpgme_release(ctx);
+        return NULL;
+    }
+
+    keys[0] = key;
+
+    gpgme_data_t plain;
+    gpgme_data_t cipher;
+    gpgme_data_new_from_mem(&plain, message, strlen(message), 1);
+    gpgme_data_new(&cipher);
+
+    gpgme_set_armor(ctx, 1);
+    error = gpgme_op_encrypt(ctx, keys, GPGME_ENCRYPT_ALWAYS_TRUST, plain, cipher);
+    if (error) {
+        log_error("GPG: Failed to encrypt message. %s %s", gpgme_strsource(error), gpgme_strerror(error));
+        gpgme_release(ctx);
+        return NULL;
+    }
+    gpgme_data_release(plain);
+
+    char *cipher_str = NULL;
+    char *result = NULL;
+    size_t len;
+    cipher_str = gpgme_data_release_and_get_mem(cipher, &len);
+    if (cipher_str) {
+        result = _remove_header_footer(cipher_str, PGP_MESSAGE_FOOTER);
+    }
+
+    gpgme_free(cipher_str);
+    gpgme_release(ctx);
+
+    return result;
+}
+
+char *
+p_gpg_decrypt(const char * const barejid, const char * const cipher)
+{
+    char *cipher_with_headers = _add_header_footer(cipher, PGP_MESSAGE_HEADER, PGP_MESSAGE_FOOTER);
+
+    gpgme_ctx_t ctx;
+    gpgme_error_t error = gpgme_new(&ctx);
+    if (error) {
+        log_error("GPG: Failed to create gpgme context. %s %s", gpgme_strsource(error), gpgme_strerror(error));
+        return NULL;
+    }
+
+    gpgme_data_t plain_data;
+    gpgme_data_t cipher_data;
+    gpgme_data_new_from_mem (&cipher_data, cipher_with_headers, strlen(cipher_with_headers), 1);
+    gpgme_data_new(&plain_data);
+
+    error = gpgme_op_decrypt(ctx, cipher_data, plain_data);
+    if (error) {
+        log_error("GPG: Failed to encrypt message. %s %s", gpgme_strsource(error), gpgme_strerror(error));
+        gpgme_release(ctx);
+        return NULL;
+    }
+
+    gpgme_data_release(cipher_data);
+
+    size_t len = 0;
+    char *plain_str = gpgme_data_release_and_get_mem(plain_data, &len);
+    char *result = NULL;
+    if (plain_str) {
+        plain_str[len] = 0;
+        result = g_strdup(plain_str);
+    }
+    gpgme_free(plain_str);
+
+    gpgme_release(ctx);
+
+    return result;
+}
+
+static char*
+_remove_header_footer(char *str, const char * const footer)
+{
+    int pos = 0;
+    int newlines = 0;
+
+    while (newlines < 3) {
+        if (str[pos] == '\n') {
+            newlines++;
+        }
+        pos++;
+
+        if (str[pos] == '\0') {
+            return NULL;
+        }
+    }
+
+    char *stripped = strdup(&str[pos]);
+    char *footer_start = g_strrstr(stripped, footer);
+    footer_start[0] = '\0';
+
+    return stripped;
+}
+
+static char*
+_add_header_footer(const char * const str, const char * const header, const char * const footer)
+{
+    GString *result_str = g_string_new("");
+
+    g_string_append(result_str, header);
+    g_string_append(result_str, "\n\n");
+    g_string_append(result_str, str);
+    g_string_append(result_str, "\n");
+    g_string_append(result_str, footer);
+
+    char *result = result_str->str;
+    g_string_free(result_str, FALSE);
+
+    return result;
+}
+
+static void
+_save_fps(void)
+{
+    gsize g_data_size;
+    gchar *g_fps_data = g_key_file_to_data(fpskeyfile, &g_data_size, NULL);
+    g_file_set_contents(fpsloc, g_fps_data, g_data_size, NULL);
+    g_chmod(fpsloc, S_IRUSR | S_IWUSR);
+    g_free(g_fps_data);
+}
diff --git a/src/pgp/gpg.h b/src/pgp/gpg.h
new file mode 100644
index 00000000..07c99465
--- /dev/null
+++ b/src/pgp/gpg.h
@@ -0,0 +1,59 @@
+/*
+ * gpg.h
+ *
+ * Copyright (C) 2012 - 2015 James Booth <boothj5@gmail.com>
+ *
+ * This file is part of Profanity.
+ *
+ * Profanity is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Profanity is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Profanity.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * In addition, as a special exception, the copyright holders give permission to
+ * link the code of portions of this program with the OpenSSL library under
+ * certain conditions as described in each individual source file, and
+ * distribute linked combinations including the two.
+ *
+ * You must obey the GNU General Public License in all respects for all of the
+ * code used other than OpenSSL. If you modify file(s) with this exception, you
+ * may extend this exception to your version of the file(s), but you are not
+ * obligated to do so. If you do not wish to do so, delete this exception
+ * statement from your version. If you delete this exception statement from all
+ * source files in the program, then also delete it here.
+ *
+ */
+
+#ifndef GPG_H
+#define GPG_H
+
+typedef struct pgp_key_t {
+    char *id;
+    char *name;
+    char *fp;
+} ProfPGPKey;
+
+void p_gpg_init(void);
+void p_gpg_close(void);
+void p_gpg_on_connect(const char * const barejid);
+void p_gpg_on_disconnect(void);
+GSList* p_gpg_list_keys(void);
+gboolean p_gpg_addkey(const char * const jid, const char * const keyid);
+GHashTable* p_gpg_fingerprints(void);
+gboolean p_gpg_available(const char * const barejid);
+const char* p_gpg_libver(void);
+void p_gpg_free_key(ProfPGPKey *key);
+char* p_gpg_sign(const char * const str, const char * const fp);
+void p_gpg_verify(const char * const barejid, const char *const sign);
+char* p_gpg_encrypt(const char * const barejid, const char * const message);
+char* p_gpg_decrypt(const char * const barejid, const char * const cipher);
+
+#endif
diff --git a/src/profanity.c b/src/profanity.c
index 55a73430..a56eb5e9 100644
--- a/src/profanity.c
+++ b/src/profanity.c
@@ -41,6 +41,7 @@
 #include <signal.h>
 #include <stdlib.h>
 #include <string.h>
+#include <assert.h>
 
 #include <glib.h>
 
@@ -59,10 +60,13 @@
 #ifdef HAVE_LIBOTR
 #include "otr/otr.h"
 #endif
+#ifdef HAVE_LIBGPGME
+#include "pgp/gpg.h"
+#endif
 #include "resource.h"
 #include "xmpp/xmpp.h"
 #include "ui/ui.h"
-#include "ui/windows.h"
+#include "window_list.h"
 #include "event/client_events.h"
 
 static void _check_autoaway(void);
@@ -85,11 +89,13 @@ prof_run(const int disable_tls, char *log_level, char *account_name)
 
     char *line = NULL;
     while(cont) {
+        log_stderr_handler();
         _check_autoaway();
 
         line = ui_readline();
         if (line) {
-            cont = cmd_process_input(line);
+            ProfWin *window = wins_get_current();
+            cont = cmd_process_input(window, line);
             free(line);
             line = NULL;
         } else {
@@ -100,7 +106,7 @@ prof_run(const int disable_tls, char *log_level, char *account_name)
         otr_poll();
 #endif
         notify_remind();
-        jabber_process_events();
+        jabber_process_events(10);
         ui_update();
     }
 }
@@ -129,11 +135,12 @@ prof_handle_idle(void)
 void
 prof_handle_activity(void)
 {
-    win_type_t win_type = ui_current_win_type();
     jabber_conn_status_t status = jabber_get_connection_status();
+    ProfWin *current = wins_get_current();
 
-    if ((status == JABBER_CONNECTED) && (win_type == WIN_CHAT)) {
-        ProfChatWin *chatwin = wins_get_current_chat();
+    if ((status == JABBER_CONNECTED) && (current->type == WIN_CHAT)) {
+        ProfChatWin *chatwin = (ProfChatWin*)current;
+        assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
         chat_state_handle_typing(chatwin->barejid, chatwin->state);
     }
 }
@@ -141,12 +148,13 @@ prof_handle_activity(void)
 static void
 _connect_default(const char * const account)
 {
+    ProfWin *window = wins_get_current();
     if (account) {
-        cmd_execute_connect(account);
+        cmd_execute_connect(window, account);
     } else {
         char *pref_connect_account = prefs_get_string(PREF_CONNECT_ACCOUNT);
         if (pref_connect_account) {
-            cmd_execute_connect(pref_connect_account);
+            cmd_execute_connect(window, pref_connect_account);
             prefs_free_string(pref_connect_account);
         }
     }
@@ -218,6 +226,7 @@ _init(const int disable_tls, char *log_level)
     log_level_t prof_log_level = log_level_from_string(log_level);
     prefs_load();
     log_init(prof_log_level);
+    log_stderr_init(PROF_LEVEL_ERROR);
     if (strcmp(PACKAGE_STATUS, "development") == 0) {
 #ifdef HAVE_GIT_VERSION
             log_info("Starting Profanity (%sdev.%s.%s)...", PACKAGE_VERSION, PROF_GIT_BRANCH, PROF_GIT_REVISION);
@@ -242,6 +251,9 @@ _init(const int disable_tls, char *log_level)
 #ifdef HAVE_LIBOTR
     otr_init();
 #endif
+#ifdef HAVE_LIBGPGME
+    p_gpg_init();
+#endif
     atexit(_shutdown);
     ui_input_nonblocking(TRUE);
 }
@@ -266,12 +278,16 @@ _shutdown(void)
 #ifdef HAVE_LIBOTR
     otr_shutdown();
 #endif
+#ifdef HAVE_LIBGPGME
+    p_gpg_close();
+#endif
     chat_log_close();
-    prefs_close();
     theme_close();
     accounts_close();
     cmd_uninit();
+    log_stderr_close();
     log_close();
+    prefs_close();
 }
 
 static void
diff --git a/src/ui/buffer.c b/src/ui/buffer.c
index 0848b60f..b4771f1a 100644
--- a/src/ui/buffer.c
+++ b/src/ui/buffer.c
@@ -87,7 +87,7 @@ buffer_push(ProfBuff buffer, const char show_char, GDateTime *time,
     e->show_char = show_char;
     e->flags = flags;
     e->theme_item = theme_item;
-    e->time = time;
+    e->time = g_date_time_ref(time);
     e->from = strdup(from);
     e->message = strdup(message);
     e->receipt = receipt;
@@ -136,4 +136,4 @@ _free_entry(ProfBuffEntry *entry)
         free(entry->receipt);
     }
     free(entry);
-}
\ No newline at end of file
+}
diff --git a/src/ui/console.c b/src/ui/console.c
index 895efda4..8bf873a5 100644
--- a/src/ui/console.c
+++ b/src/ui/console.c
@@ -50,7 +50,7 @@
 #include "config/preferences.h"
 #include "config/theme.h"
 #include "ui/window.h"
-#include "ui/windows.h"
+#include "window_list.h"
 #include "ui/ui.h"
 #include "ui/statusbar.h"
 #include "xmpp/xmpp.h"
@@ -710,6 +710,10 @@ cons_show_account(ProfAccount *account)
         g_string_free(always, TRUE);
     }
 
+    if (account->pgp_keyid) {
+        cons_show   ("PGP Key ID        : %s", account->pgp_keyid);
+    }
+
     cons_show       ("Priority          : chat:%d, online:%d, away:%d, xa:%d, dnd:%d",
         account->priority_chat, account->priority_online, account->priority_away,
         account->priority_xa, account->priority_dnd);
@@ -872,6 +876,25 @@ cons_wrap_setting(void)
 }
 
 void
+cons_winstidy_setting(void)
+{
+    if (prefs_get_boolean(PREF_WINS_AUTO_TIDY))
+        cons_show("Window Auto Tidy (/winstidy)  : ON");
+    else
+        cons_show("Window Auto Tidy (/winstidy)  : OFF");
+}
+
+void
+cons_encwarn_setting(void)
+{
+    if (prefs_get_boolean(PREF_ENC_WARN)) {
+        cons_show("Warn unencrypted (/encwarn)   : ON");
+    } else {
+        cons_show("Warn unencrypted (/encwarn)   : OFF");
+    }
+}
+
+void
 cons_presence_setting(void)
 {
     if (prefs_get_boolean(PREF_PRESENCE))
@@ -1018,6 +1041,7 @@ cons_roster_setting(void)
 
     char *by = prefs_get_string(PREF_ROSTER_BY);
     cons_show("Roster by (/roster)           : %s", by);
+    prefs_free_string(by);
 
     int size = prefs_get_roster_size();
     cons_show("Roster size (/roster)         : %d", size);
@@ -1033,6 +1057,7 @@ cons_show_ui_prefs(void)
     cons_flash_setting();
     cons_splash_setting();
     cons_wrap_setting();
+    cons_winstidy_setting();
     cons_time_setting();
     cons_resource_setting();
     cons_vercheck_setting();
@@ -1042,6 +1067,7 @@ cons_show_ui_prefs(void)
     cons_roster_setting();
     cons_privileges_setting();
     cons_titlebar_setting();
+    cons_encwarn_setting();
     cons_presence_setting();
     cons_inpblock_setting();
 
@@ -1382,12 +1408,6 @@ cons_show_otr_prefs(void)
     cons_show("OTR policy (/otr policy) : %s", policy_value);
     prefs_free_string(policy_value);
 
-    if (prefs_get_boolean(PREF_OTR_WARN)) {
-        cons_show("Warn non-OTR (/otr warn) : ON");
-    } else {
-        cons_show("Warn non-OTR (/otr warn) : OFF");
-    }
-
     char *log_value = prefs_get_string(PREF_OTR_LOG);
     if (strcmp(log_value, "on") == 0) {
         cons_show("OTR logging (/otr log)   : ON");
@@ -1402,6 +1422,25 @@ cons_show_otr_prefs(void)
 }
 
 void
+cons_show_pgp_prefs(void)
+{
+    cons_show("PGP preferences:");
+    cons_show("");
+
+    char *log_value = prefs_get_string(PREF_PGP_LOG);
+    if (strcmp(log_value, "on") == 0) {
+        cons_show("PGP logging (/pgp log)   : ON");
+    } else if (strcmp(log_value, "off") == 0) {
+        cons_show("PGP logging (/pgp log)   : OFF");
+    } else {
+        cons_show("PGP logging (/pgp log)   : Redacted");
+    }
+    prefs_free_string(log_value);
+
+    cons_alert();
+}
+
+void
 cons_show_themes(GSList *themes)
 {
     cons_show("");
@@ -1437,6 +1476,8 @@ cons_prefs(void)
     cons_show("");
     cons_show_otr_prefs();
     cons_show("");
+    cons_show_pgp_prefs();
+    cons_show("");
 
     cons_alert();
 }
@@ -1548,7 +1589,8 @@ cons_show_contacts(GSList *list)
 void
 cons_alert(void)
 {
-    if (ui_current_win_type() != WIN_CONSOLE) {
+    ProfWin *current = wins_get_current();
+    if (current->type != WIN_CONSOLE) {
         status_bar_new(1);
     }
 }
diff --git a/src/ui/core.c b/src/ui/core.c
index e7059ef0..fa419e45 100644
--- a/src/ui/core.c
+++ b/src/ui/core.c
@@ -72,7 +72,7 @@
 #include "ui/statusbar.h"
 #include "ui/inputwin.h"
 #include "ui/window.h"
-#include "ui/windows.h"
+#include "window_list.h"
 #include "xmpp/xmpp.h"
 #include "event/ui_events.h"
 
@@ -254,7 +254,7 @@ ui_resize(void)
     resizeterm(w.ws_row, w.ws_col);
     refresh();
 
-    log_info("Resizing UI");
+    log_debug("Resizing UI");
     title_bar_resize();
     wins_resize_all();
     status_bar_resize();
@@ -405,12 +405,6 @@ ui_get_chat_recipients(void)
     return recipients;
 }
 
-ProfChatWin *
-ui_get_current_chat(void)
-{
-    return wins_get_current_chat();
-}
-
 void
 ui_message_receipt(const char * const barejid, const char * const id)
 {
@@ -422,25 +416,16 @@ ui_message_receipt(const char * const barejid, const char * const id)
 }
 
 void
-ui_incoming_msg(const char * const barejid, const char * const resource, const char * const message, GTimeVal *tv_stamp)
+ui_incoming_msg(ProfChatWin *chatwin, const char * const resource, const char * const message, GDateTime *timestamp, gboolean win_created)
 {
-    gboolean win_created = FALSE;
-
-    ProfChatWin *chatwin = wins_get_chat(barejid);
-    if (chatwin == NULL) {
-        ProfWin *window = wins_new_chat(barejid);
-        chatwin = (ProfChatWin*)window;
-        win_created = TRUE;
-    }
-
-    ProfWin *window = (ProfWin*) chatwin;
+    ProfWin *window = (ProfWin*)chatwin;
     int num = wins_get_num(window);
 
-    char *display_name = roster_get_msg_display_name(barejid, resource);
+    char *display_name = roster_get_msg_display_name(chatwin->barejid, resource);
 
     // currently viewing chat window with sender
     if (wins_is_current(window)) {
-        win_print_incoming_message(window, tv_stamp, display_name, message);
+        win_print_incoming_message(window, timestamp, display_name, message);
         title_bar_set_typing(FALSE);
         status_bar_active(num);
 
@@ -455,18 +440,18 @@ ui_incoming_msg(const char * const barejid, const char * const resource, const c
 
         chatwin->unread++;
         if (prefs_get_boolean(PREF_CHLOG) && prefs_get_boolean(PREF_HISTORY)) {
-            _win_show_history(chatwin, barejid);
+            _win_show_history(chatwin, chatwin->barejid);
         }
 
         // show users status first, when receiving message via delayed delivery
-        if (tv_stamp && (win_created)) {
-            PContact pcontact = roster_get_contact(barejid);
+        if (timestamp && win_created) {
+            PContact pcontact = roster_get_contact(chatwin->barejid);
             if (pcontact) {
                 win_show_contact(window, pcontact);
             }
         }
 
-        win_print_incoming_message(window, tv_stamp, display_name, message);
+        win_print_incoming_message(window, timestamp, display_name, message);
     }
 
     if (prefs_get_boolean(PREF_BEEP)) {
@@ -481,7 +466,7 @@ ui_incoming_msg(const char * const barejid, const char * const resource, const c
 }
 
 void
-ui_incoming_private_msg(const char * const fulljid, const char * const message, GTimeVal *tv_stamp)
+ui_incoming_private_msg(const char * const fulljid, const char * const message, GDateTime *timestamp)
 {
     char *display_from = NULL;
     display_from = get_nick_from_full_jid(fulljid);
@@ -497,7 +482,7 @@ ui_incoming_private_msg(const char * const fulljid, const char * const message,
 
     // currently viewing chat window with sender
     if (wins_is_current(window)) {
-        win_print_incoming_message(window, tv_stamp, display_from, message);
+        win_print_incoming_message(window, timestamp, display_from, message);
         title_bar_set_typing(FALSE);
         status_bar_active(num);
 
@@ -506,7 +491,7 @@ ui_incoming_private_msg(const char * const fulljid, const char * const message,
         privatewin->unread++;
         status_bar_new(num);
         cons_show_incoming_message(display_from, num);
-        win_print_incoming_message(window, tv_stamp, display_from, message);
+        win_print_incoming_message(window, timestamp, display_from, message);
 
         if (prefs_get_boolean(PREF_FLASH)) {
             flash();
@@ -695,7 +680,8 @@ ui_invalid_command_usage(const char * const usage, void (*setting_func)(void))
     } else {
         cons_show("");
         cons_show("Usage: %s", usage);
-        if (ui_current_win_type() == WIN_CHAT) {
+        ProfWin *current = wins_get_current();
+        if (current->type == WIN_CHAT) {
             char usage_cpy[strlen(usage) + 8];
             sprintf(usage_cpy, "Usage: %s", usage);
             ui_current_print_line(usage_cpy);
@@ -909,7 +895,7 @@ ui_gone_secure(const char * const barejid, gboolean trusted)
     }
 
     chatwin->enc_mode = PROF_ENC_OTR;
-    chatwin->is_trusted = trusted;
+    chatwin->otr_is_trusted = trusted;
     if (trusted) {
         win_print(window, '!', NULL, 0, THEME_OTR_STARTED_TRUSTED, "", "OTR session started (trusted).");
     } else {
@@ -937,7 +923,7 @@ ui_gone_insecure(const char * const barejid)
     ProfChatWin *chatwin = wins_get_chat(barejid);
     if (chatwin) {
         chatwin->enc_mode = PROF_ENC_NONE;
-        chatwin->is_trusted = FALSE;
+        chatwin->otr_is_trusted = FALSE;
 
         ProfWin *window = (ProfWin*)chatwin;
         win_print(window, '!', NULL, 0, THEME_OTR_ENDED, "", "OTR session ended.");
@@ -1056,7 +1042,7 @@ ui_trust(const char * const barejid)
     ProfChatWin *chatwin = wins_get_chat(barejid);
     if (chatwin) {
         chatwin->enc_mode = PROF_ENC_OTR;
-        chatwin->is_trusted = TRUE;
+        chatwin->otr_is_trusted = TRUE;
 
         ProfWin *window = (ProfWin*)chatwin;
         win_print(window, '!', NULL, 0, THEME_OTR_TRUSTED, "", "OTR session trusted.");
@@ -1072,7 +1058,7 @@ ui_untrust(const char * const barejid)
     ProfChatWin *chatwin = wins_get_chat(barejid);
     if (chatwin) {
         chatwin->enc_mode = PROF_ENC_OTR;
-        chatwin->is_trusted = FALSE;
+        chatwin->otr_is_trusted = FALSE;
 
         ProfWin *window = (ProfWin*)chatwin;
         win_print(window, '!', NULL, 0, THEME_OTR_UNTRUSTED, "", "OTR session untrusted.");
@@ -1083,12 +1069,6 @@ ui_untrust(const char * const barejid)
 }
 
 void
-ui_clear_current(void)
-{
-    wins_clear_current();
-}
-
-void
 ui_close_win(int index)
 {
     ProfWin *window = wins_get_by_num(index);
@@ -1105,16 +1085,10 @@ ui_close_win(int index)
     status_bar_active(1);
 }
 
-void
+gboolean
 ui_tidy_wins(void)
 {
-    gboolean tidied = wins_tidy();
-
-    if (tidied) {
-        cons_show("Windows tidied.");
-    } else {
-        cons_show("No tidy needed.");
-    }
+    return wins_tidy();
 }
 
 void
@@ -1163,26 +1137,6 @@ ui_swap_wins(int source_win, int target_win)
 }
 
 win_type_t
-ui_current_win_type(void)
-{
-    ProfWin *current = wins_get_current();
-    return current->type;
-}
-
-gboolean
-ui_current_win_is_otr(void)
-{
-    ProfWin *current = wins_get_current();
-    if (current->type == WIN_CHAT) {
-        ProfChatWin *chatwin = (ProfChatWin*)current;
-        assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
-        return chatwin->enc_mode == PROF_ENC_OTR;
-    } else {
-        return FALSE;
-    }
-}
-
-win_type_t
 ui_win_type(int index)
 {
     ProfWin *window = wins_get_by_num(index);
@@ -1314,12 +1268,6 @@ ui_new_chat_win(const char * const barejid)
     ProfWin *window = wins_new_chat(barejid);
     ProfChatWin *chatwin = (ProfChatWin *)window;
 
-#ifdef HAVE_LIBOTR
-    if (otr_is_secure(barejid)) {
-        chatwin->enc_mode = PROF_ENC_OTR;
-    }
-#endif
-
     if (prefs_get_boolean(PREF_CHLOG) && prefs_get_boolean(PREF_HISTORY)) {
         _win_show_history(chatwin, barejid);
     }
@@ -1717,7 +1665,7 @@ ui_room_nick_change(const char * const roomjid, const char * const nick)
 
 void
 ui_room_history(const char * const roomjid, const char * const nick,
-    GTimeVal tv_stamp, const char * const message)
+    GDateTime *timestamp, const char * const message)
 {
     ProfWin *window = (ProfWin*)wins_get_muc(roomjid);
     if (window == NULL) {
@@ -1736,7 +1684,7 @@ ui_room_history(const char * const roomjid, const char * const nick,
             g_string_append(line, message);
         }
 
-        win_print(window, '-', &tv_stamp, NO_COLOUR_DATE, 0, "", line->str);
+        win_print(window, '-', timestamp, NO_COLOUR_DATE, 0, "", line->str);
         g_string_free(line, TRUE);
     }
 }
@@ -2229,6 +2177,12 @@ ui_clear_win_title(void)
 }
 
 void
+ui_clear_win(ProfWin *window)
+{
+    win_clear(window);
+}
+
+void
 ui_goodbye_title(void)
 {
     int result = system("/bin/echo -ne \"\033]0;Thanks for using Profanity\007\"");
@@ -2854,11 +2808,9 @@ _win_show_history(ProfChatWin *chatwin, const char * const contact)
                 char hh[3]; memcpy(hh, &line[0], 2); hh[2] = '\0'; int ihh = atoi(hh);
                 char mm[3]; memcpy(mm, &line[3], 2); mm[2] = '\0'; int imm = atoi(mm);
                 char ss[3]; memcpy(ss, &line[6], 2); ss[2] = '\0'; int iss = atoi(ss);
-                GDateTime *time = g_date_time_new_local(2000, 1, 1, ihh, imm, iss);
-                GTimeVal tv;
-                g_date_time_to_timeval(time, &tv);
-                win_print((ProfWin*)chatwin, '-', &tv, NO_COLOUR_DATE, 0, "", curr->data+11);
-                g_date_time_unref(time);
+                GDateTime *timestamp = g_date_time_new_local(2000, 1, 1, ihh, imm, iss);
+                win_print((ProfWin*)chatwin, '-', timestamp, NO_COLOUR_DATE, 0, "", curr->data+11);
+                g_date_time_unref(timestamp);
             // header
             } else {
                 win_print((ProfWin*)chatwin, '-', NULL, 0, 0, "", curr->data);
diff --git a/src/ui/inputwin.c b/src/ui/inputwin.c
index caea8ea9..2c42a628 100644
--- a/src/ui/inputwin.c
+++ b/src/ui/inputwin.c
@@ -62,7 +62,8 @@
 #include "ui/ui.h"
 #include "ui/statusbar.h"
 #include "ui/inputwin.h"
-#include "ui/windows.h"
+#include "ui/window.h"
+#include "window_list.h"
 #include "event/ui_events.h"
 #include "xmpp/xmpp.h"
 
@@ -245,15 +246,8 @@ inp_get_password(void)
     get_password = TRUE;
     while (!password) {
         password = inp_readline();
-        ui_update();
-        werase(inp_win);
-        wmove(inp_win, 0, 0);
-        pad_start = 0;
-        _inp_win_update_virtual();
-        doupdate();
     }
     get_password = FALSE;
-
     status_bar_clear();
     return password;
 }
@@ -414,7 +408,8 @@ _inp_rl_getc(FILE *stream)
 {
     int ch = rl_getc(stream);
     if (_inp_printable(ch)) {
-        cmd_reset_autocomplete();
+        ProfWin *window = wins_get_current();
+        cmd_reset_autocomplete(window);
     }
     return ch;
 }
@@ -422,7 +417,8 @@ _inp_rl_getc(FILE *stream)
 static int
 _inp_rl_clear_handler(int count, int key)
 {
-    ui_clear_current();
+    ProfWin *win = wins_get_current();
+    win_clear(win);
     return 0;
 }
 
@@ -433,17 +429,21 @@ _inp_rl_tab_handler(int count, int key)
         return 0;
     }
 
-    if ((strncmp(rl_line_buffer, "/", 1) != 0) && (ui_current_win_type() == WIN_MUC)) {
-        char *result = muc_autocomplete(rl_line_buffer);
+    ProfWin *current = wins_get_current();
+    if ((strncmp(rl_line_buffer, "/", 1) != 0) && (current->type == WIN_MUC)) {
+        char *result = muc_autocomplete(current, rl_line_buffer);
         if (result) {
             rl_replace_line(result, 0);
             rl_point = rl_end;
+            free(result);
         }
     } else if (strncmp(rl_line_buffer, "/", 1) == 0) {
-        char *result = cmd_autocomplete(rl_line_buffer);
+        ProfWin *window = wins_get_current();
+        char *result = cmd_autocomplete(window, rl_line_buffer);
         if (result) {
             rl_replace_line(result, 0);
             rl_point = rl_end;
+            free(result);
         }
     }
 
diff --git a/src/ui/notifier.c b/src/ui/notifier.c
index 76290daf..12367190 100644
--- a/src/ui/notifier.c
+++ b/src/ui/notifier.c
@@ -48,7 +48,7 @@
 #include "log.h"
 #include "muc.h"
 #include "ui/ui.h"
-#include "ui/windows.h"
+#include "window_list.h"
 #include "config/preferences.h"
 
 static void _notify(const char * const message, int timeout, const char * const category);
diff --git a/src/ui/occupantswin.c b/src/ui/occupantswin.c
index bba9d0b9..fe4a600c 100644
--- a/src/ui/occupantswin.c
+++ b/src/ui/occupantswin.c
@@ -36,7 +36,7 @@
 
 #include "ui/ui.h"
 #include "ui/window.h"
-#include "ui/windows.h"
+#include "window_list.h"
 #include "config/preferences.h"
 
 static void
@@ -124,4 +124,4 @@ occupantswin_occupants(const char * const roomjid)
 
         g_list_free(occupants);
     }
-}
\ No newline at end of file
+}
diff --git a/src/ui/rosterwin.c b/src/ui/rosterwin.c
index 763490c3..00bc28a4 100644
--- a/src/ui/rosterwin.c
+++ b/src/ui/rosterwin.c
@@ -38,7 +38,7 @@
 #include "contact.h"
 #include "ui/ui.h"
 #include "ui/window.h"
-#include "ui/windows.h"
+#include "window_list.h"
 #include "config/preferences.h"
 #include "roster_list.h"
 
@@ -192,4 +192,4 @@ rosterwin_roster(void)
         }
         free(by);
     }
-}
\ No newline at end of file
+}
diff --git a/src/ui/statusbar.c b/src/ui/statusbar.c
index f3d204f1..5541f648 100644
--- a/src/ui/statusbar.c
+++ b/src/ui/statusbar.c
@@ -140,6 +140,7 @@ status_bar_resize(void)
         } else {
             mvwprintw(status_bar, 0, 1, message);
         }
+        prefs_free_string(time_pref);
     }
     if (last_time) {
         g_date_time_unref(last_time);
@@ -316,6 +317,7 @@ status_bar_print_message(const char * const msg)
     } else {
         mvwprintw(status_bar, 0, 1, message);
     }
+    prefs_free_string(time_pref);
 
     int cols = getmaxx(stdscr);
     int bracket_attrs = theme_attrs(THEME_STATUS_BRACKET);
@@ -455,6 +457,7 @@ _status_bar_draw(void)
         wattroff(status_bar, bracket_attrs);
         g_free(date_fmt);
     }
+    prefs_free_string(time_pref);
 
     _update_win_statuses();
     wnoutrefresh(status_bar);
diff --git a/src/ui/statusbar.h b/src/ui/statusbar.h
index 7d2c5ea0..c37f43f3 100644
--- a/src/ui/statusbar.h
+++ b/src/ui/statusbar.h
@@ -42,10 +42,6 @@ void status_bar_clear(void);
 void status_bar_clear_message(void);
 void status_bar_get_password(void);
 void status_bar_print_message(const char * const msg);
-void status_bar_inactive(const int win);
-void status_bar_active(const int win);
-void status_bar_new(const int win);
-void status_bar_set_all_inactive(void);
 void status_bar_current(int i);
 
-#endif
\ No newline at end of file
+#endif
diff --git a/src/ui/titlebar.c b/src/ui/titlebar.c
index 9bb84f9d..16a9eaff 100644
--- a/src/ui/titlebar.c
+++ b/src/ui/titlebar.c
@@ -44,7 +44,7 @@
 #include "ui/ui.h"
 #include "ui/titlebar.h"
 #include "ui/inputwin.h"
-#include "ui/windows.h"
+#include "window_list.h"
 #include "ui/window.h"
 #include "roster_list.h"
 #include "chat_session.h"
@@ -58,9 +58,7 @@ static GTimer *typing_elapsed;
 static void _title_bar_draw(void);
 static void _show_self_presence(void);
 static void _show_contact_presence(ProfChatWin *chatwin);
-#ifdef HAVE_LIBOTR
 static void _show_privacy(ProfChatWin *chatwin);
-#endif
 
 void
 create_title_bar(void)
@@ -174,10 +172,7 @@ _title_bar_draw(void)
         ProfChatWin *chatwin = (ProfChatWin*) current;
         assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
         _show_contact_presence(chatwin);
-
-#ifdef HAVE_LIBOTR
         _show_privacy(chatwin);
-#endif
 
         if (typing) {
             wprintw(win, " (typing...)");
@@ -246,66 +241,81 @@ _show_self_presence(void)
     wattroff(win, bracket_attrs);
 }
 
-#ifdef HAVE_LIBOTR
 static void
 _show_privacy(ProfChatWin *chatwin)
 {
     int bracket_attrs = theme_attrs(THEME_TITLE_BRACKET);
+    int encrypted_attrs = theme_attrs(THEME_TITLE_ENCRYPTED);
+    int unencrypted_attrs = theme_attrs(THEME_TITLE_UNENCRYPTED);
+    int trusted_attrs = theme_attrs(THEME_TITLE_TRUSTED);
+    int untrusted_attrs = theme_attrs(THEME_TITLE_UNTRUSTED);
+
+    switch (chatwin->enc_mode) {
+        case PROF_ENC_NONE:
+            if (prefs_get_boolean(PREF_ENC_WARN)) {
+                wprintw(win, " ");
+                wattron(win, bracket_attrs);
+                wprintw(win, "[");
+                wattroff(win, bracket_attrs);
+                wattron(win, unencrypted_attrs);
+                wprintw(win, "unencrypted");
+                wattroff(win, unencrypted_attrs);
+                wattron(win, bracket_attrs);
+                wprintw(win, "]");
+                wattroff(win, bracket_attrs);
+            }
+            break;
 
-    if (chatwin->enc_mode == PROF_ENC_NONE) {
-        if (prefs_get_boolean(PREF_OTR_WARN)) {
-            int unencrypted_attrs = theme_attrs(THEME_TITLE_UNENCRYPTED);
+        case PROF_ENC_OTR:
             wprintw(win, " ");
             wattron(win, bracket_attrs);
             wprintw(win, "[");
             wattroff(win, bracket_attrs);
-            wattron(win, unencrypted_attrs);
-            wprintw(win, "unencrypted");
-            wattroff(win, unencrypted_attrs);
+            wattron(win, encrypted_attrs);
+            wprintw(win, "OTR");
+            wattroff(win, encrypted_attrs);
             wattron(win, bracket_attrs);
             wprintw(win, "]");
             wattroff(win, bracket_attrs);
-        }
-    } else {
-        int encrypted_attrs = theme_attrs(THEME_TITLE_ENCRYPTED);
-        wprintw(win, " ");
-        wattron(win, bracket_attrs);
-        wprintw(win, "[");
-        wattroff(win, bracket_attrs);
-        wattron(win, encrypted_attrs);
-        wprintw(win, "OTR");
-        wattroff(win, encrypted_attrs);
-        wattron(win, bracket_attrs);
-        wprintw(win, "]");
-        wattroff(win, bracket_attrs);
-        if (chatwin->is_trusted) {
-            int trusted_attrs = theme_attrs(THEME_TITLE_TRUSTED);
-            wprintw(win, " ");
-            wattron(win, bracket_attrs);
-            wprintw(win, "[");
-            wattroff(win, bracket_attrs);
-            wattron(win, trusted_attrs);
-            wprintw(win, "trusted");
-            wattroff(win, trusted_attrs);
-            wattron(win, bracket_attrs);
-            wprintw(win, "]");
-            wattroff(win, bracket_attrs);
-        } else {
-            int untrusted_attrs = theme_attrs(THEME_TITLE_UNTRUSTED);
+            if (chatwin->otr_is_trusted) {
+                wprintw(win, " ");
+                wattron(win, bracket_attrs);
+                wprintw(win, "[");
+                wattroff(win, bracket_attrs);
+                wattron(win, trusted_attrs);
+                wprintw(win, "trusted");
+                wattroff(win, trusted_attrs);
+                wattron(win, bracket_attrs);
+                wprintw(win, "]");
+                wattroff(win, bracket_attrs);
+            } else {
+                wprintw(win, " ");
+                wattron(win, bracket_attrs);
+                wprintw(win, "[");
+                wattroff(win, bracket_attrs);
+                wattron(win, untrusted_attrs);
+                wprintw(win, "untrusted");
+                wattroff(win, untrusted_attrs);
+                wattron(win, bracket_attrs);
+                wprintw(win, "]");
+                wattroff(win, bracket_attrs);
+            }
+            break;
+
+        case PROF_ENC_PGP:
             wprintw(win, " ");
             wattron(win, bracket_attrs);
             wprintw(win, "[");
             wattroff(win, bracket_attrs);
-            wattron(win, untrusted_attrs);
-            wprintw(win, "untrusted");
-            wattroff(win, untrusted_attrs);
+            wattron(win, encrypted_attrs);
+            wprintw(win, "PGP");
+            wattroff(win, encrypted_attrs);
             wattron(win, bracket_attrs);
             wprintw(win, "]");
             wattroff(win, bracket_attrs);
-        }
+            break;
     }
 }
-#endif
 
 static void
 _show_contact_presence(ProfChatWin *chatwin)
diff --git a/src/ui/ui.h b/src/ui/ui.h
index 0ee21be4..7682f57b 100644
--- a/src/ui/ui.h
+++ b/src/ui/ui.h
@@ -35,21 +35,14 @@
 #ifndef UI_UI_H
 #define UI_UI_H
 
-#include "config.h"
+#include "ui/win_types.h"
+#include "muc.h"
 
-#include <wchar.h>
-
-#include <glib.h>
-#ifdef HAVE_NCURSESW_NCURSES_H
-#include <ncursesw/ncurses.h>
-#elif HAVE_NCURSES_H
-#include <ncurses.h>
-#endif
-
-#include "contact.h"
-#include "jid.h"
-#include "ui/window.h"
-#include "xmpp/xmpp.h"
+#define NO_ME           1
+#define NO_DATE         2
+#define NO_EOL          4
+#define NO_COLOUR_FROM  8
+#define NO_COLOUR_DATE  16
 
 // ui startup and control
 void ui_init(void);
@@ -93,12 +86,6 @@ int ui_close_all_wins(void);
 int ui_close_read_wins(void);
 
 // current window actions
-void ui_clear_current(void);
-win_type_t ui_current_win_type(void);
-gboolean ui_current_win_is_otr(void);
-
-ProfChatWin *ui_get_current_chat(void);
-
 void ui_current_print_line(const char * const msg, ...);
 void ui_current_print_formatted_line(const char show_char, int attrs, const char * const msg, ...);
 void ui_current_error_line(const char * const msg);
@@ -114,8 +101,8 @@ void ui_handle_stanza(const char * const msg);
 // ui events
 void ui_contact_online(char *barejid, Resource *resource, GDateTime *last_activity);
 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_incoming_msg(ProfChatWin *chatwin, const char * const resource,  const char * const message, GDateTime *timestamp, gboolean win_created);
+void ui_incoming_private_msg(const char * const fulljid, const char * const message, GDateTime *timestamp);
 void ui_message_receipt(const char * const barejid, const char * const id);
 
 void ui_disconnected(void);
@@ -142,7 +129,7 @@ void ui_room_occupant_role_and_affiliation_change(const char * const roomjid, co
     const char * const affiliation, const char * const actor, const char * const reason);
 void ui_room_roster(const char * const roomjid, GList *occupants, const char * const presence);
 void ui_room_history(const char * const roomjid, const char * const nick,
-    GTimeVal tv_stamp, const char * const message);
+    GDateTime *timestamp, const char * const message);
 void ui_room_message(const char * const roomjid, const char * const nick,
     const char * const message);
 void ui_room_subject(const char * const roomjid, const char * const nick, const char * const subject);
@@ -216,7 +203,7 @@ void ui_show_all_room_rosters(void);
 void ui_hide_all_room_rosters(void);
 gboolean ui_chat_win_exists(const char * const barejid);
 
-void ui_tidy_wins(void);
+gboolean ui_tidy_wins(void);
 void ui_prune_wins(void);
 gboolean ui_swap_wins(int source_win, int target_win);
 
@@ -224,6 +211,7 @@ void ui_page_up(void);
 void ui_page_down(void);
 void ui_subwin_page_up(void);
 void ui_subwin_page_down(void);
+void ui_clear_win(ProfWin *window);
 
 void ui_auto_away(void);
 void ui_end_auto_away(void);
@@ -262,6 +250,7 @@ void cons_show_log_prefs(void);
 void cons_show_presence_prefs(void);
 void cons_show_connection_prefs(void);
 void cons_show_otr_prefs(void);
+void cons_show_pgp_prefs(void);
 void cons_show_account(ProfAccount *account);
 void cons_debug(const char * const msg, ...);
 void cons_show_time(void);
@@ -300,11 +289,13 @@ void cons_privileges_setting(void);
 void cons_beep_setting(void);
 void cons_flash_setting(void);
 void cons_splash_setting(void);
+void cons_encwarn_setting(void);
 void cons_vercheck_setting(void);
 void cons_occupants_setting(void);
 void cons_roster_setting(void);
 void cons_presence_setting(void);
 void cons_wrap_setting(void);
+void cons_winstidy_setting(void);
 void cons_time_setting(void);
 void cons_mouse_setting(void);
 void cons_statuses_setting(void);
@@ -331,12 +322,43 @@ void cons_show_contact_online(PContact contact, Resource *resource, GDateTime *l
 void cons_show_contact_offline(PContact contact, char *resource, char *status);
 void cons_theme_colours(void);
 
+// status bar
+void status_bar_inactive(const int win);
+void status_bar_active(const int win);
+void status_bar_new(const int win);
+void status_bar_set_all_inactive(void);
+
 // roster window
 void rosterwin_roster(void);
 
 // occupants window
 void occupantswin_occupants(const char * const room);
 
+// window interface
+ProfWin* win_create_console(void);
+ProfWin* win_create_xmlconsole(void);
+ProfWin* win_create_chat(const char * const barejid);
+ProfWin* win_create_muc(const char * const roomjid);
+ProfWin* win_create_muc_config(const char * const title, DataForm *form);
+ProfWin* win_create_private(const char * const fulljid);
+
+void win_update_virtual(ProfWin *window);
+void win_free(ProfWin *window);
+int win_unread(ProfWin *window);
+void win_resize(ProfWin *window);
+void win_hide_subwin(ProfWin *window);
+void win_show_subwin(ProfWin *window);
+void win_refresh_without_subwin(ProfWin *window);
+void win_refresh_with_subwin(ProfWin *window);
+void win_print(ProfWin *window, const char show_char, GDateTime *timestamp, int flags, theme_item_t theme_item, const char * const from, const char * const message);
+void win_vprint(ProfWin *window, const char show_char, GDateTime *timestamp, int flags, theme_item_t theme_item, const char * const from, const char * const message, ...);
+char* win_get_title(ProfWin *window);
+void win_show_occupant(ProfWin *window, Occupant *occupant);
+void win_show_occupant_info(ProfWin *window, const char * const room, Occupant *occupant);
+void win_show_contact(ProfWin *window, PContact contact);
+void win_show_info(ProfWin *window, PContact contact);
+void win_println(ProfWin *window, const char * const message);
+
 // desktop notifier actions
 void notifier_initialise(void);
 void notifier_uninit(void);
diff --git a/src/ui/win_types.h b/src/ui/win_types.h
new file mode 100644
index 00000000..3214fa94
--- /dev/null
+++ b/src/ui/win_types.h
@@ -0,0 +1,146 @@
+/*
+ * win_types.h
+ *
+ * Copyright (C) 2012 - 2015 James Booth <boothj5@gmail.com>
+ *
+ * This file is part of Profanity.
+ *
+ * Profanity is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Profanity is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Profanity.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * In addition, as a special exception, the copyright holders give permission to
+ * link the code of portions of this program with the OpenSSL library under
+ * certain conditions as described in each individual source file, and
+ * distribute linked combinations including the two.
+ *
+ * You must obey the GNU General Public License in all respects for all of the
+ * code used other than OpenSSL. If you modify file(s) with this exception, you
+ * may extend this exception to your version of the file(s), but you are not
+ * obligated to do so. If you do not wish to do so, delete this exception
+ * statement from your version. If you delete this exception statement from all
+ * source files in the program, then also delete it here.
+ *
+ */
+
+#ifndef UI_WIN_TYPES_H
+#define UI_WIN_TYPES_H
+
+#include "config.h"
+
+#include <wchar.h>
+#include <glib.h>
+#ifdef HAVE_NCURSESW_NCURSES_H
+#include <ncursesw/ncurses.h>
+#elif HAVE_NCURSES_H
+#include <ncurses.h>
+#endif
+
+#include "xmpp/xmpp.h"
+#include "ui/buffer.h"
+#include "chat_state.h"
+
+#define LAYOUT_SPLIT_MEMCHECK       12345671
+#define PROFCHATWIN_MEMCHECK        22374522
+#define PROFMUCWIN_MEMCHECK         52345276
+#define PROFPRIVATEWIN_MEMCHECK     77437483
+#define PROFCONFWIN_MEMCHECK        64334685
+#define PROFXMLWIN_MEMCHECK         87333463
+
+typedef enum {
+    LAYOUT_SIMPLE,
+    LAYOUT_SPLIT
+} layout_type_t;
+
+typedef struct prof_layout_t {
+    layout_type_t type;
+    WINDOW *win;
+    ProfBuff buffer;
+    int y_pos;
+    int paged;
+} ProfLayout;
+
+typedef struct prof_layout_simple_t {
+    ProfLayout base;
+} ProfLayoutSimple;
+
+typedef struct prof_layout_split_t {
+    ProfLayout base;
+    WINDOW *subwin;
+    int sub_y_pos;
+    unsigned long memcheck;
+} ProfLayoutSplit;
+
+typedef enum {
+    WIN_CONSOLE,
+    WIN_CHAT,
+    WIN_MUC,
+    WIN_MUC_CONFIG,
+    WIN_PRIVATE,
+    WIN_XML
+} win_type_t;
+
+typedef enum {
+    PROF_ENC_NONE,
+    PROF_ENC_OTR,
+    PROF_ENC_PGP
+} prof_enc_t;
+
+typedef struct prof_win_t {
+    win_type_t type;
+    ProfLayout *layout;
+} ProfWin;
+
+typedef struct prof_console_win_t {
+    ProfWin window;
+} ProfConsoleWin;
+
+typedef struct prof_chat_win_t {
+    ProfWin window;
+    char *barejid;
+    int unread;
+    ChatState *state;
+    prof_enc_t enc_mode;
+    gboolean otr_is_trusted;
+    char *resource_override;
+    gboolean history_shown;
+    unsigned long memcheck;
+} ProfChatWin;
+
+typedef struct prof_muc_win_t {
+    ProfWin window;
+    char *roomjid;
+    int unread;
+    gboolean showjid;
+    unsigned long memcheck;
+} ProfMucWin;
+
+typedef struct prof_mucconf_win_t {
+    ProfWin window;
+    char *roomjid;
+    DataForm *form;
+    unsigned long memcheck;
+} ProfMucConfWin;
+
+typedef struct prof_private_win_t {
+    ProfWin window;
+    char *fulljid;
+    int unread;
+    unsigned long memcheck;
+} ProfPrivateWin;
+
+typedef struct prof_xml_win_t {
+    ProfWin window;
+    unsigned long memcheck;
+} ProfXMLWin;
+
+#endif
diff --git a/src/ui/window.c b/src/ui/window.c
index 60b47659..c008e44d 100644
--- a/src/ui/window.c
+++ b/src/ui/window.c
@@ -136,7 +136,7 @@ win_create_chat(const char * const barejid)
     new_win->barejid = strdup(barejid);
     new_win->resource_override = NULL;
     new_win->enc_mode = PROF_ENC_NONE;
-    new_win->is_trusted = FALSE;
+    new_win->otr_is_trusted = FALSE;
     new_win->history_shown = FALSE;
     new_win->unread = 0;
     new_win->state = chat_state_new();
@@ -460,6 +460,46 @@ win_sub_page_up(ProfWin *window)
 }
 
 void
+win_clear(ProfWin *window)
+{
+    werase(window->layout->win);
+    win_update_virtual(window);
+}
+
+void
+win_resize(ProfWin *window)
+{
+    int subwin_cols = 0;
+    int cols = getmaxx(stdscr);
+
+    if (window->layout->type == LAYOUT_SPLIT) {
+        ProfLayoutSplit *layout = (ProfLayoutSplit*)window->layout;
+        if (layout->subwin) {
+            if (window->type == WIN_CONSOLE) {
+                subwin_cols = win_roster_cols();
+            } else if (window->type == WIN_MUC) {
+                subwin_cols = win_occpuants_cols();
+            }
+            wresize(layout->base.win, PAD_SIZE, cols - subwin_cols);
+            wresize(layout->subwin, PAD_SIZE, subwin_cols);
+            if (window->type == WIN_CONSOLE) {
+                rosterwin_roster();
+            } else if (window->type == WIN_MUC) {
+                ProfMucWin *mucwin = (ProfMucWin *)window;
+                assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
+                occupantswin_occupants(mucwin->roomjid);
+            }
+        } else {
+            wresize(layout->base.win, PAD_SIZE, cols);
+        }
+    } else {
+        wresize(window->layout->win, PAD_SIZE, cols);
+    }
+
+    win_redraw(window);
+}
+
+void
 win_mouse(ProfWin *window, const wint_t ch, const int result)
 {
     int rows = getmaxy(stdscr);
@@ -532,6 +572,37 @@ win_update_virtual(ProfWin *window)
 }
 
 void
+win_refresh_without_subwin(ProfWin *window)
+{
+    int rows, cols;
+    getmaxyx(stdscr, rows, cols);
+
+    if ((window->type == WIN_MUC) || (window->type == WIN_CONSOLE)) {
+        pnoutrefresh(window->layout->win, window->layout->y_pos, 0, 1, 0, rows-3, cols-1);
+    }
+}
+
+void
+win_refresh_with_subwin(ProfWin *window)
+{
+    int rows, cols;
+    getmaxyx(stdscr, rows, cols);
+    int subwin_cols = 0;
+
+    if (window->type == WIN_MUC) {
+        ProfLayoutSplit *layout = (ProfLayoutSplit*)window->layout;
+        subwin_cols = win_occpuants_cols();
+        pnoutrefresh(layout->base.win, layout->base.y_pos, 0, 1, 0, rows-3, (cols-subwin_cols)-1);
+        pnoutrefresh(layout->subwin, layout->sub_y_pos, 0, 1, (cols-subwin_cols), rows-3, cols-1);
+    } else if (window->type == WIN_CONSOLE) {
+        ProfLayoutSplit *layout = (ProfLayoutSplit*)window->layout;
+        subwin_cols = win_roster_cols();
+        pnoutrefresh(layout->base.win, layout->base.y_pos, 0, 1, 0, rows-3, (cols-subwin_cols)-1);
+        pnoutrefresh(layout->subwin, layout->sub_y_pos, 0, 1, (cols-subwin_cols), rows-3, cols-1);
+    }
+}
+
+void
 win_move_to_end(ProfWin *window)
 {
     window->layout->paged = 0;
@@ -852,14 +923,14 @@ win_show_status_string(ProfWin *window, const char * const from,
 }
 
 void
-win_print_incoming_message(ProfWin *window, GTimeVal *tv_stamp,
+win_print_incoming_message(ProfWin *window, GDateTime *timestamp,
     const char * const from, const char * const message)
 {
     switch (window->type)
     {
         case WIN_CHAT:
         case WIN_PRIVATE:
-            win_print(window, '-', tv_stamp, NO_ME, THEME_TEXT_THEM, from, message);
+            win_print(window, '-', timestamp, NO_ME, THEME_TEXT_THEM, from, message);
             break;
         default:
             assert(FALSE);
@@ -868,31 +939,25 @@ win_print_incoming_message(ProfWin *window, GTimeVal *tv_stamp,
 }
 
 void
-win_vprint(ProfWin *window, const char show_char, GTimeVal *tstamp,
+win_vprint(ProfWin *window, const char show_char, GDateTime *timestamp,
     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_print(window, show_char, tstamp, flags, theme_item, from, fmt_msg->str);
+    win_print(window, show_char, timestamp, flags, theme_item, from, fmt_msg->str);
     g_string_free(fmt_msg, TRUE);
 }
 
 void
-win_print(ProfWin *window, const char show_char, GTimeVal *tstamp,
+win_print(ProfWin *window, const char show_char, GDateTime *timestamp,
     int flags, theme_item_t theme_item, const char * const from, const char * const message)
 {
-    GDateTime *time;
+    if (timestamp == NULL) timestamp = g_date_time_new_now_local();
 
-    if (tstamp == NULL) {
-        time = g_date_time_new_now_local();
-    } else {
-        time = g_date_time_new_from_timeval_utc(tstamp);
-    }
-
-    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);
+    buffer_push(window->layout->buffer, show_char, timestamp, flags, theme_item, from, message, NULL);
+    _win_print(window, show_char, timestamp, flags, theme_item, from, message, NULL);
     // TODO: cross-reference.. this should be replaced by a real event-based system
     ui_input_nonblocking(TRUE);
 }
@@ -954,17 +1019,17 @@ _win_print(ProfWin *window, const char show_char, GDateTime *time,
     int colour = theme_attrs(THEME_ME);
     size_t indent = 0;
 
-    if ((flags & NO_DATE) == 0) {
-        gchar *date_fmt = NULL;
-        char *time_pref = prefs_get_string(PREF_TIME);
-        date_fmt = g_date_time_format(time, time_pref);
-        free(time_pref);
-        assert(date_fmt != NULL);
-
-        if(strlen(date_fmt) != 0){
-            indent = 3 + strlen(date_fmt);
-        }
+    gchar *date_fmt = NULL;
+    char *time_pref = prefs_get_string(PREF_TIME);
+    date_fmt = g_date_time_format(time, time_pref);
+    free(time_pref);
+    assert(date_fmt != NULL);
 
+    if(strlen(date_fmt) != 0){
+        indent = 3 + strlen(date_fmt);
+    }
+
+    if ((flags & NO_DATE) == 0) {
         if (date_fmt && strlen(date_fmt)) {
             if ((flags & NO_COLOUR_DATE) == 0) {
                 wattron(window->layout->win, theme_attrs(THEME_TIME));
diff --git a/src/ui/window.h b/src/ui/window.h
index d5e57971..2728c66c 100644
--- a/src/ui/window.h
+++ b/src/ui/window.h
@@ -39,159 +39,42 @@
 
 #include <wchar.h>
 
-#ifdef HAVE_NCURSESW_NCURSES_H
-#include <ncursesw/ncurses.h>
-#elif HAVE_NCURSES_H
-#include <ncurses.h>
-#endif
-
 #include "contact.h"
 #include "muc.h"
+#include "ui/ui.h"
 #include "ui/buffer.h"
 #include "xmpp/xmpp.h"
 #include "chat_state.h"
 
-#define NO_ME           1
-#define NO_DATE         2
-#define NO_EOL          4
-#define NO_COLOUR_FROM  8
-#define NO_COLOUR_DATE  16
+#ifdef HAVE_NCURSESW_NCURSES_H
+#include <ncursesw/ncurses.h>
+#elif HAVE_NCURSES_H
+#include <ncurses.h>
+#endif
 
 #define PAD_SIZE 1000
 
-#define LAYOUT_SPLIT_MEMCHECK       12345671
-#define PROFCHATWIN_MEMCHECK        22374522
-#define PROFMUCWIN_MEMCHECK         52345276
-#define PROFPRIVATEWIN_MEMCHECK     77437483
-#define PROFCONFWIN_MEMCHECK        64334685
-#define PROFXMLWIN_MEMCHECK         87333463
-
-typedef enum {
-    LAYOUT_SIMPLE,
-    LAYOUT_SPLIT
-} layout_type_t;
-
-typedef struct prof_layout_t {
-    layout_type_t type;
-    WINDOW *win;
-    ProfBuff buffer;
-    int y_pos;
-    int paged;
-} ProfLayout;
-
-typedef struct prof_layout_simple_t {
-    ProfLayout base;
-} ProfLayoutSimple;
-
-typedef struct prof_layout_split_t {
-    ProfLayout base;
-    WINDOW *subwin;
-    int sub_y_pos;
-    unsigned long memcheck;
-} ProfLayoutSplit;
-
-typedef enum {
-    WIN_CONSOLE,
-    WIN_CHAT,
-    WIN_MUC,
-    WIN_MUC_CONFIG,
-    WIN_PRIVATE,
-    WIN_XML
-} win_type_t;
-
-typedef enum {
-    PROF_ENC_NONE,
-    PROF_ENC_OTR
-} prof_enc_t;
-
-typedef struct prof_win_t {
-    win_type_t type;
-    ProfLayout *layout;
-} ProfWin;
-
-typedef struct prof_console_win_t {
-    ProfWin window;
-} ProfConsoleWin;
-
-typedef struct prof_chat_win_t {
-    ProfWin window;
-    char *barejid;
-    int unread;
-    ChatState *state;
-    prof_enc_t enc_mode;
-    gboolean is_trusted;
-    char *resource_override;
-    gboolean history_shown;
-    unsigned long memcheck;
-} ProfChatWin;
-
-typedef struct prof_muc_win_t {
-    ProfWin window;
-    char *roomjid;
-    int unread;
-    gboolean showjid;
-    unsigned long memcheck;
-} ProfMucWin;
-
-typedef struct prof_mucconf_win_t {
-    ProfWin window;
-    char *roomjid;
-    DataForm *form;
-    unsigned long memcheck;
-} ProfMucConfWin;
-
-typedef struct prof_private_win_t {
-    ProfWin window;
-    char *fulljid;
-    int unread;
-    unsigned long memcheck;
-} ProfPrivateWin;
-
-typedef struct prof_xml_win_t {
-    ProfWin window;
-    unsigned long memcheck;
-} ProfXMLWin;
-
-ProfWin* win_create_console(void);
-ProfWin* win_create_chat(const char * const barejid);
-ProfWin* win_create_muc(const char * const roomjid);
-ProfWin* win_create_muc_config(const char * const title, DataForm *form);
-ProfWin* win_create_private(const char * const fulljid);
-ProfWin* win_create_xmlconsole(void);
-
-char *win_get_title(ProfWin *window);
-
-void win_free(ProfWin *window);
-void win_update_virtual(ProfWin *window);
 void win_move_to_end(ProfWin *window);
-void win_show_contact(ProfWin *window, PContact contact);
-void win_show_occupant(ProfWin *window, Occupant *occupant);
 void win_show_status_string(ProfWin *window, const char * const from,
     const char * const show, const char * const status,
     GDateTime *last_activity, const char * const pre,
     const char * const default_show);
-void win_print_incoming_message(ProfWin *window, GTimeVal *tv_stamp,
+void win_print_incoming_message(ProfWin *window, GDateTime *timestamp,
     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_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);
 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);
 
+void win_clear(ProfWin *window);
+
 void win_page_up(ProfWin *window);
 void win_page_down(ProfWin *window);
 void win_sub_page_down(ProfWin *window);
diff --git a/src/ui/windows.c b/src/window_list.c
index 2334efc8..2892cc53 100644
--- a/src/ui/windows.c
+++ b/src/window_list.c
@@ -1,5 +1,5 @@
 /*
- * windows.c
+ * window_list.c
  *
  * Copyright (C) 2012 - 2015 James Booth <boothj5@gmail.com>
  *
@@ -40,24 +40,16 @@
 
 #include <glib.h>
 
-#ifdef HAVE_NCURSESW_NCURSES_H
-#include <ncursesw/ncurses.h>
-#elif HAVE_NCURSES_H
-#include <ncurses.h>
-#endif
-
 #include "common.h"
 #include "roster_list.h"
 #include "config/theme.h"
 #include "ui/ui.h"
 #include "ui/statusbar.h"
-#include "ui/window.h"
-#include "ui/windows.h"
+#include "window_list.h"
 #include "event/ui_events.h"
 
 static GHashTable *windows;
 static int current;
-static int max_cols;
 
 void
 wins_init(void)
@@ -65,7 +57,6 @@ wins_init(void)
     windows = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL,
         (GDestroyNotify)win_free);
 
-    max_cols = getmaxx(stdscr);
     ProfWin *console = win_create_console();
     g_hash_table_insert(windows, GINT_TO_POINTER(1), console);
 
@@ -176,74 +167,6 @@ wins_get_current(void)
     }
 }
 
-ProfChatWin *
-wins_get_current_chat(void)
-{
-    if (windows) {
-        ProfWin *window = g_hash_table_lookup(windows, GINT_TO_POINTER(current));
-        if (window) {
-            ProfChatWin *chatwin = (ProfChatWin*)window;
-            assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
-            return chatwin;
-        } else {
-            return NULL;
-        }
-    } else {
-        return NULL;
-    }
-}
-
-ProfMucWin *
-wins_get_current_muc(void)
-{
-    if (windows) {
-        ProfWin *window = g_hash_table_lookup(windows, GINT_TO_POINTER(current));
-        if (window) {
-            ProfMucWin *mucwin = (ProfMucWin*)window;
-            assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
-            return mucwin;
-        } else {
-            return NULL;
-        }
-    } else {
-        return NULL;
-    }
-}
-
-ProfPrivateWin *
-wins_get_current_private(void)
-{
-    if (windows) {
-        ProfWin *window = g_hash_table_lookup(windows, GINT_TO_POINTER(current));
-        if (window) {
-            ProfPrivateWin *privatewin = (ProfPrivateWin*)window;
-            assert(privatewin->memcheck == PROFPRIVATEWIN_MEMCHECK);
-            return privatewin;
-        } else {
-            return NULL;
-        }
-    } else {
-        return NULL;
-    }
-}
-
-ProfMucConfWin *
-wins_get_current_muc_conf(void)
-{
-    if (windows) {
-        ProfWin *window = g_hash_table_lookup(windows, GINT_TO_POINTER(current));
-        if (window) {
-            ProfMucConfWin *confwin = (ProfMucConfWin*)window;
-            assert(confwin->memcheck == PROFCONFWIN_MEMCHECK);
-            return confwin;
-        } else {
-            return NULL;
-        }
-    } else {
-        return NULL;
-    }
-}
-
 GList *
 wins_get_nums(void)
 {
@@ -386,14 +309,6 @@ wins_close_by_num(int i)
     }
 }
 
-void
-wins_clear_current(void)
-{
-    ProfWin *window = wins_get_current();
-    werase(window->layout->win);
-    win_update_virtual(window);
-}
-
 gboolean
 wins_is_current(ProfWin *window)
 {
@@ -480,39 +395,11 @@ wins_get_total_unread(void)
 void
 wins_resize_all(void)
 {
-    int cols = getmaxx(stdscr);
-
     GList *values = g_hash_table_get_values(windows);
     GList *curr = values;
     while (curr) {
         ProfWin *window = curr->data;
-        int subwin_cols = 0;
-
-        if (window->layout->type == LAYOUT_SPLIT) {
-            ProfLayoutSplit *layout = (ProfLayoutSplit*)window->layout;
-            if (layout->subwin) {
-                if (window->type == WIN_CONSOLE) {
-                    subwin_cols = win_roster_cols();
-                } else if (window->type == WIN_MUC) {
-                    subwin_cols = win_occpuants_cols();
-                }
-                wresize(layout->base.win, PAD_SIZE, cols - subwin_cols);
-                wresize(layout->subwin, PAD_SIZE, subwin_cols);
-                if (window->type == WIN_CONSOLE) {
-                    rosterwin_roster();
-                } else if (window->type == WIN_MUC) {
-                    ProfMucWin *mucwin = (ProfMucWin *)window;
-                    assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
-                    occupantswin_occupants(mucwin->roomjid);
-                }
-            } else {
-                wresize(layout->base.win, PAD_SIZE, cols);
-            }
-        } else {
-            wresize(window->layout->win, PAD_SIZE, cols);
-        }
-
-        win_redraw(window);
+        win_resize(window);
         curr = g_list_next(curr);
     }
     g_list_free(values);
@@ -524,38 +411,19 @@ wins_resize_all(void)
 void
 wins_hide_subwin(ProfWin *window)
 {
-    int rows, cols;
-    getmaxyx(stdscr, rows, cols);
-
     win_hide_subwin(window);
 
     ProfWin *current_win = wins_get_current();
-    if ((current_win->type == WIN_MUC) || (current_win->type == WIN_CONSOLE)) {
-        pnoutrefresh(current_win->layout->win, current_win->layout->y_pos, 0, 1, 0, rows-3, cols-1);
-    }
+    win_refresh_without_subwin(current_win);
 }
 
 void
 wins_show_subwin(ProfWin *window)
 {
-    int rows, cols;
-    getmaxyx(stdscr, rows, cols);
-    int subwin_cols = 0;
-
     win_show_subwin(window);
 
     ProfWin *current_win = wins_get_current();
-    if (current_win->type == WIN_MUC) {
-        ProfLayoutSplit *layout = (ProfLayoutSplit*)current_win->layout;
-        subwin_cols = win_occpuants_cols();
-        pnoutrefresh(layout->base.win, layout->base.y_pos, 0, 1, 0, rows-3, (cols-subwin_cols)-1);
-        pnoutrefresh(layout->subwin, layout->sub_y_pos, 0, 1, (cols-subwin_cols), rows-3, cols-1);
-    } else if (current_win->type == WIN_CONSOLE) {
-        ProfLayoutSplit *layout = (ProfLayoutSplit*)current_win->layout;
-        subwin_cols = win_roster_cols();
-        pnoutrefresh(layout->base.win, layout->base.y_pos, 0, 1, 0, rows-3, (cols-subwin_cols)-1);
-        pnoutrefresh(layout->subwin, layout->sub_y_pos, 0, 1, (cols-subwin_cols), rows-3, cols-1);
-    }
+    win_refresh_with_subwin(current_win);
 }
 
 ProfXMLWin *
diff --git a/src/ui/windows.h b/src/window_list.h
index 97183d51..74073bd6 100644
--- a/src/ui/windows.h
+++ b/src/window_list.h
@@ -1,5 +1,5 @@
 /*
- * windows.h
+ * window_list.h
  *
  * Copyright (C) 2012 - 2015 James Booth <boothj5@gmail.com>
  *
@@ -32,8 +32,10 @@
  *
  */
 
-#ifndef UI_WINDOWS_H
-#define UI_WINDOWS_H
+#ifndef WINDOW_LIST_H
+#define WINDOW_LIST_H
+
+#include "ui/ui.h"
 
 void wins_init(void);
 
@@ -51,10 +53,6 @@ ProfPrivateWin *wins_get_private(const char * const fulljid);
 ProfXMLWin * wins_get_xmlconsole(void);
 
 ProfWin * wins_get_current(void);
-ProfChatWin * wins_get_current_chat(void);
-ProfMucWin * wins_get_current_muc(void);
-ProfPrivateWin * wins_get_current_private(void);
-ProfMucConfWin * wins_get_current_muc_conf(void);
 
 void wins_set_current_by_num(int i);
 
@@ -66,7 +64,6 @@ int wins_get_num(ProfWin *window);
 int wins_get_current_num(void);
 void wins_close_current(void);
 void wins_close_by_num(int i);
-void wins_clear_current(void);
 gboolean wins_is_current(ProfWin *window);
 int wins_get_total_unread(void);
 void wins_resize_all(void);
diff --git a/src/xmpp/connection.c b/src/xmpp/connection.c
index 70d49b7c..3c9ba214 100644
--- a/src/xmpp/connection.c
+++ b/src/xmpp/connection.c
@@ -194,7 +194,7 @@ jabber_disconnect(void)
         xmpp_disconnect(jabber_conn.conn);
 
         while (jabber_get_connection_status() == JABBER_DISCONNECTING) {
-            jabber_process_events();
+            jabber_process_events(10);
         }
         _connection_free_saved_account();
         _connection_free_saved_details();
@@ -222,10 +222,11 @@ jabber_shutdown(void)
     _connection_free_session_data();
     xmpp_shutdown();
     free(jabber_conn.log);
+    jabber_conn.log = NULL;
 }
 
 void
-jabber_process_events(void)
+jabber_process_events(int millis)
 {
     int reconnect_sec;
 
@@ -234,7 +235,7 @@ jabber_process_events(void)
         case JABBER_CONNECTED:
         case JABBER_CONNECTING:
         case JABBER_DISCONNECTING:
-            xmpp_run_once(jabber_conn.ctx, 10);
+            xmpp_run_once(jabber_conn.ctx, millis);
             break;
         case JABBER_DISCONNECTED:
             reconnect_sec = prefs_get_reconnect();
@@ -365,6 +366,7 @@ _jabber_connect(const char * const fulljid, const char * const passwd,
     } else if (jid->fulljid == NULL) {
         log_error("Full JID required to connect, received: %s", fulljid);
         jabber_conn.conn_status = JABBER_DISCONNECTED;
+        jid_destroy(jid);
         return jabber_conn.conn_status;
     }
 
@@ -577,4 +579,4 @@ _xmpp_get_file_logger()
     file_log->userdata = &level;
 
     return file_log;
-}
\ No newline at end of file
+}
diff --git a/src/xmpp/message.c b/src/xmpp/message.c
index bc702199..9aa278c1 100644
--- a/src/xmpp/message.c
+++ b/src/xmpp/message.c
@@ -50,6 +50,7 @@
 #include "roster_list.h"
 #include "xmpp/stanza.h"
 #include "xmpp/xmpp.h"
+#include "pgp/gpg.h"
 
 #define HANDLE(ns, type, func) xmpp_handler_add(conn, func, ns, STANZA_NAME_MESSAGE, type, ctx)
 
@@ -76,37 +77,57 @@ message_add_handlers(void)
     HANDLE(STANZA_NS_RECEIPTS,   NULL,                   _receipt_received_handler);
 }
 
-char *
-message_send_chat(const char * const barejid, const char * const msg)
+static char*
+_session_jid(const char * const barejid)
 {
-    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) {
-        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);
         jid = strdup(jidp->fulljid);
         jid_destroy(jidp);
+    } else {
+        jid = strdup(barejid);
+    }
 
+    return jid;
+}
+
+static char*
+_session_state(const char * const barejid)
+{
+    ChatSession *session = chat_session_get(barejid);
+    char *state = NULL;
+    if (session) {
+        if (prefs_get_boolean(PREF_STATES) && session->send_states) {
+            state = STANZA_NAME_ACTIVE;
+        }
     } else {
         if (prefs_get_boolean(PREF_STATES)) {
             state = STANZA_NAME_ACTIVE;
         }
-        jid = strdup(barejid);
     }
 
+    return state;
+}
+
+char *
+message_send_chat(const char * const barejid, const char * const msg)
+{
+    xmpp_conn_t * const conn = connection_get_conn();
+    xmpp_ctx_t * const ctx = connection_get_ctx();
+
+    char *state = _session_state(barejid);
+    char *jid = _session_jid(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_REQUEST)) {
         stanza_attach_receipt_request(ctx, message);
     }
@@ -118,36 +139,81 @@ message_send_chat(const char * const barejid, const char * const msg)
 }
 
 char *
-message_send_chat_encrypted(const char * const barejid, const char * const msg)
+message_send_chat_pgp(const char * const barejid, const char * const msg)
 {
     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) {
-        if (prefs_get_boolean(PREF_STATES) && session->send_states) {
-            state = STANZA_NAME_ACTIVE;
+    char *state = _session_state(barejid);
+    char *jid = _session_jid(barejid);
+    char *id = create_unique_id("msg");
+
+    xmpp_stanza_t *message = NULL;
+#ifdef HAVE_LIBGPGME
+    char *account_name = jabber_get_account_name();
+    ProfAccount *account = accounts_get_account(account_name);
+    if (account->pgp_keyid) {
+        Jid *jidp = jid_create(jid);
+        char *encrypted = p_gpg_encrypt(jidp->barejid, msg);
+        if (encrypted) {
+            message = stanza_create_message(ctx, id, jid, STANZA_TYPE_CHAT, "This message is encrypted.");
+            xmpp_stanza_t *x = xmpp_stanza_new(ctx);
+            xmpp_stanza_set_name(x, STANZA_NAME_X);
+            xmpp_stanza_set_ns(x, STANZA_NS_ENCRYPTED);
+            xmpp_stanza_t *enc_st = xmpp_stanza_new(ctx);
+            xmpp_stanza_set_text(enc_st, encrypted);
+            xmpp_stanza_add_child(x, enc_st);
+            xmpp_stanza_release(enc_st);
+            xmpp_stanza_add_child(message, x);
+            xmpp_stanza_release(x);
+            free(encrypted);
+        } else {
+            message = stanza_create_message(ctx, id, jid, STANZA_TYPE_CHAT, msg);
         }
-        Jid *jidp = jid_create_from_bare_and_resource(session->barejid, session->resource);
-        jid = strdup(jidp->fulljid);
         jid_destroy(jidp);
     } else {
-        if (prefs_get_boolean(PREF_STATES)) {
-            state = STANZA_NAME_ACTIVE;
-        }
-        jid = strdup(barejid);
+        message = stanza_create_message(ctx, id, jid, STANZA_TYPE_CHAT, msg);
+    }
+#else
+    message = stanza_create_message(ctx, id, jid, STANZA_TYPE_CHAT, msg);
+#endif
+    free(jid);
+
+    if (state) {
+        stanza_attach_state(ctx, message, state);
+    }
+
+    stanza_attach_carbons_private(ctx, message);
+
+    if (prefs_get_boolean(PREF_RECEIPTS_REQUEST)) {
+        stanza_attach_receipt_request(ctx, message);
     }
 
+    xmpp_send(conn, message);
+    xmpp_stanza_release(message);
+
+    return id;
+}
+
+char *
+message_send_chat_otr(const char * const barejid, const char * const msg)
+{
+    xmpp_conn_t * const conn = connection_get_conn();
+    xmpp_ctx_t * const ctx = connection_get_ctx();
+
+    char *state = _session_state(barejid);
+    char *jid = _session_jid(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_REQUEST)) {
         stanza_attach_receipt_request(ctx, message);
     }
@@ -384,6 +450,7 @@ _conference_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void
     // XEP-0249
     char *room = xmpp_stanza_get_attribute(xns_conference, STANZA_ATTR_JID);
     if (!room) {
+        jid_destroy(jidp);
         return 1;
     }
 
@@ -492,10 +559,10 @@ _groupchat_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void
     }
 
     // determine if the notifications happened whilst offline
-    GTimeVal tv_stamp;
-    gboolean delayed = stanza_get_delay(stanza, &tv_stamp);
-    if (delayed) {
-        sv_ev_room_history(jid->barejid, jid->resourcepart, tv_stamp, message);
+    GDateTime *timestamp = stanza_get_delay(stanza);
+    if (timestamp) {
+        sv_ev_room_history(jid->barejid, jid->resourcepart, timestamp, message);
+        g_date_time_unref(timestamp);
     } else {
         sv_ev_room_message(jid->barejid, jid->resourcepart, message);
     }
@@ -596,10 +663,10 @@ _private_chat_handler(xmpp_stanza_t * const stanza, const char * const fulljid)
         return;
     }
 
-    GTimeVal tv_stamp;
-    gboolean delayed = stanza_get_delay(stanza, &tv_stamp);
-    if (delayed) {
-        sv_ev_delayed_private_message(fulljid, message, tv_stamp);
+    GDateTime *timestamp = stanza_get_delay(stanza);
+    if (timestamp) {
+        sv_ev_delayed_private_message(fulljid, message, timestamp);
+        g_date_time_unref(timestamp);
     } else {
         sv_ev_incoming_private_message(fulljid, message);
     }
@@ -640,11 +707,11 @@ _handle_carbons(xmpp_stanza_t * const stanza)
             if (message) {
                 // if we are the recipient, treat as standard incoming message
                 if(g_strcmp0(my_jid->barejid, jid_to->barejid) == 0){
-                    sv_ev_incoming_message(jid_from->barejid, jid_from->resourcepart, message);
+                    sv_ev_incoming_carbon(jid_from->barejid, jid_from->resourcepart, message);
                 }
                 // else treat as a sent message
                 else{
-                    sv_ev_carbon(jid_to->barejid, message);
+                    sv_ev_outgoing_carbon(jid_to->barejid, message);
                 }
                 xmpp_free(ctx, message);
             }
@@ -689,21 +756,25 @@ _chat_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * con
     // private message from chat room use full jid (room/nick)
     if (muc_active(jid->barejid)) {
         _private_chat_handler(stanza, jid->fulljid);
+        jid_destroy(jid);
         return 1;
     }
 
     // standard chat message, use jid without resource
-    GTimeVal tv_stamp;
-    gboolean delayed = stanza_get_delay(stanza, &tv_stamp);
-
+    GDateTime *timestamp = stanza_get_delay(stanza);
     xmpp_stanza_t *body = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_BODY);
     if (body) {
         char *message = xmpp_stanza_get_text(body);
         if (message) {
-            if (delayed) {
-                sv_ev_delayed_message(jid->barejid, message, tv_stamp);
+            if (timestamp) {
+                sv_ev_delayed_message(jid->barejid, message, timestamp);
             } else {
-                sv_ev_incoming_message(jid->barejid, jid->resourcepart, message);
+                char *enc_message = NULL;
+                xmpp_stanza_t *x = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_ENCRYPTED);
+                if (x) {
+                    enc_message = xmpp_stanza_get_text(x);
+                }
+                sv_ev_incoming_message(jid->barejid, jid->resourcepart, message, enc_message);
             }
 
             _receipt_request_handler(stanza);
@@ -714,7 +785,7 @@ _chat_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * con
     }
 
     // handle chat sessions and states
-    if (!delayed && jid->resourcepart) {
+    if (!timestamp && jid->resourcepart) {
         gboolean gone = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_GONE) != NULL;
         gboolean typing = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_COMPOSING) != NULL;
         gboolean paused = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_PAUSED) != NULL;
@@ -734,6 +805,7 @@ _chat_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * con
         }
     }
 
+    if (timestamp) g_date_time_unref(timestamp);
     jid_destroy(jid);
     return 1;
 }
diff --git a/src/xmpp/presence.c b/src/xmpp/presence.c
index e46730e3..42215322 100644
--- a/src/xmpp/presence.c
+++ b/src/xmpp/presence.c
@@ -193,7 +193,7 @@ presence_reset_sub_request_search(void)
 }
 
 void
-presence_send(const resource_presence_t presence_type, const char * const msg, const int idle)
+presence_send(const resource_presence_t presence_type, const char * const msg, const int idle, char *signed_status)
 {
     if (jabber_get_connection_status() != JABBER_CONNECTED) {
         log_warning("Error setting presence, not connected.");
@@ -218,7 +218,21 @@ presence_send(const resource_presence_t presence_type, const char * const msg, c
     char *id = create_unique_id("presence");
     xmpp_stanza_set_id(presence, id);
     stanza_attach_show(ctx, presence, show);
+
     stanza_attach_status(ctx, presence, msg);
+
+    if (signed_status) {
+        xmpp_stanza_t *x = xmpp_stanza_new(ctx);
+        xmpp_stanza_set_name(x, STANZA_NAME_X);
+        xmpp_stanza_set_ns(x, STANZA_NS_SIGNED);
+        xmpp_stanza_t *signed_text = xmpp_stanza_new(ctx);
+        xmpp_stanza_set_text(signed_text, signed_status);
+        xmpp_stanza_add_child(x, signed_text);
+        xmpp_stanza_release(signed_text);
+        xmpp_stanza_add_child(presence, x);
+        xmpp_stanza_release(x);
+    }
+
     stanza_attach_priority(ctx, presence, pri);
     stanza_attach_last_activity(ctx, presence, idle);
     stanza_attach_caps(ctx, presence);
@@ -603,7 +617,14 @@ _available_handler(xmpp_conn_t * const conn,
     if (g_strcmp0(xmpp_presence->jid->barejid, my_jid->barejid) == 0) {
         connection_add_available_resource(resource);
     } else {
-        sv_ev_contact_online(xmpp_presence->jid->barejid, resource, xmpp_presence->last_activity);
+        char *pgpsig = NULL;
+        xmpp_stanza_t *x = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_SIGNED);
+        if (x) {
+            pgpsig = xmpp_stanza_get_text(x);
+        }
+        sv_ev_contact_online(xmpp_presence->jid->barejid, resource, xmpp_presence->last_activity, pgpsig);
+        xmpp_ctx_t *ctx = connection_get_ctx();
+        xmpp_free(ctx, pgpsig);
     }
 
     jid_destroy(my_jid);
@@ -649,6 +670,7 @@ _muc_user_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void *
     // invalid from attribute
     Jid *from_jid = jid_create(from);
     if (from_jid == NULL || from_jid->resourcepart == NULL) {
+        jid_destroy(from_jid);
         return 1;
     }
 
@@ -783,4 +805,4 @@ _muc_user_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void *
     jid_destroy(from_jid);
 
     return 1;
-}
\ No newline at end of file
+}
diff --git a/src/xmpp/stanza.c b/src/xmpp/stanza.c
index 1f25239b..beb03f45 100644
--- a/src/xmpp/stanza.c
+++ b/src/xmpp/stanza.c
@@ -982,17 +982,21 @@ stanza_create_ping_iq(xmpp_ctx_t *ctx, const char * const target)
     return iq;
 }
 
-gboolean
-stanza_get_delay(xmpp_stanza_t * const stanza, GTimeVal *tv_stamp)
+GDateTime*
+stanza_get_delay(xmpp_stanza_t * const stanza)
 {
+    GTimeVal utc_stamp;
     // first check for XEP-0203 delayed delivery
     xmpp_stanza_t *delay = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_DELAY);
     if (delay) {
         char *xmlns = xmpp_stanza_get_attribute(delay, STANZA_ATTR_XMLNS);
         if (xmlns && (strcmp(xmlns, "urn:xmpp:delay") == 0)) {
             char *stamp = xmpp_stanza_get_attribute(delay, STANZA_ATTR_STAMP);
-            if (stamp && (g_time_val_from_iso8601(stamp, tv_stamp))) {
-                return TRUE;
+            if (stamp && (g_time_val_from_iso8601(stamp, &utc_stamp))) {
+                GDateTime *utc_datetime = g_date_time_new_from_timeval_utc(&utc_stamp);
+                GDateTime *local_datetime = g_date_time_to_local(utc_datetime);
+                g_date_time_unref(utc_datetime);
+                return local_datetime;
             }
         }
     }
@@ -1004,13 +1008,16 @@ stanza_get_delay(xmpp_stanza_t * const stanza, GTimeVal *tv_stamp)
         char *xmlns = xmpp_stanza_get_attribute(x, STANZA_ATTR_XMLNS);
         if (xmlns && (strcmp(xmlns, "jabber:x:delay") == 0)) {
             char *stamp = xmpp_stanza_get_attribute(x, STANZA_ATTR_STAMP);
-            if (stamp && (g_time_val_from_iso8601(stamp, tv_stamp))) {
-                return TRUE;
+            if (stamp && (g_time_val_from_iso8601(stamp, &utc_stamp))) {
+                GDateTime *utc_datetime = g_date_time_new_from_timeval_utc(&utc_stamp);
+                GDateTime *local_datetime = g_date_time_to_local(utc_datetime);
+                g_date_time_unref(utc_datetime);
+                return local_datetime;
             }
         }
     }
 
-    return FALSE;
+    return NULL;
 }
 
 char *
diff --git a/src/xmpp/stanza.h b/src/xmpp/stanza.h
index 89dbda57..2dd1a141 100644
--- a/src/xmpp/stanza.h
+++ b/src/xmpp/stanza.h
@@ -160,6 +160,8 @@
 #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_NS_SIGNED "jabber:x:signed"
+#define STANZA_NS_ENCRYPTED "jabber:x:encrypted"
 
 #define STANZA_DATAFORM_SOFTWARE "urn:xmpp:dataforms:softwareinfo"
 
@@ -221,7 +223,7 @@ xmpp_stanza_t* stanza_create_mediated_invite(xmpp_ctx_t *ctx, const char * const
 
 gboolean stanza_contains_chat_state(xmpp_stanza_t *stanza);
 
-gboolean stanza_get_delay(xmpp_stanza_t * const stanza, GTimeVal *tv_stamp);
+GDateTime* stanza_get_delay(xmpp_stanza_t * const stanza);
 
 gboolean stanza_is_muc_presence(xmpp_stanza_t * const stanza);
 gboolean stanza_is_muc_self_presence(xmpp_stanza_t * const stanza,
diff --git a/src/xmpp/xmpp.h b/src/xmpp/xmpp.h
index 398c9f46..575f9ae1 100644
--- a/src/xmpp/xmpp.h
+++ b/src/xmpp/xmpp.h
@@ -136,7 +136,7 @@ jabber_conn_status_t jabber_connect_with_details(const char * const jid,
 jabber_conn_status_t jabber_connect_with_account(const ProfAccount * const account);
 void jabber_disconnect(void);
 void jabber_shutdown(void);
-void jabber_process_events(void);
+void jabber_process_events(int millis);
 const char * jabber_get_fulljid(void);
 const char * jabber_get_domain(void);
 jabber_conn_status_t jabber_get_connection_status(void);
@@ -146,7 +146,8 @@ GList * jabber_get_available_resources(void);
 
 // message functions
 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);
+char* message_send_chat_otr(const char * const barejid, const char * const msg);
+char* message_send_chat_pgp(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);
@@ -168,8 +169,7 @@ char * presence_sub_request_find(const char * const search_str);
 void presence_join_room(char *room, char *nick, char * passwd);
 void presence_change_room_nick(const char * const room, const char * const nick);
 void presence_leave_chat_room(const char * const room_jid);
-void presence_send(resource_presence_t status, const char * const msg,
-    int idle);
+void presence_send(resource_presence_t status, const char * const msg, int idle, char *signed_status);
 gboolean presence_sub_request_exists(const char * const bare_jid);
 
 // iq functions
diff --git a/tests/functionaltests/functionaltests.c b/tests/functionaltests/functionaltests.c
new file mode 100644
index 00000000..fac7685c
--- /dev/null
+++ b/tests/functionaltests/functionaltests.c
@@ -0,0 +1,119 @@
+#include <stdarg.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+#include <sys/stat.h>
+
+#include "config.h"
+
+#include "proftest.h"
+#include "test_connect.h"
+#include "test_ping.h"
+#include "test_rooms.h"
+#include "test_presence.h"
+#include "test_message.h"
+#include "test_chat_session.h"
+
+int main(int argc, char* argv[]) {
+
+    const UnitTest all_tests[] = {
+
+        unit_test_setup_teardown(connect_jid_requests_roster,
+            init_prof_test,
+            close_prof_test),
+        unit_test_setup_teardown(connect_jid_sends_presence_after_receiving_roster,
+            init_prof_test,
+            close_prof_test),
+        unit_test_setup_teardown(connect_jid_requests_bookmarks,
+            init_prof_test,
+            close_prof_test),
+        unit_test_setup_teardown(connect_bad_password,
+            init_prof_test,
+            close_prof_test),
+        unit_test_setup_teardown(connect_shows_presence_updates,
+            init_prof_test,
+            close_prof_test),
+
+        unit_test_setup_teardown(ping_multiple,
+            init_prof_test,
+            close_prof_test),
+        unit_test_setup_teardown(ping_responds,
+            init_prof_test,
+            close_prof_test),
+
+        unit_test_setup_teardown(rooms_query,
+            init_prof_test,
+            close_prof_test),
+
+        unit_test_setup_teardown(presence_away,
+            init_prof_test,
+            close_prof_test),
+        unit_test_setup_teardown(presence_away_with_message,
+            init_prof_test,
+            close_prof_test),
+        unit_test_setup_teardown(presence_online,
+            init_prof_test,
+            close_prof_test),
+        unit_test_setup_teardown(presence_online_with_message,
+            init_prof_test,
+            close_prof_test),
+        unit_test_setup_teardown(presence_xa,
+            init_prof_test,
+            close_prof_test),
+        unit_test_setup_teardown(presence_xa_with_message,
+            init_prof_test,
+            close_prof_test),
+        unit_test_setup_teardown(presence_dnd,
+            init_prof_test,
+            close_prof_test),
+        unit_test_setup_teardown(presence_dnd_with_message,
+            init_prof_test,
+            close_prof_test),
+        unit_test_setup_teardown(presence_chat,
+            init_prof_test,
+            close_prof_test),
+        unit_test_setup_teardown(presence_chat_with_message,
+            init_prof_test,
+            close_prof_test),
+        unit_test_setup_teardown(presence_set_priority,
+            init_prof_test,
+            close_prof_test),
+        unit_test_setup_teardown(presence_includes_priority,
+            init_prof_test,
+            close_prof_test),
+        unit_test_setup_teardown(presence_received,
+            init_prof_test,
+            close_prof_test),
+
+        unit_test_setup_teardown(message_send,
+            init_prof_test,
+            close_prof_test),
+        unit_test_setup_teardown(message_receive,
+            init_prof_test,
+            close_prof_test),
+
+        unit_test_setup_teardown(sends_message_to_barejid_when_contact_offline,
+            init_prof_test,
+            close_prof_test),
+        unit_test_setup_teardown(sends_message_to_barejid_when_contact_online,
+            init_prof_test,
+            close_prof_test),
+        unit_test_setup_teardown(sends_message_to_fulljid_when_received_from_fulljid,
+            init_prof_test,
+            close_prof_test),
+        unit_test_setup_teardown(sends_subsequent_messages_to_fulljid,
+            init_prof_test,
+            close_prof_test),
+        unit_test_setup_teardown(resets_to_barejid_after_presence_received,
+            init_prof_test,
+            close_prof_test),
+        unit_test_setup_teardown(new_session_when_message_received_from_different_fulljid,
+            init_prof_test,
+            close_prof_test),
+    };
+
+    return run_tests(all_tests);
+}
diff --git a/tests/functionaltests/proftest.c b/tests/functionaltests/proftest.c
new file mode 100644
index 00000000..613c2c7a
--- /dev/null
+++ b/tests/functionaltests/proftest.c
@@ -0,0 +1,238 @@
+#include <sys/stat.h>
+#include <glib.h>
+
+#include <setjmp.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <cmocka.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+
+#include <stabber.h>
+#include <expect.h>
+
+#include "proftest.h"
+
+char *config_orig;
+char *data_orig;
+
+int fd = 0;
+
+gboolean
+_create_dir(char *name)
+{
+    struct stat sb;
+
+    if (stat(name, &sb) != 0) {
+        if (errno != ENOENT || mkdir(name, S_IRWXU) != 0) {
+            return FALSE;
+        }
+    } else {
+        if ((sb.st_mode & S_IFDIR) != S_IFDIR) {
+            return FALSE;
+        }
+    }
+
+    return TRUE;
+}
+
+gboolean
+_mkdir_recursive(const char *dir)
+{
+    int i;
+    gboolean result = TRUE;
+
+    for (i = 1; i <= strlen(dir); i++) {
+        if (dir[i] == '/' || dir[i] == '\0') {
+            gchar *next_dir = g_strndup(dir, i);
+            result = _create_dir(next_dir);
+            g_free(next_dir);
+            if (!result) {
+                break;
+            }
+        }
+    }
+
+    return result;
+}
+
+void
+_create_config_dir(void)
+{
+    GString *profanity_dir = g_string_new(XDG_CONFIG_HOME);
+    g_string_append(profanity_dir, "/profanity");
+
+    if (!_mkdir_recursive(profanity_dir->str)) {
+        assert_true(FALSE);
+    }
+
+    g_string_free(profanity_dir, TRUE);
+}
+
+void
+_create_data_dir(void)
+{
+    GString *profanity_dir = g_string_new(XDG_DATA_HOME);
+    g_string_append(profanity_dir, "/profanity");
+
+    if (!_mkdir_recursive(profanity_dir->str)) {
+        assert_true(FALSE);
+    }
+
+    g_string_free(profanity_dir, TRUE);
+}
+
+void
+_create_chatlogs_dir(void)
+{
+    GString *chatlogs_dir = g_string_new(XDG_DATA_HOME);
+    g_string_append(chatlogs_dir, "/profanity/chatlogs");
+
+    if (!_mkdir_recursive(chatlogs_dir->str)) {
+        assert_true(FALSE);
+    }
+
+    g_string_free(chatlogs_dir, TRUE);
+}
+
+void
+_create_logs_dir(void)
+{
+    GString *logs_dir = g_string_new(XDG_DATA_HOME);
+    g_string_append(logs_dir, "/profanity/logs");
+
+    if (!_mkdir_recursive(logs_dir->str)) {
+        assert_true(FALSE);
+    }
+
+    g_string_free(logs_dir, TRUE);
+}
+
+void
+_cleanup_dirs(void)
+{
+    int res = system("rm -rf ./tests/functionaltests/files");
+    if (res == -1) {
+        assert_true(FALSE);
+    }
+}
+
+void
+prof_start(void)
+{
+    // helper script sets terminal columns, avoids assertions failing
+    // based on the test runner terminal size
+    fd = exp_spawnl("sh",
+        "sh",
+        "-c",
+        "./tests/functionaltests/start_profanity.sh",
+        NULL);
+    FILE *fp = fdopen(fd, "r+");
+
+    assert_true(fp != NULL);
+
+    setbuf(fp, (char *)0);
+}
+
+void
+init_prof_test(void **state)
+{
+    if (stbbr_start(STBBR_LOGDEBUG ,5230, 0) != 0) {
+        assert_true(FALSE);
+        return;
+    }
+
+    config_orig = getenv("XDG_CONFIG_HOME");
+    data_orig = getenv("XDG_DATA_HOME");
+
+    setenv("XDG_CONFIG_HOME", XDG_CONFIG_HOME, 1);
+    setenv("XDG_DATA_HOME", XDG_DATA_HOME, 1);
+
+    _cleanup_dirs();
+
+    _create_config_dir();
+    _create_data_dir();
+    _create_chatlogs_dir();
+    _create_logs_dir();
+
+    prof_start();
+    assert_true(prof_output_exact("Profanity"));
+
+    // set UI options to make expect assertions faster and more reliable
+    prof_input("/inpblock timeout 5");
+    assert_true(prof_output_exact("Input blocking set to 5 milliseconds"));
+    prof_input("/inpblock dynamic off");
+    assert_true(prof_output_exact("Dynamic input blocking disabled"));
+    prof_input("/notify message off");
+    assert_true(prof_output_exact("Message notifications disabled"));
+    prof_input("/wrap off");
+    assert_true(prof_output_exact("Word wrap disabled"));
+    prof_input("/roster hide");
+    assert_true(prof_output_exact("Roster disabled"));
+    prof_input("/time off");
+}
+
+void
+close_prof_test(void **state)
+{
+    prof_input("/quit");
+    waitpid(exp_pid, NULL, 0);
+    _cleanup_dirs();
+
+    setenv("XDG_CONFIG_HOME", config_orig, 1);
+    setenv("XDG_DATA_HOME", data_orig, 1);
+
+    stbbr_stop();
+}
+
+void
+prof_input(char *input)
+{
+    GString *inp_str = g_string_new(input);
+    g_string_append(inp_str, "\r");
+    write(fd, inp_str->str, inp_str->len);
+    g_string_free(inp_str, TRUE);
+}
+
+int
+prof_output_exact(char *text)
+{
+    return (1 == exp_expectl(fd, exp_exact, text, 1, exp_end));
+}
+
+int
+prof_output_regex(char *text)
+{
+    return (1 == exp_expectl(fd, exp_regexp, text, 1, exp_end));
+}
+
+void
+prof_connect(void)
+{
+    stbbr_for_query("jabber:iq:roster",
+        "<iq type=\"result\" to=\"stabber@localhost/profanity\">"
+            "<query xmlns=\"jabber:iq:roster\" ver=\"362\">"
+                "<item jid=\"buddy1@localhost\" subscription=\"both\" name=\"Buddy1\"/>"
+                "<item jid=\"buddy2@localhost\" subscription=\"both\" name=\"Buddy2\"/>"
+            "</query>"
+        "</iq>"
+    );
+    stbbr_for_id("prof_presence_1",
+        "<presence id=\"prof_presence_1\" lang=\"en\" to=\"stabber@localhost/profanity\" from=\"stabber@localhost/profanity\">"
+            "<priority>0</priority>"
+            "<c hash=\"sha-1\" xmlns=\"http://jabber.org/protocol/caps\" node=\"http://www.profanity.im\" ver=\"f8mrtdyAmhnj8Ca+630bThSL718=\"/>"
+        "</presence>"
+    );
+
+    prof_input("/connect stabber@localhost port 5230");
+    prof_input("password");
+
+    // Allow time for profanity to connect
+    exp_timeout = 30;
+    assert_true(prof_output_regex("stabber@localhost logged in successfully, .+online.+ \\(priority 0\\)\\."));
+    exp_timeout = 10;
+    stbbr_wait_for("prof_presence_1");
+}
diff --git a/tests/functionaltests/proftest.h b/tests/functionaltests/proftest.h
new file mode 100644
index 00000000..2283ab01
--- /dev/null
+++ b/tests/functionaltests/proftest.h
@@ -0,0 +1,17 @@
+#ifndef __H_PROFTEST
+#define __H_PROFTEST
+
+#define XDG_CONFIG_HOME "./tests/functionaltests/files/xdg_config_home"
+#define XDG_DATA_HOME   "./tests/functionaltests/files/xdg_data_home"
+
+void init_prof_test(void **state);
+void close_prof_test(void **state);
+
+void prof_start(void);
+void prof_connect(void);
+void prof_input(char *input);
+
+int prof_output_exact(char *text);
+int prof_output_regex(char *text);
+
+#endif
diff --git a/tests/functionaltests/start_profanity.sh b/tests/functionaltests/start_profanity.sh
new file mode 100755
index 00000000..58ceadd7
--- /dev/null
+++ b/tests/functionaltests/start_profanity.sh
@@ -0,0 +1,2 @@
+export COLUMNS=300
+./profanity -l DEBUG
diff --git a/tests/functionaltests/test_chat_session.c b/tests/functionaltests/test_chat_session.c
new file mode 100644
index 00000000..d1229e90
--- /dev/null
+++ b/tests/functionaltests/test_chat_session.c
@@ -0,0 +1,207 @@
+#include <glib.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <stabber.h>
+#include <expect.h>
+
+#include "proftest.h"
+
+void
+sends_message_to_barejid_when_contact_offline(void **state)
+{
+    prof_connect();
+
+    prof_input("/msg buddy1@localhost Hi there");
+
+    assert_true(stbbr_received(
+        "<message id=\"*\" to=\"buddy1@localhost\" type=\"chat\">"
+            "<body>Hi there</body>"
+        "</message>"
+    ));
+}
+
+void
+sends_message_to_barejid_when_contact_online(void **state)
+{
+    prof_connect();
+
+    stbbr_send(
+        "<presence to=\"stabber@localhost/profanity\" from=\"buddy1@localhost/mobile\">"
+            "<priority>10</priority>"
+        "</presence>"
+    );
+    assert_true(prof_output_exact("Buddy1 (mobile) is online"));
+
+    prof_input("/msg buddy1@localhost Hi there");
+
+    assert_true(stbbr_received(
+        "<message id=\"*\" to=\"buddy1@localhost\" type=\"chat\">"
+            "<body>Hi there</body>"
+        "</message>"
+    ));
+}
+
+void
+sends_message_to_fulljid_when_received_from_fulljid(void **state)
+{
+    prof_connect();
+
+    stbbr_send(
+        "<presence to=\"stabber@localhost\" from=\"buddy1@localhost/mobile\">"
+            "<priority>10</priority>"
+        "</presence>"
+    );
+    assert_true(prof_output_exact("Buddy1 (mobile) is online"));
+
+    stbbr_send(
+        "<message id=\"message1\" to=\"stabber@localhost\" from=\"buddy1@localhost/mobile\" type=\"chat\">"
+            "<body>First message</body>"
+        "</message>"
+    );
+    assert_true(prof_output_exact("<< incoming from Buddy1/mobile (2)"));
+
+    prof_input("/msg buddy1@localhost Hi there");
+
+    assert_true(stbbr_received(
+        "<message id=\"*\" to=\"buddy1@localhost/mobile\" type=\"chat\">"
+            "<body>Hi there</body>"
+        "</message>"
+    ));
+}
+
+void
+sends_subsequent_messages_to_fulljid(void **state)
+{
+    prof_connect();
+
+    stbbr_send(
+        "<presence to=\"stabber@localhost\" from=\"buddy1@localhost/mobile\">"
+            "<priority>10</priority>"
+        "</presence>"
+    );
+    assert_true(prof_output_exact("Buddy1 (mobile) is online"));
+
+    stbbr_send(
+        "<message id=\"message1\" to=\"stabber@localhost\" from=\"buddy1@localhost/mobile\" type=\"chat\">"
+            "<body>First message</body>"
+        "</message>"
+    );
+    assert_true(prof_output_exact("<< incoming from Buddy1/mobile (2)"));
+
+    prof_input("/msg buddy1@localhost Outgoing 1");
+    assert_true(stbbr_received(
+        "<message id=\"*\" to=\"buddy1@localhost/mobile\" type=\"chat\">"
+            "<body>Outgoing 1</body>"
+        "</message>"
+    ));
+
+    prof_input("/msg buddy1@localhost Outgoing 2");
+    assert_true(stbbr_received(
+        "<message id=\"*\" to=\"buddy1@localhost/mobile\" type=\"chat\">"
+            "<body>Outgoing 2</body>"
+        "</message>"
+    ));
+
+    prof_input("/msg buddy1@localhost Outgoing 3");
+    assert_true(stbbr_received(
+        "<message id=\"*\" to=\"buddy1@localhost/mobile\" type=\"chat\">"
+            "<body>Outgoing 3</body>"
+        "</message>"
+    ));
+}
+
+void
+resets_to_barejid_after_presence_received(void **state)
+{
+    prof_connect();
+
+    stbbr_send(
+        "<presence to=\"stabber@localhost\" from=\"buddy1@localhost/mobile\">"
+            "<priority>10</priority>"
+        "</presence>"
+    );
+    assert_true(prof_output_exact("Buddy1 (mobile) is online"));
+
+    stbbr_send(
+        "<message id=\"message1\" to=\"stabber@localhost\" from=\"buddy1@localhost/mobile\" type=\"chat\">"
+            "<body>First message</body>"
+        "</message>"
+    );
+    assert_true(prof_output_exact("<< incoming from Buddy1/mobile (2)"));
+
+    prof_input("/msg buddy1@localhost Outgoing 1");
+    assert_true(stbbr_received(
+        "<message id=\"*\" to=\"buddy1@localhost/mobile\" type=\"chat\">"
+            "<body>Outgoing 1</body>"
+        "</message>"
+    ));
+
+    stbbr_send(
+        "<presence to=\"stabber@localhost\" from=\"buddy1@localhost/laptop\">"
+            "<priority>5</priority>"
+            "<show>dnd</show>"
+        "</presence>"
+    );
+    assert_true(prof_output_exact("Buddy1 (laptop) is dnd"));
+
+    prof_input("/msg buddy1@localhost Outgoing 2");
+    assert_true(stbbr_received(
+        "<message id=\"*\" to=\"buddy1@localhost\" type=\"chat\">"
+            "<body>Outgoing 2</body>"
+        "</message>"
+    ));
+}
+
+void
+new_session_when_message_received_from_different_fulljid(void **state)
+{
+    prof_connect();
+
+    stbbr_send(
+        "<presence to=\"stabber@localhost\" from=\"buddy1@localhost/mobile\">"
+            "<priority>10</priority>"
+        "</presence>"
+    );
+    assert_true(prof_output_exact("Buddy1 (mobile) is online"));
+
+    stbbr_send(
+        "<presence to=\"stabber@localhost\" from=\"buddy1@localhost/laptop\">"
+            "<priority>8</priority>"
+            "<show>away</show>"
+        "</presence>"
+    );
+    assert_true(prof_output_exact("Buddy1 (laptop) is away"));
+
+    stbbr_send(
+        "<message id=\"message1\" to=\"stabber@localhost\" from=\"buddy1@localhost/mobile\" type=\"chat\">"
+            "<body>From first resource</body>"
+        "</message>"
+    );
+    assert_true(prof_output_exact("<< incoming from Buddy1/mobile (2)"));
+
+    prof_input("/msg buddy1@localhost Outgoing 1");
+    assert_true(stbbr_received(
+        "<message id=\"*\" to=\"buddy1@localhost/mobile\" type=\"chat\">"
+            "<body>Outgoing 1</body>"
+        "</message>"
+    ));
+
+    stbbr_send(
+        "<message id=\"message1\" to=\"stabber@localhost\" from=\"buddy1@localhost/laptop\" type=\"chat\">"
+            "<body>From second resource</body>"
+        "</message>"
+    );
+    assert_true(prof_output_regex("Buddy1/laptop:.+From second resource"));
+
+    prof_input("/msg buddy1@localhost Outgoing 2");
+    assert_true(stbbr_received(
+        "<message id=\"*\" to=\"buddy1@localhost/laptop\" type=\"chat\">"
+            "<body>Outgoing 2</body>"
+        "</message>"
+    ));
+}
diff --git a/tests/functionaltests/test_chat_session.h b/tests/functionaltests/test_chat_session.h
new file mode 100644
index 00000000..2ba75e05
--- /dev/null
+++ b/tests/functionaltests/test_chat_session.h
@@ -0,0 +1,7 @@
+void sends_message_to_barejid_when_contact_offline(void **state);
+void sends_message_to_barejid_when_contact_online(void **state);
+void sends_message_to_fulljid_when_received_from_fulljid(void **state);
+void sends_subsequent_messages_to_fulljid(void **state);
+void resets_to_barejid_after_presence_received(void **state);
+void new_session_when_message_received_from_different_fulljid(void **state);
+
diff --git a/tests/functionaltests/test_connect.c b/tests/functionaltests/test_connect.c
new file mode 100644
index 00000000..5fcb8a2f
--- /dev/null
+++ b/tests/functionaltests/test_connect.c
@@ -0,0 +1,95 @@
+#include <glib.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <stabber.h>
+#include <expect.h>
+
+#include "proftest.h"
+
+void
+connect_jid_requests_roster(void **state)
+{
+    prof_connect();
+
+    assert_true(stbbr_received(
+        "<iq id=\"*\" type=\"get\"><query xmlns=\"jabber:iq:roster\"/></iq>"
+    ));
+}
+
+void
+connect_jid_sends_presence_after_receiving_roster(void **state)
+{
+    prof_connect();
+
+    assert_true(stbbr_received(
+        "<presence id=\"*\">"
+            "<c hash=\"sha-1\" xmlns=\"http://jabber.org/protocol/caps\" ver=\"*\" node=\"http://www.profanity.im\"/>"
+        "</presence>"
+    ));
+}
+
+void
+connect_jid_requests_bookmarks(void **state)
+{
+    prof_connect();
+
+    assert_true(stbbr_received(
+        "<iq id=\"*\" type=\"get\">"
+            "<query xmlns=\"jabber:iq:private\">"
+                "<storage xmlns=\"storage:bookmarks\"/>"
+            "</query>"
+        "</iq>"
+    ));
+}
+
+void
+connect_bad_password(void **state)
+{
+    prof_input("/connect stabber@localhost port 5230");
+    prof_input("badpassword");
+
+    assert_true(prof_output_exact("Login failed."));
+}
+
+void
+connect_shows_presence_updates(void **state)
+{
+    prof_connect();
+
+    stbbr_send(
+        "<presence to=\"stabber@localhost\" from=\"buddy1@localhost/mobile\">"
+            "<show>dnd</show>"
+            "<status>busy!</status>"
+        "</presence>"
+    );
+    assert_true(prof_output_exact("Buddy1 (mobile) is dnd, \"busy!\""));
+
+    stbbr_send(
+        "<presence to=\"stabber@localhost\" from=\"buddy1@localhost/laptop\">"
+            "<show>chat</show>"
+            "<status>Talk to me!</status>"
+        "</presence>"
+    );
+    assert_true(prof_output_exact("Buddy1 (laptop) is chat, \"Talk to me!\""));
+
+    stbbr_send(
+        "<presence to=\"stabber@localhost\" from=\"buddy2@localhost/work\">"
+            "<show>away</show>"
+            "<status>Out of office</status>"
+        "</presence>"
+    );
+    assert_true(prof_output_exact("Buddy2 (work) is away, \"Out of office\""));
+
+    stbbr_send(
+        "<presence to=\"stabber@localhost\" from=\"buddy1@localhost/mobile\">"
+            "<show>xa</show>"
+            "<status>Gone :(</status>"
+        "</presence>"
+    );
+    assert_true(prof_output_exact("Buddy1 (mobile) is xa, \"Gone :(\""));
+}
diff --git a/tests/functionaltests/test_connect.h b/tests/functionaltests/test_connect.h
new file mode 100644
index 00000000..66010e25
--- /dev/null
+++ b/tests/functionaltests/test_connect.h
@@ -0,0 +1,6 @@
+void connect_jid_requests_roster(void **state);
+void connect_jid_sends_presence_after_receiving_roster(void **state);
+void connect_jid_requests_bookmarks(void **state);
+void connect_bad_password(void **state);
+void connect_shows_presence_updates(void **state);
+
diff --git a/tests/functionaltests/test_message.c b/tests/functionaltests/test_message.c
new file mode 100644
index 00000000..0957b309
--- /dev/null
+++ b/tests/functionaltests/test_message.c
@@ -0,0 +1,42 @@
+#include <glib.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <stabber.h>
+#include <expect.h>
+
+#include "proftest.h"
+
+void
+message_send(void **state)
+{
+    prof_connect();
+
+    prof_input("/msg somejid@someserver.com Hi there");
+
+    assert_true(stbbr_received(
+        "<message id=\"*\" to=\"somejid@someserver.com\" type=\"chat\">"
+            "<body>Hi there</body>"
+        "</message>"
+    ));
+
+    assert_true(prof_output_regex("me: .+Hi there"));
+}
+
+void
+message_receive(void **state)
+{
+    prof_connect();
+
+    stbbr_send(
+        "<message id=\"message1\" to=\"stabber@localhost\" from=\"someuser@chatserv.org/laptop\" type=\"chat\">"
+            "<body>How are you?</body>"
+        "</message>"
+    );
+
+    assert_true(prof_output_exact("<< incoming from someuser@chatserv.org/laptop (2)"));
+}
diff --git a/tests/functionaltests/test_message.h b/tests/functionaltests/test_message.h
new file mode 100644
index 00000000..b8f03a7e
--- /dev/null
+++ b/tests/functionaltests/test_message.h
@@ -0,0 +1,2 @@
+void message_send(void **state);
+void message_receive(void **state);
diff --git a/tests/functionaltests/test_ping.c b/tests/functionaltests/test_ping.c
new file mode 100644
index 00000000..76fd979c
--- /dev/null
+++ b/tests/functionaltests/test_ping.c
@@ -0,0 +1,57 @@
+#include <glib.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <stabber.h>
+#include <expect.h>
+
+#include "proftest.h"
+
+void
+ping_multiple(void **state)
+{
+    stbbr_for_id("prof_ping_2",
+        "<iq id=\"prof_ping_2\" type=\"result\" to=\"stabber@localhost/profanity\"/>"
+    );
+    stbbr_for_id("prof_ping_3",
+        "<iq id=\"prof_ping_3\" type=\"result\" to=\"stabber@localhost/profanity\"/>"
+    );
+
+    prof_connect();
+
+    prof_input("/ping");
+    assert_true(stbbr_received(
+        "<iq id=\"prof_ping_2\" type=\"get\">"
+            "<ping xmlns=\"urn:xmpp:ping\"/>"
+        "</iq>"
+    ));
+    assert_true(prof_output_exact("Ping response from server"));
+
+    prof_input("/ping");
+    assert_true(stbbr_received(
+        "<iq id=\"prof_ping_3\" type=\"get\">"
+            "<ping xmlns=\"urn:xmpp:ping\"/>"
+        "</iq>"
+    ));
+    assert_true(prof_output_exact("Ping response from server"));
+}
+
+void
+ping_responds(void **state)
+{
+    prof_connect();
+
+    stbbr_send(
+        "<iq id=\"pingtest1\" type=\"get\" to=\"stabber@localhost/profanity\" from=\"localhost\">"
+            "<ping xmlns=\"urn:xmpp:ping\"/>"
+        "</iq>"
+    );
+
+    assert_true(stbbr_received(
+        "<iq id=\"pingtest1\" type=\"result\" from=\"stabber@localhost/profanity\" to=\"localhost\"/>"
+    ));
+}
diff --git a/tests/functionaltests/test_ping.h b/tests/functionaltests/test_ping.h
new file mode 100644
index 00000000..a222a486
--- /dev/null
+++ b/tests/functionaltests/test_ping.h
@@ -0,0 +1,2 @@
+void ping_multiple(void **state);
+void ping_responds(void **state);
diff --git a/tests/functionaltests/test_presence.c b/tests/functionaltests/test_presence.c
new file mode 100644
index 00000000..cb2eacd3
--- /dev/null
+++ b/tests/functionaltests/test_presence.c
@@ -0,0 +1,243 @@
+#include <glib.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <stabber.h>
+#include <expect.h>
+
+#include "proftest.h"
+
+void
+presence_online(void **state)
+{
+    prof_connect();
+
+    prof_input("/online");
+
+    assert_true(stbbr_received(
+        "<presence id=\"prof_presence_2\">"
+            "<c hash=\"sha-1\" xmlns=\"http://jabber.org/protocol/caps\" ver=\"*\" node=\"http://www.profanity.im\"/>"
+        "</presence>"
+    ));
+
+    assert_true(prof_output_exact("Status set to online (priority 0)"));
+}
+
+void
+presence_online_with_message(void **state)
+{
+    prof_connect();
+
+    prof_input("/online \"Hi there\"");
+
+    assert_true(stbbr_received(
+        "<presence id=\"prof_presence_2\">"
+            "<status>Hi there</status>"
+            "<c hash=\"sha-1\" xmlns=\"http://jabber.org/protocol/caps\" ver=\"*\" node=\"http://www.profanity.im\"/>"
+        "</presence>"
+    ));
+
+    assert_true(prof_output_exact("Status set to online (priority 0), \"Hi there\"."));
+}
+
+void
+presence_away(void **state)
+{
+    prof_connect();
+
+    prof_input("/away");
+
+    assert_true(stbbr_received(
+        "<presence id=\"prof_presence_2\">"
+            "<show>away</show>"
+            "<c hash=\"sha-1\" xmlns=\"http://jabber.org/protocol/caps\" ver=\"*\" node=\"http://www.profanity.im\"/>"
+        "</presence>"
+    ));
+
+    assert_true(prof_output_exact("Status set to away (priority 0)"));
+}
+
+void
+presence_away_with_message(void **state)
+{
+    prof_connect();
+
+    prof_input("/away \"I'm not here for a bit\"");
+
+    assert_true(stbbr_received(
+        "<presence id=\"prof_presence_2\">"
+            "<show>away</show>"
+            "<status>I'm not here for a bit</status>"
+            "<c hash=\"sha-1\" xmlns=\"http://jabber.org/protocol/caps\" ver=\"*\" node=\"http://www.profanity.im\"/>"
+        "</presence>"
+    ));
+
+    assert_true(prof_output_exact("Status set to away (priority 0), \"I'm not here for a bit\"."));
+}
+
+void
+presence_xa(void **state)
+{
+    prof_connect();
+
+    prof_input("/xa");
+
+    assert_true(stbbr_received(
+        "<presence id=\"prof_presence_2\">"
+            "<show>xa</show>"
+            "<c hash=\"sha-1\" xmlns=\"http://jabber.org/protocol/caps\" ver=\"*\" node=\"http://www.profanity.im\"/>"
+        "</presence>"
+    ));
+
+    assert_true(prof_output_exact("Status set to xa (priority 0)"));
+}
+
+void
+presence_xa_with_message(void **state)
+{
+    prof_connect();
+
+    prof_input("/xa \"Gone to the shops\"");
+
+    assert_true(stbbr_received(
+        "<presence id=\"prof_presence_2\">"
+            "<show>xa</show>"
+            "<status>Gone to the shops</status>"
+            "<c hash=\"sha-1\" xmlns=\"http://jabber.org/protocol/caps\" ver=\"*\" node=\"http://www.profanity.im\"/>"
+        "</presence>"
+    ));
+
+    assert_true(prof_output_exact("Status set to xa (priority 0), \"Gone to the shops\"."));
+}
+
+void
+presence_dnd(void **state)
+{
+    prof_connect();
+
+    prof_input("/dnd");
+
+    assert_true(stbbr_received(
+        "<presence id=\"prof_presence_2\">"
+            "<show>dnd</show>"
+            "<c hash=\"sha-1\" xmlns=\"http://jabber.org/protocol/caps\" ver=\"*\" node=\"http://www.profanity.im\"/>"
+        "</presence>"
+    ));
+
+    assert_true(prof_output_exact("Status set to dnd (priority 0)"));
+}
+
+void
+presence_dnd_with_message(void **state)
+{
+    prof_connect();
+
+    prof_input("/dnd \"Working\"");
+
+    assert_true(stbbr_received(
+        "<presence id=\"prof_presence_2\">"
+            "<show>dnd</show>"
+            "<status>Working</status>"
+            "<c hash=\"sha-1\" xmlns=\"http://jabber.org/protocol/caps\" ver=\"*\" node=\"http://www.profanity.im\"/>"
+        "</presence>"
+    ));
+
+    assert_true(prof_output_exact("Status set to dnd (priority 0), \"Working\"."));
+}
+
+void
+presence_chat(void **state)
+{
+    prof_connect();
+
+    prof_input("/chat");
+
+    assert_true(stbbr_received(
+        "<presence id=\"prof_presence_2\">"
+            "<show>chat</show>"
+            "<c hash=\"sha-1\" xmlns=\"http://jabber.org/protocol/caps\" ver=\"*\" node=\"http://www.profanity.im\"/>"
+        "</presence>"
+    ));
+
+    assert_true(prof_output_exact("Status set to chat (priority 0)"));
+}
+
+void
+presence_chat_with_message(void **state)
+{
+    prof_connect();
+
+    prof_input("/chat \"Free to talk\"");
+
+    assert_true(stbbr_received(
+        "<presence id=\"prof_presence_2\">"
+            "<show>chat</show>"
+            "<status>Free to talk</status>"
+            "<c hash=\"sha-1\" xmlns=\"http://jabber.org/protocol/caps\" ver=\"*\" node=\"http://www.profanity.im\"/>"
+        "</presence>"
+    ));
+
+    assert_true(prof_output_exact("Status set to chat (priority 0), \"Free to talk\"."));
+}
+
+void
+presence_set_priority(void **state)
+{
+    prof_connect();
+
+    prof_input("/priority 25");
+
+    assert_true(stbbr_received(
+        "<presence id=\"prof_presence_2\">"
+            "<priority>25</priority>"
+            "<c hash=\"sha-1\" xmlns=\"http://jabber.org/protocol/caps\" ver=\"*\" node=\"http://www.profanity.im\"/>"
+        "</presence>"
+    ));
+
+    assert_true(prof_output_exact("Priority set to 25."));
+}
+
+void
+presence_includes_priority(void **state)
+{
+    prof_connect();
+
+    prof_input("/priority 25");
+    assert_true(stbbr_received(
+        "<presence id=\"prof_presence_2\">"
+            "<priority>25</priority>"
+            "<c hash=\"sha-1\" xmlns=\"http://jabber.org/protocol/caps\" ver=\"*\" node=\"http://www.profanity.im\"/>"
+        "</presence>"
+    ));
+    assert_true(prof_output_exact("Priority set to 25."));
+
+    prof_input("/chat \"Free to talk\"");
+    assert_true(stbbr_received(
+        "<presence id=\"prof_presence_3\">"
+            "<priority>25</priority>"
+            "<show>chat</show>"
+            "<status>Free to talk</status>"
+            "<c hash=\"sha-1\" xmlns=\"http://jabber.org/protocol/caps\" ver=\"*\" node=\"http://www.profanity.im\"/>"
+        "</presence>"
+    ));
+    assert_true(prof_output_exact("Status set to chat (priority 25), \"Free to talk\"."));
+}
+
+void
+presence_received(void **state)
+{
+    prof_connect();
+
+    stbbr_send(
+        "<presence to=\"stabber@localhost\" from=\"buddy1@localhost/mobile\">"
+            "<priority>10</priority>"
+            "<status>I'm here</status>"
+        "</presence>"
+    );
+
+    assert_true(prof_output_exact("Buddy1 (mobile) is online, \"I'm here\""));
+}
diff --git a/tests/functionaltests/test_presence.h b/tests/functionaltests/test_presence.h
new file mode 100644
index 00000000..0603732a
--- /dev/null
+++ b/tests/functionaltests/test_presence.h
@@ -0,0 +1,13 @@
+void presence_away(void **state);
+void presence_away_with_message(void **state);
+void presence_online(void **state);
+void presence_online_with_message(void **state);
+void presence_xa(void **state);
+void presence_xa_with_message(void **state);
+void presence_dnd(void **state);
+void presence_dnd_with_message(void **state);
+void presence_chat(void **state);
+void presence_chat_with_message(void **state);
+void presence_set_priority(void **state);
+void presence_includes_priority(void **state);
+void presence_received(void **state);
diff --git a/tests/functionaltests/test_rooms.c b/tests/functionaltests/test_rooms.c
new file mode 100644
index 00000000..c81ded6b
--- /dev/null
+++ b/tests/functionaltests/test_rooms.c
@@ -0,0 +1,38 @@
+#include <glib.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <stabber.h>
+#include <expect.h>
+
+#include "proftest.h"
+
+void
+rooms_query(void **state)
+{
+    stbbr_for_id("confreq",
+        "<iq id=\"confreq\" type=\"result\" to=\"stabber@localhost/profanity\" from=\"conference.localhost\">"
+            "<query xmlns=\"http://jabber.org/protocol/disco#items\">"
+                "<item jid=\"chatroom@conference.localhost\" name=\"A chat room\"/>"
+                "<item jid=\"hangout@conference.localhost\" name=\"Another chat room\"/>"
+            "</query>"
+        "</iq>"
+    );
+
+    prof_connect();
+
+    prof_input("/rooms");
+
+    assert_true(prof_output_exact("chatroom@conference.localhost, (A chat room)"));
+    assert_true(prof_output_exact("hangout@conference.localhost, (Another chat room)"));
+
+    assert_true(stbbr_last_received(
+        "<iq id=\"confreq\" to=\"conference.localhost\" type=\"get\">"
+            "<query xmlns=\"http://jabber.org/protocol/disco#items\"/>"
+        "</iq>"
+    ));
+}
diff --git a/tests/functionaltests/test_rooms.h b/tests/functionaltests/test_rooms.h
new file mode 100644
index 00000000..a0cf5db8
--- /dev/null
+++ b/tests/functionaltests/test_rooms.h
@@ -0,0 +1,2 @@
+void rooms_query(void **state);
+
diff --git a/tests/config/stub_accounts.c b/tests/unittests/config/stub_accounts.c
index 32a80fda..c046be86 100644
--- a/tests/config/stub_accounts.c
+++ b/tests/unittests/config/stub_accounts.c
@@ -122,6 +122,7 @@ void accounts_set_otr_policy(const char * const account_name, const char * const
 }
 
 void accounts_set_last_presence(const char * const account_name, const char * const value) {}
+void accounts_set_pgp_keyid(const char * const account_name, const char * const value) {}
 
 void accounts_set_login_presence(const char * const account_name, const char * const value)
 {
@@ -182,4 +183,5 @@ void accounts_clear_eval_password(const char * const account_name) {}
 void accounts_clear_server(const char * const account_name) {}
 void accounts_clear_port(const char * const account_name) {}
 void accounts_clear_otr(const char * const account_name) {}
+void accounts_clear_pgp_keyid(const char * const account_name) {}
 void accounts_add_otr_policy(const char * const account_name, const char * const contact_jid, const char * const policy) {}
diff --git a/tests/helpers.c b/tests/unittests/helpers.c
index 564b2716..564b2716 100644
--- a/tests/helpers.c
+++ b/tests/unittests/helpers.c
diff --git a/tests/helpers.h b/tests/unittests/helpers.h
index 75d446d0..75d446d0 100644
--- a/tests/helpers.h
+++ b/tests/unittests/helpers.h
diff --git a/tests/log/stub_log.c b/tests/unittests/log/stub_log.c
index f88871a7..3baddb69 100644
--- a/tests/log/stub_log.c
+++ b/tests/unittests/log/stub_log.c
@@ -49,14 +49,20 @@ log_level_t log_level_from_string(char *log_level)
     return (log_level_t)mock();
 }
 
+void log_stderr_init(log_level_t level) {}
+void log_stderr_close(void) {}
+void log_stderr_handler(void) {}
+
 void chat_log_init(void) {}
 
 void chat_log_msg_out(const char * const barejid, const char * const msg) {}
 void chat_log_otr_msg_out(const char * const barejid, const char * const msg) {}
+void chat_log_pgp_msg_out(const char * const barejid, const char * const msg) {}
 
 void chat_log_msg_in(const char * const barejid, const char * const msg) {}
-void chat_log_msg_in_delayed(const char * const barejid, const char * msg, GTimeVal *tv_stamp) {}
+void chat_log_msg_in_delayed(const char * const barejid, const char * msg, GDateTime *timestamp) {}
 void chat_log_otr_msg_in(const char * const barejid, const char * const msg, gboolean was_decrypted) {}
+void chat_log_pgp_msg_in(const char * const barejid, const char * const msg) {}
 
 void chat_log_close(void) {}
 GSList * chat_log_get_previous(const gchar * const login,
diff --git a/tests/otr/stub_otr.c b/tests/unittests/otr/stub_otr.c
index 482f0a7f..098484d5 100644
--- a/tests/otr/stub_otr.c
+++ b/tests/unittests/otr/stub_otr.c
@@ -41,8 +41,14 @@ char* otr_start_query(void)
 
 void otr_poll(void) {}
 void otr_on_connect(ProfAccount *account) {}
-void otr_on_message_recv(const char * const barejid, const char * const resource, const char * const message) {}
-void otr_on_message_send(ProfChatWin *chatwin, const char * const message) {}
+char* otr_on_message_recv(const char * const barejid, const char * const resource, const char * const message, gboolean *was_decrypted)
+{
+    return NULL;
+}
+gboolean otr_on_message_send(ProfChatWin *chatwin, const char * const message)
+{
+    return FALSE;
+}
 
 void otr_keygen(ProfAccount *account)
 {
diff --git a/tests/unittests/pgp/stub_gpg.c b/tests/unittests/pgp/stub_gpg.c
new file mode 100644
index 00000000..671b2092
--- /dev/null
+++ b/tests/unittests/pgp/stub_gpg.c
@@ -0,0 +1,49 @@
+#include <glib.h>
+
+#include "pgp/gpg.h"
+
+void p_gpg_init(void) {}
+void p_gpg_close(void) {}
+
+GSList* p_gpg_list_keys(void)
+{
+    return NULL;
+}
+
+GHashTable*
+p_gpg_fingerprints(void)
+{
+    return NULL;
+}
+
+const char* p_gpg_libver(void)
+{
+    return NULL;
+}
+
+void p_gpg_free_key(ProfPGPKey *key) {}
+
+void p_gpg_verify(const char * const barejid, const char *const sign) {}
+
+char* p_gpg_sign(const char * const str, const char * const fp)
+{
+    return NULL;
+}
+
+gboolean p_gpg_available(const char * const barejid)
+{
+    return FALSE;
+}
+char * p_gpg_decrypt(const char * const barejid, const char * const cipher)
+{
+    return NULL;
+}
+
+void p_gpg_on_connect(const char * const barejid) {}
+void p_gpg_on_disconnect(void) {}
+
+gboolean p_gpg_addkey(const char * const jid, const char * const keyid)
+{
+    return TRUE;
+}
+
diff --git a/tests/test_autocomplete.c b/tests/unittests/test_autocomplete.c
index f6ef8653..f6ef8653 100644
--- a/tests/test_autocomplete.c
+++ b/tests/unittests/test_autocomplete.c
diff --git a/tests/test_autocomplete.h b/tests/unittests/test_autocomplete.h
index 4ad327c0..4ad327c0 100644
--- a/tests/test_autocomplete.h
+++ b/tests/unittests/test_autocomplete.h
diff --git a/tests/test_chat_session.c b/tests/unittests/test_chat_session.c
index b5e1f7b6..b5e1f7b6 100644
--- a/tests/test_chat_session.c
+++ b/tests/unittests/test_chat_session.c
diff --git a/tests/test_chat_session.h b/tests/unittests/test_chat_session.h
index 4ce03fd5..4ce03fd5 100644
--- a/tests/test_chat_session.h
+++ b/tests/unittests/test_chat_session.h
diff --git a/tests/test_cmd_account.c b/tests/unittests/test_cmd_account.c
index bddc4c6d..51213eb5 100644
--- a/tests/test_cmd_account.c
+++ b/tests/unittests/test_cmd_account.c
@@ -25,7 +25,7 @@ void cmd_account_shows_usage_when_not_connected_and_no_args(void **state)
 
     expect_cons_show("Usage: some usage");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -36,7 +36,7 @@ void cmd_account_shows_account_when_connected_and_no_args(void **state)
 {
     CommandHelp *help = malloc(sizeof(CommandHelp));
     ProfAccount *account = account_new("jabber_org", "me@jabber.org", NULL, NULL,
-        TRUE, NULL, 0, NULL, NULL, NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL);
+        TRUE, NULL, 0, NULL, NULL, NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
     gchar *args[] = { NULL };
 
     will_return(jabber_get_connection_status, JABBER_CONNECTED);
@@ -46,7 +46,7 @@ void cmd_account_shows_account_when_connected_and_no_args(void **state)
 
     expect_memory(cons_show_account, account, account, sizeof(ProfAccount));
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -67,7 +67,7 @@ void cmd_account_list_shows_accounts(void **state)
 
     expect_memory(cons_show_account_list, accounts, accounts, sizeof(accounts));
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -81,7 +81,7 @@ void cmd_account_show_shows_usage_when_no_arg(void **state)
 
     expect_cons_show("Usage: some usage");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -98,7 +98,7 @@ void cmd_account_show_shows_message_when_account_does_not_exist(void **state)
     expect_cons_show("No such account.");
     expect_cons_show("");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -109,14 +109,14 @@ void cmd_account_show_shows_account_when_exists(void **state)
     CommandHelp *help = malloc(sizeof(CommandHelp));
     gchar *args[] = { "show", "account_name", NULL };
     ProfAccount *account = account_new("jabber_org", "me@jabber.org", NULL, NULL,
-        TRUE, NULL, 0, NULL, NULL, NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL);
+        TRUE, NULL, 0, NULL, NULL, NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
 
     expect_any(accounts_get_account, name);
     will_return(accounts_get_account, account);
 
     expect_memory(cons_show_account, account, account, sizeof(account));
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -130,7 +130,7 @@ void cmd_account_add_shows_usage_when_no_arg(void **state)
 
     expect_cons_show("Usage: some usage");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -147,7 +147,7 @@ void cmd_account_add_adds_account(void **state)
     expect_cons_show("Account created.");
     expect_cons_show("");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -161,7 +161,7 @@ void cmd_account_enable_shows_usage_when_no_arg(void **state)
 
     expect_cons_show("Usage: some usage");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -178,7 +178,7 @@ void cmd_account_enable_enables_account(void **state)
     expect_cons_show("Account enabled.");
     expect_cons_show("");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -195,7 +195,7 @@ void cmd_account_enable_shows_message_when_account_doesnt_exist(void **state)
     expect_cons_show("No such account: account_name");
     expect_cons_show("");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -209,7 +209,7 @@ void cmd_account_disable_shows_usage_when_no_arg(void **state)
 
     expect_cons_show("Usage: some usage");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -226,7 +226,7 @@ void cmd_account_disable_disables_account(void **state)
     expect_cons_show("Account disabled.");
     expect_cons_show("");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -243,7 +243,7 @@ void cmd_account_disable_shows_message_when_account_doesnt_exist(void **state)
     expect_cons_show("No such account: account_name");
     expect_cons_show("");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -257,7 +257,7 @@ void cmd_account_rename_shows_usage_when_no_args(void **state)
 
     expect_cons_show("Usage: some usage");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -271,7 +271,7 @@ void cmd_account_rename_shows_usage_when_one_arg(void **state)
 
     expect_cons_show("Usage: some usage");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -289,7 +289,7 @@ void cmd_account_rename_renames_account(void **state)
     expect_cons_show("Account renamed.");
     expect_cons_show("");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -307,7 +307,7 @@ void cmd_account_rename_shows_message_when_not_renamed(void **state)
     expect_cons_show("Either account original_name doesn't exist, or account new_name already exists.");
     expect_cons_show("");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -321,7 +321,7 @@ void cmd_account_set_shows_usage_when_no_args(void **state)
 
     expect_cons_show("Usage: some usage");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -335,7 +335,7 @@ void cmd_account_set_shows_usage_when_one_arg(void **state)
 
     expect_cons_show("Usage: some usage");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -349,7 +349,7 @@ void cmd_account_set_shows_usage_when_two_args(void **state)
 
     expect_cons_show("Usage: some usage");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -366,7 +366,7 @@ void cmd_account_set_shows_message_when_account_doesnt_exist(void **state)
     expect_cons_show("Account a_account doesn't exist");
     expect_cons_show("");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -382,7 +382,7 @@ void cmd_account_set_jid_shows_message_for_malformed_jid(void **state)
 
     expect_cons_show("Malformed jid: @malformed");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -402,7 +402,7 @@ void cmd_account_set_jid_sets_barejid(void **state)
     expect_cons_show("Updated jid for account a_account: a_local@a_domain");
     expect_cons_show("");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -427,7 +427,7 @@ void cmd_account_set_jid_sets_resource(void **state)
     expect_cons_show("Updated resource for account a_account: a_resource");
     expect_cons_show("");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -447,7 +447,7 @@ void cmd_account_set_server_sets_server(void **state)
     expect_cons_show("Updated server for account a_account: a_server");
     expect_cons_show("");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -458,6 +458,8 @@ void cmd_account_set_resource_sets_resource(void **state)
     CommandHelp *help = malloc(sizeof(CommandHelp));
     gchar *args[] = { "set", "a_account", "resource", "a_resource", NULL };
 
+    will_return(jabber_get_connection_status, JABBER_DISCONNECTED);
+
     expect_any(accounts_account_exists, account_name);
     will_return(accounts_account_exists, TRUE);
 
@@ -467,7 +469,29 @@ void cmd_account_set_resource_sets_resource(void **state)
     expect_cons_show("Updated resource for account a_account: a_resource");
     expect_cons_show("");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
+    assert_true(result);
+
+    free(help);
+}
+
+void cmd_account_set_resource_sets_resource_with_online_message(void **state)
+{
+    CommandHelp *help = malloc(sizeof(CommandHelp));
+    gchar *args[] = { "set", "a_account", "resource", "a_resource", NULL };
+
+    will_return(jabber_get_connection_status, JABBER_CONNECTED);
+
+    expect_any(accounts_account_exists, account_name);
+    will_return(accounts_account_exists, TRUE);
+
+    expect_string(accounts_set_resource, account_name, "a_account");
+    expect_string(accounts_set_resource, value, "a_resource");
+
+    expect_cons_show("Updated resource for account a_account: a_resource, you will need to reconnect to pick up the change.");
+    expect_cons_show("");
+
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -478,7 +502,7 @@ void cmd_account_set_password_sets_password(void **state)
     CommandHelp *help = malloc(sizeof(CommandHelp));
     gchar *args[] = { "set", "a_account", "password", "a_password", NULL };
     ProfAccount *account = account_new("a_account", NULL, NULL, NULL,
-    TRUE, NULL, 0, NULL, NULL, NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL);
+    TRUE, NULL, 0, NULL, NULL, NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
 
 
     expect_any(accounts_account_exists, account_name);
@@ -493,7 +517,7 @@ void cmd_account_set_password_sets_password(void **state)
     expect_cons_show("Updated password for account a_account");
     expect_cons_show("");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -504,7 +528,7 @@ void cmd_account_set_eval_password_sets_eval_password(void **state)
     CommandHelp *help = malloc(sizeof(CommandHelp));
     gchar *args[] = { "set", "a_account", "eval_password", "a_password", NULL };
     ProfAccount *account = account_new("a_account", NULL, NULL, NULL,
-    TRUE, NULL, 0, NULL, NULL, NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL);
+    TRUE, NULL, 0, NULL, NULL, NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
 
 
     expect_any(accounts_account_exists, account_name);
@@ -519,7 +543,7 @@ void cmd_account_set_eval_password_sets_eval_password(void **state)
     expect_cons_show("Updated eval_password for account a_account");
     expect_cons_show("");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -529,7 +553,7 @@ void cmd_account_set_password_when_eval_password_set(void **state) {
     CommandHelp *help = malloc(sizeof(CommandHelp));
     gchar *args[] = { "set", "a_account", "password", "a_password", NULL };
     ProfAccount *account = account_new("a_account", NULL, NULL, "a_password",
-    TRUE, NULL, 0, NULL, NULL, NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL);
+    TRUE, NULL, 0, NULL, NULL, NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
 
 
     expect_any(accounts_account_exists, account_name);
@@ -540,7 +564,7 @@ void cmd_account_set_password_when_eval_password_set(void **state) {
 
     expect_cons_show("Cannot set password when eval_password is set.");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -550,7 +574,7 @@ void cmd_account_set_eval_password_when_password_set(void **state) {
     CommandHelp *help = malloc(sizeof(CommandHelp));
     gchar *args[] = { "set", "a_account", "eval_password", "a_password", NULL };
     ProfAccount *account = account_new("a_account", NULL, "a_password", NULL,
-    TRUE, NULL, 0, NULL, NULL, NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL);
+    TRUE, NULL, 0, NULL, NULL, NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
 
 
     expect_any(accounts_account_exists, account_name);
@@ -561,7 +585,7 @@ void cmd_account_set_eval_password_when_password_set(void **state) {
 
     expect_cons_show("Cannot set eval_password when password is set.");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -581,7 +605,7 @@ void cmd_account_set_muc_sets_muc(void **state)
     expect_cons_show("Updated muc service for account a_account: a_muc");
     expect_cons_show("");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -601,7 +625,7 @@ void cmd_account_set_nick_sets_nick(void **state)
     expect_cons_show("Updated muc nick for account a_account: a_nick");
     expect_cons_show("");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -615,7 +639,7 @@ void cmd_account_show_message_for_missing_otr_policy(void **state)
 
     expect_cons_show("Usage: Some usage");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -631,7 +655,7 @@ void cmd_account_show_message_for_invalid_otr_policy(void **state)
 
     expect_cons_show("OTR policy must be one of: manual, opportunistic or always.");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -651,7 +675,7 @@ void cmd_account_set_otr_sets_otr(void **state)
     expect_cons_show("Updated OTR policy for account a_account: opportunistic");
     expect_cons_show("");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -668,7 +692,7 @@ void cmd_account_set_status_shows_message_when_invalid_status(void **state)
     expect_cons_show("Invalid status: bad_status");
     expect_cons_show("");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -688,7 +712,7 @@ void cmd_account_set_status_sets_status_when_valid(void **state)
     expect_cons_show("Updated login status for account a_account: away");
     expect_cons_show("");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -708,7 +732,7 @@ void cmd_account_set_status_sets_status_when_last(void **state)
     expect_cons_show("Updated login status for account a_account: last");
     expect_cons_show("");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -725,7 +749,7 @@ void cmd_account_set_invalid_presence_string_priority_shows_message(void **state
     expect_cons_show("Invalid property: blah");
     expect_cons_show("");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -742,7 +766,7 @@ void cmd_account_set_last_priority_shows_message(void **state)
     expect_cons_show("Invalid property: last");
     expect_cons_show("");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -764,7 +788,7 @@ void cmd_account_set_online_priority_sets_preference(void **state)
     expect_cons_show("Updated online priority for account a_account: 10");
     expect_cons_show("");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -786,7 +810,7 @@ void cmd_account_set_chat_priority_sets_preference(void **state)
     expect_cons_show("Updated chat priority for account a_account: 10");
     expect_cons_show("");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -808,7 +832,7 @@ void cmd_account_set_away_priority_sets_preference(void **state)
     expect_cons_show("Updated away priority for account a_account: 10");
     expect_cons_show("");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -830,7 +854,7 @@ void cmd_account_set_xa_priority_sets_preference(void **state)
     expect_cons_show("Updated xa priority for account a_account: 10");
     expect_cons_show("");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -852,7 +876,7 @@ void cmd_account_set_dnd_priority_sets_preference(void **state)
     expect_cons_show("Updated dnd priority for account a_account: 10");
     expect_cons_show("");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -868,7 +892,7 @@ void cmd_account_set_priority_too_low_shows_message(void **state)
 
     expect_cons_show("Value -150 out of range. Must be in -128..127.");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -884,7 +908,7 @@ void cmd_account_set_priority_too_high_shows_message(void **state)
 
     expect_cons_show("Value 150 out of range. Must be in -128..127.");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -900,7 +924,7 @@ void cmd_account_set_priority_when_not_number_shows_message(void **state)
 
     expect_cons_show("Could not convert \"abc\" to a number.");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -916,7 +940,7 @@ void cmd_account_set_priority_when_empty_shows_message(void **state)
 
     expect_cons_show("Could not convert \"\" to a number.");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -927,6 +951,7 @@ void cmd_account_set_priority_updates_presence_when_account_connected_with_prese
     CommandHelp *help = malloc(sizeof(CommandHelp));
     gchar *args[] = { "set", "a_account", "online", "10", NULL };
 
+
     expect_any(accounts_account_exists, account_name);
     will_return(accounts_account_exists, TRUE);
 
@@ -934,21 +959,32 @@ void cmd_account_set_priority_updates_presence_when_account_connected_with_prese
     expect_any(accounts_set_priority_online, value);
 
     will_return(jabber_get_connection_status, JABBER_CONNECTED);
-    will_return(jabber_get_account_name, "a_account");
 
     expect_any(accounts_get_last_presence, account_name);
     will_return(accounts_get_last_presence, RESOURCE_ONLINE);
 
+    will_return(jabber_get_account_name, "a_account");
+
+#ifdef HAVE_LIBGPGME
+    ProfAccount *account = account_new("a_account", "a_jid", NULL, NULL, TRUE, NULL, 5222, "a_resource",
+        NULL, NULL, 10, 10, 10, 10, 10, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+
+    will_return(jabber_get_account_name, "a_account");
+    expect_any(accounts_get_account, name);
+    will_return(accounts_get_account, account);
+#endif
+
     will_return(jabber_get_presence_message, "Free to chat");
 
     expect_value(presence_send, status, RESOURCE_ONLINE);
     expect_string(presence_send, msg, "Free to chat");
     expect_value(presence_send, idle, 0);
+    expect_value(presence_send, signed_status, NULL);
 
     expect_cons_show("Updated online priority for account a_account: 10");
     expect_cons_show("");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -962,7 +998,7 @@ void cmd_account_clear_shows_usage_when_no_args(void **state)
 
     expect_cons_show("Usage: some usage");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -976,7 +1012,7 @@ void cmd_account_clear_shows_usage_when_one_arg(void **state)
 
     expect_cons_show("Usage: some usage");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -993,7 +1029,7 @@ void cmd_account_clear_shows_message_when_account_doesnt_exist(void **state)
     expect_cons_show("Account a_account doesn't exist");
     expect_cons_show("");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -1010,7 +1046,7 @@ void cmd_account_clear_shows_message_when_invalid_property(void **state)
     expect_cons_show("Invalid property: badproperty");
     expect_cons_show("");
 
-    gboolean result = cmd_account(args, *help);
+    gboolean result = cmd_account(NULL, args, *help);
     assert_true(result);
 
     free(help);
diff --git a/tests/test_cmd_account.h b/tests/unittests/test_cmd_account.h
index 91bd1e70..e8806c4f 100644
--- a/tests/test_cmd_account.h
+++ b/tests/unittests/test_cmd_account.h
@@ -25,6 +25,7 @@ void cmd_account_set_jid_sets_barejid(void **state);
 void cmd_account_set_jid_sets_resource(void **state);
 void cmd_account_set_server_sets_server(void **state);
 void cmd_account_set_resource_sets_resource(void **state);
+void cmd_account_set_resource_sets_resource_with_online_message(void **state);
 void cmd_account_set_password_sets_password(void **state);
 void cmd_account_set_eval_password_sets_eval_password(void **state);
 void cmd_account_set_password_when_eval_password_set(void **state);
diff --git a/tests/test_cmd_alias.c b/tests/unittests/test_cmd_alias.c
index 10ab7f53..37513f8d 100644
--- a/tests/test_cmd_alias.c
+++ b/tests/unittests/test_cmd_alias.c
@@ -24,7 +24,7 @@ void cmd_alias_add_shows_usage_when_no_args(void **state)
 
     expect_cons_show("Usage: some usage");
 
-    gboolean result = cmd_alias(args, *help);
+    gboolean result = cmd_alias(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -38,7 +38,7 @@ void cmd_alias_add_shows_usage_when_no_value(void **state)
 
     expect_cons_show("Usage: some usage");
 
-    gboolean result = cmd_alias(args, *help);
+    gboolean result = cmd_alias(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -52,7 +52,7 @@ void cmd_alias_remove_shows_usage_when_no_args(void **state)
 
     expect_cons_show("Usage: some usage");
 
-    gboolean result = cmd_alias(args, *help);
+    gboolean result = cmd_alias(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -66,7 +66,7 @@ void cmd_alias_show_usage_when_invalid_subcmd(void **state)
 
     expect_cons_show("Usage: some usage");
 
-    gboolean result = cmd_alias(args, *help);
+    gboolean result = cmd_alias(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -79,7 +79,7 @@ void cmd_alias_add_adds_alias(void **state)
 
     expect_cons_show("Command alias added /hc -> /help commands");
 
-    gboolean result = cmd_alias(args, *help);
+    gboolean result = cmd_alias(NULL, args, *help);
 
     char *returned_val = prefs_get_alias("hc");
 
@@ -100,7 +100,7 @@ void cmd_alias_add_shows_message_when_exists(void **state)
 
     expect_cons_show("Command or alias '/hc' already exists.");
 
-    gboolean result = cmd_alias(args, *help);
+    gboolean result = cmd_alias(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -115,7 +115,7 @@ void cmd_alias_remove_removes_alias(void **state)
 
     expect_cons_show("Command alias removed -> /hn");
 
-    gboolean result = cmd_alias(args, *help);
+    gboolean result = cmd_alias(NULL, args, *help);
 
     char *returned_val = prefs_get_alias("hn");
 
@@ -132,7 +132,7 @@ void cmd_alias_remove_shows_message_when_no_alias(void **state)
 
     expect_cons_show("No such command alias /hn");
 
-    gboolean result = cmd_alias(args, *help);
+    gboolean result = cmd_alias(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -152,7 +152,7 @@ void cmd_alias_list_shows_all_aliases(void **state)
     // write a custom checker to check the correct list is passed
     expect_any(cons_show_aliases, aliases);
 
-    gboolean result = cmd_alias(args, *help);
+    gboolean result = cmd_alias(NULL, args, *help);
     assert_true(result);
 
     free(help);
diff --git a/tests/test_cmd_alias.h b/tests/unittests/test_cmd_alias.h
index bd93ef1a..bd93ef1a 100644
--- a/tests/test_cmd_alias.h
+++ b/tests/unittests/test_cmd_alias.h
diff --git a/tests/test_cmd_bookmark.c b/tests/unittests/test_cmd_bookmark.c
index 695e7362..04326f98 100644
--- a/tests/test_cmd_bookmark.c
+++ b/tests/unittests/test_cmd_bookmark.c
@@ -27,7 +27,7 @@ static void test_with_connection_status(jabber_conn_status_t status)
     will_return(jabber_get_connection_status, status);
     expect_cons_show("You are not currently connected.");
 
-    gboolean result = cmd_bookmark(NULL, *help);
+    gboolean result = cmd_bookmark(NULL, NULL, *help);
     assert_true(result);
 
     free(help);
@@ -63,13 +63,14 @@ void cmd_bookmark_shows_usage_when_no_args(void **state)
     CommandHelp *help = malloc(sizeof(CommandHelp));
     help->usage = "some usage";
     gchar *args[] = { NULL };
+    ProfWin window;
+    window.type = WIN_CONSOLE;
 
     will_return(jabber_get_connection_status, JABBER_CONNECTED);
-    will_return(ui_current_win_type, WIN_CONSOLE);
 
     expect_cons_show("Usage: some usage");
 
-    gboolean result = cmd_bookmark(args, *help);
+    gboolean result = cmd_bookmark(&window, args, *help);
     assert_true(result);
 
     free(help);
@@ -103,6 +104,8 @@ void cmd_bookmark_list_shows_bookmarks(void **state)
     CommandHelp *help = malloc(sizeof(CommandHelp));
     gchar *args[] = { "list", NULL };
     GList *bookmarks = NULL;
+    ProfWin window;
+    window.type = WIN_CONSOLE;
 
     Bookmark *bm1 = malloc(sizeof(Bookmark));
     bm1->jid = strdup("room1@conf.org");
@@ -132,14 +135,13 @@ void cmd_bookmark_list_shows_bookmarks(void **state)
     bookmarks = g_list_append(bookmarks, bm5);
 
     will_return(jabber_get_connection_status, JABBER_CONNECTED);
-    will_return(ui_current_win_type, WIN_CONSOLE);
     will_return(bookmark_get_list, bookmarks);
 
     // TODO - Custom list compare
     glist_set_cmp((GCompareFunc)_cmp_bookmark);
     expect_any(cons_show_bookmarks, list);
 
-    gboolean result = cmd_bookmark(args, *help);
+    gboolean result = cmd_bookmark(&window, args, *help);
     assert_true(result);
 
     free(help);
@@ -151,13 +153,14 @@ void cmd_bookmark_add_shows_message_when_invalid_jid(void **state)
     char *jid = "room";
     CommandHelp *help = malloc(sizeof(CommandHelp));
     gchar *args[] = { "add", jid, NULL };
+    ProfWin window;
+    window.type = WIN_CONSOLE;
 
     will_return(jabber_get_connection_status, JABBER_CONNECTED);
-    will_return(ui_current_win_type, WIN_CONSOLE);
 
     expect_cons_show("Can't add bookmark with JID 'room'; should be 'room@domain.tld'");
 
-    gboolean result = cmd_bookmark(args, *help);
+    gboolean result = cmd_bookmark(&window, args, *help);
     assert_true(result);
 
     free(help);
@@ -168,9 +171,10 @@ void cmd_bookmark_add_adds_bookmark_with_jid(void **state)
     char *jid = "room@conf.server";
     CommandHelp *help = malloc(sizeof(CommandHelp));
     gchar *args[] = { "add", jid, NULL };
+    ProfWin window;
+    window.type = WIN_CONSOLE;
 
     will_return(jabber_get_connection_status, JABBER_CONNECTED);
-    will_return(ui_current_win_type, WIN_CONSOLE);
 
     expect_string(bookmark_add, jid, jid);
     expect_any(bookmark_add, nick);
@@ -180,7 +184,7 @@ void cmd_bookmark_add_adds_bookmark_with_jid(void **state)
 
     expect_cons_show("Bookmark added for room@conf.server.");
 
-    gboolean result = cmd_bookmark(args, *help);
+    gboolean result = cmd_bookmark(&window, args, *help);
     assert_true(result);
 
     free(help);
@@ -191,9 +195,10 @@ void cmd_bookmark_add_adds_bookmark_with_jid_nick(void **state)
     char *nick = "bob";
     CommandHelp *help = malloc(sizeof(CommandHelp));
     gchar *args[] = { "add", jid, "nick", nick, NULL };
+    ProfWin window;
+    window.type = WIN_CONSOLE;
 
     will_return(jabber_get_connection_status, JABBER_CONNECTED);
-    will_return(ui_current_win_type, WIN_CONSOLE);
 
     expect_string(bookmark_add, jid, jid);
     expect_string(bookmark_add, nick, nick);
@@ -203,7 +208,7 @@ void cmd_bookmark_add_adds_bookmark_with_jid_nick(void **state)
 
     expect_cons_show("Bookmark added for room@conf.server.");
 
-    gboolean result = cmd_bookmark(args, *help);
+    gboolean result = cmd_bookmark(&window, args, *help);
     assert_true(result);
 
     free(help);
@@ -214,9 +219,10 @@ void cmd_bookmark_add_adds_bookmark_with_jid_autojoin(void **state)
     char *jid = "room@conf.server";
     CommandHelp *help = malloc(sizeof(CommandHelp));
     gchar *args[] = { "add", jid, "autojoin", "on", NULL };
+    ProfWin window;
+    window.type = WIN_CONSOLE;
 
     will_return(jabber_get_connection_status, JABBER_CONNECTED);
-    will_return(ui_current_win_type, WIN_CONSOLE);
 
     expect_string(bookmark_add, jid, jid);
     expect_any(bookmark_add, nick);
@@ -226,7 +232,7 @@ void cmd_bookmark_add_adds_bookmark_with_jid_autojoin(void **state)
 
     expect_cons_show("Bookmark added for room@conf.server.");
 
-    gboolean result = cmd_bookmark(args, *help);
+    gboolean result = cmd_bookmark(&window, args, *help);
     assert_true(result);
 
     free(help);
@@ -238,9 +244,10 @@ void cmd_bookmark_add_adds_bookmark_with_jid_nick_autojoin(void **state)
     char *nick = "bob";
     CommandHelp *help = malloc(sizeof(CommandHelp));
     gchar *args[] = { "add", jid, "nick", nick, "autojoin", "on", NULL };
+    ProfWin window;
+    window.type = WIN_CONSOLE;
 
     will_return(jabber_get_connection_status, JABBER_CONNECTED);
-    will_return(ui_current_win_type, WIN_CONSOLE);
 
     expect_string(bookmark_add, jid, jid);
     expect_string(bookmark_add, nick, nick);
@@ -250,7 +257,7 @@ void cmd_bookmark_add_adds_bookmark_with_jid_nick_autojoin(void **state)
 
     expect_cons_show("Bookmark added for room@conf.server.");
 
-    gboolean result = cmd_bookmark(args, *help);
+    gboolean result = cmd_bookmark(&window, args, *help);
     assert_true(result);
 
     free(help);
@@ -261,16 +268,17 @@ void cmd_bookmark_remove_removes_bookmark(void **state)
     char *jid = "room@conf.server";
     CommandHelp *help = malloc(sizeof(CommandHelp));
     gchar *args[] = { "remove", jid, NULL };
+    ProfWin window;
+    window.type = WIN_CONSOLE;
 
     will_return(jabber_get_connection_status, JABBER_CONNECTED);
-    will_return(ui_current_win_type, WIN_CONSOLE);
 
     expect_string(bookmark_remove, jid, jid);
     will_return(bookmark_remove, TRUE);
 
     expect_cons_show("Bookmark removed for room@conf.server.");
 
-    gboolean result = cmd_bookmark(args, *help);
+    gboolean result = cmd_bookmark(&window, args, *help);
     assert_true(result);
 
     free(help);
@@ -281,16 +289,17 @@ void cmd_bookmark_remove_shows_message_when_no_bookmark(void **state)
     char *jid = "room@conf.server";
     CommandHelp *help = malloc(sizeof(CommandHelp));
     gchar *args[] = { "remove", jid, NULL };
+    ProfWin window;
+    window.type = WIN_CONSOLE;
 
     will_return(jabber_get_connection_status, JABBER_CONNECTED);
-    will_return(ui_current_win_type, WIN_CONSOLE);
 
     expect_any(bookmark_remove, jid);
     will_return(bookmark_remove, FALSE);
 
     expect_cons_show("No bookmark exists for room@conf.server.");
 
-    gboolean result = cmd_bookmark(args, *help);
+    gboolean result = cmd_bookmark(&window, args, *help);
     assert_true(result);
 
     free(help);
diff --git a/tests/test_cmd_bookmark.h b/tests/unittests/test_cmd_bookmark.h
index 9505e105..9505e105 100644
--- a/tests/test_cmd_bookmark.h
+++ b/tests/unittests/test_cmd_bookmark.h
diff --git a/tests/test_cmd_connect.c b/tests/unittests/test_cmd_connect.c
index e2089a09..b812bf23 100644
--- a/tests/test_cmd_connect.c
+++ b/tests/unittests/test_cmd_connect.c
@@ -22,7 +22,7 @@ static void test_with_connection_status(jabber_conn_status_t status)
 
     expect_cons_show("You are either connected already, or a login is in process.");
 
-    gboolean result = cmd_connect(NULL, *help);
+    gboolean result = cmd_connect(NULL, NULL, *help);
     assert_true(result);
 
     free(help);
@@ -59,7 +59,7 @@ void cmd_connect_shows_usage_when_no_server_value(void **state)
     expect_cons_show("Usage: some usage");
     expect_cons_show("");
 
-    gboolean result = cmd_connect(args, *help);
+    gboolean result = cmd_connect(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -76,7 +76,7 @@ void cmd_connect_shows_usage_when_server_no_port_value(void **state)
     expect_cons_show("Usage: some usage");
     expect_cons_show("");
 
-    gboolean result = cmd_connect(args, *help);
+    gboolean result = cmd_connect(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -93,7 +93,7 @@ void cmd_connect_shows_usage_when_no_port_value(void **state)
     expect_cons_show("Usage: some usage");
     expect_cons_show("");
 
-    gboolean result = cmd_connect(args, *help);
+    gboolean result = cmd_connect(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -110,7 +110,7 @@ void cmd_connect_shows_usage_when_port_no_server_value(void **state)
     expect_cons_show("Usage: some usage");
     expect_cons_show("");
 
-    gboolean result = cmd_connect(args, *help);
+    gboolean result = cmd_connect(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -126,7 +126,7 @@ void cmd_connect_shows_message_when_port_0(void **state)
     expect_cons_show("Value 0 out of range. Must be in 1..65535.");
     expect_cons_show("");
 
-    gboolean result = cmd_connect(args, *help);
+    gboolean result = cmd_connect(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -142,7 +142,7 @@ void cmd_connect_shows_message_when_port_minus1(void **state)
     expect_cons_show("Value -1 out of range. Must be in 1..65535.");
     expect_cons_show("");
 
-    gboolean result = cmd_connect(args, *help);
+    gboolean result = cmd_connect(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -158,7 +158,7 @@ void cmd_connect_shows_message_when_port_65536(void **state)
     expect_cons_show("Value 65536 out of range. Must be in 1..65535.");
     expect_cons_show("");
 
-    gboolean result = cmd_connect(args, *help);
+    gboolean result = cmd_connect(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -174,7 +174,7 @@ void cmd_connect_shows_message_when_port_contains_chars(void **state)
     expect_cons_show("Could not convert \"52f66\" to a number.");
     expect_cons_show("");
 
-    gboolean result = cmd_connect(args, *help);
+    gboolean result = cmd_connect(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -191,7 +191,7 @@ void cmd_connect_shows_usage_when_server_provided_twice(void **state)
     expect_cons_show("Usage: some usage");
     expect_cons_show("");
 
-    gboolean result = cmd_connect(args, *help);
+    gboolean result = cmd_connect(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -208,7 +208,7 @@ void cmd_connect_shows_usage_when_port_provided_twice(void **state)
     expect_cons_show("Usage: some usage");
     expect_cons_show("");
 
-    gboolean result = cmd_connect(args, *help);
+    gboolean result = cmd_connect(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -225,7 +225,7 @@ void cmd_connect_shows_usage_when_invalid_first_property(void **state)
     expect_cons_show("Usage: some usage");
     expect_cons_show("");
 
-    gboolean result = cmd_connect(args, *help);
+    gboolean result = cmd_connect(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -242,7 +242,7 @@ void cmd_connect_shows_usage_when_invalid_second_property(void **state)
     expect_cons_show("Usage: some usage");
     expect_cons_show("");
 
-    gboolean result = cmd_connect(args, *help);
+    gboolean result = cmd_connect(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -268,7 +268,7 @@ void cmd_connect_when_no_account(void **state)
     expect_value(jabber_connect_with_details, port, 0);
     will_return(jabber_connect_with_details, JABBER_CONNECTING);
 
-    gboolean result = cmd_connect(args, *help);
+    gboolean result = cmd_connect(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -294,7 +294,7 @@ void cmd_connect_with_server_when_provided(void **state)
     expect_value(jabber_connect_with_details, port, 0);
     will_return(jabber_connect_with_details, JABBER_CONNECTING);
 
-    gboolean result = cmd_connect(args, *help);
+    gboolean result = cmd_connect(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -320,7 +320,7 @@ void cmd_connect_with_port_when_provided(void **state)
     expect_value(jabber_connect_with_details, port, 5432);
     will_return(jabber_connect_with_details, JABBER_CONNECTING);
 
-    gboolean result = cmd_connect(args, *help);
+    gboolean result = cmd_connect(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -346,7 +346,7 @@ void cmd_connect_with_server_and_port_when_provided(void **state)
     expect_value(jabber_connect_with_details, port, 5432);
     will_return(jabber_connect_with_details, JABBER_CONNECTING);
 
-    gboolean result = cmd_connect(args, *help);
+    gboolean result = cmd_connect(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -374,7 +374,7 @@ void cmd_connect_fail_message(void **state)
 
     expect_cons_show_error("Connection attempt for user@server.org failed.");
 
-    gboolean result = cmd_connect(args, *help);
+    gboolean result = cmd_connect(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -400,7 +400,7 @@ void cmd_connect_lowercases_argument(void **state)
     expect_any(jabber_connect_with_details, port);
     will_return(jabber_connect_with_details, JABBER_CONNECTING);
 
-    gboolean result = cmd_connect(args, *help);
+    gboolean result = cmd_connect(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -411,7 +411,7 @@ void cmd_connect_asks_password_when_not_in_account(void **state)
     CommandHelp *help = malloc(sizeof(CommandHelp));
     gchar *args[] = { "jabber_org", NULL };
     ProfAccount *account = account_new("jabber_org", "me@jabber.org", NULL, NULL,
-        TRUE, NULL, 0, NULL, NULL, NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL);
+        TRUE, NULL, 0, NULL, NULL, NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
 
     will_return(jabber_get_connection_status, JABBER_DISCONNECTED);
 
@@ -425,7 +425,7 @@ void cmd_connect_asks_password_when_not_in_account(void **state)
     expect_any(jabber_connect_with_account, account);
     will_return(jabber_connect_with_account, JABBER_CONNECTING);
 
-    gboolean result = cmd_connect(args, *help);
+    gboolean result = cmd_connect(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -436,7 +436,7 @@ void cmd_connect_shows_message_when_connecting_with_account(void **state)
     CommandHelp *help = malloc(sizeof(CommandHelp));
     gchar *args[] = { "jabber_org", NULL };
     ProfAccount *account = account_new("jabber_org", "user@jabber.org", "password", NULL,
-        TRUE, NULL, 0, "laptop", NULL, NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL);
+        TRUE, NULL, 0, "laptop", NULL, NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
 
     will_return(jabber_get_connection_status, JABBER_DISCONNECTED);
 
@@ -448,7 +448,7 @@ void cmd_connect_shows_message_when_connecting_with_account(void **state)
     expect_any(jabber_connect_with_account, account);
     will_return(jabber_connect_with_account, JABBER_CONNECTING);
 
-    gboolean result = cmd_connect(args, *help);
+    gboolean result = cmd_connect(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -459,7 +459,7 @@ void cmd_connect_connects_with_account(void **state)
     CommandHelp *help = malloc(sizeof(CommandHelp));
     gchar *args[] = { "jabber_org", NULL };
     ProfAccount *account = account_new("jabber_org", "me@jabber.org", "password", NULL,
-        TRUE, NULL, 0, NULL, NULL, NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL);
+        TRUE, NULL, 0, NULL, NULL, NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
 
     will_return(jabber_get_connection_status, JABBER_DISCONNECTED);
 
@@ -471,7 +471,7 @@ void cmd_connect_connects_with_account(void **state)
     expect_memory(jabber_connect_with_account, account, account, sizeof(account));
     will_return(jabber_connect_with_account, JABBER_CONNECTING);
 
-    gboolean result = cmd_connect(args, *help);
+    gboolean result = cmd_connect(NULL, args, *help);
     assert_true(result);
 
     free(help);
diff --git a/tests/test_cmd_connect.h b/tests/unittests/test_cmd_connect.h
index ad27a0a5..ad27a0a5 100644
--- a/tests/test_cmd_connect.h
+++ b/tests/unittests/test_cmd_connect.h
diff --git a/tests/test_cmd_disconnect.c b/tests/unittests/test_cmd_disconnect.c
index 68253820..957516c2 100644
--- a/tests/test_cmd_disconnect.c
+++ b/tests/unittests/test_cmd_disconnect.c
@@ -25,7 +25,7 @@ void clears_chat_sessions(void **state)
     will_return(jabber_get_fulljid, "myjid@myserver.com");
     expect_any_cons_show();
 
-    gboolean result = cmd_disconnect(NULL, *help);
+    gboolean result = cmd_disconnect(NULL, NULL, *help);
 
     assert_true(result);
 
@@ -34,4 +34,4 @@ void clears_chat_sessions(void **state)
     assert_null(session1);
     assert_null(session2);
     free(help);
-}
\ No newline at end of file
+}
diff --git a/tests/test_cmd_disconnect.h b/tests/unittests/test_cmd_disconnect.h
index 856b501e..856b501e 100644
--- a/tests/test_cmd_disconnect.h
+++ b/tests/unittests/test_cmd_disconnect.h
diff --git a/tests/test_cmd_join.c b/tests/unittests/test_cmd_join.c
index 19824b3a..cdb275d9 100644
--- a/tests/test_cmd_join.c
+++ b/tests/unittests/test_cmd_join.c
@@ -24,7 +24,7 @@ static void test_with_connection_status(jabber_conn_status_t status)
 
     expect_cons_show("You are not currently connected.");
 
-    gboolean result = cmd_join(NULL, *help);
+    gboolean result = cmd_join(NULL, NULL, *help);
     assert_true(result);
 
     free(help);
@@ -60,7 +60,7 @@ void cmd_join_shows_error_message_when_invalid_room_jid(void **state)
     expect_cons_show_error("Specified room has incorrect format.");
     expect_cons_show("");
 
-    gboolean result = cmd_join(args, *help);
+    gboolean result = cmd_join(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -76,7 +76,7 @@ void cmd_join_uses_account_mucservice_when_no_service_specified(void **state)
     CommandHelp *help = malloc(sizeof(CommandHelp));
     gchar *args[] = { room, "nick", nick, NULL };
     ProfAccount *account = account_new(account_name, "user@server.org", NULL, NULL,
-        TRUE, NULL, 0, "laptop", NULL, NULL, 0, 0, 0, 0, 0, account_service, NULL, NULL, NULL, NULL, NULL);
+        TRUE, NULL, 0, "laptop", NULL, NULL, 0, 0, 0, 0, 0, account_service, NULL, NULL, NULL, NULL, NULL, NULL);
 
     muc_init();
 
@@ -90,7 +90,7 @@ void cmd_join_uses_account_mucservice_when_no_service_specified(void **state)
     expect_string(presence_join_room, nick, nick);
     expect_value(presence_join_room, passwd, NULL);
 
-    gboolean result = cmd_join(args, *help);
+    gboolean result = cmd_join(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -104,7 +104,7 @@ void cmd_join_uses_supplied_nick(void **state)
     CommandHelp *help = malloc(sizeof(CommandHelp));
     gchar *args[] = { room, "nick", nick, NULL };
     ProfAccount *account = account_new(account_name, "user@server.org", NULL, NULL,
-        TRUE, NULL, 0, "laptop", NULL, NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL);
+        TRUE, NULL, 0, "laptop", NULL, NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
 
     muc_init();
 
@@ -118,7 +118,7 @@ void cmd_join_uses_supplied_nick(void **state)
     expect_string(presence_join_room, nick, nick);
     expect_value(presence_join_room, passwd, NULL);
 
-    gboolean result = cmd_join(args, *help);
+    gboolean result = cmd_join(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -132,7 +132,7 @@ void cmd_join_uses_account_nick_when_not_supplied(void **state)
     CommandHelp *help = malloc(sizeof(CommandHelp));
     gchar *args[] = { room, NULL };
     ProfAccount *account = account_new(account_name, "user@server.org", NULL, NULL,
-        TRUE, NULL, 0, "laptop", NULL, NULL, 0, 0, 0, 0, 0, NULL, account_nick, NULL, NULL, NULL, NULL);
+        TRUE, NULL, 0, "laptop", NULL, NULL, 0, 0, 0, 0, 0, NULL, account_nick, NULL, NULL, NULL, NULL, NULL);
 
     muc_init();
 
@@ -146,7 +146,7 @@ void cmd_join_uses_account_nick_when_not_supplied(void **state)
     expect_string(presence_join_room, nick, account_nick);
     expect_value(presence_join_room, passwd, NULL);
 
-    gboolean result = cmd_join(args, *help);
+    gboolean result = cmd_join(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -163,7 +163,7 @@ void cmd_join_uses_password_when_supplied(void **state)
     CommandHelp *help = malloc(sizeof(CommandHelp));
     gchar *args[] = { room, "password", password, NULL };
     ProfAccount *account = account_new(account_name, "user@server.org", NULL, NULL,
-        TRUE, NULL, 0, "laptop", NULL, NULL, 0, 0, 0, 0, 0, account_service, account_nick, NULL, NULL, NULL, NULL);
+        TRUE, NULL, 0, "laptop", NULL, NULL, 0, 0, 0, 0, 0, account_service, account_nick, NULL, NULL, NULL, NULL, NULL);
 
     muc_init();
 
@@ -177,7 +177,7 @@ void cmd_join_uses_password_when_supplied(void **state)
     expect_string(presence_join_room, nick, account_nick);
     expect_value(presence_join_room, passwd, password);
 
-    gboolean result = cmd_join(args, *help);
+    gboolean result = cmd_join(NULL, args, *help);
     assert_true(result);
 
     free(help);
diff --git a/tests/test_cmd_join.h b/tests/unittests/test_cmd_join.h
index a96fa435..a96fa435 100644
--- a/tests/test_cmd_join.h
+++ b/tests/unittests/test_cmd_join.h
diff --git a/tests/test_cmd_otr.c b/tests/unittests/test_cmd_otr.c
index dae17947..7d4a3291 100644
--- a/tests/test_cmd_otr.c
+++ b/tests/unittests/test_cmd_otr.c
@@ -17,6 +17,7 @@
 
 #include "command/command.h"
 #include "command/commands.h"
+#include "window_list.h"
 
 #include "ui/ui.h"
 #include "ui/stub_ui.h"
@@ -30,7 +31,7 @@ void cmd_otr_shows_usage_when_no_args(void **state)
 
     expect_cons_show("Usage: Some usage");
 
-    gboolean result = cmd_otr(args, *help);
+    gboolean result = cmd_otr(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -46,7 +47,7 @@ void cmd_otr_shows_usage_when_invalid_subcommand(void **state)
 
     expect_cons_show("Usage: Some usage");
 
-    gboolean result = cmd_otr(args, *help);
+    gboolean result = cmd_otr(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -60,7 +61,7 @@ void cmd_otr_log_shows_usage_when_no_args(void **state)
 
     expect_cons_show("Usage: Some usage");
 
-    gboolean result = cmd_otr(args, *help);
+    gboolean result = cmd_otr(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -74,7 +75,7 @@ void cmd_otr_log_shows_usage_when_invalid_subcommand(void **state)
 
     expect_cons_show("Usage: Some usage");
 
-    gboolean result = cmd_otr(args, *help);
+    gboolean result = cmd_otr(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -89,7 +90,7 @@ void cmd_otr_log_on_enables_logging(void **state)
 
     expect_cons_show("OTR messages will be logged as plaintext.");
 
-    gboolean result = cmd_otr(args, *help);
+    gboolean result = cmd_otr(NULL, args, *help);
     char *pref_otr_log = prefs_get_string(PREF_OTR_LOG);
 
     assert_true(result);
@@ -108,7 +109,7 @@ void cmd_otr_log_on_shows_warning_when_chlog_disabled(void **state)
     expect_cons_show("OTR messages will be logged as plaintext.");
     expect_cons_show("Chat logging is currently disabled, use '/chlog on' to enable.");
 
-    gboolean result = cmd_otr(args, *help);
+    gboolean result = cmd_otr(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -123,7 +124,7 @@ void cmd_otr_log_off_disables_logging(void **state)
 
     expect_cons_show("OTR message logging disabled.");
 
-    gboolean result = cmd_otr(args, *help);
+    gboolean result = cmd_otr(NULL, args, *help);
     char *pref_otr_log = prefs_get_string(PREF_OTR_LOG);
 
     assert_true(result);
@@ -141,7 +142,7 @@ void cmd_otr_redact_redacts_logging(void **state)
 
     expect_cons_show("OTR messages will be logged as '[redacted]'.");
 
-    gboolean result = cmd_otr(args, *help);
+    gboolean result = cmd_otr(NULL, args, *help);
     char *pref_otr_log = prefs_get_string(PREF_OTR_LOG);
 
     assert_true(result);
@@ -160,74 +161,12 @@ void cmd_otr_log_redact_shows_warning_when_chlog_disabled(void **state)
     expect_cons_show("OTR messages will be logged as '[redacted]'.");
     expect_cons_show("Chat logging is currently disabled, use '/chlog on' to enable.");
 
-    gboolean result = cmd_otr(args, *help);
+    gboolean result = cmd_otr(NULL, args, *help);
     assert_true(result);
 
     free(help);
 }
 
-void cmd_otr_warn_shows_usage_when_no_args(void **state)
-{
-    CommandHelp *help = malloc(sizeof(CommandHelp));
-    help->usage = "Some usage";
-    gchar *args[] = { "warn", NULL };
-
-    expect_cons_show("Usage: Some usage");
-
-    gboolean result = cmd_otr(args, *help);
-    assert_true(result);
-
-    free(help);
-}
-
-void cmd_otr_warn_shows_usage_when_invalid_arg(void **state)
-{
-    CommandHelp *help = malloc(sizeof(CommandHelp));
-    help->usage = "Some usage";
-    gchar *args[] = { "warn", "badarg", NULL };
-
-    expect_cons_show("Usage: Some usage");
-
-    gboolean result = cmd_otr(args, *help);
-    assert_true(result);
-
-    free(help);
-}
-
-void cmd_otr_warn_on_enables_unencrypted_warning(void **state)
-{
-    CommandHelp *help = malloc(sizeof(CommandHelp));
-    gchar *args[] = { "warn", "on", NULL };
-    prefs_set_boolean(PREF_OTR_WARN, FALSE);
-
-    expect_cons_show("OTR warning message enabled.");
-
-    gboolean result = cmd_otr(args, *help);
-    gboolean otr_warn_enabled = prefs_get_boolean(PREF_OTR_WARN);
-
-    assert_true(result);
-    assert_true(otr_warn_enabled);
-
-    free(help);
-}
-
-void cmd_otr_warn_off_disables_unencrypted_warning(void **state)
-{
-    CommandHelp *help = malloc(sizeof(CommandHelp));
-    gchar *args[] = { "warn", "off", NULL };
-    prefs_set_boolean(PREF_OTR_WARN, TRUE);
-
-    expect_cons_show("OTR warning message disabled.");
-
-    gboolean result = cmd_otr(args, *help);
-    gboolean otr_warn_enabled = prefs_get_boolean(PREF_OTR_WARN);
-
-    assert_true(result);
-    assert_false(otr_warn_enabled);
-
-    free(help);
-}
-
 void cmd_otr_libver_shows_libotr_version(void **state)
 {
     CommandHelp *help = malloc(sizeof(CommandHelp));
@@ -240,7 +179,7 @@ void cmd_otr_libver_shows_libotr_version(void **state)
 
     expect_cons_show(message->str);
 
-    gboolean result = cmd_otr(args, *help);
+    gboolean result = cmd_otr(NULL, args, *help);
     assert_true(result);
 
     g_string_free(message, TRUE);
@@ -256,7 +195,7 @@ void cmd_otr_gen_shows_message_when_not_connected(void **state)
 
     expect_cons_show("You must be connected with an account to load OTR information.");
 
-    gboolean result = cmd_otr(args, *help);
+    gboolean result = cmd_otr(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -271,7 +210,7 @@ static void test_with_command_and_connection_status(char *command, jabber_conn_s
 
     expect_cons_show("You must be connected with an account to load OTR information.");
 
-    gboolean result = cmd_otr(args, *help);
+    gboolean result = cmd_otr(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -308,7 +247,7 @@ void cmd_otr_gen_generates_key_for_connected_account(void **state)
     gchar *args[] = { "gen", NULL };
     char *account_name = "myaccount";
     ProfAccount *account = account_new(account_name, "me@jabber.org", NULL, NULL,
-        TRUE, NULL, 0, NULL, NULL, NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL);
+        TRUE, NULL, 0, NULL, NULL, NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
 
     will_return(jabber_get_connection_status, JABBER_CONNECTED);
     will_return(jabber_get_account_name, account_name);
@@ -319,7 +258,7 @@ void cmd_otr_gen_generates_key_for_connected_account(void **state)
 
     expect_memory(otr_keygen, account, account, sizeof(ProfAccount));
 
-    gboolean result = cmd_otr(args, *help);
+    gboolean result = cmd_otr(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -360,7 +299,7 @@ void cmd_otr_myfp_shows_message_when_no_key(void **state)
 
     expect_ui_current_print_formatted_line('!', 0, "You have not generated or loaded a private key, use '/otr gen'");
 
-    gboolean result = cmd_otr(args, *help);
+    gboolean result = cmd_otr(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -380,7 +319,7 @@ void cmd_otr_myfp_shows_my_fingerprint(void **state)
 
     expect_ui_current_print_formatted_line('!', 0, message->str);
 
-    gboolean result = cmd_otr(args, *help);
+    gboolean result = cmd_otr(NULL, args, *help);
     assert_true(result);
 
     g_string_free(message, TRUE);
@@ -392,13 +331,14 @@ test_cmd_otr_theirfp_from_wintype(win_type_t wintype)
 {
     CommandHelp *help = malloc(sizeof(CommandHelp));
     gchar *args[] = { "theirfp", NULL };
+    ProfWin window;
+    window.type = wintype;
 
     will_return(jabber_get_connection_status, JABBER_CONNECTED);
-    will_return(ui_current_win_type, wintype);
 
     expect_ui_current_print_line("You must be in a regular chat window to view a recipient's fingerprint.");
 
-    gboolean result = cmd_otr(args, *help);
+    gboolean result = cmd_otr(&window, args, *help);
     assert_true(result);
 
     free(help);
@@ -424,13 +364,17 @@ void cmd_otr_theirfp_shows_message_when_non_otr_chat_window(void **state)
     CommandHelp *help = malloc(sizeof(CommandHelp));
     gchar *args[] = { "theirfp", NULL };
 
+    ProfWin window;
+    window.type = WIN_CHAT;
+    ProfChatWin chatwin;
+    chatwin.window = window;
+    chatwin.memcheck = PROFCHATWIN_MEMCHECK;
+
     will_return(jabber_get_connection_status, JABBER_CONNECTED);
-    will_return(ui_current_win_type, WIN_CHAT);
-    will_return(ui_current_win_is_otr, FALSE);
 
     expect_ui_current_print_formatted_line('!', 0, "You are not currently in an OTR session.");
 
-    gboolean result = cmd_otr(args, *help);
+    gboolean result = cmd_otr((ProfWin*)&chatwin, args, *help);
     assert_true(result);
 
     free(help);
@@ -442,29 +386,30 @@ void cmd_otr_theirfp_shows_fingerprint(void **state)
     char *fingerprint = "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD EEEEEEEE";
     CommandHelp *help = malloc(sizeof(CommandHelp));
     gchar *args[] = { "theirfp", NULL };
-    ProfChatWin *chatwin = malloc(sizeof(ProfChatWin));
-    chatwin->barejid = strdup(recipient);
-    GString *message = g_string_new(chatwin->barejid);
+    GString *message = g_string_new(recipient);
     g_string_append(message, "'s OTR fingerprint: ");
     g_string_append(message, fingerprint);
 
+    ProfWin window;
+    window.type = WIN_CHAT;
+    ProfChatWin chatwin;
+    chatwin.window = window;
+    chatwin.barejid = recipient;
+    chatwin.memcheck = PROFCHATWIN_MEMCHECK;
+    chatwin.enc_mode = PROF_ENC_OTR;
+
     will_return(jabber_get_connection_status, JABBER_CONNECTED);
-    will_return(ui_current_win_type, WIN_CHAT);
-    will_return(ui_get_current_chat, chatwin);
-    will_return(ui_current_win_is_otr, TRUE);
 
-    expect_string(otr_get_their_fingerprint, recipient, chatwin->barejid);
+    expect_string(otr_get_their_fingerprint, recipient, recipient);
     will_return(otr_get_their_fingerprint, strdup(fingerprint));
 
     expect_ui_current_print_formatted_line('!', 0, message->str);
 
-    gboolean result = cmd_otr(args, *help);
+    gboolean result = cmd_otr((ProfWin*)&chatwin, args, *help);
     assert_true(result);
 
     g_string_free(message, TRUE);
     free(help);
-    free(chatwin->barejid);
-    free(chatwin);
 }
 
 static void
@@ -472,13 +417,14 @@ test_cmd_otr_start_from_wintype(win_type_t wintype)
 {
     CommandHelp *help = malloc(sizeof(CommandHelp));
     gchar *args[] = { "start", NULL };
+    ProfWin window;
+    window.type = wintype;
 
     will_return(jabber_get_connection_status, JABBER_CONNECTED);
-    will_return(ui_current_win_type, wintype);
 
     expect_ui_current_print_line("You must be in a regular chat window to start an OTR session.");
 
-    gboolean result = cmd_otr(args, *help);
+    gboolean result = cmd_otr(&window, args, *help);
     assert_true(result);
 
     free(help);
@@ -501,16 +447,23 @@ void cmd_otr_start_shows_message_when_in_private(void **state)
 
 void cmd_otr_start_shows_message_when_already_started(void **state)
 {
+    char *recipient = "someone@server.org";
     CommandHelp *help = malloc(sizeof(CommandHelp));
     gchar *args[] = { "start", NULL };
 
     will_return(jabber_get_connection_status, JABBER_CONNECTED);
-    will_return(ui_current_win_type, WIN_CHAT);
-    will_return(ui_current_win_is_otr, TRUE);
+
+    ProfWin window;
+    window.type = WIN_CHAT;
+    ProfChatWin chatwin;
+    chatwin.window = window;
+    chatwin.barejid = recipient;
+    chatwin.memcheck = PROFCHATWIN_MEMCHECK;
+    chatwin.enc_mode = PROF_ENC_OTR;
 
     expect_ui_current_print_formatted_line('!', 0, "You are already in an OTR session.");
 
-    gboolean result = cmd_otr(args, *help);
+    gboolean result = cmd_otr((ProfWin*)&chatwin, args, *help);
     assert_true(result);
 
     free(help);
@@ -518,17 +471,24 @@ void cmd_otr_start_shows_message_when_already_started(void **state)
 
 void cmd_otr_start_shows_message_when_no_key(void **state)
 {
+    char *recipient = "someone@server.org";
     CommandHelp *help = malloc(sizeof(CommandHelp));
     gchar *args[] = { "start", NULL };
 
     will_return(jabber_get_connection_status, JABBER_CONNECTED);
-    will_return(ui_current_win_type, WIN_CHAT);
-    will_return(ui_current_win_is_otr, FALSE);
     will_return(otr_key_loaded, FALSE);
 
+    ProfWin window;
+    window.type = WIN_CHAT;
+    ProfChatWin chatwin;
+    chatwin.window = window;
+    chatwin.barejid = recipient;
+    chatwin.memcheck = PROFCHATWIN_MEMCHECK;
+    chatwin.enc_mode = PROF_ENC_NONE;
+
     expect_ui_current_print_formatted_line('!', 0, "You have not generated or loaded a private key, use '/otr gen'");
 
-    gboolean result = cmd_otr(args, *help);
+    gboolean result = cmd_otr((ProfWin*)&chatwin, args, *help);
     assert_true(result);
 
     free(help);
@@ -538,28 +498,29 @@ void
 cmd_otr_start_sends_otr_query_message_to_current_recipeint(void **state)
 {
     char *recipient = "buddy@chat.com";
-    ProfChatWin *chatwin = malloc(sizeof(ProfChatWin));
-    chatwin->barejid = strdup(recipient);
     char *query_message = "?OTR?";
     CommandHelp *help = malloc(sizeof(CommandHelp));
     gchar *args[] = { "start", NULL };
 
+    ProfWin window;
+    window.type = WIN_CHAT;
+    ProfChatWin chatwin;
+    chatwin.window = window;
+    chatwin.barejid = recipient;
+    chatwin.memcheck = PROFCHATWIN_MEMCHECK;
+    chatwin.enc_mode = PROF_ENC_NONE;
+
     will_return(jabber_get_connection_status, JABBER_CONNECTED);
-    will_return(ui_current_win_type, WIN_CHAT);
-    will_return(ui_get_current_chat, chatwin);
-    will_return(ui_current_win_is_otr, FALSE);
     will_return(otr_key_loaded, TRUE);
     will_return(otr_start_query, query_message);
 
-    expect_string(message_send_chat_encrypted, barejid, chatwin->barejid);
-    expect_string(message_send_chat_encrypted, msg, query_message);
+    expect_string(message_send_chat_otr, barejid, recipient);
+    expect_string(message_send_chat_otr, msg, query_message);
 
-    gboolean result = cmd_otr(args, *help);
+    gboolean result = cmd_otr((ProfWin*)&chatwin, args, *help);
     assert_true(result);
 
     free(help);
-    free(chatwin->barejid);
-    free(chatwin);
 }
 
 #else
@@ -570,7 +531,7 @@ void cmd_otr_shows_message_when_otr_unsupported(void **state)
 
     expect_cons_show("This version of Profanity has not been built with OTR support enabled");
 
-    gboolean result = cmd_otr(args, *help);
+    gboolean result = cmd_otr(NULL, args, *help);
     assert_true(result);
 
     free(help);
diff --git a/tests/test_cmd_otr.h b/tests/unittests/test_cmd_otr.h
index 8ef182e9..469d7c54 100644
--- a/tests/test_cmd_otr.h
+++ b/tests/unittests/test_cmd_otr.h
@@ -10,10 +10,6 @@ void cmd_otr_log_off_disables_logging(void **state);
 void cmd_otr_redact_redacts_logging(void **state);
 void cmd_otr_log_on_shows_warning_when_chlog_disabled(void **state);
 void cmd_otr_log_redact_shows_warning_when_chlog_disabled(void **state);
-void cmd_otr_warn_shows_usage_when_no_args(void **state);
-void cmd_otr_warn_shows_usage_when_invalid_arg(void **state);
-void cmd_otr_warn_on_enables_unencrypted_warning(void **state);
-void cmd_otr_warn_off_disables_unencrypted_warning(void **state);
 void cmd_otr_libver_shows_libotr_version(void **state);
 void cmd_otr_gen_shows_message_when_not_connected(void **state);
 void cmd_otr_gen_generates_key_for_connected_account(void **state);
diff --git a/tests/unittests/test_cmd_pgp.c b/tests/unittests/test_cmd_pgp.c
new file mode 100644
index 00000000..aef4baf7
--- /dev/null
+++ b/tests/unittests/test_cmd_pgp.c
@@ -0,0 +1,129 @@
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+
+#include "config.h"
+
+#include "command/commands.h"
+
+#include "ui/stub_ui.h"
+
+#ifdef HAVE_LIBGPGME
+void cmd_pgp_shows_usage_when_no_args(void **state)
+{
+    CommandHelp *help = malloc(sizeof(CommandHelp));
+    help->usage = "Some usage";
+    gchar *args[] = { NULL };
+
+    expect_cons_show("Usage: Some usage");
+
+    gboolean result = cmd_pgp(NULL, args, *help);
+    assert_true(result);
+
+    free(help);
+}
+
+void cmd_pgp_start_shows_message_when_connection(jabber_conn_status_t conn_status)
+{
+    CommandHelp *help = malloc(sizeof(CommandHelp));
+    help->usage = "Some usage";
+    gchar *args[] = { "start", NULL };
+    ProfWin window;
+    window.type = WIN_CHAT;
+
+    will_return(jabber_get_connection_status, conn_status);
+
+    expect_cons_show("You must be connected to start PGP encrpytion.");
+
+    gboolean result = cmd_pgp(&window, args, *help);
+    assert_true(result);
+
+    free(help);
+}
+
+void cmd_pgp_start_shows_message_when_disconnected(void **state)
+{
+    cmd_pgp_start_shows_message_when_connection(JABBER_DISCONNECTED);
+}
+
+void cmd_pgp_start_shows_message_when_disconnecting(void **state)
+{
+    cmd_pgp_start_shows_message_when_connection(JABBER_DISCONNECTING);
+}
+
+void cmd_pgp_start_shows_message_when_connecting(void **state)
+{
+    cmd_pgp_start_shows_message_when_connection(JABBER_CONNECTING);
+}
+
+void cmd_pgp_start_shows_message_when_undefined(void **state)
+{
+    cmd_pgp_start_shows_message_when_connection(JABBER_UNDEFINED);
+}
+
+void cmd_pgp_start_shows_message_when_started(void **state)
+{
+    cmd_pgp_start_shows_message_when_connection(JABBER_STARTED);
+}
+
+void cmd_pgp_start_shows_message_when_no_arg_in_wintype(win_type_t wintype)
+{
+    CommandHelp *help = malloc(sizeof(CommandHelp));
+    help->usage = "Some usage";
+    gchar *args[] = { "start", NULL };
+    ProfWin window;
+    window.type = wintype;
+
+    will_return(jabber_get_connection_status, JABBER_CONNECTED);
+
+    expect_cons_show("You must be in a regular chat window to start PGP encrpytion.");
+
+    gboolean result = cmd_pgp(&window, args, *help);
+    assert_true(result);
+
+    free(help);
+}
+
+void cmd_pgp_start_shows_message_when_no_arg_in_console(void **state)
+{
+    cmd_pgp_start_shows_message_when_no_arg_in_wintype(WIN_CONSOLE);
+}
+
+void cmd_pgp_start_shows_message_when_no_arg_in_muc(void **state)
+{
+    cmd_pgp_start_shows_message_when_no_arg_in_wintype(WIN_MUC);
+}
+
+void cmd_pgp_start_shows_message_when_no_arg_in_mucconf(void **state)
+{
+    cmd_pgp_start_shows_message_when_no_arg_in_wintype(WIN_MUC_CONFIG);
+}
+
+void cmd_pgp_start_shows_message_when_no_arg_in_private(void **state)
+{
+    cmd_pgp_start_shows_message_when_no_arg_in_wintype(WIN_PRIVATE);
+}
+
+void cmd_pgp_start_shows_message_when_no_arg_in_xmlconsole(void **state)
+{
+    cmd_pgp_start_shows_message_when_no_arg_in_wintype(WIN_XML);
+}
+
+#else
+void cmd_pgp_shows_message_when_pgp_unsupported(void **state)
+{
+    CommandHelp *help = malloc(sizeof(CommandHelp));
+    gchar *args[] = { "gen", NULL };
+
+    expect_cons_show("This version of Profanity has not been built with PGP support enabled");
+
+    gboolean result = cmd_pgp(NULL, args, *help);
+    assert_true(result);
+
+    free(help);
+}
+#endif
diff --git a/tests/unittests/test_cmd_pgp.h b/tests/unittests/test_cmd_pgp.h
new file mode 100644
index 00000000..fcb24500
--- /dev/null
+++ b/tests/unittests/test_cmd_pgp.h
@@ -0,0 +1,17 @@
+#include "config.h"
+
+#ifdef HAVE_LIBGPGME
+void cmd_pgp_shows_usage_when_no_args(void **state);
+void cmd_pgp_start_shows_message_when_disconnected(void **state);
+void cmd_pgp_start_shows_message_when_disconnecting(void **state);
+void cmd_pgp_start_shows_message_when_connecting(void **state);
+void cmd_pgp_start_shows_message_when_undefined(void **state);
+void cmd_pgp_start_shows_message_when_started(void **state);
+void cmd_pgp_start_shows_message_when_no_arg_in_console(void **state);
+void cmd_pgp_start_shows_message_when_no_arg_in_muc(void **state);
+void cmd_pgp_start_shows_message_when_no_arg_in_mucconf(void **state);
+void cmd_pgp_start_shows_message_when_no_arg_in_private(void **state);
+void cmd_pgp_start_shows_message_when_no_arg_in_xmlconsole(void **state);
+#else
+void cmd_pgp_shows_message_when_pgp_unsupported(void **state);
+#endif
diff --git a/tests/test_cmd_rooms.c b/tests/unittests/test_cmd_rooms.c
index 5114bfbf..7a336561 100644
--- a/tests/test_cmd_rooms.c
+++ b/tests/unittests/test_cmd_rooms.c
@@ -22,7 +22,7 @@ static void test_with_connection_status(jabber_conn_status_t status)
 
     expect_cons_show("You are not currently connected.");
 
-    gboolean result = cmd_rooms(NULL, *help);
+    gboolean result = cmd_rooms(NULL, NULL, *help);
     assert_true(result);
 
     free(help);
@@ -80,7 +80,7 @@ void cmd_rooms_uses_account_default_when_no_arg(void **state)
 
     expect_string(iq_room_list_request, conferencejid, "default_conf_server");
 
-    gboolean result = cmd_rooms(args, *help);
+    gboolean result = cmd_rooms(NULL, args, *help);
 
     assert_true(result);
 
@@ -96,7 +96,7 @@ void cmd_rooms_arg_used_when_passed(void **state)
 
     expect_string(iq_room_list_request, conferencejid, "conf_server_arg");
 
-    gboolean result = cmd_rooms(args, *help);
+    gboolean result = cmd_rooms(NULL, args, *help);
 
     assert_true(result);
 
diff --git a/tests/test_cmd_rooms.h b/tests/unittests/test_cmd_rooms.h
index a9a7af83..a9a7af83 100644
--- a/tests/test_cmd_rooms.h
+++ b/tests/unittests/test_cmd_rooms.h
diff --git a/tests/test_cmd_roster.c b/tests/unittests/test_cmd_roster.c
index a7160cf5..0fd1647a 100644
--- a/tests/test_cmd_roster.c
+++ b/tests/unittests/test_cmd_roster.c
@@ -16,12 +16,13 @@
 static void test_with_connection_status(jabber_conn_status_t status)
 {
     CommandHelp *help = malloc(sizeof(CommandHelp));
+    gchar *args[] = { NULL };
 
     will_return(jabber_get_connection_status, status);
 
     expect_cons_show("You are not currently connected.");
 
-    gboolean result = cmd_roster(NULL, *help);
+    gboolean result = cmd_roster(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -60,7 +61,7 @@ void cmd_roster_shows_roster_when_no_args(void **state)
 
     expect_memory(cons_show_roster, list, roster, sizeof(roster));
 
-    gboolean result = cmd_roster(args, *help);
+    gboolean result = cmd_roster(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -77,7 +78,7 @@ void cmd_roster_add_shows_message_when_no_jid(void **state)
 
     expect_cons_show("Usage: some usage");
 
-    gboolean result = cmd_roster(args, *help);
+    gboolean result = cmd_roster(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -95,7 +96,7 @@ void cmd_roster_add_sends_roster_add_request(void **state)
     expect_string(roster_send_add_new, barejid, jid);
     expect_string(roster_send_add_new, name, nick);
 
-    gboolean result = cmd_roster(args, *help);
+    gboolean result = cmd_roster(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -111,7 +112,7 @@ void cmd_roster_remove_shows_message_when_no_jid(void **state)
 
     expect_cons_show("Usage: some usage");
 
-    gboolean result = cmd_roster(args, *help);
+    gboolean result = cmd_roster(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -127,7 +128,7 @@ void cmd_roster_remove_sends_roster_remove_request(void **state)
 
     expect_string(roster_send_remove, barejid, jid);
 
-    gboolean result = cmd_roster(args, *help);
+    gboolean result = cmd_roster(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -143,7 +144,7 @@ void cmd_roster_nick_shows_message_when_no_jid(void **state)
 
     expect_cons_show("Usage: some usage");
 
-    gboolean result = cmd_roster(args, *help);
+    gboolean result = cmd_roster(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -159,7 +160,7 @@ void cmd_roster_nick_shows_message_when_no_nick(void **state)
 
     expect_cons_show("Usage: some usage");
 
-    gboolean result = cmd_roster(args, *help);
+    gboolean result = cmd_roster(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -177,7 +178,7 @@ void cmd_roster_nick_shows_message_when_no_contact_exists(void **state)
 
     expect_cons_show("Contact not found in roster: bob@server.org");
 
-    gboolean result = cmd_roster(args, *help);
+    gboolean result = cmd_roster(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -204,7 +205,7 @@ void cmd_roster_nick_sends_name_change_request(void **state)
 
     expect_cons_show("Nickname for bob@server.org set to: bobster.");
 
-    gboolean result = cmd_roster(args, *help);
+    gboolean result = cmd_roster(NULL, args, *help);
     assert_true(result);
 
     PContact contact = roster_get_contact(jid);
@@ -224,7 +225,7 @@ void cmd_roster_clearnick_shows_message_when_no_jid(void **state)
 
     expect_cons_show("Usage: some usage");
 
-    gboolean result = cmd_roster(args, *help);
+    gboolean result = cmd_roster(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -242,7 +243,7 @@ void cmd_roster_clearnick_shows_message_when_no_contact_exists(void **state)
 
     expect_cons_show("Contact not found in roster: bob@server.org");
 
-    gboolean result = cmd_roster(args, *help);
+    gboolean result = cmd_roster(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -268,7 +269,7 @@ void cmd_roster_clearnick_sends_name_change_request_with_empty_nick(void **state
 
     expect_cons_show("Nickname for bob@server.org removed.");
 
-    gboolean result = cmd_roster(args, *help);
+    gboolean result = cmd_roster(NULL, args, *help);
     assert_true(result);
 
     PContact contact = roster_get_contact(jid);
diff --git a/tests/test_cmd_roster.h b/tests/unittests/test_cmd_roster.h
index 79f69ec8..79f69ec8 100644
--- a/tests/test_cmd_roster.h
+++ b/tests/unittests/test_cmd_roster.h
diff --git a/tests/test_cmd_statuses.c b/tests/unittests/test_cmd_statuses.c
index ed37021a..0ecb22ae 100644
--- a/tests/test_cmd_statuses.c
+++ b/tests/unittests/test_cmd_statuses.c
@@ -21,7 +21,7 @@ void cmd_statuses_shows_usage_when_bad_subcmd(void **state)
 
     expect_cons_show("Usage: some usage");
 
-    gboolean result = cmd_statuses(args, *help);
+    gboolean result = cmd_statuses(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -35,7 +35,7 @@ void cmd_statuses_shows_usage_when_bad_console_setting(void **state)
 
     expect_cons_show("Usage: some usage");
 
-    gboolean result = cmd_statuses(args, *help);
+    gboolean result = cmd_statuses(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -49,7 +49,7 @@ void cmd_statuses_shows_usage_when_bad_chat_setting(void **state)
 
     expect_cons_show("Usage: some usage");
 
-    gboolean result = cmd_statuses(args, *help);
+    gboolean result = cmd_statuses(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -63,7 +63,7 @@ void cmd_statuses_shows_usage_when_bad_muc_setting(void **state)
 
     expect_cons_show("Usage: some usage");
 
-    gboolean result = cmd_statuses(args, *help);
+    gboolean result = cmd_statuses(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -76,7 +76,7 @@ void cmd_statuses_console_sets_all(void **state)
 
     expect_cons_show("All presence updates will appear in the console.");
 
-    gboolean result = cmd_statuses(args, *help);
+    gboolean result = cmd_statuses(NULL, args, *help);
 
     char *setting = prefs_get_string(PREF_STATUSES_CONSOLE);
     assert_non_null(setting);
@@ -93,7 +93,7 @@ void cmd_statuses_console_sets_online(void **state)
 
     expect_cons_show("Only online/offline presence updates will appear in the console.");
 
-    gboolean result = cmd_statuses(args, *help);
+    gboolean result = cmd_statuses(NULL, args, *help);
 
     char *setting = prefs_get_string(PREF_STATUSES_CONSOLE);
     assert_non_null(setting);
@@ -110,7 +110,7 @@ void cmd_statuses_console_sets_none(void **state)
 
     expect_cons_show("Presence updates will not appear in the console.");
 
-    gboolean result = cmd_statuses(args, *help);
+    gboolean result = cmd_statuses(NULL, args, *help);
 
     char *setting = prefs_get_string(PREF_STATUSES_CONSOLE);
     assert_non_null(setting);
@@ -127,7 +127,7 @@ void cmd_statuses_chat_sets_all(void **state)
 
     expect_cons_show("All presence updates will appear in chat windows.");
 
-    gboolean result = cmd_statuses(args, *help);
+    gboolean result = cmd_statuses(NULL, args, *help);
 
     char *setting = prefs_get_string(PREF_STATUSES_CHAT);
     assert_non_null(setting);
@@ -144,7 +144,7 @@ void cmd_statuses_chat_sets_online(void **state)
 
     expect_cons_show("Only online/offline presence updates will appear in chat windows.");
 
-    gboolean result = cmd_statuses(args, *help);
+    gboolean result = cmd_statuses(NULL, args, *help);
 
     char *setting = prefs_get_string(PREF_STATUSES_CHAT);
     assert_non_null(setting);
@@ -161,7 +161,7 @@ void cmd_statuses_chat_sets_none(void **state)
 
     expect_cons_show("Presence updates will not appear in chat windows.");
 
-    gboolean result = cmd_statuses(args, *help);
+    gboolean result = cmd_statuses(NULL, args, *help);
 
     char *setting = prefs_get_string(PREF_STATUSES_CHAT);
     assert_non_null(setting);
@@ -178,7 +178,7 @@ void cmd_statuses_muc_sets_all(void **state)
 
     expect_cons_show("All presence updates will appear in chat room windows.");
 
-    gboolean result = cmd_statuses(args, *help);
+    gboolean result = cmd_statuses(NULL, args, *help);
 
     char *setting = prefs_get_string(PREF_STATUSES_MUC);
     assert_non_null(setting);
@@ -195,7 +195,7 @@ void cmd_statuses_muc_sets_online(void **state)
 
     expect_cons_show("Only join/leave presence updates will appear in chat room windows.");
 
-    gboolean result = cmd_statuses(args, *help);
+    gboolean result = cmd_statuses(NULL, args, *help);
 
     char *setting = prefs_get_string(PREF_STATUSES_MUC);
     assert_non_null(setting);
@@ -212,7 +212,7 @@ void cmd_statuses_muc_sets_none(void **state)
 
     expect_cons_show("Presence updates will not appear in chat room windows.");
 
-    gboolean result = cmd_statuses(args, *help);
+    gboolean result = cmd_statuses(NULL, args, *help);
 
     char *setting = prefs_get_string(PREF_STATUSES_MUC);
     assert_non_null(setting);
diff --git a/tests/test_cmd_statuses.h b/tests/unittests/test_cmd_statuses.h
index 306a6fc7..306a6fc7 100644
--- a/tests/test_cmd_statuses.h
+++ b/tests/unittests/test_cmd_statuses.h
diff --git a/tests/test_cmd_sub.c b/tests/unittests/test_cmd_sub.c
index 80b85f15..55b9de60 100644
--- a/tests/test_cmd_sub.c
+++ b/tests/unittests/test_cmd_sub.c
@@ -22,7 +22,7 @@ void cmd_sub_shows_message_when_not_connected(void **state)
 
     expect_cons_show("You are currently not connected.");
 
-    gboolean result = cmd_sub(args, *help);
+    gboolean result = cmd_sub(NULL, args, *help);
     assert_true(result);
 
     free(help);
@@ -38,7 +38,7 @@ void cmd_sub_shows_usage_when_no_arg(void **state)
 
     expect_cons_show("Usage: Some usage");
 
-    gboolean result = cmd_sub(args, *help);
+    gboolean result = cmd_sub(NULL, args, *help);
     assert_true(result);
 
     free(help);
diff --git a/tests/test_cmd_sub.h b/tests/unittests/test_cmd_sub.h
index 6e8addd3..6e8addd3 100644
--- a/tests/test_cmd_sub.h
+++ b/tests/unittests/test_cmd_sub.h
diff --git a/tests/test_common.c b/tests/unittests/test_common.c
index 980f2198..980f2198 100644
--- a/tests/test_common.c
+++ b/tests/unittests/test_common.c
diff --git a/tests/test_common.h b/tests/unittests/test_common.h
index b4b98e5a..b4b98e5a 100644
--- a/tests/test_common.h
+++ b/tests/unittests/test_common.h
diff --git a/tests/test_contact.c b/tests/unittests/test_contact.c
index cad88907..cad88907 100644
--- a/tests/test_contact.c
+++ b/tests/unittests/test_contact.c
diff --git a/tests/test_contact.h b/tests/unittests/test_contact.h
index c9d8c1fd..c9d8c1fd 100644
--- a/tests/test_contact.h
+++ b/tests/unittests/test_contact.h
diff --git a/tests/test_form.c b/tests/unittests/test_form.c
index b3158a83..b3158a83 100644
--- a/tests/test_form.c
+++ b/tests/unittests/test_form.c
diff --git a/tests/test_form.h b/tests/unittests/test_form.h
index 65911d0a..65911d0a 100644
--- a/tests/test_form.h
+++ b/tests/unittests/test_form.h
diff --git a/tests/test_jid.c b/tests/unittests/test_jid.c
index ff5f4c9a..ff5f4c9a 100644
--- a/tests/test_jid.c
+++ b/tests/unittests/test_jid.c
diff --git a/tests/test_jid.h b/tests/unittests/test_jid.h
index 9b96d0b8..9b96d0b8 100644
--- a/tests/test_jid.h
+++ b/tests/unittests/test_jid.h
diff --git a/tests/test_keyhandlers.c b/tests/unittests/test_keyhandlers.c
index a6d39143..a6d39143 100644
--- a/tests/test_keyhandlers.c
+++ b/tests/unittests/test_keyhandlers.c
diff --git a/tests/test_keyhandlers.h b/tests/unittests/test_keyhandlers.h
index 4be429a9..4be429a9 100644
--- a/tests/test_keyhandlers.h
+++ b/tests/unittests/test_keyhandlers.h
diff --git a/tests/test_muc.c b/tests/unittests/test_muc.c
index e3b7f9b0..e3b7f9b0 100644
--- a/tests/test_muc.c
+++ b/tests/unittests/test_muc.c
diff --git a/tests/test_muc.h b/tests/unittests/test_muc.h
index 8df54a5d..8df54a5d 100644
--- a/tests/test_muc.h
+++ b/tests/unittests/test_muc.h
diff --git a/tests/test_parser.c b/tests/unittests/test_parser.c
index faefc9c7..faefc9c7 100644
--- a/tests/test_parser.c
+++ b/tests/unittests/test_parser.c
diff --git a/tests/test_parser.h b/tests/unittests/test_parser.h
index 51d768fe..51d768fe 100644
--- a/tests/test_parser.h
+++ b/tests/unittests/test_parser.h
diff --git a/tests/test_preferences.c b/tests/unittests/test_preferences.c
index c4bcbf77..c4bcbf77 100644
--- a/tests/test_preferences.c
+++ b/tests/unittests/test_preferences.c
diff --git a/tests/test_preferences.h b/tests/unittests/test_preferences.h
index 5bf79a6a..5bf79a6a 100644
--- a/tests/test_preferences.h
+++ b/tests/unittests/test_preferences.h
diff --git a/tests/test_roster_list.c b/tests/unittests/test_roster_list.c
index 41ccb8cf..41ccb8cf 100644
--- a/tests/test_roster_list.c
+++ b/tests/unittests/test_roster_list.c
diff --git a/tests/test_roster_list.h b/tests/unittests/test_roster_list.h
index 080bca9f..080bca9f 100644
--- a/tests/test_roster_list.h
+++ b/tests/unittests/test_roster_list.h
diff --git a/tests/test_server_events.c b/tests/unittests/test_server_events.c
index 58489807..fac1ac38 100644
--- a/tests/test_server_events.c
+++ b/tests/unittests/test_server_events.c
@@ -26,7 +26,7 @@ void console_shows_online_presence_when_set_online(void **state)
     expect_memory(ui_contact_online, resource, resource, sizeof(resource));
     expect_value(ui_contact_online, last_activity, NULL);
 
-    sv_ev_contact_online(barejid, resource, NULL);
+    sv_ev_contact_online(barejid, resource, NULL, NULL);
 
     roster_clear();
 }
@@ -43,7 +43,7 @@ void console_shows_online_presence_when_set_all(void **state)
     expect_memory(ui_contact_online, resource, resource, sizeof(resource));
     expect_value(ui_contact_online, last_activity, NULL);
 
-    sv_ev_contact_online(barejid, resource, NULL);
+    sv_ev_contact_online(barejid, resource, NULL, NULL);
 
     roster_clear();
 }
@@ -60,7 +60,7 @@ void console_shows_dnd_presence_when_set_all(void **state)
     expect_memory(ui_contact_online, resource, resource, sizeof(resource));
     expect_value(ui_contact_online, last_activity, NULL);
 
-    sv_ev_contact_online(barejid, resource, NULL);
+    sv_ev_contact_online(barejid, resource, NULL, NULL);
 
     roster_clear();
 }
diff --git a/tests/test_server_events.h b/tests/unittests/test_server_events.h
index 81a436f4..81a436f4 100644
--- a/tests/test_server_events.h
+++ b/tests/unittests/test_server_events.h
diff --git a/tests/ui/stub_ui.c b/tests/unittests/ui/stub_ui.c
index 52c38570..71a577f5 100644
--- a/tests/ui/stub_ui.c
+++ b/tests/unittests/ui/stub_ui.c
@@ -7,7 +7,7 @@
 #include "ui/window.h"
 #include "ui/ui.h"
 
-#include "tests/ui/stub_ui.h"
+#include "tests/unittests/ui/stub_ui.h"
 
 // mock state
 
@@ -121,21 +121,6 @@ int ui_close_read_wins(void)
 // current window actions
 void ui_clear_current(void) {}
 
-win_type_t ui_current_win_type(void)
-{
-    return (win_type_t)mock();
-}
-
-gboolean ui_current_win_is_otr(void)
-{
-    return (gboolean)mock();
-}
-
-ProfChatWin *ui_get_current_chat(void)
-{
-    return (ProfChatWin*)mock();
-}
-
 void ui_current_print_line(const char * const msg, ...)
 {
     va_list args;
@@ -176,6 +161,7 @@ void ui_page_up(void) {}
 void ui_page_down(void) {}
 void ui_subwin_page_up(void) {}
 void ui_subwin_page_down(void) {}
+void ui_clear_win(ProfWin *window) {}
 
 char * ui_ask_password(void)
 {
@@ -193,10 +179,10 @@ void ui_contact_online(char *barejid, Resource *resource, GDateTime *last_activi
 }
 
 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_msg(ProfChatWin *chatwin, const char * const resource, const char * const message, GDateTime *timestamp, gboolean win_created) {}
 void ui_message_receipt(const char * const barejid, const char * const id) {}
 
-void ui_incoming_private_msg(const char * const fulljid, const char * const message, GTimeVal *tv_stamp) {}
+void ui_incoming_private_msg(const char * const fulljid, const char * const message, GDateTime *timestamp) {}
 
 void ui_disconnected(void) {}
 void ui_recipient_gone(const char * const barejid, const char * const resource) {}
@@ -222,7 +208,7 @@ void ui_room_occupant_role_and_affiliation_change(const char * const roomjid, co
     const char * const affiliation, const char * const actor, const char * const reason) {}
 void ui_room_roster(const char * const roomjid, GList *occupants, const char * const presence) {}
 void ui_room_history(const char * const roomjid, const char * const nick,
-    GTimeVal tv_stamp, const char * const message) {}
+    GDateTime *timestamp, const char * const message) {}
 void ui_room_message(const char * const roomjid, const char * const nick,
     const char * const message) {}
 void ui_room_subject(const char * const roomjid, const char * const nick, const char * const subject) {}
@@ -315,7 +301,9 @@ void ui_redraw_all_room_rosters(void) {}
 void ui_show_all_room_rosters(void) {}
 void ui_hide_all_room_rosters(void) {}
 
-void ui_tidy_wins(void) {}
+gboolean ui_tidy_wins(void) {
+    return TRUE;
+}
 void ui_prune_wins(void) {}
 gboolean ui_swap_wins(int source_win, int target_win)
 {
@@ -381,6 +369,7 @@ void cons_show_log_prefs(void) {}
 void cons_show_presence_prefs(void) {}
 void cons_show_connection_prefs(void) {}
 void cons_show_otr_prefs(void) {}
+void cons_show_pgp_prefs(void) {}
 
 void cons_show_account(ProfAccount *account)
 {
@@ -458,6 +447,8 @@ void cons_occupants_setting(void) {}
 void cons_roster_setting(void) {}
 void cons_presence_setting(void) {}
 void cons_wrap_setting(void) {}
+void cons_winstidy_setting(void) {}
+void cons_encwarn_setting(void) {}
 void cons_time_setting(void) {}
 void cons_mouse_setting(void) {}
 void cons_statuses_setting(void) {}
@@ -490,12 +481,68 @@ void cons_show_contact_online(PContact contact, Resource *resource, GDateTime *l
 void cons_show_contact_offline(PContact contact, char *resource, char *status) {}
 void cons_theme_colours(void) {}
 
+// status bar
+void status_bar_inactive(const int win) {}
+void status_bar_active(const int win) {}
+void status_bar_new(const int win) {}
+void status_bar_set_all_inactive(void) {}
+
 // roster window
 void rosterwin_roster(void) {}
 
 // occupants window
 void occupantswin_occupants(const char * const room) {}
 
+// window interface
+ProfWin* win_create_console(void)
+{
+    return NULL;
+}
+ProfWin* win_create_xmlconsole(void)
+{
+    return NULL;
+}
+ProfWin* win_create_chat(const char * const barejid)
+{
+    return (ProfWin*)mock();
+}
+ProfWin* win_create_muc(const char * const roomjid)
+{
+    return NULL;
+}
+ProfWin* win_create_muc_config(const char * const title, DataForm *form)
+{
+    return NULL;
+}
+ProfWin* win_create_private(const char * const fulljid)
+{
+    return NULL;
+}
+
+void win_update_virtual(ProfWin *window) {}
+void win_free(ProfWin *window) {}
+int win_unread(ProfWin *window)
+{
+    return 0;
+}
+
+void win_resize(ProfWin *window) {}
+void win_hide_subwin(ProfWin *window) {}
+void win_show_subwin(ProfWin *window) {}
+void win_refresh_without_subwin(ProfWin *window) {}
+void win_refresh_with_subwin(ProfWin *window) {}
+void win_print(ProfWin *window, const char show_char, GDateTime *timestamp, int flags, theme_item_t theme_item, const char * const from, const char * const message) {}
+void win_vprint(ProfWin *window, const char show_char, GDateTime *timestamp, int flags, theme_item_t theme_item, const char * const from, const char * const message, ...) {}
+char* win_get_title(ProfWin *window)
+{
+    return NULL;
+}
+void win_show_occupant(ProfWin *window, Occupant *occupant) {}
+void win_show_occupant_info(ProfWin *window, const char * const room, Occupant *occupant) {}
+void win_show_contact(ProfWin *window, PContact contact) {}
+void win_show_info(ProfWin *window, PContact contact) {}
+void win_println(ProfWin *window, const char * const message) {}
+
 // desktop notifier actions
 void notifier_uninit(void) {}
 
diff --git a/tests/ui/stub_ui.h b/tests/unittests/ui/stub_ui.h
index 81357a86..81357a86 100644
--- a/tests/ui/stub_ui.h
+++ b/tests/unittests/ui/stub_ui.h
diff --git a/tests/testsuite.c b/tests/unittests/unittests.c
index 3f860178..61c58e33 100644
--- a/tests/testsuite.c
+++ b/tests/unittests/unittests.c
@@ -21,6 +21,7 @@
 #include "test_cmd_sub.h"
 #include "test_cmd_statuses.h"
 #include "test_cmd_otr.h"
+#include "test_cmd_pgp.h"
 #include "test_jid.h"
 #include "test_parser.h"
 #include "test_roster_list.h"
@@ -324,6 +325,7 @@ int main(int argc, char* argv[]) {
         unit_test(cmd_account_set_jid_sets_resource),
         unit_test(cmd_account_set_server_sets_server),
         unit_test(cmd_account_set_resource_sets_resource),
+        unit_test(cmd_account_set_resource_sets_resource_with_online_message),
         unit_test(cmd_account_set_password_sets_password),
         unit_test(cmd_account_set_eval_password_sets_eval_password),
         unit_test(cmd_account_set_password_when_eval_password_set),
@@ -499,14 +501,6 @@ int main(int argc, char* argv[]) {
         unit_test_setup_teardown(cmd_otr_log_redact_shows_warning_when_chlog_disabled,
             load_preferences,
             close_preferences),
-        unit_test(cmd_otr_warn_shows_usage_when_no_args),
-        unit_test(cmd_otr_warn_shows_usage_when_invalid_arg),
-        unit_test_setup_teardown(cmd_otr_warn_on_enables_unencrypted_warning,
-            load_preferences,
-            close_preferences),
-        unit_test_setup_teardown(cmd_otr_warn_off_disables_unencrypted_warning,
-            load_preferences,
-            close_preferences),
         unit_test(cmd_otr_libver_shows_libotr_version),
         unit_test(cmd_otr_gen_shows_message_when_not_connected),
         unit_test(cmd_otr_gen_generates_key_for_connected_account),
@@ -539,6 +533,22 @@ int main(int argc, char* argv[]) {
         unit_test(cmd_otr_shows_message_when_otr_unsupported),
 #endif
 
+#ifdef HAVE_LIBGPGME
+        unit_test(cmd_pgp_shows_usage_when_no_args),
+        unit_test(cmd_pgp_start_shows_message_when_disconnected),
+        unit_test(cmd_pgp_start_shows_message_when_disconnecting),
+        unit_test(cmd_pgp_start_shows_message_when_connecting),
+        unit_test(cmd_pgp_start_shows_message_when_undefined),
+        unit_test(cmd_pgp_start_shows_message_when_started),
+        unit_test(cmd_pgp_start_shows_message_when_no_arg_in_console),
+        unit_test(cmd_pgp_start_shows_message_when_no_arg_in_muc),
+        unit_test(cmd_pgp_start_shows_message_when_no_arg_in_mucconf),
+        unit_test(cmd_pgp_start_shows_message_when_no_arg_in_private),
+        unit_test(cmd_pgp_start_shows_message_when_no_arg_in_xmlconsole),
+#else
+        unit_test(cmd_pgp_shows_message_when_pgp_unsupported),
+#endif
+
         unit_test(cmd_join_shows_message_when_disconnecting),
         unit_test(cmd_join_shows_message_when_connecting),
         unit_test(cmd_join_shows_message_when_disconnected),
diff --git a/tests/xmpp/stub_xmpp.c b/tests/unittests/xmpp/stub_xmpp.c
index cc9580bf..79f2595a 100644
--- a/tests/xmpp/stub_xmpp.c
+++ b/tests/unittests/xmpp/stub_xmpp.c
@@ -26,7 +26,7 @@ jabber_conn_status_t jabber_connect_with_account(const ProfAccount * const accou
 
 void jabber_disconnect(void) {}
 void jabber_shutdown(void) {}
-void jabber_process_events(void) {}
+void jabber_process_events(int millis) {}
 const char * jabber_get_fulljid(void)
 {
     return (char *)mock();
@@ -65,13 +65,18 @@ char* message_send_chat(const char * const barejid, const char * const msg)
     return NULL;
 }
 
-char* message_send_chat_encrypted(const char * const barejid, const char * const msg)
+char* message_send_chat_otr(const char * const barejid, const char * const msg)
 {
     check_expected(barejid);
     check_expected(msg);
     return NULL;
 }
 
+char* message_send_chat_pgp(const char * const barejid, const char * const msg)
+{
+    return NULL;
+}
+
 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) {}
@@ -114,11 +119,12 @@ void presence_join_room(char *room, char *nick, char * passwd)
 void presence_change_room_nick(const char * const room, const char * const nick) {}
 void presence_leave_chat_room(const char * const room_jid) {}
 
-void presence_send(resource_presence_t status, const char * const msg, int idle)
+void presence_send(resource_presence_t status, const char * const msg, int idle, char *signed_status)
 {
     check_expected(status);
     check_expected(msg);
     check_expected(idle);
+    check_expected(signed_status);
 }
 
 gboolean presence_sub_request_exists(const char * const bare_jid)
diff --git a/theme_template b/theme_template
index c088e181..e2380b44 100644
--- a/theme_template
+++ b/theme_template
@@ -58,7 +58,7 @@ time=
 privileges=
 presence=
 intype=
-otr.warn=
+enc.warn=
 resource.title=
 resource.message=
 statuses.console=
@@ -72,3 +72,4 @@ roster.size=
 occupants=
 occupants.size=
 occupants.jid=
+wins.autotidy=
diff --git a/themes/boothj5 b/themes/boothj5
index 9ed3fe69..090f7bc3 100644
--- a/themes/boothj5
+++ b/themes/boothj5
@@ -17,7 +17,7 @@ statusbar=blue
 statusbar.text=bold_white
 statusbar.brackets=white
 statusbar.active=bold_cyan
-statusbar.new=bold_green
+statusbar.new=bold_white
 main.text=white
 main.text.me=cyan
 main.text.them=bold_white
@@ -59,7 +59,7 @@ time.statusbar=seconds
 privileges=true
 presence=true
 intype=true
-otr.warn=true
+enc.warn=true
 resource.title=true
 resource.message=true
 statuses.console=all
@@ -73,3 +73,4 @@ roster.size=25
 occupants=true
 occupants.size=15
 occupants.jid=true
+wins.autotidy=true
diff --git a/themes/complex b/themes/complex
index a5510baa..46e926c2 100644
--- a/themes/complex
+++ b/themes/complex
@@ -21,4 +21,4 @@ roster.size=25
 privileges=true
 presence=true
 intype=true
-otr.warn=true
+enc.warn=true
diff --git a/themes/simple b/themes/simple
index 2885db12..e873723b 100644
--- a/themes/simple
+++ b/themes/simple
@@ -20,4 +20,5 @@ roster.size=25
 privileges=false
 presence=false
 intype=false
-otr.warn=false
+enc.warn=false
+wins.autotidy=false