A new type of DCERPC over SMB transport.
[obnox/wireshark/wip.git] / packet-wsp.c
index f37ae5c40a4befc56172752847b68c6ab3c1981b..a71ce47628a487a31203554627f096d8200fdb1c 100644 (file)
@@ -2,15 +2,16 @@
  *
  * Routines to dissect WSP component of WAP traffic.
  * 
- * $Id: packet-wsp.c,v 1.18 2001/02/13 00:17:54 guy Exp $
+ * $Id: packet-wsp.c,v 1.54 2002/02/22 07:23:24 guy Exp $
  *
  * Ethereal - Network traffic analyzer
- * By Gerald Combs <gerald@zing.org>
- * Copyright 1998 Didier Jorand
+ * By Gerald Combs <gerald@ethereal.com>
+ * Copyright 1998 Gerald Combs
  *
  * WAP dissector based on original work by Ben Fowler
  * Updated by Neil Hunter <neil.hunter@energis-squared.com>
  * WTLS support by Alexandre P. Ferreira (Splice IP)
+ * Openwave header support by Dermot Bradley <dermot.bradley@openwave.com>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
 
 #include <string.h>
 #include <glib.h>
-#include "packet.h"
+#include <epan/packet.h>
+#include <epan/ipv6-utils.h>
+#include <epan/conversation.h>
 #include "packet-wap.h"
 #include "packet-wsp.h"
 
 /* File scoped variables for the protocol and registered fields */
-static int proto_wsp                                                   = HF_EMPTY;
+static int proto_wsp                                   = HF_EMPTY;
 
 /* These fields used by fixed part of header */
-static int hf_wsp_header_tid                                   = HF_EMPTY;
-static int hf_wsp_header_pdu_type                              = HF_EMPTY;
-static int hf_wsp_version_major                                        = HF_EMPTY;
-static int hf_wsp_version_minor                                        = HF_EMPTY;
-static int hf_wsp_capability_length                            = HF_EMPTY;
+static int hf_wsp_header_tid                           = HF_EMPTY;
+static int hf_wsp_header_pdu_type                      = HF_EMPTY;
+static int hf_wsp_version_major                                = HF_EMPTY;
+static int hf_wsp_version_minor                                = HF_EMPTY;
+static int hf_wsp_capability_length                    = HF_EMPTY;
 static int hf_wsp_capabilities_section                 = HF_EMPTY;
-static int hf_wsp_header_uri_len                               = HF_EMPTY;
-static int hf_wsp_header_uri                                   = HF_EMPTY;
-static int hf_wsp_server_session_id                            = HF_EMPTY;
-static int hf_wsp_header_status                                        = HF_EMPTY;
-static int hf_wsp_header_length                                        = HF_EMPTY;
-static int hf_wsp_headers_section                              = HF_EMPTY;
-static int hf_wsp_header                                               = HF_EMPTY;
-static int hf_wsp_content_type                                 = HF_EMPTY;
-static int hf_wsp_parameter_well_known_charset = HF_EMPTY;
-static int hf_wsp_reply_data                                   = HF_EMPTY;
-static int hf_wsp_post_data                                            = HF_EMPTY;
-
-static int hf_wsp_header_accept                                        = HF_EMPTY;
-static int hf_wsp_header_accept_str                            = HF_EMPTY;
+static int hf_wsp_capabilities_client_SDU              = HF_EMPTY;
+static int hf_wsp_capabilities_server_SDU              = HF_EMPTY;
+static int hf_wsp_capabilities_protocol_opt            = HF_EMPTY;
+static int hf_wsp_capabilities_method_MOR              = HF_EMPTY;
+static int hf_wsp_capabilities_push_MOR                        = HF_EMPTY;
+static int hf_wsp_capabilities_extended_methods                = HF_EMPTY;
+static int hf_wsp_capabilities_header_code_pages       = HF_EMPTY;
+static int hf_wsp_capabilities_aliases                 = HF_EMPTY;
+static int hf_wsp_header_uri_len                       = HF_EMPTY;
+static int hf_wsp_header_uri                           = HF_EMPTY;
+static int hf_wsp_server_session_id                    = HF_EMPTY;
+static int hf_wsp_header_status                                = HF_EMPTY;
+static int hf_wsp_header_length                                = HF_EMPTY;
+static int hf_wsp_headers_section                      = HF_EMPTY;
+static int hf_wsp_header                               = HF_EMPTY;
+static int hf_wsp_content_type                         = HF_EMPTY;
+static int hf_wsp_content_type_str                     = HF_EMPTY;
+static int hf_wsp_parameter_well_known_charset         = HF_EMPTY;
+static int hf_wsp_parameter_type                       = HF_EMPTY;
+static int hf_wsp_parameter_name                       = HF_EMPTY;
+static int hf_wsp_parameter_filename                   = HF_EMPTY;
+static int hf_wsp_parameter_start                      = HF_EMPTY;
+static int hf_wsp_parameter_start_info                 = HF_EMPTY;
+static int hf_wsp_parameter_comment                    = HF_EMPTY;
+static int hf_wsp_parameter_domain                     = HF_EMPTY;
+static int hf_wsp_parameter_path                       = HF_EMPTY;
+static int hf_wsp_parameter_upart_type                 = HF_EMPTY;
+static int hf_wsp_parameter_upart_type_value           = HF_EMPTY;
+static int hf_wsp_reply_data                           = HF_EMPTY;
+static int hf_wsp_post_data                            = HF_EMPTY;
+static int hf_wsp_push_data                            = HF_EMPTY;
+static int hf_wsp_multipart_data                       = HF_EMPTY;
+static int hf_wsp_mpart                                        = HF_EMPTY;
+
+static int hf_wsp_header_shift_code                    = HF_EMPTY;
+static int hf_wsp_header_accept                                = HF_EMPTY;
+static int hf_wsp_header_accept_str                    = HF_EMPTY;
+static int hf_wsp_header_accept_application            = HF_EMPTY;
+static int hf_wsp_header_accept_application_str                = HF_EMPTY;
 static int hf_wsp_header_accept_charset                        = HF_EMPTY;
 static int hf_wsp_header_accept_charset_str            = HF_EMPTY;
 static int hf_wsp_header_accept_language               = HF_EMPTY;
-static int hf_wsp_header_accept_language_str   = HF_EMPTY;
+static int hf_wsp_header_accept_language_str           = HF_EMPTY;
 static int hf_wsp_header_accept_ranges                 = HF_EMPTY;
+static int hf_wsp_header_accept_ranges_str             = HF_EMPTY;
 static int hf_wsp_header_cache_control                 = HF_EMPTY;
+static int hf_wsp_header_cache_control_str             = HF_EMPTY;
+static int hf_wsp_header_cache_control_field_name      = HF_EMPTY;
+static int hf_wsp_header_connection                    = HF_EMPTY;
+static int hf_wsp_header_connection_str                        = HF_EMPTY;
+static int hf_wsp_header_cache_control_field_name_str  = HF_EMPTY;
 static int hf_wsp_header_content_length                        = HF_EMPTY;
-static int hf_wsp_header_age                                   = HF_EMPTY;
-static int hf_wsp_header_date                                  = HF_EMPTY;
-static int hf_wsp_header_etag                                  = HF_EMPTY;
-static int hf_wsp_header_expires                               = HF_EMPTY;
+static int hf_wsp_header_age                           = HF_EMPTY;
+static int hf_wsp_header_bearer_indication             = HF_EMPTY;
+static int hf_wsp_header_date                          = HF_EMPTY;
+static int hf_wsp_header_etag                          = HF_EMPTY;
+static int hf_wsp_header_expires                       = HF_EMPTY;
 static int hf_wsp_header_last_modified                 = HF_EMPTY;
-static int hf_wsp_header_location                              = HF_EMPTY;
+static int hf_wsp_header_location                      = HF_EMPTY;
 static int hf_wsp_header_if_modified_since             = HF_EMPTY;
-static int hf_wsp_header_server                                        = HF_EMPTY;
-static int hf_wsp_header_user_agent                            = HF_EMPTY;
+static int hf_wsp_header_profile                       = HF_EMPTY;
+static int hf_wsp_header_pragma                                = HF_EMPTY;
+static int hf_wsp_header_server                                = HF_EMPTY;
+static int hf_wsp_header_user_agent                    = HF_EMPTY;
+static int hf_wsp_header_warning                       = HF_EMPTY;
+static int hf_wsp_header_warning_code                  = HF_EMPTY;
+static int hf_wsp_header_warning_agent                 = HF_EMPTY;
+static int hf_wsp_header_warning_text                  = HF_EMPTY;
 static int hf_wsp_header_application_header            = HF_EMPTY;
 static int hf_wsp_header_application_value             = HF_EMPTY;
-static int hf_wsp_header_x_wap_tod                             = HF_EMPTY;
+static int hf_wsp_header_x_wap_tod                     = HF_EMPTY;
+static int hf_wsp_header_content_ID                    = HF_EMPTY;
 static int hf_wsp_header_transfer_encoding             = HF_EMPTY;
-static int hf_wsp_header_transfer_encoding_str = HF_EMPTY;
-static int hf_wsp_header_via                                   = HF_EMPTY;
+static int hf_wsp_header_transfer_encoding_str         = HF_EMPTY;
+static int hf_wsp_header_via                           = HF_EMPTY;
+static int hf_wsp_header_wap_application_id            = HF_EMPTY;
+static int hf_wsp_header_wap_application_id_str                = HF_EMPTY;
+
+
+/* Openwave-specific WSP headers */
+static int hf_wsp_header_openwave_proxy_push_addr      = HF_EMPTY;
+static int hf_wsp_header_openwave_proxy_push_accept    = HF_EMPTY;
+static int hf_wsp_header_openwave_proxy_push_seq       = HF_EMPTY;
+static int hf_wsp_header_openwave_proxy_notify         = HF_EMPTY;
+static int hf_wsp_header_openwave_proxy_operator_domain        = HF_EMPTY;
+static int hf_wsp_header_openwave_proxy_home_page      = HF_EMPTY;
+static int hf_wsp_header_openwave_devcap_has_color     = HF_EMPTY;
+static int hf_wsp_header_openwave_devcap_num_softkeys  = HF_EMPTY;
+static int hf_wsp_header_openwave_devcap_softkey_size  = HF_EMPTY;
+static int hf_wsp_header_openwave_devcap_screen_chars  = HF_EMPTY;
+static int hf_wsp_header_openwave_devcap_screen_pixels = HF_EMPTY;
+static int hf_wsp_header_openwave_devcap_em_size       = HF_EMPTY;
+static int hf_wsp_header_openwave_devcap_screen_depth  = HF_EMPTY;
+static int hf_wsp_header_openwave_devcap_immed_alert   = HF_EMPTY;
+static int hf_wsp_header_openwave_proxy_net_ask                = HF_EMPTY;
+static int hf_wsp_header_openwave_proxy_uplink_version = HF_EMPTY;
+static int hf_wsp_header_openwave_proxy_tod            = HF_EMPTY;
+static int hf_wsp_header_openwave_proxy_ba_enable      = HF_EMPTY;
+static int hf_wsp_header_openwave_proxy_ba_realm       = HF_EMPTY;
+static int hf_wsp_header_openwave_proxy_redirect_enable        = HF_EMPTY;
+static int hf_wsp_header_openwave_proxy_request_uri    = HF_EMPTY;
+static int hf_wsp_header_openwave_proxy_redirect_status        = HF_EMPTY;
+static int hf_wsp_header_openwave_proxy_trans_charset  = HF_EMPTY;
+static int hf_wsp_header_openwave_proxy_trans_charset_str      = HF_EMPTY;
+static int hf_wsp_header_openwave_proxy_linger         = HF_EMPTY;
+static int hf_wsp_header_openwave_proxy_client_id      = HF_EMPTY;
+static int hf_wsp_header_openwave_proxy_enable_trust   = HF_EMPTY;
+static int hf_wsp_header_openwave_proxy_trust_old      = HF_EMPTY;
+static int hf_wsp_header_openwave_proxy_trust          = HF_EMPTY;
+static int hf_wsp_header_openwave_proxy_bookmark       = HF_EMPTY;
+static int hf_wsp_header_openwave_devcap_gui           = HF_EMPTY;
+
+
+static int hf_wsp_redirect_flags                       = HF_EMPTY;
+static int hf_wsp_redirect_permanent                   = HF_EMPTY;
+static int hf_wsp_redirect_reuse_security_session      = HF_EMPTY;
+static int hf_wsp_redirect_afl                         = HF_EMPTY;
+static int hf_wsp_redirect_afl_bearer_type_included    = HF_EMPTY;
+static int hf_wsp_redirect_afl_port_number_included    = HF_EMPTY;
+static int hf_wsp_redirect_afl_address_len             = HF_EMPTY;
+static int hf_wsp_redirect_bearer_type                 = HF_EMPTY;
+static int hf_wsp_redirect_port_num                    = HF_EMPTY;
+static int hf_wsp_redirect_ipv4_addr                   = HF_EMPTY;
+static int hf_wsp_redirect_ipv6_addr                   = HF_EMPTY;
+static int hf_wsp_redirect_addr                                = HF_EMPTY;
 
 /* Initialize the subtree pointers */
-static gint ett_wsp                                                    = ETT_EMPTY;
-static gint ett_header                                                         = ETT_EMPTY;
-static gint ett_headers                                                        = ETT_EMPTY;
-static gint ett_capabilities                                   = ETT_EMPTY;
-static gint ett_content_type                                   = ETT_EMPTY;
+static gint ett_wsp                                    = ETT_EMPTY;
+static gint ett_content_type_parameters                        = ETT_EMPTY;
+static gint ett_header                                         = ETT_EMPTY;
+static gint ett_headers                                        = ETT_EMPTY;
+static gint ett_header_warning                         = ETT_EMPTY;
+static gint ett_header_cache_control_parameters                = ETT_EMPTY;
+static gint ett_header_cache_control_field_names       = ETT_EMPTY;
+static gint ett_capabilities                           = ETT_EMPTY;
+static gint ett_content_type                           = ETT_EMPTY;
+static gint ett_redirect_flags                         = ETT_EMPTY;
+static gint ett_redirect_afl                           = ETT_EMPTY;
+static gint ett_multiparts                             = ETT_EMPTY;
+static gint ett_mpartlist                              = ETT_EMPTY;
+
+/* Handle for WSP-over-UDP dissector */
+static dissector_handle_t wsp_fromudp_handle;
+
+/* Handle for WTP-over-UDP dissector */
+static dissector_handle_t wtp_fromudp_handle;
 
 /* Handle for WMLC dissector */
 static dissector_handle_t wmlc_handle;
@@ -168,6 +274,7 @@ static const value_string vals_status[] = {
        { 0x33, "See Other" },
        { 0x34, "Not Modified" },
        { 0x35, "Use Proxy" },
+       { 0x37, "Temporary Redirect" },
 
        { 0x40, "Bad Request" },
        { 0x41, "Unauthorised" },
@@ -185,6 +292,8 @@ static const value_string vals_status[] = {
        { 0x4D, "Request Entity Too Large" },
        { 0x4E, "Request-URI Too Large" },
        { 0x4F, "Unsupported Media Type" },
+       { 0x50, "Requested Range Not Satisfiable" },
+       { 0x51, "Expectation Failed" },
 
        { 0x60, "Internal Server Error" },
        { 0x61, "Not Implemented" },
@@ -195,6 +304,296 @@ static const value_string vals_status[] = {
        { 0x00, NULL }
 };
 
+/*
+ * Field names.
+ */
+#define FN_ACCEPT              0x00
+#define FN_ACCEPT_CHARSET_DEP  0x01    /* encoding version 1.1, deprecated */
+#define FN_ACCEPT_ENCODING_DEP 0x02    /* encoding version 1.1, deprecated */
+#define FN_ACCEPT_LANGUAGE     0x03
+#define FN_ACCEPT_RANGES       0x04
+#define FN_AGE                 0x05
+#define FN_ALLOW               0x06
+#define FN_AUTHORIZATION       0x07
+#define FN_CACHE_CONTROL_DEP   0x08    /* encoding version 1.1, deprecated */
+#define FN_CONNECTION          0x09
+#define FN_CONTENT_BASE                0x0A
+#define FN_CONTENT_ENCODING    0x0B
+#define FN_CONTENT_LANGUAGE    0x0C
+#define FN_CONTENT_LENGTH      0x0D
+#define FN_CONTENT_LOCATION    0x0E
+#define FN_CONTENT_MD5         0x0F
+#define FN_CONTENT_RANGE_DEP   0x10    /* encoding version 1.1, deprecated */
+#define FN_CONTENT_TYPE                0x11
+#define FN_DATE                        0x12
+#define FN_ETAG                        0x13
+#define FN_EXPIRES             0x14
+#define FN_FROM                        0x15
+#define FN_HOST                        0x16
+#define FN_IF_MODIFIED_SINCE   0x17
+#define FN_IF_MATCH            0x18
+#define FN_IF_NONE_MATCH       0x19
+#define FN_IF_RANGE            0x1A
+#define FN_IF_UNMODIFIED_SINCE 0x1B
+#define FN_LOCATION            0x1C
+#define FN_LAST_MODIFIED       0x1D
+#define FN_MAX_FORWARDS                0x1E
+#define FN_PRAGMA              0x1F
+#define FN_PROXY_AUTHENTICATE  0x20
+#define FN_PROXY_AUTHORIZATION 0x21
+#define FN_PUBLIC              0x22
+#define FN_RANGE               0x23
+#define FN_REFERER             0x24
+#define FN_RETRY_AFTER         0x25
+#define FN_SERVER              0x26
+#define FN_TRANSFER_ENCODING   0x27
+#define FN_UPGRADE             0x28
+#define FN_USER_AGENT          0x29
+#define FN_VARY                        0x2A
+#define FN_VIA                 0x2B
+#define FN_WARNING             0x2C
+#define FN_WWW_AUTHENTICATE    0x2D
+#define FN_CONTENT_DISPOSITION 0x2E
+#define FN_X_WAP_APPLICATION_ID        0x2F
+#define FN_X_WAP_CONTENT_URI   0x30
+#define FN_X_WAP_INITIATOR_URI 0x31
+#define FN_ACCEPT_APPLICATION  0x32
+#define FN_BEARER_INDICATION   0x33
+#define FN_PUSH_FLAG           0x34
+#define FN_PROFILE             0x35
+#define FN_PROFILE_DIFF                0x36
+#define FN_PROFILE_WARNING     0x37
+#define FN_EXPECT              0x38
+#define FN_TE                  0x39
+#define FN_TRAILER             0x3A
+#define FN_ACCEPT_CHARSET      0x3B    /* encoding version 1.3 */
+#define FN_ACCEPT_ENCODING     0x3C    /* encoding version 1.3 */
+#define FN_CACHE_CONTROL       0x3D    /* encoding version 1.3 */
+#define FN_CONTENT_RANGE       0x3E    /* encoding version 1.3 */
+#define FN_X_WAP_TOD           0x3F
+#define FN_CONTENT_ID          0x40
+#define FN_SET_COOKIE          0x41
+#define FN_COOKIE              0x42
+#define FN_ENCODING_VERSION    0x43
+#define FN_PROFILE_WARNING14   0x44    /* encoding version 1.4 */
+#define FN_CONTENT_DISPOSITION14       0x45    /* encoding version 1.4 */
+#define FN_X_WAP_SECURITY      0x46
+#define FN_CACHE_CONTROL14     0x47    /* encoding version 1.4 */
+
+
+/*
+ * Openwave field names.
+ */
+#define FN_OPENWAVE_PROXY_PUSH_ADDR            0x00
+#define FN_OPENWAVE_PROXY_PUSH_ACCEPT          0x01
+#define FN_OPENWAVE_PROXY_PUSH_SEQ             0x02
+#define FN_OPENWAVE_PROXY_NOTIFY               0x03
+#define FN_OPENWAVE_PROXY_OPERATOR_DOMAIN      0x04
+#define FN_OPENWAVE_PROXY_HOME_PAGE            0x05
+#define FN_OPENWAVE_DEVCAP_HAS_COLOR           0x06
+#define FN_OPENWAVE_DEVCAP_NUM_SOFTKEYS                0x07
+#define FN_OPENWAVE_DEVCAP_SOFTKEY_SIZE                0x08
+#define FN_OPENWAVE_DEVCAP_SCREEN_CHARS                0x09
+#define FN_OPENWAVE_DEVCAP_SCREEN_PIXELS       0x0A
+#define FN_OPENWAVE_DEVCAP_EM_SIZE             0x0B
+#define FN_OPENWAVE_DEVCAP_SCREEN_DEPTH                0x0C
+#define FN_OPENWAVE_DEVCAP_IMMED_ALERT         0x0D
+#define FN_OPENWAVE_PROXY_NET_ASK              0x0E
+#define FN_OPENWAVE_PROXY_UPLINK_VERSION       0x0F
+#define FN_OPENWAVE_PROXY_TOD                  0x10
+#define FN_OPENWAVE_PROXY_BA_ENABLE            0x11
+#define FN_OPENWAVE_PROXY_BA_REALM             0x12
+#define FN_OPENWAVE_PROXY_REDIRECT_ENABLE      0x13
+#define FN_OPENWAVE_PROXY_REQUEST_URI          0x14
+#define FN_OPENWAVE_PROXY_REDIRECT_STATUS      0x15
+#define FN_OPENWAVE_PROXY_TRANS_CHARSET                0x16
+#define FN_OPENWAVE_PROXY_LINGER               0x17
+#define FN_OPENWAVE_PROXY_CLIENT_ID            0x18
+#define FN_OPENWAVE_PROXY_ENABLE_TRUST         0x19
+#define FN_OPENWAVE_PROXY_TRUST_OLD            0x1A
+#define FN_OPENWAVE_PROXY_TRUST                        0x20
+#define FN_OPENWAVE_PROXY_BOOKMARK             0x21
+#define FN_OPENWAVE_DEVCAP_GUI                 0x22
+
+static const value_string vals_openwave_field_names[] = {
+       { FN_OPENWAVE_PROXY_PUSH_ADDR,         "x-up-proxy-push-addr" },
+       { FN_OPENWAVE_PROXY_PUSH_ACCEPT,       "x-up-proxy-push-accept" },
+       { FN_OPENWAVE_PROXY_PUSH_SEQ,          "x-up-proxy-seq" },
+       { FN_OPENWAVE_PROXY_NOTIFY,            "x-up-proxy-notify" },
+       { FN_OPENWAVE_PROXY_OPERATOR_DOMAIN,   "x-up-proxy-operator-domain" },
+       { FN_OPENWAVE_PROXY_HOME_PAGE,         "x-up-proxy-home-page" },
+       { FN_OPENWAVE_DEVCAP_HAS_COLOR,        "x-up-devcap-has-color" },
+       { FN_OPENWAVE_DEVCAP_NUM_SOFTKEYS,     "x-up-devcap-num-softkeys" },
+       { FN_OPENWAVE_DEVCAP_SOFTKEY_SIZE,     "x-up-devcap-softkey-size" },
+       { FN_OPENWAVE_DEVCAP_SCREEN_CHARS,     "x-up-devcap-screen-chars" },
+       { FN_OPENWAVE_DEVCAP_SCREEN_PIXELS,    "x-up-devcap-screen-pixels" },
+       { FN_OPENWAVE_DEVCAP_EM_SIZE,          "x-up-devcap-em-size" },
+       { FN_OPENWAVE_DEVCAP_SCREEN_DEPTH,     "x-up-devcap-screen-depth" },
+       { FN_OPENWAVE_DEVCAP_IMMED_ALERT,      "x-up-devcap-immed-alert" },
+       { FN_OPENWAVE_PROXY_NET_ASK,           "x-up-proxy-net-ask" },
+       { FN_OPENWAVE_PROXY_UPLINK_VERSION,    "x-up-proxy-uplink-version" },
+       { FN_OPENWAVE_PROXY_TOD,               "x-up-proxy-tod" },
+       { FN_OPENWAVE_PROXY_BA_ENABLE,         "x-up-proxy-ba-enable" },
+       { FN_OPENWAVE_PROXY_BA_REALM,          "x-up-proxy-ba-realm" },
+       { FN_OPENWAVE_PROXY_REDIRECT_ENABLE,   "x-up-proxy-redirect-enable" },
+       { FN_OPENWAVE_PROXY_REQUEST_URI,       "x-up-proxy-request-uri" },
+       { FN_OPENWAVE_PROXY_REDIRECT_STATUS,   "x-up-proxy-redirect-status" },
+       { FN_OPENWAVE_PROXY_TRANS_CHARSET,     "x-up-proxy-trans-charset" },
+       { FN_OPENWAVE_PROXY_LINGER,            "x-up-proxy-linger" },
+       { FN_OPENWAVE_PROXY_CLIENT_ID,         "x-up-proxy-client-id" },
+       { FN_OPENWAVE_PROXY_ENABLE_TRUST,      "x-up-proxy-enable-trust" },
+       { FN_OPENWAVE_PROXY_TRUST_OLD,         "x-up-proxy-trust-old" },
+       { FN_OPENWAVE_PROXY_TRUST,             "x-up-proxy-trust" },
+       { FN_OPENWAVE_PROXY_BOOKMARK,          "x-up-proxy-bookmark" },
+       { FN_OPENWAVE_DEVCAP_GUI,              "x-up-devcap-gui" },
+       { 0,                                   NULL }
+};     
+
+
+static const value_string vals_field_names[] = {
+       { FN_ACCEPT,               "Accept" },
+       { FN_ACCEPT_CHARSET_DEP,   "Accept-Charset (encoding 1.1)" },
+       { FN_ACCEPT_ENCODING_DEP,  "Accept-Encoding (encoding 1.1)" },
+       { FN_ACCEPT_LANGUAGE,      "Accept-Language" },
+       { FN_ACCEPT_RANGES,        "Accept-Ranges" },
+       { FN_AGE,                  "Age" },
+       { FN_ALLOW,                "Allow" },
+       { FN_AUTHORIZATION,        "Authorization" },
+       { FN_CACHE_CONTROL_DEP,    "Cache-Control (encoding 1.1)" },
+       { FN_CONNECTION,           "Connection" },
+       { FN_CONTENT_BASE,         "Content-Base" },
+       { FN_CONTENT_ENCODING,     "Content-Encoding" },
+       { FN_CONTENT_LANGUAGE,     "Content-Language" },
+       { FN_CONTENT_LENGTH,       "Content-Length" },
+       { FN_CONTENT_LOCATION,     "Content-Location" },
+       { FN_CONTENT_MD5,          "Content-MD5" },
+       { FN_CONTENT_RANGE_DEP,    "Content-Range (encoding 1.1)" },
+       { FN_CONTENT_TYPE,         "Content-Type" },
+       { FN_DATE,                 "Date" },
+       { FN_ETAG,                 "Etag" },
+       { FN_EXPIRES,              "Expires" },
+       { FN_FROM,                 "From" },
+       { FN_HOST,                 "Host" },
+       { FN_IF_MODIFIED_SINCE,    "If-Modified-Since" },
+       { FN_IF_MATCH,             "If-Match" },
+       { FN_IF_NONE_MATCH,        "If-None-Match" },
+       { FN_IF_RANGE,             "If-Range" },
+       { FN_IF_UNMODIFIED_SINCE,  "If-Unmodified-Since" },
+       { FN_LOCATION,             "Location" },
+       { FN_LAST_MODIFIED,        "Last-Modified" },
+       { FN_MAX_FORWARDS,         "Max-Forwards" },
+       { FN_PRAGMA,               "Pragma" },
+       { FN_PROXY_AUTHENTICATE,   "Proxy-Authenticate" },
+       { FN_PROXY_AUTHORIZATION,  "Proxy-Authorization" },
+       { FN_PUBLIC,               "Public" },
+       { FN_RANGE,                "Range" },
+       { FN_REFERER,              "Referer" },
+       { FN_RETRY_AFTER,          "Retry-After" },
+       { FN_SERVER,               "Server" },
+       { FN_TRANSFER_ENCODING,    "Transfer-Encoding" },
+       { FN_UPGRADE,              "Upgrade" },
+       { FN_USER_AGENT,           "User-Agent" },
+       { FN_VARY,                 "Vary" },
+       { FN_VIA,                  "Via" },
+       { FN_WARNING,              "Warning" },
+       { FN_WWW_AUTHENTICATE,     "WWW-Authenticate" },
+       { FN_CONTENT_DISPOSITION,  "Content-Disposition" },
+       { FN_X_WAP_APPLICATION_ID, "X-Wap-Application-ID" },
+       { FN_X_WAP_CONTENT_URI,    "X-Wap-Content-URI" },
+       { FN_X_WAP_INITIATOR_URI,  "X-Wap-Initiator-URI" },
+       { FN_ACCEPT_APPLICATION,   "Accept-Application" },
+       { FN_BEARER_INDICATION,    "Bearer-Indication" },
+       { FN_PUSH_FLAG,            "Push-Flag" },
+       { FN_PROFILE,              "Profile" },
+       { FN_PROFILE_DIFF,         "Profile-Diff" },
+       { FN_PROFILE_WARNING,      "Profile-Warning" },
+       { FN_EXPECT,               "Expect" },
+       { FN_TE,                   "TE" },
+       { FN_TRAILER,              "Trailer" },
+       { FN_ACCEPT_CHARSET,       "Accept-Charset" },
+       { FN_ACCEPT_ENCODING,      "Accept-Encoding" },
+       { FN_CACHE_CONTROL,        "Cache-Control" },
+       { FN_CONTENT_RANGE,        "Content-Range" },
+       { FN_X_WAP_TOD,            "X-Wap-Tod" },
+       { FN_CONTENT_ID,           "Content-ID" },
+       { FN_SET_COOKIE,           "Set-Cookie" },
+       { FN_COOKIE,               "Cookie" },
+       { FN_ENCODING_VERSION,     "Encoding-Version" },
+       { FN_PROFILE_WARNING14,    "Profile-Warning (encoding 1.4)" },
+       { FN_CONTENT_DISPOSITION14,"Content-Disposition (encoding 1.4)" },
+       { FN_X_WAP_SECURITY,       "X-WAP-Security" },
+       { FN_CACHE_CONTROL14,      "Cache-Control (encoding 1.4)" },
+       { 0,                       NULL }
+};     
+
+/*
+ * Bearer types (from the WDP specification).
+ */
+#define BT_IPv4                        0x00
+#define BT_IPv6                        0x01
+#define BT_GSM_USSD            0x02
+#define BT_GSM_SMS             0x03
+#define BT_ANSI_136_GUTS       0x04
+#define BT_IS_95_SMS           0x05
+#define BT_IS_95_CSD           0x06
+#define BT_IS_95_PACKET_DATA   0x07
+#define BT_ANSI_136_CSD                0x08
+#define BT_ANSI_136_PACKET_DATA        0x09
+#define BT_GSM_CSD             0x0A
+#define BT_GSM_GPRS            0x0B
+#define BT_GSM_USSD_IPv4       0x0C
+#define BT_AMPS_CDPD           0x0D
+#define BT_PDC_CSD             0x0E
+#define BT_PDC_PACKET_DATA     0x0F
+#define BT_IDEN_SMS            0x10
+#define BT_IDEN_CSD            0x11
+#define BT_IDEN_PACKET_DATA    0x12
+#define BT_PAGING_FLEX         0x13
+#define BT_PHS_SMS             0x14
+#define BT_PHS_CSD             0x15
+#define BT_GSM_USSD_GSM_SC     0x16
+#define BT_TETRA_SDS_ITSI      0x17
+#define BT_TETRA_SDS_MSISDN    0x18
+#define BT_TETRA_PACKET_DATA   0x19
+#define BT_PAGING_REFLEX       0x1A
+#define BT_GSM_USSD_MSISDN     0x1B
+#define BT_MOBITEX_MPAK                0x1C
+#define BT_ANSI_136_GHOST      0x1D
+
+static const value_string vals_bearer_types[] = {
+       { BT_IPv4,                 "IPv4" },
+       { BT_IPv6,                 "IPv6" },
+       { BT_GSM_USSD,             "GSM USSD" },
+       { BT_GSM_SMS,              "GSM SMS" },
+       { BT_ANSI_136_GUTS,        "ANSI-136 GUTS/R-Data" },
+       { BT_IS_95_SMS,            "IS-95 CDMA SMS" },
+       { BT_IS_95_CSD,            "IS-95 CDMA CSD" },
+       { BT_IS_95_PACKET_DATA,    "IS-95 CDMA Packet data" },
+       { BT_ANSI_136_CSD,         "ANSI-136 CSD" },
+       { BT_ANSI_136_PACKET_DATA, "ANSI-136 Packet data" },
+       { BT_GSM_CSD,              "GSM CSD" },
+       { BT_GSM_GPRS,             "GSM GPRS" },
+       { BT_GSM_USSD_IPv4,        "GSM USSD (IPv4 addresses)" },
+       { BT_AMPS_CDPD,            "AMPS CDPD" },
+       { BT_PDC_CSD,              "PDC CSD" },
+       { BT_PDC_PACKET_DATA,      "PDC Packet data" },
+       { BT_IDEN_SMS,             "IDEN SMS" },
+       { BT_IDEN_CSD,             "IDEN CSD" },
+       { BT_IDEN_PACKET_DATA,     "IDEN Packet data" },
+       { BT_PAGING_FLEX,          "Paging network FLEX(TM)" },
+       { BT_PHS_SMS,              "PHS SMS" },
+       { BT_PHS_CSD,              "PHS CSD" },
+       { BT_GSM_USSD_GSM_SC,      "GSM USSD (GSM Service Code addresses)" },
+       { BT_TETRA_SDS_ITSI,       "TETRA SDS (ITSI addresses)" },
+       { BT_TETRA_SDS_MSISDN,     "TETRA SDS (MSISDN addresses)" },
+       { BT_TETRA_PACKET_DATA,    "TETRA Packet data" },
+       { BT_PAGING_REFLEX,        "Paging network ReFLEX(TM)" },
+       { BT_GSM_USSD_MSISDN,      "GSM USSD (MSISDN addresses)" },
+       { BT_MOBITEX_MPAK,         "Mobitex MPAK" },
+       { BT_ANSI_136_GHOST,       "ANSI-136 GHOST/R-Data" },
+       { 0,                       NULL }
+};
+
 static const value_string vals_content_types[] = {
        { 0x00, "*/*" },
        { 0x01, "text/*" },
@@ -249,6 +648,28 @@ static const value_string vals_content_types[] = {
        { 0x32, "application/vnd.wap.coc" },
        { 0x33, "application/vnd.wap.multipart.related" },
        { 0x34, "application/vnd.wap.sia" },
+       { 0x35, "text/vnd.wap.connectivity-xml" },
+       { 0x36, "application/vnd.wap.connectivity-wbxml" },
+       { 0x37, "application/pkcs7-mime" },
+       { 0x38, "application/vnd.wap.hashed-certificate" },
+       { 0x39, "application/vnd.wap.signed-certificate" },
+       { 0x3A, "application/vnd.wap.cert-response" },
+       { 0x3B, "application/xhtml+xml" },
+       { 0x3C, "application/wml+xml" },
+       { 0x3D, "text/css" },
+       { 0x3E, "application/vnd.wap.mms-message" },
+       { 0x3F, "application/vnd.wap.rollover-certificate" },
+       { 0x201, "application/vnd.uplanet.cachop-wbxml" },
+       { 0x202, "application/vnd.uplanet.signal" },
+       { 0x203, "application/vnd.uplanet.alert-wbxml" },
+       { 0x204, "application/vnd.uplanet.list-wbxml" },
+       { 0x205, "application/vnd.uplanet.listcmd-wbxml" },
+       { 0x206, "application/vnd.uplanet.channel-wbxml" },
+       { 0x207, "application/vnd.uplanet.provisioning-status-uri" },
+       { 0x208, "x-wap.multipart/vnd.uplanet.header-set" },
+       { 0x209, "application/vnd.uplanet.bearer-choice-wbxml" },
+       { 0x20A, "application/vnd.phonecom.mmc-wbxml" },
+       { 0x20B, "application/vnd.nokia.syncset+wbxml" },
        { 0x00, NULL }
 };
 
@@ -285,7 +706,9 @@ static const value_string vals_languages[] = {
        { 0x1E, "Persian (fa)" },
        { 0x1F, "Finnish (fi)" },
        { 0x20, "Fiji (fj)" },
+       { 0x21, "Urdu (ur)" },
        { 0x22, "French (fr)" },
+       { 0x23, "Uzbek (uz)" },
        { 0x24, "Irish (ga)" },
        { 0x25, "Scots Gaelic (gd)" },
        { 0x26, "Galician (gl)" },
@@ -297,7 +720,30 @@ static const value_string vals_languages[] = {
        { 0x2C, "Croatian (hr)" },
        { 0x2D, "Hungarian (hu)" },
        { 0x2E, "Armenian (hy)" },
+       { 0x2F, "Vietnamese (vi)" },
        { 0x30, "Indonesian (formerly in) (id)" },
+       { 0x31, "Wolof (wo)" },
+       { 0x32, "Xhosa (xh)" },
+       { 0x33, "Icelandic (is)" },
+       { 0x34, "Italian (it)" },
+       { 0x35, "Yoruba (yo)" },
+       { 0x36, "Japanese (ja)" },
+       { 0x37, "Javanese (jw)" },
+       { 0x38, "Georgian (ka)" },
+       { 0x39, "Kazakh (kk)" },
+       { 0x3A, "Zhuang (za)" },
+       { 0x3B, "Cambodian (km)" },
+       { 0x3C, "Kannada (kn)" },
+       { 0x3D, "Korean (ko)" },
+       { 0x3E, "Kashmiri (ks)" },
+       { 0x3F, "Kurdish (ku)" },
+       { 0x40, "Kirghiz (ky)" },
+       { 0x41, "Chinese (zh)" },
+       { 0x42, "Lingala (ln)" },
+       { 0x43, "Laothian (lo)" },
+       { 0x44, "Lithuanian (lt)" },
+       { 0x45, "Latvian, Lettish (lv)" },
+       { 0x46, "Malagasy (mg)" },
        { 0x47, "Maori (mi)" },
        { 0x48, "Macedonian (mk)" },
        { 0x49, "Malayalam (ml)" },
@@ -307,6 +753,7 @@ static const value_string vals_languages[] = {
        { 0x4D, "Malay (ms)" },
        { 0x4E, "Maltese (mt)" },
        { 0x4F, "Burmese (my)" },
+       { 0x50, "Ukrainian (uk)" },
        { 0x51, "Nepali (ne)" },
        { 0x52, "Dutch (nl)" },
        { 0x53, "Norwegian (no)" },
@@ -318,6 +765,7 @@ static const value_string vals_languages[] = {
        { 0x59, "Pashto, Pushto (ps)" },
        { 0x5A, "Portuguese (pt)" },
        { 0x5B, "Quechua (qu)" },
+       { 0x5C, "Zulu (zu)" },
        { 0x5D, "Kirundi (rn)" },
        { 0x5E, "Romanian (ro)" },
        { 0x5F, "Russian (ru)" },
@@ -344,40 +792,93 @@ static const value_string vals_languages[] = {
        { 0x74, "Tajik (tg)" },
        { 0x75, "Thai (th)" },
        { 0x76, "Tigrinya (ti)" },
+       { 0x77, "Turkmen (tk)" },
+       { 0x78, "Tagalog (tl)" },
+       { 0x79, "Setswana (tn)" },
+       { 0x7A, "Tonga (to)" },
+       { 0x7B, "Turkish (tr)" },
+       { 0x7C, "Tsonga (ts)" },
+       { 0x7D, "Tatar (tt)" },
+       { 0x7E, "Twi (tw)" },
+       { 0x7F, "Uighur (ug)" },
        { 0x81, "Nauru (na)" },
        { 0x82, "Faeroese (fo)" },
        { 0x83, "Frisian (fy)" },
        { 0x84, "Interlingua (ia)" },
+       { 0x85, "Volapuk (vo)" },
+       { 0x86, "Interlingue (ie)" },
+       { 0x87, "Inupiak (ik)" },
+       { 0x88, "Yiddish (formerly ji) (yi)" },
+       { 0x89, "Inuktitut (iu)" },
+       { 0x8A, "Greenlandic (kl)" },
+       { 0x8B, "Latin (la)" },
        { 0x8C, "Rhaeto-Romance (rm)" },
        { 0x00, NULL }
 };
 
 static const value_string vals_accept_ranges[] = {
-       { 0x80, "None" },
-       { 0x81, "Bytes" },
+       { 0x00, "None" },
+       { 0x01, "Bytes" },
        { 0x00, NULL }
 };
 
+#define NO_CACHE               0x00
+#define NO_STORE               0x01
+#define MAX_AGE                        0x02
+#define MAX_STALE              0x03
+#define MIN_FRESH              0x04
+#define ONLY_IF_CACHED         0x05
+#define PUBLIC                 0x06
+#define PRIVATE                        0x07
+#define NO_TRANSFORM           0x08
+#define MUST_REVALIDATE                0x09
+#define PROXY_REVALIDATE       0x0A
+#define S_MAXAGE               0x0B
+
 static const value_string vals_cache_control[] = {
-       { 0x80, "No-cache" },
-       { 0x81, "No-store" },
-       { 0x82, "Max-age" },
-       { 0x83, "Max-stale" },
-       { 0x84, "Min-fresh" },
-       { 0x85, "Only-if-cached" },
-       { 0x86, "Public" },
-       { 0x87, "Private" },
-       { 0x88, "No-transform" },
-       { 0x89, "Must-revalidate" },
-       { 0x8A, "Proxy-revalidate" },
+       { NO_CACHE,         "No-cache" },
+       { NO_STORE,         "No-store" },
+       { MAX_AGE,          "Max-age" },
+       { MAX_STALE,        "Max-stale" },
+       { MIN_FRESH,        "Min-fresh" },
+       { ONLY_IF_CACHED,   "Only-if-cached" },
+       { PUBLIC,           "Public" },
+       { PRIVATE,          "Private" },
+       { NO_TRANSFORM,     "No-transform" },
+       { MUST_REVALIDATE,  "Must-revalidate" },
+       { PROXY_REVALIDATE, "Proxy-revalidate" },
+       { S_MAXAGE,         "S-max-age" },
+       { 0x00,             NULL }
+};
+
+static const value_string vals_connection[] = {
+       { 0x00, "Close" },
        { 0x00, NULL }
 };
 
 static const value_string vals_transfer_encoding[] = {
-       { 0x80, "Chunked" },
+       { 0x00, "Chunked" },
        { 0x00, NULL }
 };
 
+/*
+ * Redirect flags.
+ */
+#define PERMANENT_REDIRECT     0x80
+#define REUSE_SECURITY_SESSION 0x40
+
+/*
+ * Redirect address flags and length.
+ */
+#define BEARER_TYPE_INCLUDED   0x80
+#define PORT_NUMBER_INCLUDED   0x40
+#define ADDRESS_LEN            0x3f
+
+static const true_false_string yes_no_truth = { 
+       "Yes" ,
+       "No"
+};
+
 /*
  * Windows appears to define DELETE.
  */
@@ -388,43 +889,281 @@ static const value_string vals_transfer_encoding[] = {
 enum {
        RESERVED                = 0x00,
        CONNECT                 = 0x01,
-       CONNECTREPLY    = 0x02,
+       CONNECTREPLY            = 0x02,
        REDIRECT                = 0x03,                 /* No sample data */
        REPLY                   = 0x04,
        DISCONNECT              = 0x05,
        PUSH                    = 0x06,                 /* No sample data */
-       CONFIRMEDPUSH   = 0x07,                 /* No sample data */
+       CONFIRMEDPUSH           = 0x07,                 /* No sample data */
        SUSPEND                 = 0x08,                 /* No sample data */
        RESUME                  = 0x09,                 /* No sample data */
 
-       GET                             = 0x40,
+       GET                     = 0x40,
        OPTIONS                 = 0x41,                 /* No sample data */
        HEAD                    = 0x42,                 /* No sample data */
        DELETE                  = 0x43,                 /* No sample data */
        TRACE                   = 0x44,                 /* No sample data */
 
        POST                    = 0x60,
-       PUT                             = 0x61,                 /* No sample data */
+       PUT                     = 0x61,                 /* No sample data */
 };
 
-static void add_uri (proto_tree *, tvbuff_t *, guint, guint);
+typedef enum {
+       VALUE_LEN_SUPPLIED,
+       VALUE_IS_TEXT_STRING,
+       VALUE_IN_LEN,
+} value_type_t;
+
+static dissector_table_t wsp_dissector_table;
+static heur_dissector_list_t heur_subdissector_list;
+
+static void add_uri (proto_tree *, packet_info *, tvbuff_t *, guint, guint);
 static void add_headers (proto_tree *, tvbuff_t *);
-static void add_header (proto_tree *, tvbuff_t *, tvbuff_t *);
-static guint get_value_length (tvbuff_t *, guint, guint *);
-static guint add_content_type (proto_tree *, tvbuff_t *, guint, guint *);
-static gint get_date_value (tvbuff_t * ,guint ,struct timeval *);
-static void add_date_value (tvbuff_t * ,guint ,proto_tree * ,int ,
-       tvbuff_t * ,guint ,guint ,struct timeval *, const char *);
-static guint add_parameter (proto_tree *, tvbuff_t *, guint);
-static guint add_parameter_charset (proto_tree *, tvbuff_t *, guint, guint);
-static void add_post_data (proto_tree *, tvbuff_t *, guint);
+static int add_well_known_header (proto_tree *, tvbuff_t *, int, guint8);
+static int add_unknown_header (proto_tree *, tvbuff_t *, int, guint8);
+static int add_application_header (proto_tree *, tvbuff_t *, int);
+static void add_accept_header (proto_tree *, tvbuff_t *, int,
+    tvbuff_t *, value_type_t, int);
+static void add_accept_xxx_header (proto_tree *, tvbuff_t *, int,
+    tvbuff_t *, value_type_t, int, int, int, const value_string *,
+    const char *);
+static void add_accept_ranges_header (proto_tree *, tvbuff_t *, int,
+    tvbuff_t *, value_type_t, int);
+static void add_cache_control_header (proto_tree *, tvbuff_t *, int,
+    tvbuff_t *, value_type_t, int);
+static int add_cache_control_field_name (proto_tree *, tvbuff_t *, int, guint);
+static void add_connection_header (proto_tree *, tvbuff_t *, int,
+    tvbuff_t *, value_type_t, int);
+static void add_content_type_value (proto_tree *, tvbuff_t *, int, int,
+    tvbuff_t *, value_type_t, int, int, int, guint *, const char **);
+static void add_wap_application_id_header (proto_tree *, tvbuff_t *, int,
+    tvbuff_t *, value_type_t, int);
+static void add_integer_value_header_common (proto_tree *, tvbuff_t *, int,
+    tvbuff_t *, value_type_t, int, int, guint8, const value_string *);
+static void add_integer_value_header (proto_tree *, tvbuff_t *, int,
+    tvbuff_t *, value_type_t, int, int, guint8);
+static void add_string_value_header_common (proto_tree *, tvbuff_t *, int,
+    tvbuff_t *, value_type_t, int, int, guint8, const value_string *);
+static void add_string_value_header (proto_tree *, tvbuff_t *, int,
+    tvbuff_t *, value_type_t, int, int, guint8);
+static void add_quoted_string_value_header (proto_tree *, tvbuff_t *, int,
+    tvbuff_t *, value_type_t, int, int, guint8);
+static void add_date_value_header (proto_tree *, tvbuff_t *, int,
+    tvbuff_t *, value_type_t, int, int, guint8);
+static int add_parameter (proto_tree *, tvbuff_t *, int);
+static int add_untyped_parameter (proto_tree *, tvbuff_t *, int, int);
+static int add_parameter_charset (proto_tree *, tvbuff_t *, int, int);
+static int add_constrained_encoding (proto_tree *, tvbuff_t *, int, int);
+static int add_parameter_type (proto_tree *, tvbuff_t *, int, int);
+static int add_parameter_text (proto_tree *, tvbuff_t *, int, int, int, const char *);
+static void add_post_data (proto_tree *, tvbuff_t *, guint, const char *);
 static void add_post_variable (proto_tree *, tvbuff_t *, guint, guint, guint, guint);
+static void add_pragma_header (proto_tree *, tvbuff_t *, int, tvbuff_t *,
+    value_type_t, int);
+static void add_transfer_encoding_header (proto_tree *, tvbuff_t *, int,
+    tvbuff_t *, value_type_t, int);
+static void add_warning_header (proto_tree *, tvbuff_t *, int, tvbuff_t *,
+    value_type_t, int);
+static void add_accept_application_header (proto_tree *, tvbuff_t *, int,
+    tvbuff_t *, value_type_t, int);
+static void add_capabilities (proto_tree *tree, tvbuff_t *tvb, int type);
+static void add_capability_vals(tvbuff_t *, gboolean, int, guint, guint, char *, size_t);
+static value_type_t get_value_type_len (tvbuff_t *, int, guint *, int *, int *);
+static guint get_uintvar (tvbuff_t *, guint, guint);
+static gint get_integer (tvbuff_t *, guint, guint, value_type_t, guint *);
+
+static int add_well_known_openwave_header (proto_tree *, tvbuff_t *, int, guint8);
+static void add_openwave_integer_value_header (proto_tree *, tvbuff_t *, int,
+    tvbuff_t *, value_type_t, int, int, guint8);
+static void add_openwave_string_value_header (proto_tree *, tvbuff_t *, int,
+    tvbuff_t *, value_type_t, int, int, guint8);
+
 
 /* Code to actually dissect the packets */
 static void
-dissect_wsp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+dissect_redirect(tvbuff_t *tvb, int offset, packet_info *pinfo,
+    proto_tree *tree, dissector_handle_t dissector_handle)
+{
+       guint8 flags;
+       proto_item *ti;
+       proto_tree *flags_tree;
+       guint8 bearer_type;
+       guint8 address_flags_len;
+       int address_len;
+       proto_tree *atf_tree;
+       guint16 port_num;
+       guint32 address_ipv4;
+       struct e_in6_addr address_ipv6;
+       address redir_address;
+       conversation_t *conv;
+
+       flags = tvb_get_guint8 (tvb, offset);
+       if (tree) {
+               ti = proto_tree_add_uint (tree, hf_wsp_redirect_flags,
+                   tvb, offset, 1, flags);
+               flags_tree = proto_item_add_subtree (ti, ett_redirect_flags);
+               proto_tree_add_boolean (flags_tree, hf_wsp_redirect_permanent,
+                   tvb, offset, 1, flags);
+               proto_tree_add_boolean (flags_tree, hf_wsp_redirect_reuse_security_session,
+                   tvb, offset, 1, flags);
+       }
+       offset++;
+       while (tvb_reported_length_remaining (tvb, offset) > 0) {
+               address_flags_len = tvb_get_guint8 (tvb, offset);
+               if (tree) {
+                       ti = proto_tree_add_uint (tree, hf_wsp_redirect_afl,
+                           tvb, offset, 1, address_flags_len);
+                       atf_tree = proto_item_add_subtree (ti, ett_redirect_afl);
+                       proto_tree_add_boolean (atf_tree, hf_wsp_redirect_afl_bearer_type_included,
+                           tvb, offset, 1, address_flags_len);
+                       proto_tree_add_boolean (atf_tree, hf_wsp_redirect_afl_port_number_included,
+                           tvb, offset, 1, address_flags_len);
+                       proto_tree_add_uint (atf_tree, hf_wsp_redirect_afl_address_len,
+                           tvb, offset, 1, address_flags_len);
+               }
+               offset++;
+               if (address_flags_len & BEARER_TYPE_INCLUDED) {
+                       bearer_type = tvb_get_guint8 (tvb, offset);
+                       if (tree) {
+                               proto_tree_add_uint (tree, hf_wsp_redirect_bearer_type,
+                                   tvb, offset, 1, bearer_type);
+                       }
+                       offset++;
+               } else
+                       bearer_type = 0x00;     /* XXX */
+               if (address_flags_len & PORT_NUMBER_INCLUDED) {
+                       port_num = tvb_get_ntohs (tvb, offset);
+                       if (tree) {
+                               proto_tree_add_uint (tree, hf_wsp_redirect_port_num,
+                                   tvb, offset, 2, port_num);
+                       }
+                       offset += 2;
+               } else {
+                       /*
+                        * Redirecting to the same server port number as was
+                        * being used, i.e. the source port number of this
+                        * redirect.
+                        */
+                       port_num = pinfo->srcport;
+               }
+               address_len = address_flags_len & ADDRESS_LEN;
+               if (!(address_flags_len & BEARER_TYPE_INCLUDED)) {
+                       /*
+                        * We don't have the bearer type in the message,
+                        * so we don't know the address type.
+                        * (It's the same bearer type as the original
+                        * connection.)
+                        */
+                       goto unknown_address_type;
+               }
+
+               /*
+                * We know the bearer type, so we know the address type.
+                */
+               switch (bearer_type) {
+
+               case BT_IPv4:
+               case BT_IS_95_CSD:
+               case BT_IS_95_PACKET_DATA:
+               case BT_ANSI_136_CSD:
+               case BT_ANSI_136_PACKET_DATA:
+               case BT_GSM_CSD:
+               case BT_GSM_GPRS:
+               case BT_GSM_USSD_IPv4:
+               case BT_AMPS_CDPD:
+               case BT_PDC_CSD:
+               case BT_PDC_PACKET_DATA:
+               case BT_IDEN_CSD:
+               case BT_IDEN_PACKET_DATA:
+               case BT_PHS_CSD:
+               case BT_TETRA_PACKET_DATA:
+                       /*
+                        * IPv4.
+                        */
+                       if (address_len != 4) {
+                               /*
+                                * Say what?
+                                */
+                               goto unknown_address_type;
+                       }
+                       tvb_memcpy(tvb, (guint8 *)&address_ipv4, offset, 4);
+                       if (tree) {
+                               proto_tree_add_ipv4 (tree,
+                                   hf_wsp_redirect_ipv4_addr,
+                                   tvb, offset, 4, address_ipv4);
+                       }
+
+                       /*
+                        * Create a conversation so that the
+                        * redirected session will be dissected
+                        * as WAP.
+                        */
+                       redir_address.type = AT_IPv4;
+                       redir_address.len = 4;
+                       redir_address.data = (const guint8 *)&address_ipv4;
+                       conv = find_conversation(&redir_address, &pinfo->dst,
+                           PT_UDP, port_num, 0, NO_PORT_B);
+                       if (conv == NULL) {
+                               conv = conversation_new(&redir_address,
+                                   &pinfo->dst, PT_UDP, port_num, 0, NO_PORT2);
+                       }
+                       conversation_set_dissector(conv, dissector_handle);
+                       break;
+
+               case BT_IPv6:
+                       /*
+                        * IPv6.
+                        */
+                       if (address_len != 16) {
+                               /*
+                                * Say what?
+                                */
+                               goto unknown_address_type;
+                       }
+                       tvb_memcpy(tvb, (guint8 *)&address_ipv6, offset, 16);
+                       if (tree) {
+                               proto_tree_add_ipv6 (tree,
+                                   hf_wsp_redirect_ipv6_addr,
+                                   tvb, offset, 16, (guint8 *)&address_ipv6);
+                       }
+
+                       /*
+                        * Create a conversation so that the
+                        * redirected session will be dissected
+                        * as WAP.
+                        */
+                       redir_address.type = AT_IPv6;
+                       redir_address.len = 16;
+                       redir_address.data = (const guint8 *)&address_ipv4;
+                       conv = find_conversation(&redir_address, &pinfo->dst,
+                           PT_UDP, port_num, 0, NO_PORT_B);
+                       if (conv == NULL) {
+                               conv = conversation_new(&redir_address,
+                                   &pinfo->dst, PT_UDP, port_num, 0, NO_PORT2);
+                       }
+                       conversation_set_dissector(conv, dissector_handle);
+                       break;
+
+               unknown_address_type:
+               default:
+                       if (address_len != 0) {
+                               if (tree) {
+                                       proto_tree_add_item (tree,
+                                           hf_wsp_redirect_addr,
+                                           tvb, offset, address_len,
+                                           bo_little_endian);
+                               }
+                       }
+                       break;
+               }
+               offset += address_len;
+       }
+}
+
+static void
+dissect_wsp_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+    dissector_handle_t dissector_handle, gboolean is_connectionless)
 {
-       frame_data *fdata = pinfo->fd;
        int offset = 0;
 
        guint8 pdut;
@@ -440,41 +1179,20 @@ dissect_wsp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
        guint nextOffset = 0;
        guint contentTypeStart = 0;
        guint contentType = 0;
+       const char *contentTypeStr;
        tvbuff_t *tmp_tvb;
 
 /* Set up structures we will need to add the protocol subtree and manage it */
        proto_item *ti;
-       proto_tree *wsp_tree;
+       proto_tree *wsp_tree = NULL;
 /*     proto_tree *wsp_header_fixed; */
-       proto_tree *wsp_capabilities;
        
 /* This field shows up as the "Info" column in the display; you should make
    it, if possible, summarize what's in the packet, so that a user looking
    at the list of packets can tell what type of packet it is. */
     
-       /* Display protocol type depending on the port */
-       if (check_col(fdata, COL_PROTOCOL)) 
-       {
-               switch ( pinfo->match_port )
-               {
-                       case UDP_PORT_WSP:
-                               col_set_str(fdata, COL_PROTOCOL, "WSP" );
-                               break;
-                       case UDP_PORT_WTLS_WSP:
-                               col_set_str(fdata, COL_PROTOCOL, "WTLS+WSP" );
-                               break;
-               }
-       }
-
-       /* Clear the Info column before we fetch anything from the packet */
-       if (check_col(fdata, COL_INFO))
-       {
-               col_clear(fdata, COL_INFO);
-       }
-
        /* Connection-less mode has a TID first */
-       if (    (pinfo->match_port == UDP_PORT_WSP) ||
-                       (pinfo->match_port == UDP_PORT_WTLS_WSP))
+       if (is_connectionless)
        {
                offset++;
        };
@@ -483,213 +1201,327 @@ dissect_wsp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
        pdut = tvb_get_guint8 (tvb, offset);
 
        /* Develop the string to put in the Info column */
-       if (check_col(fdata, COL_INFO))
+       if (check_col(pinfo->cinfo, COL_INFO))
        {
-               col_add_fstr(fdata, COL_INFO, "WSP %s",
+               col_append_fstr(pinfo->cinfo, COL_INFO, "WSP %s",
                        val_to_str (pdut, vals_pdu_type, "Unknown PDU type (0x%02x)"));
        };
 
 /* In the interest of speed, if "tree" is NULL, don't do any work not
    necessary to generate protocol tree items. */
        if (tree) {
-               ti = proto_tree_add_item(tree, proto_wsp, tvb, 0,
-                   tvb_length(tvb), bo_little_endian);
+               ti = proto_tree_add_item(tree, proto_wsp, tvb, 0, -1,
+                   bo_little_endian);
                wsp_tree = proto_item_add_subtree(ti, ett_wsp);
 
 /* Code to process the packet goes here */
 /*
-                       wsp_header_fixed = proto_item_add_subtree(ti, ett_header );
+               wsp_header_fixed = proto_item_add_subtree(ti, ett_header );
 */
 
-                       /* Add common items: only TID and PDU Type */
+               /* Add common items: only TID and PDU Type */
 
-                       /* If this is connectionless, then the TID Field is always first */
-                       if (    (pinfo->match_port == UDP_PORT_WSP) ||
-                                       (pinfo->match_port == UDP_PORT_WTLS_WSP))
-                       {
-                               ti = proto_tree_add_item (wsp_tree, hf_wsp_header_tid,tvb,
-                                       0,1,bo_little_endian);
-                       }
+               /* If this is connectionless, then the TID Field is always first */
+               if (is_connectionless)
+               {
+                       ti = proto_tree_add_item (wsp_tree, hf_wsp_header_tid,tvb,
+                               0,1,bo_little_endian);
+               }
 
-                       ti = proto_tree_add_item(
-                                       wsp_tree,               /* tree */
-                                       hf_wsp_header_pdu_type,         /* id */
-                                       tvb, 
-                                       offset++,                       /* start of high light */
-                                       1,                              /* length of high light */
-                                       bo_little_endian                                /* value */
-                            );
+               ti = proto_tree_add_item(
+                               wsp_tree,               /* tree */
+                               hf_wsp_header_pdu_type, /* id */
+                               tvb, 
+                               offset,                 /* start of high light */
+                               1,                      /* length of high light */
+                               bo_little_endian        /* value */
+                    );
+       }
+       offset++;
 
-                       switch (pdut)
-                       {
-                               case CONNECT:
+       switch (pdut)
+       {
+               case CONNECT:
+               case CONNECTREPLY:
+               case RESUME:
+                       if (tree) {
+                               if (pdut == CONNECT)
+                               {
                                        ti = proto_tree_add_item (wsp_tree, hf_wsp_version_major,tvb,offset,1,bo_little_endian);
                                        ti = proto_tree_add_item (wsp_tree, hf_wsp_version_minor,tvb,offset,1,bo_little_endian);
                                        offset++;
-                                       capabilityStart = offset;
+                               } else {
                                        count = 0;      /* Initialise count */
-                                       capabilityLength = tvb_get_guintvar (tvb, offset, &count);
+                                       value = tvb_get_guintvar (tvb, offset, &count);
+                                       ti = proto_tree_add_uint (wsp_tree, hf_wsp_server_session_id,tvb,offset,count,value);
                                        offset += count;
-                                       ti = proto_tree_add_uint (wsp_tree, hf_wsp_capability_length,tvb,capabilityStart,count,capabilityLength);
+                               }
+                               capabilityStart = offset;
+                               count = 0;      /* Initialise count */
+                               capabilityLength = tvb_get_guintvar (tvb, offset, &count);
+                               offset += count;
+                               ti = proto_tree_add_uint (wsp_tree, hf_wsp_capability_length,tvb,capabilityStart,count,capabilityLength);
 
-                                       headerStart = offset;
+                               if (pdut != RESUME)
+                               {
                                        count = 0;      /* Initialise count */
                                        headerLength = tvb_get_guintvar (tvb, offset, &count);
+                                       ti = proto_tree_add_uint (wsp_tree, hf_wsp_header_length,tvb,offset,count,headerLength);
                                        offset += count;
-                                       ti = proto_tree_add_uint (wsp_tree, hf_wsp_header_length,tvb,headerStart,count,headerLength);
-                                       if (capabilityLength > 0)
-                                       {
-                                               ti = proto_tree_add_item (wsp_tree, hf_wsp_capabilities_section,tvb,offset,capabilityLength,bo_little_endian);
-                                               wsp_capabilities = proto_item_add_subtree( ti, ett_capabilities );
-                                               offset += capabilityLength;
-                                       }
+                                       capabilityStart = offset;
+                                       headerStart = capabilityStart + capabilityLength;
+                               } else {
+                                               /* Resume computes the headerlength by remaining bytes */
+                                       capabilityStart = offset;
+                                       headerStart = capabilityStart + capabilityLength;
+                                       headerLength = tvb_reported_length_remaining (tvb, headerStart);
+                               }
+                               if (capabilityLength > 0)
+                               {
+                                       tmp_tvb = tvb_new_subset (tvb, offset, capabilityLength, capabilityLength);
+                                       add_capabilities (wsp_tree, tmp_tvb, pdut);
+                                       offset += capabilityLength;
+                               }
 
-                                       if (headerLength > 0)
-                                       {
-                                               tmp_tvb = tvb_new_subset (tvb, offset, headerLength, headerLength);
-                                               add_headers (wsp_tree, tmp_tvb);
-                                       }
+                               if (headerLength > 0)
+                               {
+                                       tmp_tvb = tvb_new_subset (tvb, offset, headerLength, headerLength);
+                                       add_headers (wsp_tree, tmp_tvb);
+                               }
+                       }
 
-                                       break;
+                       break;
 
-                               case CONNECTREPLY:
-                                       count = 0;      /* Initialise count */
-                                       value = tvb_get_guintvar (tvb, offset, &count);
-                                       ti = proto_tree_add_uint (wsp_tree, hf_wsp_server_session_id,tvb,offset,count,value);
-                                       offset += count;
+               case REDIRECT:
+                       dissect_redirect(tvb, offset, pinfo, wsp_tree,
+                         dissector_handle);
+                       break;
 
-                                       capabilityStart = offset;
-                                       count = 0;      /* Initialise count */
-                                       capabilityLength = tvb_get_guintvar (tvb, offset, &count);
-                                       offset += count;
-                                       ti = proto_tree_add_uint (wsp_tree, hf_wsp_capability_length,tvb,capabilityStart,count,capabilityLength);
+               case DISCONNECT:
+               case SUSPEND:
+                       if (tree) {
+                               count = 0;      /* Initialise count */
+                               value = tvb_get_guintvar (tvb, offset, &count);
+                               ti = proto_tree_add_uint (wsp_tree, hf_wsp_server_session_id,tvb,offset,count,value);
+                       }
+                       break;
 
-                                       headerStart = offset;
-                                       count = 0;      /* Initialise count */
-                                       headerLength = tvb_get_guintvar (tvb, offset, &count);
-                                       offset += count;
-                                       ti = proto_tree_add_uint (wsp_tree, hf_wsp_header_length,tvb,headerStart,count,headerLength);
-                                       if (capabilityLength > 0)
-                                       {
-                                               ti = proto_tree_add_item (wsp_tree, hf_wsp_capabilities_section,tvb,offset,capabilityLength,bo_little_endian);
-                                               wsp_capabilities = proto_item_add_subtree( ti, ett_capabilities );
-                                               offset += capabilityLength;
-                                       }
+               case GET:
+                       count = 0;      /* Initialise count */
+                               /* Length of URI and size of URILen field */
+                       value = tvb_get_guintvar (tvb, offset, &count);
+                       nextOffset = offset + count;
+                       add_uri (wsp_tree, pinfo, tvb, offset, nextOffset);
+                       if (tree) {
+                               offset += (value+count); /* VERIFY */
+                               tmp_tvb = tvb_new_subset (tvb, offset, -1, -1);
+                               add_headers (wsp_tree, tmp_tvb);
+                       }
+                       break;
 
-                                       if (headerLength > 0)
-                                       {
+               case POST:
+                       uriStart = offset;
+                       count = 0;      /* Initialise count */
+                       uriLength = tvb_get_guintvar (tvb, offset, &count);
+                       headerStart = uriStart+count;
+                       count = 0;      /* Initialise count */
+                       headersLength = tvb_get_guintvar (tvb, headerStart, &count);
+                       offset = headerStart + count;
 
-                                               /*
-                                               ti = proto_tree_add_item (wsp_tree, hf_wsp_headers_section,tvb,offset,headerLength,bo_little_endian);
-                                               wsp_headers = proto_item_add_subtree( ti, ett_headers );
-                                               */
-                                               tmp_tvb = tvb_new_subset (tvb, offset, headerLength, headerLength);
-                                               add_headers (wsp_tree, tmp_tvb);
-                                       }
+                       add_uri (wsp_tree, pinfo, tvb, uriStart, offset);
+                       if (tree) {
+                               offset += uriLength;
 
-                                       break;
+                               ti = proto_tree_add_uint (wsp_tree, hf_wsp_header_length,tvb,headerStart,count,headersLength);
 
-                               case DISCONNECT:
-                                       count = 0;      /* Initialise count */
-                                       value = tvb_get_guintvar (tvb, offset, &count);
-                                       ti = proto_tree_add_uint (wsp_tree, hf_wsp_server_session_id,tvb,offset,count,value);
+                               if (headersLength == 0)
                                        break;
 
-                               case GET:
-                                       count = 0;      /* Initialise count */
-                                       /* Length of URI and size of URILen field */
-                                       value = tvb_get_guintvar (tvb, offset, &count);
-                                       nextOffset = offset + count;
-                                       add_uri (wsp_tree, tvb, offset, nextOffset);
-                                       offset += (value+count); /* VERIFY */
-                                       tmp_tvb = tvb_new_subset (tvb, offset, -1, -1);
-                                       add_headers (wsp_tree, tmp_tvb);
-                                       break;
+                               contentTypeStart = offset;
+                               nextOffset = add_content_type (wsp_tree,
+                                   tvb, offset, &contentType,
+                                   &contentTypeStr);
 
-                               case POST:
-                                       uriStart = offset;
-                                       count = 0;      /* Initialise count */
-                                       uriLength = tvb_get_guintvar (tvb, offset, &count);
-                                       headerStart = uriStart+count;
-                                       count = 0;      /* Initialise count */
-                                       headersLength = tvb_get_guintvar (tvb, headerStart, &count);
-                                       offset = headerStart + count;
+                               /* Add headers subtree that will hold the headers fields */
+                               /* Runs from nextOffset for headersLength-(length of content-type field)*/
+                               headerLength = headersLength-(nextOffset-contentTypeStart);
+                               if (headerLength > 0)
+                               {
+                                       tmp_tvb = tvb_new_subset (tvb, nextOffset, headerLength, headerLength);
+                                       add_headers (wsp_tree, tmp_tvb);
+                               }
 
-                                       add_uri (wsp_tree, tvb, uriStart, offset);
-                                       offset += uriLength;
+                               /* TODO: Post DATA */
+                               /* Runs from start of headers+headerLength to end of frame */
+                               offset = nextOffset+headerLength;
+                               tmp_tvb = tvb_new_subset (tvb, offset, tvb_reported_length (tvb)-offset, tvb_reported_length (tvb)-offset);
+                               add_post_data (wsp_tree, tmp_tvb,
+                                   contentType, contentTypeStr);
+                       }
+                       if (tvb_reported_length_remaining(tvb, headerStart + count + uriLength + headersLength) > 0)
+                       {
+                               tmp_tvb = tvb_new_subset (tvb, headerStart + count + uriLength + headersLength, -1, -1);
+                               if (!dissector_try_port(wsp_dissector_table, contentType, tmp_tvb, pinfo, tree))
+                                       dissector_try_heuristic(heur_subdissector_list, tmp_tvb, pinfo, tree);
+                       }
+                       break;
 
-                                       ti = proto_tree_add_item (wsp_tree, hf_wsp_header_length,tvb,headerStart,count,bo_little_endian);
+               case REPLY:
+                       count = 0;      /* Initialise count */
+                       headersLength = tvb_get_guintvar (tvb, offset+1, &count);
+                       headerStart = offset + count + 1;
+                       if (tree) {
+                               ti = proto_tree_add_item (wsp_tree, hf_wsp_header_status,tvb,offset,1,bo_little_endian);
+                               nextOffset = offset + 1 + count;
+                               ti = proto_tree_add_uint (wsp_tree, hf_wsp_header_length,tvb,offset+1,count,headersLength);
+
+                               if (headersLength == 0)
+                                       break;
 
-                                       contentTypeStart = offset;
-                                       nextOffset = add_content_type (wsp_tree, tvb, offset, &contentType);
+                               contentTypeStart = nextOffset;
+                               nextOffset = add_content_type (wsp_tree,
+                                   tvb, nextOffset, &contentType,
+                                   &contentTypeStr);
 
-                                       /* Add headers subtree that will hold the headers fields */
-                                       /* Runs from nextOffset for value-(length of content-type field)*/
-                                       headerLength = headersLength-(nextOffset-contentTypeStart);
+                               /* Add headers subtree that will hold the headers fields */
+                               /* Runs from nextOffset for headersLength-(length of content-type field)*/
+                               headerLength = headersLength-(nextOffset-contentTypeStart);
+                               if (headerLength > 0)
+                               {
                                        tmp_tvb = tvb_new_subset (tvb, nextOffset, headerLength, headerLength);
                                        add_headers (wsp_tree, tmp_tvb);
+                               }
+                               offset += count+headersLength+1;
 
-                                       /* TODO: Post DATA */
-                                       /* Runs from start of headers+headerLength to END_OF_FRAME */
-                                       offset = nextOffset+headerLength;
-                                       tmp_tvb = tvb_new_subset (tvb, offset, tvb_reported_length (tvb)-offset, tvb_reported_length (tvb)-offset);
-                                       add_post_data (wsp_tree, tmp_tvb, contentType);
-                                       break;
+                               /* TODO: Data - decode WMLC */
+                               /* Runs from offset+1+count+headerLength+1 to end of frame */
+                               if (tvb_reported_length_remaining (tvb, offset) > 0)
+                               {
+                                       ti = proto_tree_add_item (wsp_tree, hf_wsp_reply_data,tvb,offset,-1,bo_little_endian);
+                               }
+                       }
+                       if (tvb_reported_length_remaining(tvb, headerStart + headersLength) > 0)
+                       {
+                               tmp_tvb = tvb_new_subset (tvb, headerStart + headersLength, -1, -1);
+                               if (!dissector_try_port(wsp_dissector_table, contentType, tmp_tvb, pinfo, tree))
+                                       dissector_try_heuristic(heur_subdissector_list, tmp_tvb, pinfo, tree);
+                       }
+                       break;
 
-                               case REPLY:
-                                       ti = proto_tree_add_item (wsp_tree, hf_wsp_header_status,tvb,offset,1,bo_little_endian);
-                                       count = 0;      /* Initialise count */
-                                       value = tvb_get_guintvar (tvb, offset+1, &count);
-                                       nextOffset = offset + 1 + count;
-                                       ti = proto_tree_add_uint (wsp_tree, hf_wsp_header_length,tvb,offset+1,count,value);
+               case PUSH:
+               case CONFIRMEDPUSH:
+                       count = 0;      /* Initialise count */
+                       headersLength = tvb_get_guintvar (tvb, offset, &count);
+                       headerStart = offset + count;
+
+                       if (tree) {
+                               ti = proto_tree_add_uint (wsp_tree, hf_wsp_header_length,tvb,offset,count,headersLength);
 
-                                       contentTypeStart = nextOffset;
-                                       nextOffset = add_content_type (wsp_tree, tvb, nextOffset, &contentType);
+                               if (headersLength == 0)
+                                       break;
+
+                               offset += count;
+                               contentTypeStart = offset;
+                               nextOffset = add_content_type (wsp_tree,
+                                   tvb, offset, &contentType,
+                                   &contentTypeStr);
 
-                                       /* Add headers subtree that will hold the headers fields */
-                                       /* Runs from nextOffset for value-(length of content-type field)*/
-                                       headerLength = value-(nextOffset-contentTypeStart);
+                               /* Add headers subtree that will hold the headers fields */
+                               /* Runs from nextOffset for headersLength-(length of content-type field)*/
+                               headerLength = headersLength-(nextOffset-contentTypeStart);
+                               if (headerLength > 0)
+                               {
                                        tmp_tvb = tvb_new_subset (tvb, nextOffset, headerLength, headerLength);
                                        add_headers (wsp_tree, tmp_tvb);
-                                       offset += count+value+1;
+                               }
+                               offset += headersLength;
 
-                                       /* TODO: Data - decode WMLC */
-                                       /* Runs from offset+1+count+value+1 to END_OF_FRAME */
-                                       if (offset < tvb_reported_length (tvb))
-                                       {
-                                               ti = proto_tree_add_item (wsp_tree, hf_wsp_reply_data,tvb,offset,END_OF_FRAME,bo_little_endian);
-                                       }
-                                       break;
+                               /* Push DATA */
+                               if (tvb_reported_length_remaining (tvb, offset) > 0)
+                               {
+                                       ti = proto_tree_add_item (wsp_tree, hf_wsp_push_data,tvb,offset,-1,bo_little_endian);
+                               }
                        }
+                       if (tvb_reported_length_remaining(tvb, headerStart + headersLength) > 0)
+                       {
+                               tmp_tvb = tvb_new_subset (tvb, headerStart + headersLength, -1, -1);
+                               if (!dissector_try_port(wsp_dissector_table, contentType, tmp_tvb, pinfo, tree))
+                                       dissector_try_heuristic(heur_subdissector_list, tmp_tvb, pinfo, tree);
+                       }
+                       break;
+
        }
 }
 
+/*
+ * Called directly from UDP.
+ * Put "WSP" into the "Protocol" column.
+ */
+static void
+dissect_wsp_fromudp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+       if (check_col(pinfo->cinfo, COL_PROTOCOL))
+               col_set_str(pinfo->cinfo, COL_PROTOCOL, "WSP" );
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_clear(pinfo->cinfo, COL_INFO);
+
+       dissect_wsp_common(tvb, pinfo, tree, wsp_fromudp_handle, TRUE);
+}
+
+/*
+ * Called from a higher-level WAP dissector, in connection-oriented mode.
+ * Leave the "Protocol" column alone - the dissector calling us should
+ * have set it.
+ */
+static void
+dissect_wsp_fromwap_co(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+       /*
+        * XXX - what about WTLS->WTP->WSP?
+        */
+       dissect_wsp_common(tvb, pinfo, tree, wtp_fromudp_handle, FALSE);
+}
+
+/*
+ * Called from a higher-level WAP dissector, in connectionless mode.
+ * Leave the "Protocol" column alone - the dissector calling us should
+ * have set it.
+ */
+static void
+dissect_wsp_fromwap_cl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+       /*
+        * XXX - what about WTLS->WSP?
+        */
+       if (check_col(pinfo->cinfo, COL_INFO))
+       {
+               col_clear(pinfo->cinfo, COL_INFO);
+       }
+       dissect_wsp_common(tvb, pinfo, tree, wtp_fromudp_handle, TRUE);
+}
+
 static void
-add_uri (proto_tree *tree, tvbuff_t *tvb, guint URILenOffset, guint URIOffset)
+add_uri (proto_tree *tree, packet_info *pinfo, tvbuff_t *tvb, guint URILenOffset, guint URIOffset)
 {
        proto_item *ti;
-       guint8 terminator = 0;
        char *newBuffer;
 
        guint count = 0;
        guint uriLen = tvb_get_guintvar (tvb, URILenOffset, &count);
 
-       ti = proto_tree_add_uint (tree, hf_wsp_header_uri_len,tvb,URILenOffset,count,uriLen);
-
-       /* If string doesn't end with a 0x00, we need to add one to be on the safe side */
-       terminator = tvb_get_guint8 (tvb, URIOffset+uriLen-1);
-       if (terminator != 0)
-       {
-               newBuffer = g_malloc (uriLen+1);
-               strncpy (newBuffer, tvb_get_ptr (tvb, URIOffset, uriLen), uriLen);
-               newBuffer[uriLen] = 0;
-               ti = proto_tree_add_string (tree, hf_wsp_header_uri,tvb,URIOffset,uriLen,newBuffer);
-               g_free (newBuffer);
-       }
-       else
-       {
-               ti = proto_tree_add_item (tree, hf_wsp_header_uri,tvb,URIOffset,uriLen,bo_little_endian);
-       }
+       if (tree)
+               ti = proto_tree_add_uint (tree, hf_wsp_header_uri_len,tvb,URILenOffset,count,uriLen);
+
+       newBuffer = g_malloc (uriLen+2);
+       newBuffer[0] = ' ';  /* This is for COL_INFO */
+       strncpy (newBuffer+1, tvb_get_ptr (tvb, URIOffset, uriLen), uriLen);
+       newBuffer[uriLen+1] = 0;
+       if (tree)
+               ti = proto_tree_add_string (tree, hf_wsp_header_uri,tvb,URIOffset,uriLen,newBuffer+1);
+       if (check_col(pinfo->cinfo, COL_INFO)) {
+               col_append_str(pinfo->cinfo, COL_INFO, newBuffer);
+       };
+       g_free (newBuffer);
 }
 
 static void
@@ -699,13 +1531,9 @@ add_headers (proto_tree *tree, tvbuff_t *tvb)
        proto_tree *wsp_headers;
        guint offset = 0;
        guint headersLen = tvb_reported_length (tvb);
-       guint8 headerStart = 0;
+       guint headerStart = 0;
        guint peek = 0;
-       tvbuff_t *header_buff;
-       tvbuff_t *value_buff;
-       guint count = 0;
-       guint valueStart = 0;
-       guint valueEnd = 0;
+       guint pageCode = 1;
 
 #ifdef DEBUG
        fprintf (stderr, "dissect_wsp: Offset is %d, size is %d\n", offset, headersLen);
@@ -732,661 +1560,2251 @@ add_headers (proto_tree *tree, tvbuff_t *tvb)
                headerStart = offset;
                peek = tvb_get_guint8 (tvb, headerStart);
 
-               if (peek < 32)          /* Short-cut shift delimeter */
+               if (peek < 32)          /* Short-cut shift delimiter */
                {
-                       fprintf (stderr, "dissect_wsp: header: short-cut shift %d (0x%02X)\n", peek, peek);
-                       offset++;
+                       pageCode = peek;
+                       proto_tree_add_uint (wsp_headers,
+                           hf_wsp_header_shift_code, tvb, offset, 1,
+                           pageCode);
+                       offset += 1;
+                       continue;
                }
-               else if (peek == 0x7F)  /* Shift delimeter */
+               else if (peek == 0x7F)  /* Shift delimiter */
                {
-                       fprintf (stderr, "dissect_wsp: header: shift delimeter %d (0x%02X)\n", peek, peek);
-                       offset++;
+                       pageCode = tvb_get_guint8(tvb, offset+1);
+                       proto_tree_add_uint (wsp_headers,
+                           hf_wsp_header_shift_code, tvb, offset, 2,
+                           pageCode);
+                       offset += 2;
+                       continue;
                }
                else if (peek < 127)
                {
 #ifdef DEBUG
                        fprintf (stderr, "dissect_wsp: header: application-header start %d (0x%02X)\n", peek, peek);
 #endif
-                       while (tvb_get_guint8 (tvb, offset++)) { /* Do nothing, just look for NULL */ }
+                       /*
+                        * Token-text, followed by Application-specific-value.
+                        */
+                       offset = add_application_header (wsp_headers, tvb,
+                           headerStart);
                }
-               else if (peek & 0x80)   /* Well-known header */
+               else if (peek & 0x80)
                {
 #ifdef DEBUG
                        fprintf (stderr, "dissect_wsp: header: well-known %d (0x%02X)\n", peek, peek);
 #endif
-                       offset++;
-               }
-
-               /* Get value part of header */
-               valueStart = offset;
-               peek = tvb_get_guint8 (tvb, valueStart);
-               if (peek <= 30)
-               {
-#ifdef DEBUG
-                       fprintf (stderr, "dissect_wsp: Looking for %d octets\n", peek);
-#endif
-                       /* VERIFY: valueStart++; */
-                       valueEnd = offset+1+peek;
-                       offset += (peek+1);
-               }
-               else if (peek == 31)
-               {
-#ifdef DEBUG
-                       fprintf (stderr, "dissect_wsp: Looking for uintvar octets\n");
-#endif
-                       count = 0;      /* Initialise count */
-                       tvb_get_guintvar (tvb, valueStart, &count);
-                       valueEnd = offset+1+count;
-                       offset += (count+1);
-               }
-               else if (peek <= 127)
-               {
-#ifdef DEBUG
-                       fprintf (stderr, "dissect_wsp: Looking for NULL-terminated string\n");
-#endif
-                       valueEnd = valueStart+1;
-                       while (tvb_get_guint8 (tvb, valueEnd++)) { /* Do nothing, just look for NULL */ }
-                       offset = valueEnd;
-               }
-               else
-               {
-#ifdef DEBUG
-                       fprintf (stderr, "dissect_wsp: Value is %d\n", (peek & 0x7F));
-#endif
-                       valueEnd = offset+1;
-                       offset++;
-               }
-#ifdef DEBUG
-               fprintf (stderr, "dissect_wsp: Creating value buffer from offset %d, size=%d\n", headerStart, (offset-headerStart));
-#endif
+                       /*
+                        * Well-known-header; the lower 7 bits of "peek"
+                        * are the header code.
+                        */
+                       switch (pageCode) {
+                       case 1:
+                               offset = add_well_known_header (wsp_headers,
+                                   tvb, headerStart, peek & 0x7F);
+                               break;
 
-               header_buff = tvb_new_subset (tvb, headerStart, (offset-headerStart), (offset-headerStart));
-               value_buff = tvb_new_subset (tvb, valueStart, (valueEnd-valueStart), (valueEnd-valueStart));
+                       case 2:
+                       case 16:
+                               offset = add_well_known_openwave_header (wsp_headers,
+                                   tvb, headerStart, peek & 0x7F);
+                               break;
 
-               add_header (wsp_headers, header_buff, value_buff);
+                       default:
+                               offset = add_unknown_header (wsp_headers,
+                                   tvb, headerStart, peek & 0x7F);
+                               break;
+                       }
+               }
        }
 }
 
-static void
-add_header (proto_tree *tree, tvbuff_t *header_buff, tvbuff_t *value_buff)
+static int
+add_well_known_header (proto_tree *tree, tvbuff_t *tvb, int offset,
+    guint8 headerType)
 {
-       guint offset = 0;
-       guint valueStart = 0;
-       guint8 headerType = 0;
-       proto_item *ti;
-       guint headerLen = tvb_reported_length (header_buff);
-       guint valueLen = tvb_reported_length (value_buff);
-       guint peek = 0;
-       struct timeval timeValue;
-       guint count = 0;
-       guint value = 0;
-       guint valueLength = 0;
-       char valString[100];
-       char *valMatch = NULL;
-       double q_value = 1.0;
-
-       /* Initialise time values */
-       timeValue.tv_sec=0;
-       timeValue.tv_usec = 0;
+       int headerStart;
+       value_type_t valueType;
+       int headerLen;
+       guint valueLen;
+       int valueStart;
+       tvbuff_t *header_buff;
+       tvbuff_t *value_buff;
 
-       headerType = tvb_get_guint8 (header_buff, 0);
-       peek = tvb_get_guint8 (value_buff, 0);
 #ifdef DEBUG
        fprintf (stderr, "dissect_wsp: Got header 0x%02x\n", headerType);
-       fprintf (stderr, "dissect_wsp: First value octet is 0x%02x\n", peek);
 #endif
+       headerStart = offset;
 
-       if (headerType == 0x7F)
-       {
-       }
-       else if (headerType < 0x1F)
-       {
-       }
-       else if (headerType & 0x80)
-       {
-               headerType = headerType & 0x7F;
-               switch (headerType)
-               {
-                       case 0x00:              /* Accept */
-                               if (peek & 0x80)
-                               {
-                                       proto_tree_add_uint (tree, hf_wsp_header_accept, header_buff, offset, headerLen, (peek & 0x7F));
-                               }
-                               else
-                               {
-                                       proto_tree_add_string (tree, hf_wsp_header_accept_str,header_buff,offset,headerLen,tvb_get_ptr (value_buff, 0, valueLen));
-                               }
-                               break;
+       /*
+        * Skip the Short-Integer header type.
+        */
+       offset++;
 
-                       case 0x01:              /* Accept-Charset */
-                               if (peek <= 31)                         /* Accept-charset-general-form */
-                               {
-                                       /* Get Value-Length */
-                                       valueLength = get_value_length (value_buff, offset,
-                                               &valueStart);
-                                       offset = valueStart;
-
-                                       peek = tvb_get_guint8 (value_buff, offset);
-                                       if ((peek >= 0x80) || (peek <= 30))     /* Well-known-charset */
-                                       {
-                                               if (peek == 0x80)               /* Any */
-                                               {
-                                                       value = peek;
-                                                       offset++;
-                                               }
-                                               else if (peek & 0x80)   /* Short-Integer */
-                                               {
-                                                       value = peek & 0x7F;
-                                                       offset++;
-                                               }
-                                               else if (peek <= 30)    /* Long-Integer */
-                                               {
-                                                       offset++;
+       /*
+        * Get the value type and length (or, if the type is VALUE_IN_LEN,
+        * meaning the value is a Short-integer, get the value type
+        * and the value itself).
+        */ 
+       valueType = get_value_type_len (tvb, offset, &valueLen,
+           &valueStart, &offset);
+       headerLen = offset - headerStart;
+
+       /*
+        * Get a tvbuff for the entire header.
+        * XXX - cut the actual length short so that it doesn't run
+        * past the actual length of tvb.
+        */
+       header_buff = tvb_new_subset (tvb, headerStart, headerLen,
+           headerLen);
+
+       /*
+        * If the value wasn't in the length, get a tvbuff for the value.
+        * XXX - can valueLen be 0?
+        * XXX - cut the actual length short so that it doesn't run
+        * past the actual length of tvb.
+        */
+       if (valueType != VALUE_IN_LEN) {
+               value_buff = tvb_new_subset (tvb, valueStart, valueLen,
+                   valueLen);
+       } else {
+               /*
+                * XXX - when the last dissector is tvbuffified,
+                * so that NULL is no longer a valid tvb pointer
+                * value in "proto_tree_add" calls, just
+                * set "value_buff" to NULL.
+                *
+                * XXX - can we already do that?  I.e., will that
+                * cause us always to crash if we mistakenly try
+                * to fetch the value of a VALUE_IN_LEN item?
+                */
+               value_buff = tvb_new_subset (tvb, headerStart, 0, 0);
+       }
+
+       switch (headerType) {
+
+       case FN_ACCEPT:                 /* Accept */
+               add_accept_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen);
+               break;
+
+       case FN_ACCEPT_CHARSET_DEP:     /* Accept-Charset */
+               /*
+                * XXX - should both encoding versions 1.1 and
+                * 1.3 be handled this way?
+                */
+               add_accept_xxx_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen,
+                   hf_wsp_header_accept_charset,
+                   hf_wsp_header_accept_charset_str,
+                   vals_character_sets, "Unknown charset (%u)");
+               break;
+
+       case FN_ACCEPT_LANGUAGE:        /* Accept-Language */
+               add_accept_xxx_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen,
+                   hf_wsp_header_accept_language,
+                   hf_wsp_header_accept_language_str,
+                   vals_languages, "Unknown language (%u)");
+               break;
+
+       case FN_ACCEPT_RANGES:          /* Accept-Ranges */
+               add_accept_ranges_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen);
+               break;
+
+       case FN_AGE:                    /* Age */
+               add_integer_value_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen, hf_wsp_header_age,
+                   headerType);
+               break;
+
+       case FN_CACHE_CONTROL_DEP:      /* Cache-Control */
+       case FN_CACHE_CONTROL:
+       case FN_CACHE_CONTROL14:
+               /*
+                * XXX - is the only difference in the three different
+                * versions (1.1, 1.3, 1.4) really only S_MAXAGE?
+                */
+               add_cache_control_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen);
+               break;
+                               
+       case FN_CONNECTION:     /* Connection */
+               add_connection_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen);
+               break;
+
+       case FN_CONTENT_LENGTH:         /* Content-Length */
+               add_integer_value_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen,
+                   hf_wsp_header_content_length,
+                   headerType);
+               break;
+                               
+       case FN_DATE:                   /* Date */
+               add_date_value_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen,
+                   hf_wsp_header_date, headerType);
+               break;
+
+       case FN_ETAG:                   /* Etag */
+               add_string_value_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen,
+                   hf_wsp_header_etag, headerType);
+               break;
+
+       case FN_EXPIRES:                /* Expires */
+               add_date_value_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen,
+                   hf_wsp_header_expires, headerType);
+               break;
+
+       case FN_IF_MODIFIED_SINCE:      /* If-Modified-Since */
+               add_date_value_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen,
+                   hf_wsp_header_if_modified_since, headerType);
+               break;
+                               
+       case FN_LOCATION:               /* Location */
+               add_string_value_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen,
+                   hf_wsp_header_location, headerType);
+               break;
+
+       case FN_LAST_MODIFIED:          /* Last-Modified */
+               add_date_value_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen,
+                   hf_wsp_header_last_modified, headerType);
+               break;
+                               
+       case FN_PRAGMA:                 /* Pragma */
+               add_pragma_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen);
+               break;
+                               
+       case FN_SERVER:                 /* Server */
+               add_string_value_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen,
+                   hf_wsp_header_server, headerType);
+               break;
+
+       case FN_TRANSFER_ENCODING:      /* Transfer-Encoding */
+               add_transfer_encoding_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen);
+               break;
+
+       case FN_USER_AGENT:             /* User-Agent */
+               add_string_value_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen,
+                   hf_wsp_header_user_agent, headerType);
+               break;
+
+       case FN_VIA:                    /* Via */
+               add_string_value_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen,
+                   hf_wsp_header_via, headerType);
+               break;
+
+       case FN_WARNING:                /* Warning */
+               add_warning_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen);
+               break;
+
+       case FN_ACCEPT_APPLICATION:     /* Accept-Application */
+               add_accept_application_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen);
+               break;
+
+       case FN_BEARER_INDICATION:      /* Bearer-Indication */
+               add_integer_value_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen,
+                   hf_wsp_header_bearer_indication, headerType);
+               break;
+
+       case FN_PROFILE:                /* Profile */
+               add_string_value_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen,
+                   hf_wsp_header_profile, headerType);
+               break;
+
+       case FN_X_WAP_APPLICATION_ID:   /* X-Wap-Application-Id */
+               add_wap_application_id_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen);
+               break;
+
+       case FN_CONTENT_ID:             /* Content-ID   */
+               add_quoted_string_value_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen,
+                   hf_wsp_header_content_ID, headerType);
+               break;
+
+       default:
+               proto_tree_add_text (tree, header_buff, 0, headerLen,
+                   "Unsupported Header: %s",
+                   val_to_str (headerType, vals_field_names, "Unknown (0x%02X)"));
+               break;
+       }
+       return offset;
+}
+
+
+static int
+add_well_known_openwave_header (proto_tree *tree, tvbuff_t *tvb, int offset,
+    guint8 headerType)
+{
+       int headerStart;
+       value_type_t valueType;
+       int headerLen;
+       guint valueLen;
+       int valueStart;
+       tvbuff_t *header_buff;
+       tvbuff_t *value_buff;
+
+#ifdef DEBUG
+       fprintf (stderr, "dissect_wsp: Got Openwave header 0x%02x\n", headerType);
+#endif
+       headerStart = offset;
+
+       /*
+        * Skip the Short-Integer header type.
+        */
+       offset++;
+
+       /*
+        * Get the value type and length (or, if the type is VALUE_IN_LEN,
+        * meaning the value is a Short-integer, get the value type
+        * and the value itself).
+        */ 
+       valueType = get_value_type_len (tvb, offset, &valueLen,
+           &valueStart, &offset);
+       headerLen = offset - headerStart;
+
+       /*
+        * Get a tvbuff for the entire header.
+        * XXX - cut the actual length short so that it doesn't run
+        * past the actual length of tvb.
+        */
+       header_buff = tvb_new_subset (tvb, headerStart, headerLen,
+           headerLen);
+
+       /*
+        * If the value wasn't in the length, get a tvbuff for the value.
+        * XXX - can valueLen be 0?
+        * XXX - cut the actual length short so that it doesn't run
+        * past the actual length of tvb.
+        */
+       if (valueType != VALUE_IN_LEN) {
+               value_buff = tvb_new_subset (tvb, valueStart, valueLen,
+                   valueLen);
+       } else {
+               /*
+                * XXX - when the last dissector is tvbuffified,
+                * so that NULL is no longer a valid tvb pointer
+                * value in "proto_tree_add" calls, just
+                * set "value_buff" to NULL.
+                *
+                * XXX - can we already do that?  I.e., will that
+                * cause us always to crash if we mistakenly try
+                * to fetch the value of a VALUE_IN_LEN item?
+                */
+               value_buff = tvb_new_subset (tvb, headerStart, 0, 0);
+       }
+
+       switch (headerType) {
+
+/*     case FN_OPENWAVE_PROXY_PUSH_ADDR:       / x-up-proxy-push-addr */
+/*             add_openwave_push_address_header (tree, header_buff, headerLen, */
+/*                 value_buff, valueType, valueLen); */
+/*             break; */
+
+       case FN_OPENWAVE_PROXY_PUSH_ACCEPT:     /* x-up-proxy-push-accept */
+               add_accept_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen);
+               break;
+
+       case FN_OPENWAVE_PROXY_PUSH_SEQ:        /* x-up-proxy-push-seq */
+               add_openwave_integer_value_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen, hf_wsp_header_openwave_proxy_push_seq,
+                   headerType);
+               break;
+
+       case FN_OPENWAVE_PROXY_NOTIFY:          /* x-up-proxy-notify */
+               add_openwave_integer_value_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen, hf_wsp_header_openwave_proxy_notify,
+                   headerType);
+               break;
+
+       case FN_OPENWAVE_PROXY_OPERATOR_DOMAIN: /* x-up-proxy-operator-domain */
+               add_openwave_string_value_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen,
+                   hf_wsp_header_openwave_proxy_operator_domain, headerType);
+               break;
+
+       case FN_OPENWAVE_PROXY_HOME_PAGE:       /* x-up-proxy-home-page */
+               add_openwave_string_value_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen,
+                   hf_wsp_header_openwave_proxy_home_page, headerType);
+               break;
+
+       case FN_OPENWAVE_DEVCAP_HAS_COLOR:      /* x-up-devcap-has-color */
+               add_openwave_integer_value_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen, hf_wsp_header_openwave_devcap_has_color,
+                   headerType);
+               break;
+
+       case FN_OPENWAVE_DEVCAP_NUM_SOFTKEYS:   /* x-up-devcap-num-softkeys */
+               add_openwave_integer_value_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen, hf_wsp_header_openwave_devcap_num_softkeys,
+                   headerType);
+               break;
+
+       case FN_OPENWAVE_DEVCAP_SOFTKEY_SIZE:   /* x-up-devcap-softkey-size */
+               add_openwave_integer_value_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen, hf_wsp_header_openwave_devcap_softkey_size,
+                   headerType);
+               break;
+
+/*     case FN_OPENWAVE_DEVCAP_SCREEN_CHARS:   / x-up-devcap-screen-chars */
+/*             add_openwave_integer_value_header (tree, header_buff, headerLen, */
+/*                 value_buff, valueType, valueLen, hf_wsp_header_openwave_devcap_screen_chars, */
+/*                 headerType); */
+/*             break; */
+
+/*     case FN_OPENWAVE_DEVCAP_SCREEN_PIXELS:  / x-up-devcap-screen-pixels */
+/*             add_openwave_integer_value_header (tree, header_buff, headerLen, */
+/*                 value_buff, valueType, valueLen, hf_wsp_header_openwave_devcap_screen_pixels, */
+/*                 headerType); */
+/*             break; */
+
+/*     case FN_OPENWAVE_DEVCAP_EM_SIZE:        / x-up-devcap-em-size */
+/*             add_openwave_integer_value_header (tree, header_buff, headerLen, */
+/*                 value_buff, valueType, valueLen, hf_wsp_header_openwave_devcap_em_size, */
+/*                 headerType); */
+/*             break; */
+
+       case FN_OPENWAVE_DEVCAP_SCREEN_DEPTH:   /* x-up-devcap-screen-depth */
+               add_openwave_integer_value_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen, hf_wsp_header_openwave_devcap_screen_depth,
+                   headerType);
+               break;
+
+       case FN_OPENWAVE_DEVCAP_IMMED_ALERT:    /* x-up-devcap-immed-alert */
+               add_openwave_integer_value_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen, hf_wsp_header_openwave_devcap_immed_alert,
+                   headerType);
+               break;
+
+       case FN_OPENWAVE_PROXY_NET_ASK:         /* x-up-proxy-net-ask */
+               add_openwave_integer_value_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen, hf_wsp_header_openwave_proxy_net_ask,
+                   headerType);
+               break;
+
+       case FN_OPENWAVE_PROXY_UPLINK_VERSION:          /* x-up-proxy-uplink-version */
+               add_openwave_string_value_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen,
+                   hf_wsp_header_openwave_proxy_uplink_version, headerType);
+               break;
+
+       case FN_OPENWAVE_PROXY_TOD:             /* x-up-proxy-tod */
+               add_openwave_integer_value_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen,
+                   hf_wsp_header_openwave_proxy_tod, headerType);
+               break;
+
+       case FN_OPENWAVE_PROXY_BA_ENABLE:               /* x-up-proxy-ba-enable */
+               add_openwave_integer_value_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen,
+                   hf_wsp_header_openwave_proxy_ba_enable, headerType);
+               break;
+
+       case FN_OPENWAVE_PROXY_BA_REALM:                /* x-up-proxy-ba-realm */
+               add_openwave_string_value_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen,
+                   hf_wsp_header_openwave_proxy_ba_realm, headerType);
+               break;
+
+       case FN_OPENWAVE_PROXY_REDIRECT_ENABLE:         /* x-up-proxy-redirect-enable */
+               add_openwave_integer_value_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen,
+                   hf_wsp_header_openwave_proxy_redirect_enable, headerType);
+               break;
+
+       case FN_OPENWAVE_PROXY_REQUEST_URI:             /* x-up-proxy-request-uri */
+               add_openwave_string_value_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen,
+                   hf_wsp_header_openwave_proxy_request_uri, headerType);
+               break;
+
+/*     case FN_OPENWAVE_PROXY_REDIRECT_STATUS:         / x-up-proxy-redirect-status */
+/*             add_openwave_integer_value_header (tree, header_buff, headerLen, */
+/*                 value_buff, valueType, valueLen, hf_wsp_header_openwave_proxy_redirect_status, */
+/*                 headerType); */
+/*             break; */
+
+       case FN_OPENWAVE_PROXY_TRANS_CHARSET:           /* x-up-proxy-trans-charset */
+               add_accept_xxx_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen,
+                   hf_wsp_header_openwave_proxy_trans_charset,
+                   hf_wsp_header_openwave_proxy_trans_charset_str,
+                   vals_character_sets, "Unknown charset (%u)");
+               break;
+
+       case FN_OPENWAVE_PROXY_LINGER:                  /* x-up-proxy-linger */
+               add_openwave_integer_value_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen, hf_wsp_header_openwave_proxy_linger,
+                   headerType);
+               break;
+
+/*     case FN_OPENWAVE_PROXY_CLIENT_ID:               / x-up-proxy-client-id */
+/*             add_openwave_string_value_header (tree, header_buff, headerLen, */
+/*                 value_buff, valueType, valueLen, */
+/*                 hf_wsp_header_openwave_proxy_client_id, headerType); */
+/*             break; */
+
+       case FN_OPENWAVE_PROXY_ENABLE_TRUST:            /* x-up-proxy-enable-trust */
+               add_openwave_integer_value_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen, hf_wsp_header_openwave_proxy_enable_trust,
+                   headerType);
+               break;
+
+       case FN_OPENWAVE_PROXY_TRUST_OLD:               /* x-up-proxy-trust old value */
+               add_openwave_integer_value_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen, hf_wsp_header_openwave_proxy_trust_old,
+                   headerType);
+               break;
+
+       case FN_OPENWAVE_PROXY_TRUST:                   /* x-up-proxy-trust */
+               add_openwave_integer_value_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen, hf_wsp_header_openwave_proxy_trust,
+                   headerType);
+               break;
+
+       case FN_OPENWAVE_PROXY_BOOKMARK:                /* x-up-proxy-bookmark */
+               add_openwave_string_value_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen,
+                   hf_wsp_header_openwave_proxy_bookmark, headerType);
+               break;
+
+       case FN_OPENWAVE_DEVCAP_GUI:                    /* x-up-devcap-gui */
+               add_openwave_integer_value_header (tree, header_buff, headerLen,
+                   value_buff, valueType, valueLen, hf_wsp_header_openwave_devcap_gui,
+                   headerType);
+               break;
+
+       default:
+               proto_tree_add_text (tree, header_buff, 0, headerLen,
+                   "Unsupported Openwave Header: %s",
+                   val_to_str (headerType, vals_openwave_field_names, "Unknown (0x%02X)"));
+               break;
+       }
+       return offset;
+}
+
+static void
+add_openwave_push_address_header (proto_tree *tree, tvbuff_t *header_buff,
+    int headerLen, tvbuff_t *value_buff, value_type_t valueType,
+    int valueLen)
+{
+
+/*     ??? */
        
-                                                       /* TODO: Need to read peek octets */
-                                                       value = 0x00;
-                                                       offset += peek;
-                                               }
-                                               valMatch = match_strval(value, vals_character_sets);
-                                       }
-                                       else                                            /* Assume Token-text */
-                                       {
-                                               fprintf (stderr, "dissect_wsp: Accept-Charset Token-text NYI\n");
-                                       }
+}
+
+
+static int
+add_unknown_header (proto_tree *tree, tvbuff_t *tvb, int offset,
+    guint8 headerType)
+{
+       int headerStart;
+       int valueStart;
+       value_type_t valueType;
+       int headerLen;
+       guint valueLen;
+       int valueOffset;
+
+       headerStart = offset;
+
+       /*
+        * Skip the Short-Integer header type.
+        */
+       offset++;
+
+       valueStart = offset;
+
+       /*
+        * Get the value type and length (or, if the type is VALUE_IN_LEN,
+        * meaning the value is a Short-integer, get the value type
+        * and the value itself).
+        */ 
+       valueType = get_value_type_len (tvb, valueStart, &valueLen,
+           &valueOffset, &offset);
+       headerLen = offset - headerStart;
+
+       proto_tree_add_text (tree, tvb, headerStart, headerLen,
+                      "Unsupported Header (0x%02X)", headerType);
+       return offset;
+}
+
+static int
+add_application_header (proto_tree *tree, tvbuff_t *tvb, int offset)
+{
+       int startOffset;
+       guint tokenSize;
+       const guint8 *token;
+       value_type_t valueType;
+       int subvalueLen;
+       int subvalueOffset;
+       guint secs;
+       nstime_t timeValue;
+       int asvOffset;
+       guint stringSize;
+
+       startOffset = offset;
+       tokenSize = tvb_strsize (tvb, startOffset);
+       token = tvb_get_ptr (tvb, startOffset, tokenSize);
+       offset += tokenSize;
+
+       /*
+        * Special case header "X-WAP.TOD" that is sometimes followed
+        * by a 4-byte date value.
+        *
+        * XXX - according to the 4-May-2000 WSP spec, X-Wap-Tod is
+        * encoded as a well known header, with a code of 0x3F.
+        */
+       if (tokenSize == 10 && strncasecmp ("x-wap.tod", token, 9) == 0)
+       {
+               valueType = get_value_type_len (tvb, offset,
+                   &subvalueLen, &subvalueOffset, &offset);
+               if (get_integer (tvb, subvalueOffset, subvalueLen,
+                   valueType, &secs) == 0)
+               {
+                       /*
+                        * Fill in the "struct timeval", and add it to the
+                        * protocol tree.
+                        * Note: this will succeed even if it's a Short-integer.
+                        * A Short-integer would work, but, as the time values
+                        * are UNIX seconds-since-the-Epoch value, and as
+                        * there weren't WAP phones or Web servers back in
+                        * late 1969/early 1970, they're unlikely to be used.
+                        */
+                       timeValue.secs = secs;
+                       timeValue.nsecs = 0;
+                       proto_tree_add_time (tree, hf_wsp_header_x_wap_tod,
+                           tvb, startOffset, offset - startOffset, &timeValue);
+               }
+               else
+               {
+                       proto_tree_add_text (tree, tvb, startOffset,
+                           offset - startOffset,
+                           "%s: invalid date value", token);
+               }
+       }
+       else
+       {
+               asvOffset = offset;
+               stringSize = tvb_strsize (tvb, asvOffset);
+               offset += stringSize;
+               proto_tree_add_text (tree, tvb, startOffset,
+                   offset - startOffset,
+                   "%s: %s", token,
+                   tvb_get_ptr (tvb, asvOffset, stringSize));
+       }
+       return offset;
+}
+
+static void
+add_accept_header (proto_tree *tree, tvbuff_t *header_buff,
+    int headerLen, tvbuff_t *value_buff, value_type_t valueType,
+    int valueLen)
+{
+       guint contentType;
+       const char *contentTypeStr;
+
+       add_content_type_value (tree, header_buff, 0, headerLen, value_buff,
+           valueType, valueLen, hf_wsp_header_accept,
+           hf_wsp_header_accept_str, &contentType, &contentTypeStr);
+}
+
+static void
+add_accept_xxx_header (proto_tree *tree, tvbuff_t *header_buff,
+    int headerLen, tvbuff_t *value_buff, value_type_t valueType,
+    int valueLen, int hf_numeric, int hf_string,
+    const value_string *vals, const char *unknown_tag)
+{
+       int offset = 0;
+       int subvalueLen;
+       int subvalueOffset;
+       guint value = 0;
+       char valString[100];
+       const char *valMatch;
+       guint peek;
+       double q_value = 1.0;
+
+       if (valueType == VALUE_IN_LEN)
+       {
+               /*
+                * Constrained-{charset,language} (Short-Integer).
+                */
+               proto_tree_add_uint (tree, hf_numeric,
+                   header_buff, 0, headerLen,
+                   valueLen);  /* valueLen is the value */
+               return;
+       }
+       if (valueType == VALUE_IS_TEXT_STRING)
+       {
+               /*
+                * Constrained-{charset,language} (text, i.e.
+                * Extension-Media).
+                */
+               proto_tree_add_string (tree, hf_string,
+                   header_buff, 0, headerLen,
+                   tvb_get_ptr (value_buff, 0, valueLen));
+               return;
+       }
+
+       /*
+        * First byte had the 8th bit set.
+        */
+       if (valueLen == 0) {
+               /*
+                * Any-{charset,language}.
+                */
+               proto_tree_add_string (tree, hf_string,
+                       header_buff, 0, headerLen,
+                       "*");
+               return;
+       }
+
+       /*
+        * Accept-{charset,language}-general-form; Value-length, followed
+        * by Well-known-{charset,language} or {Token-text,Text-string},
+        * possibly followed by a Q-value.
+        * 
+        * Get Value-length.
+        */
+       valueType = get_value_type_len (value_buff, 0, &subvalueLen,
+           &subvalueOffset, &offset);
+       if (valueType == VALUE_IS_TEXT_STRING)
+       {
+               /*
+                * {Token-text,Text-string}.
+                */
+               valMatch =
+                   tvb_get_ptr (value_buff, subvalueOffset, subvalueLen);
+               proto_tree_add_string (tree, hf_string,
+                       value_buff, 0, valueLen, valMatch);
+       } else {
+               /*
+                * Well-known-{charset,langugage}; starts with an
+                * Integer-value.
+                */
+               if (get_integer (value_buff, subvalueOffset, subvalueLen,
+                   valueType, &value) < 0)
+               {
+                       valMatch = "Invalid integer";
+               }
+               else
+               {
+                       valMatch = val_to_str(value, vals, unknown_tag);
+               }
+       }
+
+       /* Any remaining data relates to Q-value */
+       if (offset < valueLen)
+       {
+               peek = tvb_get_guintvar (value_buff, offset, NULL);
+               if (peek <= 100) {
+                       peek = (peek - 1) * 10;
+               }
+               else {
+                       peek -= 100;
+               }
+               q_value = peek/1000.0;
+       }
+
+       /* Build string including Q-value if present */
+       if (q_value == 1.0)                     /* Default */
+       {
+               snprintf (valString, 100, "%s", valMatch);
+       }
+       else
+       {
+               snprintf (valString, 100, "%s; Q=%5.3f", valMatch, q_value);
+       }
+       /* Add string to tree */
+       proto_tree_add_string (tree, hf_string,
+           header_buff, 0, headerLen, valString);
+}
+
+static void
+add_accept_ranges_header (proto_tree *tree, tvbuff_t *header_buff,
+    int headerLen, tvbuff_t *value_buff, value_type_t valueType,
+    int valueLen)
+{
+       if (valueType == VALUE_IN_LEN)
+       {
+               /*
+                * Must be 0 (None) or 1 (Bytes) (the 8th bit was stripped
+                * off).
+                */
+               proto_tree_add_uint (tree, hf_wsp_header_accept_ranges,
+                   header_buff, 0, headerLen,
+                   valueLen);  /* valueLen is the value */
+               return;
+       }
+       if (valueType == VALUE_IS_TEXT_STRING)
+       {
+               /*
+                * Token-text.
+                */
+               proto_tree_add_string (tree, hf_wsp_header_accept_ranges_str,
+                   header_buff, 0, headerLen,
+                   tvb_get_ptr (value_buff, 0, valueLen));
+               return;
+       }
+
+       /*
+        * Not valid.
+        */
+       fprintf(stderr, "dissect_wsp: Accept-Ranges is neither None, Bytes, nor Token-text\n");
+       return;
+}
+
+static void
+add_cache_control_header (proto_tree *tree, tvbuff_t *header_buff,
+    int headerLen, tvbuff_t *value_buff, value_type_t valueType,
+    int valueLen)
+{
+       int offset = 0;
+       int subvalueLen;
+       int subvalueOffset;
+       guint value;
+       proto_item *ti;
+       proto_tree *parameter_tree;
+       proto_tree *field_names_tree;
+       guint delta_secs;
+
+       if (valueType == VALUE_IN_LEN)
+       {
+               /*
+                * No-cache, No-store, Max-age, Max-stale, Min-fresh,
+                * Only-if-cached, Public, Private, No-transform,
+                * Must-revalidate, Proxy-revalidate, or S-maxage.
+                */
+               proto_tree_add_uint (tree, hf_wsp_header_cache_control,
+                   header_buff, 0, headerLen,
+                   valueLen);  /* valueLen is the value */
+               return;
+       }
+       if (valueType == VALUE_IS_TEXT_STRING)
+       {
+               /*
+                * Cache-extension.
+                */
+               proto_tree_add_string (tree, hf_wsp_header_cache_control_str,
+                   header_buff, 0, headerLen,
+                   tvb_get_ptr (value_buff, 0, valueLen));
+               return;
+       }
+
+       /*
+        * Value-length Cache-directive.
+        * Get first field of Cache-directive.
+        */
+       valueType = get_value_type_len (value_buff, offset, &subvalueLen,
+           &subvalueOffset, &offset);
+       if (valueType == VALUE_IS_TEXT_STRING)
+       {
+               /*
+                * Cache-extension Parameter.
+                */
+               ti = proto_tree_add_string (tree, hf_wsp_header_cache_control_str,
+                   header_buff, 0, headerLen,
+                   tvb_get_ptr (value_buff, 0, valueLen));
+               parameter_tree = proto_item_add_subtree (ti,
+                   ett_header_cache_control_parameters);
+
+               /*
+                * Process the rest of the value as parameters.
+                */
+               while (tvb_reported_length_remaining (value_buff, offset) > 0) {
+                       offset = add_parameter (parameter_tree, value_buff,
+                           offset);
+               }
+               return;
+       }
+       if (get_integer (value_buff, subvalueOffset, subvalueLen, valueType,
+           &value) < 0)
+       {
+               proto_tree_add_text (tree, header_buff, 0, headerLen,
+                   "Invalid Cache-Control Cache-directive value");
+       }
+       else
+       {
+               switch (value) {
+
+               case NO_CACHE:
+               case PRIVATE:
+                       /*
+                        * Loop, processing Field-names.
+                        */
+                       ti = proto_tree_add_uint (tree,
+                           hf_wsp_header_cache_control,
+                           header_buff, 0, headerLen,
+                           value);
+                       field_names_tree = proto_item_add_subtree (ti,
+                           ett_header_cache_control_field_names);
+                       while (tvb_reported_length_remaining (value_buff, offset)
+                           > 0) {
+                               offset = add_cache_control_field_name (tree,
+                                   value_buff, offset, value);
+                       }
+                       break;
+
+               case MAX_AGE:
+               case MAX_STALE:
+               case MIN_FRESH:
+               case S_MAXAGE:
+                       /*
+                        * Get Delta-second-value.
+                        */
+                       valueType = get_value_type_len (value_buff, offset,
+                           &subvalueLen, &subvalueOffset, &offset);
+                       if (get_integer (value_buff, subvalueOffset,
+                           subvalueLen, valueType, &delta_secs) < 0)
+                       {
+                               proto_tree_add_text (tree,
+                                   header_buff, 0, headerLen,
+                                   "Invalid Cache-Control %s Delta-second-value",
+                                   match_strval (value, vals_cache_control));
+                       }
+                       else 
+                       {
+                               proto_tree_add_uint_format (tree,
+                                   hf_wsp_header_cache_control,
+                                   header_buff, 0, headerLen,
+                                   value,
+                                   "Cache-Control: %s %u secs",
+                                   match_strval (value, vals_cache_control),
+                                   delta_secs);
+                       }
+                       break;
+
+               default:
+                       /*
+                        * This should not happen, but handle it anyway.
+                        */
+                       proto_tree_add_uint (tree,
+                           hf_wsp_header_cache_control,
+                           header_buff, 0, headerLen,
+                           value);
+                       break;
+               }
+       }
+}
+
+static int
+add_cache_control_field_name (proto_tree *tree, tvbuff_t *value_buff,
+    int offset, guint cache_control_value)
+{
+       value_type_t valueType;
+       int startOffset;
+       int subvalueLen;
+       int subvalueOffset;
+
+       startOffset = offset;
+       valueType = get_value_type_len (value_buff, offset,
+           &subvalueLen, &subvalueOffset, &offset);
+       if (valueType == VALUE_IS_TEXT_STRING)
+       {
+               /*
+                * Token-text.
+                */
+               proto_tree_add_item (tree, 
+                   hf_wsp_header_cache_control_field_name_str,
+                   value_buff, startOffset, offset - startOffset,
+                   bo_little_endian);
+       }
+       else if (valueType == VALUE_IN_LEN)
+       {
+               /*
+                * Short-integer Field-name.
+                */
+               proto_tree_add_uint (tree,
+                   hf_wsp_header_cache_control_field_name,
+                   value_buff, startOffset, offset - startOffset,
+                   subvalueLen);
+       }
+       else
+       {
+               /*
+                * Long-integer - illegal.
+                */
+               proto_tree_add_text (tree,
+                   value_buff, startOffset, offset - startOffset,
+                   "Invalid Cache-Control %s Field-name",
+                   match_strval (cache_control_value, vals_cache_control));
+       }       
+       return offset;
+}
+
+static void
+add_connection_header (proto_tree *tree, tvbuff_t *header_buff,
+    int headerLen, tvbuff_t *value_buff, value_type_t valueType,
+    int valueLen)
+{
+       int offset = 0;
+
+       if (valueType == VALUE_LEN_SUPPLIED)
+       {
+               /*
+                * Invalid.
+                */
+               proto_tree_add_text (tree, header_buff, 0, headerLen,
+                   "Invalid Connection value");
+               return;
+       }
+       if (valueType == VALUE_IS_TEXT_STRING)
+       {
+               /*
+                * Token-text.
+                */
+               proto_tree_add_string (tree,
+                   hf_wsp_header_connection_str,
+                   header_buff, 0, headerLen,
+                   tvb_get_ptr (value_buff, 0, valueLen));
+               return;
+       }
+
+       /*
+        * First byte had the 8th bit set.
+        */
+       if (valueLen == 0) {
+               /*
+                * Close.
+                */
+               proto_tree_add_uint (tree, hf_wsp_header_connection,
+                   header_buff, offset, headerLen, valueLen);
+               return;
+       }
+
+       /*
+        * Invalid.
+        */
+       proto_tree_add_text (tree, header_buff, 0, headerLen,
+           "Invalid Connection value");
+}
+
+static void
+add_pragma_header (proto_tree *tree, tvbuff_t *header_buff,
+    int headerLen, tvbuff_t *value_buff, value_type_t valueType,
+    int valueLen)
+{
+       int offset = 0;
+       int subvalueLen;
+       int subvalueOffset;
+
+       if (valueType == VALUE_IN_LEN)
+       {
+               /*
+                * Invalid.
+                */
+               proto_tree_add_text (tree, header_buff, 0, headerLen,
+                   "Invalid Pragma");
+               return;
+       }
+       if (valueType == VALUE_IS_TEXT_STRING)
+       {
+               /*
+                * Invalid?
+                */
+               proto_tree_add_text (tree, header_buff, 0, headerLen,
+                   "Invalid Pragma");
+               return;
+       }
+
+       /*
+        * First byte had the 8th bit set.
+        */
+       if (valueLen == 0) {
+               /*
+                * No-cache.
+                */
+               proto_tree_add_string (tree, hf_wsp_header_pragma,
+                   header_buff, 0, headerLen, "No-cache");
+               return;
+       }
+
+       /*
+        * Value-length, followed by Parameter.
+        * 
+        * Get Value-length.
+        */
+       valueType = get_value_type_len (value_buff, 0, &subvalueLen,
+           &subvalueOffset, &offset);
+       if (valueType == VALUE_IS_TEXT_STRING)
+       {
+               /*
+                * Parameter - a text string.
+                */
+               proto_tree_add_string (tree, hf_wsp_header_pragma,
+                   header_buff, 0, headerLen,
+                   tvb_get_ptr (value_buff, subvalueOffset, subvalueLen));
+       } else {
+               /*
+                * Parameter - numeric; illegal?
+                */
+               proto_tree_add_text (tree, header_buff, 0, headerLen,
+                   "Invalid Pragma");
+       }
+}
+
+static void
+add_transfer_encoding_header (proto_tree *tree, tvbuff_t *header_buff,
+    int headerLen, tvbuff_t *value_buff, value_type_t valueType,
+    int valueLen)
+{
+       int offset = 0;
+
+       if (valueType == VALUE_LEN_SUPPLIED)
+       {
+               /*
+                * Invalid.
+                */
+               proto_tree_add_text (tree, header_buff, 0, headerLen,
+                   "Invalid Transfer-Encoding value");
+               return;
+       }
+       if (valueType == VALUE_IS_TEXT_STRING)
+       {
+               /*
+                * Token-text.
+                */
+               proto_tree_add_string (tree,
+                   hf_wsp_header_transfer_encoding_str,
+                   header_buff, 0, headerLen,
+                   tvb_get_ptr (value_buff, 0, valueLen));
+               return;
+       }
+
+       /*
+        * First byte had the 8th bit set.
+        */
+       if (valueLen == 0) {
+               /*
+                * Chunked.
+                */
+               proto_tree_add_uint (tree, hf_wsp_header_transfer_encoding,
+                   header_buff, offset, headerLen, valueLen);
+               return;
+       }
+
+       /*
+        * Invalid.
+        */
+       proto_tree_add_text (tree, header_buff, 0, headerLen,
+           "Invalid Transfer Encoding value");
+}
+
+static void
+add_warning_header (proto_tree *tree, tvbuff_t *header_buff,
+    int headerLen, tvbuff_t *value_buff, value_type_t valueType,
+    int valueLen)
+{
+       int offset = 0;
+       proto_item *ti;
+       proto_tree *warning_tree;
+       int subvalueLen;
+       int subvalueOffset;
+
+       /*
+        * Put the items under a header.
+        * XXX - make the text of the item summarize the elements.
+        */
+       ti = proto_tree_add_item (tree, hf_wsp_header_warning,
+           header_buff, 0, headerLen, bo_little_endian);
+       warning_tree = proto_item_add_subtree(ti, ett_header_warning);
+       
+       if (valueType == VALUE_IN_LEN)
+       {
+               /*
+                * Warn-code (Short-integer).
+                */
+               proto_tree_add_uint (warning_tree, hf_wsp_header_warning_code,
+                   header_buff, 0, headerLen,
+                   valueLen);  /* valueLen is the value */
+               return;
+       }
+       if (valueType == VALUE_IS_TEXT_STRING)
+       {
+               /*
+                * Invalid.
+                */
+               proto_tree_add_text (warning_tree, header_buff, 0, headerLen,
+                   "Invalid Warning (all text)");
+               return;
+       }
+
+       /*
+        * Warning-value; Warn-code, followed by Warn-agent, followed by
+        * Warn-text.
+        */
+       /*
+        * Get Short-integer Warn-code.
+        */
+       valueType = get_value_type_len (value_buff, offset, &subvalueLen,
+           &subvalueOffset, &offset);
+       if (valueType != VALUE_IN_LEN)
+       {
+               /*
+                * Not a Short-integer.
+                */
+               proto_tree_add_text (warning_tree, value_buff, subvalueOffset,
+                   subvalueLen, "Invalid Warn-code (not a Short-integer)");
+               return;
+       }
+       proto_tree_add_uint (warning_tree, hf_wsp_header_warning_code,
+           value_buff, subvalueOffset, 1,
+           subvalueLen);       /* subvalueLen is the value */
+
+       /*
+        * Warn-agent; must be text.
+        */
+       valueType = get_value_type_len (value_buff, offset, &subvalueLen,
+           &subvalueOffset, &offset);
+       if (valueType != VALUE_IS_TEXT_STRING)
+       {
+               /*
+                * Not text.
+                */
+               proto_tree_add_text (warning_tree, value_buff, subvalueOffset,
+                   subvalueLen, "Invalid Warn-agent (not a text string)");
+               return;
+       }
+       proto_tree_add_item (warning_tree,
+               hf_wsp_header_warning_agent,
+               value_buff, subvalueOffset, subvalueLen, bo_little_endian);
+
+       /*
+        * Warn-text; must be text.
+        */
+       valueType = get_value_type_len (value_buff, offset, &subvalueLen,
+           &subvalueOffset, &offset);
+       if (valueType != VALUE_IS_TEXT_STRING)
+       {
+               /*
+                * Not text.
+                */
+               proto_tree_add_text (warning_tree, value_buff, subvalueOffset,
+                   subvalueLen, "Invalid Warn-text (not a text string)");
+               return;
+       }
+       proto_tree_add_item (warning_tree,
+               hf_wsp_header_warning_text,
+               value_buff, subvalueOffset, subvalueLen, bo_little_endian);
+}
+
+static void
+add_accept_application_header (proto_tree *tree, tvbuff_t *header_buff,
+    int headerLen, tvbuff_t *value_buff, value_type_t valueType,
+    int valueLen)
+{
+       guint value;
+
+       if (valueType == VALUE_IN_LEN)
+       {
+               /*
+                * Application-id-value; numeric, so it's App-assigned-code.
+                */
+               proto_tree_add_uint (tree, hf_wsp_header_accept_application,
+                   header_buff, 0, headerLen,
+                   valueLen);  /* valueLen is the value */
+               return;
+       }
+       if (valueType == VALUE_IS_TEXT_STRING)
+       {
+               /*
+                * Uri-value.
+                */
+               proto_tree_add_string (tree, hf_wsp_header_accept_application_str,
+                   header_buff, 0, headerLen,
+                   tvb_get_ptr (value_buff, 0, valueLen));
+               return;
+       }
+
+       /*
+        * First byte had the 8th bit set.
+        */
+       if (valueLen == 0) {
+               /*
+                * Any-application.
+                */
+               proto_tree_add_string (tree, hf_wsp_header_accept_application_str,
+                       header_buff, 0, headerLen,
+                       "*");
+               return;
+       }
+
+       /*
+        * Integer-value, hence App-assigned-code.
+        */
+       if (get_integer (value_buff, 0, valueLen, valueType, &value) < 0)
+       {
+               proto_tree_add_text (tree, header_buff, 0, headerLen,
+                       "Invalid Accept-Application App-assigned-code");
+       }
+       else
+       {
+               proto_tree_add_uint (tree, hf_wsp_header_accept_application,
+                   header_buff, 0, headerLen, value);
+       }
+}
+
+static void
+add_wap_application_id_header (proto_tree *tree, tvbuff_t *header_buff,
+    int headerLen, tvbuff_t *value_buff, value_type_t valueType,
+    int valueLen)
+{
+       if (valueType == VALUE_IN_LEN)
+       {
+               /*
+                * Must application-id (the 8th bit was stripped off).
+                */
+               proto_tree_add_uint (tree, hf_wsp_header_wap_application_id,
+                   header_buff, 0, headerLen,
+                   valueLen);  /* valueLen is the value */
+               return;
+       }
+       if (valueType == VALUE_IS_TEXT_STRING)
+       {
+               /*
+                * Token-text.
+                */
+               proto_tree_add_string (tree, hf_wsp_header_wap_application_id_str,
+                   header_buff, 0, headerLen,
+                   tvb_get_ptr (value_buff, 0, valueLen));
+               return;
+       }
+
+       /*
+        * Not valid.
+        */
+       fprintf(stderr, "dissect_wsp: Suprising format of X-Wap-Application-Id\n");
+       return;
+}
 
-                                       /* Any remaining data relates to Q-Value */
-                                       if (offset < valueLen)
-                                       {
-                                               count = 0;
-                                               q_value = tvb_get_guintvar (value_buff, offset, &count);
-                                               if (count == 1)                 /* Two decimal quality factors */
-                                               {
-                                                       q_value -= 1;
-                                                       q_value /= 100;
-                                               }
-                                               else if (count == 2) /* Three decimal quality factors */
-                                               {
-                                                       q_value -= 100;
-                                                       q_value /= 1000;
-                                               }
-                                               else
-                                               {
-                                                       fprintf (stderr, "dissect_wsp: Accept-Charset invalid Q-value %f\n", q_value);
-                                               }
-                                       }
+static void
+add_capabilities (proto_tree *tree, tvbuff_t *tvb, int type)
+{
+       proto_item *ti;
+       proto_tree *wsp_capabilities;
+       guint offset = 0;
+       guint offsetStr = 0;
+       guint capabilitiesLen = tvb_reported_length (tvb);
+       guint capabilitiesStart = 0;
+       guint peek = 0;
+       guint length = 0;
+       guint value = 0;
+       guint i;
+       int ret;
+       char valString[200];
 
-                                       /* Build string including Q values if present */
-                                       if (q_value == 1.0)                     /* Default */
-                                       {
-                                               if (valMatch == NULL)
-                                               {
-                                                       snprintf (valString, 100, "Unknown (%02X)", peek);
-                                               }
-                                               else
-                                               {
-                                                       snprintf (valString, 100, "%s", valMatch);
-                                               }
-                                       }
-                                       else
-                                       {
-                                               snprintf (valString, 100, "Unknown %d; Q=%5.3f", value,
-                                                       q_value/1000.0);
-                                               if (valMatch == NULL)
-                                               {
-                                                       snprintf (valString, 100, "Unknown (%02X)", peek);
-                                               }
-                                               else
-                                               {
-                                                       snprintf (valString, 100, "%s", valMatch);
-                                               }
-                                       }
+#ifdef DEBUG
+       fprintf (stderr, "dissect_wsp: Offset is %d, size is %d\n", offset, capabilitiesLen);
+#endif
 
-                                       /* Add string to tree */
-                                       proto_tree_add_string (tree, hf_wsp_header_accept_charset_str,
-                                               header_buff, 0, headerLen, valString);
-                               }
-                               else                                            /* Constrained-charset */
-                               {
-                                       if (peek == 0x80)               /* Any-charset */
-                                       {
-                                               proto_tree_add_string (tree, hf_wsp_header_accept_charset,
-                                                       header_buff, offset, headerLen,
-                                                       "*");
-                                       }
-                                       else if (peek & 0x80)   /* Short-Integer */
-                                       {
-                                               proto_tree_add_uint (tree, hf_wsp_header_accept_charset,
-                                                       header_buff, offset, headerLen, (peek & 0x7F) );
-                                       }
-                                       else                                    /* Assume *TEXT */
-                                       {
-                                               proto_tree_add_string (tree, hf_wsp_header_accept_charset,
-                                                       header_buff, offset, headerLen,
-                                                       tvb_get_ptr (value_buff, 0, valueLen));
-                                       }
-                               }
-                               break;
+       /* End of buffer */
+       if (capabilitiesLen <= 0)
+       {
+               fprintf (stderr, "dissect_wsp: Capabilities = 0\n");
+               return;
+       }
 
-                       case 0x03:              /* Accept-Language */
-                               if (peek < 31)
-                               {
-                                       /* Peek contains the number of octets to follow */
-                                       switch (peek)
-                                       {
-                                               case 1:
-                                                       proto_tree_add_uint (tree, hf_wsp_header_accept_language, header_buff, offset, 
-                                                               headerLen, tvb_get_guint8 (value_buff, 1) );
-                                               break;
-                                               case 2:
-                                                       proto_tree_add_uint (tree, hf_wsp_header_accept_language, header_buff, offset, 
-                                                               headerLen, tvb_get_ntohs (value_buff, 1) );
-                                               break;
-                                               case 4:
-                                                       proto_tree_add_uint (tree, hf_wsp_header_accept_language, header_buff, offset, 
-                                                               headerLen, tvb_get_ntohl (value_buff, 1) );
-                                               break;
-                                               default:
-                                                       fprintf (stderr, "dissect_wsp: accept-language size %d NYI\n", peek);
-                                       }
-                               }
-                               else if (peek & 0x80)
-                               {
-                                       proto_tree_add_uint (tree, hf_wsp_header_accept_language, header_buff, offset, headerLen, (peek & 0x7F) );
-                               }
-                               else
-                               {
-                                       proto_tree_add_string (tree, hf_wsp_header_accept_language_str, header_buff, offset,headerLen,
-                                               tvb_get_ptr (value_buff, 0, valueLen));
-                               }
-                               break;
+#ifdef DEBUG
+       fprintf (stderr, "dissect_wsp: capabilities to process\n");
+#endif
 
-                       case 0x04:              /* Accept-Ranges */
-                               if ((peek == 128) || (peek == 129))
-                               {
-                                       proto_tree_add_uint (tree, hf_wsp_header_accept_ranges, header_buff, offset, headerLen, peek);
-                               }
-                               else
-                               {
-                                       fprintf (stderr, "dissect_wsp: accept-ranges NYI\n");
-                               }
-                               
-                               break;
+       ti = proto_tree_add_item (tree, hf_wsp_capabilities_section,tvb,offset,capabilitiesLen,bo_little_endian);
+       wsp_capabilities = proto_item_add_subtree( ti, ett_capabilities );
 
-                       case 0x05:              /* Age */
-                               switch (valueLen)
-                               {
-                                       case 1:
-                                               proto_tree_add_uint (tree, hf_wsp_header_age, header_buff, offset, headerLen, tvb_get_guint8 (value_buff, 0));
-                                               break;
-                                       case 2:
-                                               proto_tree_add_uint (tree, hf_wsp_header_age, header_buff, offset, headerLen, tvb_get_ntohs (value_buff, 0));
-                                               break;
-                                       case 3:
-                                               proto_tree_add_uint (tree, hf_wsp_header_age, header_buff, offset, headerLen, tvb_get_ntoh24 (value_buff, 0));
-                                               break;
-                                       case 4:
-                                               proto_tree_add_uint (tree, hf_wsp_header_age, header_buff, offset, headerLen, tvb_get_ntohl (value_buff, 0));
-                                               break;
-                               };
-                               break;
+       /* Parse Headers */
+
+       while (offset < capabilitiesLen)
+       {
+               /* Loop round each header */
+               capabilitiesStart = offset;
+               length = tvb_get_guint8 (tvb, capabilitiesStart);
 
-                       case 0x08:              /* Cache-Control */
-                               if (peek & 0x80)
+               if (length >= 127)              /* length */
+               {
+#ifdef DEBUG
+                       fprintf (stderr, "dissect_wsp: capabilities length invalid %d\n",length);
+#endif
+                       offset+=length;
+                       continue;
+               }
+               offset++;
+               peek = tvb_get_guint8 (tvb, offset);
+               offset++;
+               switch (peek & 0x7f)
+               {
+                       case 0x00 : /* Client-SDU-Size */
+                               value = get_uintvar (tvb, offset, length+capabilitiesStart+1);
+                               proto_tree_add_uint (wsp_capabilities, hf_wsp_capabilities_client_SDU, tvb, capabilitiesStart, length+1, value);
+                               break;
+                       case 0x01 : /* Server-SDU-Size */
+                               value = get_uintvar (tvb, offset, length+capabilitiesStart+1);
+                               proto_tree_add_uint (wsp_capabilities, hf_wsp_capabilities_server_SDU, tvb, capabilitiesStart, length+1, value);
+                               break;
+                       case 0x02 : /* Protocol Options */ 
+                               value = get_uintvar (tvb, offset, length+capabilitiesStart+1);
+                               i = 0;
+                               valString[0]=0;
+                               if (value & 0x80)
                                {
-                                       if (valueLen == 1)      /* Well-known value */
-                                       {
-                                               proto_tree_add_uint (tree, hf_wsp_header_cache_control, header_buff, offset, headerLen, peek);
-                                       }
-                                       else
-                                       {
-                                               if ((peek == 0x82) || (peek == 0x83) || (peek == 0x84)) /* Delta seconds value to follow */
-                                               {
-                                                       value = tvb_get_guint8 (value_buff, 1);
-                                                       if (value & 0x80)
-                                                       {
-                                                               proto_tree_add_text (tree, header_buff, 0,
-                                                                       headerLen, "Cache-Control: %s %d (0x%02X)",
-                                                               val_to_str (peek, vals_cache_control,
-                                                                       "Unknown (0x%02x)"),
-                                                               (value & 0x7F), peek);
-                                                       }
-                                                       else
-                                                       {
-                                                               fprintf (stderr, "dissect_wsp: Cache-Control integer value Delta seconds NYI\n");
-                                                       }
-                                               }
-                                               else if ((peek == 0x80) || (peek == 0x87))      /* Fields to follow */
-                                               {
-                                                       fprintf (stderr, "dissect_wsp: Cache-Control field values NYI\n");
-                                               }
-                                               else
-                                               {
-                                                       fprintf (stderr, "dissect_wsp: Cache-Control cache extension NYI\n");
-                                               }
+                                       ret = snprintf(valString+i,200-i,"%s","(Confirmed push facility) ");
+                                       if (ret == -1) {
+                                               /*
+                                                * Some versions of snprintf
+                                                * return -1 if they'd
+                                                * truncate the output.
+                                                */
+                                               goto add_string;
                                        }
+                                       i += ret;
                                }
-                               else
+                               if (value & 0x40)
                                {
-                                       fprintf (stderr, "dissect_wsp: Cache-Control cache extension NYI\n");
-                               }
-                               break;
-                               
-                       case 0x0D:              /* Content-Length */
-                               if (peek < 31)
-                               {
-                                       switch (peek)
-                                       {
-                                               case 1:
-                                                       proto_tree_add_uint (tree,
-                                                               hf_wsp_header_content_length, header_buff, offset,
-                                                               headerLen, tvb_get_guint8 (value_buff, 1) );
-                                                       break;
-                                               case 2:
-                                                       proto_tree_add_uint (tree,
-                                                               hf_wsp_header_content_length, header_buff, offset,
-                                                               headerLen, tvb_get_ntohs (value_buff, 1) );
-                                                       break;
-                                               case 3:
-                                                       proto_tree_add_uint (tree,
-                                                               hf_wsp_header_content_length, header_buff, offset,
-                                                               headerLen, (tvb_get_ntohs (value_buff, 1) << 8) +
-                                                               tvb_get_guint8 (value_buff, 3) );
-                                                       break;
-                                               case 4:
-                                                       proto_tree_add_uint (tree,
-                                                               hf_wsp_header_content_length, header_buff, offset,
-                                                               headerLen, tvb_get_ntohl (value_buff, 1) );
-                                                       break;
-                                               default:
-                                                       fprintf (stderr, "dissect_wsp: accept-charset size %d NYI\n", peek);
+                                       if (i >= 200) {
+                                               /* No more room. */
+                                               goto add_string;
                                        }
+                                       ret = snprintf(valString+i,200-i,"%s","(Push facility) ");
+                                       if (ret == -1) {
+                                               /*
+                                                * Some versions of snprintf
+                                                * return -1 if they'd
+                                                * truncate the output.
+                                                */
+                                               goto add_string;
+                                       }
+                                       i += ret;
                                }
-                               else if (peek & 0x80)
+                               if (value & 0x20)
                                {
-                                       proto_tree_add_uint (tree, hf_wsp_header_content_length, header_buff, offset, headerLen, (peek & 0x7F));
+                                       if (i >= 200) {
+                                               /* No more room. */
+                                               goto add_string;
+                                       }
+                                       ret = snprintf(valString+i,200-i,"%s","(Session resume facility) ");
+                                       if (ret == -1) {
+                                               /*
+                                                * Some versions of snprintf
+                                                * return -1 if they'd
+                                                * truncate the output.
+                                                */
+                                               goto add_string;
+                                       }
+                                       i += ret;
                                }
-                               else
+                               if (value & 0x10)
                                {
-                                       fprintf (stderr, "dissect_wsp: Content-Length long-integer size NYI\n");
+                                       if (i >= 200) {
+                                               /* No more room. */
+                                               goto add_string;
+                                       }
+                                       ret = snprintf(valString+i,200-i,"%s","(Acknowledgement headers) ");
+                                       if (ret == -1) {
+                                               /*
+                                                * Some versions of snprintf
+                                                * return -1 if they'd
+                                                * truncate the output.
+                                                */
+                                               goto add_string;
+                                       }
+                                       i += ret;
                                }
+                       add_string:
+                               proto_tree_add_string(wsp_capabilities, hf_wsp_capabilities_protocol_opt, tvb, capabilitiesStart, length+1, valString);
                                break;
-                               
-                       case 0x12:              /* Date */
-                               add_date_value (value_buff, 0, tree,
-                                       hf_wsp_header_date, header_buff, offset,
-                                       headerLen, &timeValue, "Date");
-                               break;
-
-                       case 0x13:              /* Etag */
-                               ti = proto_tree_add_string (tree, hf_wsp_header_etag,header_buff,offset,headerLen,tvb_get_ptr (value_buff, 0, valueLen));
-                               break;
-
-                       case 0x14:              /* Expires */
-                               add_date_value (value_buff, 0, tree,
-                                       hf_wsp_header_expires, header_buff, offset,
-                                       headerLen, &timeValue, "Expires");
-                               break;
-
-                       case 0x17:              /* If-Modified-Since */
-                               add_date_value (value_buff, 0, tree,
-                                       hf_wsp_header_if_modified_since, header_buff, offset,
-                                       headerLen, &timeValue, "If-Modified-Since");
-                               break;
-                               
-                       case 0x1C:              /* Location */
-                               ti = proto_tree_add_string (tree, hf_wsp_header_location,header_buff,offset,headerLen,tvb_get_ptr (value_buff, 0, valueLen));
-                               break;
-
-                       case 0x1D:              /* Last-Modified */
-                               add_date_value (value_buff, 0, tree,
-                                       hf_wsp_header_last_modified, header_buff, offset,
-                                       headerLen, &timeValue, "Last-Modified");
+                       case 0x03 : /* Method-MOR */ 
+                               value = tvb_get_guint8(tvb, offset);
+                               proto_tree_add_uint (wsp_capabilities, hf_wsp_capabilities_method_MOR, tvb, capabilitiesStart, length+1, value);
                                break;
-                               
-                       case 0x1F:              /* Pragma */
-                               if (peek == 0x80)
-                               {
-                                       proto_tree_add_text (tree, header_buff, 0, headerLen, "Pragma: No-cache");
-                               }
-                               else
-                               {
-                                       proto_tree_add_text (tree, header_buff, 0, headerLen, "Unsupported Header (0x%02X)", (tvb_get_guint8 (header_buff, 0) & 0x7F));
-                               }
+                       case 0x04 : /* Push-MOR */ 
+                               value = tvb_get_guint8(tvb, offset);
+                               proto_tree_add_uint (wsp_capabilities, hf_wsp_capabilities_push_MOR, tvb, capabilitiesStart, length+1, value);
                                break;
-                               
-                       case 0x26:              /* Server */
-                               ti = proto_tree_add_string (tree, hf_wsp_header_server,header_buff,offset,headerLen,tvb_get_ptr (value_buff, 0, valueLen));
                                break;
-
-                       case 0x27:              /* Transfer encoding */
-                               if (peek & 0x80)
-                               {
-                                       proto_tree_add_uint (tree, hf_wsp_header_transfer_encoding,
-                                               header_buff, offset, headerLen, peek);
-                               }
-                               else
-                               {
-                                       proto_tree_add_string (tree,
-                                               hf_wsp_header_transfer_encoding_str, header_buff, offset,
-                                               headerLen,tvb_get_ptr (value_buff, 0, valueLen));
-                               }
+                       case 0x05 : /* Extended Methods */ 
+                               offsetStr = offset;
+                               offset++;
+                               add_capability_vals(tvb, (type == CONNECT),
+                                   offsetStr, length, capabilitiesStart,
+                                   valString, sizeof valString);
+                               proto_tree_add_string(wsp_capabilities, hf_wsp_capabilities_extended_methods, tvb, capabilitiesStart, length+1, valString);
                                break;
-
-                       case 0x29:              /* User-Agent */
-                               ti = proto_tree_add_string (tree, hf_wsp_header_user_agent,header_buff,offset,headerLen,tvb_get_ptr (value_buff, 0, valueLen));
+                       case 0x06 : /* Header Code Pages */ 
+                               offsetStr = offset;
+                               offset++;
+                               add_capability_vals(tvb, (type == CONNECT),
+                                   offsetStr, length, capabilitiesStart,
+                                   valString, sizeof valString);
+                               proto_tree_add_string(wsp_capabilities, hf_wsp_capabilities_header_code_pages, tvb, capabilitiesStart, length+1, valString);
                                break;
-
-                       case 0x2B:              /* Via */
-                               ti = proto_tree_add_string (tree, hf_wsp_header_via, header_buff,
-                                       offset, headerLen, tvb_get_ptr (value_buff, 0, valueLen));
+                       case 0x07 : /* Aliases */
                                break;
-
                        default:
-                               ti = proto_tree_add_text (tree, header_buff, 0, headerLen, "Unsupported Header (0x%02X)", (tvb_get_guint8 (header_buff, 0) & 0x7F));
+                               proto_tree_add_text (wsp_capabilities, tvb , capabilitiesStart, length+1,
+                                      "Unsupported Header (0x%02X)", peek & 0x7F);
                                break;
                }
+               offset=capabilitiesStart+length+1;
        }
-       else
+}
+
+static void
+add_capability_vals(tvbuff_t *tvb, gboolean add_string, int offsetStr,
+    guint length, guint capabilitiesStart, char *valString,
+    size_t valStringSize)
+{
+       guint i;
+       int ret;
+       guint value;
+       guint8 c;
+
+       i = 0;
+       while ((offsetStr-capabilitiesStart) <= length)
        {
-               /* Special case header X-WAP.TOD that is sometimes followed
-                * by a 4-byte date value */
-               if (strncasecmp ("x-wap.tod", tvb_get_ptr (header_buff, 0, headerLen), 9) == 0)
+               value = tvb_get_guint8(tvb, offsetStr);
+               if (i >= valStringSize) {
+                       /* No more room. */
+                       break;
+               }
+               if (add_string)
                {
-                       if (tvb_reported_length (value_buff) == 4)      /* Probably a date value */
-                       {
-                               timeValue.tv_sec = tvb_get_ntohl (value_buff, 0);
-                               ti = proto_tree_add_time (tree, hf_wsp_header_x_wap_tod, header_buff, offset, headerLen, &timeValue);
-                       }
-                       else if (tvb_reported_length (value_buff) == 5) /* Probably a date */
-                       {
-                               add_date_value (value_buff, 0, tree,
-                                       hf_wsp_header_x_wap_tod, header_buff, offset,
-                                       headerLen, &timeValue, "X-Wap.Tod");
-                       }
-                       else
-                       {
-                               ti = proto_tree_add_text (tree, header_buff, 0, headerLen, "%s: %s", tvb_get_ptr (header_buff, 0, headerLen), tvb_get_ptr (value_buff, 0, valueLen));
-                       }
+                       ret = snprintf(valString+i,valStringSize-i,
+                           "(%d - ",value);
                }
                else
                {
-                       ti = proto_tree_add_text (tree, header_buff, 0, headerLen, "%s: %s", tvb_get_ptr (header_buff, 0, headerLen), tvb_get_ptr (value_buff, 0, valueLen));
+                       ret = snprintf(valString+i,valStringSize-i,"(%d) ",
+                           value);
+               }
+               if (ret == -1) {
+                       /*
+                        * Some versions of snprintf return -1
+                        * if they'd truncate the output.
+                        */
+                       break;
+               }
+               i += ret;
+               offsetStr++;
+               if (add_string)
+               {
+                       for (;(c = tvb_get_guint8(tvb, offsetStr))
+                           && i < valStringSize - 1; i++,offsetStr++)
+                               valString[i] = c;
+                       offsetStr++;
+                       if (i < valStringSize - 2) {
+                               valString[i++] = ')';
+                               valString[i++] = ' ';
+                       }
                }
        }
-
+       valString[i] = '\0';
 }
 
-static guint
-get_value_length (tvbuff_t *tvb, guint offset, guint *nextOffset)
+static value_type_t
+get_value_type_len (tvbuff_t *tvb, int offset, guint *valueLen,
+    int *valueOffset, int *nextOffset)
 {
-       guint value = 0;
-       guint count = 0;
-       guint octet = tvb_get_guint8 (tvb, offset);
+       guint8 peek;
+       guint32 len;
+       guint count;
 
-       if (octet <= 30)        /* Short length */
+       /* Get value part of header */
+       peek = tvb_get_guint8 (tvb, offset);
+       if (peek <= 30)
+       {
+               /*
+                * The value follows "peek", and is "peek" octets long.
+                */
+#ifdef DEBUG
+               fprintf (stderr, "dissect_wsp: Looking for %d octets\n", peek);
+#endif
+               len = peek;
+               *valueLen = len;        /* Length of value */
+               offset++;               /* Skip the length */
+               *valueOffset = offset;  /* Offset of value */
+               offset += len;          /* Skip the value */
+               *nextOffset = offset;   /* Offset after value */
+               return VALUE_LEN_SUPPLIED;
+       }
+       else if (peek == 31)
        {
-               value = octet;
-               *nextOffset = offset+1;
+               /*
+                * A uintvar giving the length of the value follows
+                * "peek", and the value follows that.
+                */
+#ifdef DEBUG
+               fprintf (stderr, "dissect_wsp: Looking for uintvar octets\n");
+#endif
+               offset++;               /* Skip the uintvar indicator */
+               count = 0;              /* Initialise count */
+               len = tvb_get_guintvar (tvb, offset, &count);
+               *valueLen = len;        /* Length of value */
+               offset += count;        /* Skip the length */
+               *valueOffset = offset;  /* Offset of value */
+               offset += len;          /* Skip the value */
+               *nextOffset = offset;   /* Offset after value */
+               return VALUE_LEN_SUPPLIED;
        }
-       else if (octet == 31)
+       else if (peek <= 127)
        {
-               value = tvb_get_guintvar (tvb, offset+1, &count);
-               *nextOffset = offset+1+count;
+               /*
+                * The value is a NUL-terminated string, and "peek"
+                * is the first octet of the string.
+                */
+#ifdef DEBUG
+               fprintf (stderr, "dissect_wsp: Looking for NUL-terminated string\n");
+#endif
+               len = tvb_strsize (tvb, offset);
+               *valueLen = len;        /* Length of value */
+               *valueOffset = offset;  /* Offset of value */
+               offset += len;          /* Skip the value */
+               *nextOffset = offset;   /* Offset after value */
+               return VALUE_IS_TEXT_STRING;
        }
        else
        {
-               fprintf (stderr, "dissect_wsp: get_value_length: case NYI\n");
+               /*
+                * "peek", with the 8th bit stripped off, is the value.
+                */
+#ifdef DEBUG
+               fprintf (stderr, "dissect_wsp: Value is %d\n", (peek & 0x7F));
+#endif
+               *valueLen = peek & 0x7F; /* Return the value itself */
+               *valueOffset = offset;  /* Offset of value */
+               offset++;               /* Skip the value */
+               *nextOffset = offset;   /* Offset after value */
+               return VALUE_IN_LEN;
        }
-
-       return (value);
 }
 
 static guint
-add_content_type (proto_tree *tree, tvbuff_t *tvb, guint offset, guint *contentType)
+get_uintvar (tvbuff_t *tvb, guint offset, guint offsetEnd)
 {
-       proto_tree *contentTypeTree;
-       guint nextOffset = offset;
-       guint fieldLength = 0;
-       guint octet = tvb_get_guint8 (tvb, offset);
-       guint totalSizeOfField = 0;
+       guint value = 0;
+       guint octet;
 
-       if (octet <= 31)
+       do
        {
-               fieldLength = get_value_length (tvb, offset, &nextOffset);
-               totalSizeOfField = (nextOffset-offset)+fieldLength;
+                       octet = tvb_get_guint8 (tvb, offset);
+               offset++;
+               value <<= 7;
+               value += octet & 0x7f;
        }
-       else if (octet & 0x80)
+       while ((offsetEnd > offset) && (octet & 0x80));
+       return value;
+}
+
+static void
+add_content_type_value (proto_tree *tree, tvbuff_t *header_buff,
+    int headerOffset, int headerLen, tvbuff_t *value_buff,
+    value_type_t valueType, int valueLen, int hf_numeric, int hf_string,
+    guint *contentTypep, const char **contentTypeStrp)
+{
+       proto_item *ti;
+       proto_tree *parameter_tree;
+       const char *contentTypeStr;
+       int offset;
+       int subvalueLen;
+       int subvalueOffset;
+       guint value;
+
+       if (valueType == VALUE_IN_LEN)
        {
-               fieldLength = 1;
-               totalSizeOfField = 1;
+               /*
+                * Constrained-media (Short-Integer).
+                */
+               proto_tree_add_uint (tree, hf_numeric,
+                   header_buff, headerOffset, headerLen,
+                   valueLen);  /* valueLen is the value */
+
+               /*
+                * Return the numerical value, and a null string value
+                * indicating that the value is numerical.
+                */
+               *contentTypep = valueLen;
+               *contentTypeStrp = NULL;
+               return;
        }
-       else
+       if (valueType == VALUE_IS_TEXT_STRING)
        {
-               fprintf (stderr, "dissect-wsp: Content-type is un-supported\n");
+               /*
+                * Constrained-media (text, i.e. Extension-Media).
+                */
+               contentTypeStr = tvb_get_ptr (value_buff, 0, valueLen);
+               proto_tree_add_string (tree, hf_string,
+                   header_buff, headerOffset, headerLen,
+                   contentTypeStr);
+
+               /*
+                * Return the string value, and set the numerical value
+                * to 0 (as it shouldn't be used).
+                */
+               *contentTypep = 0;
+               *contentTypeStrp = contentTypeStr;
+               return;
        }
 
-       *contentType = (tvb_get_guint8 (tvb, nextOffset) & 0x7F);
-       contentTypeTree = proto_tree_add_uint (tree, hf_wsp_content_type, tvb, offset, totalSizeOfField, (tvb_get_guint8(tvb,nextOffset++) & 0x7F));
-
-       while (nextOffset < (offset+totalSizeOfField))
+       /*
+        * Content-general-form; Value-length, followed by Media-range,
+        * followed by optional Accept-parameters.
+        *
+        * Get Value-length.
+        */
+       valueType = get_value_type_len (value_buff, 0, &subvalueLen,
+           &subvalueOffset, &offset);
+       if (valueType == VALUE_IS_TEXT_STRING)
        {
-               /* add_parameter */
-               nextOffset = add_parameter (contentTypeTree, tvb, nextOffset);
+               /*
+                * Extension-Media; value is a string.
+                */
+               contentTypeStr =
+                   tvb_get_ptr (value_buff, subvalueOffset, subvalueLen);
+               ti = proto_tree_add_string (tree, hf_string, header_buff,
+                   headerOffset, headerLen, contentTypeStr);
+
+               /*
+                * Return the string value, and set the numerical value
+                * to 0 (as it shouldn't be used).
+                */
+               *contentTypep = 0;
+               *contentTypeStrp = contentTypeStr;
+       }
+       else
+       {
+               /*
+                * Well-known-media; value is an Integer.
+                */
+               if (get_integer (value_buff, subvalueOffset, subvalueLen,
+                   valueType, &value) < 0)
+               {
+                       proto_tree_add_text (tree, header_buff,
+                           headerOffset, headerLen,
+                           "Invalid integer for Well-known-media");
+
+                       /*
+                        * Content type is invalid.
+                        * Don't try to parse the rest of the value.
+                        */
+                       *contentTypep = 0;
+                       *contentTypeStrp = NULL;
+                       return;
+               }
+               ti = proto_tree_add_uint (tree, hf_numeric,
+                   header_buff, headerOffset, headerLen, value);
+
+               /*
+                * Return the numerical value, and a null string value
+                * indicating that the value is numerical.
+                */
+               *contentTypep = value;
+               *contentTypeStrp = NULL;
        }
 
-       return (offset+totalSizeOfField);
+       /*
+        * Process the rest of the value as parameters.
+        */
+       parameter_tree = proto_item_add_subtree(ti,
+           ett_content_type_parameters);
+       while (tvb_reported_length_remaining (value_buff, offset) > 0)
+               offset = add_parameter (parameter_tree, value_buff, offset);
 }
 
-/* Utility function to extract date values from the packet */
-static gint
-get_date_value (tvbuff_t *buffer, guint offset, struct timeval *timeValue)
+guint
+add_content_type (proto_tree *tree, tvbuff_t *tvb, guint offset,
+    guint *contentTypep, const char **contentTypeStrp)
 {
-       guint ShortLength = 0;
+       int valueStart;
+       value_type_t valueType;
+       int valueTypeLen;
+       guint valueLen;
+       int valueOffset;
+       tvbuff_t *value_buff;
+
+       valueStart = offset;
 
-       /* Initialise time values */
-       timeValue->tv_sec=0;
-       timeValue->tv_usec = 0;
+       /*
+        * Get the value type and length (or, if the type is VALUE_IN_LEN,
+        * meaning the value is a Short-integer, get the value type
+        * and the value itself).
+        */ 
+       valueType = get_value_type_len (tvb, valueStart, &valueLen,
+           &valueOffset, &offset);
+       valueTypeLen = offset - valueStart;
 
-       /* Date values are encoded as: Short-length Multi-octet-integer
-        * Where Short-length = 0-30
+       /*
+        * Get a tvbuff for the value.
+        * XXX - can valueLen be 0?
+        * XXX - cut the actual length short so that it doesn't run
+        * past the actual length of tvb.
         */
-       ShortLength = tvb_get_guint8 (buffer, offset++);
-       if (ShortLength > 30)
+       if (valueType != VALUE_IN_LEN) {
+               value_buff = tvb_new_subset (tvb, valueOffset, valueLen,
+                   valueLen);
+       } else {
+               /*
+                * XXX - when the last dissector is tvbuffified,
+                * so that NULL is no longer a valid tvb pointer
+                * value in "proto_tree_add" calls, just
+                * set "value_buff" to NULL.
+                *
+                * XXX - can we already do that?  I.e., will that
+                * cause us always to crash if we mistakenly try
+                * to fetch the value of a VALUE_IN_LEN item?
+                */
+               value_buff = tvb_new_subset (tvb, valueStart, 0, 0);
+       }
+
+       add_content_type_value (tree, tvb, valueStart, valueTypeLen, value_buff,
+           valueType, valueLen, hf_wsp_content_type,
+           hf_wsp_content_type_str, contentTypep, contentTypeStrp);
+
+       return offset;
+}
+
+static void
+add_integer_value_header (proto_tree *tree, tvbuff_t *header_buff,
+    int headerLen, tvbuff_t *value_buff, value_type_t valueType,
+    int valueLen, int hf_numeric, guint8 headerType)
+{
+       add_integer_value_header_common (tree, header_buff, headerLen,
+           value_buff, valueType, valueLen, hf_numeric, headerType,
+           vals_field_names);
+}
+
+static void
+add_openwave_integer_value_header (proto_tree *tree, tvbuff_t *header_buff,
+    int headerLen, tvbuff_t *value_buff, value_type_t valueType,
+    int valueLen, int hf_numeric, guint8 headerType)
+{
+       add_integer_value_header_common (tree, header_buff, headerLen,
+           value_buff, valueType, valueLen, hf_numeric, headerType,
+           vals_openwave_field_names);
+}
+
+static void
+add_integer_value_header_common (proto_tree *tree, tvbuff_t *header_buff,
+    int headerLen, tvbuff_t *value_buff, value_type_t valueType,
+    int valueLen, int hf_numeric, guint8 headerType,
+    const value_string *vals)
+{
+       guint value;
+
+       if (get_integer (value_buff, 0, valueLen, valueType, &value) < 0)
+       {
+               proto_tree_add_text (tree, header_buff, 0, headerLen,
+                   "Invalid %s integer value",
+                   match_strval (headerType, vals));
+       }
+       else
        {
-               fprintf (stderr, "dissect_wsp: Invalid Date-value (Short-length=%d)\n",
-                       ShortLength);
-               return (-1);
+               proto_tree_add_uint (tree, hf_numeric,
+                   header_buff, 0, headerLen, value);
        }
+}
+
+static void
+add_string_value_header (proto_tree *tree, tvbuff_t *header_buff,
+    int headerLen, tvbuff_t *value_buff, value_type_t valueType,
+    int valueLen, int hf_string, guint8 headerType)
+{
+       add_string_value_header_common (tree, header_buff, headerLen,
+           value_buff, valueType, valueLen, hf_string, headerType,
+           vals_field_names);
+}
+
+static void
+add_openwave_string_value_header (proto_tree *tree, tvbuff_t *header_buff,
+    int headerLen, tvbuff_t *value_buff, value_type_t valueType,
+    int valueLen, int hf_string, guint8 headerType)
+{
+       add_string_value_header_common (tree, header_buff, headerLen,
+           value_buff, valueType, valueLen, hf_string, headerType,
+           vals_openwave_field_names);
+}
 
-       switch (ShortLength)
+static void
+add_string_value_header_common (proto_tree *tree, tvbuff_t *header_buff,
+    int headerLen, tvbuff_t *value_buff, value_type_t valueType,
+    int valueLen, int hf_string, guint8 headerType,
+    const value_string *vals)
+{
+       if (valueType != VALUE_IS_TEXT_STRING)
        {
-               case 1:
-                       timeValue->tv_sec = tvb_get_guint8 (buffer, offset);
-                       break;
-               case 2:
-                       timeValue->tv_sec = tvb_get_ntohs (buffer, offset);
-                       break;
-               case 3:
-                       timeValue->tv_sec = tvb_get_ntoh24 (buffer, offset);
-                       break;
-               case 4:
-                       timeValue->tv_sec = tvb_get_ntohl (buffer, offset);
-                       break;
-               default:
-                       fprintf (stderr, "dissect_wsp: Date-value Short-length of %d NYI\n",
-                               ShortLength);
-                       return (-1);
-                       break;
+               proto_tree_add_text (tree, header_buff, 0, headerLen,
+                   "Invalid %s string value",
+                   match_strval (headerType, vals));
+       }
+       else
+       {
+               proto_tree_add_string (tree, hf_string, header_buff,
+                       0, headerLen, tvb_get_ptr (value_buff, 0, valueLen));
        }
+}
 
-       return (0);
+static void
+add_quoted_string_value_header (proto_tree *tree, tvbuff_t *header_buff,
+    int headerLen, tvbuff_t *value_buff, value_type_t valueType,
+    int valueLen, int hf_string, guint8 headerType)
+{
+       if (valueType != VALUE_IS_TEXT_STRING)
+       {
+               proto_tree_add_text (tree, header_buff, 0, headerLen,
+                   "Invalid %s quoted string value",
+                   match_strval (headerType, vals_field_names));
+       }
+       else
+       {
+               proto_tree_add_string (tree, hf_string, header_buff,
+                       0, headerLen, tvb_get_ptr (value_buff, 1, valueLen - 1));
+       }
 }
 
 /* Utility function to add a date value to the protocol tree */
 static void
-add_date_value (tvbuff_t *buffer, guint offset, proto_tree *tree,
-               int header, tvbuff_t *headerBuffer, guint headerOffset,
-               guint headerLen, struct timeval *timeValue, const char *fieldName)
+add_date_value_header (proto_tree *tree, tvbuff_t *header_buff,
+    int headerLen, tvbuff_t *value_buff, value_type_t valueType,
+    int valueLen, int hf_time, guint8 headerType)
 {
+       guint secs;
+       nstime_t timeValue;
+
        /* Attempt to get the date value from the buffer */
-       if (get_date_value (buffer, offset, timeValue) == 0)
+       if (get_integer (value_buff, 0, valueLen, valueType, &secs) == 0)
        {
-               /* If successful, add it to the protocol tree */
-               proto_tree_add_time (tree, header, headerBuffer, headerOffset,
-                       headerLen, timeValue);
+               /*
+                * Fill in the "struct timeval", and add it to the
+                * protocol tree.
+                * Note: this will succeed even if it's a Short-integer.
+                * A Short-integer would work, but, as the time values
+                * are UNIX seconds-since-the-Epoch value, and as
+                * there weren't WAP phones or Web servers back in
+                * late 1969/early 1970, they're unlikely to be used.
+                */
+               timeValue.secs = secs;
+               timeValue.nsecs = 0;
+               proto_tree_add_time (tree, hf_time, header_buff, 0,
+                       headerLen, &timeValue);
        }
        else
        {
-               fprintf (stderr, "dissect_wsp: Invalid %s value\n", fieldName);
+               proto_tree_add_text (tree, header_buff, 0, headerLen,
+                   "Invalid %s date value",
+                   match_strval (headerType, vals_field_names));
        }
 }
 
-static guint
-add_parameter (proto_tree *tree, tvbuff_t *tvb, guint offset)
+static int
+add_parameter (proto_tree *tree, tvbuff_t *value_buff, int offset)
 {
-       guint octet = tvb_get_guint8 (tvb, offset);
-       if (octet & 0x80)       /* Short integer */
+       int startOffset;
+       value_type_t valueType;
+       int subvalueLen;
+       int subvalueOffset;
+       guint value;
+
+       startOffset = offset;
+       valueType = get_value_type_len (value_buff, offset,
+           &subvalueLen, &subvalueOffset, &offset);
+       if (valueType == VALUE_IS_TEXT_STRING)
        {
-               offset++;
-               octet = octet & 0x7F;
-               switch ( octet )
-               {
-                       case 0x01:
-                               offset = add_parameter_charset (tree, tvb, offset, offset-1);
-                               break;
+               /*
+                * Untyped-parameter.
+                */
+               offset = add_untyped_parameter (tree, value_buff, startOffset, offset);
+               return offset;
+       }
 
-                       default:
-                               fprintf (stderr, "dissect-wsp: add_parameter octet=0x%02x\n", octet);
-               };
+       /*
+        * Well-known-parameter-token.
+        */
+       if (get_integer (value_buff, subvalueOffset,
+           subvalueLen, valueType, &value) < 0)
+       {
+               proto_tree_add_text (tree, value_buff, startOffset,
+                   offset - startOffset,
+                   "Invalid Well-known-parameter-token");
+               return offset;
+       }
+
+       switch (value) {
+
+       case 0x01:      /* Charset */
+               offset = add_parameter_charset (tree, value_buff, startOffset, offset);
+               break;
+
+       case 0x03:      /* Type */
+               offset = add_parameter_type (tree, value_buff, startOffset, offset);
+               break;
+
+       case 0x05:      /* Name */
+               offset = add_parameter_text (tree, value_buff, startOffset, offset,
+                                   hf_wsp_parameter_name, "Name");
+               break;
+
+       case 0x06:      /* Filename */
+               offset = add_parameter_text (tree, value_buff, startOffset, offset,
+                                   hf_wsp_parameter_filename, "Filename");
+               break;
+
+       case 0x09:      /* Type (special) */
+               offset = add_constrained_encoding(tree, value_buff, startOffset, offset);
+               break;
+
+       case 0x0A:      /* Start */
+               offset = add_parameter_text (tree, value_buff, startOffset, offset,
+                                   hf_wsp_parameter_start, "Start");
+               break;
+
+       case 0x0B:      /* Start-info */
+               offset = add_parameter_text (tree, value_buff, startOffset, offset,
+                                   hf_wsp_parameter_start_info, "Start-info");
+               break;
+
+       case 0x0C:      /* Comment */
+               offset = add_parameter_text (tree, value_buff, startOffset, offset,
+                                   hf_wsp_parameter_comment, "Comment");
+               break;
+
+       case 0x0D:      /* Domain */
+               offset = add_parameter_text (tree, value_buff, startOffset, offset,
+                                   hf_wsp_parameter_domain, "Domain");
+               break;
+
+       case 0x0F:      /* Path */
+               offset = add_parameter_text (tree, value_buff, startOffset, offset,
+                                   hf_wsp_parameter_path, "Path");
+               break;
+
+       case 0x00:      /* Q */
+       case 0x02:      /* Level */
+       case 0x07:      /* Differences */
+       case 0x08:      /* Padding */
+       case 0x0E:      /* Max-Age */
+       case 0x10:      /* Secure */
+       default:
+               break;
+       }
+
+       return offset;
+}
+
+static int
+add_untyped_parameter (proto_tree *tree, tvbuff_t *value_buff, int startOffset,
+    int offset)
+{
+       const guint8 *token;
+       value_type_t valueType;
+       int subvalueLen;
+       int subvalueOffset;
+       guint value;
+       int vOffset = offset;
+
+       token = tvb_get_ptr (value_buff, startOffset, offset - startOffset);
+       /*
+        * Now an Untyped-value; either an Integer-value or a Text-value.
+        */
+       valueType = get_value_type_len (value_buff, offset,
+           &subvalueLen, &subvalueOffset, &offset);
+       if (valueType == VALUE_IS_TEXT_STRING)
+       {
+               /*
+                * Text-value.
+                */
+               if ((offset - vOffset) == 1) {
+                       /*
+                        * No-value.  (stringSize includes the terminating
+                        * null byte, so an empty string has a size of 1.)
+                        */
+                       proto_tree_add_text (tree, value_buff, startOffset,
+                           offset - startOffset,
+                           "%s", token);
+                       return offset;
+               }
+               proto_tree_add_text (tree, value_buff, startOffset,
+                   offset - startOffset,
+                   "%s: %s", token,
+                   tvb_get_ptr (value_buff, vOffset, offset - vOffset));
        }
        else
        {
-               fprintf (stderr, "dissect-wsp: add_parameter octet=0x%02x\n", octet);
+               /*
+                * Integer-value.
+                */
+               if (get_integer (value_buff, subvalueOffset, subvalueLen,
+                   valueType, &value) == 0)
+               {
+                       proto_tree_add_text (tree, value_buff, startOffset,
+                           offset - startOffset,
+                           "%s: %u", token, value);
+               }
+               else
+               {
+                       proto_tree_add_text (tree, value_buff, startOffset,
+                           offset - startOffset,
+                           "%s: Invalid Integer-value", token);
+               }
+       }
+       return offset;
+}
+
+static int
+add_parameter_charset (proto_tree *tree, tvbuff_t *value_buff, int startOffset,
+    int offset)
+{
+       value_type_t valueType;
+       int subvalueLen;
+       int subvalueOffset;
+       guint value;
+
+       valueType = get_value_type_len (value_buff, offset,
+           &subvalueLen, &subvalueOffset, &offset);
+       if (valueType == VALUE_IN_LEN)
+       {
+               /*
+                * Integer-value.
+                */
+               proto_tree_add_uint (tree, hf_wsp_parameter_well_known_charset,
+                   value_buff, startOffset, offset - startOffset,
+                   subvalueLen);       /* subvalueLen is the value */
+               return offset;
+       }
+       if (valueType == VALUE_IS_TEXT_STRING)
+       {
+               /*
+                * Invalid.
+                */
+               proto_tree_add_text (tree, value_buff, startOffset,
+                   offset - startOffset, "Invalid Well-known charset");
+               return offset;
+       }
+
+       /*
+        * First byte had the 8th bit set.
+        */
+       if (subvalueLen == 0) {
+               /*
+                * Any-charset.
+                * XXX - add this as a field?
+                */
+               proto_tree_add_text (tree, value_buff, startOffset,
+                   offset- startOffset, "*");
+               return offset;
        }
 
-       return (offset);
+       if (get_integer(value_buff, subvalueOffset, subvalueLen,
+           valueType, &value) == -1) {
+               proto_tree_add_text (tree, value_buff, startOffset,
+                   offset - startOffset, "Length %u not handled in Well-known charset",
+                       subvalueLen);
+       } else {
+               proto_tree_add_uint (tree, hf_wsp_parameter_well_known_charset,
+                   value_buff, startOffset, offset - startOffset, value);
+       }
+       return offset;
 }
 
-static guint
-add_parameter_charset (proto_tree *tree, tvbuff_t *tvb, guint offset, guint startOffset)
+static int
+add_constrained_encoding (proto_tree *tree, tvbuff_t *value_buff, int startOffset,
+    int offset)
 {
-       guint octet = tvb_get_guint8 (tvb, offset);
-       if (octet < 31)
+       value_type_t valueType;
+       int subvalueLen;
+       int subvalueOffset;
+       guint value;
+
+       valueType = get_value_type_len (value_buff, offset,
+           &subvalueLen, &subvalueOffset, &offset);
+       if (valueType == VALUE_IN_LEN)
        {
-               offset += octet+1;
-               proto_tree_add_item (tree, hf_wsp_parameter_well_known_charset, tvb, startOffset+1, octet, bo_big_endian);
+               /*
+                * Integer-value, invalid
+                */
+               proto_tree_add_text (tree, value_buff, startOffset,
+                   offset - startOffset, "Invalid multipart type parameter");
+               return offset;
        }
-       else if (octet & 0x80)
+       if (valueType == VALUE_IS_TEXT_STRING)
        {
-               offset++;
-               proto_tree_add_uint (tree, hf_wsp_parameter_well_known_charset, tvb, startOffset, offset-startOffset, (octet & 0x7F));
+               /*
+                * type-label.
+                */
+               proto_tree_add_string (tree, hf_wsp_parameter_upart_type,
+                   value_buff, startOffset, offset - startOffset,
+                   tvb_get_ptr (value_buff, subvalueOffset, subvalueLen));
+               return offset;
+       }
+       /*
+        * First byte had the 8th bit set.
+        */
+       get_integer(value_buff, subvalueOffset, subvalueLen, valueType, &value);
+       proto_tree_add_uint (tree, hf_wsp_parameter_upart_type_value,
+           value_buff, startOffset, offset - startOffset, value);
+       return offset;
+}
+
+static int
+add_parameter_type (proto_tree *tree, tvbuff_t *value_buff, int startOffset,
+    int offset)
+{
+       value_type_t valueType;
+       int subvalueLen;
+       int subvalueOffset;
+       guint value;
+
+       valueType = get_value_type_len (value_buff, offset,
+           &subvalueLen, &subvalueOffset, &offset);
+       if (get_integer(value_buff, subvalueOffset, subvalueLen,
+           valueType, &value) == -1) {
+               proto_tree_add_text (tree, value_buff, startOffset,
+                   offset - startOffset, "Invalid type");
+       } else {
+               proto_tree_add_uint (tree, hf_wsp_parameter_type, value_buff,
+                   startOffset, offset - startOffset, value);
        }
+       return offset;
+}
 
+static int
+add_parameter_text (proto_tree *tree, tvbuff_t *value_buff, int startOffset,
+    int offset, int hf_string, const char *paramName)
+{
+       value_type_t valueType;
+       int subvalueLen;
+       int subvalueOffset;
+
+       valueType = get_value_type_len (value_buff, offset,
+           &subvalueLen, &subvalueOffset, &offset);
+       if (valueType != VALUE_IS_TEXT_STRING) {
+               proto_tree_add_text (tree, value_buff, startOffset,
+                   offset - startOffset, "Invalid %s", paramName);
+       } else {
+               proto_tree_add_string (tree, hf_string, value_buff,
+                           startOffset, offset - startOffset,
+                           tvb_get_ptr (value_buff, subvalueOffset, subvalueLen));
+       }
        return offset;
 }
 
 static void
-add_post_data (proto_tree *tree, tvbuff_t *tvb, guint contentType)
+add_post_data (proto_tree *tree, tvbuff_t *tvb, guint contentType,
+    const char *contentTypeStr)
 {
        guint offset = 0;
        guint variableStart = 0;
@@ -1396,12 +3814,15 @@ add_post_data (proto_tree *tree, tvbuff_t *tvb, guint contentType)
        guint8 peek = 0;
        proto_item *ti;
        
-       /* VERIFY ti = proto_tree_add_item (tree, hf_wsp_post_data,tvb,offset,END_OF_FRAME,bo_little_endian); */
-       ti = proto_tree_add_item (tree, hf_wsp_post_data,tvb,offset,tvb_reported_length(tvb),bo_little_endian);
+       /* VERIFY ti = proto_tree_add_item (tree, hf_wsp_post_data,tvb,offset,-1,bo_little_endian); */
+       ti = proto_tree_add_item (tree, hf_wsp_post_data,tvb,offset,-1,bo_little_endian);
 
-       if (contentType == 0x12)        /* URL Encoded data */
+       if (contentTypeStr == NULL && contentType == 0x12)
        {
-               /* Iterate through post data */
+               /*
+                * URL Encoded data.
+                * Iterate through post data.
+                */
                for (offset = 0; offset < tvb_reported_length (tvb); offset++)
                {
                        peek = tvb_get_guint8 (tvb, offset);
@@ -1429,6 +3850,11 @@ add_post_data (proto_tree *tree, tvbuff_t *tvb, guint contentType)
                        add_post_variable (ti, tvb, variableStart, variableEnd, valueStart, offset);
                }
        }
+       else if ((contentType == 0x22) || (contentType == 0x23) || (contentType == 0x23) || (contentType == 0x24) ||
+                (contentType == 0x25) || (contentType == 0x26) || (contentType == 0x33))
+       {
+               add_multipart_data(ti, tvb);
+       }
 }
 
 static void
@@ -1471,6 +3897,102 @@ add_post_variable (proto_tree *tree, tvbuff_t *tvb, guint variableStart, guint v
        g_free (valueBuffer);
 }
 
+void
+add_multipart_data (proto_tree *tree, tvbuff_t *tvb)
+{
+       int              offset = 0;
+       guint            nextOffset;
+       guint            nEntries = 0;
+       guint            count;
+       guint            HeadersLen;
+       guint            DataLen;
+       guint            contentType = 0;
+       const char      *contentTypeStr;
+       tvbuff_t        *tmp_tvb;
+       int              partnr = 1;
+       int              part_start;
+
+       proto_item      *sub_tree = NULL,
+                       *ti;
+       proto_tree      *mpart_tree;
+
+       nEntries = tvb_get_guintvar (tvb, offset, &count);
+       offset += count;
+       if (nEntries)
+       {
+               sub_tree = proto_tree_add_text(tree, tvb, offset - count, 0,
+                                       "Multipart body");
+               proto_item_add_subtree(sub_tree, ett_mpartlist);
+       }
+       while (nEntries--)
+       {
+               part_start = offset;
+               HeadersLen = tvb_get_guintvar (tvb, offset, &count);
+               offset += count;
+               DataLen = tvb_get_guintvar (tvb, offset, &count);
+               offset += count;
+               ti = proto_tree_add_uint(sub_tree, hf_wsp_mpart, tvb, part_start,
+                                       HeadersLen + DataLen + (offset - part_start), partnr);
+               mpart_tree = proto_item_add_subtree(ti, ett_multiparts);
+               nextOffset = add_content_type (mpart_tree, tvb, offset, &contentType, &contentTypeStr);
+               HeadersLen -= (nextOffset - offset);
+               if (HeadersLen > 0)
+               {
+                       tmp_tvb = tvb_new_subset (tvb, nextOffset, HeadersLen, HeadersLen);
+                       add_headers (mpart_tree, tmp_tvb);
+               }
+               offset = nextOffset + HeadersLen;
+               proto_tree_add_item (mpart_tree, hf_wsp_multipart_data, tvb, offset, DataLen, bo_little_endian);
+               offset += DataLen;
+               partnr++;
+       }
+}
+
+static gint
+get_integer (tvbuff_t *tvb, guint offset, guint valueLength,
+    value_type_t valueType, guint *value)
+{
+       if (valueType == VALUE_IS_TEXT_STRING) {
+               /*
+                * Not valid.
+                */
+               return -1;
+       }
+
+       if (valueType == VALUE_IN_LEN) {
+               /*
+                * Short-integer.
+                */
+               *value = valueLength;
+               return 0;
+       }
+
+       /*
+        * Long-integer.
+        */
+       switch (valueLength)
+       {
+               case 1:
+                       *value = tvb_get_guint8(tvb, offset);
+                       break;
+               case 2:
+                       *value = tvb_get_ntohs(tvb, offset);
+                       break;
+               case 3:
+                       *value = tvb_get_ntoh24(tvb, offset);
+                       break;
+               case 4:
+                       *value = tvb_get_ntohl(tvb, offset);
+                       break;
+               default:
+                       /* TODO: Need to read peek octets */
+                       *value = 0;
+                       fprintf (stderr, "dissect_wsp: get_integer size %u NYI\n", valueLength);
+                       break;
+       }
+       return 0;
+}
+
 /* Register the protocol with Ethereal */
 void
 proto_register_wsp(void)
@@ -1482,112 +4004,197 @@ proto_register_wsp(void)
                        {       "Transmission ID",           
                                "wsp.TID",
                                 FT_UINT8, BASE_HEX, NULL, 0x00,
-                               "Transmission ID" 
+                               "Transmission ID", HFILL
                        }
                },
                { &hf_wsp_header_pdu_type,
                        {       "PDU Type",           
                                "wsp.pdu_type",
                                 FT_UINT8, BASE_HEX, VALS( vals_pdu_type ), 0x00,
-                               "PDU Type" 
+                               "PDU Type", HFILL
                        }
                },
                { &hf_wsp_version_major,
                        {       "Version (Major)",           
                                "wsp.version.major",
                                 FT_UINT8, BASE_DEC, NULL, 0xF0,
-                               "Version (Major)" 
+                               "Version (Major)", HFILL
                        }
                },
                { &hf_wsp_version_minor,
                        {       "Version (Minor)",           
                                "wsp.version.minor",
                                 FT_UINT8, BASE_DEC, NULL, 0x0F,
-                               "Version (Minor)" 
+                               "Version (Minor)", HFILL
                        }
                },
                { &hf_wsp_capability_length,
                        {       "Capability Length",           
                                "wsp.capability.length",
                                 FT_UINT32, BASE_DEC, NULL, 0x00,
-                               "Capability Length" 
+                               "Capability Length", HFILL
                        }
                },
                { &hf_wsp_header_length,
                        {       "Headers Length",           
                                "wsp.headers_length",
                                 FT_UINT32, BASE_DEC, NULL, 0x00,
-                               "Headers Length" 
+                               "Headers Length", HFILL
                        }
                },
                { &hf_wsp_capabilities_section,
                        {       "Capabilities",           
                                "wsp.capabilities",
                                 FT_NONE, BASE_DEC, NULL, 0x00,
-                               "Capabilities" 
+                               "Capabilities", HFILL
                        }
                },
                { &hf_wsp_headers_section,
                        {       "Headers",           
                                "wsp.headers",
                                 FT_NONE, BASE_DEC, NULL, 0x00,
-                               "Headers" 
+                               "Headers", HFILL
                        }
                },
                { &hf_wsp_header,
                        {       "Header",           
                                "wsp.headers.header",
                                 FT_NONE, BASE_DEC, NULL, 0x00,
-                               "Header" 
+                               "Header", HFILL
                        }
                },
                { &hf_wsp_header_uri_len,
                        {       "URI Length",           
                                "wsp.uri_length",
                                 FT_UINT32, BASE_DEC, NULL, 0x00,
-                               "URI Length" 
+                               "URI Length", HFILL
                        }
                },
                { &hf_wsp_header_uri,
                        {       "URI",           
                                "wsp.uri",
                                 FT_STRING, BASE_NONE, NULL, 0x00,
-                               "URI" 
+                               "URI", HFILL
                        }
                },
                { &hf_wsp_server_session_id,
                        {       "Server Session ID",           
                                "wsp.server.session_id",
                                 FT_UINT32, BASE_DEC, NULL, 0x00,
-                               "Server Session ID" 
+                               "Server Session ID", HFILL
                        }
                },
                { &hf_wsp_header_status,
                        {       "Status",           
                                "wsp.reply.status",
                                 FT_UINT8, BASE_HEX, VALS( vals_status ), 0x00,
-                               "Status" 
+                               "Status", HFILL
                        }
                },
                { &hf_wsp_content_type,
                        {       "Content Type",           
                                "wsp.content_type.type",
                                 FT_UINT8, BASE_HEX, VALS ( vals_content_types ), 0x00,
-                               "Content Type" 
+                               "Content Type", HFILL
+                       }
+               },
+               { &hf_wsp_content_type_str,
+                       {       "Content Type",           
+                               "wsp.content_type.type.string",
+                                FT_STRING, BASE_NONE, NULL, 0x00,
+                               "Content Type", HFILL
                        }
                },
                { &hf_wsp_parameter_well_known_charset,
                        {       "Charset",           
                                "wsp.content_type.parameter.charset",
                                 FT_UINT16, BASE_HEX, VALS ( vals_character_sets ), 0x00,
-                               "Charset" 
+                               "Charset", HFILL
+                       }
+               },
+               { &hf_wsp_parameter_type,
+                       {       "Type",           
+                               "wsp.content_type.parameter.type",
+                                FT_UINT32, BASE_DEC, NULL, 0x00,
+                               "Type", HFILL
+                       }
+               },
+               { &hf_wsp_parameter_name,
+                       {       "Name",
+                               "wsp.content_type.parameter.name",
+                                FT_STRING, BASE_NONE, NULL, 0x00,
+                               "Name", HFILL
+                       }
+               },
+               { &hf_wsp_parameter_filename,
+                       {       "Filename",
+                               "wsp.content_type.parameter.filename",
+                                FT_STRING, BASE_NONE, NULL, 0x00,
+                               "Filename", HFILL
+                       }
+               },
+               { &hf_wsp_parameter_start,
+                       {       "Start",
+                               "wsp.content_type.parameter.start",
+                                FT_STRING, BASE_NONE, NULL, 0x00,
+                               "Start", HFILL
+                       }
+               },
+               { &hf_wsp_parameter_start_info,
+                       {       "Start-info",
+                               "wsp.content_type.parameter.start_info",
+                                FT_STRING, BASE_NONE, NULL, 0x00,
+                               "Start-info", HFILL
+                       }
+               },
+               { &hf_wsp_parameter_comment,
+                       {       "Comment",
+                               "wsp.content_type.parameter.comment",
+                                FT_STRING, BASE_NONE, NULL, 0x00,
+                               "Comment", HFILL
+                       }
+               },
+               { &hf_wsp_parameter_domain,
+                       {       "Domain",
+                               "wsp.content_type.parameter.domain",
+                                FT_STRING, BASE_NONE, NULL, 0x00,
+                               "Domain", HFILL
+                       }
+               },
+               { &hf_wsp_parameter_path,
+                       {       "Path",
+                               "wsp.content_type.parameter.path",
+                                FT_STRING, BASE_NONE, NULL, 0x00,
+                               "Path", HFILL
+                       }
+               },
+               { &hf_wsp_parameter_upart_type,
+                       {       "Type",
+                               "wsp.content_type.parameter.upart.type",
+                                FT_STRING, BASE_NONE, NULL, 0x00,
+                               "Multipart type", HFILL
+                       }
+               },
+               { &hf_wsp_parameter_upart_type_value,
+                       {       "Type",
+                               "wsp.content_type.parameter.upart.type.int",
+                                FT_UINT8, BASE_DEC, NULL, 0x00,
+                               "Multipart type (int value)", HFILL
                        }
                },
                { &hf_wsp_reply_data,
                        {       "Data",           
                                "wsp.reply.data",
                                 FT_NONE, BASE_NONE, NULL, 0x00,
-                               "Data" 
+                               "Data", HFILL
+                       }
+               },
+               { &hf_wsp_header_shift_code,
+                       {       "Shift code",           
+                               "wsp.header.shift",
+                                /*FT_NONE, BASE_DEC, NULL, 0x00,*/
+                                FT_UINT8, BASE_HEX, NULL, 0x00,
+                               "Shift code", HFILL
                        }
                },
                { &hf_wsp_header_accept,
@@ -1595,77 +4202,361 @@ proto_register_wsp(void)
                                "wsp.header.accept",
                                 /*FT_NONE, BASE_DEC, NULL, 0x00,*/
                                 FT_UINT8, BASE_HEX, VALS ( vals_content_types ), 0x00,
-                               "Accept" 
+                               "Accept", HFILL
                        }
                },
                { &hf_wsp_header_accept_str,
                        {       "Accept",           
                                "wsp.header.accept.string",
                                 FT_STRING, BASE_NONE, NULL, 0x00,
-                               "Accept" 
+                               "Accept", HFILL
+                       }
+               },
+               { &hf_wsp_header_accept_application,
+                       {       "Accept-Application",           
+                               "wsp.header.accept_application",
+                                FT_UINT32, BASE_HEX, NULL, 0x00,
+                               "Accept-Application", HFILL
+                       }
+               },
+               { &hf_wsp_header_accept_application_str,
+                       {       "Accept-Application",
+                               "wsp.header.accept_application.string",
+                                FT_STRING, BASE_NONE, NULL, 0x00,
+                               "Accept-Application", HFILL
                        }
                },
                { &hf_wsp_header_accept_charset,
                        {       "Accept-Charset",           
                                "wsp.header.accept_charset",
                                 FT_UINT16, BASE_HEX, VALS ( vals_character_sets ), 0x00,
-                               "Accept-Charset" 
+                               "Accept-Charset", HFILL
                        }
                },
                { &hf_wsp_header_accept_charset_str,
                        {       "Accept-Charset",           
                                "wsp.header.accept_charset.string",
                                 FT_STRING, BASE_NONE, NULL, 0x00,
-                               "Accept-Charset" 
+                               "Accept-Charset", HFILL
                        }
                },
                { &hf_wsp_header_accept_language,
                        {       "Accept-Language",           
                                "wsp.header.accept_language",
                                 FT_UINT8, BASE_HEX, VALS ( vals_languages ), 0x00,
-                               "Accept-Language" 
+                               "Accept-Language", HFILL
                        }
                },
                { &hf_wsp_header_accept_language_str,
                        {       "Accept-Language",           
                                "wsp.header.accept_language.string",
                                 FT_STRING, BASE_NONE, NULL, 0x00,
-                               "Accept-Language" 
+                               "Accept-Language", HFILL
                        }
                },
                { &hf_wsp_header_accept_ranges,
                        {       "Accept-Ranges",           
                                "wsp.header.accept_ranges",
                                 FT_UINT8, BASE_HEX, VALS ( vals_accept_ranges ), 0x00,
-                               "Accept-Ranges" 
+                               "Accept-Ranges", HFILL
+                       }
+               },
+               { &hf_wsp_header_accept_ranges_str,
+                       {       "Accept-Ranges",           
+                               "wsp.header.accept_ranges.string",
+                                FT_STRING, BASE_NONE, NULL, 0x00,
+                               "Accept-Ranges", HFILL
                        }
                },
                { &hf_wsp_header_age,
                        {       "Age",           
                                "wsp.header.age",
                                 FT_UINT32, BASE_DEC, NULL, 0x00,
-                               "Age" 
+                               "Age", HFILL
+                       }
+               },
+               { &hf_wsp_header_openwave_proxy_push_addr,
+                       {       "x-up-proxy-push-addr",
+                               "wsp.header.x-up-proxy-push-addr",
+                                FT_BYTES, BASE_HEX, NULL, 0x00,
+                               "The network address and port number that the handset can receive UPNOTIFY pushes on.", HFILL
+                       }
+               },
+               { &hf_wsp_header_openwave_proxy_push_accept,
+                       {       "x-up-proxy-push-accept",
+                               "wsp.header.x-up-proxy-push-accept",
+                                FT_STRING, BASE_NONE, NULL, 0x00,
+                               "The content types that the handset can handle when sent via UPNOTIFY pushes.", HFILL
+                       }
+               },
+               { &hf_wsp_header_openwave_proxy_push_seq,
+                       {       "x-up-proxy-push-seq",
+                               "wsp.header.x-up-proxy-push-seq",
+                                FT_UINT16, BASE_DEC, NULL, 0x00,
+                               "Specifies the sequence number of the last UPNOTIFY push sent.", HFILL
+                       }
+               },
+               { &hf_wsp_header_openwave_proxy_notify,
+                       {       "x-up-proxy-notify",           
+                               "wsp.header.x-up-proxy-notify",
+                                FT_UINT8, BASE_DEC, NULL, 0x00,
+                               "Indicates to the handset that there are pending UPNOTIFY pushes waiting.", HFILL
+                       }
+               },
+               { &hf_wsp_header_openwave_proxy_operator_domain,
+                       {       "x-up-proxy-operator-domain",           
+                               "wsp.header.x-up-proxy-operator-domain",
+                                FT_STRING, BASE_NONE, NULL, 0x00,
+                               "Indicates the Trusted Provisioning Domain.", HFILL
+                       }
+               },
+               { &hf_wsp_header_openwave_proxy_home_page,
+                       {       "x-up-proxy-home-page",           
+                               "wsp.header.x-up-proxy-home-page",
+                                FT_STRING, BASE_NONE, NULL, 0x00,
+                               "Specifies the server-assigned home page URL.", HFILL
+                       }
+               },
+               { &hf_wsp_header_openwave_devcap_has_color,
+                       {       "x-up-devcap-has-color",           
+                               "wsp.header.x-up-devcap-has-color",
+                                FT_UINT8, BASE_DEC, NULL, 0x00,
+                               "Indicates if the handset supports colour.", HFILL
+                       }
+               },
+               { &hf_wsp_header_openwave_devcap_num_softkeys,
+                       {       "x-up-devcap-num-softkeys",
+                               "wsp.header.x-up-devcap-num-softkeys",
+                                FT_UINT8, BASE_DEC, NULL, 0x00,
+                               "The number of softkeys that can be displayed on the handset.", HFILL
+                       }
+               },
+               { &hf_wsp_header_openwave_devcap_softkey_size,
+                       {       "x-up-devcap-softkey-size",
+                               "wsp.header.x-up-devcap-softkey-size",
+                                FT_UINT8, BASE_DEC, NULL, 0x00,
+                               "The number of chars that can be displayed on a softkey label.", HFILL
+                       }
+               },
+               { &hf_wsp_header_openwave_devcap_screen_chars,
+                       {       "x-up-devcap-screen-chars",
+                               "wsp.header.x-up-devcap-screen-chars",
+                                FT_UINT8, BASE_DEC, NULL, 0x00,
+                               "The height and width of the handset's display in characters.", HFILL
+                       }
+               },
+               { &hf_wsp_header_openwave_devcap_screen_pixels,
+                       {       "x-up-devcap-screen-pixels",
+                               "wsp.header.x-up-devcap-screen-pixels",
+                                FT_UINT32, BASE_DEC, NULL, 0x00,
+                               "The height and width of the handset's display in pixels.", HFILL
+                       }
+               },
+               { &hf_wsp_header_openwave_devcap_em_size,
+                       {       "x-up-devcap-em-size",
+                               "wsp.header.x-up-devcap-em-size",
+                                FT_UINT32, BASE_DEC, NULL, 0x00,
+                               "The height and width of an uppercase M in pixels in a handset.", HFILL
+                       }
+               },
+               { &hf_wsp_header_openwave_devcap_screen_depth,
+                       {       "x-up-devcap-screen-depth",
+                               "wsp.header.x-up-devcap-screen-depth",
+                                FT_UINT8, BASE_DEC, NULL, 0x00,
+                               "The colour/gray depth of the display in bits.", HFILL
+                       }
+               },
+               { &hf_wsp_header_openwave_devcap_immed_alert,
+                       {       "x-up-devcap-immed-alert",
+                               "wsp.header.x-up-devcap-immed-alert",
+                                FT_UINT8, BASE_DEC, NULL, 0x00,
+                               "Indicates if the handset has support for immediate UPNOTIFY alerts.", HFILL
+                       }
+               },
+               { &hf_wsp_header_openwave_proxy_net_ask,
+                       {       "x-up-proxy-net-ask",
+                               "wsp.header.x-up-proxy-net-ask",
+                                FT_UINT8, BASE_DEC, NULL, 0x00,
+                               "Indicates to browser if circuit switched call is allowed without user interaction", HFILL
+                       }
+               },
+               { &hf_wsp_header_openwave_proxy_uplink_version,
+                       {       "x-up-proxy-uplink-version",
+                               "wsp.header.x-up-proxy-uplink-version",
+                                FT_STRING, BASE_NONE, NULL, 0x00,
+                               "Version of the MAG WAP gateway", HFILL
+                       }
+               },
+               { &hf_wsp_header_openwave_proxy_tod,
+                       {       "x-up-proxy-tod",
+                               "wsp.header.x-up-proxy-tod",
+                                FT_UINT8, BASE_DEC, NULL, 0x00,
+                               "Time of day", HFILL
+                       }
+               },
+               { &hf_wsp_header_openwave_proxy_ba_enable,
+                       {       "x-up-proxy-ba-enable",
+                               "wsp.header.x-up-proxy-ba-enable",
+                                FT_UINT8, BASE_DEC, NULL, 0x00,
+                               "Indicates if the WAP gateway should cache basic authentication details on behalf of the handset", HFILL
+                       }
+               },
+               { &hf_wsp_header_openwave_proxy_ba_realm,
+                       {       "x-up-proxy-ba-realm",
+                               "wsp.header.x-up-proxy-ba-realm",
+                                FT_STRING, BASE_NONE, NULL, 0x00,
+                               "Indicates the realm within which basic authentication credentials apply", HFILL
+                       }
+               },
+               { &hf_wsp_header_openwave_proxy_redirect_enable,
+                       {       "x-up-proxy-redirect-enable",
+                               "wsp.header.x-up-proxy-redirect-enable",
+                                FT_UINT8, BASE_DEC, NULL, 0x00,
+                               "Indicates if the handset wants the WAP gateway to handle HTTP redirects on its behalf", HFILL
+                       }
+               },
+               { &hf_wsp_header_openwave_proxy_request_uri,
+                       {       "x-up-proxy-request-uri",
+                               "wsp.header.x-up-proxy-request-uri",
+                                FT_STRING, BASE_NONE, NULL, 0x00,
+                               "Indicates to the handset that the previous request was redirected to the specified URI", HFILL
+                       }
+               },
+               { &hf_wsp_header_openwave_proxy_redirect_status,
+                       {       "x-up-proxy-redirect-status",
+                               "wsp.header.x-up-proxy-redirect-status",
+                                FT_UINT32, BASE_DEC, NULL, 0x00,
+                               "Indicates the status of a redirect performed on behalf of a handset", HFILL
+                       }
+               },
+               { &hf_wsp_header_openwave_proxy_trans_charset,
+                       {       "x-up-proxy-trans-charset",
+                               "wsp.header.x-up-proxy-trans-charset",
+                                FT_UINT16, BASE_HEX, VALS ( vals_character_sets ), 0x00,
+                               "For POSTs indicates the charset encoding of a document", HFILL
+                       }
+               },
+               { &hf_wsp_header_openwave_proxy_trans_charset_str,
+                       {       "x-up-proxy-trans-charset",
+                               "wsp.header.x-up-proxy-trans-charset.string",
+                                FT_STRING, BASE_NONE, NULL, 0x00,
+                               "For POSTs indicates the charset encoding of a document", HFILL
+                       }
+               },
+               { &hf_wsp_header_openwave_proxy_linger,
+                       {       "x-up-proxy-linger",
+                               "wsp.header.x-up-proxy-linger",
+                                FT_UINT8, BASE_DEC, NULL, 0x00,
+                               "Indicates the circuit linger time in seconds", HFILL
+                       }
+               },
+               { &hf_wsp_header_openwave_proxy_client_id,
+                       {       "x-up-proxy-client-id",
+                               "wsp.header.x-up-proxy-client-id",
+                                FT_BYTES, BASE_DEC, NULL, 0x00,
+                               "The ClientId of the handset", HFILL
+                       }
+               },
+               { &hf_wsp_header_openwave_proxy_enable_trust,
+                       {       "x-up-proxy-enable-trust",
+                               "wsp.header.x-up-proxy-enable-trust",
+                                FT_UINT8, BASE_DEC, NULL, 0x00,
+                               "Indicates whether to enable Trusted Provisioning Domain", HFILL
+                       }
+               },
+               { &hf_wsp_header_openwave_proxy_trust_old,
+                       {       "x-up-proxy-trust-old",
+                               "wsp.header.x-up-proxy-trust-old",
+                                FT_UINT8, BASE_DEC, NULL, 0x00,
+                               "Indicates if the content being returned was received from within the Trusted Provisioning Domain", HFILL
+                       }
+               },
+               { &hf_wsp_header_openwave_proxy_trust,
+                       {       "x-up-proxy-trust",
+                               "wsp.header.x-up-proxy-trust",
+                                FT_UINT8, BASE_DEC, NULL, 0x00,
+                               "Indicates if the content being returned was received from within the Trusted Provisioning Domain", HFILL
+                       }
+               },
+               { &hf_wsp_header_openwave_proxy_bookmark,
+                       {       "x-up-proxy-bookmark",
+                               "wsp.header.x-up-proxy-bookmark",
+                                FT_STRING, BASE_NONE, NULL, 0x00,
+                               "Specifies the URL to use for server-side bookmarks", HFILL
+                       }
+               },
+               { &hf_wsp_header_openwave_devcap_gui,
+                       {       "x-up-devcap-gui",
+                               "wsp.header.x-up-devcap-gui",
+                                FT_UINT8, BASE_DEC, NULL, 0x00,
+                               "Indicates if the handset has a GUI", HFILL
+                       }
+               },
+               { &hf_wsp_header_bearer_indication,
+                       /*
+                        * XXX - I'm assuming that the bearer indication is
+                        * just a bearer type.
+                        */
+                       {       "Bearer-indication",           
+                               "wsp.header.bearer_indication",
+                                FT_UINT32, BASE_HEX, VALS(vals_bearer_types), 0x00,
+                               "Bearer-indication", HFILL
                        }
                },
                { &hf_wsp_header_cache_control,
                        {       "Cache-Control",           
                                "wsp.header.cache_control",
                                 FT_UINT8, BASE_HEX, VALS ( vals_cache_control ), 0x00,
-                               "Cache-Control" 
+                               "Cache-Control", HFILL
+                       }
+               },
+               { &hf_wsp_header_cache_control_str,
+                       {       "Cache-Control",           
+                               "wsp.header.cache_control.string",
+                                FT_STRING, BASE_NONE, NULL, 0x00,
+                               "Cache-Control", HFILL
+                       }
+               },
+               { &hf_wsp_header_cache_control_field_name,
+                       {       "Field Name",
+                               "wsp.header.cache_control.field_name",
+                                FT_UINT8, BASE_HEX, VALS ( vals_field_names ), 0x00,
+                               "Cache-Control field name", HFILL
+                       }
+               },
+               { &hf_wsp_header_cache_control_field_name_str,
+                       {       "Field Name",
+                               "wsp.header.cache_control.field_name.str",
+                                FT_STRING, BASE_NONE, NULL, 0x00,
+                               "Cache-Control field name", HFILL
+                       }
+               },
+               { &hf_wsp_header_connection,
+                       {       "Connection",           
+                               "wsp.header.connection",
+                                FT_UINT8, BASE_HEX, VALS ( vals_connection ), 0x00,
+                               "Connection", HFILL
+                       }
+               },
+               { &hf_wsp_header_connection_str,
+                       {       "Connection",           
+                               "wsp.header.connection_str",
+                                FT_STRING, BASE_NONE, NULL, 0x00,
+                               "Connection", HFILL
                        }
                },
                { &hf_wsp_header_content_length,
                        {       "Content-Length",           
                                "wsp.header.content_length",
                                 FT_UINT32, BASE_DEC, NULL, 0x00,
-                               "Content-Length" 
+                               "Content-Length", HFILL
                        }
                },
                { &hf_wsp_header_date,
                        {       "Date",           
                                "wsp.header.date",
                                 FT_ABSOLUTE_TIME, BASE_NONE, NULL, 0x0,
-                               "Date" 
+                               "Date", HFILL
                        }
                },
                { &hf_wsp_header_etag,
@@ -1673,35 +4564,51 @@ proto_register_wsp(void)
                                "wsp.header.etag",
                                 /*FT_NONE, BASE_DEC, NULL, 0x00,*/
                                 FT_STRING, BASE_NONE, NULL, 0x00,
-                               "Etag" 
+                               "Etag", HFILL
                        }
                },
                { &hf_wsp_header_expires,
                        {       "Expires",           
                                "wsp.header.expires",
                                 FT_ABSOLUTE_TIME, BASE_NONE, NULL, 0x0,
-                               "Expires" 
+                               "Expires", HFILL
                        }
                },
                { &hf_wsp_header_last_modified,
                        {       "Last-Modified",           
                                "wsp.header.last_modified",
                                 FT_ABSOLUTE_TIME, BASE_NONE, NULL, 0x0,
-                               "Last-Modified" 
+                               "Last-Modified", HFILL
                        }
                },
                { &hf_wsp_header_location,
                        {       "Location",           
                                "wsp.header.location",
                                 FT_STRING, BASE_NONE, NULL, 0x00,
-                               "Location" 
+                               "Location", HFILL
                        }
                },
                { &hf_wsp_header_if_modified_since,
                        {       "If-Modified-Since",           
                                "wsp.header.if_modified_since",
                                 FT_ABSOLUTE_TIME, BASE_NONE, NULL, 0x0,
-                               "If-Modified-Since" 
+                               "If-Modified-Since", HFILL
+                       }
+               },
+               { &hf_wsp_header_pragma,
+                       {       "Pragma",           
+                               "wsp.header.pragma",
+                                /*FT_NONE, BASE_DEC, NULL, 0x00,*/
+                                FT_STRING, BASE_NONE, NULL, 0x00,
+                               "pragma", HFILL
+                       }
+               },
+               { &hf_wsp_header_profile,
+                       {       "Profile",           
+                               "wsp.header.profile",
+                                /*FT_NONE, BASE_DEC, NULL, 0x00,*/
+                                FT_STRING, BASE_NONE, NULL, 0x00,
+                               "Profile", HFILL
                        }
                },
                { &hf_wsp_header_server,
@@ -1709,7 +4616,7 @@ proto_register_wsp(void)
                                "wsp.header.server",
                                 /*FT_NONE, BASE_DEC, NULL, 0x00,*/
                                 FT_STRING, BASE_NONE, NULL, 0x00,
-                               "Server" 
+                               "Server", HFILL
                        }
                },
                { &hf_wsp_header_transfer_encoding,
@@ -1717,14 +4624,14 @@ proto_register_wsp(void)
                                "wsp.header.transfer_enc",
                                 /*FT_NONE, BASE_DEC, NULL, 0x00,*/
                                 FT_UINT8, BASE_HEX, VALS ( vals_transfer_encoding ), 0x00,
-                               "Transfer Encoding" 
+                               "Transfer Encoding", HFILL
                        }
                },
                { &hf_wsp_header_transfer_encoding_str,
                        {       "Transfer Encoding",           
                                "wsp.header.transfer_enc_str",
                                 FT_STRING, BASE_NONE, NULL, 0x00,
-                               "Transfer Encoding" 
+                               "Transfer Encoding", HFILL
                        }
                },
                { &hf_wsp_header_user_agent,
@@ -1732,42 +4639,252 @@ proto_register_wsp(void)
                                "wsp.header.user_agent",
                                 /*FT_NONE, BASE_DEC, NULL, 0x00,*/
                                 FT_STRING, BASE_NONE, NULL, 0x00,
-                               "User-Agent" 
+                               "User-Agent", HFILL
                        }
                },
                { &hf_wsp_header_via,
                        {       "Via",           
                                "wsp.header.via",
                                 FT_STRING, BASE_NONE, NULL, 0x00,
-                               "Via" 
+                               "Via", HFILL
+                       }
+               },
+               { &hf_wsp_header_wap_application_id,
+                       {       "X-Wap-Application-Id",           
+                               "wsp.header.wap_application_id",
+                                FT_UINT8, BASE_HEX, NULL, 0x00,
+                               "WAP application id", HFILL
+                       }
+               },
+               { &hf_wsp_header_wap_application_id_str,
+                       {       "X-Wap-Application-Id",           
+                               "wsp.header.wap_application_id.string",
+                                FT_STRING, BASE_NONE, NULL, 0x00,
+                               "WAP application id", HFILL
+                       }
+               },
+               { &hf_wsp_header_warning,
+                       {       "Warning",
+                               "wsp.header.warning",
+                                FT_NONE, BASE_NONE, NULL, 0x00,
+                               "Warning", HFILL
+                       }
+               },
+               { &hf_wsp_header_warning_code,
+                       {       "Warning Code",
+                               "wsp.header.warning.code",
+                                FT_UINT32, BASE_DEC, NULL, 0x00,
+                               "Warning Code", HFILL
+                       }
+               },
+               { &hf_wsp_header_warning_agent,
+                       {       "Warning Agent",
+                               "wsp.header.warning.agent",
+                                FT_STRING, BASE_NONE, NULL, 0x00,
+                               "Warning Agent", HFILL
+                       }
+               },
+               { &hf_wsp_header_warning_text,
+                       {       "Warning Text",
+                               "wsp.header.warning.text",
+                                FT_STRING, BASE_NONE, NULL, 0x00,
+                               "Warning Text", HFILL
                        }
                },
                { &hf_wsp_header_application_header,
                        {       "Application Header",           
                                "wsp.header.application_header",
                                 FT_STRING, BASE_NONE, NULL, 0x00,
-                               "Application Header" 
+                               "Application Header", HFILL
                        }
                },
                { &hf_wsp_header_application_value,
                        {       "Application Header Value",           
                                "wsp.header.application_header.value",
                                 FT_STRING, BASE_NONE, NULL, 0x00,
-                               "Application Header Value" 
+                               "Application Header Value", HFILL
+                       }
+               },
+               { &hf_wsp_header_content_ID,
+                       {       "Content-ID",           
+                               "wsp.header.content-id",
+                                FT_STRING, BASE_NONE, NULL, 0x00,
+                               "Content-ID", HFILL
                        }
                },
                { &hf_wsp_header_x_wap_tod,
                        {       "X-WAP.TOD",           
                                "wsp.header.x_wap_tod",
                                 FT_ABSOLUTE_TIME, BASE_NONE, NULL, 0x0,
-                               "X-WAP.TOD" 
+                               "X-WAP.TOD", HFILL
+                       }
+               },
+               { &hf_wsp_capabilities_client_SDU,
+                       {       "Client SDU",
+                               "wsp.capabilities.client_SDU",
+                                FT_UINT8, BASE_DEC, NULL, 0x00,
+                               "Client SDU", HFILL
+                       }
+               },
+               { &hf_wsp_capabilities_server_SDU,
+                       {       "Server SDU",
+                               "wsp.capabilities.server_SDU",
+                                FT_UINT8, BASE_DEC, NULL, 0x00,
+                               "Server SDU", HFILL
+                       }
+               },
+               { &hf_wsp_capabilities_protocol_opt,
+                       {       "Protocol Options",
+                               "wsp.capabilities.protocol_opt",
+                                FT_STRING, BASE_HEX, NULL, 0x00,
+                               "Protocol Options", HFILL
+                       }
+               },
+               { &hf_wsp_capabilities_method_MOR,
+                       {       "Method MOR",
+                               "wsp.capabilities.method_mor",
+                                FT_UINT8, BASE_DEC, NULL, 0x00,
+                               "Method MOR", HFILL
+                       }
+               },
+               { &hf_wsp_capabilities_push_MOR,
+                       {       "Push MOR",
+                               "wsp.capabilities.push_mor",
+                                FT_UINT8, BASE_DEC, NULL, 0x00,
+                               "Push MOR", HFILL
+                       }
+               },
+               { &hf_wsp_capabilities_extended_methods,
+                       {       "Extended Methods",
+                               "wsp.capabilities.extend_methods",
+                                FT_STRING, BASE_HEX, NULL, 0x00,
+                               "Extended Methods", HFILL
+                       }
+               },
+               { &hf_wsp_capabilities_header_code_pages,
+                       {       "Header Code Pages",
+                               "wsp.capabilities.code_pages",
+                                FT_STRING, BASE_HEX, NULL, 0x00,
+                               "Header Code Pages", HFILL
+                       }
+               },
+               { &hf_wsp_capabilities_aliases,
+                       {       "Aliases",
+                               "wsp.capabilities.aliases",
+                                FT_UINT8, BASE_HEX, NULL, 0x00,
+                               "Aliases", HFILL
                        }
                },
                { &hf_wsp_post_data,
                        {       "Post Data",           
                                "wsp.post.data",
                                 FT_NONE, BASE_NONE, NULL, 0x00,
-                               "Post Data" 
+                               "Post Data", HFILL
+                       }
+               },
+               { &hf_wsp_push_data,
+                       {       "Push Data",           
+                               "wsp.push.data",
+                                FT_NONE, BASE_NONE, NULL, 0x00,
+                               "Push Data", HFILL
+                       }
+               },
+               { &hf_wsp_multipart_data,
+                       {       "Data in this part",           
+                               "wsp.multipart.data",
+                                FT_NONE, BASE_NONE, NULL, 0x00,
+                               "The data of 1 MIME-multipart part.", HFILL
+                       }
+               },
+               { &hf_wsp_mpart,
+                       {       "Part",           
+                               "wsp.multipart",
+                                FT_UINT32, BASE_DEC, NULL, 0x00,
+                               "MIME part of multipart data.", HFILL
+                       }
+               },
+               { &hf_wsp_redirect_flags,
+                       {       "Flags",
+                               "wsp.redirect_flags",
+                                FT_UINT8, BASE_HEX, NULL, 0x00,
+                               "Redirect Flags", HFILL
+                       }
+               },
+               { &hf_wsp_redirect_permanent,
+                       {       "Permanent Redirect",
+                               "wsp.redirect_flags.permanent",
+                                FT_BOOLEAN, 8, TFS(&yes_no_truth), PERMANENT_REDIRECT,
+                               "Permanent Redirect", HFILL
+                       }
+               },
+               { &hf_wsp_redirect_reuse_security_session,
+                       {       "Reuse Security Session",
+                               "wsp.redirect_flags.reuse_security_session",
+                                FT_BOOLEAN, 8, TFS(&yes_no_truth), REUSE_SECURITY_SESSION,
+                               "Permanent Redirect", HFILL
+                       }
+               },
+               { &hf_wsp_redirect_afl,
+                       {       "Flags/Length",
+                               "wsp.redirect_afl",
+                                FT_UINT8, BASE_HEX, NULL, 0x00,
+                               "Redirect Address Flags/Length", HFILL
+                       }
+               },
+               { &hf_wsp_redirect_afl_bearer_type_included,
+                       {       "Bearer Type Included",
+                               "wsp.redirect_afl.bearer_type_included",
+                                FT_BOOLEAN, 8, TFS(&yes_no_truth), BEARER_TYPE_INCLUDED,
+                               "Redirect Address bearer type included", HFILL
+                       }
+               },
+               { &hf_wsp_redirect_afl_port_number_included,
+                       {       "Port Number Included",
+                               "wsp.redirect_afl.port_number_included",
+                                FT_BOOLEAN, 8, TFS(&yes_no_truth), PORT_NUMBER_INCLUDED,
+                               "Redirect Address port number included", HFILL
+                       }
+               },
+               { &hf_wsp_redirect_afl_address_len,
+                       {       "Address Len",
+                               "wsp.redirect_afl.address_len",
+                                FT_UINT8, BASE_DEC, NULL, ADDRESS_LEN,
+                               "Redirect Address Length", HFILL
+                       }
+               },
+               { &hf_wsp_redirect_bearer_type,
+                       {       "Bearer Type",
+                               "wsp.redirect_bearer_type",
+                                FT_UINT8, BASE_HEX, VALS(vals_bearer_types), 0x0,
+                               "Redirect Bearer Type", HFILL
+                       }
+               },
+               { &hf_wsp_redirect_port_num,
+                       {       "Port Number",
+                               "wsp.redirect_port_num",
+                                FT_UINT16, BASE_DEC, NULL, 0x0,
+                               "Redirect Port Number", HFILL
+                       }
+               },
+               { &hf_wsp_redirect_ipv4_addr,
+                       {       "IP Address",
+                               "wsp.redirect_ipv4_addr",
+                                FT_IPv4, BASE_NONE, NULL, 0x0,
+                               "Redirect Address (IP)", HFILL
+                       }
+               },
+               { &hf_wsp_redirect_ipv6_addr,
+                       {       "IPv6 Address",
+                               "wsp.redirect_ipv6_addr",
+                                FT_IPv6, BASE_NONE, NULL, 0x0,
+                               "Redirect Address (IPv6)", HFILL
+                       }
+               },
+               { &hf_wsp_redirect_addr,
+                       {       "Address",
+                               "wsp.redirect_addr",
+                                FT_BYTES, BASE_NONE, NULL, 0x0,
+                               "Redirect Address", HFILL
                        }
                },
        };
@@ -1775,17 +4892,25 @@ proto_register_wsp(void)
 /* Setup protocol subtree array */
        static gint *ett[] = {
                &ett_wsp,
+               &ett_content_type_parameters,
                &ett_header,
                &ett_headers,
+               &ett_header_warning,
+               &ett_header_cache_control_parameters,
+               &ett_header_cache_control_field_names,
                &ett_capabilities,
                &ett_content_type,
+               &ett_redirect_flags,
+               &ett_redirect_afl,
+               &ett_multiparts,
+               &ett_mpartlist
        };
 
 /* Register the protocol name and description */
        proto_wsp = proto_register_protocol(
                "Wireless Session Protocol",    /* protocol name for use by ethereal */ 
                "WSP",                          /* short version of name */
-               "wap-wsp"                       /* Abbreviated protocol name, should Match IANA 
+               "wap-wsp"                       /* Abbreviated protocol name, should Match IANA 
                                                    < URL:http://www.isi.edu/in-notes/iana/assignments/port-numbers/ >
                                                  */
        );
@@ -1794,19 +4919,32 @@ proto_register_wsp(void)
        proto_register_field_array(proto_wsp, hf, array_length(hf));
        proto_register_subtree_array(ett, array_length(ett));
 
-       register_dissector("wsp", dissect_wsp, proto_wsp);
+       register_dissector("wsp-co", dissect_wsp_fromwap_co, proto_wsp);
+       register_dissector("wsp-cl", dissect_wsp_fromwap_cl, proto_wsp);
+       wsp_dissector_table = register_dissector_table("wsp.content_type.type",
+           "WSP content type", FT_UINT8, BASE_HEX);
+       register_heur_dissector_list("wsp", &heur_subdissector_list);
+
+       wsp_fromudp_handle = create_dissector_handle(dissect_wsp_fromudp,
+           proto_wsp);
 };
 
 void
 proto_reg_handoff_wsp(void)
 {
        /*
-        * Get a handle for the WMLC dissector
+        * Get a handle for the WMLC dissector.
         */
        wmlc_handle = find_dissector("wmlc");   /* Coming soon :) */
 
+       /*
+        * And get a handle for the WTP-over-UDP dissector.
+        */
+       wtp_fromudp_handle = find_dissector("wtp-udp");
+
        /* Only connection-less WSP has no previous handler */
-       dissector_add("udp.port", UDP_PORT_WSP, dissect_wsp, proto_wsp);
+       dissector_add("udp.port", UDP_PORT_WSP, wsp_fromudp_handle);
+       dissector_add("udp.port", UDP_PORT_WSP_PUSH, wsp_fromudp_handle);
 
        /* This dissector is also called from the WTP and WTLS dissectors */
 }