📄 nua_notifier.c
字号:
/* * This file is part of the Sofia-SIP package * * Copyright (C) 2006 Nokia Corporation. * * Contact: Pekka Pessi <pekka.pessi@nokia.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * *//**@CFILE nua_notifier.c * @brief SUBSCRIBE server, NOTIFY client and REFER server * * Simpler event server. See nua_event_server.c for more complex event * server. * * @author Pekka Pessi <Pekka.Pessi@nokia.com> * * @date Created: Wed Mar 8 15:10:08 EET 2006 ppessi */#include "config.h"#include <stddef.h>#include <stdlib.h>#include <string.h>#include <limits.h>#include <assert.h>#include <sofia-sip/string0.h>#include <sofia-sip/sip_protos.h>#include <sofia-sip/sip_extra.h>#include <sofia-sip/sip_status.h>#include <sofia-sip/sip_util.h>#include <sofia-sip/su_uniqueid.h>#include <sofia-sip/su_md5.h>#include <sofia-sip/token64.h>#include "nua_stack.h"/* ---------------------------------------------------------------------- *//* Notifier event usage */struct notifier_usage{ enum nua_substate nu_substate; /**< Subscription state */ sip_time_t nu_expires; /**< Expiration time */ sip_time_t nu_requested; /**< Requested expiration time */#if SU_HAVE_EXPERIMENTAL char *nu_tag; /**< @ETag in last NOTIFY */ unsigned nu_etags:1; /**< Subscriber supports etags */ unsigned nu_appl_etags:1; /**< Application generates etags */ unsigned nu_no_body:1; /**< Suppress body */#endif};static char const *nua_notify_usage_name(nua_dialog_usage_t const *du);static int nua_notify_usage_add(nua_handle_t *nh, nua_dialog_state_t *ds, nua_dialog_usage_t *du);static void nua_notify_usage_remove(nua_handle_t *nh, nua_dialog_state_t *ds, nua_dialog_usage_t *du, nua_client_request_t *cr, nua_server_request_t *sr);static void nua_notify_usage_refresh(nua_handle_t *nh, nua_dialog_state_t *ds, nua_dialog_usage_t *du, sip_time_t now);static int nua_notify_usage_shutdown(nua_handle_t *nh, nua_dialog_state_t *ds, nua_dialog_usage_t *du);static nua_usage_class const nua_notify_usage[1] = { { sizeof (struct notifier_usage), (sizeof nua_notify_usage), nua_notify_usage_add, nua_notify_usage_remove, nua_notify_usage_name, nua_base_usage_update_params, NULL, nua_notify_usage_refresh, nua_notify_usage_shutdown, }};static char const *nua_notify_usage_name(nua_dialog_usage_t const *du){ return "notify";}static int nua_notify_usage_add(nua_handle_t *nh, nua_dialog_state_t *ds, nua_dialog_usage_t *du){ ds->ds_has_events++; ds->ds_has_notifys++; return 0;}static void nua_notify_usage_remove(nua_handle_t *nh, nua_dialog_state_t *ds, nua_dialog_usage_t *du, nua_client_request_t *cr, nua_server_request_t *sr){ ds->ds_has_events--; ds->ds_has_notifys--; }/* ====================================================================== *//* SUBSCRIBE server *//** @NUA_EVENT nua_i_subscribe * * Incoming @b SUBSCRIBE request. * * @b SUBSCRIBE request is used to query SIP event state or establish a SIP * event subscription. * * @param status status code of response sent automatically by stack * @param phrase response phrase sent automatically by stack * @param nh operation handle associated with the incoming request * @param hmagic application context associated with the handle * (NULL when handle is created by the stack) * @param sip SUBSCRIBE request headers * @param tags NUTAG_SUBSTATE() * * Initial SUBSCRIBE requests are dropped with <i>489 Bad Event</i> * response, unless the application has explicitly included the @Event in * the list of allowed events with nua_set_params() tag NUTAG_ALLOW_EVENTS() * (or SIPTAG_ALLOW_EVENTS() or SIPTAG_ALLOW_EVENTS_STR()). * * If the event has been allowed the application * can decide whether to accept the SUBSCRIBE request or reject it. The * nua_response() call responding to a SUBSCRIBE request must have * NUTAG_WITH() (or NUTAG_WITH_THIS()/NUTAG_WITH_SAVED()) tag. * * If the application accepts the SUBSCRIBE request, it must immediately * send an initial NOTIFY establishing the dialog. This is because the * response to the SUBSCRIBE request may be lost by an intermediate proxy * because it had forked the SUBSCRIBE request. * * SUBSCRIBE requests modifying (usually refreshing or terminating) an * existing event subscription are accepted by default and a <i>200 OK</i> * response along with a copy of previously sent NOTIFY is sent * automatically to the subscriber. * * By default, only event subscriptions accepted are those created * implicitly by REFER request. See #nua_i_refer how the application must * handle the REFER requests. * * @par Subscription Lifetime and Terminating Subscriptions * * Accepting the SUBSCRIBE request creates a dialog with a <i>notifier * dialog usage</i> on the handle. The dialog usage is active, until the * subscriber terminates the subscription, it times out or the application * terminates the usage with nua_notify() call containing the tag * NUTAG_SUBSTATE(nua_substate_terminated) or @SubscriptionState header with * state "terminated" and/or expiration time 0. * * When the subscriber terminates the subscription, the application is * notified of an termination by a #nua_i_subscribe event with * NUTAG_SUBSTATE(nua_substate_terminated) tag. When the subscription times * out, nua automatically initiates a NOTIFY transaction. When it is * terminated, the application is sent a #nua_r_notify event with * NUTAG_SUBSTATE(nua_substate_terminated) tag. * * @sa @RFC3265, nua_notify(), NUTAG_SUBSTATE(), @SubscriptionState, * @Event, nua_subscribe(), #nua_r_subscribe, #nua_i_refer, nua_refer() * * @END_NUA_EVENT */static int nua_subscribe_server_init(nua_server_request_t *sr);static int nua_subscribe_server_preprocess(nua_server_request_t *sr);static int nua_subscribe_server_respond(nua_server_request_t*, tagi_t const *);static int nua_subscribe_server_report(nua_server_request_t*, tagi_t const *);nua_server_methods_t const nua_subscribe_server_methods = { SIP_METHOD_SUBSCRIBE, nua_i_subscribe, /* Event */ { 1, /* Create dialog */ 0, /* Initial request */ 1, /* Target refresh request */ 1, /* Add Contact */ }, nua_subscribe_server_init, nua_subscribe_server_preprocess, nua_base_server_params, nua_subscribe_server_respond, nua_subscribe_server_report, };int nua_subscribe_server_init(nua_server_request_t *sr){ nua_handle_t *nh = sr->sr_owner; nua_dialog_state_t *ds = nh->nh_ds; sip_allow_events_t const *allow_events = NH_PGET(nh, allow_events); sip_t const *sip = sr->sr_request.sip; sip_event_t *o = sip->sip_event; char const *event = o ? o->o_type : NULL; if (sr->sr_initial || !nua_dialog_usage_get(ds, nua_notify_usage, o)) { if (event && str0cmp(event, "refer") == 0) /* refer event subscription should be initiated with REFER */ return SR_STATUS1(sr, SIP_403_FORBIDDEN); /* XXX - event is case-sensitive, should use msg_header_find_item() */ if (!event || !msg_header_find_param(allow_events->k_common, event)) return SR_STATUS1(sr, SIP_489_BAD_EVENT); } return 0;}int nua_subscribe_server_preprocess(nua_server_request_t *sr){ nua_handle_t *nh = sr->sr_owner; nua_dialog_state_t *ds = nh->nh_ds; nua_dialog_usage_t *du; struct notifier_usage *nu; sip_t const *sip = sr->sr_request.sip; sip_event_t *o = sip->sip_event; char const *event = o ? o->o_type : NULL; /* Maximum expiration time */ unsigned long expires = sip->sip_expires ? sip->sip_expires->ex_delta : 3600; sip_time_t now = sip_now(); assert(nh && nh->nh_nua->nua_dhandle != nh); du = nua_dialog_usage_get(ds, nua_notify_usage, o); if (du == NULL) { /* Create a new subscription */ du = nua_dialog_usage_add(nh, ds, nua_notify_usage, o); if (du == NULL) return SR_STATUS1(sr, SIP_500_INTERNAL_SERVER_ERROR); } else { /* Refresh existing subscription */ if (str0cmp(event, "refer") == 0) expires = NH_PGET(nh, refer_expires); SR_STATUS1(sr, SIP_200_OK); } nu = nua_dialog_usage_private(du); if (now + expires >= now) nu->nu_requested = now + expires; else nu->nu_requested = SIP_TIME_MAX - 1;#if SU_HAVE_EXPERIMENTAL nu->nu_etags = sip_suppress_body_if_match(sip) || sip_suppress_notify_if_match(sip) || sip_has_feature(sr->sr_request.sip->sip_supported, "etags");#endif sr->sr_usage = du; return sr->sr_status <= 100 ? 0 : sr->sr_status;}/** @internal Respond to a SUBSCRIBE request. * */staticint nua_subscribe_server_respond(nua_server_request_t *sr, tagi_t const *tags){ struct notifier_usage *nu = nua_dialog_usage_private(sr->sr_usage); msg_t *msg = sr->sr_response.msg; sip_t *sip = sr->sr_response.sip; if (200 <= sr->sr_status && sr->sr_status < 300) { sip_expires_t ex[1]; sip_expires_init(ex); if (nu) { sip_time_t now = sip_now(); if (nu->nu_requested) { if (sip->sip_expires) { /* Expires in response can only shorten the expiration time */ if (nu->nu_requested > now + sip->sip_expires->ex_delta) nu->nu_requested = now + sip->sip_expires->ex_delta; } else { unsigned sub_expires = NH_PGET(sr->sr_owner, sub_expires); if (nu->nu_requested > now + sub_expires) nu->nu_requested = now + sub_expires; } if (nu->nu_requested >= now) nu->nu_expires = nu->nu_requested; else nu->nu_expires = now; if (nu->nu_expires <= now) nu->nu_substate = nua_substate_terminated; } if (nu->nu_expires > now) ex->ex_delta = nu->nu_expires - now; } else { /* Always add header Expires: 0 */ } if (!sip->sip_expires || sip->sip_expires->ex_delta > ex->ex_delta) sip_add_dup(msg, sip, (sip_header_t *)ex); } return nua_base_server_respond(sr, tags);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -