added sipevent module + sip dialog fixes
This commit is contained in:
commit
ecdc83ce46
15 changed files with 2044 additions and 22 deletions
2
Makefile
2
Makefile
|
@ -17,7 +17,7 @@ MK := mk/re.mk
|
|||
include $(MK)
|
||||
|
||||
# List of modules
|
||||
MODULES += sip sipreg sipsess
|
||||
MODULES += sip sipevent sipreg sipsess
|
||||
MODULES += uri httpauth
|
||||
MODULES += stun turn ice
|
||||
MODULES += natbd
|
||||
|
|
|
@ -46,6 +46,7 @@ Modules:
|
|||
* sdp unstable Session Description Protocol
|
||||
* sha testing Secure Hash Standard, NIST, FIPS PUB 180-1
|
||||
* sip unstable Core SIP library
|
||||
* sipevent unstable SIP Event framework
|
||||
* sipreg testing SIP register client
|
||||
* sipsess unstable SIP Sessions
|
||||
* stun unstable Session Traversal Utilities for NAT (STUN)
|
||||
|
@ -76,9 +77,11 @@ Features:
|
|||
* RFC 3261 - SIP: Session Initiation Protocol
|
||||
* RFC 3263 - Locating SIP Servers
|
||||
* RFC 3264 - An Offer/Answer Model with SDP
|
||||
* RFC 3265 - SIP-Specific Event Notification
|
||||
* RFC 3327 - SIP Extension Header Field for Registering Non-Adjacent Contacts
|
||||
* RFC 3428 - SIP Extension for Instant Messaging
|
||||
* RFC 3489 - STUN - Simple Traversal of UDP Through NATs
|
||||
* RFC 3515 - The SIP Refer Method
|
||||
* RFC 3550 - RTP: A Transport Protocol for Real-Time Applications
|
||||
* RFC 3551 - RTP Profile for Audio and Video Conferences with Minimal Control
|
||||
* RFC 3555 - MIME Type Registration of RTP Payload Formats
|
||||
|
|
|
@ -36,6 +36,7 @@ extern "C" {
|
|||
#include "re_sdp.h"
|
||||
#include "re_uri.h"
|
||||
#include "re_sip.h"
|
||||
#include "re_sipevent.h"
|
||||
#include "re_sipreg.h"
|
||||
#include "re_sipsess.h"
|
||||
#include "re_stun.h"
|
||||
|
|
|
@ -301,10 +301,16 @@ int sip_dialog_alloc(struct sip_dialog **dlgp,
|
|||
const char *routev[], uint32_t routec);
|
||||
int sip_dialog_accept(struct sip_dialog **dlgp, const struct sip_msg *msg);
|
||||
int sip_dialog_create(struct sip_dialog *dlg, const struct sip_msg *msg);
|
||||
int sip_dialog_fork(struct sip_dialog **dlgp, struct sip_dialog *odlg,
|
||||
const struct sip_msg *msg);
|
||||
int sip_dialog_update(struct sip_dialog *dlg, const struct sip_msg *msg);
|
||||
bool sip_dialog_rseq_valid(struct sip_dialog *dlg, const struct sip_msg *msg);
|
||||
const char *sip_dialog_callid(const struct sip_dialog *dlg);
|
||||
uint32_t sip_dialog_lseq(const struct sip_dialog *dlg);
|
||||
bool sip_dialog_established(const struct sip_dialog *dlg);
|
||||
bool sip_dialog_cmp(const struct sip_dialog *dlg, const struct sip_msg *msg);
|
||||
bool sip_dialog_cmp_half(const struct sip_dialog *dlg,
|
||||
const struct sip_msg *msg);
|
||||
|
||||
|
||||
/* msg */
|
||||
|
|
123
include/re_sipevent.h
Normal file
123
include/re_sipevent.h
Normal file
|
@ -0,0 +1,123 @@
|
|||
/**
|
||||
* @file re_sipevent.h SIP Event Framework
|
||||
*
|
||||
* Copyright (C) 2010 Creytiv.com
|
||||
*/
|
||||
|
||||
|
||||
/* Message Components */
|
||||
|
||||
struct sipevent_event {
|
||||
struct pl event;
|
||||
struct pl params;
|
||||
struct pl id;
|
||||
};
|
||||
|
||||
enum sipevent_subst {
|
||||
SIPEVENT_ACTIVE = 0,
|
||||
SIPEVENT_PENDING,
|
||||
SIPEVENT_TERMINATED,
|
||||
};
|
||||
|
||||
enum sipevent_reason {
|
||||
SIPEVENT_DEACTIVATED = 0,
|
||||
SIPEVENT_PROBATION,
|
||||
SIPEVENT_REJECTED,
|
||||
SIPEVENT_TIMEOUT,
|
||||
SIPEVENT_GIVEUP,
|
||||
SIPEVENT_NORESOURCE,
|
||||
};
|
||||
|
||||
struct sipevent_substate {
|
||||
enum sipevent_subst state;
|
||||
enum sipevent_reason reason;
|
||||
struct pl expires;
|
||||
struct pl retry_after;
|
||||
struct pl params;
|
||||
};
|
||||
|
||||
int sipevent_event_decode(struct sipevent_event *se, const struct pl *pl);
|
||||
int sipevent_substate_decode(struct sipevent_substate *ss,
|
||||
const struct pl *pl);
|
||||
const char *sipevent_substate_name(enum sipevent_subst state);
|
||||
const char *sipevent_reason_name(enum sipevent_reason reason);
|
||||
|
||||
|
||||
/* Listener Socket */
|
||||
|
||||
struct sipevent_sock;
|
||||
|
||||
int sipevent_listen(struct sipevent_sock **sockp, struct sip *sip,
|
||||
uint32_t htsize_not, uint32_t htsize_sub,
|
||||
sip_msg_h *subh, void *arg);
|
||||
|
||||
|
||||
/* Notifier */
|
||||
|
||||
struct sipnot;
|
||||
|
||||
typedef void (sipnot_close_h)(int err, const struct sip_msg *msg, void *arg);
|
||||
|
||||
int sipevent_accept(struct sipnot **notp, struct sipevent_sock *sock,
|
||||
const struct sip_msg *msg, struct sip_dialog *dlg,
|
||||
const struct sipevent_event *event,
|
||||
uint16_t scode, const char *reason, uint32_t expires_min,
|
||||
uint32_t expires_dfl, uint32_t expires_max,
|
||||
const char *cuser, const char *ctype,
|
||||
sip_auth_h *authh, void *aarg, bool aref,
|
||||
sipnot_close_h *closeh, void *arg, const char *fmt, ...);
|
||||
int sipevent_notify(struct sipnot *not, struct mbuf *mb,
|
||||
enum sipevent_subst state, enum sipevent_reason reason,
|
||||
uint32_t retry_after);
|
||||
int sipevent_notifyf(struct sipnot *not, struct mbuf **mbp,
|
||||
enum sipevent_subst state, enum sipevent_reason reason,
|
||||
uint32_t retry_after, const char *fmt, ...);
|
||||
|
||||
|
||||
/* Subscriber */
|
||||
|
||||
struct sipsub;
|
||||
|
||||
typedef int (sipsub_fork_h)(struct sipsub **subp, struct sipsub *osub,
|
||||
const struct sip_msg *msg, void *arg);
|
||||
typedef void (sipsub_notify_h)(struct sip *sip, const struct sip_msg *msg,
|
||||
void *arg);
|
||||
typedef void (sipsub_close_h)(int err, const struct sip_msg *msg,
|
||||
const struct sipevent_substate *substate,
|
||||
void *arg);
|
||||
|
||||
int sipevent_subscribe(struct sipsub **subp, struct sipevent_sock *sock,
|
||||
const char *uri, const char *from_name,
|
||||
const char *from_uri, const char *event, const char *id,
|
||||
uint32_t expires, const char *cuser,
|
||||
const char *routev[], uint32_t routec,
|
||||
sip_auth_h *authh, void *aarg, bool aref,
|
||||
sipsub_fork_h *forkh, sipsub_notify_h *notifyh,
|
||||
sipsub_close_h *closeh, void *arg,
|
||||
const char *fmt, ...);
|
||||
int sipevent_dsubscribe(struct sipsub **subp, struct sipevent_sock *sock,
|
||||
struct sip_dialog *dlg, const char *event,
|
||||
const char *id, uint32_t expires, const char *cuser,
|
||||
sip_auth_h *authh, void *aarg, bool aref,
|
||||
sipsub_notify_h *notifyh, sipsub_close_h *closeh,
|
||||
void *arg, const char *fmt, ...);
|
||||
|
||||
int sipevent_refer(struct sipsub **subp, struct sipevent_sock *sock,
|
||||
const char *uri, const char *from_name,
|
||||
const char *from_uri, const char *cuser,
|
||||
const char *routev[], uint32_t routec,
|
||||
sip_auth_h *authh, void *aarg, bool aref,
|
||||
sipsub_fork_h *forkh, sipsub_notify_h *notifyh,
|
||||
sipsub_close_h *closeh, void *arg,
|
||||
const char *fmt, ...);
|
||||
int sipevent_drefer(struct sipsub **subp, struct sipevent_sock *sock,
|
||||
struct sip_dialog *dlg, const char *cuser,
|
||||
sip_auth_h *authh, void *aarg, bool aref,
|
||||
sipsub_notify_h *notifyh, sipsub_close_h *closeh,
|
||||
void *arg, const char *fmt, ...);
|
||||
|
||||
int sipevent_fork(struct sipsub **subp, struct sipsub *osub,
|
||||
const struct sip_msg *msg,
|
||||
sip_auth_h *authh, void *aarg, bool aref,
|
||||
sipsub_notify_h *notifyh, sipsub_close_h *closeh,
|
||||
void *arg);
|
|
@ -35,6 +35,7 @@ PRJ_EXPORTS
|
|||
..\..\include\re_sdp.h \epoc32\include\re\re_sdp.h
|
||||
..\..\include\re_sha.h \epoc32\include\re\re_sha.h
|
||||
..\..\include\re_sip.h \epoc32\include\re\re_sip.h
|
||||
..\..\include\re_sipevent.h \epoc32\include\re\re_sipevent.h
|
||||
..\..\include\re_sipreg.h \epoc32\include\re\re_sipreg.h
|
||||
..\..\include\re_sipsess.h \epoc32\include\re\re_sipsess.h
|
||||
..\..\include\re_stun.h \epoc32\include\re\re_stun.h
|
||||
|
@ -56,5 +57,6 @@ rebfcp.mmp
|
|||
redns.mmp
|
||||
resdp.mmp
|
||||
resip.mmp
|
||||
resipevent.mmp
|
||||
resipsess.mmp
|
||||
restun.mmp
|
||||
|
|
34
mk/symbian/resipevent.mmp
Normal file
34
mk/symbian/resipevent.mmp
Normal file
|
@ -0,0 +1,34 @@
|
|||
/**
|
||||
* @file resipevent.mmp Symbian makefile for libre SIP Event
|
||||
*
|
||||
* Copyright (C) 2010 Creytiv.com
|
||||
*/
|
||||
TARGET resipevent.lib
|
||||
TARGETTYPE lib
|
||||
TARGETPATH system\libs
|
||||
UID 0x10000fd3 0x20011307
|
||||
|
||||
#ifdef EKA2
|
||||
VENDORID 0
|
||||
CAPABILITY NetworkServices
|
||||
#endif
|
||||
|
||||
SOURCEPATH .
|
||||
SOURCE dll.cpp
|
||||
|
||||
SOURCEPATH ..\..\src\sipevent
|
||||
SOURCE listen.c
|
||||
SOURCE msg.c
|
||||
SOURCE notify.c
|
||||
SOURCE subscribe.c
|
||||
|
||||
USERINCLUDE . ..\..\include
|
||||
SYSTEMINCLUDE \epoc32\include
|
||||
SYSTEMINCLUDE \epoc32\include\libc
|
||||
SYSTEMINCLUDE ..\..\include
|
||||
#ifndef EKA2
|
||||
LIBRARY estlib.lib euser.lib
|
||||
LIBRARY esock.lib insock.lib
|
||||
#endif
|
||||
|
||||
EXPORTUNFROZEN
|
169
src/sip/dialog.c
169
src/sip/dialog.c
|
@ -219,7 +219,6 @@ int sip_dialog_accept(struct sip_dialog **dlgp, const struct sip_msg *msg)
|
|||
|
||||
err |= sip_msg_hdr_apply(msg, true, SIP_HDR_RECORD_ROUTE,
|
||||
record_route_handler, &renc) ? ENOMEM : 0;
|
||||
dlg->cpos = dlg->mb->pos;
|
||||
err |= mbuf_printf(dlg->mb, "To: %r\r\n", &msg->from.val);
|
||||
err |= mbuf_printf(dlg->mb, "From: %r;tag=%016llx\r\n", &msg->to.val,
|
||||
msg->tag);
|
||||
|
@ -251,13 +250,14 @@ int sip_dialog_accept(struct sip_dialog **dlgp, const struct sip_msg *msg)
|
|||
|
||||
int sip_dialog_create(struct sip_dialog *dlg, const struct sip_msg *msg)
|
||||
{
|
||||
char *uri = NULL, *rtag = NULL;
|
||||
const struct sip_hdr *contact;
|
||||
struct route_enc renc;
|
||||
struct sip_addr addr;
|
||||
struct pl pl;
|
||||
int err;
|
||||
|
||||
if (!dlg || dlg->rtag || !msg)
|
||||
if (!dlg || dlg->rtag || !dlg->cpos || !msg)
|
||||
return EINVAL;
|
||||
|
||||
contact = sip_msg_hdr(msg, SIP_HDR_CONTACT);
|
||||
|
@ -268,20 +268,18 @@ int sip_dialog_create(struct sip_dialog *dlg, const struct sip_msg *msg)
|
|||
if (sip_addr_decode(&addr, &contact->val))
|
||||
return EBADMSG;
|
||||
|
||||
dlg->uri = mem_deref(dlg->uri);
|
||||
|
||||
err = pl_strdup(&dlg->uri, &addr.auri);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = pl_strdup(&dlg->rtag, msg->req ? &msg->from.tag : &msg->to.tag);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
renc.mb = mbuf_alloc(512);
|
||||
if (!renc.mb)
|
||||
return ENOMEM;
|
||||
|
||||
err = pl_strdup(&uri, &addr.auri);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = pl_strdup(&rtag, msg->req ? &msg->from.tag : &msg->to.tag);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
renc.end = 0;
|
||||
|
||||
err |= sip_msg_hdr_apply(msg, msg->req, SIP_HDR_RECORD_ROUTE,
|
||||
|
@ -303,6 +301,106 @@ int sip_dialog_create(struct sip_dialog *dlg, const struct sip_msg *msg)
|
|||
pl.p = (const char *)mbuf_buf(renc.mb) + ROUTE_OFFSET;
|
||||
pl.l = renc.end - ROUTE_OFFSET;
|
||||
err = sip_addr_decode(&addr, &pl);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
dlg->route = addr.uri;
|
||||
}
|
||||
else {
|
||||
struct uri tmp;
|
||||
|
||||
pl_set_str(&pl, uri);
|
||||
err = uri_decode(&tmp, &pl);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
dlg->route = tmp;
|
||||
}
|
||||
|
||||
mem_deref(dlg->mb);
|
||||
mem_deref(dlg->uri);
|
||||
|
||||
dlg->mb = mem_ref(renc.mb);
|
||||
dlg->rtag = mem_ref(rtag);
|
||||
dlg->uri = mem_ref(uri);
|
||||
dlg->rseq = msg->req ? msg->cseq.num : 0;
|
||||
dlg->cpos = 0;
|
||||
|
||||
out:
|
||||
mem_deref(renc.mb);
|
||||
mem_deref(rtag);
|
||||
mem_deref(uri);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int sip_dialog_fork(struct sip_dialog **dlgp, struct sip_dialog *odlg,
|
||||
const struct sip_msg *msg)
|
||||
{
|
||||
const struct sip_hdr *contact;
|
||||
struct sip_dialog *dlg;
|
||||
struct route_enc renc;
|
||||
struct sip_addr addr;
|
||||
struct pl pl;
|
||||
int err;
|
||||
|
||||
if (!dlgp || !odlg || !odlg->cpos || !msg)
|
||||
return EINVAL;
|
||||
|
||||
contact = sip_msg_hdr(msg, SIP_HDR_CONTACT);
|
||||
|
||||
if (!contact || !msg->callid.p)
|
||||
return EBADMSG;
|
||||
|
||||
if (sip_addr_decode(&addr, &contact->val))
|
||||
return EBADMSG;
|
||||
|
||||
dlg = mem_zalloc(sizeof(*dlg), destructor);
|
||||
if (!dlg)
|
||||
return ENOMEM;
|
||||
|
||||
dlg->callid = mem_ref(odlg->callid);
|
||||
dlg->ltag = mem_ref(odlg->ltag);
|
||||
dlg->lseq = odlg->lseq;
|
||||
dlg->rseq = msg->req ? msg->cseq.num : 0;
|
||||
|
||||
err = pl_strdup(&dlg->uri, &addr.auri);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = pl_strdup(&dlg->rtag, msg->req ? &msg->from.tag : &msg->to.tag);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
dlg->mb = mbuf_alloc(512);
|
||||
if (!dlg->mb) {
|
||||
err = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
renc.mb = dlg->mb;
|
||||
renc.end = 0;
|
||||
|
||||
err |= sip_msg_hdr_apply(msg, msg->req, SIP_HDR_RECORD_ROUTE,
|
||||
record_route_handler, &renc) ? ENOMEM : 0;
|
||||
err |= mbuf_printf(dlg->mb, "To: %r\r\n",
|
||||
msg->req ? &msg->from.val : &msg->to.val);
|
||||
|
||||
odlg->mb->pos = odlg->cpos;
|
||||
err |= mbuf_write_mem(dlg->mb, mbuf_buf(odlg->mb),
|
||||
mbuf_get_left(odlg->mb));
|
||||
odlg->mb->pos = 0;
|
||||
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
dlg->mb->pos = 0;
|
||||
|
||||
if (renc.end) {
|
||||
pl.p = (const char *)mbuf_buf(dlg->mb) + ROUTE_OFFSET;
|
||||
pl.l = renc.end - ROUTE_OFFSET;
|
||||
err = sip_addr_decode(&addr, &pl);
|
||||
dlg->route = addr.uri;
|
||||
}
|
||||
else {
|
||||
|
@ -310,14 +408,11 @@ int sip_dialog_create(struct sip_dialog *dlg, const struct sip_msg *msg)
|
|||
err = uri_decode(&dlg->route, &pl);
|
||||
}
|
||||
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
mem_deref(dlg->mb);
|
||||
dlg->mb = mem_ref(renc.mb);
|
||||
|
||||
out:
|
||||
mem_deref(renc.mb);
|
||||
if (err)
|
||||
mem_deref(dlg);
|
||||
else
|
||||
*dlgp = dlg;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
@ -327,7 +422,6 @@ int sip_dialog_update(struct sip_dialog *dlg, const struct sip_msg *msg)
|
|||
{
|
||||
const struct sip_hdr *contact;
|
||||
struct sip_addr addr;
|
||||
struct pl pl;
|
||||
char *uri;
|
||||
int err;
|
||||
|
||||
|
@ -347,10 +441,15 @@ int sip_dialog_update(struct sip_dialog *dlg, const struct sip_msg *msg)
|
|||
|
||||
if (dlg->route.scheme.p == dlg->uri) {
|
||||
|
||||
struct uri tmp;
|
||||
struct pl pl;
|
||||
|
||||
pl_set_str(&pl, uri);
|
||||
err = uri_decode(&dlg->route, &pl);
|
||||
err = uri_decode(&tmp, &pl);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
dlg->route = tmp;
|
||||
}
|
||||
|
||||
mem_deref(dlg->uri);
|
||||
|
@ -412,6 +511,18 @@ const char *sip_dialog_callid(const struct sip_dialog *dlg)
|
|||
}
|
||||
|
||||
|
||||
uint32_t sip_dialog_lseq(const struct sip_dialog *dlg)
|
||||
{
|
||||
return dlg ? dlg->lseq : 0;
|
||||
}
|
||||
|
||||
|
||||
bool sip_dialog_established(const struct sip_dialog *dlg)
|
||||
{
|
||||
return dlg && dlg->rtag;
|
||||
}
|
||||
|
||||
|
||||
bool sip_dialog_cmp(const struct sip_dialog *dlg, const struct sip_msg *msg)
|
||||
{
|
||||
if (!dlg || !msg)
|
||||
|
@ -428,3 +539,19 @@ bool sip_dialog_cmp(const struct sip_dialog *dlg, const struct sip_msg *msg)
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool sip_dialog_cmp_half(const struct sip_dialog *dlg,
|
||||
const struct sip_msg *msg)
|
||||
{
|
||||
if (!dlg || !msg)
|
||||
return false;
|
||||
|
||||
if (pl_strcmp(&msg->callid, dlg->callid))
|
||||
return false;
|
||||
|
||||
if (pl_strcmp(msg->req ? &msg->to.tag : &msg->from.tag, dlg->ltag))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
353
src/sipevent/listen.c
Normal file
353
src/sipevent/listen.c
Normal file
|
@ -0,0 +1,353 @@
|
|||
/**
|
||||
* @file listen.c SIP Event Listen
|
||||
*
|
||||
* Copyright (C) 2010 Creytiv.com
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <re_types.h>
|
||||
#include <re_mem.h>
|
||||
#include <re_mbuf.h>
|
||||
#include <re_sa.h>
|
||||
#include <re_list.h>
|
||||
#include <re_hash.h>
|
||||
#include <re_fmt.h>
|
||||
#include <re_uri.h>
|
||||
#include <re_tmr.h>
|
||||
#include <re_sip.h>
|
||||
#include <re_sipevent.h>
|
||||
#include "sipevent.h"
|
||||
|
||||
|
||||
struct subcmp {
|
||||
const struct sipevent_event *evt;
|
||||
const struct sip_msg *msg;
|
||||
};
|
||||
|
||||
|
||||
static void destructor(void *arg)
|
||||
{
|
||||
struct sipevent_sock *sock = arg;
|
||||
|
||||
mem_deref(sock->lsnr);
|
||||
hash_flush(sock->ht_not);
|
||||
hash_flush(sock->ht_sub);
|
||||
mem_deref(sock->ht_not);
|
||||
mem_deref(sock->ht_sub);
|
||||
}
|
||||
|
||||
|
||||
static bool event_cmp(const struct sipevent_event *evt,
|
||||
const char *event, const char *id,
|
||||
int32_t refer_cseq)
|
||||
{
|
||||
if (pl_strcmp(&evt->event, event))
|
||||
return false;
|
||||
|
||||
if (!pl_isset(&evt->id) && !id)
|
||||
return true;
|
||||
|
||||
if (!pl_isset(&evt->id))
|
||||
return false;
|
||||
|
||||
if (!id) {
|
||||
if (refer_cseq >= 0 && (int32_t)pl_u32(&evt->id) == refer_cseq)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pl_strcmp(&evt->id, id))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool not_cmp_handler(struct le *le, void *arg)
|
||||
{
|
||||
const struct subcmp *cmp = arg;
|
||||
struct sipnot *not = le->data;
|
||||
|
||||
return sip_dialog_cmp(not->dlg, cmp->msg) &&
|
||||
event_cmp(cmp->evt, not->event, not->id, -1);
|
||||
}
|
||||
|
||||
|
||||
static bool sub_cmp_handler(struct le *le, void *arg)
|
||||
{
|
||||
const struct subcmp *cmp = arg;
|
||||
struct sipsub *sub = le->data;
|
||||
|
||||
return sip_dialog_cmp(sub->dlg, cmp->msg) &&
|
||||
(!cmp->evt || event_cmp(cmp->evt, sub->event, sub->id,
|
||||
sub->refer_cseq));
|
||||
}
|
||||
|
||||
|
||||
static bool sub_cmp_half_handler(struct le *le, void *arg)
|
||||
{
|
||||
const struct subcmp *cmp = arg;
|
||||
struct sipsub *sub = le->data;
|
||||
|
||||
return sip_dialog_cmp_half(sub->dlg, cmp->msg) &&
|
||||
!sip_dialog_established(sub->dlg) &&
|
||||
(!cmp->evt || event_cmp(cmp->evt, sub->event, sub->id,
|
||||
sub->refer_cseq));
|
||||
}
|
||||
|
||||
|
||||
static struct sipnot *sipnot_find(struct sipevent_sock *sock,
|
||||
const struct sip_msg *msg,
|
||||
const struct sipevent_event *evt)
|
||||
{
|
||||
struct subcmp cmp;
|
||||
|
||||
cmp.msg = msg;
|
||||
cmp.evt = evt;
|
||||
|
||||
return list_ledata(hash_lookup(sock->ht_not,
|
||||
hash_joaat_pl(&msg->callid),
|
||||
not_cmp_handler, &cmp));
|
||||
}
|
||||
|
||||
|
||||
struct sipsub *sipsub_find(struct sipevent_sock *sock,
|
||||
const struct sip_msg *msg,
|
||||
const struct sipevent_event *evt, bool full)
|
||||
{
|
||||
struct subcmp cmp;
|
||||
|
||||
cmp.msg = msg;
|
||||
cmp.evt = evt;
|
||||
|
||||
return list_ledata(hash_lookup(sock->ht_sub,
|
||||
hash_joaat_pl(&msg->callid), full ?
|
||||
sub_cmp_handler : sub_cmp_half_handler,
|
||||
&cmp));
|
||||
}
|
||||
|
||||
|
||||
static void notify_handler(struct sipevent_sock *sock,
|
||||
const struct sip_msg *msg)
|
||||
{
|
||||
struct sipevent_substate state;
|
||||
struct sipevent_event event;
|
||||
struct sip *sip = sock->sip;
|
||||
const struct sip_hdr *hdr;
|
||||
struct sipsub *sub;
|
||||
uint32_t nrefs;
|
||||
int err;
|
||||
|
||||
hdr = sip_msg_hdr(msg, SIP_HDR_EVENT);
|
||||
if (!hdr || sipevent_event_decode(&event, &hdr->val)) {
|
||||
(void)sip_reply(sip, msg, 489, "Bad Event");
|
||||
return;
|
||||
}
|
||||
|
||||
hdr = sip_msg_hdr(msg, SIP_HDR_SUBSCRIPTION_STATE);
|
||||
if (!hdr || sipevent_substate_decode(&state, &hdr->val)) {
|
||||
(void)sip_reply(sip, msg, 400,"Bad Subscription-State Header");
|
||||
return;
|
||||
}
|
||||
|
||||
sub = sipsub_find(sock, msg, &event, true);
|
||||
if (!sub) {
|
||||
sub = sipsub_find(sock, msg, &event, false);
|
||||
if (!sub) {
|
||||
(void)sip_reply(sip, msg,
|
||||
481, "Subscription Does Not Exist");
|
||||
return;
|
||||
}
|
||||
|
||||
if (sub->forkh) {
|
||||
|
||||
struct sipsub *fsub;
|
||||
|
||||
err = sub->forkh(&fsub, sub, msg, sub->arg);
|
||||
if (err) {
|
||||
(void)sip_reply(sip, msg, 500, strerror(err));
|
||||
return;
|
||||
}
|
||||
|
||||
sub = fsub;
|
||||
}
|
||||
else {
|
||||
err = sip_dialog_create(sub->dlg, msg);
|
||||
if (err) {
|
||||
(void)sip_reply(sip, msg, 500, strerror(err));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!sip_dialog_rseq_valid(sub->dlg, msg)) {
|
||||
(void)sip_reply(sip, msg, 500, "Bad Sequence");
|
||||
return;
|
||||
}
|
||||
|
||||
(void)sip_dialog_update(sub->dlg, msg);
|
||||
}
|
||||
|
||||
if (sub->refer_cseq >= 0 && !sub->id && pl_isset(&event.id)) {
|
||||
|
||||
err = pl_strdup(&sub->id, &event.id);
|
||||
if (err) {
|
||||
(void)sip_treply(NULL, sip, msg, 500, strerror(err));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch (state.state) {
|
||||
|
||||
case SIPEVENT_ACTIVE:
|
||||
case SIPEVENT_PENDING:
|
||||
if (!sub->termconf)
|
||||
sub->subscribed = true;
|
||||
|
||||
if (!sub->terminated && !sub->termwait &&
|
||||
pl_isset(&state.expires))
|
||||
sipsub_reschedule(sub, pl_u32(&state.expires) * 900);
|
||||
break;
|
||||
|
||||
case SIPEVENT_TERMINATED:
|
||||
sub->subscribed = false;
|
||||
sub->termconf = true;
|
||||
break;
|
||||
}
|
||||
|
||||
mem_ref(sub);
|
||||
sub->notifyh(sip, msg, sub->arg);
|
||||
nrefs = mem_nrefs(sub);
|
||||
mem_deref(sub);
|
||||
|
||||
/* check if subscription was deref'd from notify handler */
|
||||
if (nrefs == 1)
|
||||
return;
|
||||
|
||||
if (state.state == SIPEVENT_TERMINATED) {
|
||||
|
||||
if (!sub->terminated) {
|
||||
sub->termwait = false;
|
||||
sipsub_terminate(sub, 0, msg, &state);
|
||||
}
|
||||
else if (sub->termwait) {
|
||||
sub->termwait = false;
|
||||
tmr_cancel(&sub->tmr);
|
||||
mem_deref(sub);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void subscribe_handler(struct sipevent_sock *sock,
|
||||
const struct sip_msg *msg)
|
||||
{
|
||||
struct sipevent_event event;
|
||||
struct sip *sip = sock->sip;
|
||||
const struct sip_hdr *hdr;
|
||||
struct sipnot *not;
|
||||
uint32_t expires;
|
||||
|
||||
hdr = sip_msg_hdr(msg, SIP_HDR_EVENT);
|
||||
if (!hdr || sipevent_event_decode(&event, &hdr->val)) {
|
||||
(void)sip_reply(sip, msg, 400, "Bad Event Header");
|
||||
return;
|
||||
}
|
||||
|
||||
not = sipnot_find(sock, msg, &event);
|
||||
if (!not || not->terminated) {
|
||||
(void)sip_reply(sip, msg, 481, "Subscription Does Not Exist");
|
||||
return;
|
||||
}
|
||||
|
||||
if (pl_isset(&msg->expires))
|
||||
expires = pl_u32(&msg->expires);
|
||||
else
|
||||
expires = not->expires_dfl;
|
||||
|
||||
if (expires > 0 && expires < not->expires_min) {
|
||||
(void)sip_replyf(sip, msg, 423, "Interval Too Brief",
|
||||
"Min-Expires: %u\r\n"
|
||||
"Content-Length: 0\r\n"
|
||||
"\r\n",
|
||||
not->expires_min);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!sip_dialog_rseq_valid(not->dlg, msg)) {
|
||||
(void)sip_reply(sip, msg, 500, "Bad Sequence");
|
||||
return;
|
||||
}
|
||||
|
||||
(void)sip_dialog_update(not->dlg, msg);
|
||||
|
||||
sipnot_refresh(not, expires);
|
||||
|
||||
(void)sipnot_reply(not, msg, 200, "OK");
|
||||
|
||||
(void)sipnot_notify(not);
|
||||
}
|
||||
|
||||
|
||||
static bool request_handler(const struct sip_msg *msg, void *arg)
|
||||
{
|
||||
struct sipevent_sock *sock = arg;
|
||||
|
||||
if (!pl_strcmp(&msg->met, "SUBSCRIBE")) {
|
||||
|
||||
if (pl_isset(&msg->to.tag)) {
|
||||
subscribe_handler(sock, msg);
|
||||
return true;
|
||||
}
|
||||
|
||||
return sock->subh ? sock->subh(msg, arg) : false;
|
||||
}
|
||||
else if (!pl_strcmp(&msg->met, "NOTIFY")) {
|
||||
|
||||
notify_handler(sock, msg);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int sipevent_listen(struct sipevent_sock **sockp, struct sip *sip,
|
||||
uint32_t htsize_not, uint32_t htsize_sub,
|
||||
sip_msg_h *subh, void *arg)
|
||||
{
|
||||
struct sipevent_sock *sock;
|
||||
int err;
|
||||
|
||||
if (!sockp || !sip || !htsize_not || !htsize_sub)
|
||||
return EINVAL;
|
||||
|
||||
sock = mem_zalloc(sizeof(*sock), destructor);
|
||||
if (!sock)
|
||||
return ENOMEM;
|
||||
|
||||
err = sip_listen(&sock->lsnr, sip, true, request_handler, sock);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = hash_alloc(&sock->ht_not, htsize_not);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = hash_alloc(&sock->ht_sub, htsize_sub);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
sock->sip = sip;
|
||||
sock->subh = subh;
|
||||
sock->arg = arg;
|
||||
|
||||
out:
|
||||
if (err)
|
||||
mem_deref(sock);
|
||||
else
|
||||
*sockp = sock;
|
||||
|
||||
return err;
|
||||
}
|
10
src/sipevent/mod.mk
Normal file
10
src/sipevent/mod.mk
Normal file
|
@ -0,0 +1,10 @@
|
|||
#
|
||||
# mod.mk
|
||||
#
|
||||
# Copyright (C) 2010 Creytiv.com
|
||||
#
|
||||
|
||||
SRCS += sipevent/listen.c
|
||||
SRCS += sipevent/msg.c
|
||||
SRCS += sipevent/notify.c
|
||||
SRCS += sipevent/subscribe.c
|
119
src/sipevent/msg.c
Normal file
119
src/sipevent/msg.c
Normal file
|
@ -0,0 +1,119 @@
|
|||
/**
|
||||
* @file msg.c SIP event messages
|
||||
*
|
||||
* Copyright (C) 2010 Creytiv.com
|
||||
*/
|
||||
#include <re_types.h>
|
||||
#include <re_fmt.h>
|
||||
#include <re_mbuf.h>
|
||||
#include <re_uri.h>
|
||||
#include <re_list.h>
|
||||
#include <re_sa.h>
|
||||
#include <re_sip.h>
|
||||
#include <re_sipevent.h>
|
||||
|
||||
|
||||
int sipevent_event_decode(struct sipevent_event *se, const struct pl *pl)
|
||||
{
|
||||
struct pl param;
|
||||
int err;
|
||||
|
||||
if (!se || !pl)
|
||||
return EINVAL;
|
||||
|
||||
err = re_regex(pl->p, pl->l, "[^; \t\r\n]+[ \t\r\n]*[^]*",
|
||||
&se->event, NULL, &se->params);
|
||||
if (err)
|
||||
return EBADMSG;
|
||||
|
||||
if (!sip_param_decode(&se->params, "id", ¶m))
|
||||
se->id = param;
|
||||
else
|
||||
se->id = pl_null;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int sipevent_substate_decode(struct sipevent_substate *ss, const struct pl *pl)
|
||||
{
|
||||
struct pl state, param;
|
||||
int err;
|
||||
|
||||
if (!ss || !pl)
|
||||
return EINVAL;
|
||||
|
||||
err = re_regex(pl->p, pl->l, "[a-z]+[ \t\r\n]*[^]*",
|
||||
&state, NULL, &ss->params);
|
||||
if (err)
|
||||
return EBADMSG;
|
||||
|
||||
if (!pl_strcasecmp(&state, "active"))
|
||||
ss->state = SIPEVENT_ACTIVE;
|
||||
else if (!pl_strcasecmp(&state, "pending"))
|
||||
ss->state = SIPEVENT_PENDING;
|
||||
else if (!pl_strcasecmp(&state, "terminated"))
|
||||
ss->state = SIPEVENT_TERMINATED;
|
||||
else
|
||||
ss->state = -1;
|
||||
|
||||
if (!sip_param_decode(&ss->params, "reason", ¶m)) {
|
||||
|
||||
if (!pl_strcasecmp(¶m, "deactivated"))
|
||||
ss->reason = SIPEVENT_DEACTIVATED;
|
||||
else if (!pl_strcasecmp(¶m, "probation"))
|
||||
ss->reason = SIPEVENT_PROBATION;
|
||||
else if (!pl_strcasecmp(¶m, "rejected"))
|
||||
ss->reason = SIPEVENT_REJECTED;
|
||||
else if (!pl_strcasecmp(¶m, "timeout"))
|
||||
ss->reason = SIPEVENT_TIMEOUT;
|
||||
else if (!pl_strcasecmp(¶m, "giveup"))
|
||||
ss->reason = SIPEVENT_GIVEUP;
|
||||
else if (!pl_strcasecmp(¶m, "noresource"))
|
||||
ss->reason = SIPEVENT_NORESOURCE;
|
||||
else
|
||||
ss->reason = -1;
|
||||
}
|
||||
else {
|
||||
ss->reason = -1;
|
||||
}
|
||||
|
||||
if (!sip_param_decode(&ss->params, "expires", ¶m))
|
||||
ss->expires = param;
|
||||
else
|
||||
ss->expires = pl_null;
|
||||
|
||||
if (!sip_param_decode(&ss->params, "retry-after", ¶m))
|
||||
ss->retry_after = param;
|
||||
else
|
||||
ss->retry_after = pl_null;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
const char *sipevent_substate_name(enum sipevent_subst state)
|
||||
{
|
||||
switch (state) {
|
||||
|
||||
case SIPEVENT_ACTIVE: return "active";
|
||||
case SIPEVENT_PENDING: return "pending";
|
||||
case SIPEVENT_TERMINATED: return "terminated";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const char *sipevent_reason_name(enum sipevent_reason reason)
|
||||
{
|
||||
switch (reason) {
|
||||
|
||||
case SIPEVENT_DEACTIVATED: return "deactivated";
|
||||
case SIPEVENT_PROBATION: return "probation";
|
||||
case SIPEVENT_REJECTED: return "rejected";
|
||||
case SIPEVENT_TIMEOUT: return "timeout";
|
||||
case SIPEVENT_GIVEUP: return "giveup";
|
||||
case SIPEVENT_NORESOURCE: return "noresource";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
482
src/sipevent/notify.c
Normal file
482
src/sipevent/notify.c
Normal file
|
@ -0,0 +1,482 @@
|
|||
/**
|
||||
* @file notify.c SIP Event Notify
|
||||
*
|
||||
* Copyright (C) 2010 Creytiv.com
|
||||
*/
|
||||
#include <re_types.h>
|
||||
#include <re_mem.h>
|
||||
#include <re_mbuf.h>
|
||||
#include <re_sa.h>
|
||||
#include <re_list.h>
|
||||
#include <re_hash.h>
|
||||
#include <re_fmt.h>
|
||||
#include <re_uri.h>
|
||||
#include <re_sys.h>
|
||||
#include <re_tmr.h>
|
||||
#include <re_sip.h>
|
||||
#include <re_sipevent.h>
|
||||
#include "sipevent.h"
|
||||
|
||||
|
||||
static int notify_request(struct sipnot *not, bool reset_ls);
|
||||
|
||||
|
||||
static void internal_close_handler(int err, const struct sip_msg *msg,
|
||||
void *arg)
|
||||
{
|
||||
(void)err;
|
||||
(void)msg;
|
||||
(void)arg;
|
||||
}
|
||||
|
||||
|
||||
static bool terminate(struct sipnot *not, enum sipevent_reason reason)
|
||||
{
|
||||
not->terminated = true;
|
||||
not->reason = reason;
|
||||
not->closeh = internal_close_handler;
|
||||
|
||||
if (not->req) {
|
||||
mem_ref(not);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (not->subscribed && !notify_request(not, true)) {
|
||||
mem_ref(not);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static void destructor(void *arg)
|
||||
{
|
||||
struct sipnot *not = arg;
|
||||
|
||||
tmr_cancel(¬->tmr);
|
||||
|
||||
if (!not->terminated) {
|
||||
|
||||
if (terminate(not, SIPEVENT_DEACTIVATED))
|
||||
return;
|
||||
}
|
||||
|
||||
hash_unlink(¬->he);
|
||||
mem_deref(not->req);
|
||||
mem_deref(not->dlg);
|
||||
mem_deref(not->auth);
|
||||
mem_deref(not->mb);
|
||||
mem_deref(not->event);
|
||||
mem_deref(not->id);
|
||||
mem_deref(not->cuser);
|
||||
mem_deref(not->hdrs);
|
||||
mem_deref(not->ctype);
|
||||
mem_deref(not->sock);
|
||||
mem_deref(not->sip);
|
||||
}
|
||||
|
||||
|
||||
static void sipnot_terminate(struct sipnot *not, int err,
|
||||
const struct sip_msg *msg,
|
||||
enum sipevent_reason reason)
|
||||
{
|
||||
sipnot_close_h *closeh;
|
||||
void *arg;
|
||||
|
||||
closeh = not->closeh;
|
||||
arg = not->arg;
|
||||
|
||||
tmr_cancel(¬->tmr);
|
||||
(void)terminate(not, reason);
|
||||
|
||||
closeh(err, msg, arg);
|
||||
}
|
||||
|
||||
|
||||
static void tmr_handler(void *arg)
|
||||
{
|
||||
struct sipnot *not = arg;
|
||||
|
||||
if (not->terminated)
|
||||
return;
|
||||
|
||||
sipnot_terminate(not, ETIMEDOUT, NULL, SIPEVENT_TIMEOUT);
|
||||
}
|
||||
|
||||
|
||||
void sipnot_refresh(struct sipnot *not, uint32_t expires)
|
||||
{
|
||||
not->expires = min(expires, not->expires_max);
|
||||
|
||||
tmr_start(¬->tmr, not->expires * 1000, tmr_handler, not);
|
||||
}
|
||||
|
||||
|
||||
static void response_handler(int err, const struct sip_msg *msg, void *arg)
|
||||
{
|
||||
struct sipnot *not = arg;
|
||||
|
||||
if (err) {
|
||||
if (err == ETIMEDOUT)
|
||||
not->subscribed = false;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (sip_request_loops(¬->ls, msg->scode)) {
|
||||
not->subscribed = false;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (msg->scode < 200) {
|
||||
return;
|
||||
}
|
||||
else if (msg->scode < 300) {
|
||||
|
||||
(void)sip_dialog_update(not->dlg, msg);
|
||||
}
|
||||
else {
|
||||
switch (msg->scode) {
|
||||
|
||||
case 401:
|
||||
case 407:
|
||||
err = sip_auth_authenticate(not->auth, msg);
|
||||
if (err) {
|
||||
err = (err == EAUTH) ? 0 : err;
|
||||
break;
|
||||
}
|
||||
|
||||
err = notify_request(not, false);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
not->subscribed = false;
|
||||
}
|
||||
|
||||
out:
|
||||
if (not->termsent) {
|
||||
mem_deref(not);
|
||||
}
|
||||
else if (not->terminated) {
|
||||
if (!not->subscribed || notify_request(not, true))
|
||||
mem_deref(not);
|
||||
}
|
||||
else if (!not->subscribed) {
|
||||
sipnot_terminate(not, err, msg, -1);
|
||||
}
|
||||
else if (not->notify_pending) {
|
||||
(void)notify_request(not, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int send_handler(enum sip_transp tp, const struct sa *src,
|
||||
const struct sa *dst, struct mbuf *mb, void *arg)
|
||||
{
|
||||
struct sipnot *not = arg;
|
||||
(void)dst;
|
||||
|
||||
return mbuf_printf(mb, "Contact: <sip:%s@%J%s>\r\n",
|
||||
not->cuser, src, sip_transp_param(tp));
|
||||
}
|
||||
|
||||
|
||||
static int print_event(struct re_printf *pf, const struct sipnot *not)
|
||||
{
|
||||
if (not->id)
|
||||
return re_hprintf(pf, "%s;id=%s", not->event, not->id);
|
||||
else
|
||||
return re_hprintf(pf, "%s", not->event);
|
||||
}
|
||||
|
||||
|
||||
static int print_substate(struct re_printf *pf, const struct sipnot *not)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (not->terminated) {
|
||||
|
||||
err = re_hprintf(pf, "terminated;reason=%s",
|
||||
sipevent_reason_name(not->reason));
|
||||
|
||||
if (not->retry_after)
|
||||
err |= re_hprintf(pf, ";retry-after=%u",
|
||||
not->retry_after);
|
||||
}
|
||||
else {
|
||||
uint32_t expires;
|
||||
|
||||
expires = (uint32_t)(tmr_get_expire(¬->tmr) / 1000);
|
||||
|
||||
err = re_hprintf(pf, "%s;expires=%u",
|
||||
sipevent_substate_name(not->substate),
|
||||
expires);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
static int print_content(struct re_printf *pf, const struct sipnot *not)
|
||||
{
|
||||
if (!not->mb)
|
||||
return re_hprintf(pf,
|
||||
"Content-Length: 0\r\n"
|
||||
"\r\n");
|
||||
else
|
||||
return re_hprintf(pf,
|
||||
"Content-Type: %s\r\n"
|
||||
"Content-Length: %zu\r\n"
|
||||
"\r\n"
|
||||
"%b",
|
||||
not->ctype,
|
||||
mbuf_get_left(not->mb),
|
||||
mbuf_buf(not->mb),
|
||||
mbuf_get_left(not->mb));
|
||||
}
|
||||
|
||||
|
||||
static int notify_request(struct sipnot *not, bool reset_ls)
|
||||
{
|
||||
if (reset_ls)
|
||||
sip_loopstate_reset(¬->ls);
|
||||
|
||||
if (not->terminated)
|
||||
not->termsent = true;
|
||||
|
||||
not->notify_pending = false;
|
||||
|
||||
return sip_drequestf(¬->req, not->sip, true, "NOTIFY",
|
||||
not->dlg, 0, not->auth,
|
||||
send_handler, response_handler, not,
|
||||
"Event: %H\r\n"
|
||||
"Subscription-State: %H\r\n"
|
||||
"%s"
|
||||
"%H",
|
||||
print_event, not,
|
||||
print_substate, not,
|
||||
not->hdrs,
|
||||
print_content, not);
|
||||
}
|
||||
|
||||
|
||||
int sipnot_notify(struct sipnot *not)
|
||||
{
|
||||
if (not->expires == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (not->req) {
|
||||
not->notify_pending = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return notify_request(not, true);
|
||||
}
|
||||
|
||||
|
||||
int sipnot_reply(struct sipnot *not, const struct sip_msg *msg,
|
||||
uint16_t scode, const char *reason)
|
||||
{
|
||||
uint32_t expires;
|
||||
|
||||
expires = (uint32_t)(tmr_get_expire(¬->tmr) / 1000);
|
||||
|
||||
return sip_treplyf(NULL, NULL, not->sip, msg, true, scode, reason,
|
||||
"Contact: <sip:%s@%J%s>\r\n"
|
||||
"Expires: %u\r\n"
|
||||
"Content-Length: 0\r\n"
|
||||
"\r\n",
|
||||
not->cuser, &msg->dst, sip_transp_param(msg->tp),
|
||||
expires);
|
||||
}
|
||||
|
||||
|
||||
int sipevent_accept(struct sipnot **notp, struct sipevent_sock *sock,
|
||||
const struct sip_msg *msg, struct sip_dialog *dlg,
|
||||
const struct sipevent_event *event,
|
||||
uint16_t scode, const char *reason, uint32_t expires_min,
|
||||
uint32_t expires_dfl, uint32_t expires_max,
|
||||
const char *cuser, const char *ctype,
|
||||
sip_auth_h *authh, void *aarg, bool aref,
|
||||
sipnot_close_h *closeh, void *arg, const char *fmt, ...)
|
||||
{
|
||||
struct sipnot *not;
|
||||
uint32_t expires;
|
||||
int err;
|
||||
|
||||
if (!notp || !sock || !msg || !scode || !reason || !expires_dfl ||
|
||||
!expires_max || !cuser || !ctype || expires_dfl < expires_min)
|
||||
return EINVAL;
|
||||
|
||||
not = mem_zalloc(sizeof(*not), destructor);
|
||||
if (!not)
|
||||
return ENOMEM;
|
||||
|
||||
if (!pl_strcmp(&msg->met, "REFER")) {
|
||||
|
||||
err = str_dup(¬->event, "refer");
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = re_sdprintf(¬->id, "%u", msg->cseq.num);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
else {
|
||||
if (!event) {
|
||||
err = EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = pl_strdup(¬->event, &event->event);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if (pl_isset(&event->id)) {
|
||||
|
||||
err = pl_strdup(¬->id, &event->id);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (dlg) {
|
||||
not->dlg = mem_ref(dlg);
|
||||
}
|
||||
else {
|
||||
err = sip_dialog_accept(¬->dlg, msg);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
hash_append(sock->ht_not,
|
||||
hash_joaat_str(sip_dialog_callid(not->dlg)),
|
||||
¬->he, not);
|
||||
|
||||
err = sip_auth_alloc(¬->auth, authh, aarg, aref);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = str_dup(¬->cuser, cuser);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = str_dup(¬->ctype, ctype);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if (fmt) {
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
err = re_vsdprintf(¬->hdrs, fmt, ap);
|
||||
va_end(ap);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
not->expires_min = expires_min;
|
||||
not->expires_dfl = expires_dfl;
|
||||
not->expires_max = expires_max;
|
||||
not->substate = SIPEVENT_PENDING;
|
||||
not->sock = mem_ref(sock);
|
||||
not->sip = mem_ref(sock->sip);
|
||||
not->closeh = closeh ? closeh : internal_close_handler;
|
||||
not->arg = arg;
|
||||
|
||||
if (pl_isset(&msg->expires))
|
||||
expires = pl_u32(&msg->expires);
|
||||
else
|
||||
expires = not->expires_dfl;
|
||||
|
||||
sipnot_refresh(not, expires);
|
||||
|
||||
err = sipnot_reply(not, msg, scode, reason);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
not->subscribed = true;
|
||||
|
||||
out:
|
||||
if (err)
|
||||
mem_deref(not);
|
||||
else
|
||||
*notp = not;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int sipevent_notify(struct sipnot *not, struct mbuf *mb,
|
||||
enum sipevent_subst state, enum sipevent_reason reason,
|
||||
uint32_t retry_after)
|
||||
{
|
||||
if (!not || not->terminated)
|
||||
return EINVAL;
|
||||
|
||||
if (mb || state != SIPEVENT_TERMINATED) {
|
||||
mem_deref(not->mb);
|
||||
not->mb = mem_ref(mb);
|
||||
}
|
||||
|
||||
switch (state) {
|
||||
|
||||
case SIPEVENT_ACTIVE:
|
||||
case SIPEVENT_PENDING:
|
||||
not->substate = state;
|
||||
return sipnot_notify(not);
|
||||
|
||||
case SIPEVENT_TERMINATED:
|
||||
tmr_cancel(¬->tmr);
|
||||
not->retry_after = retry_after;
|
||||
(void)terminate(not, reason);
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int sipevent_notifyf(struct sipnot *not, struct mbuf **mbp,
|
||||
enum sipevent_subst state, enum sipevent_reason reason,
|
||||
uint32_t retry_after, const char *fmt, ...)
|
||||
{
|
||||
struct mbuf *mb;
|
||||
va_list ap;
|
||||
int err;
|
||||
|
||||
if (!not || not->terminated || !fmt)
|
||||
return EINVAL;
|
||||
|
||||
if (mbp && *mbp)
|
||||
return sipevent_notify(not, *mbp, state, reason, retry_after);
|
||||
|
||||
mb = mbuf_alloc(1024);
|
||||
if (!mb)
|
||||
return ENOMEM;
|
||||
|
||||
va_start(ap, fmt);
|
||||
err = mbuf_vprintf(mb, fmt, ap);
|
||||
va_end(ap);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
mb->pos = 0;
|
||||
|
||||
err = sipevent_notify(not, mb, state, reason, retry_after);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
out:
|
||||
if (err || !mbp)
|
||||
mem_deref(mb);
|
||||
else
|
||||
*mbp = mb;
|
||||
|
||||
return err;
|
||||
}
|
92
src/sipevent/sipevent.h
Normal file
92
src/sipevent/sipevent.h
Normal file
|
@ -0,0 +1,92 @@
|
|||
/**
|
||||
* @file sipevent.h SIP Event Private Interface
|
||||
*
|
||||
* Copyright (C) 2010 Creytiv.com
|
||||
*/
|
||||
|
||||
/* Listener Socket */
|
||||
|
||||
struct sipevent_sock {
|
||||
struct sip_lsnr *lsnr;
|
||||
struct hash *ht_not;
|
||||
struct hash *ht_sub;
|
||||
struct sip *sip;
|
||||
sip_msg_h *subh;
|
||||
void *arg;
|
||||
};
|
||||
|
||||
|
||||
/* Notifier */
|
||||
|
||||
struct sipnot {
|
||||
struct le he;
|
||||
struct sip_loopstate ls;
|
||||
struct tmr tmr;
|
||||
struct sipevent_sock *sock;
|
||||
struct sip_request *req;
|
||||
struct sip_dialog *dlg;
|
||||
struct sip_auth *auth;
|
||||
struct sip *sip;
|
||||
struct mbuf *mb;
|
||||
char *event;
|
||||
char *id;
|
||||
char *cuser;
|
||||
char *hdrs;
|
||||
char *ctype;
|
||||
sipnot_close_h *closeh;
|
||||
void *arg;
|
||||
uint32_t expires;
|
||||
uint32_t expires_min;
|
||||
uint32_t expires_dfl;
|
||||
uint32_t expires_max;
|
||||
uint32_t retry_after;
|
||||
enum sipevent_subst substate;
|
||||
enum sipevent_reason reason;
|
||||
bool notify_pending;
|
||||
bool subscribed;
|
||||
bool terminated;
|
||||
bool termsent;
|
||||
};
|
||||
|
||||
void sipnot_refresh(struct sipnot *not, uint32_t expires);
|
||||
int sipnot_notify(struct sipnot *not);
|
||||
int sipnot_reply(struct sipnot *not, const struct sip_msg *msg,
|
||||
uint16_t scode, const char *reason);
|
||||
|
||||
|
||||
/* Subscriber */
|
||||
|
||||
struct sipsub {
|
||||
struct le he;
|
||||
struct sip_loopstate ls;
|
||||
struct tmr tmr;
|
||||
struct sipevent_sock *sock;
|
||||
struct sip_request *req;
|
||||
struct sip_dialog *dlg;
|
||||
struct sip_auth *auth;
|
||||
struct sip *sip;
|
||||
char *event;
|
||||
char *id;
|
||||
char *cuser;
|
||||
char *hdrs;
|
||||
char *refer_hdrs;
|
||||
sipsub_fork_h *forkh;
|
||||
sipsub_notify_h *notifyh;
|
||||
sipsub_close_h *closeh;
|
||||
void *arg;
|
||||
int32_t refer_cseq;
|
||||
uint32_t expires;
|
||||
uint32_t failc;
|
||||
bool subscribed;
|
||||
bool terminated;
|
||||
bool termconf;
|
||||
bool termwait;
|
||||
bool refer;
|
||||
};
|
||||
|
||||
struct sipsub *sipsub_find(struct sipevent_sock *sock,
|
||||
const struct sip_msg *msg,
|
||||
const struct sipevent_event *evt, bool full);
|
||||
void sipsub_reschedule(struct sipsub *sub, uint64_t wait);
|
||||
void sipsub_terminate(struct sipsub *sub, int err, const struct sip_msg *msg,
|
||||
const struct sipevent_substate *substate);
|
666
src/sipevent/subscribe.c
Normal file
666
src/sipevent/subscribe.c
Normal file
|
@ -0,0 +1,666 @@
|
|||
/**
|
||||
* @file subscribe.c SIP Event Subscribe
|
||||
*
|
||||
* Copyright (C) 2010 Creytiv.com
|
||||
*/
|
||||
#include <re_types.h>
|
||||
#include <re_mem.h>
|
||||
#include <re_mbuf.h>
|
||||
#include <re_sa.h>
|
||||
#include <re_list.h>
|
||||
#include <re_hash.h>
|
||||
#include <re_fmt.h>
|
||||
#include <re_uri.h>
|
||||
#include <re_sys.h>
|
||||
#include <re_tmr.h>
|
||||
#include <re_sip.h>
|
||||
#include <re_sipevent.h>
|
||||
#include "sipevent.h"
|
||||
|
||||
|
||||
enum {
|
||||
DEFAULT_EXPIRES = 3600,
|
||||
RESUB_FAIL_WAIT = 60000,
|
||||
RESUB_FAILC_MAX = 7,
|
||||
NOTIFY_TIMEOUT = 10000,
|
||||
};
|
||||
|
||||
|
||||
static int request(struct sipsub *sub, bool reset_ls);
|
||||
|
||||
|
||||
static void internal_notify_handler(struct sip *sip, const struct sip_msg *msg,
|
||||
void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
|
||||
(void)sip_treply(NULL, sip, msg, 200, "OK");
|
||||
}
|
||||
|
||||
|
||||
static void internal_close_handler(int err, const struct sip_msg *msg,
|
||||
const struct sipevent_substate *substate,
|
||||
void *arg)
|
||||
{
|
||||
(void)err;
|
||||
(void)msg;
|
||||
(void)substate;
|
||||
(void)arg;
|
||||
}
|
||||
|
||||
|
||||
static bool terminate(struct sipsub *sub)
|
||||
{
|
||||
sub->terminated = true;
|
||||
sub->forkh = NULL;
|
||||
sub->notifyh = internal_notify_handler;
|
||||
sub->closeh = internal_close_handler;
|
||||
|
||||
if (sub->termwait) {
|
||||
mem_ref(sub);
|
||||
return true;
|
||||
}
|
||||
|
||||
tmr_cancel(&sub->tmr);
|
||||
|
||||
if (sub->req) {
|
||||
mem_ref(sub);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sub->expires && sub->subscribed && !request(sub, true)) {
|
||||
mem_ref(sub);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static void destructor(void *arg)
|
||||
{
|
||||
struct sipsub *sub = arg;
|
||||
|
||||
if (!sub->terminated) {
|
||||
|
||||
if (terminate(sub))
|
||||
return;
|
||||
}
|
||||
|
||||
tmr_cancel(&sub->tmr);
|
||||
hash_unlink(&sub->he);
|
||||
mem_deref(sub->req);
|
||||
mem_deref(sub->dlg);
|
||||
mem_deref(sub->auth);
|
||||
mem_deref(sub->event);
|
||||
mem_deref(sub->id);
|
||||
mem_deref(sub->cuser);
|
||||
mem_deref(sub->hdrs);
|
||||
mem_deref(sub->refer_hdrs);
|
||||
mem_deref(sub->sock);
|
||||
mem_deref(sub->sip);
|
||||
}
|
||||
|
||||
|
||||
static void notify_timeout_handler(void *arg)
|
||||
{
|
||||
struct sipsub *sub = arg;
|
||||
|
||||
sub->termwait = false;
|
||||
|
||||
if (sub->terminated)
|
||||
mem_deref(sub);
|
||||
else
|
||||
sipsub_terminate(sub, ETIMEDOUT, NULL, NULL);
|
||||
}
|
||||
|
||||
|
||||
static void tmr_handler(void *arg)
|
||||
{
|
||||
struct sipsub *sub = arg;
|
||||
int err;
|
||||
|
||||
if (sub->req || sub->terminated)
|
||||
return;
|
||||
|
||||
err = request(sub, true);
|
||||
if (err) {
|
||||
if (++sub->failc < RESUB_FAILC_MAX) {
|
||||
sipsub_reschedule(sub, RESUB_FAIL_WAIT);
|
||||
}
|
||||
else {
|
||||
sipsub_terminate(sub, err, NULL, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void sipsub_reschedule(struct sipsub *sub, uint64_t wait)
|
||||
{
|
||||
tmr_start(&sub->tmr, wait, tmr_handler, sub);
|
||||
}
|
||||
|
||||
|
||||
void sipsub_terminate(struct sipsub *sub, int err, const struct sip_msg *msg,
|
||||
const struct sipevent_substate *substate)
|
||||
{
|
||||
sipsub_close_h *closeh;
|
||||
void *arg;
|
||||
|
||||
closeh = sub->closeh;
|
||||
arg = sub->arg;
|
||||
|
||||
(void)terminate(sub);
|
||||
|
||||
closeh(err, msg, substate, arg);
|
||||
}
|
||||
|
||||
|
||||
static void response_handler(int err, const struct sip_msg *msg, void *arg)
|
||||
{
|
||||
const struct sip_hdr *minexp;
|
||||
struct sipsub *sub = arg;
|
||||
|
||||
if (err || sip_request_loops(&sub->ls, msg->scode))
|
||||
goto out;
|
||||
|
||||
if (msg->scode < 200) {
|
||||
return;
|
||||
}
|
||||
else if (msg->scode < 300) {
|
||||
|
||||
uint32_t wait;
|
||||
|
||||
if (sub->forkh) {
|
||||
|
||||
struct sipsub *fsub;
|
||||
|
||||
fsub = sipsub_find(sub->sock, msg, NULL, true);
|
||||
if (!fsub) {
|
||||
|
||||
err = sub->forkh(&fsub, sub, msg, sub->arg);
|
||||
if (err)
|
||||
return;
|
||||
}
|
||||
else {
|
||||
(void)sip_dialog_update(fsub->dlg, msg);
|
||||
}
|
||||
|
||||
sub = fsub;
|
||||
}
|
||||
else if (!sip_dialog_established(sub->dlg)) {
|
||||
|
||||
err = sip_dialog_create(sub->dlg, msg);
|
||||
if (err) {
|
||||
sub->subscribed = false;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Ignore 2xx responses for other dialogs
|
||||
* if forking is disabled */
|
||||
if (!sip_dialog_cmp(sub->dlg, msg))
|
||||
return;
|
||||
|
||||
(void)sip_dialog_update(sub->dlg, msg);
|
||||
}
|
||||
|
||||
if (!sub->termconf)
|
||||
sub->subscribed = true;
|
||||
|
||||
sub->failc = 0;
|
||||
|
||||
if (!sub->expires && !sub->termconf) {
|
||||
|
||||
tmr_start(&sub->tmr, NOTIFY_TIMEOUT,
|
||||
notify_timeout_handler, sub);
|
||||
sub->termwait = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (sub->terminated)
|
||||
goto out;
|
||||
|
||||
if (sub->refer) {
|
||||
sub->refer = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (pl_isset(&msg->expires))
|
||||
wait = pl_u32(&msg->expires);
|
||||
else
|
||||
wait = sub->expires;
|
||||
|
||||
sipsub_reschedule(sub, wait * 900);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if (sub->terminated && !sub->subscribed)
|
||||
goto out;
|
||||
|
||||
switch (msg->scode) {
|
||||
|
||||
case 401:
|
||||
case 407:
|
||||
err = sip_auth_authenticate(sub->auth, msg);
|
||||
if (err) {
|
||||
err = (err == EAUTH) ? 0 : err;
|
||||
break;
|
||||
}
|
||||
|
||||
err = request(sub, false);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
return;
|
||||
|
||||
case 403:
|
||||
sip_auth_reset(sub->auth);
|
||||
break;
|
||||
|
||||
case 423:
|
||||
minexp = sip_msg_hdr(msg, SIP_HDR_MIN_EXPIRES);
|
||||
if (!minexp || !pl_u32(&minexp->val) || !sub->expires)
|
||||
break;
|
||||
|
||||
sub->expires = pl_u32(&minexp->val);
|
||||
|
||||
err = request(sub, false);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
return;
|
||||
|
||||
case 481:
|
||||
sub->subscribed = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
sub->refer = false;
|
||||
|
||||
if (sub->terminated) {
|
||||
|
||||
if (!sub->expires || !sub->subscribed || request(sub, true))
|
||||
mem_deref(sub);
|
||||
}
|
||||
else {
|
||||
if (sub->subscribed && ++sub->failc < RESUB_FAILC_MAX)
|
||||
sipsub_reschedule(sub, RESUB_FAIL_WAIT);
|
||||
else
|
||||
sipsub_terminate(sub, err, msg, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int send_handler(enum sip_transp tp, const struct sa *src,
|
||||
const struct sa *dst, struct mbuf *mb, void *arg)
|
||||
{
|
||||
struct sipsub *sub = arg;
|
||||
(void)dst;
|
||||
|
||||
return mbuf_printf(mb, "Contact: <sip:%s@%J%s>\r\n",
|
||||
sub->cuser, src, sip_transp_param(tp));
|
||||
}
|
||||
|
||||
|
||||
static int print_event(struct re_printf *pf, const struct sipsub *sub)
|
||||
{
|
||||
if (sub->id)
|
||||
return re_hprintf(pf, "%s;id=%s", sub->event, sub->id);
|
||||
else
|
||||
return re_hprintf(pf, "%s", sub->event);
|
||||
}
|
||||
|
||||
|
||||
static int request(struct sipsub *sub, bool reset_ls)
|
||||
{
|
||||
if (reset_ls)
|
||||
sip_loopstate_reset(&sub->ls);
|
||||
|
||||
if (sub->refer) {
|
||||
|
||||
sub->refer_cseq = sip_dialog_lseq(sub->dlg);
|
||||
|
||||
return sip_drequestf(&sub->req, sub->sip, true, "REFER",
|
||||
sub->dlg, 0, sub->auth,
|
||||
send_handler, response_handler, sub,
|
||||
"%s"
|
||||
"Content-Length: 0\r\n"
|
||||
"\r\n",
|
||||
sub->refer_hdrs);
|
||||
}
|
||||
else {
|
||||
if (sub->terminated)
|
||||
sub->expires = 0;
|
||||
|
||||
return sip_drequestf(&sub->req, sub->sip, true, "SUBSCRIBE",
|
||||
sub->dlg, 0, sub->auth,
|
||||
send_handler, response_handler, sub,
|
||||
"Event: %H\r\n"
|
||||
"Expires: %u\r\n"
|
||||
"%s"
|
||||
"Content-Length: 0\r\n"
|
||||
"\r\n",
|
||||
print_event, sub,
|
||||
sub->expires,
|
||||
sub->hdrs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int sipsub_alloc(struct sipsub **subp, struct sipevent_sock *sock,
|
||||
bool refer, struct sip_dialog *dlg, const char *uri,
|
||||
const char *from_name, const char *from_uri,
|
||||
const char *event, const char *id, uint32_t expires,
|
||||
const char *cuser,
|
||||
const char *routev[], uint32_t routec,
|
||||
sip_auth_h *authh, void *aarg, bool aref,
|
||||
sipsub_fork_h *forkh, sipsub_notify_h *notifyh,
|
||||
sipsub_close_h *closeh, void *arg,
|
||||
const char *fmt, va_list ap)
|
||||
{
|
||||
struct sipsub *sub;
|
||||
int err;
|
||||
|
||||
if (!subp || !sock || !event || !cuser)
|
||||
return EINVAL;
|
||||
|
||||
if (!dlg && (!uri || !from_uri))
|
||||
return EINVAL;
|
||||
|
||||
sub = mem_zalloc(sizeof(*sub), destructor);
|
||||
if (!sub)
|
||||
return ENOMEM;
|
||||
|
||||
if (dlg) {
|
||||
sub->dlg = mem_ref(dlg);
|
||||
}
|
||||
else {
|
||||
err = sip_dialog_alloc(&sub->dlg, uri, uri, from_name,
|
||||
from_uri, routev, routec);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
hash_append(sock->ht_sub,
|
||||
hash_joaat_str(sip_dialog_callid(sub->dlg)),
|
||||
&sub->he, sub);
|
||||
|
||||
err = sip_auth_alloc(&sub->auth, authh, aarg, aref);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = str_dup(&sub->event, event);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if (id) {
|
||||
err = str_dup(&sub->id, id);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = str_dup(&sub->cuser, cuser);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if (fmt) {
|
||||
err = re_vsdprintf(refer ? &sub->refer_hdrs : &sub->hdrs,
|
||||
fmt, ap);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
sub->refer_cseq = -1;
|
||||
sub->refer = refer;
|
||||
sub->sock = mem_ref(sock);
|
||||
sub->sip = mem_ref(sock->sip);
|
||||
sub->expires = expires;
|
||||
sub->forkh = forkh;
|
||||
sub->notifyh = notifyh ? notifyh : internal_notify_handler;
|
||||
sub->closeh = closeh ? closeh : internal_close_handler;
|
||||
sub->arg = arg;
|
||||
|
||||
err = request(sub, true);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
out:
|
||||
if (err)
|
||||
mem_deref(sub);
|
||||
else
|
||||
*subp = sub;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Allocate a SIP subscriber client
|
||||
*
|
||||
* @param subp Pointer to allocated SIP subscriber client
|
||||
* @param sock SIP Event socket
|
||||
* @param uri SIP Request URI
|
||||
* @param from_name SIP From-header Name (optional)
|
||||
* @param from_uri SIP From-header URI
|
||||
* @param event SIP Event to subscribe to
|
||||
* @param id SIP Event ID (optional)
|
||||
* @param expires Subscription expires value
|
||||
* @param cuser Contact username
|
||||
* @param routev Optional route vector
|
||||
* @param routec Number of routes
|
||||
* @param authh Authentication handler
|
||||
* @param aarg Authentication handler argument
|
||||
* @param aref True to ref argument
|
||||
* @param forkh Fork handler
|
||||
* @param notifyh Notify handler
|
||||
* @param closeh Close handler
|
||||
* @param arg Response handler argument
|
||||
* @param fmt Formatted strings with extra SIP Headers
|
||||
*
|
||||
* @return 0 if success, otherwise errorcode
|
||||
*/
|
||||
int sipevent_subscribe(struct sipsub **subp, struct sipevent_sock *sock,
|
||||
const char *uri, const char *from_name,
|
||||
const char *from_uri, const char *event, const char *id,
|
||||
uint32_t expires, const char *cuser,
|
||||
const char *routev[], uint32_t routec,
|
||||
sip_auth_h *authh, void *aarg, bool aref,
|
||||
sipsub_fork_h *forkh, sipsub_notify_h *notifyh,
|
||||
sipsub_close_h *closeh, void *arg,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int err;
|
||||
|
||||
va_start(ap, fmt);
|
||||
err = sipsub_alloc(subp, sock, false, NULL, uri, from_name, from_uri,
|
||||
event, id, expires, cuser,
|
||||
routev, routec, authh, aarg, aref, forkh, notifyh,
|
||||
closeh, arg, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Allocate a SIP subscriber client using an existing dialog
|
||||
*
|
||||
* @param subp Pointer to allocated SIP subscriber client
|
||||
* @param sock SIP Event socket
|
||||
* @param dlg Established SIP Dialog
|
||||
* @param event SIP Event to subscribe to
|
||||
* @param id SIP Event ID (optional)
|
||||
* @param expires Subscription expires value
|
||||
* @param cuser Contact username
|
||||
* @param authh Authentication handler
|
||||
* @param aarg Authentication handler argument
|
||||
* @param aref True to ref argument
|
||||
* @param noth Notify handler
|
||||
* @param closeh Close handler
|
||||
* @param arg Response handler argument
|
||||
* @param fmt Formatted strings with extra SIP Headers
|
||||
*
|
||||
* @return 0 if success, otherwise errorcode
|
||||
*/
|
||||
int sipevent_dsubscribe(struct sipsub **subp, struct sipevent_sock *sock,
|
||||
struct sip_dialog *dlg, const char *event,
|
||||
const char *id, uint32_t expires, const char *cuser,
|
||||
sip_auth_h *authh, void *aarg, bool aref,
|
||||
sipsub_notify_h *notifyh, sipsub_close_h *closeh,
|
||||
void *arg, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int err;
|
||||
|
||||
va_start(ap, fmt);
|
||||
err = sipsub_alloc(subp, sock, false, dlg, NULL, NULL, NULL,
|
||||
event, id, expires, cuser,
|
||||
NULL, 0, authh, aarg, aref, NULL, notifyh,
|
||||
closeh, arg, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Allocate a SIP refer client
|
||||
*
|
||||
* @param subp Pointer to allocated SIP subscriber client
|
||||
* @param sock SIP Event socket
|
||||
* @param uri SIP Request URI
|
||||
* @param from_name SIP From-header Name (optional)
|
||||
* @param from_uri SIP From-header URI
|
||||
* @param cuser Contact username
|
||||
* @param routev Optional route vector
|
||||
* @param routec Number of routes
|
||||
* @param authh Authentication handler
|
||||
* @param aarg Authentication handler argument
|
||||
* @param aref True to ref argument
|
||||
* @param forkh Fork handler
|
||||
* @param notifyh Notify handler
|
||||
* @param closeh Close handler
|
||||
* @param arg Response handler argument
|
||||
* @param fmt Formatted strings with extra SIP Headers
|
||||
*
|
||||
* @return 0 if success, otherwise errorcode
|
||||
*/
|
||||
int sipevent_refer(struct sipsub **subp, struct sipevent_sock *sock,
|
||||
const char *uri, const char *from_name,
|
||||
const char *from_uri, const char *cuser,
|
||||
const char *routev[], uint32_t routec,
|
||||
sip_auth_h *authh, void *aarg, bool aref,
|
||||
sipsub_fork_h *forkh, sipsub_notify_h *notifyh,
|
||||
sipsub_close_h *closeh, void *arg,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int err;
|
||||
|
||||
va_start(ap, fmt);
|
||||
err = sipsub_alloc(subp, sock, true, NULL, uri, from_name, from_uri,
|
||||
"refer", NULL, DEFAULT_EXPIRES, cuser,
|
||||
routev, routec, authh, aarg, aref, forkh, notifyh,
|
||||
closeh, arg, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Allocate a SIP refer client using an existing dialog
|
||||
*
|
||||
* @param subp Pointer to allocated SIP subscriber client
|
||||
* @param sock SIP Event socket
|
||||
* @param dlg Established SIP Dialog
|
||||
* @param cuser Contact username
|
||||
* @param authh Authentication handler
|
||||
* @param aarg Authentication handler argument
|
||||
* @param aref True to ref argument
|
||||
* @param notifyh Notify handler
|
||||
* @param closeh Close handler
|
||||
* @param arg Response handler argument
|
||||
* @param fmt Formatted strings with extra SIP Headers
|
||||
*
|
||||
* @return 0 if success, otherwise errorcode
|
||||
*/
|
||||
int sipevent_drefer(struct sipsub **subp, struct sipevent_sock *sock,
|
||||
struct sip_dialog *dlg, const char *cuser,
|
||||
sip_auth_h *authh, void *aarg, bool aref,
|
||||
sipsub_notify_h *notifyh, sipsub_close_h *closeh,
|
||||
void *arg, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int err;
|
||||
|
||||
va_start(ap, fmt);
|
||||
err = sipsub_alloc(subp, sock, true, dlg, NULL, NULL, NULL,
|
||||
"refer", NULL, DEFAULT_EXPIRES, cuser,
|
||||
NULL, 0, authh, aarg, aref, NULL, notifyh,
|
||||
closeh, arg, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int sipevent_fork(struct sipsub **subp, struct sipsub *osub,
|
||||
const struct sip_msg *msg,
|
||||
sip_auth_h *authh, void *aarg, bool aref,
|
||||
sipsub_notify_h *notifyh, sipsub_close_h *closeh,
|
||||
void *arg)
|
||||
{
|
||||
struct sipsub *sub;
|
||||
int err;
|
||||
|
||||
if (!subp || !osub || !msg)
|
||||
return EINVAL;
|
||||
|
||||
sub = mem_zalloc(sizeof(*sub), destructor);
|
||||
if (!sub)
|
||||
return ENOMEM;
|
||||
|
||||
err = sip_dialog_fork(&sub->dlg, osub->dlg, msg);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
hash_append(osub->sock->ht_sub,
|
||||
hash_joaat_str(sip_dialog_callid(sub->dlg)),
|
||||
&sub->he, sub);
|
||||
|
||||
err = sip_auth_alloc(&sub->auth, authh, aarg, aref);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
sub->event = mem_ref(osub->event);
|
||||
sub->id = mem_ref(osub->id);
|
||||
sub->cuser = mem_ref(osub->cuser);
|
||||
sub->hdrs = mem_ref(osub->hdrs);
|
||||
sub->refer = osub->refer;
|
||||
sub->sock = mem_ref(osub->sock);
|
||||
sub->sip = mem_ref(osub->sip);
|
||||
sub->expires = osub->expires;
|
||||
sub->forkh = NULL;
|
||||
sub->notifyh = notifyh ? notifyh : internal_notify_handler;
|
||||
sub->closeh = closeh ? closeh : internal_close_handler;
|
||||
sub->arg = arg;
|
||||
|
||||
if (!sub->expires) {
|
||||
tmr_start(&sub->tmr, NOTIFY_TIMEOUT,
|
||||
notify_timeout_handler, sub);
|
||||
sub->termwait = true;
|
||||
}
|
||||
|
||||
out:
|
||||
if (err)
|
||||
mem_deref(sub);
|
||||
else
|
||||
*subp = sub;
|
||||
|
||||
return err;
|
||||
}
|
|
@ -266,6 +266,10 @@ static bool request_handler(const struct sip_msg *msg, void *arg)
|
|||
return true;
|
||||
}
|
||||
else if (!pl_strcmp(&msg->met, "REFER")) {
|
||||
|
||||
if (!pl_isset(&msg->to.tag))
|
||||
return false;
|
||||
|
||||
refer_handler(sock, msg);
|
||||
return true;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue