[Pkg-voip-commits] [sngrep] 01/04: Imported Upstream version 1.1.0

Victor Seva Lopez maniac-guest at moszumanska.debian.org
Tue Oct 27 22:15:44 UTC 2015


This is an automated email from the git hooks/post-receive script.

maniac-guest pushed a commit to branch pre_1.1.0
in repository sngrep.

commit 53e1c8dd07bd2b7924c335467b40d1ffc010901f
Author: Victor Seva <linuxmaniac at torreviejawireless.org>
Date:   Tue Oct 27 22:21:24 2015 +0100

    Imported Upstream version 1.1.0
---
 README.md                                |  34 ++--
 config/sngreprc                          |   2 +-
 configure.ac                             |  55 +++++-
 doc/sngrep.8                             |   2 +-
 src/Makefile.am                          |  12 +-
 src/capture.c                            |  11 +-
 src/capture.h                            |   1 +
 src/{capture_tls.c => capture_gnutls.c}  | 125 +++++++-----
 src/{capture_tls.h => capture_gnutls.h}  |  43 ++---
 src/{capture_tls.c => capture_openssl.c} |   2 +-
 src/{capture_tls.h => capture_openssl.h} |   0
 src/capture_reasm.c                      |  10 +-
 src/filter.c                             |  54 ++++--
 src/filter.h                             |  10 +
 src/keybinding.c                         | 242 +++++++++---------------
 src/keybinding.h                         |  17 +-
 src/main.c                               |  35 +++-
 src/option.c                             |   3 -
 src/rtp.c                                | 187 +++++++++++++-----
 src/rtp.h                                | 218 ++++++++++++++++++++-
 src/sip.c                                |  38 +++-
 src/sip.h                                |   6 +
 src/ui_call_flow.c                       | 313 ++++++++++++++++++++++++++++---
 src/ui_call_flow.h                       |  28 ++-
 src/ui_call_list.c                       |  10 +-
 src/ui_filter.c                          |  53 +++---
 src/ui_filter.h                          |   1 +
 src/ui_manager.c                         |   3 +-
 src/ui_manager.h                         |   3 +
 src/ui_settings.c                        |   6 +-
 src/ui_stats.c                           | 222 ++++++++++++++++++++++
 src/ui_stats.h                           |  71 +++++++
 src/vector.c                             |   6 +
 src/vector.h                             |   6 +
 34 files changed, 1427 insertions(+), 402 deletions(-)

diff --git a/README.md b/README.md
index f12b092..c189010 100644
--- a/README.md
+++ b/README.md
@@ -10,33 +10,19 @@ as PCAP viewer.
 ## Installing
 
 ### Binaries
-#### Debian / Ubuntu
-[Install sngrep Debian/Ubuntu package](https://github.com/irontec/sngrep/wiki/Installing-Binaries#debian--ubuntu)
-
-#### CentOS / RedHat / Fedora
-[Install sngrep CentOS/RedHat/Fedora package](https://github.com/irontec/sngrep/wiki/Installing-Binaries#centos--fedora--rhel)
-
-#### Gentoo
-You can find unofficial ebuilds for sngrep at [Gentoo Bugtracker System](https://bugs.gentoo.org/show_bug.cgi?id=534780) (thanks to Space Dream)
-
-Feel free to vote if you would like to see sngrep be part of Gentoo portage tree.
-
-#### Arch
-You can find an unofficial PKGBUILD for Arch at [ArchLinux User Repositories](https://aur.archlinux.org/packages/sngrep/) (thanks to w1ngnutt)
-
-Feel free to vote if you would like to see sngrep at official Arch repositories.
-
-#### OSX
-OSX users can install sngrep using [homebrew](https://github.com/Homebrew/homebrew)
-
-    brew install sngrep
+* [Debian / Ubuntu] (https://github.com/irontec/sngrep/wiki/Installing-Binaries#debian--ubuntu)
+* [CentOS / RedHat / Fedora](https://github.com/irontec/sngrep/wiki/Installing-Binaries#centos--fedora--rhel)
+* [Gentoo](https://github.com/irontec/sngrep/wiki/Installing-Binaries#gentoo)
+* [Arch](https://github.com/irontec/sngrep/wiki/Installing-Binaries#arch)
+* [OSX] (https://github.com/irontec/sngrep/wiki/Installing-Binaries#osx)
 
 ### Building from sources
 Prerequisites
 
  - libncurse5 - for UI, windows, panels.
  - libpcap - for capturing packets.
- - libssl - (optional) for TLS transport decrypt
+ - libssl - (optional) for TLS transport decrypt using OpenSSL and libcrypt
+ - gnutls - (optional) for TLS transport decrypt using GnuTLS and libgcrypt
  - libncursesw5 - (optional) for UI, windows, panels (wide-character support)
  - libpcre - (optional) for Perl Compatible regular expressions
 
@@ -52,9 +38,11 @@ You can pass following flags to ./configure to enable some features
 | configure flag | Feature |
 | ------------- | ------------- |
 | `--with-openssl` | Adds OpenSSL support to parse TLS captured messages (req. libssl)  |
+| `--with-gnutls` | Adds GnuTLS support to parse TLS captured messages (req. gnutls)  |
 | `--with-pcre`|  Adds Perl Compatible regular expressions support in regexp fields |
 | `--enable-unicode`   | Adds Ncurses UTF-8/Unicode support (req. libncursesw5) |
-| `--enable-ipv6`   | Enables IPv6 packet capture support. |
+| `--disable-ipv6`   | Disable IPv6 packet capture support. (default: enabled) |
+| `--disable-eep`   | Disable EEP packet send/receive support. (default: enabled) |
 
 You can find [detailed instructions for some distributions] (https://github.com/irontec/sngrep/wiki/Building) on wiki.
 
@@ -76,6 +64,8 @@ or live capturing, saving packets to a new file
 You can configure some options using [sngreprc] (https://github.com/irontec/sngrep/wiki/Configuration) file
 
 ## Frequent Asked Questions
+Any feedback, request or question are welcomed at [#sngrep](https://webchat.freenode.net/?channels=sngrep) channel at irc.freenode.net
+
 See FAQ on [Github Wiki](https://github.com/irontec/sngrep/wiki#frequent-asked-questions)
 
 ## License 
diff --git a/config/sngreprc b/config/sngreprc
index 630e8fe..b9304b2 100644
--- a/config/sngreprc
+++ b/config/sngreprc
@@ -99,4 +99,4 @@
 
 ##-----------------------------------------------------------------------------
 ## Uncomment to display dialogs that does not start with a request method
-# set sip.ignoreincomplete off
+# set sip.noincomplete off
diff --git a/configure.ac b/configure.ac
index 272bc50..5963d9a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,5 +1,5 @@
 AC_PREREQ([2.59])
-AC_INIT([sngrep], [1.0.0], [kaian at irontec.com], [sngrep], [http://www.irontec.com/])
+AC_INIT([sngrep], [1.0.1], [kaian at irontec.com], [sngrep], [http://www.irontec.com/])
 AM_INIT_AUTOMAKE([1.9])
 m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
 AC_CONFIG_HEADERS([src/config.h])
@@ -97,6 +97,30 @@ AS_IF([test "x$enable_unicode" == "xyes"], [
 ])
 
 ####
+#### GnuTLS Support
+####
+AC_ARG_WITH([gnutls],
+    AS_HELP_STRING([--with-gnutls], [Enable SSL Support (TLS SIP Transport)]),
+    [AC_SUBST(WITH_GNUTLS, $withval)],
+    [AC_SUBST(WITH_GNUTLS, no)]
+)
+
+AS_IF([test "x$WITH_GNUTLS" == "xyes"], [
+	AC_CHECK_LIB([gnutls], [gnutls_init], [], [
+	    AC_MSG_ERROR([ You need to have gnutls installed to compile sngrep])
+	])
+
+	AC_CHECK_LIB([gnutls-openssl], [SSL_new], [], [
+	    AC_MSG_ERROR([ You need to have gnutls installed to compile sngrep])
+	])
+	
+	AC_CHECK_LIB([gcrypt], [gcry_md_map_name], [], [
+	    AC_MSG_ERROR([ You need to have libgcrypt installed to compile sngrep])
+	])
+	AC_DEFINE([WITH_GNUTLS],[],[Compile With GnuTLS compatibility])
+], [])
+
+####
 #### OpenSSL Support
 ####
 AC_ARG_WITH([openssl],
@@ -106,16 +130,21 @@ AC_ARG_WITH([openssl],
 )
 
 AS_IF([test "x$WITH_OPENSSL" == "xyes"], [
+	AS_IF([test "x$WITH_GNUTLS" == "xyes"], [
+	    AC_MSG_ERROR([ GnuTLS and OpenSSL can not be enabled at the same time ])
+	], [])
+
 	AC_CHECK_LIB([ssl], [SSL_new], [], [
 	    AC_MSG_ERROR([ You need to have libssl installed to compile sngrep])
 	])
-	
+
 	AC_CHECK_LIB([crypto], [EVP_get_cipherbyname], [], [
 	    AC_MSG_ERROR([ You need to have libcrypto installed to compile sngrep])
 	])
 	AC_DEFINE([WITH_OPENSSL],[],[Compile With Openssl compatibility])
 ], [])
 
+
 ####
 #### PCRE Support
 ####
@@ -141,7 +170,7 @@ AS_IF([test "x$WITH_PCRE" == "xyes"], [
 AC_ARG_ENABLE([ipv6],
     AS_HELP_STRING([--enable-ipv6], [Enable IPv6 Support]),
     [AC_SUBST(USE_IPV6, $enableval)],
-    [AC_SUBST(USE_IPV6, no)]
+    [AC_SUBST(USE_IPV6, yes)]
 )
 
 AS_IF([test "x$USE_IPV6" == "xyes"], [
@@ -152,8 +181,24 @@ AS_IF([test "x$USE_IPV6" == "xyes"], [
 ], [])
 
 
+####
+#### EEP Support
+####
+AC_ARG_ENABLE([eep],
+    AS_HELP_STRING([--enable-eep], [Enable EEP/HEP Support]),
+    [AC_SUBST(USE_EEP, $enableval)],
+    [AC_SUBST(USE_EEP, yes)]
+)
+
+AS_IF([test "x$USE_EEP" == "xyes"], [
+	AC_DEFINE([USE_EEP],[],[Compile With EEP support])
+], [])
+
+
 # Conditional Source inclusion 
+AM_CONDITIONAL([WITH_GNUTLS], [test "x$WITH_GNUTLS" == "xyes"])
 AM_CONDITIONAL([WITH_OPENSSL], [test "x$WITH_OPENSSL" == "xyes"])
+AM_CONDITIONAL([USE_EEP], [test "x$USE_EEP" == "xyes"])
 
 
 ######################################################################
@@ -177,10 +222,12 @@ AS_IF([test "x$enable_logo" == "xyes"], [
 AC_MSG_NOTICE
 AC_MSG_NOTICE(   sngrep configure finished                            	)
 AC_MSG_NOTICE( ====================================================== 	)
+AC_MSG_NOTICE( GnuTLS Support               : ${WITH_GNUTLS}		)
 AC_MSG_NOTICE( OpenSSL Support              : ${WITH_OPENSSL} 			)
-AC_MSG_NOTICE( Unicode Support              : ${UNICODE}  				)
+AC_MSG_NOTICE( Unicode Support              : ${UNICODE}  		)
 AC_MSG_NOTICE( Perl Expressions Support     : ${WITH_PCRE}              )
 AC_MSG_NOTICE( IPv6 Support                 : ${USE_IPV6}               )
+AC_MSG_NOTICE( EEP Support                  : ${USE_EEP}               )
 AC_MSG_NOTICE( ====================================================== 	)
 AC_MSG_NOTICE
 
diff --git a/doc/sngrep.8 b/doc/sngrep.8
index f63a2bf..212a3fc 100644
--- a/doc/sngrep.8
+++ b/doc/sngrep.8
@@ -3,7 +3,7 @@
 .\" Copyright (c) 2013-2015  Ivan Alonso <kaian at irontec.com>
 .\" Copyright (c) 2013-2015  Irontec S.L.
 
-.TH SNGREP 8 "June 2015" "sngrep 1.0.0"
+.TH SNGREP 8 "June 2015" "sngrep 1.0.1"
 
 .SH NAME
 
diff --git a/src/Makefile.am b/src/Makefile.am
index 96a7cd1..21ea3bc 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,10 +1,16 @@
 bin_PROGRAMS=sngrep
-sngrep_SOURCES=capture.c capture_eep.c capture_reasm.c capture_ws.c
+sngrep_SOURCES=capture.c capture_reasm.c capture_ws.c
+if USE_EEP
+sngrep_SOURCES+=capture_eep.c
+endif
+if WITH_GNUTLS
+sngrep_SOURCES+=capture_gnutls.c
+endif
 if WITH_OPENSSL
-sngrep_SOURCES+=capture_tls.c
+sngrep_SOURCES+=capture_openssl.c
 endif
 sngrep_SOURCES+=sip.c sip_call.c sip_msg.c sip_attr.c main.c option.c
 sngrep_SOURCES+=group.c filter.c keybinding.c media.c setting.c rtp.c util.c vector.c
-sngrep_SOURCES+=ui_manager.c ui_call_list.c ui_call_flow.c ui_call_raw.c
+sngrep_SOURCES+=ui_manager.c ui_call_list.c ui_call_flow.c ui_call_raw.c ui_stats.c
 sngrep_SOURCES+=ui_filter.c ui_save.c ui_msg_diff.c ui_column_select.c ui_settings.c
 
diff --git a/src/capture.c b/src/capture.c
index e204d10..e5a1f59 100644
--- a/src/capture.c
+++ b/src/capture.c
@@ -35,9 +35,14 @@
 #include "capture.h"
 #include "capture_ws.h"
 #include "capture_reasm.h"
+#ifdef USE_EEP
 #include "capture_eep.h"
+#endif
+#ifdef WITH_GNUTLS
+#include "capture_gnutls.h"
+#endif
 #ifdef WITH_OPENSSL
-#include "capture_tls.h"
+#include "capture_openssl.h"
 #endif
 #include "sip.h"
 #include "rtp.h"
@@ -300,7 +305,7 @@ parse_packet(u_char *info, const struct pcap_pkthdr *header, const u_char *packe
         if (!(pkt = capture_packet_reasm_tcp(pkt, tcp, payload, size_payload)))
             return;
 
-#ifdef WITH_OPENSSL
+#if defined(WITH_GNUTLS) || defined(WITH_OPENSSL)
         // Check if packet is TLS
         if (capture_cfg.keyfile)
             tls_process_segment(pkt, tcp);
@@ -319,8 +324,10 @@ parse_packet(u_char *info, const struct pcap_pkthdr *header, const u_char *packe
     capture_lock();
     // Check if we can handle this packet
     if (capture_packet_parse(pkt) == 0) {
+#ifdef USE_EEP
         // Send this packet through eep
         capture_eep_send(pkt);
+#endif
         // Store this packets in output file
         dump_packet(capture_cfg.pd, pkt);
         // If storage is disabled, delete frames payload
diff --git a/src/capture.h b/src/capture.h
index 86f9797..2ca77bc 100644
--- a/src/capture.h
+++ b/src/capture.h
@@ -108,6 +108,7 @@ enum capture_packet_type {
     CAPTURE_PACKET_SIP_WS,
     CAPTURE_PACKET_SIP_WSS,
     CAPTURE_PACKET_RTP,
+    CAPTURE_PACKET_RTCP,
 };
 
 /**
diff --git a/src/capture_tls.c b/src/capture_gnutls.c
similarity index 79%
copy from src/capture_tls.c
copy to src/capture_gnutls.c
index 3ba3455..22345d5 100644
--- a/src/capture_tls.c
+++ b/src/capture_gnutls.c
@@ -32,7 +32,7 @@
 
 #include <unistd.h>
 #include "capture.h"
-#include "capture_tls.h"
+#include "capture_gnutls.h"
 #include "option.h"
 #include "util.h"
 #include "sip.h"
@@ -48,14 +48,15 @@ int
 P_hash(const char *digest, unsigned char *dest, int dlen, unsigned char *secret, int sslen,
        unsigned char *seed, int slen)
 {
-    unsigned char hmac[20];
+    unsigned char hmac[48];
     unsigned int hlen;
-    HMAC_CTX hm;
-    const EVP_MD *md = EVP_get_digestbyname(digest);
+    gcry_md_hd_t md;
     unsigned int tmpslen;
     unsigned char tmpseed[slen];
     unsigned char *out = dest;
     int pending = dlen;
+    int algo = gcry_md_map_name(digest);
+    int algolen = gcry_md_get_algo_dlen(algo);
 
     // Copy initial seed
     memcpy(tmpseed, seed, slen);
@@ -63,21 +64,25 @@ P_hash(const char *digest, unsigned char *dest, int dlen, unsigned char *secret,
 
     // Calculate enough data to fill destination
     while (pending > 0) {
-        HMAC_Init(&hm, secret, sslen, md);
-        HMAC_Update(&hm, tmpseed, tmpslen);
-        HMAC_Final(&hm, tmpseed, &tmpslen);
-
-        HMAC_Init(&hm, secret, sslen, md);
-        HMAC_Update(&hm, tmpseed, tmpslen);
-        HMAC_Update(&hm, seed, slen);
-        HMAC_Final(&hm, hmac, &hlen);
+        gcry_md_open(&md, algo, GCRY_MD_FLAG_HMAC);
+        gcry_md_setkey(md, secret, sslen);
+        gcry_md_write(md, tmpseed, tmpslen);
+        memcpy(tmpseed, gcry_md_read(md, algo), algolen);
+        tmpslen = algolen;
+        gcry_md_close(md);
+
+        gcry_md_open(&md, algo, GCRY_MD_FLAG_HMAC);
+        gcry_md_setkey(md, secret, sslen);
+        gcry_md_write(md, tmpseed, tmpslen);
+        gcry_md_write(md, seed, slen);
+        memcpy(hmac, gcry_md_read(md, algo), algolen);
+        hlen = algolen;
 
         hlen = (hlen > pending) ? pending : hlen;
         memcpy(out, hmac, hlen);
         out += hlen;
         pending -= hlen;
     }
-    HMAC_cleanup(&hm);
 
     return hlen;
 }
@@ -119,6 +124,12 @@ PRF(unsigned char *dest, int dlen, unsigned char *pre_master_secret, int plen, u
 struct SSLConnection *
 tls_connection_create(struct in_addr caddr, u_short cport, struct in_addr saddr, u_short sport) {
     struct SSLConnection *conn = NULL;
+    gnutls_datum_t keycontent = { NULL, 0 };
+    FILE *keyfp;
+    gnutls_x509_privkey_t spkey;
+    size_t br;
+
+    // Allocate memory for this connection
     conn = sng_malloc(sizeof(struct SSLConnection));
 
     memcpy(&conn->client_addr, &caddr, sizeof(struct in_addr));
@@ -127,18 +138,28 @@ tls_connection_create(struct in_addr caddr, u_short cport, struct in_addr saddr,
     memcpy(&conn->server_port, &sport, sizeof(u_short));
 
     SSL_library_init();
-    ERR_load_crypto_strings();
     OpenSSL_add_all_algorithms();
 
     if (!(conn->ssl_ctx = SSL_CTX_new(SSLv23_server_method())))
         return NULL;
 
-    SSL_CTX_use_PrivateKey_file(conn->ssl_ctx, capture_get_keyfile(),
-                                SSL_FILETYPE_PEM);
     if (!(conn->ssl = SSL_new(conn->ssl_ctx)))
         return NULL;
 
-    conn->server_private_key = SSL_get_privatekey(conn->ssl);
+    if (!(keyfp = fopen(capture_get_keyfile(), "rb")))
+        return NULL;
+    fseek(keyfp, 0, SEEK_END);
+    keycontent.size = ftell(keyfp);
+    fseek(keyfp, 0, SEEK_SET);
+    keycontent.data = sng_malloc(keycontent.size);
+    br = fread(keycontent.data, 1, keycontent.size, keyfp);
+    fclose(keyfp);
+
+    gnutls_x509_privkey_init(&spkey);
+    gnutls_x509_privkey_import(spkey, &keycontent, GNUTLS_X509_FMT_PEM);
+    sng_free(keycontent.data);
+    gnutls_privkey_init(&conn->server_private_key);
+    gnutls_privkey_import_x509(conn->server_private_key, spkey, 0);
 
     // Add this connection to the list
     conn->next = connections;
@@ -179,25 +200,31 @@ tls_connection_destroy(struct SSLConnection *conn)
 int
 tls_check_keyfile(const char *keyfile)
 {
-    SSL *ssl;
-    SSL_CTX *ssl_ctx;
+    gnutls_x509_privkey_t key;
+    gnutls_datum_t keycontent = { NULL, 0 };
+    FILE *keyfp;
+    size_t br;
 
     SSL_library_init();
-    ERR_load_crypto_strings();
     OpenSSL_add_all_algorithms();
 
     if (access(capture_get_keyfile(), R_OK) != 0)
         return 0;
 
-    if (!(ssl_ctx = SSL_CTX_new(SSLv23_server_method())))
+    if (!(keyfp = fopen(capture_get_keyfile(), "rb")))
         return 0;
 
-    SSL_CTX_use_PrivateKey_file(ssl_ctx, capture_get_keyfile(), SSL_FILETYPE_PEM);
-    if (!(ssl = SSL_new(ssl_ctx)))
-        return 0;
+    fseek(keyfp, 0, SEEK_END);
+    keycontent.size = ftell(keyfp);
+    fseek(keyfp, 0, SEEK_SET);
+    keycontent.data = sng_malloc(keycontent.size);
+    br = fread(keycontent.data, 1, keycontent.size, keyfp);
+    fclose(keyfp);
 
-    if (!SSL_get_privatekey(ssl))
+    gnutls_x509_privkey_init(&key);
+    if (gnutls_x509_privkey_import(key, &keycontent, GNUTLS_X509_FMT_PEM) < 0)
         return 0;
+    sng_free(keycontent.data);
 
     return 1;
 }
@@ -358,7 +385,6 @@ tls_process_record_handshake(struct SSLConnection *conn, const opaque *fragment)
                 // Store client random
                 clienthello = (struct ClientHello *) body;
                 memcpy(&conn->client_random, &clienthello->random, sizeof(struct Random));
-
                 // Check we have a TLS handshake
                 if (!(clienthello->client_version.major == 0x03
                       && clienthello->client_version.minor == 0x01)) {
@@ -389,16 +415,16 @@ tls_process_record_handshake(struct SSLConnection *conn, const opaque *fragment)
                 // Decrypt PreMasterKey
                 clientkeyex = (struct ClientKeyExchange *) body;
 
-                RSA_private_decrypt(UINT16_INT(clientkeyex->length),
-                                    (const unsigned char *) &clientkeyex->exchange_keys,
-                                    (unsigned char *) &conn->pre_master_secret,
-                                    conn->server_private_key->pkey.rsa, RSA_PKCS1_PADDING);
+                gnutls_datum_t exkeys, pms;
+                exkeys.size = UINT16_INT(clientkeyex->length);
+                exkeys.data = (unsigned char *)&clientkeyex->exchange_keys;
+                gnutls_privkey_decrypt_data(conn->server_private_key, 0, &exkeys, &pms);
+                memcpy(&conn->pre_master_secret, pms.data, pms.size);
 
+                // Get MasterSecret
                 unsigned char *seed = sng_malloc(sizeof(struct Random) * 2);
                 memcpy(seed, &conn->client_random, sizeof(struct Random));
                 memcpy(seed + sizeof(struct Random), &conn->server_random, sizeof(struct Random));
-
-                // Get MasterSecret
                 PRF((unsigned char *) &conn->master_secret, sizeof(struct MasterSecret),
                     (unsigned char *) &conn->pre_master_secret, sizeof(struct PreMasterSecret),
                     (unsigned char *) "master secret", seed, sizeof(struct Random) * 2);
@@ -415,15 +441,22 @@ tls_process_record_handshake(struct SSLConnection *conn, const opaque *fragment)
                 sng_free(seed);
 
                 // Create Client decoder
-                EVP_CIPHER_CTX_init(&conn->client_cipher_ctx);
-                EVP_CipherInit(&conn->client_cipher_ctx, conn->ciph,
-                               conn->key_material.client_write_key, conn->key_material.client_write_IV,
-                               0);
-
-                EVP_CIPHER_CTX_init(&conn->server_cipher_ctx);
-                EVP_CipherInit(&conn->server_cipher_ctx, conn->ciph,
-                               conn->key_material.server_write_key, conn->key_material.server_write_IV,
-                               0);
+                gcry_cipher_open(&conn->client_cipher_ctx, conn->ciph, GCRY_CIPHER_MODE_CBC, 0);
+                gcry_cipher_setkey(conn->client_cipher_ctx,
+                                   conn->key_material.client_write_key,
+                                   gcry_cipher_get_algo_keylen(conn->ciph));
+                gcry_cipher_setiv(conn->client_cipher_ctx,
+                                  conn->key_material.client_write_IV,
+                                  gcry_cipher_get_algo_blklen(conn->ciph));
+
+                // Create Server decoder
+                gcry_cipher_open(&conn->server_cipher_ctx, conn->ciph, GCRY_CIPHER_MODE_CBC, 0);
+                gcry_cipher_setkey(conn->server_cipher_ctx,
+                                   conn->key_material.server_write_key,
+                                   gcry_cipher_get_algo_keylen(conn->ciph));
+                gcry_cipher_setiv(conn->server_cipher_ctx,
+                                  conn->key_material.server_write_IV,
+                                  gcry_cipher_get_algo_blklen(conn->ciph));
 
                 break;
             case finished:
@@ -447,10 +480,10 @@ int
 tls_process_record_data(struct SSLConnection *conn, const opaque *fragment, const int len,
                         uint8 **out, uint32_t *outl)
 {
-    EVP_CIPHER_CTX *evp;
+    gcry_cipher_hd_t *evp;
     unsigned char pad;
     unsigned char *decoded;
-    uint32_t dlen;
+    size_t dlen = len;
 
     if (conn->direction == 0) {
         evp = &conn->client_cipher_ctx;
@@ -459,7 +492,7 @@ tls_process_record_data(struct SSLConnection *conn, const opaque *fragment, cons
     }
 
     decoded = sng_malloc(len);
-    EVP_Cipher(evp, decoded, (unsigned char *) fragment, len);
+    gcry_cipher_decrypt(*evp, decoded, dlen, (unsigned char *) fragment, len);
 
     // Get padding counter and remove from data
     pad = decoded[len - 1];
@@ -482,9 +515,9 @@ tls_connection_load_cipher(struct SSLConnection *conn)
         return 1;
 
     if (conn->cipher_suite.cs2 == TLS_RSA_WITH_AES_256_CBC_SHA.cs2) {
-        conn->ciph = EVP_get_cipherbyname("AES256");
+        conn->ciph = gcry_cipher_map_name("AES256");
     } else if (conn->cipher_suite.cs2 == TLS_RSA_WITH_AES_128_CBC_SHA.cs2) {
-        conn->ciph = EVP_get_cipherbyname("AES128");
+        conn->ciph = gcry_cipher_map_name("AES");
     } else {
         return 1;
     }
diff --git a/src/capture_tls.h b/src/capture_gnutls.h
similarity index 92%
copy from src/capture_tls.h
copy to src/capture_gnutls.h
index 4208066..931cbce 100644
--- a/src/capture_tls.h
+++ b/src/capture_gnutls.h
@@ -47,10 +47,11 @@
 #define __SNGREP_CAPTURE_TLS_
 
 #include "config.h"
-#include <openssl/ssl.h>
-#include <openssl/tls1.h>
-#include <openssl/err.h>
-#include <openssl/evp.h>
+#include <gnutls/openssl.h>
+#include <gnutls/crypto.h>
+#include <gnutls/abstract.h>
+#include <gnutls/x509.h>
+#include <gcrypt.h>
 #include "capture.h"
 
 //! Cast two bytes into decimal (Big Endian)
@@ -97,23 +98,23 @@ enum SSLConnectionState {
 
 //! ContentType values as defined in RFC5246
 enum ContentType {
-    change_cipher_spec = SSL3_RT_CHANGE_CIPHER_SPEC,
-    alert = SSL3_RT_ALERT,
-    handshake = SSL3_RT_HANDSHAKE,
-    application_data = SSL3_RT_APPLICATION_DATA
+    change_cipher_spec  = 20,
+    alert               = 21,
+    handshake           = 22,
+    application_data    = 23
 };
 
 //! HanshakeType values as defined in RFC5246
 enum HandshakeType {
-    hello_request = SSL3_MT_HELLO_REQUEST,
-    client_hello = SSL3_MT_CLIENT_HELLO,
-    server_hello = SSL3_MT_SERVER_HELLO,
-    certificate = SSL3_MT_CERTIFICATE,
-    certificate_request = SSL3_MT_CERTIFICATE_REQUEST,
-    server_hello_done = SSL3_MT_SERVER_DONE,
-    certificate_verify = SSL3_MT_CERTIFICATE_VERIFY,
-    client_key_exchange = SSL3_MT_CLIENT_KEY_EXCHANGE,
-    finished = SSL3_MT_FINISHED
+    hello_request       = GNUTLS_HANDSHAKE_HELLO_REQUEST,
+    client_hello        = GNUTLS_HANDSHAKE_CLIENT_HELLO,
+    server_hello        = GNUTLS_HANDSHAKE_SERVER_HELLO,
+    certificate         = GNUTLS_HANDSHAKE_CERTIFICATE_PKT,
+    certificate_request = GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST,
+    server_hello_done   = GNUTLS_HANDSHAKE_SERVER_HELLO_DONE,
+    certificate_verify  = GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY,
+    client_key_exchange = GNUTLS_HANDSHAKE_CLIENT_KEY_EXCHANGE,
+    finished            = GNUTLS_HANDSHAKE_FINISHED
 };
 
 //! ProtocolVersion header as defined in RFC5246
@@ -208,8 +209,8 @@ struct SSLConnection {
 
     SSL *ssl;
     SSL_CTX *ssl_ctx;
-    EVP_PKEY *server_private_key;
-    const EVP_CIPHER *ciph;
+    int ciph;
+    gnutls_privkey_t server_private_key;
     struct Random client_random;
     struct Random server_random;
     struct CipherSuite cipher_suite;
@@ -225,8 +226,8 @@ struct SSLConnection {
         uint8 server_write_IV[16];
     } key_material;
 
-    EVP_CIPHER_CTX client_cipher_ctx;
-    EVP_CIPHER_CTX server_cipher_ctx;
+    gcry_cipher_hd_t client_cipher_ctx;
+    gcry_cipher_hd_t server_cipher_ctx;
 
     struct SSLConnection *next;
 };
diff --git a/src/capture_tls.c b/src/capture_openssl.c
similarity index 99%
rename from src/capture_tls.c
rename to src/capture_openssl.c
index 3ba3455..17f2c31 100644
--- a/src/capture_tls.c
+++ b/src/capture_openssl.c
@@ -32,7 +32,7 @@
 
 #include <unistd.h>
 #include "capture.h"
-#include "capture_tls.h"
+#include "capture_openssl.h"
 #include "option.h"
 #include "util.h"
 #include "sip.h"
diff --git a/src/capture_tls.h b/src/capture_openssl.h
similarity index 100%
rename from src/capture_tls.h
rename to src/capture_openssl.h
diff --git a/src/capture_reasm.c b/src/capture_reasm.c
index ebc258e..3aea2fa 100644
--- a/src/capture_reasm.c
+++ b/src/capture_reasm.c
@@ -58,6 +58,8 @@ capture_packet_reasm_ip(capture_info_t *capinfo, const struct pcap_pkthdr *heade
     uint32_t ip_hl = 0;
     // Fragment offset
     uint16_t ip_off = 0;
+    // IP content len
+    uint16_t ip_len = 0;
     // Fragmentation flag
     uint16_t ip_frag = 0;
     // Fragmentation identifier
@@ -92,6 +94,7 @@ capture_packet_reasm_ip(capture_info_t *capinfo, const struct pcap_pkthdr *heade
             ip_hl = ip4->ip_hl * 4;
             ip_proto = ip4->ip_p;
             ip_off = ntohs(ip4->ip_off);
+            ip_len = ntohs(ip4->ip_len);
 
             ip_frag = ip_off & (IP_MF | IP_OFFMASK);
             ip_frag_off = (ip_frag) ? (ip_off & IP_OFFMASK) * 8 : 0;
@@ -104,6 +107,7 @@ capture_packet_reasm_ip(capture_info_t *capinfo, const struct pcap_pkthdr *heade
         case 6:
             ip_hl = sizeof(struct ip6_hdr);
             ip_proto = ip6->ip6_nxt;
+            ip_len = ntohs(ip6->ip6_ctlun.ip6_un1.ip6_un1_plen) + ip_hl;
 
             if (ip_proto == IPPROTO_FRAGMENT) {
                 struct ip6_frag *ip6f = (struct ip6_frag *) (ip6 + ip_hl);
@@ -120,7 +124,11 @@ capture_packet_reasm_ip(capture_info_t *capinfo, const struct pcap_pkthdr *heade
     }
 
     // Remove IP Header length from payload
-    *size = *caplen - capinfo->link_hl - ip_hl;
+    if (*caplen > capinfo->link_hl + ip_len) {
+        *size = ip_len - ip_hl;
+    } else {
+        *size = *caplen - capinfo->link_hl - ip_hl;
+    }
 
     // If no fragmentation
     if (ip_frag == 0) {
diff --git a/src/filter.c b/src/filter.c
index b460ebd..4dbae8b 100644
--- a/src/filter.c
+++ b/src/filter.c
@@ -45,7 +45,7 @@ filter_set(int type, const char *expr)
     if (expr) {
         const char *re_err = NULL;
         int32_t err_offset;
-        int32_t pcre_options = PCRE_UNGREEDY |  PCRE_CASELESS;
+        int32_t pcre_options = PCRE_UNGREEDY | PCRE_CASELESS;
 
         // Check if we have a valid expression
         if (!(regex = pcre_compile(expr, pcre_options, &re_err, &err_offset, 0)))
@@ -95,8 +95,10 @@ int
 filter_check_call(void *item)
 {
     int i;
-    char data[256];
+    char data[MAX_SIP_PAYLOAD];
     sip_call_t *call = (sip_call_t*) item;
+    sip_msg_t *msg;
+    vector_iter_t it;
 
     // Dont filter calls without messages
     if (call_msg_count(call) == 0)
@@ -135,6 +137,8 @@ filter_check_call(void *item)
             case FILTER_METHOD:
                 call_get_attribute(call, SIP_ATTR_METHOD, data);
                 break;
+            case FILTER_PAYLOAD:
+                break;
             case FILTER_CALL_LIST:
                 // FIXME Maybe call should know hot to calculate this line
                 call_list_line_text(ui_get_panel(ui_find_by_type(PANEL_CALL_LIST)), call, data);
@@ -144,26 +148,48 @@ filter_check_call(void *item)
                 return 0;
         }
 
-#ifdef WITH_PCRE
-        if (pcre_exec(filters[i].regex, 0, data, strlen(data), 0, 0, 0, 0)) {
-            // Mak as filtered
+        // For payload filtering, check all messages payload
+        if (i == FILTER_PAYLOAD) {
+            // Assume this call doesn't match the filter
             call->filtered = 1;
-            break;
-        }
-#else
-        // Call doesn't match this filter
-        if (regexec(&filters[i].regex, data, 0, NULL, 0)) {
-            // Mak as filtered
-            call->filtered = 1;
-            break;
+            // Create an iterator for the call messages
+            it = vector_iterator(call->msgs);
+            while ((msg = vector_iterator_next(&it))) {
+                // Copy message payload
+                strcpy(data, msg_get_payload(msg));
+                // Check if this payload matches the filter
+                if (filter_check_expr(filters[i], data) == 0) {
+                    call->filtered = 0;
+                    break;
+                }
+            }
+            if (call->filtered == 1)
+                break;
+        } else {
+            // Check the filter against given data
+            if (filter_check_expr(filters[i], data) != 0) {
+                // The data didn't matched the filter
+                call->filtered = 1;
+                break;
+            }
         }
-#endif
     }
 
     // Return the final filter status
     return (call->filtered == 0);
 }
 
+int
+filter_check_expr(filter_t filter, const char *data)
+{
+#ifdef WITH_PCRE
+        return pcre_exec(filter.regex, 0, data, strlen(data), 0, 0, 0, 0);
+#else
+        // Call doesn't match this filter
+        return regexec(&filter.regex, data, 0, NULL, 0);
+#endif
+}
+
 void
 filter_reset_calls()
 {
diff --git a/src/filter.h b/src/filter.h
index 44cf630..e31b23c 100644
--- a/src/filter.h
+++ b/src/filter.h
@@ -65,6 +65,8 @@ enum filter_type {
     FILTER_DESTINATION,
     //! SIP Method in packet payload
     FILTER_METHOD,
+    //! SIP Payload in any call packet
+    FILTER_PAYLOAD,
     //! Displayed line in call list
     FILTER_CALL_LIST,
     //! Number of available filter types
@@ -119,6 +121,14 @@ int
 filter_check_call(void *item);
 
 /**
+ * @brief Check if data matches the filter regexp
+ *
+ * @return 0 if the given data matches the filter
+ */
+int
+filter_check_expr(filter_t filter, const char *data);
+
+/**
  * @brief Reset filtered flag in all calls
  *
  * This function can be used to force reevaluation
diff --git a/src/keybinding.c b/src/keybinding.c
index 28732d8..c34fedf 100644
--- a/src/keybinding.c
+++ b/src/keybinding.c
@@ -36,145 +36,117 @@
 #include "keybinding.h"
 
 //! sngrep keybindings
-key_binding_t bindings[ACTION_SENTINEL];
-
-void
-key_bindings_init()
-{
-    // Initialize bindings structure
-    memset(bindings, 0, sizeof(key_binding_t) * ACTION_SENTINEL);
-
-    // Set default keybindings
-    key_bind_action(ACTION_UP, KEY_UP);
-    key_bind_action(ACTION_UP, 'j');
-    key_bind_action(ACTION_DOWN, KEY_DOWN);
-    key_bind_action(ACTION_DOWN, 'k');
-    key_bind_action(ACTION_LEFT, KEY_LEFT);
-    key_bind_action(ACTION_LEFT, 'h');
-    key_bind_action(ACTION_RIGHT, KEY_RIGHT);
-    key_bind_action(ACTION_RIGHT, 'l');
-    key_bind_action(ACTION_DELETE, KEY_DC);
-    key_bind_action(ACTION_BACKSPACE, KEY_BACKSPACE);
-    key_bind_action(ACTION_BACKSPACE, KEY_BACKSPACE2);
-    key_bind_action(ACTION_BACKSPACE, KEY_BACKSPACE3);
-    key_bind_action(ACTION_NPAGE, KEY_NPAGE);
-    key_bind_action(ACTION_NPAGE, KEY_CTRL('F'));
-    key_bind_action(ACTION_PPAGE, KEY_PPAGE);
-    key_bind_action(ACTION_PPAGE, KEY_CTRL('B'));
-    key_bind_action(ACTION_HNPAGE, KEY_CTRL('D'));
-    key_bind_action(ACTION_HPPAGE, KEY_CTRL('U'));
-    key_bind_action(ACTION_BEGIN, KEY_HOME);
-    key_bind_action(ACTION_BEGIN, KEY_CTRL('A'));
-    key_bind_action(ACTION_END, KEY_END);
-    key_bind_action(ACTION_END, KEY_CTRL('E'));
-    key_bind_action(ACTION_PREV_FIELD, KEY_UP);
-    key_bind_action(ACTION_NEXT_FIELD, KEY_TAB);
-    key_bind_action(ACTION_NEXT_FIELD, KEY_DOWN);
-    key_bind_action(ACTION_RESIZE_SCREEN, KEY_RESIZE);
-    key_bind_action(ACTION_CLEAR, KEY_CTRL('U'));
-    key_bind_action(ACTION_CLEAR, KEY_CTRL('W'));
-    key_bind_action(ACTION_CLEAR_CALLS, KEY_F(5));
-    key_bind_action(ACTION_TOGGLE_SYNTAX, KEY_F(8));
-    key_bind_action(ACTION_TOGGLE_SYNTAX, 'C');
-    key_bind_action(ACTION_CYCLE_COLOR, KEY_F(7));
-    key_bind_action(ACTION_CYCLE_COLOR, 'c');
-    key_bind_action(ACTION_SHOW_HOSTNAMES, KEY_F(9));
-    key_bind_action(ACTION_SHOW_ALIAS, 'a');
-    key_bind_action(ACTION_TOGGLE_PAUSE, 'p');
-    key_bind_action(ACTION_PREV_SCREEN, KEY_ESC);
-    key_bind_action(ACTION_PREV_SCREEN, 'q');
-    key_bind_action(ACTION_PREV_SCREEN, 'Q');
-    key_bind_action(ACTION_SHOW_HELP, KEY_F(1));
-    key_bind_action(ACTION_SHOW_HELP, 'h');
-    key_bind_action(ACTION_SHOW_HELP, 'H');
-    key_bind_action(ACTION_SHOW_HELP, '?');
-    key_bind_action(ACTION_SHOW_RAW, KEY_F(6));
-    key_bind_action(ACTION_SHOW_RAW, 'r');
-    key_bind_action(ACTION_SHOW_RAW, 'R');
-    key_bind_action(ACTION_SHOW_FLOW, KEY_INTRO);
-    key_bind_action(ACTION_SHOW_FLOW_EX, KEY_F(4));
-    key_bind_action(ACTION_SHOW_FLOW_EX, 'x');
-    key_bind_action(ACTION_SHOW_FLOW_EX, 'X');
-    key_bind_action(ACTION_SHOW_FILTERS, KEY_F(7));
-    key_bind_action(ACTION_SHOW_FILTERS, 'f');
-    key_bind_action(ACTION_SHOW_FILTERS, 'F');
-    key_bind_action(ACTION_SHOW_COLUMNS, KEY_F(10));
-    key_bind_action(ACTION_SHOW_COLUMNS, 't');
-    key_bind_action(ACTION_SHOW_COLUMNS, 'T');
-    key_bind_action(ACTION_SHOW_SETTINGS, KEY_F(8));
-    key_bind_action(ACTION_SHOW_SETTINGS, 'o');
-    key_bind_action(ACTION_SHOW_SETTINGS, 'O');
-    key_bind_action(ACTION_COLUMN_MOVE_UP, '-');
-    key_bind_action(ACTION_COLUMN_MOVE_DOWN, '+');
-    key_bind_action(ACTION_DISP_FILTER, KEY_F(3));
-    key_bind_action(ACTION_DISP_FILTER, '/');
-    key_bind_action(ACTION_DISP_FILTER, KEY_TAB);
-    key_bind_action(ACTION_DISP_INVITE, 'i');
-    key_bind_action(ACTION_DISP_INVITE, 'I');
-    key_bind_action(ACTION_SAVE, KEY_F(2));
-    key_bind_action(ACTION_SAVE, 's');
-    key_bind_action(ACTION_SAVE, 'S');
-    key_bind_action(ACTION_SELECT, KEY_SPACE);
-    key_bind_action(ACTION_CONFIRM, KEY_INTRO);
-    key_bind_action(ACTION_TOGGLE_RAW, 't');
-    key_bind_action(ACTION_TOGGLE_MEDIA, KEY_F(3));
-    key_bind_action(ACTION_TOGGLE_MEDIA, 'm');
-    key_bind_action(ACTION_INCREASE_RAW, '9');
-    key_bind_action(ACTION_DECREASE_RAW, '0');
-    key_bind_action(ACTION_RESET_RAW, 'T');
-    key_bind_action(ACTION_ONLY_SDP, 'D');
-    key_bind_action(ACTION_SDP_INFO, KEY_F(2));
-    key_bind_action(ACTION_SDP_INFO, 'd');
-    key_bind_action(ACTION_COMPRESS, KEY_F(5));
-    key_bind_action(ACTION_COMPRESS, 's');
-    key_bind_action(ACTION_TOGGLE_HINT, 'K');
-}
+key_binding_t bindings[ACTION_SENTINEL] = {
+   { ACTION_PRINTABLE,      "",             { }, 0 },
+   { ACTION_UP,             "up",           { KEY_UP, 'j' }, 2 },
+   { ACTION_DOWN,           "down",         { KEY_DOWN, 'k' }, 2 },
+   { ACTION_LEFT,           "left",         { KEY_LEFT, 'h' }, 2 },
+   { ACTION_RIGHT,          "right",        { KEY_RIGHT, 'l'}, 2 },
+   { ACTION_DELETE,         "delete",       { KEY_DC }, 1 },
+   { ACTION_BACKSPACE,      "backspace",    { KEY_BACKSPACE, KEY_BACKSPACE2, KEY_BACKSPACE3 }, 3 },
+   { ACTION_NPAGE,          "npage",        { KEY_NPAGE, KEY_CTRL('F') }, 2 },
+   { ACTION_PPAGE,          "ppage",        { KEY_PPAGE, KEY_CTRL('B') }, 2 },
+   { ACTION_HNPAGE,         "hnpage",       { KEY_CTRL('D') }, 1 },
+   { ACTION_HPPAGE,         "hppage",       { KEY_CTRL('U') }, 2 },
+   { ACTION_BEGIN,          "begin",        { KEY_HOME, KEY_CTRL('A') }, 2 },
+   { ACTION_END,            "end",          { KEY_END, KEY_CTRL('E') }, 2 },
+   { ACTION_PREV_FIELD,     "pfield",       { KEY_UP }, 1 },
+   { ACTION_NEXT_FIELD,     "nfield",       { KEY_DOWN, KEY_TAB }, 2 },
+   { ACTION_RESIZE_SCREEN,  "",             { KEY_RESIZE }, 1 },
+   { ACTION_CLEAR,          "clear",        { KEY_CTRL('U'), KEY_CTRL('W')}, 2 },
+   { ACTION_CLEAR_CALLS,    "clearcalls",   { KEY_F(5) }, 1 },
+   { ACTION_TOGGLE_SYNTAX,  "togglesyntax", { KEY_F(8), 'C' }, 2 },
+   { ACTION_CYCLE_COLOR,    "colormode",    { 'c' }, 1 },
+   { ACTION_SHOW_HOSTNAMES, "togglehostname", { KEY_F(9) }, 1 },
+   { ACTION_SHOW_ALIAS,     "togglealias",  { 'a' }, 1 },
+   { ACTION_TOGGLE_PAUSE,   "pause",        { 'p' }, 1 },
+   { ACTION_PREV_SCREEN,    "prevscreen",   { KEY_ESC, 'q', 'Q' }, 3 },
+   { ACTION_SHOW_HELP,      "help",         { KEY_F(1), 'h', 'H', '?' }, 4 },
+   { ACTION_SHOW_RAW,       "raw",          { KEY_F(6), 'R', 'r' }, 3 },
+   { ACTION_SHOW_FLOW,      "flow",         { KEY_INTRO }, 1 },
+   { ACTION_SHOW_FLOW_EX,   "flowex",       { KEY_F(4), 'x', 'X' }, 3 },
+   { ACTION_SHOW_FILTERS,   "filters",      { KEY_F(7), 'f', 'F' }, 3 },
+   { ACTION_SHOW_COLUMNS,   "columns",      { KEY_F(10), 't', 'T' }, 3 },
+   { ACTION_SHOW_SETTINGS,  "settings",     { KEY_F(8), 'o', 'O' }, 3 },
+   { ACTION_SHOW_STATS,     "stats",        { 'i' }, 1 },
+   { ACTION_COLUMN_MOVE_UP, "columnup",     { '-' }, 1 },
+   { ACTION_COLUMN_MOVE_DOWN, "columndown", { '+' }, 1 },
+   { ACTION_DISP_FILTER,    "search",       { KEY_F(3), '/', KEY_TAB }, 3 },
+   { ACTION_SAVE,           "save",         { KEY_F(2), 's', 'S'}, 3 },
+   { ACTION_SELECT,         "select",       { KEY_SPACE }, 1 },
+   { ACTION_CONFIRM,        "confirm",      { KEY_INTRO }, 1 },
+   { ACTION_TOGGLE_MEDIA,   "togglemedia",  { KEY_F(3), 'm' }, 2 },
+   { ACTION_TOGGLE_RAW,     "rawpreview",   { 't' }, 1 },
+   { ACTION_INCREASE_RAW,   "morerawpreview", { '9' }, 1 },
+   { ACTION_DECREASE_RAW,   "lessrawpreview", { '0' }, 1 },
+   { ACTION_RESET_RAW,      "resetrawpreview", { 'T' }, 1 },
+   { ACTION_ONLY_SDP,       "onlysdp",      { 'D' }, 1 },
+   { ACTION_SDP_INFO,       "sdpinfo",      { KEY_F(2), 'd' }, 2 },
+   { ACTION_COMPRESS,       "compress",     { 's' }, 1 },
+   { ACTION_TOGGLE_HINT,    "hintalt",      { 'K' }, 1 },
+};
 
 void
 key_bindings_dump()
 {
     int i, j;
     for (i = 1; i < ACTION_SENTINEL; i++) {
-
         for (j = 0; j < bindings[i].bindcnt; j++) {
-            printf("ActionID: %d\t Key: %d \t%s\n", i,
+            printf("ActionID: %d\t ActionName: %-21s Key: %d (%s)\n",
+                   bindings[i].id,
+                   bindings[i].name,
                    bindings[i].keys[j],
                    key_to_str(bindings[i].keys[j]));
         }
     }
 }
 
+key_binding_t *
+key_binding_data(int action)
+{
+    int i;
+    for (i = 1; i < ACTION_SENTINEL; i++) {
+        if (bindings[i].id == action)
+            return &bindings[i];
+    }
+
+    return NULL;
+}
+
 void
 key_bind_action(int action, int key)
 {
-    if (action < 0)
+    key_binding_t *bind;
+
+    if (!(bind = key_binding_data(action)))
         return;
 
-    if (bindings[action].bindcnt == MAX_BINDINGS)
+    if (bind->bindcnt == MAX_BINDINGS)
         return;
 
-    bindings[action].keys[bindings[action].bindcnt++] = key;
+    bind->keys[bind->bindcnt++] = key;
 }
 
 void
 key_unbind_action(int action, int key)
 {
-    key_binding_t bind;
+    key_binding_t tmp, *bind;
     int i;
 
     // Action is not valid
-    if (action < 0)
+    if (!(bind = key_binding_data(action)))
         return;
 
     // Copy binding to temporal struct
-    memcpy(&bind, &bindings[action], sizeof(key_binding_t));
+    memcpy(&tmp, bind, sizeof(key_binding_t));
+
     // Reset bindings for this action
-    memset(&bindings[action], 0, sizeof(key_binding_t));
+    memset(&bind->keys, 0, sizeof(int) * MAX_BINDINGS);
 
     // Add all bindings but the unbinded
-    for (i=0; i < bind.bindcnt; i++) {
-        if (bind.keys[i] != key) {
-            key_bind_action(action, bind.keys[i]);
+    for (i=0; i < tmp.bindcnt; i++) {
+        if (tmp.keys[i] != key) {
+            key_bind_action(action, tmp.keys[i]);
         }
     }
 }
@@ -190,7 +162,7 @@ key_find_action(int key, int start)
 
         for (j = 0; j < bindings[i].bindcnt; j++)
             if (bindings[i].keys[j] == key)
-                return i;
+                return bindings[i].id;
     }
     return -1;
 }
@@ -198,49 +170,12 @@ key_find_action(int key, int start)
 int
 key_action_id(const char *action)
 {
+    int i;
+    for (i = 1; i < ACTION_SENTINEL; i++) {
+        if (!strcasecmp(action, bindings[i].name))
+            return bindings[i].id;
 
-    if (!strcmp(action, "up")) return ACTION_UP;
-    if (!strcmp(action, "down")) return ACTION_DOWN;
-    if (!strcmp(action, "left")) return ACTION_LEFT;
-    if (!strcmp(action, "right")) return ACTION_RIGHT;
-    if (!strcmp(action, "delete")) return ACTION_DELETE;
-    if (!strcmp(action, "backspace")) return ACTION_BACKSPACE;
-    if (!strcmp(action, "npage")) return ACTION_NPAGE;
-    if (!strcmp(action, "ppage")) return ACTION_PPAGE;
-    if (!strcmp(action, "hnpage")) return ACTION_HNPAGE;
-    if (!strcmp(action, "hppage")) return ACTION_HPPAGE;
-    if (!strcmp(action, "begin")) return ACTION_BEGIN;
-    if (!strcmp(action, "end")) return ACTION_END;
-    if (!strcmp(action, "pfield")) return ACTION_PREV_FIELD;
-    if (!strcmp(action, "nfield")) return ACTION_NEXT_FIELD;
-    if (!strcmp(action, "clear")) return ACTION_CLEAR;
-    if (!strcmp(action, "clearcalls")) return ACTION_CLEAR_CALLS;
-    if (!strcmp(action, "togglesyntax")) return ACTION_TOGGLE_SYNTAX;
-    if (!strcmp(action, "colormode")) return ACTION_CYCLE_COLOR;
-    if (!strcmp(action, "togglehostname")) return ACTION_SHOW_HOSTNAMES;
-    if (!strcmp(action, "togglealias")) return ACTION_SHOW_ALIAS;
-    if (!strcmp(action, "pause")) return ACTION_TOGGLE_PAUSE;
-    if (!strcmp(action, "prevscreen")) return ACTION_PREV_SCREEN;
-    if (!strcmp(action, "help")) return ACTION_SHOW_HELP;
-    if (!strcmp(action, "raw")) return ACTION_SHOW_RAW;
-    if (!strcmp(action, "flow")) return ACTION_SHOW_FLOW;
-    if (!strcmp(action, "flowex")) return ACTION_SHOW_FLOW_EX;
-    if (!strcmp(action, "filters")) return ACTION_SHOW_FILTERS;
-    if (!strcmp(action, "columns")) return ACTION_SHOW_COLUMNS;
-    if (!strcmp(action, "columnup")) return ACTION_COLUMN_MOVE_UP;
-    if (!strcmp(action, "columndown")) return ACTION_COLUMN_MOVE_DOWN;
-    if (!strcmp(action, "search")) return ACTION_DISP_FILTER;
-    if (!strcmp(action, "save")) return ACTION_SAVE;
-    if (!strcmp(action, "select")) return ACTION_SELECT;
-    if (!strcmp(action, "confirm")) return ACTION_CONFIRM;
-    if (!strcmp(action, "rawpreview")) return ACTION_TOGGLE_RAW;
-    if (!strcmp(action, "morerawpreview")) return ACTION_INCREASE_RAW;
-    if (!strcmp(action, "lessrawpreview")) return ACTION_DECREASE_RAW;
-    if (!strcmp(action, "resetrawpreview")) return ACTION_RESET_RAW;
-    if (!strcmp(action, "onlysdp")) return ACTION_ONLY_SDP;
-    if (!strcmp(action, "sdpinfo")) return ACTION_SDP_INFO;
-    if (!strcmp(action, "compress")) return ACTION_COMPRESS;
-    if (!strcmp(action, "hintalt")) return ACTION_TOGGLE_HINT;
+    }
     return -1;
 }
 
@@ -323,11 +258,16 @@ key_from_str(const char *key)
 const char *
 key_action_key_str(int action)
 {
-    if (setting_enabled(SETTING_ALTKEY_HINT) && bindings[action].bindcnt > 1) {
+    key_binding_t *bind;
+
+    if (!(bind = key_binding_data(action)))
+        return NULL;
+
+    if (setting_enabled(SETTING_ALTKEY_HINT) && bind->bindcnt > 1) {
         // First alt keybinding
-        return key_to_str(bindings[action].keys[1]);
+        return key_to_str(bind->keys[1]);
     } else {
         // Default keybinding
-        return key_to_str(bindings[action].keys[0]);
+        return key_to_str(bind->keys[0]);
     }
 }
diff --git a/src/keybinding.h b/src/keybinding.h
index ffd6c92..9057e7d 100644
--- a/src/keybinding.h
+++ b/src/keybinding.h
@@ -87,10 +87,10 @@ enum key_actions {
     ACTION_SHOW_FILTERS,
     ACTION_SHOW_COLUMNS,
     ACTION_SHOW_SETTINGS,
+    ACTION_SHOW_STATS,
     ACTION_COLUMN_MOVE_UP,
     ACTION_COLUMN_MOVE_DOWN,
     ACTION_DISP_FILTER,
-    ACTION_DISP_INVITE,
     ACTION_SAVE,
     ACTION_SELECT,
     ACTION_CONFIRM,
@@ -113,6 +113,10 @@ typedef struct key_binding key_binding_t;
  * @brief Struct to hold a keybinding data
  */
 struct key_binding {
+    //! Keybinding action id
+    int id;
+    //! Keybinding action name
+    const char *name;
     //! keybindings for this action
     int keys[MAX_BINDINGS];
     //! How many keys are binded to this action
@@ -120,16 +124,17 @@ struct key_binding {
 };
 
 /**
- * @brief Initialize default keybindings
+ * @brief Print configured keybindigs
  */
 void
-key_bindings_init();
+key_bindings_dump();
 
 /**
- * @brief Print configured keybindigs
+ * @brief Return Keybinding data for a given action
+ * @return key_binding_t structure pointer or NULL if not found
  */
-void
-key_bindings_dump();
+key_binding_t *
+key_binding_data(int action);
 
 /**
  * @brief Bind a key to an action
diff --git a/src/main.c b/src/main.c
index ad10534..5120811 100644
--- a/src/main.c
+++ b/src/main.c
@@ -37,8 +37,11 @@
 #include "ui_manager.h"
 #include "capture.h"
 #include "capture_eep.h"
+#ifdef WITH_GNUTLS
+#include "capture_gnutls.h"
+#endif
 #ifdef WITH_OPENSSL
-#include "capture_tls.h"
+#include "capture_openssl.h"
 #endif
 
 /**
@@ -49,10 +52,13 @@
 void
 usage()
 {
-    printf("Usage: %s [-hVcivNq] [-IO pcap_dump] [-d dev] [-l limit]"
-#ifdef WITH_OPENSSL
+    printf("Usage: %s [-hVcivNqrD] [-IO pcap_dump] [-d dev] [-l limit]"
+#if defined(WITH_GNUTLS) || defined(WITH_OPENSSL)
            " [-k keyfile]"
 #endif
+#ifdef USE_EEP
+           " [-LH capture_url]"
+#endif
            " [<match expression>] [<bpf filter>]\n\n"
            "    -h --help\t\t This usage\n"
            "    -V --version\t Version information\n"
@@ -60,16 +66,18 @@ usage()
            "    -I --input\t\t Read captured data from pcap file\n"
            "    -O --output\t\t Write captured data to pcap file\n"
            "    -c --calls\t\t Only display dialogs starting with INVITE\n"
-           "    -r --rtp\t\t Capture full RTP packets\n"
+           "    -r --rtp\t\t Capture RTP packets payload\n"
            "    -l --limit\t\t Set capture limit to N dialogs\n"
            "    -i --icase\t\t Make <match expression> case insensitive\n"
            "    -v --invert\t\t Invert <match expression>\n"
            "    -N --no-interface\t Don't display sngrep interface, just capture\n"
+           "    -q --quiet\t\t Don't print captured dialogs in no interface mode\n"
            "    -D --dump-config\t Print active configuration settings and exit\n"
+#ifdef USE_EEP
            "    -H --eep-send\t Homer sipcapture url (udp:X.X.X.X:XXXX)\n"
            "    -L --eep-listen\t Listen for encapsulated packets (udp:X.X.X.X:XXXX)\n"
-           "    -q --quiet\t\t Don't print captured dialogs in no interface mode\n"
-#ifdef WITH_OPENSSL
+#endif
+#if defined(WITH_GNUTLS) || defined(WITH_OPENSSL)
            "    -k --keyfile\t RSA private keyfile to decrypt captured packets\n"
 #endif
            "\n",PACKAGE);
@@ -84,6 +92,9 @@ version()
            "This is free software: you are free to change and redistribute it.\n"
            "There is NO WARRANTY, to the extent permitted by law.\n"
 
+#ifdef WITH_GNUTLS
+           " * Compiled with GnuTLS support.\n"
+#endif
 #ifdef WITH_OPENSSL
            " * Compiled with OpenSSL support.\n"
 #endif
@@ -124,7 +135,9 @@ main(int argc, char* argv[])
         { "device", required_argument, 0, 'd' },
         { "input", required_argument, 0, 'I' },
         { "output", required_argument, 0, 'O' },
+#if defined(WITH_GNUTLS) || defined(WITH_OPENSSL)
         { "keyfile", required_argument, 0, 'k' },
+#endif
         { "calls", no_argument, 0, 'c' },
         { "rtp", no_argument, 0, 'r' },
         { "limit", no_argument, 0, 'l' },
@@ -132,8 +145,10 @@ main(int argc, char* argv[])
         { "invert", no_argument, 0, 'v' },
         { "no-interface", no_argument, 0, 'N' },
         { "dump-config", no_argument, 0, 'D' },
+#ifdef USE_EEP
         { "eep-listen", required_argument, 0, 'L' },
         { "eep-send", required_argument, 0, 'H' },
+#endif
         { "quiet", no_argument, 0, 'q' },
     };
 
@@ -175,9 +190,11 @@ main(int argc, char* argv[])
                     return 0;
                 }
                 break;
+#if defined(WITH_GNUTLS) || defined(WITH_OPENSSL)
             case 'k':
                 keyfile = optarg;
                 break;
+#endif
             case 'c':
                 only_calls = 1;
                 setting_set_value(SETTING_SIP_CALLS, SETTING_ON);
@@ -208,12 +225,14 @@ main(int argc, char* argv[])
             case 't':
             case 'W':
                 break;
+#ifdef USE_EEP
             case 'L':
                 capture_eep_set_server_url(optarg);
                 break;
             case 'H':
                 capture_eep_set_client_url(optarg);
                 break;
+#endif
             case '?':
                 if (strchr(options, optopt)) {
                     fprintf(stderr, "-%c option requires an argument.\n", optopt);
@@ -228,7 +247,7 @@ main(int argc, char* argv[])
         }
     }
 
-#ifdef WITH_OPENSSL
+#if defined(WITH_GNUTLS) || defined(WITH_OPENSSL)
     // Set capture decrypt key file
     capture_set_keyfile(keyfile);
     // Check if we have a keyfile and is valid
@@ -251,8 +270,10 @@ main(int argc, char* argv[])
     // Set capture options
     capture_init(limit, rtp_capture);
 
+#ifdef USE_EEP
     // Initialize EEP if enabled
     capture_eep_init();
+#endif
 
     // If we have an input file, load it
     if (vector_count(infiles)) {
diff --git a/src/option.c b/src/option.c
index 99d563a..a6ff340 100644
--- a/src/option.c
+++ b/src/option.c
@@ -65,9 +65,6 @@ init_options()
     set_option_value("cl.column6", "dst");
     set_option_value("cl.column7", "state");
 
-    // Initialize keybindings
-    key_bindings_init();
-
     // Read options from configuration files
     read_options("/etc/sngreprc");
     read_options("/usr/local/etc/sngreprc");
diff --git a/src/rtp.c b/src/rtp.c
index 0259a6a..3faa786 100644
--- a/src/rtp.c
+++ b/src/rtp.c
@@ -65,7 +65,7 @@ rtp_encoding_t encodings[] = {
 };
 
 rtp_stream_t *
-stream_create(sdp_media_t *media, const char *dst, u_short dport)
+stream_create(sdp_media_t *media, const char *dst, u_short dport, int type)
 {
     rtp_stream_t *stream;
 
@@ -74,6 +74,7 @@ stream_create(sdp_media_t *media, const char *dst, u_short dport)
         return NULL;
 
     // Initialize all fields
+    stream->type = type;
     stream->media = media;
     strcpy(stream->ip_dst, dst);
     stream->dport = dport;
@@ -82,15 +83,20 @@ stream_create(sdp_media_t *media, const char *dst, u_short dport)
 }
 
 rtp_stream_t *
-stream_complete(rtp_stream_t *stream, const char *src, u_short sport, u_int format)
+stream_complete(rtp_stream_t *stream, const char *src, u_short sport)
 {
     strcpy(stream->ip_src, src);
     stream->sport = sport;
-    stream->fmtcode = format;
     return stream;
 }
 
 void
+stream_set_format(rtp_stream_t *stream, uint32_t format)
+{
+    stream->rtpinfo.fmtcode = format;
+}
+
+void
 stream_add_packet(rtp_stream_t *stream, capture_packet_t *packet)
 {
     if (stream->pktcnt == 0)
@@ -99,7 +105,7 @@ stream_add_packet(rtp_stream_t *stream, capture_packet_t *packet)
     stream->pktcnt++;
 }
 
-int
+uint32_t
 stream_get_count(rtp_stream_t *stream)
 {
     return stream->pktcnt;
@@ -123,11 +129,11 @@ stream_get_format(rtp_stream_t *stream)
         return NULL;
 
     // Try to get standard format form code
-    if ((fmt = rtp_get_standard_format(stream->fmtcode)))
+    if ((fmt = rtp_get_standard_format(stream->rtpinfo.fmtcode)))
         return fmt;
 
     // Try to get format form SDP payload
-    if ((fmt = media_get_format(stream->media, stream->fmtcode)))
+    if ((fmt = media_get_format(stream->media, stream->rtpinfo.fmtcode)))
         return fmt;
 
     // Not found format for this code
@@ -155,9 +161,15 @@ rtp_check_packet(capture_packet_t *packet)
     u_short sport, dport;
     rtp_stream_t *stream;
     rtp_stream_t *reverse;
-    u_int format;
+    u_char format = 0;
     u_char *payload;
-    uint32_t size;
+    uint32_t size, bsize;
+    uint16_t len;
+    struct rtcp_hdr_generic hdr;
+    struct rtcp_hdr_sr hdr_sr;
+    struct rtcp_hdr_xr hdr_xr;
+    struct rtcp_blk_xr blk_xr;
+    struct rtcp_blk_xr_voip blk_xr_voip;
 
     // Get packet data
     payload = capture_packet_get_payload(packet);
@@ -170,42 +182,121 @@ rtp_check_packet(capture_packet_t *packet)
     dport = packet->dport;
 
     // Check if we have at least RTP type
-    if (size < 2)
+    if ((int32_t) size < 2)
         return NULL;
 
     // Check RTP version
     if (RTP_VERSION(*payload) != RTP_VERSION_RFC1889)
         return NULL;
 
-    // Get RTP payload type
-    format = RTP_PAYLOAD_TYPE(*(++payload));
+    // RTP: even, RTCP: odd
+    if (((packet->dport % 2) == 0)) {
+
+        // Get RTP payload type
+        format = RTP_PAYLOAD_TYPE(*(payload + 1));
 
-    // Find the matching stream
-    stream = rtp_find_stream(src, sport, dst, dport, format);
+        // Find the matching stream
+        stream = rtp_find_stream(src, sport, dst, dport, format);
+
+        // Check if a valid stream has been found
+        if (!stream)
+            return NULL;
 
-    // A valid stream has been found
-    if (stream) {
         // We have found a stream, but with different format
-        if (stream->pktcnt && stream->fmtcode != format) {
+        if (stream_is_complete(stream) && stream->rtpinfo.fmtcode != format) {
             // Create a new stream for this new format
-            stream = stream_create(stream->media, dst, dport);
-            stream_complete(stream, src, sport, format);
+            stream = stream_create(stream->media, dst, dport, CAPTURE_PACKET_RTP);
+            stream_complete(stream, src, sport);
+            stream_set_format(stream, format);
             call_add_stream(msg_get_call(stream->media->msg), stream);
         }
 
         // First packet for this stream, set source data
-        if (stream->pktcnt == 0) {
-            stream_complete(stream, src, sport, format);
+        if (!(stream_is_complete(stream))) {
+            stream_complete(stream, src, sport);
+            stream_set_format(stream, format);
             // Check if an stream in the opposite direction exists
-            if (!(reverse = rtp_find_call_stream(stream->media->msg->call, stream->ip_dst, stream->dport,  stream->ip_src,  stream->sport, stream->fmtcode))) {
-                reverse = stream_create(stream->media, stream->ip_src, stream->sport);
-                stream_complete(reverse, stream->ip_dst, stream->dport, format);
+            if (!(reverse = rtp_find_call_stream(stream->media->msg->call, stream->ip_dst, stream->dport,  stream->ip_src,  stream->sport))) {
+                reverse = stream_create(stream->media, stream->ip_src, stream->sport, CAPTURE_PACKET_RTP);
+                stream_complete(reverse, stream->ip_dst, stream->dport);
+                stream_set_format(reverse, format);
                 call_add_stream(msg_get_call(stream->media->msg), reverse);
             }
         }
 
         // Add packet to stream
         stream_add_packet(stream, packet);
+    } else {
+        // Find the matching stream
+        if ((stream = rtp_find_stream(src, sport, dst, dport, format))) {
+
+            // Parse all packet payload headers
+            while ((int32_t) size > 0) {
+
+                // Check we have at least rtcp generic info
+                if (size < sizeof(struct rtcp_hdr_generic))
+                    break;
+
+                memcpy(&hdr, payload, sizeof(hdr));
+
+                // Check RTP version
+                if (RTP_VERSION(hdr.version) != RTP_VERSION_RFC1889)
+                    break;
+
+                // Header length
+                len = ntohs(hdr.len) * 4 + 4;
+
+                // Check RTCP packet header typ
+                switch (hdr.type) {
+                    case RTCP_HDR_SR:
+                        // Get Sender Report header
+                        memcpy(&hdr_sr, payload, sizeof(hdr_sr));
+                        stream->rtcpinfo.spc = ntohl(hdr_sr.spc);
+                        break;
+                    case RTCP_HDR_RR:
+                    case RTCP_HDR_SDES:
+                    case RTCP_HDR_BYE:
+                    case RTCP_HDR_APP:
+                    case RTCP_RTPFB:
+                    case RTCP_PSFB:
+                        break;
+                    case RTCP_XR:
+                        // Get Sender Report Extended header
+                        memcpy(&hdr_xr, payload, sizeof(hdr_xr));
+                        bsize = sizeof(hdr_xr);
+
+                        // Read all report blocks
+                        while (bsize < ntohs(hdr_xr.len) * 4 + 4) {
+                            // Read block header
+                            memcpy(&blk_xr, payload + bsize, sizeof(blk_xr));
+                            // Check block type
+                            switch (blk_xr.type) {
+                                case RTCP_XR_VOIP_METRCS:
+                                    memcpy(&blk_xr_voip, payload + sizeof(hdr_xr), sizeof(blk_xr_voip));
+                                    stream->rtcpinfo.fdiscard = blk_xr_voip.drate;
+                                    stream->rtcpinfo.flost = blk_xr_voip.lrate;
+                                    stream->rtcpinfo.mosl = blk_xr_voip.moslq;
+                                    stream->rtcpinfo.mosc = blk_xr_voip.moscq;
+                                    break;
+                                default: break;
+                            }
+                            bsize += ntohs(blk_xr.len) * 4 + 4;
+                        }
+                        break;
+                    case RTCP_AVB:
+                    case RTCP_RSI:
+                    case RTCP_TOKEN:
+                    default:
+                        break;
+                }
+                payload += len;
+                size -= len;
+            }
+
+            // Add packet to stream
+            stream_complete(stream, src, sport);
+            stream_add_packet(stream, packet);
+        }
     }
 
     return stream;
@@ -222,11 +313,12 @@ rtp_find_stream(const char *src, u_short sport, const char *dst, u_short dport,
     vector_iter_t calls;
 
     // Get active calls (during conversation)
-    calls = sip_active_calls_iterator();
+    calls = sip_calls_iterator();
+    //vector_iterator_set_current(&calls, vector_iterator_count(&calls) - 1);
 
     while ((call = vector_iterator_next(&calls))) {
         // Check if this call has an RTP stream for current packet data
-        if ((stream = rtp_find_call_stream(call, src, sport, dst, dport, format))) {
+        if ((stream = rtp_find_call_stream(call, src, sport, dst, dport))) {
             return stream;
         }
     }
@@ -235,44 +327,35 @@ rtp_find_stream(const char *src, u_short sport, const char *dst, u_short dport,
 }
 
 rtp_stream_t *
-rtp_find_call_stream(struct sip_call *call, const char *ip_src, u_short sport, const char *ip_dst, u_short dport, u_int format)
+rtp_find_call_stream(struct sip_call *call, const char *ip_src, u_short sport, const char *ip_dst, u_short dport)
 {
-    rtp_stream_t *stream, *ret = NULL;
+    rtp_stream_t *stream;
     vector_iter_t it;
 
+    // Create an iterator for call streams
     it = vector_iterator(call->streams);
 
-    // Try to look for a complete stream with this format
-    while ((stream = vector_iterator_next(&it))) {
-        if (!strcmp(ip_src, stream->ip_src) && sport == stream->sport &&
-            !strcmp(ip_dst, stream->ip_dst) && dport == stream->dport &&
-            stream->fmtcode == format && stream->pktcnt) {
-            ret = stream;
+    // Look for an incomplete stream with this destination
+    vector_iterator_set_last(&it);
+    while ((stream = vector_iterator_prev(&it))) {
+        if (!strcmp(ip_dst, stream->ip_dst) && dport == stream->dport && !stream->pktcnt) {
+            return stream;
         }
     }
 
-    // Try to look for a complete stream with any format
-    if (!ret) {
-        vector_iterator_reset(&it);
-        while ((stream = vector_iterator_next(&it))) {
+    // Try to look for an incomplete stream with this destination
+    if (ip_src && sport) {
+        vector_iterator_set_last(&it);
+        while ((stream = vector_iterator_prev(&it))) {
             if (!strcmp(ip_src, stream->ip_src) && sport == stream->sport &&
                 !strcmp(ip_dst, stream->ip_dst) && dport == stream->dport) {
-                ret = stream;
-            }
-        }
-    }
-
-    // Try to look for an incomplete stream with this destination
-    if (!ret) {
-        vector_iterator_reset(&it);
-        while ((stream = vector_iterator_next(&it))) {
-            if (!strcmp(ip_dst, stream->ip_dst) && dport == stream->dport && !stream->pktcnt) {
-                ret = stream;
+                return stream;
             }
         }
     }
 
-    return ret;
+    // Nothing found
+    return NULL;
 }
 
 int
@@ -285,3 +368,9 @@ stream_is_older(rtp_stream_t *one, rtp_stream_t *two)
     // Otherwise
     return timeval_is_older(one->time, two->time);
 }
+
+int
+stream_is_complete(rtp_stream_t *stream)
+{
+    return (stream->pktcnt != 0);
+}
diff --git a/src/rtp.h b/src/rtp.h
index 15be2e9..b4295fc 100644
--- a/src/rtp.h
+++ b/src/rtp.h
@@ -44,6 +44,40 @@
 // Handled RTP versions
 #define RTP_VERSION_RFC1889 2
 
+// RTCP header types
+//! http://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml
+enum rtcp_header_types
+{
+    RTCP_HDR_SR = 200,
+    RTCP_HDR_RR,
+    RTCP_HDR_SDES,
+    RTCP_HDR_BYE,
+    RTCP_HDR_APP,
+    RTCP_RTPFB,
+    RTCP_PSFB,
+    RTCP_XR,
+    RTCP_AVB,
+    RTCP_RSI,
+    RTCP_TOKEN,
+};
+
+//! http://www.iana.org/assignments/rtcp-xr-block-types/rtcp-xr-block-types.xhtml
+enum rtcp_xr_block_types
+{
+    RTCP_XR_LOSS_RLE = 1,
+    RTCP_XR_DUP_RLE,
+    RTCP_XR_PKT_RXTIMES,
+    RTCP_XR_REF_TIME,
+    RTCP_XR_DLRR,
+    RTCP_XR_STATS_SUMRY,
+    RTCP_XR_VOIP_METRCS,
+    RTCP_XR_BT_XNQ,
+    RTCP_XR_TI_VOIP,
+    RTCP_XR_PR_LOSS_RLE,
+    RTCP_XR_MC_ACQ,
+    RTCP_XR_IDMS
+};
+
 //! Shorter declaration of rtp_encoding structure
 typedef struct rtp_encoding rtp_encoding_t;
 //! Shorter declaration of rtp_stream structure
@@ -56,32 +90,193 @@ struct rtp_encoding {
 };
 
 struct rtp_stream {
+    //! Determine stream type
+    uint32_t type;
     //! Source address and port
     char ip_src[ADDRESSLEN];
     u_short sport;
     //! Destination address and port
     char ip_dst[ADDRESSLEN];
     u_short dport;
-    //! Format of first received packet of stre
-    u_int fmtcode;
-    //! Time of first received packet of stream
-    struct timeval time;
-    //! Packet count for this stream
-    int pktcnt;
     //! SDP media that setup this stream
     sdp_media_t *media;
+    //! Packet count for this stream
+    uint32_t pktcnt;
+    //! Time of first received packet of stream
+    struct timeval time;
+
+    // Stream information (depending on type)
+    union {
+        struct {
+            //! Format of first received packet of stre
+            uint32_t fmtcode;
+        } rtpinfo;
+        struct {
+            //! Sender packet count
+            uint32_t spc;
+            //! Fraction lost x/256
+            uint8_t flost;
+            //! uint8_t discarded x/256
+            uint8_t fdiscard;
+            //! MOS - listening Quality
+            uint8_t mosl;
+            //! MOS - Conversational Quality
+            uint8_t mosc;
+        } rtcpinfo;
+    };
+};
+
+struct rtcp_hdr_generic
+{
+    //! version (V): 2 bits
+    uint8_t version;
+    //! packet type (PT): 8 bits
+    uint8_t type;
+    //! length: 16 bits
+    uint16_t len;
+};
+
+struct rtcp_hdr_sr
+{
+    //! version (V): 2 bits
+    uint8_t version:2;
+    //! padding (P): 1 bit
+    uint8_t padding:1;
+    //! reception report count (RC): 5 bits
+    uint8_t rcount:5;
+    //! packet type (PT): 8 bits
+    uint8_t type;
+    //! length: 16 bits
+    uint16_t len;
+    //! SSRC: 32 bits
+    uint32_t ssrc;
+    //! NTP timestamp: 64 bits
+    uint64_t ntpts;
+    //! RTP timestamp: 32 bits
+    uint32_t rtpts;
+    //! sender's packet count: 32 bits
+    uint32_t spc;
+    //! sender's octet count: 32 bits
+    uint32_t soc;
+};
+
+struct rtcp_blk_sr
+{
+    //! SSRC_n (source identifier): 32 bits
+    uint32_t ssrc;
+    //! fraction lost: 8 bits
+    uint8_t flost;
+    //! cumulative number of packets lost: 24 bits
+    struct {
+        uint8_t pl1;
+        uint8_t pl2;
+        uint8_t pl3;
+    } plost;
+    //! extended highest sequence number received: 32 bits
+    uint32_t hseq;
+    //! interarrival jitter: 32 bits
+    uint32_t ijitter;
+};
+
+struct rtcp_hdr_xr
+{
+    //! version (V): 2 bits
+    uint8_t version:2;
+    //! padding (P): 1 bit
+    uint8_t padding:1;
+    //! reserved: 5 bits
+    uint8_t reserved:5;
+    //! packet type (PT): 8 bits
+    uint8_t type;
+    //! length: 16 bits
+    uint16_t len;
+    //! SSRC: 32 bits
+    uint32_t ssrc;
+};
+
+struct rtcp_blk_xr
+{
+    //! block type (BT): 8 bits
+    uint8_t type;
+    //! type-specific: 8 bits
+    uint8_t specific;
+    //! length: 16 bits
+    uint16_t len;
+};
+
+struct rtcp_blk_xr_voip
+{
+    //! block type (BT): 8 bits
+    uint8_t type;
+    //! type-specific: 8 bits
+    uint8_t reserved;
+    //! length: 16 bits
+    uint16_t len;
+    //! SSRC: 32 bits
+    uint32_t ssrc;
+    //! loss rate: 8 bits
+    uint8_t lrate;
+    //! discard rate: 8 bits
+    uint8_t drate;
+    //! burst density: 8 bits
+    uint8_t bdens;
+    //! gap density: 8 bits
+    uint8_t gdens;
+    //! burst duration: 16 bits
+    uint16_t bdur;
+    //! gap duration: 16 bits
+    uint16_t gdur;
+    //! round trip delay: 16 bits
+    uint16_t rtd;
+    //! end system delay: 16 bits
+    uint16_t esd;
+    //! signal level: 8 bits
+    uint8_t slevel;
+    //! noise level: 8 bits
+    uint8_t nlevel;
+    //! residual echo return loss (RERL): 8 bits
+    uint8_t rerl;
+    //! Gmin: 8 bits
+    uint8_t gmin;
+    //! R factor: 8 bits
+    uint8_t rfactor;
+    //! ext. R factor: 8 bits
+    uint8_t xrfactor;
+    //! MOS-LQ: 8 bits
+    uint8_t moslq;
+    //! MOS-CQ: 8 bits
+    uint8_t moscq;
+    //! receiver configuration byte (RX config): 8 bits
+    uint8_t rxc;
+    //! packet loss concealment (PLC): 2 bits
+    uint8_t plc:2;
+    //! jitter buffer adaptive (JBA): 2 bits
+    uint8_t jba:2;
+    //! jitter buffer rate (JB rate): 4 bits
+    uint8_t jbrate:4;
+    //! reserved: 8 bits
+    uint8_t reserved2;
+    //! jitter buffer nominal delay (JB nominal): 16 bits
+    uint16_t jbndelay;
+    //! jitter buffer maximum delay (JB maximum): 16 bits
+    uint16_t jbmdelay;
+    //! jitter buffer absolute maximum delay (JB abs max): 16 bits
+    uint16_t jbadelay;
 };
 
 rtp_stream_t *
-stream_create(sdp_media_t *media, const char *dst, u_short dport);
+stream_create(sdp_media_t *media, const char *dst, u_short dport, int type);
 
 rtp_stream_t *
-stream_complete(rtp_stream_t *stream, const char *src, u_short sport, u_int format);
+stream_complete(rtp_stream_t *stream, const char *src, u_short sport);
+
+void
+stream_set_format(rtp_stream_t *stream, uint32_t format);
 
 void
 stream_add_packet(rtp_stream_t *stream, capture_packet_t *packet);
 
-int
+uint32_t
 stream_get_count(rtp_stream_t *stream);
 
 struct sip_call *
@@ -100,7 +295,7 @@ rtp_stream_t *
 rtp_find_stream(const char *ip_src, u_short sport, const char *ip_dst, u_short dport, u_int format);
 
 rtp_stream_t *
-rtp_find_call_stream(struct sip_call *call, const char *ip_src, u_short sport, const char *ip_dst, u_short dport, u_int format);
+rtp_find_call_stream(struct sip_call *call, const char *ip_src, u_short sport, const char *ip_dst, u_short dport);
 
 /**
  * @brief Check if a message is older than other
@@ -113,4 +308,7 @@ rtp_find_call_stream(struct sip_call *call, const char *ip_src, u_short sport, c
 int
 stream_is_older(rtp_stream_t *one, rtp_stream_t *two);
 
+int
+stream_is_complete(rtp_stream_t *stream);
+
 #endif /* __SNGREP_RTP_H */
diff --git a/src/sip.c b/src/sip.c
index 6f75b8e..9a72153 100644
--- a/src/sip.c
+++ b/src/sip.c
@@ -524,6 +524,7 @@ sip_parse_msg_media(sip_msg_t *msg, const u_char *payload)
     u_int media_fmt_code;
     sdp_media_t *media = NULL;
     char *payload2, *tofree, *line;
+    sip_call_t *call = msg_get_call(msg);
 
     // Initialize variables
     memset(address, 0, sizeof(address));
@@ -543,13 +544,18 @@ sip_parse_msg_media(sip_msg_t *msg, const u_char *payload)
                     msg_add_media(msg, media);
 
                     /**
-                     * From SDP we can only guess destination address port. RTP Capture process
+                     * From SDP we can only guess destination address port. RTP Capture proccess
                      * will determine when the stream has been completed, getting source address
                      * and port of the stream.
                      */
                     // Create a new stream with this destination address:port
                     if (!call_msg_is_retrans(msg)) {
-                        call_add_stream(msg_get_call(msg), stream_create(media, media_address, media_port));
+                        if (!rtp_find_call_stream(call, 0, 0, media_address, media_port)) {
+                            // Create RTP stream
+                            call_add_stream(call, stream_create(media, media_address, media_port, CAPTURE_PACKET_RTP));
+                            // Create early RTCP stream
+                            call_add_stream(call, stream_create(media, media_address, media_port + 1, CAPTURE_PACKET_RTCP));
+                        }
                     }
                 }
             }
@@ -569,6 +575,17 @@ sip_parse_msg_media(sip_msg_t *msg, const u_char *payload)
             }
         }
 
+        // Check if we have attribute format RTCP port
+        if (!strncmp(line, "a=rtcp:", 7)) {
+            if (media && sscanf(line, "a=rtcp:%u", &media_port)) {
+                // Create early RTCP stream
+                if (!rtp_find_call_stream(call, 0, 0, media_address, media_port)) {
+                    call_add_stream(call, stream_create(media, media_address, media_port, CAPTURE_PACKET_RTCP));
+                }
+            }
+        }
+
+
     }
     sng_free(tofree);
 }
@@ -703,7 +720,7 @@ sip_address_format(const char *address)
     // Return address formatted depending on active settings
     if (setting_enabled(SETTING_DISPLAY_ALIAS)) {
         return get_alias_value(address);
-    } else if (setting_enabled(SETTING_DISPLAY_ALIAS)) {
+    } else if (setting_enabled(SETTING_DISPLAY_HOST)) {
         return lookup_hostname(address);
     } else {
         return address;
@@ -724,3 +741,18 @@ sip_address_port_format(const char *addrport)
 
     return aport;
 }
+
+const char *
+sip_address_strip_port(char *addrport)
+{
+    char *colon;
+
+    if (!addrport)
+        return NULL;
+
+    // FIXME Make compatible with IPv6
+    if ((colon = strchr(addrport, ':')))
+        *colon = '\0';
+
+    return addrport;
+}
diff --git a/src/sip.h b/src/sip.h
index b11bade..ccfacdf 100644
--- a/src/sip.h
+++ b/src/sip.h
@@ -379,5 +379,11 @@ sip_address_format(const char *address);
 const char *
 sip_address_port_format(const char *address);
 
+/**
+ * @brief Remove port from an address
+ */
+const char *
+sip_address_strip_port(char *addrport);
+
 
 #endif
diff --git a/src/ui_call_flow.c b/src/ui_call_flow.c
index a319065..794ce51 100644
--- a/src/ui_call_flow.c
+++ b/src/ui_call_flow.c
@@ -36,6 +36,7 @@
 #include "ui_msg_diff.h"
 #include "util.h"
 #include "vector.h"
+#include "option.h"
 
 /***
  *
@@ -189,7 +190,10 @@ call_flow_draw(PANEL *panel)
             if (!call_flow_draw_message(panel, arrow, cline))
                 break;
         } else if (arrow->type == CF_ARROW_RTP) {
-            if (!call_flow_draw_stream(panel, arrow, cline))
+            if (!call_flow_draw_rtp_stream(panel, arrow, cline))
+                break;
+        } else if (arrow->type == CF_ARROW_RTCP) {
+            if (!call_flow_draw_rtcp_stream(panel, arrow, cline))
                 break;
         }
         cline += arrow->height;
@@ -197,7 +201,18 @@ call_flow_draw(PANEL *panel)
 
     // If there are only three columns, then draw the raw message on this panel
     if (setting_enabled(SETTING_CF_FORCERAW)) {
-        call_flow_draw_raw(panel, call_flow_arrow_message(info->cur_arrow));
+        switch (info->cur_arrow->type) {
+            case CF_ARROW_RTP:
+                call_flow_draw_raw(panel, info->cur_arrow->stream->media->msg);
+                break;
+            case CF_ARROW_SIP:
+                call_flow_draw_raw(panel, info->cur_arrow->msg);
+                break;
+            case CF_ARROW_RTCP:
+                call_flow_draw_raw_rtcp(panel, info->cur_arrow->stream);
+                break;
+        }
+
     }
 
     // Draw the scrollbar
@@ -498,16 +513,17 @@ call_flow_draw_message(PANEL *panel, call_flow_arrow_t *arrow, int cline)
 
 
 call_flow_arrow_t *
-call_flow_draw_stream(PANEL *panel, call_flow_arrow_t *arrow, int cline)
+call_flow_draw_rtp_stream(PANEL *panel, call_flow_arrow_t *arrow, int cline)
 {
     call_flow_info_t *info;
     WINDOW *win;
-    char codec[50], time[20];
+    char text[50], time[20];
     int height, width;
     const char *callid;
     char msg_dst[80], msg_src[80];
     call_flow_column_t *column1, *column2;
     rtp_stream_t *stream = arrow->stream;
+    int arrow_dir = 0; /* 0: right, 1: left */
 
     // Get panel information
     info = call_flow_info(panel);
@@ -530,7 +546,7 @@ call_flow_draw_stream(PANEL *panel, call_flow_arrow_t *arrow, int cline)
     mvwprintw(win, cline, 2, "%s", time);
 
     // Get Message method (include extra info)
-    sprintf(codec, "RTP (%s) %d", stream_get_format(stream), stream_get_count(stream));
+    sprintf(text, "RTP (%s) %d", stream_get_format(stream), stream_get_count(stream));
 
     // Get message data
     msg_get_attribute(stream->media->msg, SIP_ATTR_SRC, msg_src);
@@ -562,11 +578,12 @@ call_flow_draw_stream(PANEL *panel, call_flow_arrow_t *arrow, int cline)
         tmp = column1;
         column1 = column2;
         column2 = tmp;
+        arrow_dir = 1; /* swap arrow direction */
     }
 
     int startpos = 20 + 30 * column1->colpos;
     int endpos = 20 + 30 * column2->colpos;
-    int distance = abs(endpos - startpos) - 4;
+    int distance = abs(endpos - startpos) - 4 + 1;
 
     // Highlight current message
     if (arrow == info->cur_arrow) {
@@ -585,14 +602,154 @@ call_flow_draw_stream(PANEL *panel, call_flow_arrow_t *arrow, int cline)
     // Clear the line
     mvwprintw(win, cline, startpos + 2, "%*s", distance, "");
     // Draw method
-    mvwprintw(win, cline++, startpos + (distance) / 2 - strlen(codec) / 2 + 2, "%s", codec);
+    mvwprintw(win, cline, startpos + (distance) / 2 - strlen(text) / 2 + 2, "%s", text);
+
+    if (!setting_has_value(SETTING_CF_SDP_INFO, "compressed"))
+        cline++;
 
     // Draw line between columns
     mvwhline(win, cline, startpos + 2, ACS_HLINE, distance);
-    // Write the arrow at the end of the message (two arros if this is a retrans)
-    if (call_flow_column_get(panel, 0, stream->ip_src) == column1) {
-        mvwprintw(win, cline, startpos - 5, "%d", stream->sport);
-        mvwprintw(win, cline, endpos + 1, "%d", stream->dport);
+    // Write the arrow at the end of the message (two arrows if this is a retrans)
+    if (arrow_dir == 0 /* right */) {
+        if (!setting_has_value(SETTING_CF_SDP_INFO, "compressed")) {
+            mvwprintw(win, cline, startpos - 5, "%d", stream->sport);
+            mvwprintw(win, cline, endpos + 1, "%d", stream->dport);
+        }
+        if (distance > 0)
+            mvwaddch(win, cline, endpos - 2, '>');
+        else
+            mvwaddch(win, cline, endpos, '>');
+        if (arrow->rtp_count != stream_get_count(stream)) {
+            arrow->rtp_count = stream_get_count(stream);
+            arrow->rtp_ind_pos = (arrow->rtp_ind_pos + 1) % distance;
+            mvwaddch(win, cline, startpos + arrow->rtp_ind_pos + 2, '>');
+        }
+    } else {
+        if (!setting_has_value(SETTING_CF_SDP_INFO, "compressed")) {
+            mvwprintw(win, cline, endpos  + 1, "%d", stream->sport);
+            mvwprintw(win, cline, startpos - 5, "%d", stream->dport);
+        }
+        if (distance > 0)
+            mvwaddch(win, cline, startpos + 2, '<');
+        else
+            mvwaddch(win, cline, startpos, '<');
+        if (arrow->rtp_count != stream_get_count(stream)) {
+            arrow->rtp_count = stream_get_count(stream);
+            arrow->rtp_ind_pos = (arrow->rtp_ind_pos + 1) % distance;
+            mvwaddch(win, cline, endpos - arrow->rtp_ind_pos - 2, '<');
+        }
+    }
+
+    if (setting_has_value(SETTING_CF_SDP_INFO, "compressed"))
+        mvwprintw(win, cline, startpos + (distance) / 2 - strlen(text) / 2 + 2, " %s ", text);
+
+    wattroff(win, A_BOLD | A_REVERSE);
+
+    return arrow;
+}
+
+call_flow_arrow_t *
+call_flow_draw_rtcp_stream(PANEL *panel, call_flow_arrow_t *arrow, int cline)
+{
+    call_flow_info_t *info;
+    WINDOW *win;
+    char text[50], time[20];
+    int height, width;
+    const char *callid;
+    char msg_dst[80], msg_src[80];
+    call_flow_column_t *column1, *column2;
+    rtp_stream_t *stream = arrow->stream;
+    int arrow_dir = 0; /* 0: right, 1: left */
+
+    // Get panel information
+    info = call_flow_info(panel);
+    // Get the messages window
+    win = info->flow_win;
+    getmaxyx(win, height, width);
+
+    // Store arrow start line
+    arrow->line = cline;
+
+    // Calculate how many lines this message requires
+    arrow->height = call_flow_arrow_height(panel, arrow);
+
+    // Check this media fits on the panel
+    if (cline > height + arrow->height)
+        return NULL;
+
+    // Print timestamp
+    timeval_to_time(stream->time, time);
+    mvwprintw(win, cline, 2, "%s", time);
+
+    // Arrow text
+    sprintf(text, "RTCP (%.1f) %d", (float) stream->rtcpinfo.mosc / 10, stream_get_count(stream));
+
+    // Get message data
+    msg_get_attribute(stream->media->msg, SIP_ATTR_SRC, msg_src);
+    msg_get_attribute(stream->media->msg, SIP_ATTR_DST, msg_dst);
+    callid = stream->media->msg->call->callid;
+
+    // Get origin column for this stream.
+    // If we share the same Address from its setup SIP packet, use that column instead.
+    if (!strncmp(stream->ip_src, msg_src, strlen(stream->ip_src))) {
+        column1 = call_flow_column_get(panel, callid, msg_src);
+    } else if (!strncmp(stream->ip_src, msg_dst, strlen(stream->ip_src))) {
+        column1 = call_flow_column_get(panel, callid, msg_dst);
+    } else {
+        column1 = call_flow_column_get(panel, 0, stream->ip_src);
+    }
+
+    // Get destination column for this stream.
+    // If we share the same Address from its setup SIP packet, use that column instead.
+    if (!strncmp(stream->ip_dst, msg_dst, strlen(stream->ip_dst))) {
+        column2 = call_flow_column_get(panel, callid, msg_dst);
+    } else if (!strncmp(stream->ip_dst, msg_src, strlen(stream->ip_dst))) {
+        column2 = call_flow_column_get(panel, callid, msg_src);
+    } else {
+        column2 = call_flow_column_get(panel, 0, stream->ip_dst);
+    }
+
+    call_flow_column_t *tmp;
+    if (column1->colpos > column2->colpos) {
+        tmp = column1;
+        column1 = column2;
+        column2 = tmp;
+        arrow_dir = 1; /* swap arrow direction */
+    }
+
+    int startpos = 20 + 30 * column1->colpos;
+    int endpos = 20 + 30 * column2->colpos;
+    int distance = abs(endpos - startpos) - 4 + 1;
+
+    // Highlight current message
+    if (arrow == info->cur_arrow) {
+        if (setting_has_value(SETTING_CF_HIGHTLIGHT, "reverse")) {
+            wattron(win, A_REVERSE);
+        }
+        if (setting_has_value(SETTING_CF_HIGHTLIGHT, "bold")) {
+            wattron(win, A_BOLD);
+        }
+        if (setting_has_value(SETTING_CF_HIGHTLIGHT, "reversebold")) {
+            wattron(win, A_REVERSE);
+            wattron(win, A_BOLD);
+        }
+    }
+
+    // Clear the line
+    mvwprintw(win, cline, startpos + 2, "%*s", distance, "");
+    // Draw method
+    mvwprintw(win, cline, startpos + (distance) / 2 - strlen(text) / 2 + 2, "%s", text);
+    if (!setting_has_value(SETTING_CF_SDP_INFO, "compressed"))
+        cline++;
+
+    // Draw line between columns
+    mvwhline(win, cline, startpos + 2, '-', distance);
+    // Write the arrow at the end of the message (two arrows if this is a retrans)
+    if (arrow_dir == 0 /* right */) {
+        if (!setting_has_value(SETTING_CF_SDP_INFO, "compressed")) {
+            mvwprintw(win, cline, startpos - 5, "%d", stream->sport);
+            mvwprintw(win, cline, endpos + 1, "%d", stream->dport);
+        }
         if (distance > 0)
             mvwaddch(win, cline, endpos - 2, '>');
         else
@@ -603,8 +760,10 @@ call_flow_draw_stream(PANEL *panel, call_flow_arrow_t *arrow, int cline)
             mvwaddch(win, cline, startpos + arrow->rtp_ind_pos + 2, '>');
         }
     } else {
-        mvwprintw(win, cline, endpos  + 1, "%d", stream->sport);
-        mvwprintw(win, cline, startpos - 5, "%d", stream->dport);
+        if (!setting_has_value(SETTING_CF_SDP_INFO, "compressed")) {
+            mvwprintw(win, cline, endpos  + 1, "%d", stream->sport);
+            mvwprintw(win, cline, startpos - 5, "%d", stream->dport);
+        }
         if (distance > 0)
             mvwaddch(win, cline, startpos + 2, '<');
         else
@@ -616,6 +775,9 @@ call_flow_draw_stream(PANEL *panel, call_flow_arrow_t *arrow, int cline)
         }
     }
 
+    if (setting_has_value(SETTING_CF_SDP_INFO, "compressed"))
+        mvwprintw(win, cline, startpos + (distance) / 2 - strlen(text) / 2 + 2, " %s ", text);
+
     wattroff(win, A_BOLD | A_REVERSE);
 
     return arrow;
@@ -642,7 +804,7 @@ call_flow_next_arrow(PANEL *panel, const call_flow_arrow_t *cur)
         memset(&cur_time, 0, sizeof(struct timeval));
     } else if (cur->type == CF_ARROW_SIP) {
         cur_time = msg_get_time(cur->msg);
-    } else if (cur->type == CF_ARROW_RTP) {
+    } else if (cur->type == CF_ARROW_RTP || cur->type == CF_ARROW_RTCP) {
         cur_time = cur->stream->time;
     }
 
@@ -669,7 +831,7 @@ call_flow_next_arrow(PANEL *panel, const call_flow_arrow_t *cur)
     if (!msg) {
         // Create a new arrow to store next info
         next = sng_malloc(sizeof(call_flow_arrow_t));
-        next->type = CF_ARROW_RTP;
+        next->type = (stream->type == CAPTURE_PACKET_RTP) ? CF_ARROW_RTP : CF_ARROW_RTCP;
         next->stream = stream;
     } else if (!stream) {
         /* a sip message goes next */
@@ -682,7 +844,7 @@ call_flow_next_arrow(PANEL *panel, const call_flow_arrow_t *cur)
         if (timeval_is_older(msg_get_time(msg), stream->time)) {
             // Create a new arrow to store next info
             next = sng_malloc(sizeof(call_flow_arrow_t));
-            next->type = CF_ARROW_RTP;
+            next->type = (stream->type == CAPTURE_PACKET_RTP) ? CF_ARROW_RTP : CF_ARROW_RTCP;
             next->stream = stream;
         } else {
             // Create a new arrow to store next info
@@ -731,7 +893,9 @@ call_flow_arrow_height(PANEL *panel, const call_flow_arrow_t *arrow)
             return 2;
         if (setting_has_value(SETTING_CF_SDP_INFO, "full"))
             return msg_media_count(arrow->msg) + 2;
-    } else if (arrow->type == CF_ARROW_RTP) {
+    } else if (arrow->type == CF_ARROW_RTP || arrow->type == CF_ARROW_RTCP) {
+        if (setting_has_value(SETTING_CF_SDP_INFO, "compressed"))
+            return 1;
         return 2;
     }
 
@@ -766,7 +930,7 @@ call_flow_arrow_message(const  call_flow_arrow_t *arrow)
         return NULL;
     if (arrow->type == CF_ARROW_SIP)
         return arrow->msg;
-    if (arrow->type == CF_ARROW_RTP)
+    if (arrow->type == CF_ARROW_RTP || arrow->type == CF_ARROW_RTCP)
         return arrow->stream->media->msg;
     return NULL;
 }
@@ -836,6 +1000,78 @@ call_flow_draw_raw(PANEL *panel, sip_msg_t *msg)
     return 0;
 }
 
+
+int
+call_flow_draw_raw_rtcp(PANEL *panel, rtp_stream_t *stream)
+{
+    call_flow_info_t *info;
+    WINDOW *win, *raw_win;
+    int raw_width, raw_height, height, width;
+    int min_raw_width, fixed_raw_width;
+
+    // Get panel information
+    if (!(info = call_flow_info(panel)))
+        return 1;
+
+    // Get window of main panel
+    win = panel_window(panel);
+    getmaxyx(win, height, width);
+
+    // Get min raw width
+    min_raw_width = setting_get_intvalue(SETTING_CF_RAWMINWIDTH);
+    fixed_raw_width = setting_get_intvalue(SETTING_CF_RAWFIXEDWIDTH);
+
+    // Calculate the raw data width (width - used columns for flow - vertical lines)
+    raw_width = width - (30 * vector_count(info->columns)) - 2;
+    // We can define a mininum size for rawminwidth
+    if (raw_width < min_raw_width) {
+        raw_width = min_raw_width;
+    }
+    // We can configure an exact raw size
+    if (fixed_raw_width > 0) {
+        raw_width = fixed_raw_width;
+    }
+
+    // Height of raw window is always available size minus 6 lines for header/footer
+    raw_height = height - 3;
+
+    // If we already have a raw window
+    raw_win = info->raw_win;
+    if (raw_win) {
+        // Check it has the correct size
+        if (getmaxx(raw_win) != raw_width) {
+            // We need a new raw window
+            delwin(raw_win);
+            info->raw_win = raw_win = newwin(raw_height, raw_width, 0, 0);
+        } else {
+            // We have a valid raw win, clear its content
+            werase(raw_win);
+        }
+    } else {
+        // Create the raw window of required size
+        info->raw_win = raw_win = newwin(raw_height, raw_width, 0, 0);
+    }
+
+    // Draw raw box lines
+    wattron(win, COLOR_PAIR(CP_BLUE_ON_DEF));
+    mvwvline(win, 1, width - raw_width - 2, ACS_VLINE, height - 2);
+    wattroff(win, COLOR_PAIR(CP_BLUE_ON_DEF));
+
+    mvwprintw(raw_win, 0, 0, "============ RTCP Information ============");
+    mvwprintw(raw_win, 2, 0, "Sender's packet count: %d", stream->rtcpinfo.spc);
+    mvwprintw(raw_win, 3, 0, "Fraction Lost: %d / 256", stream->rtcpinfo.flost);
+    mvwprintw(raw_win, 4, 0, "Fraction discarded: %d / 256", stream->rtcpinfo.fdiscard);
+    mvwprintw(raw_win, 6, 0, "MOS - Listening Quality: %.1f", (float) stream->rtcpinfo.mosl / 10);
+    mvwprintw(raw_win, 7, 0, "MOS - Conversational Quality: %.1f", (float) stream->rtcpinfo.mosc / 10);
+
+
+
+    // Copy the raw_win contents into the panel
+    copywin(raw_win, win, 0, 0, 1, width - raw_width - 1, raw_height, width - 2, 0);
+
+    return 0;
+}
+
 int
 call_flow_handle_key(PANEL *panel, int key)
 {
@@ -1003,7 +1239,7 @@ call_flow_help(PANEL *panel)
     int height, width;
 
     // Create a new panel and show centered
-    height = 26;
+    height = 27;
     width = 65;
     help_win = newwin(height, width, (LINES - height) / 2, (COLS - width) / 2);
 
@@ -1040,7 +1276,7 @@ call_flow_help(PANEL *panel)
     mvwprintw(help_win, 10, 2, "Enter       Show current message Raw");
     mvwprintw(help_win, 11, 2, "F1/h        Show this screen");
     mvwprintw(help_win, 12, 2, "F2/d        Toggle SDP Address:Port info");
-    mvwprintw(help_win, 13, 2, "F3/t        Toggle raw preview display");
+    mvwprintw(help_win, 13, 2, "F3/m        Toggle RTP arrows display");
     mvwprintw(help_win, 14, 2, "F4/X        Show call-flow with X-CID/X-Call-ID dialog");
     mvwprintw(help_win, 15, 2, "F5/s        Toggle compressed view (One address <=> one column");
     mvwprintw(help_win, 16, 2, "F6/R        Show original call messages in raw mode");
@@ -1048,8 +1284,10 @@ call_flow_help(PANEL *panel)
     mvwprintw(help_win, 18, 2, "F8/C        Turn on/off message syntax highlighting");
     mvwprintw(help_win, 19, 2, "F9/l        Turn on/off resolved addresses");
     mvwprintw(help_win, 20, 2, "9/0         Increase/Decrease raw preview size");
-    mvwprintw(help_win, 21, 2, "T           Restore raw preview size");
-    mvwprintw(help_win, 22, 2, "D           Only show SDP messages");
+    mvwprintw(help_win, 21, 2, "t           Toggle raw preview display");
+    mvwprintw(help_win, 22, 2, "T           Restore raw preview size");
+    mvwprintw(help_win, 23, 2, "D           Only show SDP messages");
+
 
     // Press any key to close
     wgetch(help_win);
@@ -1082,18 +1320,30 @@ call_flow_set_group(sip_call_group_t *group)
 }
 
 void
-call_flow_column_add(PANEL *panel, const char *callid, const char *addr)
+call_flow_column_add(PANEL *panel, const char *callid, const char *address)
 {
     call_flow_info_t *info;
     call_flow_column_t *column;
     vector_iter_t columns;
+    char addr[ADDRESSLEN + 6];
 
     if (!(info = call_flow_info(panel)))
         return;
 
-    if (!addr || !strlen(addr))
+    if (!address || !strlen(address))
         return;
 
+    // Coppy address to local var
+    strcpy(addr, address);
+
+    // when compressed view is enabled
+    if (setting_enabled(SETTING_CF_SPLITCALLID)) {
+        // Remove the port from the address
+        sip_address_strip_port(addr);
+        // Display the alias value of the address
+        strcpy(addr, get_alias_value(addr));
+    }
+
     if (call_flow_column_get(panel, callid, addr))
         return;
 
@@ -1113,18 +1363,31 @@ call_flow_column_add(PANEL *panel, const char *callid, const char *addr)
 }
 
 call_flow_column_t *
-call_flow_column_get(PANEL *panel, const char *callid, const char *addr)
+call_flow_column_get(PANEL *panel, const char *callid, const char *address)
 {
     call_flow_info_t *info;
     call_flow_column_t *column;
     vector_iter_t columns;
     int match_port;
     char coladdr[ADDRESSLEN + 6];
+    char addr[ADDRESSLEN + 6];
     char *dots;
 
     if (!(info = call_flow_info(panel)))
         return NULL;
 
+    // Coppy address to local var
+    strcpy(addr, address);
+
+    // when compressed view is enabled
+    if (setting_enabled(SETTING_CF_SPLITCALLID)) {
+        // Remove the port from the address
+        sip_address_strip_port(addr);
+        // Display the alias value of the address
+        strcpy(addr, get_alias_value(addr));
+    }
+
+
     // Look for address or address:port ?
     match_port = (strchr(addr, ':') != NULL);
 
diff --git a/src/ui_call_flow.h b/src/ui_call_flow.h
index 91e8d9c..7595484 100644
--- a/src/ui_call_flow.h
+++ b/src/ui_call_flow.h
@@ -71,6 +71,7 @@ typedef struct call_flow_arrow call_flow_arrow_t;
 enum call_flow_arrow_type {
     CF_ARROW_SIP,
     CF_ARROW_RTP,
+    CF_ARROW_RTCP,
 };
 
 /**
@@ -227,7 +228,20 @@ call_flow_draw_message(PANEL *panel, call_flow_arrow_t *arrow, int cline);
  * @return the arrow passed as parameter
  */
 call_flow_arrow_t *
-call_flow_draw_stream(PANEL *panel, call_flow_arrow_t *arrow, int cline);
+call_flow_draw_rtp_stream(PANEL *panel, call_flow_arrow_t *arrow, int cline);
+
+/**
+ * @brief Draw the RTCP stream data in the given line
+ *
+ * Draw the given arrow of type stream in the given line.
+ *
+ * @param panel Ncurses panel pointer
+ * @param arrow Call flow arrow to be drawn
+ * @param cline Window line to draw the message
+ * @return the arrow passed as parameter
+ */
+call_flow_arrow_t *
+call_flow_draw_rtcp_stream(PANEL *panel, call_flow_arrow_t *arrow, int cline);
 
 /**
  * @brief Get the next chronological arrow
@@ -306,6 +320,18 @@ int
 call_flow_draw_raw(PANEL *panel, sip_msg_t *msg);
 
 /**
+ * @brief Draw raw panel with RTCP data
+ *
+ * Draw the given stream data into the raw window.
+ *
+ * @param panel Ncurses panel pointer
+ * @param rtcp stream containing the RTCP conection data
+ * @return 0 in all cases
+ */
+int
+call_flow_draw_raw_rtcp(PANEL *panel, rtp_stream_t *rtcp);
+
+/**
  * @brief Handle Call flow extended key strokes
  *
  * This function will manage the custom keybindings of the panel. If this
diff --git a/src/ui_call_list.c b/src/ui_call_list.c
index 1e5b2b4..7b17c96 100644
--- a/src/ui_call_list.c
+++ b/src/ui_call_list.c
@@ -582,17 +582,13 @@ call_list_handle_key(PANEL *panel, int key)
             case ACTION_SHOW_COLUMNS:
                 ui_create_panel(PANEL_COLUMN_SELECT);
                 break;
+            case ACTION_SHOW_STATS:
+                ui_create_panel(PANEL_STATS);
+                break;
             case ACTION_SAVE:
                 next_panel = ui_create_panel(PANEL_SAVE);
                 save_set_group(ui_get_panel(next_panel), info->group);
                 break;
-            case ACTION_DISP_INVITE:
-                // Set Display filter text
-                set_field_buffer(info->fields[FLD_LIST_FILTER], 0, "invite");
-                filter_set(FILTER_CALL_LIST, "invite");
-                call_list_clear(panel);
-                filter_reset_calls();
-                break;
             case ACTION_CLEAR:
                 // Clear group calls
                 vector_clear(info->group->calls);
diff --git a/src/ui_filter.c b/src/ui_filter.c
index 7ab1fe3..085ec3d 100644
--- a/src/ui_filter.c
+++ b/src/ui_filter.c
@@ -56,7 +56,7 @@ filter_create()
     const char *method;
 
     // Calculate window dimensions
-    height = 15;
+    height = 16;
     width = 50;
 
     // Cerate a new indow for the panel and form
@@ -76,13 +76,14 @@ filter_create()
     info->fields[FLD_FILTER_SIPTO] = new_field(1, 28, 4, 18, 0, 0);
     info->fields[FLD_FILTER_SRC] = new_field(1, 18, 5, 18, 0, 0);
     info->fields[FLD_FILTER_DST] = new_field(1, 18, 6, 18, 0, 0);
-    info->fields[FLD_FILTER_REGISTER] = new_field(1, 1, 8, 15, 0, 0);
-    info->fields[FLD_FILTER_INVITE] = new_field(1, 1, 9, 15, 0, 0);
-    info->fields[FLD_FILTER_SUBSCRIBE] = new_field(1, 1, 10, 15, 0, 0);
-    info->fields[FLD_FILTER_NOTIFY] = new_field(1, 1, 11, 15, 0, 0);
-    info->fields[FLD_FILTER_OPTIONS] = new_field(1, 1, 8, 37, 0, 0);
-    info->fields[FLD_FILTER_PUBLISH] = new_field(1, 1, 9, 37, 0, 0);
-    info->fields[FLD_FILTER_MESSAGE] = new_field(1, 1, 10, 37, 0, 0);
+    info->fields[FLD_FILTER_PAYLOAD] = new_field(1, 28, 7, 18, 0, 0);
+    info->fields[FLD_FILTER_REGISTER] = new_field(1, 1, 9, 15, 0, 0);
+    info->fields[FLD_FILTER_INVITE] = new_field(1, 1, 10, 15, 0, 0);
+    info->fields[FLD_FILTER_SUBSCRIBE] = new_field(1, 1, 11, 15, 0, 0);
+    info->fields[FLD_FILTER_NOTIFY] = new_field(1, 1, 12, 15, 0, 0);
+    info->fields[FLD_FILTER_OPTIONS] = new_field(1, 1, 9, 37, 0, 0);
+    info->fields[FLD_FILTER_PUBLISH] = new_field(1, 1, 10, 37, 0, 0);
+    info->fields[FLD_FILTER_MESSAGE] = new_field(1, 1, 11, 37, 0, 0);
     info->fields[FLD_FILTER_FILTER] = new_field(1, 10, height - 2, 11, 0, 0);
     info->fields[FLD_FILTER_CANCEL] = new_field(1, 10, height - 2, 30, 0, 0);
     info->fields[FLD_FILTER_COUNT] = NULL;
@@ -92,6 +93,7 @@ filter_create()
     field_opts_off(info->fields[FLD_FILTER_SIPTO], O_AUTOSKIP);
     field_opts_off(info->fields[FLD_FILTER_SRC], O_AUTOSKIP);
     field_opts_off(info->fields[FLD_FILTER_DST], O_AUTOSKIP);
+    field_opts_off(info->fields[FLD_FILTER_PAYLOAD], O_AUTOSKIP);
     field_opts_off(info->fields[FLD_FILTER_REGISTER], O_AUTOSKIP);
     field_opts_off(info->fields[FLD_FILTER_INVITE], O_AUTOSKIP);
     field_opts_off(info->fields[FLD_FILTER_SUBSCRIBE], O_AUTOSKIP);
@@ -107,6 +109,7 @@ filter_create()
     set_field_back(info->fields[FLD_FILTER_SIPTO], A_UNDERLINE);
     set_field_back(info->fields[FLD_FILTER_SRC], A_UNDERLINE);
     set_field_back(info->fields[FLD_FILTER_DST], A_UNDERLINE);
+    set_field_back(info->fields[FLD_FILTER_PAYLOAD], A_UNDERLINE);
 
     // Create the form and post it
     info->form = new_form(info->fields);
@@ -118,13 +121,14 @@ filter_create()
     mvwprintw(win, 4, 3, "SIP To:");
     mvwprintw(win, 5, 3, "Source:");
     mvwprintw(win, 6, 3, "Destination:");
-    mvwprintw(win, 8, 3, "REGISTER   [ ]");
-    mvwprintw(win, 9, 3, "INVITE     [ ]");
-    mvwprintw(win, 10, 3, "SUBSCRIBE  [ ]");
-    mvwprintw(win, 11, 3, "NOTIFY     [ ]");
-    mvwprintw(win, 8, 25, "OPTIONS    [ ]");
-    mvwprintw(win, 9, 25, "PUBLISH    [ ]");
-    mvwprintw(win, 10, 25, "MESSAGE    [ ]");
+    mvwprintw(win, 7, 3, "Payload:");
+    mvwprintw(win, 9, 3, "REGISTER   [ ]");
+    mvwprintw(win, 10, 3, "INVITE     [ ]");
+    mvwprintw(win, 11, 3, "SUBSCRIBE  [ ]");
+    mvwprintw(win, 12, 3, "NOTIFY     [ ]");
+    mvwprintw(win, 9, 25, "OPTIONS    [ ]");
+    mvwprintw(win, 10, 25, "PUBLISH    [ ]");
+    mvwprintw(win, 11, 25, "MESSAGE    [ ]");
 
     // Get Method filter
     if (!(method = filter_get(FILTER_METHOD)))
@@ -135,6 +139,7 @@ filter_create()
     set_field_buffer(info->fields[FLD_FILTER_SIPTO], 0, filter_get(FILTER_SIPTO));
     set_field_buffer(info->fields[FLD_FILTER_SRC], 0, filter_get(FILTER_SOURCE));
     set_field_buffer(info->fields[FLD_FILTER_DST], 0, filter_get(FILTER_DESTINATION));
+    set_field_buffer(info->fields[FLD_FILTER_PAYLOAD], 0, filter_get(FILTER_PAYLOAD));
     set_field_buffer(info->fields[FLD_FILTER_REGISTER], 0,
                      strstr(method, sip_method_str(SIP_METHOD_REGISTER)) ? "*" : "");
     set_field_buffer(info->fields[FLD_FILTER_INVITE], 0,
@@ -156,9 +161,9 @@ filter_create()
     mvwprintw(win, 1, 18, "Filter options");
     wattron(win, COLOR_PAIR(CP_BLUE_ON_DEF));
     title_foot_box(panel);
-    mvwhline(win, 7, 1, ACS_HLINE, 49);
-    mvwaddch(win, 7, 0, ACS_LTEE);
-    mvwaddch(win, 7, 49, ACS_RTEE);
+    mvwhline(win, 8, 1, ACS_HLINE, 49);
+    mvwaddch(win, 8, 0, ACS_LTEE);
+    mvwaddch(win, 8, 49, ACS_RTEE);
     wattroff(win, COLOR_PAIR(CP_BLUE_ON_DEF));
 
     // Set default cursor position
@@ -199,7 +204,8 @@ filter_handle_key(PANEL *panel, int key)
     // We trim spaces with sscanf because and empty field is stored as
     // space characters
     memset(field_value, 0, sizeof(field_value));
-    sscanf(field_buffer(current_field(info->form), 0), "%[^ ]", field_value);
+    strcpy(field_value, field_buffer(current_field(info->form), 0));
+    strtrim(field_value);
 
     // Check actions for this key
     while ((action = key_find_action(key, action)) != ERR) {
@@ -208,7 +214,8 @@ filter_handle_key(PANEL *panel, int key)
             case ACTION_PRINTABLE:
                 // If this is a normal character on input field, print it
                 if (field_idx == FLD_FILTER_SIPFROM || field_idx == FLD_FILTER_SIPTO
-                    || field_idx == FLD_FILTER_SRC || field_idx == FLD_FILTER_DST) {
+                    || field_idx == FLD_FILTER_SRC || field_idx == FLD_FILTER_DST
+                    || field_idx == FLD_FILTER_PAYLOAD) {
                     form_driver(info->form, key);
                     break;
                 }
@@ -319,7 +326,8 @@ filter_save_options(PANEL *panel)
         // We trim spaces with sscanf because and empty field is stored as
         // space characters
         memset(field_value, 0, sizeof(field_value));
-        sscanf(field_buffer(info->fields[field_id], 0), "%[^ ]", field_value);
+        strcpy(field_value, field_buffer(info->fields[field_id], 0));
+        strtrim(field_value);
 
         // Set filter expression
         expr = strlen(field_value) ? field_value : NULL;
@@ -337,6 +345,9 @@ filter_save_options(PANEL *panel)
             case FLD_FILTER_DST:
                 filter_set(FILTER_DESTINATION, expr);
                 break;
+            case FLD_FILTER_PAYLOAD:
+                filter_set(FILTER_PAYLOAD, expr);
+                break;
             case FLD_FILTER_REGISTER:
             case FLD_FILTER_INVITE:
             case FLD_FILTER_SUBSCRIBE:
diff --git a/src/ui_filter.h b/src/ui_filter.h
index 74c9382..8cd26a1 100644
--- a/src/ui_filter.h
+++ b/src/ui_filter.h
@@ -48,6 +48,7 @@ enum filter_field_list {
     FLD_FILTER_SIPTO,
     FLD_FILTER_SRC,
     FLD_FILTER_DST,
+    FLD_FILTER_PAYLOAD,
     FLD_FILTER_REGISTER,
     FLD_FILTER_INVITE,
     FLD_FILTER_SUBSCRIBE,
diff --git a/src/ui_manager.c b/src/ui_manager.c
index d315bc0..06afa26 100644
--- a/src/ui_manager.c
+++ b/src/ui_manager.c
@@ -59,7 +59,8 @@ static ui_t *panel_pool[] = {
     &ui_save,
     &ui_msg_diff,
     &ui_column_select,
-    &ui_settings
+    &ui_settings,
+    &ui_stats
 };
 
 int
diff --git a/src/ui_manager.h b/src/ui_manager.h
index 55ef1ae..49cc04a 100644
--- a/src/ui_manager.h
+++ b/src/ui_manager.h
@@ -136,6 +136,8 @@ enum panel_types {
     PANEL_COLUMN_SELECT,
     //! Settings panel
     PANEL_SETTINGS,
+    //! Stats panel
+    PANEL_STATS,
     //! Panel Counter
     PANEL_COUNT,
 };
@@ -151,6 +153,7 @@ extern ui_t ui_save;
 extern ui_t ui_msg_diff;
 extern ui_t ui_column_select;
 extern ui_t ui_settings;
+extern ui_t ui_stats;
 
 /**
  * @brief Initialize ncurses mode
diff --git a/src/ui_settings.c b/src/ui_settings.c
index b503dbe..2938ca9 100644
--- a/src/ui_settings.c
+++ b/src/ui_settings.c
@@ -447,7 +447,8 @@ ui_settings_update_settings(PANEL *panel)
         if ((entry = ui_settings_is_entry(info->fields[i]))) {
             // Get field value.
             memset(field_value, 0, sizeof(field_value));
-            sscanf(field_buffer(info->fields[i], 0), "%[^ ]", field_value);
+            strcpy(field_value, field_buffer(info->fields[i], 0));
+            strtrim(field_value);
             // Change setting value
             setting_set_value(entry->setting_id, field_value);
         }
@@ -508,7 +509,8 @@ ui_settings_save(PANEL *panel)
         if ((entry = ui_settings_is_entry(info->fields[i]))) {
             // Get field value.
             memset(field_value, 0, sizeof(field_value));
-            sscanf(field_buffer(info->fields[i], 0), "%[^ ]", field_value);
+            strcpy(field_value, field_buffer(info->fields[i], 0));
+            strtrim(field_value);
 
             // Change setting value
             fprintf(fo, "set %s %s\n", setting_name(entry->setting_id), field_value);
diff --git a/src/ui_stats.c b/src/ui_stats.c
new file mode 100644
index 0000000..4037eee
--- /dev/null
+++ b/src/ui_stats.c
@@ -0,0 +1,222 @@
+/**************************************************************************
+ **
+ ** sngrep - SIP Messages flow viewer
+ **
+ ** Copyright (C) 2014 Ivan Alonso (Kaian)
+ ** Copyright (C) 2014 Irontec SL. All rights reserved.
+ **
+ ** This program 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.
+ **
+ ** This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ **
+ ****************************************************************************/
+/**
+ * @file ui_stats.c
+ * @author Ivan Alonso [aka Kaian] <kaian at irontec.com>
+ *
+ * @brief Source of functions defined in ui_stats.h
+ */
+/*
+ * +---------------------------------------------------------+
+ * |                    Stats Information                    |
+ * +---------------------------------------------------------+
+ * |  Dialogs: 725                  COMPLETED:  7 (22.1%)    |
+ * |  Calls: 10                     CANCELLED:  2 (12.2%)    |
+ * |  Messages: 200                 IN CALL:    10 (60.5%)   |
+ * |                                REJECTED:   0 (0.0%)     |
+ * |                                CALL SETUP: 0 (0.0%)     |
+ * +---------------------------------------------------------+
+ * |  INVITE:    10 (0.5%)          1XX: 123 (1.5%)          |
+ * |  REGISTER:  200 (5.1%)         2XX: 231 (3.1%)          |
+ * |  SUBSCRIBE: 20 (1.0%)          3XX: 0 (0.0%)            |
+ * |  UPDATE:    30 (1.3%)          4XX: 12 (1.5%)           |
+ * |  NOTIFY:    650 (22.7%)        5XX: 0 (0.0%)            |
+ * |  OPTIONS:   750 (27.4%)        6XX: 3 (0.5%)            |
+ * |  PUBLISH:   0 (0.0%)           7XX: 0 (0.0%)            |
+ * |  MESSAGE:   0 (0.0%)           8XX: 0 (0.0%)            |
+ * |  INFO:      0 (0.0%)                                    |
+ * |  ACK:       300 (7.2%)                                  |
+ * |  BYE:       10 (0.5%)                                   |
+ * |  CANCEL:    0 (0.0%)                                    |
+ * +---------------------------------------------------------+
+ * |               Press any key to continue                 |
+ * +---------------------------------------------------------+
+ *
+ */
+#include "config.h"
+#include "vector.h"
+#include "sip.h"
+#include "ui_manager.h"
+#include "ui_stats.h"
+
+/**
+ * Ui Structure definition for Stats panel
+ */
+ui_t ui_stats = {
+    .type = PANEL_STATS,
+    .panel = NULL,
+    .create = stats_create,
+    .destroy = stats_destroy,
+    .handle_key = stats_handle_key
+};
+
+PANEL *
+stats_create()
+{
+    PANEL *panel;
+    WINDOW *win;
+    int height, width;
+    vector_iter_t calls;
+    vector_iter_t msgs;
+    sip_call_t *call;
+    sip_msg_t *msg;
+
+    // Counters!
+    struct {
+        int dtotal, dcalls, completed, cancelled, incall, rejected, setup;
+        int mtotal, invite, regist, subscribe, update, notify, options;
+        int publish, message, info, ack, bye, cancel;
+        int r100, r200, r300, r400, r500, r600, r700, r800;
+    } stats;
+    memset(&stats, 0, sizeof(stats));
+
+    // Calculate window dimensions
+    height = 23;
+    width = 60;
+
+    // Cerate a new indow for the panel and form
+    win = newwin(height, width, (LINES - height) / 2, (COLS - width) / 2);
+
+    // Create a new panel
+    panel = new_panel(win);
+
+    // Set the window title and boxes
+    mvwprintw(win, 1, width / 2 - 9, "Stats Information");
+    wattron(win, COLOR_PAIR(CP_BLUE_ON_DEF));
+    title_foot_box(panel);
+    mvwhline(win, 8, 1, ACS_HLINE, width - 1);
+    mvwaddch(win, 8, 0, ACS_LTEE);
+    mvwaddch(win, 8, width - 1, ACS_RTEE);
+    mvwprintw(win, height - 2, width / 2 - 12, "Press any key to continue");
+    wattroff(win, COLOR_PAIR(CP_BLUE_ON_DEF));
+
+    // Parse the data
+    calls = sip_calls_iterator();
+    stats.dtotal = vector_iterator_count(&calls);
+
+    // Ignore this screen when no dialog exists
+    if (!stats.dtotal) {
+        mvwprintw(win, 3, 3, "No information to display");
+        return panel;
+    }
+
+    while ((call = vector_iterator_next(&calls))) {
+        // If this dialog is a call
+        if (call->state) {
+            // Increase call counter
+            stats.dcalls++;
+            // Increase call status counter
+            switch (call->state) {
+                case SIP_CALLSTATE_CALLSETUP: stats.setup++; break;
+                case SIP_CALLSTATE_INCALL: stats.incall++; break;
+                case SIP_CALLSTATE_CANCELLED: stats.cancelled++; break;
+                case SIP_CALLSTATE_REJECTED: stats.rejected++; break;
+                case SIP_CALLSTATE_COMPLETED: stats.completed++; break;
+            }
+        }
+        // For each message in call
+        msgs = vector_iterator(call->msgs);
+        while ((msg = vector_iterator_next(&msgs))) {
+            // Increase message counter
+            stats.mtotal++;
+            // Check message type
+            switch (msg->reqresp) {
+                case SIP_METHOD_REGISTER: stats.regist++; break;
+                case SIP_METHOD_INVITE: stats.invite++; break;
+                case SIP_METHOD_SUBSCRIBE: stats.subscribe++; break;
+                case SIP_METHOD_NOTIFY: stats.notify++; break;
+                case SIP_METHOD_OPTIONS: stats.options++; break;
+                case SIP_METHOD_PUBLISH: stats.publish++; break;
+                case SIP_METHOD_MESSAGE: stats.message++; break;
+                case SIP_METHOD_CANCEL: stats.cancel++; break;
+                case SIP_METHOD_BYE: stats.bye++; break;
+                case SIP_METHOD_ACK: stats.ack++; break;
+                // case SIP_METHOD_PRACK:
+                case SIP_METHOD_INFO: stats.info++; break;
+                // case SIP_METHOD_REFER:
+                case SIP_METHOD_UPDATE: stats.update++; break;
+                default:
+                    if (msg->reqresp >= 800) stats.r800++;
+                    else if (msg->reqresp >= 700) stats.r700++;
+                    else if (msg->reqresp >= 600) stats.r600++;
+                    else if (msg->reqresp >= 500) stats.r500++;
+                    else if (msg->reqresp >= 400) stats.r400++;
+                    else if (msg->reqresp >= 300) stats.r300++;
+                    else if (msg->reqresp >= 200) stats.r200++;
+                    else if (msg->reqresp >= 100) stats.r100++;
+            }
+        }
+
+
+    }
+
+    // Print parses data
+    mvwprintw(win, 3,  3,  "Dialogs: %d", stats.dtotal);
+    mvwprintw(win, 4,  3,  "Calls: %d (%.1f\%)", stats.dcalls, (float) stats.dcalls * 100 / stats.dtotal);
+    mvwprintw(win, 5,  3,  "Messages: %d", stats.mtotal);
+    // Print status of calls if any
+    if (stats.dcalls) {
+        mvwprintw(win, 3,  33, "COMPLETED:  %d (%.1f\%)", stats.completed, (float) stats.completed * 100 / stats.dcalls);
+        mvwprintw(win, 4,  33, "CANCELLED:  %d (%.1f\%)", stats.cancelled, (float) stats.cancelled * 100 / stats.dcalls);
+        mvwprintw(win, 5,  33, "IN CALL:    %d (%.1f\%)", stats.incall,    (float) stats.incall * 100 / stats.dcalls);
+        mvwprintw(win, 6,  33, "REJECTED:   %d (%.1f\%)", stats.rejected,  (float) stats.rejected * 100 / stats.dcalls);
+        mvwprintw(win, 7,  33, "CALL SETUP: %d (%.1f\%)", stats.setup,     (float) stats.setup * 100 / stats.dcalls);
+    }
+
+    mvwprintw(win, 9,  3, "INVITE:    %d (%.1f\%)", stats.invite,    (float) stats.invite * 100 / stats.mtotal);
+    mvwprintw(win, 10, 3, "REGISTER:  %d (%.1f\%)", stats.regist,    (float) stats.regist * 100 / stats.mtotal);
+    mvwprintw(win, 11, 3, "SUBSCRIBE: %d (%.1f\%)", stats.subscribe, (float) stats.subscribe * 100 / stats.mtotal);
+    mvwprintw(win, 12, 3, "UPDATE:    %d (%.1f\%)", stats.update,    (float) stats.update * 100 / stats.mtotal);
+    mvwprintw(win, 13, 3, "NOTIFY:    %d (%.1f\%)", stats.notify,    (float) stats.notify * 100 / stats.mtotal);
+    mvwprintw(win, 14, 3, "OPTIONS:   %d (%.1f\%)", stats.options,   (float) stats.options * 100 / stats.mtotal);
+    mvwprintw(win, 15, 3, "PUBLISH:   %d (%.1f\%)", stats.publish,   (float) stats.publish * 100 / stats.mtotal);
+    mvwprintw(win, 16, 3, "MESSAGE:   %d (%.1f\%)", stats.message,   (float) stats.message * 100 / stats.mtotal);
+    mvwprintw(win, 17, 3, "INFO:      %d (%.1f\%)", stats.info,      (float) stats.info * 100 / stats.mtotal);
+    mvwprintw(win, 18, 3, "BYE:       %d (%.1f\%)", stats.bye,       (float) stats.bye * 100 / stats.mtotal);
+    mvwprintw(win, 19, 3, "CANCEL:    %d (%.1f\%)", stats.cancel,    (float) stats.cancel * 100 / stats.mtotal);
+
+    mvwprintw(win, 9,  33, "1XX: %d (%.1f\%)", stats.r100, (float) stats.r100 * 100 / stats.mtotal);
+    mvwprintw(win, 10, 33, "2XX: %d (%.1f\%)", stats.r200, (float) stats.r200 * 100 / stats.mtotal);
+    mvwprintw(win, 11, 33, "3XX: %d (%.1f\%)", stats.r300, (float) stats.r300 * 100 / stats.mtotal);
+    mvwprintw(win, 12, 33, "4XX: %d (%.1f\%)", stats.r400, (float) stats.r400 * 100 / stats.mtotal);
+    mvwprintw(win, 13, 33, "5XX: %d (%.1f\%)", stats.r500, (float) stats.r500 * 100 / stats.mtotal);
+    mvwprintw(win, 14, 33, "6XX: %d (%.1f\%)", stats.r600, (float) stats.r600 * 100 / stats.mtotal);
+    mvwprintw(win, 15, 33, "7XX: %d (%.1f\%)", stats.r700, (float) stats.r700 * 100 / stats.mtotal);
+    mvwprintw(win, 16, 33, "8XX: %d (%.1f\%)", stats.r800, (float) stats.r800 * 100 / stats.mtotal);
+
+    return panel;
+}
+
+void
+stats_destroy(PANEL *panel)
+{
+    // Deallocate panel window
+    delwin(panel_window(panel));
+    // Deallocate panel pointer
+    del_panel(panel);
+}
+
+int
+stats_handle_key(PANEL *panel, int key)
+{
+    return KEY_ESC;
+}
diff --git a/src/ui_stats.h b/src/ui_stats.h
new file mode 100644
index 0000000..6a34de5
--- /dev/null
+++ b/src/ui_stats.h
@@ -0,0 +1,71 @@
+/**************************************************************************
+ **
+ ** sngrep - SIP Messages flow viewer
+ **
+ ** Copyright (C) 2013-2015 Ivan Alonso (Kaian)
+ ** Copyright (C) 2013-2015 Irontec SL. All rights reserved.
+ **
+ ** This program 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.
+ **
+ ** This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ **
+ ****************************************************************************/
+/**
+ * @file ui_stats.h
+ * @author Ivan Alonso [aka Kaian] <kaian at irontec.com>
+ *
+ * @brief Functions to manage ui window for capture stats display
+ */
+#ifndef __UI_STATS_H
+#define __UI_STATS_H
+
+/**
+ * @brief Creates a new stats panel
+ *
+ * This function allocates all required memory for
+ * displaying the stats panel. It also draws all the
+ * static information of the panel that will never be
+ * redrawn.
+ *
+ * @return a panel pointer
+ */
+PANEL *
+stats_create();
+
+/**
+ * @brief Destroy stats  panel
+ *
+ * This function do the final cleanups for this panel
+ */
+void
+stats_destroy();
+
+/**
+ * @brief Manage pressed keys for stats panel
+ *
+ * This function is called by UI manager every time a
+ * key is pressed. This allow the filter panel to manage
+ * its own keys.
+ * If this function return 0, the key will not be handled
+ * by ui manager. Otherwise the return will be considered
+ * a key code.
+ *
+ * @param panel Filter panel pointer
+ * @param key   key code
+ * @return 0 if the key is handled, keycode otherwise
+ */
+int
+stats_handle_key(PANEL *panel, int key);
+
+
+
+#endif /* __UI_STATS_H */
diff --git a/src/vector.c b/src/vector.c
index 27bde49..a93c1ed 100644
--- a/src/vector.c
+++ b/src/vector.c
@@ -314,6 +314,12 @@ vector_iterator_set_current(vector_iter_t *it, int current)
     it->current = current;
 }
 
+void
+vector_iterator_set_last(vector_iter_t *it)
+{
+    it->current = vector_count(it->vector);
+}
+
 int
 vector_iterator_current(vector_iter_t *it)
 {
diff --git a/src/vector.h b/src/vector.h
index ca7c23c..c33541e 100644
--- a/src/vector.h
+++ b/src/vector.h
@@ -250,6 +250,12 @@ void
 vector_iterator_set_current(vector_iter_t *it, int current);
 
 /**
+ * @brief Set iterator position to the last element
+ */
+void
+vector_iterator_set_last(vector_iter_t *it);
+
+/**
  * @brief Return current iterator position
  */
 int

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-voip/sngrep.git



More information about the Pkg-voip-commits mailing list