Implement ticket #982: Support for SIP Message Summary/Message Waiting Indication (MWI, RFC 3842)
 - PJSIP-SIMPLE:
    - implement MWI
 - PJSUA-LIB:
    - added "mwi_enabled" flag in account config
    - added "on_mwi_info" callback
 - pjsua app:
    - added "--mwi" option to enable MWI on account
    - added simple callback to log the NOTIFY message
 - other:
     - added SIPp scenario files to simulate UAS side
 - build:
     - added MWI support on VS6, VS2005, MMP, and Makefile


git-svn-id: https://svn.pjsip.org/repos/pjproject/trunk@2968 74dad513-b988-da41-8d7b-12977e46ad98
diff --git a/build.symbian/pjsip_simple.mmp b/build.symbian/pjsip_simple.mmp
index a6ddbf2..b0d85dc 100644
--- a/build.symbian/pjsip_simple.mmp
+++ b/build.symbian/pjsip_simple.mmp
@@ -37,6 +37,7 @@
 SOURCE	evsub.c
 SOURCE	evsub_msg.c
 SOURCE	iscomposing.c
+SOURCE	mwi.c
 SOURCE	pidf.c
 SOURCE	presence.c
 SOURCE	presence_body.c
diff --git a/pjsip-apps/src/pjsua/pjsua_app.c b/pjsip-apps/src/pjsua/pjsua_app.c
index aa935b5..afca391 100644
--- a/pjsip-apps/src/pjsua/pjsua_app.c
+++ b/pjsip-apps/src/pjsua/pjsua_app.c
@@ -193,6 +193,7 @@
     puts  ("  --username=string   Set authentication username");
     puts  ("  --password=string   Set authentication password");
     puts  ("  --publish           Send presence PUBLISH for this account");
+    puts  ("  --mwi               Subscribe to message summary/waiting indication");
     puts  ("  --use-100rel        Require reliable provisional response (100rel)");
     puts  ("  --use-timer         Require SIP session timers");
     puts  ("  --timer-se=N        Session timers expiration period, in secs (def:1800)");
@@ -483,7 +484,7 @@
 	   OPT_REGISTRAR, OPT_REG_TIMEOUT, OPT_PUBLISH, OPT_ID, OPT_CONTACT,
 	   OPT_BOUND_ADDR, OPT_CONTACT_PARAMS, OPT_CONTACT_URI_PARAMS,
 	   OPT_100REL, OPT_USE_IMS, OPT_REALM, OPT_USERNAME, OPT_PASSWORD,
-	   OPT_NAMESERVER, OPT_STUN_SRV,
+	   OPT_MWI, OPT_NAMESERVER, OPT_STUN_SRV,
 	   OPT_ADD_BUDDY, OPT_OFFER_X_MS_MSG, OPT_NO_PRESENCE,
 	   OPT_AUTO_ANSWER, OPT_AUTO_PLAY, OPT_AUTO_PLAY_HANGUP, OPT_AUTO_LOOP,
 	   OPT_AUTO_CONF, OPT_CLOCK_RATE, OPT_SND_CLOCK_RATE, OPT_STEREO,
@@ -535,6 +536,7 @@
 	{ "registrar",	1, 0, OPT_REGISTRAR},
 	{ "reg-timeout",1, 0, OPT_REG_TIMEOUT},
 	{ "publish",    0, 0, OPT_PUBLISH},
+	{ "mwi",	0, 0, OPT_MWI},
 	{ "use-100rel", 0, 0, OPT_100REL},
 	{ "use-ims",    0, 0, OPT_USE_IMS},
 	{ "id",		1, 0, OPT_ID},
@@ -833,6 +835,10 @@
 	    cur_acc->publish_enabled = PJ_TRUE;
 	    break;
 
+	case OPT_MWI:	/* mwi */
+	    cur_acc->mwi_enabled = PJ_TRUE;
+	    break;
+
 	case OPT_100REL: /** 100rel */
 	    cur_acc->require_100rel = PJ_TRUE;
 	    cfg->cfg.require_100rel = PJ_TRUE;
@@ -1550,6 +1556,13 @@
 	pj_strcat2(result, line);
     }
 
+    /* Publish */
+    if (acc_cfg->publish_enabled)
+	pj_strcat2(result, "--publish\n");
+
+    /* MWI */
+    if (acc_cfg->mwi_enabled)
+	pj_strcat2(result, "--mwi\n");
 }
 
 
@@ -2743,6 +2756,37 @@
 
 
 /*
+ * MWI indication
+ */
+static void on_mwi_info(pjsua_acc_id acc_id, pjsua_mwi_info *mwi_info)
+{
+    pj_str_t body;
+    
+    PJ_LOG(3,(THIS_FILE, "Received MWI for acc %d:", acc_id));
+
+    if (mwi_info->rdata->msg_info.ctype) {
+	const pjsip_ctype_hdr *ctype = mwi_info->rdata->msg_info.ctype;
+
+	PJ_LOG(3,(THIS_FILE, " Content-Type: %.*s/%.*s",
+	          (int)ctype->media.type.slen,
+		  ctype->media.type.ptr,
+		  (int)ctype->media.subtype.slen,
+		  ctype->media.subtype.ptr));
+    }
+
+    if (!mwi_info->rdata->msg_info.msg->body) {
+	PJ_LOG(3,(THIS_FILE, "  no message body"));
+	return;
+    }
+
+    body.ptr = mwi_info->rdata->msg_info.msg->body->data;
+    body.slen = mwi_info->rdata->msg_info.msg->body->len;
+
+    PJ_LOG(3,(THIS_FILE, " Body:\n%.*s", (int)body.slen, body.ptr));
+}
+
+
+/*
  * Print buddy list.
  */
 static void print_buddy_list(void)
@@ -4337,6 +4381,7 @@
     app_config.cfg.cb.on_call_transfer_status = &on_call_transfer_status;
     app_config.cfg.cb.on_call_replaced = &on_call_replaced;
     app_config.cfg.cb.on_nat_detect = &on_nat_detect;
+    app_config.cfg.cb.on_mwi_info = &on_mwi_info;
 
     /* Set sound device latency */
     if (app_config.capture_lat > 0)
diff --git a/pjsip/build/Makefile b/pjsip/build/Makefile
index 9dc959f..c0ecf99 100644
--- a/pjsip/build/Makefile
+++ b/pjsip/build/Makefile
@@ -64,7 +64,7 @@
 export PJSIP_SIMPLE_SRCDIR = ../src/pjsip-simple
 export PJSIP_SIMPLE_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \
 			errno.o evsub.o evsub_msg.o iscomposing.o \
-			pidf.o presence.o presence_body.o publishc.o \
+			mwi.o pidf.o presence.o presence_body.o publishc.o \
 			rpid.o xpidf.o
 export PJSIP_SIMPLE_CFLAGS += $(_CFLAGS)
 
diff --git a/pjsip/build/pjsip_simple.vcproj b/pjsip/build/pjsip_simple.vcproj
index 76d1d07..f2504c8 100644
--- a/pjsip/build/pjsip_simple.vcproj
+++ b/pjsip/build/pjsip_simple.vcproj
Binary files differ
diff --git a/pjsip/include/pjsip-simple/mwi.h b/pjsip/include/pjsip-simple/mwi.h
new file mode 100644
index 0000000..c112837
--- /dev/null
+++ b/pjsip/include/pjsip-simple/mwi.h
@@ -0,0 +1,208 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#ifndef __PJSIP_SIMPLE_MWI_H__
+#define __PJSIP_SIMPLE_MWI_H__
+
+/**
+ * @file mwi.h
+ * @brief SIP Extension for MWI (RFC 3842)
+ */
+#include <pjsip-simple/evsub.h>
+#include <pjsip/sip_msg.h>
+
+
+PJ_BEGIN_DECL
+
+
+/**
+ * @defgroup mwi SIP Message Summary and Message Waiting Indication (RFC 3842)
+ * @ingroup PJSIP_SIMPLE
+ * @brief Support for SIP MWI Extension (RFC 3842)
+ * @{
+ *
+ * This module implements RFC 3842: A Message Summary and Message Waiting
+ * Indication Event Package for the Session Initiation Protocol (SIP).
+ * It uses the SIP Event Notification framework (evsub.h) and extends the 
+ * framework by implementing "message-summary" event package.
+ */
+
+
+/**
+ * Initialize the MWI module and register it as endpoint module and
+ * package to the event subscription module.
+ *
+ * @param endpt		The endpoint instance.
+ * @param mod_evsub	The event subscription module instance.
+ *
+ * @return		PJ_SUCCESS if the module is successfully 
+ *			initialized and registered to both endpoint
+ *			and the event subscription module.
+ */
+PJ_DECL(pj_status_t) pjsip_mwi_init_module(pjsip_endpoint *endpt,
+					   pjsip_module *mod_evsub);
+
+/**
+ * Get the MWI module instance.
+ *
+ * @return		The MWI module instance.
+ */
+PJ_DECL(pjsip_module*) pjsip_mwi_instance(void);
+
+/**
+ * Create MWI client subscription session.
+ *
+ * @param dlg		The underlying dialog to use.
+ * @param user_cb	Pointer to callbacks to receive MWI subscription
+ *			events.
+ * @param options	Option flags. Currently only PJSIP_EVSUB_NO_EVENT_ID
+ *			is recognized.
+ * @param p_evsub	Pointer to receive the MWI subscription
+ *			session.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsip_mwi_create_uac( pjsip_dialog *dlg,
+					   const pjsip_evsub_user *user_cb,
+					   unsigned options,
+					   pjsip_evsub **p_evsub );
+
+/**
+ * Create MWI server subscription session.
+ *
+ * @param dlg		The underlying dialog to use.
+ * @param user_cb	Pointer to callbacks to receive MWI subscription
+ *			events.
+ * @param rdata		The incoming SUBSCRIBE request that creates the event 
+ *			subscription.
+ * @param p_evsub	Pointer to receive the MWI subscription
+ *			session.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsip_mwi_create_uas( pjsip_dialog *dlg,
+					   const pjsip_evsub_user *user_cb,
+					   pjsip_rx_data *rdata,
+					   pjsip_evsub **p_evsub );
+
+/**
+ * Forcefully destroy the MWI subscription. This function should only
+ * be called on special condition, such as when the subscription 
+ * initialization has failed. For other conditions, application MUST terminate
+ * the subscription by sending the appropriate un(SUBSCRIBE) or NOTIFY.
+ *
+ * @param sub		The MWI subscription.
+ * @param notify	Specify whether the state notification callback
+ *			should be called.
+ *
+ * @return		PJ_SUCCESS if subscription session has been destroyed.
+ */
+PJ_DECL(pj_status_t) pjsip_mwi_terminate( pjsip_evsub *sub,
+					  pj_bool_t notify );
+
+/**
+ * Call this function to create request to initiate MWI subscription, to 
+ * refresh subcription, or to request subscription termination.
+ *
+ * @param sub		Client subscription instance.
+ * @param expires	Subscription expiration. If the value is set to zero,
+ *			this will request unsubscription.
+ * @param p_tdata	Pointer to receive the request.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsip_mwi_initiate( pjsip_evsub *sub,
+					 pj_int32_t expires,
+					 pjsip_tx_data **p_tdata);
+
+/**
+ * Accept the incoming subscription request by sending 2xx response to
+ * incoming SUBSCRIBE request.
+ *
+ * @param sub		Server subscription instance.
+ * @param rdata		The incoming subscription request message.
+ * @param st_code	Status code, which MUST be final response.
+ * @param hdr_list	Optional list of headers to be added in the response.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsip_mwi_accept( pjsip_evsub *sub,
+				       pjsip_rx_data *rdata,
+				       int st_code,
+				       const pjsip_hdr *hdr_list );
+
+/**
+ * For notifier, create NOTIFY request to subscriber, and set the state 
+ * of the subscription. 
+ *
+ * @param sub		The server subscription (notifier) instance.
+ * @param state		New state to set.
+ * @param state_str	The state string name, if state contains value other
+ *			than active, pending, or terminated. Otherwise this
+ *			argument is ignored.
+ * @param reason	Specify reason if new state is terminated, otherwise
+ *			put NULL.
+ * @param mime_type	MIME type/content type of the message body.
+ * @param body		Message body to be included in the NOTIFY request.
+ * @param p_tdata	Pointer to receive the request.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsip_mwi_notify( pjsip_evsub *sub,
+				       pjsip_evsub_state state,
+				       const pj_str_t *state_str,
+				       const pj_str_t *reason,
+				       const pjsip_media_type *mime_type,
+				       const pj_str_t *body,
+				       pjsip_tx_data **p_tdata);
+
+/**
+ * Create NOTIFY request containing message body from the last NOITFY
+ * message created.
+ *
+ * @param sub		Server subscription object.
+ * @param p_tdata	Pointer to receive request.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsip_mwi_current_notify( pjsip_evsub *sub,
+					       pjsip_tx_data **p_tdata );
+
+
+/**
+ * Send request message that was previously created with initiate(), notify(),
+ * or current_notify(). Application may also send request created with other
+ * functions, e.g. authentication. But the request MUST be either request
+ * that creates/refresh subscription or NOTIFY request.
+ *
+ * @param sub		The subscription object.
+ * @param tdata		Request message to be sent.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsip_mwi_send_request( pjsip_evsub *sub,
+					     pjsip_tx_data *tdata );
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+
+#endif	/* __PJSIP_SIMPLE_MWI_H__ */
diff --git a/pjsip/include/pjsip/sip_msg.h b/pjsip/include/pjsip/sip_msg.h
index 437f9b2..1ca065f 100644
--- a/pjsip/include/pjsip/sip_msg.h
+++ b/pjsip/include/pjsip/sip_msg.h
@@ -511,6 +511,18 @@
     pj_str_t param;	    /**< Media type parameters (concatenated). */
 } pjsip_media_type;
 
+
+/**
+ * Copy SIP media type to another.
+ *
+ * @param pool	    Pool to duplicate strings.
+ * @param dst	    Destination structure.
+ * @param src	    Source structure.
+ */
+PJ_DECL(void) pjsip_media_type_cp(pj_pool_t *pool,
+				  pjsip_media_type *dst,
+				  const pjsip_media_type *src);
+
 /**
  * @}
  */
diff --git a/pjsip/include/pjsip_simple.h b/pjsip/include/pjsip_simple.h
index 8fda815..87ad120 100644
--- a/pjsip/include/pjsip_simple.h
+++ b/pjsip/include/pjsip_simple.h
@@ -37,6 +37,7 @@
 #include <pjsip-simple/evsub.h>
 #include <pjsip-simple/evsub_msg.h>
 #include <pjsip-simple/iscomposing.h>
+#include <pjsip-simple/mwi.h>
 #include <pjsip-simple/presence.h>
 #include <pjsip-simple/pidf.h>
 #include <pjsip-simple/publish.h>
diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h
index 356cab7..a8bb2f3 100644
--- a/pjsip/include/pjsua-lib/pjsua.h
+++ b/pjsip/include/pjsua-lib/pjsua.h
@@ -379,6 +379,17 @@
 
 
 /**
+ * Structure to be passed on MWI callback.
+ */
+typedef struct pjsua_mwi_info
+{
+    pjsip_evsub	    *evsub;	/**< Event subscription session, for
+				     reference.				*/
+    pjsip_rx_data   *rdata;	/**< The received NOTIFY request.	*/
+} pjsua_mwi_info;
+
+
+/**
  * This structure describes application callback to receive various event
  * notification from PJSUA-API. All of these callbacks are OPTIONAL, 
  * although definitely application would want to implement some of
@@ -821,6 +832,17 @@
 					    const pjsip_uri *target,
 					    const pjsip_event *e);
 
+    /**
+     * This callback is called when a NOTIFY request for message summary / 
+     * message waiting indication is received.
+     *
+     * @param acc_id	The account ID.
+     * @param mwi_info	Structure containing details of the event,
+     *			including the received NOTIFY request in the
+     *			\a rdata field.
+     */
+    void (*on_mwi_info)(pjsua_acc_id acc_id, pjsua_mwi_info *mwi_info);
+
 } pjsua_callback;
 
 
@@ -1860,6 +1882,14 @@
     pj_str_t	    reg_uri;
 
     /**
+     * Enable message summary and message waiting indication subscription
+     * (RFC 3842) for this account.
+     *
+     * Default: no
+     */
+    pj_bool_t	    mwi_enabled;
+
+    /**
      * If this flag is set, the presence information of this account will
      * be PUBLISH-ed to the server where the account belongs.
      *
diff --git a/pjsip/include/pjsua-lib/pjsua_internal.h b/pjsip/include/pjsua-lib/pjsua_internal.h
index 0f77796..c90da12 100644
--- a/pjsip/include/pjsua-lib/pjsua_internal.h
+++ b/pjsip/include/pjsua-lib/pjsua_internal.h
@@ -144,6 +144,8 @@
     pjsip_publishc  *publish_sess;  /**< Client publication session.	*/
     pj_bool_t	     publish_state; /**< Last published online status	*/
 
+    pjsip_evsub	    *mwi_sub;	    /**< MWI client subscription	*/
+    pjsip_dialog    *mwi_dlg;	    /**< Dialog for MWI sub.		*/
 } pjsua_acc;
 
 
@@ -452,6 +454,11 @@
 pj_status_t pjsua_im_init(void);
 
 /**
+ * Start MWI subscription
+ */
+void pjsua_start_mwi(pjsua_acc *acc);
+
+/**
  * Init call subsystem.
  */
 pj_status_t pjsua_call_subsys_init(const pjsua_config *cfg);
diff --git a/pjsip/src/pjsip-simple/mwi.c b/pjsip/src/pjsip-simple/mwi.c
new file mode 100644
index 0000000..9be0a6d
--- /dev/null
+++ b/pjsip/src/pjsip-simple/mwi.c
@@ -0,0 +1,599 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pjsip-simple/mwi.h>
+#include <pjsip-simple/errno.h>
+#include <pjsip-simple/evsub_msg.h>
+#include <pjsip/sip_module.h>
+#include <pjsip/sip_endpoint.h>
+#include <pjsip/sip_dialog.h>
+#include <pj/assert.h>
+#include <pj/guid.h>
+#include <pj/log.h>
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+
+
+#define THIS_FILE		    "mwi.c"
+#define MWI_DEFAULT_EXPIRES	    3600
+
+ /*
+ * MWI module (mod-mdi)
+ */
+static struct pjsip_module mod_mwi = 
+{
+    NULL, NULL,			    /* prev, next.			*/
+    { "mod-mwi", 7 },		    /* Name.				*/
+    -1,				    /* Id				*/
+    PJSIP_MOD_PRIORITY_DIALOG_USAGE,/* Priority				*/
+    NULL,			    /* load()				*/
+    NULL,			    /* start()				*/
+    NULL,			    /* stop()				*/
+    NULL,			    /* unload()				*/
+    NULL,			    /* on_rx_request()			*/
+    NULL,			    /* on_rx_response()			*/
+    NULL,			    /* on_tx_request.			*/
+    NULL,			    /* on_tx_response()			*/
+    NULL,			    /* on_tsx_state()			*/
+};
+
+
+/*
+ * This structure describe an mwi agent (both client and server)
+ */
+typedef struct pjsip_mwi
+{
+    pjsip_evsub		*sub;		/**< Event subscribtion record.	    */
+    pjsip_dialog	*dlg;		/**< The dialog.		    */
+    pjsip_evsub_user	 user_cb;	/**< The user callback.		    */
+
+    /* These are for server subscriptions */
+    pj_pool_t		*body_pool;	/**< Pool to save message body	    */
+    pjsip_media_type	 mime_type;	/**< MIME type of last msg body	    */
+    pj_str_t		 body;		/**< Last sent message body	    */
+} pjsip_mwi;
+
+
+/*
+ * Forward decl for evsub callbacks.
+ */
+static void mwi_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
+static void mwi_on_evsub_tsx_state( pjsip_evsub *sub, pjsip_transaction *tsx,
+				    pjsip_event *event);
+static void mwi_on_evsub_rx_refresh( pjsip_evsub *sub, 
+				     pjsip_rx_data *rdata,
+				     int *p_st_code,
+				     pj_str_t **p_st_text,
+				     pjsip_hdr *res_hdr,
+				     pjsip_msg_body **p_body);
+static void mwi_on_evsub_rx_notify( pjsip_evsub *sub, 
+				    pjsip_rx_data *rdata,
+				    int *p_st_code,
+				    pj_str_t **p_st_text,
+				    pjsip_hdr *res_hdr,
+				    pjsip_msg_body **p_body);
+static void mwi_on_evsub_client_refresh(pjsip_evsub *sub);
+static void mwi_on_evsub_server_timeout(pjsip_evsub *sub);
+
+
+/*
+ * Event subscription callback for mwi.
+ */
+static pjsip_evsub_user mwi_user = 
+{
+    &mwi_on_evsub_state,
+    &mwi_on_evsub_tsx_state,
+    &mwi_on_evsub_rx_refresh,
+    &mwi_on_evsub_rx_notify,
+    &mwi_on_evsub_client_refresh,
+    &mwi_on_evsub_server_timeout,
+};
+
+
+/*
+ * Some static constants.
+ */
+static const pj_str_t STR_EVENT		 = { "Event", 5 };
+static const pj_str_t STR_MWI		 = { "message-summary", 15 };
+static const pj_str_t STR_APP_SIMPLE_SMS = { "application/simple-message-summary", 34};
+
+/*
+ * Init mwi module.
+ */
+PJ_DEF(pj_status_t) pjsip_mwi_init_module( pjsip_endpoint *endpt,
+					   pjsip_module *mod_evsub)
+{
+    pj_status_t status;
+    pj_str_t accept[1];
+
+    /* Check arguments. */
+    PJ_ASSERT_RETURN(endpt && mod_evsub, PJ_EINVAL);
+
+    /* Must have not been registered */
+    PJ_ASSERT_RETURN(mod_mwi.id == -1, PJ_EINVALIDOP);
+
+    /* Register to endpoint */
+    status = pjsip_endpt_register_module(endpt, &mod_mwi);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    accept[0] = STR_APP_SIMPLE_SMS;
+
+    /* Register event package to event module. */
+    status = pjsip_evsub_register_pkg( &mod_mwi, &STR_MWI, 
+				       MWI_DEFAULT_EXPIRES, 
+				       PJ_ARRAY_SIZE(accept), accept);
+    if (status != PJ_SUCCESS) {
+	pjsip_endpt_unregister_module(endpt, &mod_mwi);
+	return status;
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Get mwi module instance.
+ */
+PJ_DEF(pjsip_module*) pjsip_mwi_instance(void)
+{
+    return &mod_mwi;
+}
+
+
+/*
+ * Create client subscription.
+ */
+PJ_DEF(pj_status_t) pjsip_mwi_create_uac( pjsip_dialog *dlg,
+					  const pjsip_evsub_user *user_cb,
+					  unsigned options,
+					  pjsip_evsub **p_evsub )
+{
+    pj_status_t status;
+    pjsip_mwi *mwi;
+    pjsip_evsub *sub;
+
+    PJ_ASSERT_RETURN(dlg && p_evsub, PJ_EINVAL);
+
+    PJ_UNUSED_ARG(options);
+
+    pjsip_dlg_inc_lock(dlg);
+
+    /* Create event subscription */
+    status = pjsip_evsub_create_uac( dlg,  &mwi_user, &STR_MWI, 
+				     options, &sub);
+    if (status != PJ_SUCCESS)
+	goto on_return;
+
+    /* Create mwi */
+    mwi = PJ_POOL_ZALLOC_T(dlg->pool, pjsip_mwi);
+    mwi->dlg = dlg;
+    mwi->sub = sub;
+    if (user_cb)
+	pj_memcpy(&mwi->user_cb, user_cb, sizeof(pjsip_evsub_user));
+
+    /* Attach to evsub */
+    pjsip_evsub_set_mod_data(sub, mod_mwi.id, mwi);
+
+    *p_evsub = sub;
+
+on_return:
+    pjsip_dlg_dec_lock(dlg);
+    return status;
+}
+
+
+/*
+ * Create server subscription.
+ */
+PJ_DEF(pj_status_t) pjsip_mwi_create_uas( pjsip_dialog *dlg,
+					  const pjsip_evsub_user *user_cb,
+					  pjsip_rx_data *rdata,
+					  pjsip_evsub **p_evsub )
+{
+    pjsip_accept_hdr *accept;
+    pjsip_event_hdr *event;
+    pjsip_evsub *sub;
+    pjsip_mwi *mwi;
+    char obj_name[PJ_MAX_OBJ_NAME];
+    pj_status_t status;
+
+    /* Check arguments */
+    PJ_ASSERT_RETURN(dlg && rdata && p_evsub, PJ_EINVAL);
+
+    /* Must be request message */
+    PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG,
+		     PJSIP_ENOTREQUESTMSG);
+
+    /* Check that request is SUBSCRIBE */
+    PJ_ASSERT_RETURN(pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
+				      &pjsip_subscribe_method)==0,
+		     PJSIP_SIMPLE_ENOTSUBSCRIBE);
+
+    /* Check that Event header contains "mwi" */
+    event = (pjsip_event_hdr*)
+    	    pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_EVENT, NULL);
+    if (!event) {
+	return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST);
+    }
+    if (pj_stricmp(&event->event_type, &STR_MWI) != 0) {
+	return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_EVENT);
+    }
+
+    /* Check that request contains compatible Accept header. */
+    accept = (pjsip_accept_hdr*)
+    	     pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, NULL);
+    if (accept) {
+	unsigned i;
+	for (i=0; i<accept->count; ++i) {
+	    if (pj_stricmp(&accept->values[i], &STR_APP_SIMPLE_SMS)==0) {
+		break;
+	    }
+	}
+
+	if (i==accept->count) {
+	    /* Nothing is acceptable */
+	    return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE);
+	}
+
+    } else {
+	/* No Accept header. 
+	 * Assume client supports "application/simple-message-summary" 
+	*/
+    }
+
+    /* Lock dialog */
+    pjsip_dlg_inc_lock(dlg);
+
+
+    /* Create server subscription */
+    status = pjsip_evsub_create_uas( dlg, &mwi_user, rdata, 0, &sub);
+    if (status != PJ_SUCCESS)
+	goto on_return;
+
+    /* Create server mwi subscription */
+    mwi = PJ_POOL_ZALLOC_T(dlg->pool, pjsip_mwi);
+    mwi->dlg = dlg;
+    mwi->sub = sub;
+    if (user_cb)
+	pj_memcpy(&mwi->user_cb, user_cb, sizeof(pjsip_evsub_user));
+
+    pj_ansi_snprintf(obj_name, PJ_MAX_OBJ_NAME, "mwibd%p", dlg->pool);
+    mwi->body_pool = pj_pool_create(dlg->pool->factory, obj_name, 
+				    512, 512, NULL);
+
+    /* Attach to evsub */
+    pjsip_evsub_set_mod_data(sub, mod_mwi.id, mwi);
+
+    /* Done: */
+    *p_evsub = sub;
+
+on_return:
+    pjsip_dlg_dec_lock(dlg);
+    return status;
+}
+
+
+/*
+ * Forcefully terminate mwi.
+ */
+PJ_DEF(pj_status_t) pjsip_mwi_terminate( pjsip_evsub *sub,
+					 pj_bool_t notify )
+{
+    return pjsip_evsub_terminate(sub, notify);
+}
+
+/*
+ * Create SUBSCRIBE
+ */
+PJ_DEF(pj_status_t) pjsip_mwi_initiate( pjsip_evsub *sub,
+					pj_int32_t expires,
+					pjsip_tx_data **p_tdata)
+{
+    return pjsip_evsub_initiate(sub, &pjsip_subscribe_method, expires, 
+				p_tdata);
+}
+
+
+/*
+ * Accept incoming subscription.
+ */
+PJ_DEF(pj_status_t) pjsip_mwi_accept( pjsip_evsub *sub,
+				      pjsip_rx_data *rdata,
+				      int st_code,
+				      const pjsip_hdr *hdr_list )
+{
+    return pjsip_evsub_accept( sub, rdata, st_code, hdr_list );
+}
+
+/*
+ * Create message body and attach it to the (NOTIFY) request.
+ */
+static pj_status_t mwi_create_msg_body( pjsip_mwi *mwi, 
+					pjsip_tx_data *tdata)
+{
+    pjsip_msg_body *body;
+    pj_str_t dup_text;
+
+    PJ_ASSERT_RETURN(mwi->mime_type.type.slen && mwi->body.slen, PJ_EINVALIDOP);
+    
+    /* Clone the message body and mime type */
+    pj_strdup(tdata->pool, &dup_text, &mwi->body);
+
+    /* Create the message body */
+    body = PJ_POOL_ZALLOC_T(tdata->pool, pjsip_msg_body);
+    pjsip_media_type_cp(tdata->pool, &body->content_type, &mwi->mime_type);
+    body->data = dup_text.ptr;
+    body->len = (unsigned)dup_text.slen;
+    body->print_body = &pjsip_print_text_body;
+    body->clone_data = &pjsip_clone_text_data;
+
+    /* Attach to tdata */
+    tdata->msg->body = body;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Create NOTIFY
+ */
+PJ_DEF(pj_status_t) pjsip_mwi_notify(  pjsip_evsub *sub,
+				       pjsip_evsub_state state,
+				       const pj_str_t *state_str,
+				       const pj_str_t *reason,
+				       const pjsip_media_type *mime_type,
+				       const pj_str_t *body,
+				       pjsip_tx_data **p_tdata)
+{
+    pjsip_mwi *mwi;
+    pjsip_tx_data *tdata;
+    pj_status_t status;
+    
+    /* Check arguments. */
+    PJ_ASSERT_RETURN(sub && mime_type && body && p_tdata, PJ_EINVAL);
+
+    /* Get the mwi object. */
+    mwi = (pjsip_mwi*) pjsip_evsub_get_mod_data(sub, mod_mwi.id);
+    PJ_ASSERT_RETURN(mwi != NULL, PJ_EINVALIDOP);
+
+    /* Lock object. */
+    pjsip_dlg_inc_lock(mwi->dlg);
+
+    /* Create the NOTIFY request. */
+    status = pjsip_evsub_notify( sub, state, state_str, reason, &tdata);
+    if (status != PJ_SUCCESS)
+	goto on_return;
+
+    /* Update the cached message body */
+    if (mime_type || body)
+	pj_pool_reset(mwi->body_pool);
+    if (mime_type)
+	pjsip_media_type_cp(mwi->body_pool, &mwi->mime_type, mime_type);
+    if (body)
+	pj_strdup(mwi->body_pool, &mwi->body, body);
+
+    /* Create message body */
+    status = mwi_create_msg_body( mwi, tdata );
+    if (status != PJ_SUCCESS)
+	goto on_return;
+
+    /* Done. */
+    *p_tdata = tdata;
+
+on_return:
+    pjsip_dlg_dec_lock(mwi->dlg);
+    return status;
+}
+
+
+/*
+ * Create NOTIFY that reflect current state.
+ */
+PJ_DEF(pj_status_t) pjsip_mwi_current_notify( pjsip_evsub *sub,
+					      pjsip_tx_data **p_tdata )
+{
+    pjsip_mwi *mwi;
+    pjsip_tx_data *tdata;
+    pj_status_t status;
+    
+    /* Check arguments. */
+    PJ_ASSERT_RETURN(sub && p_tdata, PJ_EINVAL);
+
+    /* Get the mwi object. */
+    mwi = (pjsip_mwi*) pjsip_evsub_get_mod_data(sub, mod_mwi.id);
+    PJ_ASSERT_RETURN(mwi != NULL, PJ_EINVALIDOP);
+
+    /* Lock object. */
+    pjsip_dlg_inc_lock(mwi->dlg);
+
+    /* Create the NOTIFY request. */
+    status = pjsip_evsub_current_notify( sub, &tdata);
+    if (status != PJ_SUCCESS)
+	goto on_return;
+
+
+    /* Create message body to reflect the mwi status. */
+    status = mwi_create_msg_body( mwi, tdata );
+    if (status != PJ_SUCCESS)
+	goto on_return;
+
+    /* Done. */
+    *p_tdata = tdata;
+
+on_return:
+    pjsip_dlg_dec_lock(mwi->dlg);
+    return status;
+}
+
+
+/*
+ * Send request.
+ */
+PJ_DEF(pj_status_t) pjsip_mwi_send_request( pjsip_evsub *sub,
+					     pjsip_tx_data *tdata )
+{
+    return pjsip_evsub_send_request(sub, tdata);
+}
+
+/*
+ * This callback is called by event subscription when subscription
+ * state has changed.
+ */
+static void mwi_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
+{
+    pjsip_mwi *mwi;
+
+    mwi = (pjsip_mwi*) pjsip_evsub_get_mod_data(sub, mod_mwi.id);
+    PJ_ASSERT_ON_FAIL(mwi!=NULL, {return;});
+
+    if (mwi->user_cb.on_evsub_state)
+	(*mwi->user_cb.on_evsub_state)(sub, event);
+
+    if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
+	if (mwi->body_pool) {
+	    pj_pool_release(mwi->body_pool);
+	    mwi->body_pool = NULL;
+	}
+    }
+}
+
+/*
+ * Called when transaction state has changed.
+ */
+static void mwi_on_evsub_tsx_state( pjsip_evsub *sub, pjsip_transaction *tsx,
+				     pjsip_event *event)
+{
+    pjsip_mwi *mwi;
+
+    mwi = (pjsip_mwi*) pjsip_evsub_get_mod_data(sub, mod_mwi.id);
+    PJ_ASSERT_ON_FAIL(mwi!=NULL, {return;});
+
+    if (mwi->user_cb.on_tsx_state)
+	(*mwi->user_cb.on_tsx_state)(sub, tsx, event);
+}
+
+
+/*
+ * Called when SUBSCRIBE is received.
+ */
+static void mwi_on_evsub_rx_refresh( pjsip_evsub *sub, 
+				     pjsip_rx_data *rdata,
+				     int *p_st_code,
+				     pj_str_t **p_st_text,
+				     pjsip_hdr *res_hdr,
+				     pjsip_msg_body **p_body)
+{
+    pjsip_mwi *mwi;
+
+    mwi = (pjsip_mwi*) pjsip_evsub_get_mod_data(sub, mod_mwi.id);
+    PJ_ASSERT_ON_FAIL(mwi!=NULL, {return;});
+
+    if (mwi->user_cb.on_rx_refresh) {
+	(*mwi->user_cb.on_rx_refresh)(sub, rdata, p_st_code, p_st_text,
+				       res_hdr, p_body);
+
+    } else {
+	/* Implementors MUST send NOTIFY if it implements on_rx_refresh */
+	pjsip_tx_data *tdata;
+	pj_str_t timeout = { "timeout", 7};
+	pj_status_t status;
+
+	if (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED) {
+	    status = pjsip_mwi_notify( sub, PJSIP_EVSUB_STATE_TERMINATED,
+				       NULL, &timeout, NULL, NULL, &tdata);
+	} else {
+	    status = pjsip_mwi_current_notify(sub, &tdata);
+	}
+
+	if (status == PJ_SUCCESS)
+	    pjsip_mwi_send_request(sub, tdata);
+    }
+}
+
+
+/*
+ * Called when NOTIFY is received.
+ */
+static void mwi_on_evsub_rx_notify( pjsip_evsub *sub, 
+				     pjsip_rx_data *rdata,
+				     int *p_st_code,
+				     pj_str_t **p_st_text,
+				     pjsip_hdr *res_hdr,
+				     pjsip_msg_body **p_body)
+{
+    pjsip_mwi *mwi;
+
+    mwi = (pjsip_mwi*) pjsip_evsub_get_mod_data(sub, mod_mwi.id);
+    PJ_ASSERT_ON_FAIL(mwi!=NULL, {return;});
+
+    /* Just notify application. */
+    if (mwi->user_cb.on_rx_notify) {
+	(*mwi->user_cb.on_rx_notify)(sub, rdata, p_st_code, p_st_text, 
+				     res_hdr, p_body);
+    }
+}
+
+/*
+ * Called when it's time to send SUBSCRIBE.
+ */
+static void mwi_on_evsub_client_refresh(pjsip_evsub *sub)
+{
+    pjsip_mwi *mwi;
+
+    mwi = (pjsip_mwi*) pjsip_evsub_get_mod_data(sub, mod_mwi.id);
+    PJ_ASSERT_ON_FAIL(mwi!=NULL, {return;});
+
+    if (mwi->user_cb.on_client_refresh) {
+	(*mwi->user_cb.on_client_refresh)(sub);
+    } else {
+	pj_status_t status;
+	pjsip_tx_data *tdata;
+
+	status = pjsip_mwi_initiate(sub, -1, &tdata);
+	if (status == PJ_SUCCESS)
+	    pjsip_mwi_send_request(sub, tdata);
+    }
+}
+
+/*
+ * Called when no refresh is received after the interval.
+ */
+static void mwi_on_evsub_server_timeout(pjsip_evsub *sub)
+{
+    pjsip_mwi *mwi;
+
+    mwi = (pjsip_mwi*) pjsip_evsub_get_mod_data(sub, mod_mwi.id);
+    PJ_ASSERT_ON_FAIL(mwi!=NULL, {return;});
+
+    if (mwi->user_cb.on_server_timeout) {
+	(*mwi->user_cb.on_server_timeout)(sub);
+    } else {
+	pj_status_t status;
+	pjsip_tx_data *tdata;
+	pj_str_t reason = { "timeout", 7 };
+
+	status = pjsip_mwi_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
+				   NULL, &reason, NULL, NULL, &tdata);
+	if (status == PJ_SUCCESS)
+	    pjsip_mwi_send_request(sub, tdata);
+    }
+}
+
diff --git a/pjsip/src/pjsip/sip_msg.c b/pjsip/src/pjsip/sip_msg.c
index 6e37a27..fe86362 100644
--- a/pjsip/src/pjsip/sip_msg.c
+++ b/pjsip/src/pjsip/sip_msg.c
@@ -600,6 +600,20 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 /*
+ * Media type
+ */
+PJ_DEF(void) pjsip_media_type_cp( pj_pool_t *pool,
+				  pjsip_media_type *dst,
+				  const pjsip_media_type *src)
+{
+    PJ_ASSERT_ON_FAIL(pool && dst && src, return);
+    pj_strdup(pool, &dst->type,    &src->type);
+    pj_strdup(pool, &dst->subtype, &src->subtype);
+    pj_strdup(pool, &dst->param,   &src->param);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
  * Generic pjsip_hdr_names/hvalue header.
  */
 
diff --git a/pjsip/src/pjsua-lib/pjsua_acc.c b/pjsip/src/pjsua-lib/pjsua_acc.c
index 4bfe98c..26ff39a 100644
--- a/pjsip/src/pjsua-lib/pjsua_acc.c
+++ b/pjsip/src/pjsua-lib/pjsua_acc.c
@@ -328,7 +328,11 @@
     /* If accounts has registration enabled, start registration */
     if (pjsua_var.acc[id].cfg.reg_uri.slen)
 	pjsua_acc_set_registration(id, PJ_TRUE);
-
+    else {
+	/* Otherwise subscribe to MWI, if it's enabled */
+	if (pjsua_var.acc[id].cfg.mwi_enabled)
+	    pjsua_start_mwi(&pjsua_var.acc[id]);
+    }
 
     return PJ_SUCCESS;
 }
@@ -1056,6 +1060,10 @@
 	    /* Send initial PUBLISH if it is enabled */
 	    if (acc->cfg.publish_enabled && acc->publish_sess==NULL)
 		pjsua_pres_init_publish_acc(acc->index);
+
+	    /* Subscribe to MWI, if it's enabled */
+	    if (acc->cfg.mwi_enabled)
+		pjsua_start_mwi(acc);
 	}
 
     } else {
diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c
index 0e39076..6e41cbe 100644
--- a/pjsip/src/pjsua-lib/pjsua_core.c
+++ b/pjsip/src/pjsua-lib/pjsua_core.c
@@ -820,6 +820,9 @@
     status = pjsip_pres_init_module( pjsua_var.endpt, pjsip_evsub_instance());
     PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
 
+    /* Initialize MWI support */
+    status = pjsip_mwi_init_module(pjsua_var.endpt, pjsip_evsub_instance());
+
     /* Init PUBLISH module */
     pjsip_publishc_init_module(pjsua_var.endpt);
 
diff --git a/pjsip/src/pjsua-lib/pjsua_pres.c b/pjsip/src/pjsua-lib/pjsua_pres.c
index e786637..8033729 100644
--- a/pjsip/src/pjsua-lib/pjsua_pres.c
+++ b/pjsip/src/pjsua-lib/pjsua_pres.c
@@ -1853,6 +1853,230 @@
     return PJ_SUCCESS;
 }
 
+/***************************************************************************
+ * MWI
+ */
+/* Callback called when *client* subscription state has changed. */
+static void mwi_evsub_on_state( pjsip_evsub *sub, pjsip_event *event)
+{
+    pjsua_acc *acc;
+
+    PJ_UNUSED_ARG(event);
+
+    /* Note: #937: no need to acuire PJSUA_LOCK here. Since the buddy has
+     *   a dialog attached to it, lock_buddy() will use the dialog
+     *   lock, which we are currently holding!
+     */
+    acc = (pjsua_acc*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
+    if (!acc)
+	return;
+
+    PJ_LOG(4,(THIS_FILE, 
+	      "MWI subscription for %.*s is %s",
+	      (int)acc->cfg.id.slen, acc->cfg.id.ptr, 
+	      pjsip_evsub_get_state_name(sub)));
+
+    if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
+	/* Clear subscription */
+	acc->mwi_dlg = NULL;
+	acc->mwi_sub = NULL;
+	pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
+
+    }
+}
+
+/* Callback called when we receive NOTIFY */
+static void mwi_evsub_on_rx_notify(pjsip_evsub *sub, 
+				   pjsip_rx_data *rdata,
+				   int *p_st_code,
+				   pj_str_t **p_st_text,
+				   pjsip_hdr *res_hdr,
+				   pjsip_msg_body **p_body)
+{
+    pjsua_mwi_info mwi_info;
+    pjsua_acc *acc;
+
+    PJ_UNUSED_ARG(p_st_code);
+    PJ_UNUSED_ARG(p_st_text);
+    PJ_UNUSED_ARG(res_hdr);
+    PJ_UNUSED_ARG(p_body);
+
+    acc = (pjsua_acc*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
+    if (!acc)
+	return;
+
+    /* Construct mwi_info */
+    pj_bzero(&mwi_info, sizeof(mwi_info));
+    mwi_info.evsub = sub;
+    mwi_info.rdata = rdata;
+
+    /* Call callback */
+    if (pjsua_var.ua_cfg.cb.on_mwi_info) {
+	(*pjsua_var.ua_cfg.cb.on_mwi_info)(acc->index, &mwi_info);
+    }
+}
+
+
+/* Event subscription callback. */
+static pjsip_evsub_user mwi_cb = 
+{
+    &mwi_evsub_on_state,  
+    NULL,   /* on_tsx_state: not interested */
+    NULL,   /* on_rx_refresh: don't care about SUBSCRIBE refresh, unless 
+	     * we want to authenticate 
+	     */
+
+    &mwi_evsub_on_rx_notify,
+
+    NULL,   /* on_client_refresh: Use default behaviour, which is to 
+	     * refresh client subscription. */
+
+    NULL,   /* on_server_timeout: Use default behaviour, which is to send 
+	     * NOTIFY to terminate. 
+	     */
+};
+
+void pjsua_start_mwi(pjsua_acc *acc)
+{
+    pj_pool_t *tmp_pool = NULL;
+    pj_str_t contact;
+    pjsip_tx_data *tdata;
+    pj_status_t status;
+
+    if (!acc->cfg.mwi_enabled) {
+	if (acc->mwi_sub) {
+	    /* Terminate MWI subscription */
+	    pjsip_tx_data *tdata;
+	    pjsip_evsub *sub = acc->mwi_sub;
+
+	    /* Detach sub from this account */
+	    acc->mwi_sub = NULL;
+	    acc->mwi_dlg = NULL;
+	    pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
+
+	    /* Unsubscribe */
+	    status = pjsip_mwi_initiate(acc->mwi_sub, 0, &tdata);
+	    if (status == PJ_SUCCESS) {
+		status = pjsip_mwi_send_request(acc->mwi_sub, tdata);
+	    }
+	}
+	return;
+    }
+
+    if (acc->mwi_sub) {
+	/* Subscription is already active */
+	return;
+
+    }
+
+    /* Generate suitable Contact header unless one is already set in 
+     * the account
+     */
+    if (acc->contact.slen) {
+	contact = acc->contact;
+    } else {
+	tmp_pool = pjsua_pool_create("tmpmwi", 512, 256);
+	status = pjsua_acc_create_uac_contact(tmp_pool, &contact,
+					      acc->index, &acc->cfg.id);
+	if (status != PJ_SUCCESS) {
+	    pjsua_perror(THIS_FILE, "Unable to generate Contact header", 
+		         status);
+	    pj_pool_release(tmp_pool);
+	    return;
+	}
+    }
+
+    /* Create UAC dialog */
+    status = pjsip_dlg_create_uac( pjsip_ua_instance(),
+				   &acc->cfg.id,
+				   &contact,
+				   &acc->cfg.id,
+				   NULL, &acc->mwi_dlg);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Unable to create dialog", status);
+	if (tmp_pool) pj_pool_release(tmp_pool);
+	return;
+    }
+
+    /* Increment the dialog's lock otherwise when presence session creation
+     * fails the dialog will be destroyed prematurely.
+     */
+    pjsip_dlg_inc_lock(acc->mwi_dlg);
+
+    /* Create UAC subscription */
+    status = pjsip_mwi_create_uac(acc->mwi_dlg, &mwi_cb, 
+				  PJSIP_EVSUB_NO_EVENT_ID, &acc->mwi_sub);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Error creating MWI subscription", status);
+	if (tmp_pool) pj_pool_release(tmp_pool);
+	pjsip_dlg_dec_lock(acc->mwi_dlg);
+	return;
+    }
+
+    /* If account is locked to specific transport, then lock dialog
+     * to this transport too.
+     */
+    if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
+	pjsip_tpselector tp_sel;
+
+	pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
+	pjsip_dlg_set_transport(acc->mwi_dlg, &tp_sel);
+    }
+
+    /* Set route-set */
+    if (!pj_list_empty(&acc->route_set)) {
+	pjsip_dlg_set_route_set(acc->mwi_dlg, &acc->route_set);
+    }
+
+    /* Set credentials */
+    if (acc->cred_cnt) {
+	pjsip_auth_clt_set_credentials( &acc->mwi_dlg->auth_sess, 
+					acc->cred_cnt, acc->cred);
+    }
+
+    /* Set authentication preference */
+    pjsip_auth_clt_set_prefs(&acc->mwi_dlg->auth_sess, &acc->cfg.auth_pref);
+
+    pjsip_evsub_set_mod_data(acc->mwi_sub, pjsua_var.mod.id, acc);
+
+    status = pjsip_mwi_initiate(acc->mwi_sub, -1, &tdata);
+    if (status != PJ_SUCCESS) {
+	pjsip_dlg_dec_lock(acc->mwi_dlg);
+	if (acc->mwi_sub) {
+	    pjsip_pres_terminate(acc->mwi_sub, PJ_FALSE);
+	}
+	acc->mwi_sub = NULL;
+	acc->mwi_dlg = NULL;
+	pjsua_perror(THIS_FILE, "Unable to create initial MWI SUBSCRIBE", 
+		     status);
+	if (tmp_pool) pj_pool_release(tmp_pool);
+	return;
+    }
+
+    pjsua_process_msg_data(tdata, NULL);
+
+    status = pjsip_pres_send_request(acc->mwi_sub, tdata);
+    if (status != PJ_SUCCESS) {
+	pjsip_dlg_dec_lock(acc->mwi_dlg);
+	if (acc->mwi_sub) {
+	    pjsip_pres_terminate(acc->mwi_sub, PJ_FALSE);
+	}
+	acc->mwi_sub = NULL;
+	acc->mwi_dlg = NULL;
+	pjsua_perror(THIS_FILE, "Unable to send initial MWI SUBSCRIBE", 
+		     status);
+	if (tmp_pool) pj_pool_release(tmp_pool);
+	return;
+    }
+
+    pjsip_dlg_dec_lock(acc->mwi_dlg);
+    if (tmp_pool) pj_pool_release(tmp_pool);
+
+}
+
+
+/***************************************************************************/
+
 /* Timer callback to re-create client subscription */
 static void pres_timer_cb(pj_timer_heap_t *th,
 			  pj_timer_entry *entry)
@@ -1860,14 +2084,20 @@
     unsigned i;
     pj_time_val delay = { PJSUA_PRES_TIMER, 0 };
 
-    /* Retry failed PUBLISH requests */
+    entry->id = PJ_FALSE;
+
+    /* Retry failed PUBLISH and MWI SUBSCRIBE requests */
     for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
 	pjsua_acc *acc = &pjsua_var.acc[i];
+
+	/* Retry PUBLISH */
 	if (acc->cfg.publish_enabled && acc->publish_sess==NULL)
 	    pjsua_pres_init_publish_acc(acc->index);
-    }
 
-    entry->id = PJ_FALSE;
+	/* Re-subscribe MWI subscription if it's terminated prematurely */
+	if (acc->cfg.mwi_enabled && !acc->mwi_sub)
+	    pjsua_start_mwi(acc);
+    }
 
     /* #937: No need to do bulk client refresh, as buddies have their
      *       own individual timer now.
diff --git a/tests/pjsua/scripts-sipp/uas-mwi-0.xml b/tests/pjsua/scripts-sipp/uas-mwi-0.xml
new file mode 100644
index 0000000..58f2bd4
--- /dev/null
+++ b/tests/pjsua/scripts-sipp/uas-mwi-0.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>

+<!DOCTYPE scenario SYSTEM "sipp.dtd">

+

+<!-- This program is free software; you can redistribute it and/or      -->

+<!-- modify it under the terms of the GNU General Public License as     -->

+<!-- published by the Free Software Foundation; either version 2 of the -->

+<!-- License, or (at your option) any later version.                    -->

+<!--                                                                    -->

+<!-- This program is distributed in the hope that it will be useful,    -->

+<!-- but WITHOUT ANY WARRANTY; without even the implied warranty of     -->

+<!-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the      -->

+<!-- GNU General Public License for more details.                       -->

+<!--                                                                    -->

+<!-- You should have received a copy of the GNU General Public License  -->

+<!-- along with this program; if not, write to the                      -->

+<!-- Free Software Foundation, Inc.,                                    -->

+<!-- 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA             -->

+<!--                                                                    -->

+<!-- Late NOTIFY scenario:                       			-->

+<!--  - UAC sends SUBSCRIBE, we reply with 200				-->

+<!--  - we send NOTIFY, expect 200					-->

+<!--  - UAC sends SUBSCRIBE, we ignore					-->

+<!--  - we send NOTIFY							-->

+<!-- See http://trac.pjsip.org/repos/ticket/911                         -->

+<!--                                                                    -->

+

+<scenario name="MWI server with immediate final notify">

+  <recv request="SUBSCRIBE" crlf="true">

+    <action>

+	<ereg regexp=".*" search_in="hdr" header="From" assign_to="3"/>

+	<ereg regexp="sip:(.*)>" search_in="hdr" header="Contact" assign_to="4,5"/>

+        <assign assign_to="4" variable="5" />

+    </action>

+  </recv>

+

+

+  <send>

+    <![CDATA[

+

+      SIP/2.0 200 OK

+      [last_Via:]

+      [last_From:]

+      [last_To:];tag=[call_number]

+      [last_Call-ID:]

+      [last_CSeq:]

+      Contact: <sip:sipp@[local_ip]:[local_port]>

+      Content-Length: 0

+      Expires: 600

+    ]]>

+  </send>

+

+  <!-- initial notify is final notify -->

+  <send retrans="500">

+    <![CDATA[

+      NOTIFY sip:[$5] SIP/2.0

+      Via: SIP/2.0/[transport] [local_ip]:[local_port];rport;branch=[branch]

+      From: sipp <sip:sipp@[local_ip]>;tag=[call_number]

+      To[$3]

+      Call-ID: [call_id]

+      Cseq: 1 NOTIFY

+      Contact: sip:sipp@[local_ip]:[local_port]

+      Max-Forwards: 70

+      Event: message-summary

+      Subscription-State: terminated;reason=goinghome

+      Content-Type: application/simple-message-summary

+      Content-Length: [len]

+

+      Messages-Waiting: yes

+      Voice-Message: 4/8 (1/2)

+

+    ]]>

+  </send>

+

+   <recv response="200">

+   </recv>

+

+  <pause milliseconds="5000"/> 

+

+  <!-- definition of the response time repartition table (unit is ms)   -->

+  <ResponseTimeRepartition value="10, 20, 30, 40, 50, 100, 150, 200"/>

+

+  <!-- definition of the call length repartition table (unit is ms)     -->

+  <CallLengthRepartition value="10, 50, 100, 500, 1000, 5000, 10000"/>

+

+</scenario>

+

diff --git a/tests/pjsua/scripts-sipp/uas-mwi.xml b/tests/pjsua/scripts-sipp/uas-mwi.xml
new file mode 100644
index 0000000..00e7037
--- /dev/null
+++ b/tests/pjsua/scripts-sipp/uas-mwi.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>

+<!DOCTYPE scenario SYSTEM "sipp.dtd">

+

+<!-- This program is free software; you can redistribute it and/or      -->

+<!-- modify it under the terms of the GNU General Public License as     -->

+<!-- published by the Free Software Foundation; either version 2 of the -->

+<!-- License, or (at your option) any later version.                    -->

+<!--                                                                    -->

+<!-- This program is distributed in the hope that it will be useful,    -->

+<!-- but WITHOUT ANY WARRANTY; without even the implied warranty of     -->

+<!-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the      -->

+<!-- GNU General Public License for more details.                       -->

+<!--                                                                    -->

+<!-- You should have received a copy of the GNU General Public License  -->

+<!-- along with this program; if not, write to the                      -->

+<!-- Free Software Foundation, Inc.,                                    -->

+<!-- 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA             -->

+<!--                                                                    -->

+<!-- Late NOTIFY scenario:                       			-->

+<!--  - UAC sends SUBSCRIBE, we reply with 200				-->

+<!--  - we send NOTIFY, expect 200					-->

+<!--  - UAC sends SUBSCRIBE, we ignore					-->

+<!--  - we send NOTIFY							-->

+<!-- See http://trac.pjsip.org/repos/ticket/911                         -->

+<!--                                                                    -->

+

+<scenario name="MWI server">

+  <recv request="SUBSCRIBE" crlf="true">

+    <action>

+	<ereg regexp=".*" search_in="hdr" header="From" assign_to="3"/>

+	<ereg regexp="sip:(.*)>" search_in="hdr" header="Contact" assign_to="4,5"/>

+        <assign assign_to="4" variable="5" />

+    </action>

+  </recv>

+

+

+  <send>

+    <![CDATA[

+

+      SIP/2.0 200 OK

+      [last_Via:]

+      [last_From:]

+      [last_To:];tag=[call_number]

+      [last_Call-ID:]

+      [last_CSeq:]

+      Contact: <sip:sipp@[local_ip]:[local_port]>

+      Content-Length: 0

+      Expires: 600

+    ]]>

+  </send>

+

+  <!-- initial notify -->

+  <send retrans="500">

+    <![CDATA[

+      NOTIFY sip:[$5] SIP/2.0

+      Via: SIP/2.0/[transport] [local_ip]:[local_port];rport;branch=[branch]

+      From: sipp <sip:sipp@[local_ip]>;tag=[call_number]

+      To[$3]

+      Call-ID: [call_id]

+      Cseq: 1 NOTIFY

+      Contact: sip:sipp@[local_ip]:[local_port]

+      Max-Forwards: 70

+      Event: message-summary

+      Subscription-State: active;expires=50

+      Content-Type: application/simple-message-summary

+      Content-Length: [len]

+

+      Messages-Waiting: yes

+      Voice-Message: 4/8 (1/2)

+

+    ]]>

+  </send>

+

+   <recv response="200">

+   </recv>

+

+  <pause milliseconds="10000"/> 

+

+

+  <!-- terminate subscription -->

+  <send retrans="500">

+    <![CDATA[

+      NOTIFY sip:[$5] SIP/2.0

+      Via: SIP/2.0/[transport] [local_ip]:[local_port];rport;branch=[branch]

+      From: sipp <sip:sipp@[local_ip]>;tag=[call_number]

+      To[$3]

+      Call-ID: [call_id]

+      Cseq: 2 NOTIFY

+      Contact: sip:sipp@[local_ip]:[local_port]

+      Max-Forwards: 70

+      Event: message-summary

+      Subscription-State: terminated;reason=noresource

+      Content-Type: application/simple-message-summary

+      Content-Length: [len]

+

+      Messages-Waiting: yes

+      Voice-Message: 4/8 (1/2)

+

+    ]]>

+  </send>

+

+   <recv response="200">

+   </recv>

+

+  <!-- definition of the response time repartition table (unit is ms)   -->

+  <ResponseTimeRepartition value="10, 20, 30, 40, 50, 100, 150, 200"/>

+

+  <!-- definition of the call length repartition table (unit is ms)     -->

+  <CallLengthRepartition value="10, 50, 100, 500, 1000, 5000, 10000"/>

+

+</scenario>

+