diff --git a/include/baresip.h b/include/baresip.h index b9fd26931..230ab84b7 100644 --- a/include/baresip.h +++ b/include/baresip.h @@ -868,10 +868,15 @@ enum ua_event { UA_EVENT_MODULE, UA_EVENT_END_OF_FILE, UA_EVENT_CUSTOM, + UA_EVENT_SIPSESS_CONN, UA_EVENT_MAX, }; + +struct bevent; + + /** SIP auto answer method */ enum answer_method { ANSM_NONE = 0, @@ -883,6 +888,7 @@ enum answer_method { /** Defines the User-Agent event handler */ typedef void (ua_event_h)(struct ua *ua, enum ua_event ev, struct call *call, const char *prm, void *arg); +typedef void (bevent_h)(enum ua_event ev, struct bevent *event, void *arg); typedef void (options_resp_h)(int err, const struct sip_msg *msg, void *arg); typedef void (refer_resp_h)(int err, const struct sip_msg *msg, void *arg); @@ -1654,14 +1660,30 @@ void module_app_unload(void); int event_encode_dict(struct odict *od, struct ua *ua, enum ua_event ev, struct call *call, const char *prm); +int odict_encode_bevent(struct odict *od, struct bevent *event); int event_add_au_jb_stat(struct odict *od_parent, const struct call *call); int uag_event_register(ua_event_h *eh, void *arg); void uag_event_unregister(ua_event_h *eh); +int bevent_register(bevent_h *eh, void *arg); +void bevent_unregister(bevent_h *eh); void ua_event(struct ua *ua, enum ua_event ev, struct call *call, const char *fmt, ...); +int bevent_app_emit(enum ua_event ev, void *arg, const char *fmt, ...); +int bevent_ua_emit(enum ua_event ev, struct ua *ua, const char *fmt, ...); +int bevent_call_emit(enum ua_event ev, struct call *call, + const char *fmt, ...); +int bevent_sip_msg_emit(enum ua_event ev, const struct sip_msg *msg, + const char *fmt, ...); void module_event(const char *module, const char *event, struct ua *ua, struct call *call, const char *fmt, ...); const char *uag_event_str(enum ua_event ev); +struct call *bevent_get_call(const struct bevent *event); +struct ua *bevent_get_ua(const struct bevent *event); +const struct sip_msg *bevent_get_msg(const struct bevent *event); +enum ua_event bevent_get_enum(const struct bevent *event); +const char *bevent_get_text(const struct bevent *event); +void bevent_set_error(struct bevent *event, int err); +void bevent_stop(struct bevent *event); /* diff --git a/src/bevent.c b/src/bevent.c index d840fe7f1..f37a64f77 100644 --- a/src/bevent.c +++ b/src/bevent.c @@ -1,5 +1,5 @@ /** - * @file src/event.c Baresip event handling + * @file src/bevent.c Baresip event handling * * Copyright (C) 2017 Alfred E. Heggestad */ @@ -14,6 +14,20 @@ enum { }; +struct bevent { + enum ua_event ev; + const char *txt; + int err; + bool stop; + union { + struct ua *ua; + struct call *call; + const struct sip_msg *msg; + void *arg; + } u; +}; + + struct ua_eh { struct le le; ua_event_h *h; @@ -32,7 +46,90 @@ static void eh_destructor(void *arg) } -static const char *event_class_name(enum ua_event ev) +struct ehe { + struct le le; + bevent_h *h; + void *arg; +}; + + +static struct list ehel; /**< Event handlers (struct ehe) */ + + +static void bevent_emit_base(struct bevent *event); + + +static void ehe_destructor(void *arg) +{ + struct ehe *ehe = arg; + + list_unlink(&ehe->le); +} + + +enum bevent_class { + BEVENT_CLASS_UA, + BEVENT_CLASS_CALL, + BEVENT_CLASS_APP, + BEVENT_CLASS_SIP, + BEVENT_CLASS_UNDEFINED +}; + + +static enum bevent_class bevent_class(enum ua_event ev) +{ + switch (ev) { + + case UA_EVENT_REGISTERING: + case UA_EVENT_REGISTER_OK: + case UA_EVENT_REGISTER_FAIL: + case UA_EVENT_UNREGISTERING: + case UA_EVENT_FALLBACK_OK: + case UA_EVENT_FALLBACK_FAIL: + case UA_EVENT_REFER: + case UA_EVENT_CREATE: + case UA_EVENT_MWI_NOTIFY: + case UA_EVENT_CUSTOM: + return BEVENT_CLASS_UA; + + case UA_EVENT_SHUTDOWN: + case UA_EVENT_EXIT: + return BEVENT_CLASS_APP; + + case UA_EVENT_CALL_INCOMING: + case UA_EVENT_CALL_OUTGOING: + case UA_EVENT_CALL_RINGING: + case UA_EVENT_CALL_PROGRESS: + case UA_EVENT_CALL_ANSWERED: + case UA_EVENT_CALL_ESTABLISHED: + case UA_EVENT_CALL_CLOSED: + case UA_EVENT_CALL_TRANSFER: + case UA_EVENT_CALL_TRANSFER_FAILED: + case UA_EVENT_CALL_REDIRECT: + case UA_EVENT_CALL_DTMF_START: + case UA_EVENT_CALL_DTMF_END: + case UA_EVENT_CALL_RTPESTAB: + case UA_EVENT_CALL_RTCP: + case UA_EVENT_CALL_MENC: + case UA_EVENT_CALL_LOCAL_SDP: + case UA_EVENT_CALL_REMOTE_SDP: + case UA_EVENT_CALL_HOLD: + case UA_EVENT_CALL_RESUME: + case UA_EVENT_AUDIO_ERROR: + case UA_EVENT_END_OF_FILE: + case UA_EVENT_VU_RX: + case UA_EVENT_VU_TX: + case UA_EVENT_MODULE: + return BEVENT_CLASS_CALL; + case UA_EVENT_SIPSESS_CONN: + return BEVENT_CLASS_SIP; + default: + return BEVENT_CLASS_UNDEFINED; + } +} + + +static const char *ua_event_class_name(enum ua_event ev) { switch (ev) { @@ -82,6 +179,155 @@ static const char *event_class_name(enum ua_event ev) } +static const char *bevent_class_name(enum ua_event ev) +{ + enum bevent_class ec = bevent_class(ev); + switch (ec) { + case BEVENT_CLASS_UA: + return "ua"; + + case BEVENT_CLASS_CALL: + return "call"; + + case BEVENT_CLASS_APP: + return "application"; + + case BEVENT_CLASS_SIP: + return "sip"; + + default: + return "other"; + } +} + + +/** + * Returns the call object of a call event, which was generated by function + * bevent_call_emit(). If the event was generated by another emit function + * then NULL is returned + * + * @param event Baresip event + * + * @return The call object, or NULL + */ +struct call *bevent_get_call(const struct bevent *event) +{ + if (!event) + return NULL; + + if (bevent_class(event->ev) == BEVENT_CLASS_CALL) + return event->u.call; + + return NULL; +} + + +/** + * Returns the User-Agent of a UA or call event, which was generated by + * function bevent_ua_emit() or function bevent_call_emit(). If the event was + * generated by another emit function then NULL is returned + * + * @param event Baresip event + * + * @return The User-Agent, or NULL + */ +struct ua *bevent_get_ua(const struct bevent *event) +{ + struct call *call; + + if (!event) + return NULL; + + call = bevent_get_call(event); + if (call) + return call_get_ua(call); + + if (bevent_class(event->ev) == BEVENT_CLASS_UA) + return event->u.ua; + + return NULL; +} + + +/** + * Returns the sip_msg of a SIP message event, which was generated by function + * bevent_sip_msg_emit(). If the event was generated by another emit function + * then NULL is returned + * + * @param event Baresip event + * + * @return The SIP message, or NULL + */ +const struct sip_msg *bevent_get_msg(const struct bevent *event) +{ + if (!event) + return NULL; + + if (bevent_class(event->ev) == BEVENT_CLASS_SIP) + return event->u.msg; + + return NULL; +} + + +/** + * Returns the event type + * + * @param event Baresip event + * + * @return the event type + */ +enum ua_event bevent_get_enum(const struct bevent *event) +{ + return event ? event->ev : UA_EVENT_MAX; +} + + +/** + * Returns the event text + * + * @param event Baresip event + * + * @return the event text + */ +const char *bevent_get_text(const struct bevent *event) +{ + return event ? event->txt : ""; +} + + +/** + * Set error code of given event. This should be used in the event handler to + * inform baresip that an error occurred. An event with set error is not passed + * to further event handlers + * + * @param event Baresip event + * @param err Error code + */ +void bevent_set_error(struct bevent *event, int err) +{ + if (!event) + return; + + event->err = err; +} + + +/** + * Stops event processing of given event. This should be used in an event + * handler to stop passing the event to further event handlers + * + * @param event Baresip event + */ +void bevent_stop(struct bevent *event) +{ + if (!event) + return; + + event->stop = true; +} + + static int add_rtcp_stats(struct odict *od_parent, const struct rtcp_stats *rs) { struct odict *od = NULL, *tx = NULL, *rx = NULL; @@ -151,8 +397,10 @@ int event_encode_dict(struct odict *od, struct ua *ua, enum ua_event ev, return EINVAL; err |= odict_entry_add(od, "type", ODICT_STRING, event_str); - err |= odict_entry_add(od, "class", - ODICT_STRING, event_class_name(ev)); + if (!odict_lookup(od, "class")) { + err |= odict_entry_add(od, "class", + ODICT_STRING, ua_event_class_name(ev)); + } if (ua) { err |= odict_entry_add(od, "accountaor", @@ -240,6 +488,9 @@ int event_encode_dict(struct odict *od, struct ua *ua, enum ua_event ev, if (ev == UA_EVENT_CALL_RTCP) { struct stream *strm = NULL; + if (!prm) + goto out; + if (0 == str_casecmp(prm, "audio")) strm = audio_strm(call_audio(call)); else if (0 == str_casecmp(prm, "video")) @@ -256,6 +507,25 @@ int event_encode_dict(struct odict *od, struct ua *ua, enum ua_event ev, } +int odict_encode_bevent(struct odict *od, struct bevent *event) +{ + struct ua *ua = bevent_get_ua(event); + struct call *call = bevent_get_call(event); + int err; + + if (!od) + return EINVAL; + + err = odict_entry_add(od, "class", + ODICT_STRING, bevent_class_name(event->ev)); + if (err) + return err; + + /* For now we re-use the deprecated function */ + return event_encode_dict(od, ua, event->ev, call, event->txt); +} + + /** * Add audio buffer status * @@ -324,6 +594,57 @@ void uag_event_unregister(ua_event_h *h) } +/** + * Register an Event handler + * + * @param eh Event handler + * @param arg Handler argument + * + * @return 0 if success, otherwise errorcode + */ +int bevent_register(bevent_h *eh, void *arg) +{ + struct ehe *ehe; + + if (!eh) + return EINVAL; + + bevent_unregister(eh); + + ehe = mem_zalloc(sizeof(*ehe), ehe_destructor); + if (!ehe) + return ENOMEM; + + ehe->h = eh; + ehe->arg = arg; + + list_append(&ehel, &ehe->le, ehe); + + return 0; +} + + +/** + * Unregister an Event handler + * + * @param eh Event handler + */ +void bevent_unregister(bevent_h *eh) +{ + struct le *le; + + for (le = ehel.head; le; le = le->next) { + + struct ehe *ehe = le->data; + + if (ehe->h == eh) { + mem_deref(ehe); + break; + } + } +} + + /** * Send a User-Agent event to all UA event handlers * @@ -410,6 +731,168 @@ void module_event(const char *module, const char *event, struct ua *ua, } +static void bevent_emit_base(struct bevent *event) +{ + struct le *le; + le = ehel.head; + while (le) { + struct ehe *ehe = le->data; + le = le->next; + + ehe->h(event->ev, event, ehe->arg); + + if (event->stop) + return; + } +} + + +static int bevent_emit(struct bevent *event, const char *fmt, va_list ap) +{ + char *buf; + int err; + + if (!fmt) + err = re_sdprintf(&buf, ""); + else + err = re_vsdprintf(&buf, fmt, ap); + if (err) + return err; + + event->txt = buf; + event->err = 0; + bevent_emit_base(event); + if (event->err) { + err = event->err; + goto out; + } + + if (event->stop) + goto out; + +out: + mem_deref(buf); + return err; +} + + +/** + * Emit an application event + * + * @param ev User-agent event + * @param arg Application specific argument (optional) + * @param fmt Formatted arguments + * @param ... Variable arguments + * + * @return 0 if success, otherwise errorcode + */ +int bevent_app_emit(enum ua_event ev, void *arg, const char *fmt, ...) +{ + va_list ap; + struct bevent event = {.ev = ev}; + int err; + + if (bevent_class(ev) != BEVENT_CLASS_APP) + return EINVAL; + + event.u.arg = arg; + + va_start(ap, fmt); + err = bevent_emit(&event, fmt, ap); + va_end(ap); + + return err; +} + + +/** + * Emit a User-Agent event + * + * @param ev User-Agent event + * @param ua User-Agent + * @param fmt Formatted arguments + * @param ... Variable arguments + * + * @return 0 if success, otherwise errorcode + */ +int bevent_ua_emit(enum ua_event ev, struct ua *ua, const char *fmt, ...) +{ + struct bevent event = {.ev = ev}; + va_list ap; + int err; + + if (bevent_class(ev) != BEVENT_CLASS_UA) + return EINVAL; + + event.u.ua = ua; + + va_start(ap, fmt); + err = bevent_emit(&event, fmt, ap); + va_end(ap); + + return err; +} + + +/** + * Emit a Call event + * + * @param ev User-Agent event + * @param call Call object + * @param fmt Formatted arguments + * @param ... Variable arguments + * + * @return 0 if success, otherwise errorcode + */ +int bevent_call_emit(enum ua_event ev, struct call *call, const char *fmt, ...) +{ + struct bevent event = {.ev = ev}; + va_list ap; + int err; + + if (bevent_class(ev) != BEVENT_CLASS_CALL) + return EINVAL; + + event.u.call = call; + + va_start(ap, fmt); + err = bevent_emit(&event, fmt, ap); + va_end(ap); + + return err; +} + + +/** + * Emit a SIP message event + * + * @param ev User-Agent event + * @param msg SIP message + * @param fmt Formatted arguments + * @param ... Variable arguments + * + * @return 0 if success, otherwise errorcode + */ +int bevent_sip_msg_emit(enum ua_event ev, const struct sip_msg *msg, + const char *fmt, ...) +{ + struct bevent event = {.ev = ev}; + va_list ap; + int err; + + if (bevent_class(ev) != BEVENT_CLASS_SIP) + return EINVAL; + + event.u.msg = msg; + + va_start(ap, fmt); + err = bevent_emit(&event, fmt, ap); + va_end(ap); + + return err; +} + + /** * Get the name of the User-Agent event * @@ -431,6 +914,7 @@ const char *uag_event_str(enum ua_event ev) case UA_EVENT_CREATE: return "CREATE"; case UA_EVENT_SHUTDOWN: return "SHUTDOWN"; case UA_EVENT_EXIT: return "EXIT"; + case UA_EVENT_SIPSESS_CONN: return "SIPSESS_CONN"; case UA_EVENT_CALL_INCOMING: return "CALL_INCOMING"; case UA_EVENT_CALL_OUTGOING: return "CALL_OUTGOING"; case UA_EVENT_CALL_RINGING: return "CALL_RINGING";