PJ_DEF(pj_status_t) pjsua_destroy2(unsigned flags)
{
    int i;  /* Must be signed */

    if (pjsua_var.endpt) {
	PJ_LOG(4,(THIS_FILE, "Shutting down, flags=%d...", flags));
    }

    if (pjsua_var.state > PJSUA_STATE_NULL &&
	pjsua_var.state < PJSUA_STATE_CLOSING)
    {
	pjsua_set_state(PJSUA_STATE_CLOSING);
    }

    /* Signal threads to quit: */
    pjsua_stop_worker_threads();
    
    if (pjsua_var.endpt) {
	unsigned max_wait;

	pj_log_push_indent();

	/* Terminate all calls. */
	if ((flags & PJSUA_DESTROY_NO_TX_MSG) == 0) {
	    pjsua_call_hangup_all();
	}

	/* Deinit media channel of all calls (see #1717) */
	for (i=0; i<(int)pjsua_var.ua_cfg.max_calls; ++i) {
	    /* TODO: check if we're not allowed to send to network in the
	     *       "flags", and if so do not do TURN allocation...
	     */
	    pjsua_media_channel_deinit(i);
	}

	/* Set all accounts to offline */
	for (i=0; i<(int)PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
	    if (!pjsua_var.acc[i].valid)
		continue;
	    pjsua_var.acc[i].online_status = PJ_FALSE;
	    pj_bzero(&pjsua_var.acc[i].rpid, sizeof(pjrpid_element));
	}

	/* Terminate all presence subscriptions. */
	pjsua_pres_shutdown(flags);

	/* Wait for sometime until all publish client sessions are done
	 * (ticket #364)
	 */
	/* First stage, get the maximum wait time */
	max_wait = 100;
	for (i=0; i<(int)PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
	    if (!pjsua_var.acc[i].valid)
		continue;
	    if (pjsua_var.acc[i].cfg.unpublish_max_wait_time_msec > max_wait)
		max_wait = pjsua_var.acc[i].cfg.unpublish_max_wait_time_msec;
	}
	
	/* No waiting if RX is disabled */
	if (flags & PJSUA_DESTROY_NO_RX_MSG) {
	    max_wait = 0;
	}

	/* Second stage, wait for unpublications to complete */
	for (i=0; i<(int)(max_wait/50); ++i) {
	    unsigned j;
	    for (j=0; j<PJ_ARRAY_SIZE(pjsua_var.acc); ++j) {
		if (!pjsua_var.acc[j].valid)
		    continue;

		if (pjsua_var.acc[j].publish_sess)
		    break;
	    }
	    if (j != PJ_ARRAY_SIZE(pjsua_var.acc))
		busy_sleep(50);
	    else
		break;
	}

	/* Third stage, forcefully destroy unfinished unpublications */
	for (i=0; i<(int)PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
	    if (pjsua_var.acc[i].publish_sess) {
		pjsip_publishc_destroy(pjsua_var.acc[i].publish_sess);
		pjsua_var.acc[i].publish_sess = NULL;
	    }
	}

	/* Unregister all accounts */
	for (i=0; i<(int)PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
	    if (!pjsua_var.acc[i].valid)
		continue;

	    if (pjsua_var.acc[i].regc && (flags & PJSUA_DESTROY_NO_TX_MSG)==0)
	    {
		pjsua_acc_set_registration(i, PJ_FALSE);
	    }
#if PJ_HAS_SSL_SOCK
	    pj_turn_sock_tls_cfg_wipe_keys(
			      &pjsua_var.acc[i].cfg.turn_cfg.turn_tls_setting);
#endif
	}

	/* Wait until all unregistrations are done (ticket #364) */
	/* First stage, get the maximum wait time */
	max_wait = 100;
	for (i=0; i<(int)PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
	    if (!pjsua_var.acc[i].valid)
		continue;
	    if (pjsua_var.acc[i].cfg.unreg_timeout > max_wait)
		max_wait = pjsua_var.acc[i].cfg.unreg_timeout;
	}
	
	/* No waiting if RX is disabled */
	if (flags & PJSUA_DESTROY_NO_RX_MSG) {
	    max_wait = 0;
	}

	/* Second stage, wait for unregistrations to complete */
	for (i=0; i<(int)(max_wait/50); ++i) {
	    unsigned j;
	    for (j=0; j<PJ_ARRAY_SIZE(pjsua_var.acc); ++j) {
		if (!pjsua_var.acc[j].valid)
		    continue;

		if (pjsua_var.acc[j].regc)
		    break;
	    }
	    if (j != PJ_ARRAY_SIZE(pjsua_var.acc))
		busy_sleep(50);
	    else
		break;
	}
	/* Note variable 'i' is used below */

	/* Wait for some time to allow unregistration and ICE/TURN
	 * transports shutdown to complete: 
	 */
	if (i < 20 && (flags & PJSUA_DESTROY_NO_RX_MSG) == 0) {
	    busy_sleep(1000 - i*50);
	}

	PJ_LOG(4,(THIS_FILE, "Destroying..."));
	
	/* Terminate any pending STUN resolution */
	if (!pj_list_empty(&pjsua_var.stun_res)) {
	    pjsua_stun_resolve *sess = pjsua_var.stun_res.next;
	    while (sess != &pjsua_var.stun_res) {
		pjsua_stun_resolve *next = sess->next;
		destroy_stun_resolve(sess, PJ_TRUE);
		sess = next;
	    }
	}

	/* Destroy media (to shutdown media endpoint, etc) */
	pjsua_media_subsys_destroy(flags);

	/* Must destroy endpoint first before destroying pools in
	 * buddies or accounts, since shutting down transaction layer
	 * may emit events which trigger some buddy or account callbacks
	 * to be called.
	 */
	pjsip_endpt_destroy(pjsua_var.endpt);
	pjsua_var.endpt = NULL;

	/* Destroy pool in the buddy object */
	for (i=0; i<(int)PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
	    if (pjsua_var.buddy[i].pool) {
		pj_pool_release(pjsua_var.buddy[i].pool);
		pjsua_var.buddy[i].pool = NULL;
	    }
	}

	/* Destroy accounts */
	for (i=0; i<(int)PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
	    if (pjsua_var.acc[i].pool) {
		pj_pool_release(pjsua_var.acc[i].pool);
		pjsua_var.acc[i].pool = NULL;
	    }
	}
    }

    /* Destroy mutex */
    if (pjsua_var.mutex) {
	pj_mutex_destroy(pjsua_var.mutex);
	pjsua_var.mutex = NULL;
    }
    
    if (pjsua_var.timer_mutex) {
        pj_mutex_destroy(pjsua_var.timer_mutex);
        pjsua_var.timer_mutex = NULL;
    }

    /* Destroy pools and pool factory. */
    if (pjsua_var.timer_pool) {
	pj_pool_release(pjsua_var.timer_pool);
	pjsua_var.timer_pool = NULL;
    }
    if (pjsua_var.pool) {
	pj_pool_release(pjsua_var.pool);
	pjsua_var.pool = NULL;
	pj_caching_pool_destroy(&pjsua_var.cp);

	pjsua_set_state(PJSUA_STATE_NULL);

	PJ_LOG(4,(THIS_FILE, "PJSUA destroyed..."));

	/* End logging */
	if (pjsua_var.log_file) {
	    pj_file_close(pjsua_var.log_file);
	    pjsua_var.log_file = NULL;
	}

	pj_log_pop_indent();

	/* Shutdown PJLIB */
	pj_shutdown();
    }

    /* Clear pjsua_var */
    pj_bzero(&pjsua_var, sizeof(pjsua_var));

    /* Done. */
    return PJ_SUCCESS;
}