Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package baresip for openSUSE:Factory checked in at 2026-04-07 16:34:14 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/baresip (Old) and /work/SRC/openSUSE:Factory/.baresip.new.21863 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "baresip" Tue Apr 7 16:34:14 2026 rev:15 rq:1344904 version:4.7.0 Changes: -------- --- /work/SRC/openSUSE:Factory/baresip/baresip.changes 2026-03-30 18:37:14.633969985 +0200 +++ /work/SRC/openSUSE:Factory/.baresip.new.21863/baresip.changes 2026-04-07 16:50:46.577176115 +0200 @@ -1,0 +2,8 @@ +Tue Apr 7 08:13:00 UTC 2026 - Martin Hauke <[email protected]> + +- Update to version 4.7.0 + * https://github.com/baresip/baresip/releases/tag/v4.7.0 + * https://github.com/baresip/baresip/releases/tag/v4.6.0 + * https://github.com/baresip/baresip/releases/tag/v4.5.0 + +------------------------------------------------------------------- Old: ---- baresip-4.6.0.tar.gz New: ---- baresip-4.7.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ baresip.spec ++++++ --- /var/tmp/diff_new_pack.lVzeb5/_old 2026-04-07 16:50:47.169200624 +0200 +++ /var/tmp/diff_new_pack.lVzeb5/_new 2026-04-07 16:50:47.177200955 +0200 @@ -17,10 +17,10 @@ # -%global sover 26 +%global sover 27 %global libname lib%{name}%{sover} Name: baresip -Version: 4.6.0 +Version: 4.7.0 Release: 0 Summary: Modular SIP useragent License: BSD-3-Clause ++++++ baresip-4.6.0.tar.gz -> baresip-4.7.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/baresip-4.6.0/.github/workflows/coverage.yml new/baresip-4.7.0/.github/workflows/coverage.yml --- old/baresip-4.6.0/.github/workflows/coverage.yml 2026-03-04 08:20:45.000000000 +0100 +++ new/baresip-4.7.0/.github/workflows/coverage.yml 2026-04-07 07:20:03.000000000 +0200 @@ -78,7 +78,7 @@ - name: coverage check run: | - min_cov="65.0" + min_cov="70.0" mkdir html cov=$(~/.local/bin/gcovr -r . --html-details html/index.html --json-summary | jq .line_percent) echo "Coverage: ${cov}% (min $min_cov%)" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/baresip-4.6.0/CHANGELOG.md new/baresip-4.7.0/CHANGELOG.md --- old/baresip-4.6.0/CHANGELOG.md 2026-03-04 08:20:45.000000000 +0100 +++ new/baresip-4.7.0/CHANGELOG.md 2026-04-07 07:20:03.000000000 +0200 @@ -6,6 +6,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## v4.7.0 - 2026-04-07 + +### What's Changed +* uag: allow registered ua catchall by @sreimers in https://github.com/baresip/baresip/pull/3660 +* bevent: rename bevent_odict_encode() and add to test by @alfredh in https://github.com/baresip/baresip/pull/3665 +* uag: log selected account in uag_find_msg() at level DEBUG by @petercolberg in https://github.com/baresip/baresip/pull/3661 +* modules/selfview: added support for screen rotation by @juha-h in https://github.com/baresip/baresip/pull/3666 +* Revert "call: do not go back to RINGING state if already in EARLY" by @dddaniel in https://github.com/baresip/baresip/pull/3667 +* test: check for real IP-address in test_peerconn by @alfredh in https://github.com/baresip/baresip/pull/3668 +* net,ua: add default network address family fallback by @sreimers in https://github.com/baresip/baresip/pull/3675 +* call: remove unused call_af() by @alfredh in https://github.com/baresip/baresip/pull/3669 +* test: minor increase in test-coverage of command module by @alfredh in https://github.com/baresip/baresip/pull/3670 +* test: add audio_debug() and video_debug() in BUNDLE test by @alfredh in https://github.com/baresip/baresip/pull/3676 +* test: check ua/call print and debug functions by @alfredh in https://github.com/baresip/baresip/pull/3678 +* call: improve emission of hold/resume events by @maximilianfridrich in https://github.com/baresip/baresip/pull/3677 +* ci/coverage: bump minimum coverage to 70% by @alfredh in https://github.com/baresip/baresip/pull/3679 + +### New Contributors +* @petercolberg made their first contribution in https://github.com/baresip/baresip/pull/3661 + +**Full Changelog**: https://github.com/baresip/baresip/compare/v4.6.0...v4.7.0 + + ## v4.6.0 - 2026-03-04 ### What's Changed diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/baresip-4.6.0/CMakeLists.txt new/baresip-4.7.0/CMakeLists.txt --- old/baresip-4.6.0/CMakeLists.txt 2026-03-04 08:20:45.000000000 +0100 +++ new/baresip-4.7.0/CMakeLists.txt 2026-04-07 07:20:03.000000000 +0200 @@ -13,9 +13,9 @@ cmake_minimum_required(VERSION 3.18...4.0) -project(baresip VERSION 4.6.0) +project(baresip VERSION 4.7.0) -set(PROJECT_SOVERSION 26) # bump if ABI breaks +set(PROJECT_SOVERSION 27) # bump if ABI breaks # Pre-release identifier, comment out on a release # Increment for breaking changes (dev2, dev3...) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/baresip-4.6.0/include/baresip.h new/baresip-4.7.0/include/baresip.h --- old/baresip-4.6.0/include/baresip.h 2026-03-04 08:20:45.000000000 +0100 +++ new/baresip-4.7.0/include/baresip.h 2026-04-07 07:20:03.000000000 +0200 @@ -13,7 +13,7 @@ /** Defines the Baresip version string */ -#define BARESIP_VERSION "4.6.0" +#define BARESIP_VERSION "4.7.0" #ifndef NET_MAX_NS @@ -843,6 +843,7 @@ int net_rm_address(struct network *net, const struct sa *ip); bool net_af_enabled(const struct network *net, int af); int net_set_af(struct network *net, int af); +int net_af(const struct network *net); void net_dns_refresh(struct network *net); int net_dns_debug(struct re_printf *pf, const struct network *net); int net_debug(struct re_printf *pf, const struct network *net); @@ -1728,7 +1729,7 @@ * Generic event */ -int odict_encode_bevent(struct odict *od, struct bevent *event); +int bevent_odict_encode(struct odict *od, const struct bevent *event); int event_add_au_jb_stat(struct odict *od_parent, const struct call *call); int bevent_register(bevent_h *eh, void *arg); void bevent_unregister(bevent_h *eh); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/baresip-4.6.0/mk/Doxyfile new/baresip-4.7.0/mk/Doxyfile --- old/baresip-4.6.0/mk/Doxyfile 2026-03-04 08:20:45.000000000 +0100 +++ new/baresip-4.7.0/mk/Doxyfile 2026-04-07 07:20:03.000000000 +0200 @@ -4,7 +4,7 @@ # Project related configuration options #--------------------------------------------------------------------------- PROJECT_NAME = baresip -PROJECT_NUMBER = 4.6.0 +PROJECT_NUMBER = 4.7.0 OUTPUT_DIRECTORY = ../baresip-dox CREATE_SUBDIRS = NO OUTPUT_LANGUAGE = English diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/baresip-4.6.0/modules/ctrl_dbus/ctrl_dbus.c new/baresip-4.7.0/modules/ctrl_dbus/ctrl_dbus.c --- old/baresip-4.6.0/modules/ctrl_dbus/ctrl_dbus.c 2026-03-04 08:20:45.000000000 +0100 +++ new/baresip-4.7.0/modules/ctrl_dbus/ctrl_dbus.c 2026-04-07 07:20:03.000000000 +0200 @@ -253,7 +253,7 @@ pf.vph = print_handler; pf.arg = buf; - err = odict_encode_bevent(od, event); + err = bevent_odict_encode(od, event); if (err) goto out; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/baresip-4.6.0/modules/ctrl_tcp/ctrl_tcp.c new/baresip-4.7.0/modules/ctrl_tcp/ctrl_tcp.c --- old/baresip-4.6.0/modules/ctrl_tcp/ctrl_tcp.c 2026-03-04 08:20:45.000000000 +0100 +++ new/baresip-4.7.0/modules/ctrl_tcp/ctrl_tcp.c 2026-04-07 07:20:03.000000000 +0200 @@ -268,7 +268,7 @@ return; err = odict_entry_add(od, "event", ODICT_BOOL, true); - err |= odict_encode_bevent(od, event); + err |= bevent_odict_encode(od, event); if (err) { warning("ctrl_tcp: failed to encode event (%m)\n", err); goto out; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/baresip-4.6.0/modules/mqtt/publish.c new/baresip-4.7.0/modules/mqtt/publish.c --- old/baresip-4.6.0/modules/mqtt/publish.c 2026-03-04 08:20:45.000000000 +0100 +++ new/baresip-4.7.0/modules/mqtt/publish.c 2026-04-07 07:20:03.000000000 +0200 @@ -30,7 +30,7 @@ if (err) return; - err = odict_encode_bevent(od, event); + err = bevent_odict_encode(od, event); if (err) goto out; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/baresip-4.6.0/modules/selfview/selfview.c new/baresip-4.7.0/modules/selfview/selfview.c --- old/baresip-4.6.0/modules/selfview/selfview.c 2026-03-04 08:20:45.000000000 +0100 +++ new/baresip-4.7.0/modules/selfview/selfview.c 2026-04-07 07:20:03.000000000 +0200 @@ -209,22 +209,42 @@ return 0; mtx_lock(&selfview->lock); - if (!selfview->frame) { - struct vidsz sz; - /* Use size if configured, or else 20% of main window */ - if (selfview_size.w && selfview_size.h) { - sz = selfview_size; - } - else { - sz.w = frame->size.w / 5; - sz.h = frame->size.h / 5; + struct vidsz target_sz; + + /* Use size if configured, or else 20% of main window */ + if (selfview_size.w && selfview_size.h) { + target_sz = selfview_size; + /* If source is portrait but config is landscape + (or vice versa), swap target*/ + bool frame_is_portrait = frame->size.h > frame->size.w; + bool target_is_portrait = target_sz.h > target_sz.w; + if (frame_is_portrait != target_is_portrait) { + uint32_t tmp = target_sz.w; + target_sz.w = target_sz.h; + target_sz.h = tmp; } + } + else { + target_sz.w = frame->size.w / 5; + target_sz.h = frame->size.h / 5; + } - err = vidframe_alloc(&selfview->frame, VID_FMT_YUV420P, &sz); + /* Check if capture resolution has changed (e.g. rotation) */ + if (selfview->frame && (selfview->frame->size.w != target_sz.w || + selfview->frame->size.h != target_sz.h)) { + selfview->frame = mem_deref(selfview->frame); } + + /* Reallocate if necessary */ + if (!selfview->frame) { + err = vidframe_alloc(&selfview->frame, VID_FMT_YUV420P, + &target_sz); + } + if (!err) vidconv(selfview->frame, frame, NULL); + mtx_unlock(&selfview->lock); return err; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/baresip-4.6.0/src/bevent.c new/baresip-4.7.0/src/bevent.c --- old/baresip-4.6.0/src/bevent.c 2026-03-04 08:20:45.000000000 +0100 +++ new/baresip-4.7.0/src/bevent.c 2026-04-07 07:20:03.000000000 +0200 @@ -351,7 +351,7 @@ } -int odict_encode_bevent(struct odict *od, struct bevent *event) +int bevent_odict_encode(struct odict *od, const struct bevent *event) { if (!event) return EINVAL; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/baresip-4.6.0/src/call.c new/baresip-4.7.0/src/call.c --- old/baresip-4.6.0/src/call.c 2026-03-04 08:20:45.000000000 +0100 +++ new/baresip-4.7.0/src/call.c 2026-04-07 07:20:03.000000000 +0200 @@ -54,6 +54,7 @@ struct tmr tmr_dtmf; /**< Timer for incoming DTMF events */ struct tmr tmr_answ; /**< Timer for delayed answer */ struct tmr tmr_reinv; /**< Timer for outgoing re-INVITES */ + bool remote_hold; /**< True if remote has placed us on hold */ time_t time_start; /**< Time when call started */ time_t time_conn; /**< Time when call initiated */ time_t time_stop; /**< Time when call stopped */ @@ -1129,6 +1130,22 @@ /** + * Send a re-INVITE without SDP offer + * + * @param call Call object + * + * @return 0 if success, otherwise errorcode + */ +int call_modify_nosdp(struct call *call) +{ + if (!call || !call->sess) + return EINVAL; + + return sipsess_modify(call->sess, NULL); +} + + +/** * Hangup the call * * @param call Call to hangup @@ -1810,21 +1827,77 @@ } +/* + * Returns true, if a hold or resume event is detected. + * + * If a hold or resume is detected, ev is set to BEVENT_CALL_HOLD, + * or BEVENT_CALL_RESUME. + * + * Detects hold/resume transitions per RFC 6337 section 5.3. Only detect + * transitions in established calls, not during initial call setup. + * + * When remote transitions from sendrecv or recvonly to sendonly or inactive, + * we're placed on hold. + * When remote transitions from sendonly or inactive to sendrecv or recvonly, + * we're resumed. + */ +static bool detect_hold_resume(enum bevent_ev *ev, struct call *call, + enum sdp_dir old_rdir, bool is_offer) +{ + bool emit_event = false; + + if (!ev || !call || call->state != CALL_STATE_ESTABLISHED) + return false; + + const struct sdp_media *m = stream_sdpmedia(audio_strm(call->audio)); + const enum sdp_dir new_rdir = sdp_media_rdir(m); + + bool before_on_hold = (old_rdir == SDP_RECVONLY || + old_rdir == SDP_INACTIVE); + bool now_on_hold = (new_rdir == SDP_RECVONLY || + new_rdir == SDP_INACTIVE); + + /* For offers, detect based on rdir changes. + * For answers, use the tracked remote_hold state because rdir + * may have changed when we created an offer. */ + if (is_offer) { + if (now_on_hold && !before_on_hold) { + *ev = BEVENT_CALL_HOLD; + call->remote_hold = true; + emit_event = true; + } + else if (before_on_hold && !now_on_hold) { + *ev = BEVENT_CALL_RESUME; + call->remote_hold = false; + emit_event = true; + } + } + else if (call->remote_hold && !now_on_hold) { + /* Only detect resume events for answers, not hold events. */ + *ev = BEVENT_CALL_RESUME; + call->remote_hold = false; + emit_event = true; + } + + return emit_event; +} + + static int sipsess_offer_handler(struct mbuf **descp, const struct sip_msg *msg, void *arg) { const bool got_offer = (0 != mbuf_get_left(msg->mb)); struct call *call = arg; - struct sdp_media *vmedia = NULL; enum sdp_dir ardir, vrdir; int err; MAGIC_CHECK(call); if (got_offer) { + enum bevent_ev hold_ev; const struct sdp_media *m = stream_sdpmedia(audio_strm(call->audio)); - bool aurx = sdp_media_dir(m) & SDP_SENDONLY; + const enum sdp_dir old_rdir = sdp_media_rdir(m); call->got_offer = true; /* Decode SDP Offer */ @@ -1835,10 +1908,8 @@ return err; } - if (aurx && !(sdp_media_dir(m) & SDP_SENDONLY)) - bevent_call_emit(BEVENT_CALL_HOLD, call, ""); - else if (!aurx && sdp_media_dir(m) & SDP_SENDONLY) - bevent_call_emit(BEVENT_CALL_RESUME, call, ""); + if (detect_hold_resume(&hold_ev, call, old_rdir, true)) + bevent_call_emit(hold_ev, call, ""); err = update_media(call); if (err) { @@ -1848,21 +1919,12 @@ } } - ardir = sdp_media_rdir( - stream_sdpmedia(audio_strm(call_audio(call)))); - - vmedia = stream_sdpmedia(video_strm(call_video(call))); - if (sdp_media_rport(vmedia) == 0 || - list_head(sdp_media_format_lst(vmedia, false)) == 0) { - vrdir = SDP_INACTIVE; - } - else { - vrdir = sdp_media_rdir(vmedia); - } + ardir = sdp_media_rdir(stream_sdpmedia(audio_strm(call_audio(call)))); + vrdir = sdp_media_rdir(stream_sdpmedia(video_strm(call_video(call)))); info("call: got %r%s audio-video: %s-%s\n", &msg->met, - got_offer ? " (SDP Offer)" : "", sdp_dir_name(ardir), - sdp_dir_name(vrdir)); + got_offer ? " (SDP Offer)" : "", + sdp_dir_name(ardir), sdp_dir_name(vrdir)); /* Encode SDP Answer */ err = sdp_encode(descp, call->sdp, !got_offer); @@ -1880,6 +1942,9 @@ { struct call *call = arg; int err; + enum bevent_ev hold_ev; + const struct sdp_media *m = stream_sdpmedia(audio_strm(call->audio)); + const enum sdp_dir old_rdir = sdp_media_rdir(m); MAGIC_CHECK(call); @@ -1904,9 +1969,11 @@ return err; } + if (detect_hold_resume(&hold_ev, call, old_rdir, false)) + bevent_call_emit(hold_ev, call, ""); + /* note: before update_media */ if (call->config_avt.bundle) { - bundle_sdp_decode(call->sdp, &call->streaml); } @@ -2493,8 +2560,7 @@ switch (msg->scode) { case 180: - if (call_state(call) != CALL_STATE_EARLY) - set_state(call, CALL_STATE_RINGING); + set_state(call, CALL_STATE_RINGING); break; case 183: @@ -2509,7 +2575,7 @@ call->peer_uri); mem_deref(call); } - else if (call_state(call) != CALL_STATE_EARLY) { + else { call_stream_stop(call); call_event_handler(call, CALL_EVENT_RINGING, "%s", call->peer_uri); @@ -2984,12 +3050,6 @@ } -int call_af(const struct call *call) -{ - return call ? call->af : AF_UNSPEC; -} - - /** * Get the SIP status code for the outgoing call * diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/baresip-4.6.0/src/core.h new/baresip-4.7.0/src/core.h --- old/baresip-4.6.0/src/core.h 2026-03-04 08:20:45.000000000 +0100 +++ new/baresip-4.7.0/src/core.h 2026-04-07 07:20:03.000000000 +0200 @@ -178,11 +178,11 @@ int call_sdp_get(const struct call *call, struct mbuf **descp, bool offer); int call_info(struct re_printf *pf, const struct call *call); int call_reset_transp(struct call *call, const struct sa *laddr); -int call_af(const struct call *call); void call_set_xrtpstat(struct call *call); void call_set_custom_hdrs(struct call *call, const struct list *hdrs); const struct sa *call_laddr(const struct call *call); int call_streams_alloc(struct call *call); +int call_modify_nosdp(struct call *call); /* * Custom headers @@ -364,8 +364,7 @@ struct ua; -void ua_printf(const struct ua *ua, const char *fmt, ...); - +int ua_printf(struct re_printf *pf, const struct ua *ua); int ua_print_allowed(struct re_printf *pf, const struct ua *ua); int ua_print_require(struct re_printf *pf, const struct ua *ua); struct call *ua_find_call_onhold(const struct ua *ua); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/baresip-4.6.0/src/net.c new/baresip-4.7.0/src/net.c --- old/baresip-4.6.0/src/net.c 2026-03-04 08:20:45.000000000 +0100 +++ new/baresip-4.7.0/src/net.c 2026-04-07 07:20:03.000000000 +0200 @@ -590,6 +590,19 @@ } +/** + * Get the enabled address family (AF) + * + * @param net Network instance + * + * @return AF_UNSPEC, AF_INET or AF_INET6 + */ +int net_af(const struct network *net) +{ + return net ? net->cfg.af : AF_UNSPEC; +} + + bool net_ifaddr_filter(const struct network *net, const char *ifname, const struct sa *sa) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/baresip-4.6.0/src/reg.c new/baresip-4.7.0/src/reg.c --- old/baresip-4.6.0/src/reg.c 2026-03-04 08:20:45.000000000 +0100 +++ new/baresip-4.7.0/src/reg.c 2026-04-07 07:20:03.000000000 +0200 @@ -139,12 +139,12 @@ reg->af = sipmsg_af(msg); if (msg->scode != reg->scode && reg->regint) { - ua_printf(reg->ua, "(prio %u) {%d/%s/%s} %u %r (%s)" - " [%u binding%s]\n", - prio, reg->id, sip_transp_name(msg->tp), - af_name(reg->af), msg->scode, &msg->reason, - reg->srv, n_bindings, - 1==n_bindings?"":"s"); + info("%H: (prio %u) {%d/%s/%s} %u %r (%s)" + " [%u binding%s]\n", ua_printf, reg->ua, + prio, reg->id, sip_transp_name(msg->tp), + af_name(reg->af), msg->scode, &msg->reason, + reg->srv, n_bindings, + 1==n_bindings?"":"s"); } reg->scode = msg->scode; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/baresip-4.6.0/src/ua.c new/baresip-4.7.0/src/ua.c --- old/baresip-4.6.0/src/ua.c 2026-03-04 08:20:45.000000000 +0100 +++ new/baresip-4.7.0/src/ua.c 2026-04-07 07:20:03.000000000 +0200 @@ -70,16 +70,13 @@ } -void ua_printf(const struct ua *ua, const char *fmt, ...) +int ua_printf(struct re_printf *pf, const struct ua *ua) { - va_list ap; - if (!ua) - return; + return 0; - va_start(ap, fmt); - info("%r@%r: %v", &ua->acc->luri.user, &ua->acc->luri.host, fmt, &ap); - va_end(ap); + return re_hprintf(pf, "%r@%r", &ua->acc->luri.user, + &ua->acc->luri.host); } @@ -561,17 +558,17 @@ break; case CALL_EVENT_PROGRESS: - ua_printf(ua, "Call in-progress: %s\n", peeruri); + info("%H: Call in-progress: %s\n", ua_printf, ua, peeruri); bevent_call_emit(BEVENT_CALL_PROGRESS, call, "%s", peeruri); break; case CALL_EVENT_ANSWERED: - ua_printf(ua, "Call answered: %s\n", peeruri); + info("%H: Call answered: %s\n", ua_printf, ua, peeruri); bevent_call_emit(BEVENT_CALL_ANSWERED, call, "%s", peeruri); break; case CALL_EVENT_ESTABLISHED: - ua_printf(ua, "Call established: %s\n", peeruri); + info("%H: Call established: %s\n", ua_printf, ua, peeruri); bevent_call_emit(BEVENT_CALL_ESTABLISHED, call, "%s", peeruri); break; @@ -901,6 +898,9 @@ laddr = ua_regladdr(ua); af = sa_af(laddr); } + else { + af = net_af(net); + } if (af != AF_UNSPEC && !net_af_enabled(net, af)) { warning("ua: address family %s not supported\n", @@ -1228,20 +1228,21 @@ goto out; if (ua->acc->sipnat) { - ua_printf(ua, "Using sipnat: '%s'\n", ua->acc->sipnat); + info("%H: Using sipnat: '%s'\n", ua_printf, ua, + ua->acc->sipnat); } if (ua->acc->mnat) { - ua_printf(ua, "Using medianat '%s'\n", - ua->acc->mnat->id); + info("%H: Using medianat '%s'\n", ua_printf, ua, + ua->acc->mnat->id); if (0 == str_casecmp(ua->acc->mnat->id, "ice")) ua_add_extension(ua, "ice"); } if (ua->acc->menc) { - ua_printf(ua, "Using media encryption '%s'\n", - ua->acc->menc->id); + info("%H: Using media encryption '%s'\n", ua_printf, ua, + ua->acc->menc->id); } if (ua->acc->cert) { @@ -1561,7 +1562,7 @@ /* put established call on-hold */ pcall = ua_find_call_state(ua, CALL_STATE_ESTABLISHED); if (pcall) { - ua_printf(ua, "putting call with '%s' on hold\n", + info("%H: putting call with '%s' on hold\n", ua_printf, ua, call_peeruri(pcall)); err = call_hold(pcall, true); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/baresip-4.6.0/src/uag.c new/baresip-4.7.0/src/uag.c --- old/baresip-4.6.0/src/uag.c 2026-03-04 08:20:45.000000000 +0100 +++ new/baresip-4.7.0/src/uag.c 2026-04-07 07:20:03.000000000 +0200 @@ -943,9 +943,12 @@ continue; if (0 == pl_strcasecmp(cuser, ua_local_cuser(ua))) { - ua_printf(ua, "selected for %r\n", cuser); + debug("%H: selected for %r\n", ua_printf, ua, cuser); return ua; } + + if (!uaf && ua_catchall(ua)) + uaf = ua; } /* match for peer-to-peer calls (only un-registered accounts) */ @@ -963,7 +966,8 @@ continue; if (0 == pl_casecmp(cuser, &acc->luri.user)) { - ua_printf(ua, "account match for %r\n", cuser); + debug("%H: account match for %r\n", ua_printf, ua, + cuser); return ua; } @@ -972,7 +976,7 @@ } if (uaf) - ua_printf(uaf, "selected fallback\n"); + debug("%H: selected fallback\n", ua_printf, uaf); return uaf; } @@ -1134,7 +1138,7 @@ } if (ret) { - ua_printf(ret, "selected for request\n"); + info("%H: selected for request\n", ua_printf, ret); } else { /* Ok, seems that matching account is missing. */ @@ -1143,7 +1147,7 @@ } ret = uag.ual.head->data; - ua_printf(ret, "fallback selection\n"); + info("%H: fallback selection\n", ua_printf, ret); } out: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/baresip-4.6.0/test/bevent.c new/baresip-4.7.0/test/bevent.c --- old/baresip-4.6.0/test/bevent.c 2026-03-04 08:20:45.000000000 +0100 +++ new/baresip-4.7.0/test/bevent.c 2026-04-07 07:20:03.000000000 +0200 @@ -58,7 +58,7 @@ struct odict *od = NULL; int err = odict_alloc(&od, 8); - err |= odict_encode_bevent(od, event); + err |= bevent_odict_encode(od, event); if (err) { warning("bevent: encode failed: %m\n", err); bevent_set_error(event, err); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/baresip-4.6.0/test/call.c new/baresip-4.7.0/test/call.c --- old/baresip-4.6.0/test/call.c 2026-03-04 08:20:45.000000000 +0100 +++ new/baresip-4.7.0/test/call.c 2026-04-07 07:20:03.000000000 +0200 @@ -2034,6 +2034,7 @@ struct call *callv[2]; struct audio *audiov[2]; struct video *videov[2]; + char *debug_str = NULL; unsigned i; int err; @@ -2156,11 +2157,27 @@ ASSERT_TRUE(stream_is_secure(video_strm(videov[1]))); } + for (i=0; i<2; i++) { + + err = re_sdprintf(&debug_str, "%H", + audio_debug, call_audio(callv[i])); + TEST_ERR(err); + ASSERT_TRUE(str_isset(debug_str)); + debug_str = mem_deref(debug_str); + + err = re_sdprintf(&debug_str, "%H", + video_debug, call_video(callv[i])); + TEST_ERR(err); + ASSERT_TRUE(str_isset(debug_str)); + debug_str = mem_deref(debug_str); + } + out: fixture_close(f); mem_deref(sdp); mem_deref(vidisp); + mem_deref(debug_str); module_unload("fakevideo"); mock_vidcodec_unregister(); @@ -2302,6 +2319,17 @@ } +/* + * Test: Call hold and resume with various media directions. + * + * - A calls B with sendrecv, verifies audio is enabled and bi-directional. + * - A puts B on hold (changes to sendonly), verifies B receives ON_HOLD event. + * - A resumes call (back to sendrecv), verifies B receives RESUME event. + * - A makes new call with sendonly direction, repeats hold/resume steps. + * - B puts A on hold (changes to sendonly), verifies A receives ON_HOLD event. + * - B sets media to inactive, then resumes (changes to sendrecv), + * verifies A receives RESUME event. + */ static int test_call_hold_resume_base(bool tcp) { struct fixture fix, *f = &fix; @@ -2560,6 +2588,243 @@ } +/* + * Test: B holds A, then B sends a re-INVITE without SDP. + * A responds with 200 OK containing an SDP offer (sendrecv). + * B sends the SDP answer in the ACK with sendrecv, resuming the call. + * Expectation: A emits a BEVENT_CALL_RESUME after receiving the ACK. + */ +static int test_call_hold_resume_nosdp(void) +{ + struct fixture fix, *f = &fix; + struct cancel_rule *cr; + int err = 0; + + fixture_init(f); + cancel_rule_new(BEVENT_CALL_RTPESTAB, f->a.ua, 0, 0, 1); + cr->n_audio_estab = 1; + cancel_rule_and(BEVENT_CALL_RTPESTAB, f->b.ua, 1, 0, 1); + cr->n_audio_estab = 1; + + err = module_load(".", "ausine"); + TEST_ERR(err); + err = module_load(".", "aufile"); + TEST_ERR(err); + + f->behaviour = BEHAVIOUR_ANSWER; + f->estab_action = ACTION_NOTHING; + + /* Make a call from A to B */ + err = ua_connect(f->a.ua, 0, NULL, f->buri, VIDMODE_ON); + TEST_ERR(err); + + /* wait for RTP audio */ + err = re_main_timeout(10000); + TEST_ERR(err); + TEST_ERR(fix.err); + + /* verify sendrecv on both sides */ + struct sdp_media *m; + m = stream_sdpmedia(audio_strm(call_audio(ua_call(f->a.ua)))); + ASSERT_EQ(SDP_SENDRECV, sdp_media_ldir(m)); + ASSERT_EQ(SDP_SENDRECV, sdp_media_rdir(m)); + + m = stream_sdpmedia(audio_strm(call_audio(ua_call(f->b.ua)))); + ASSERT_EQ(SDP_SENDRECV, sdp_media_ldir(m)); + ASSERT_EQ(SDP_SENDRECV, sdp_media_rdir(m)); + + /* B puts A on hold */ + cancel_rule_new(BEVENT_CALL_REMOTE_SDP, f->a.ua, 0, 0, 1); + cr->prm = "offer"; + cancel_rule_and(BEVENT_CALL_REMOTE_SDP, f->b.ua, 1, 0, 1); + cr->prm = "answer"; + + err = call_hold(ua_call(f->b.ua), true); + TEST_ERR(err); + err = re_main_timeout(10000); + TEST_ERR(err); + TEST_ERR(fix.err); + + err = agent_wait_for_ack(&f->a, -1, -1, 1); + TEST_ERR(err); + + ASSERT_EQ(1, f->a.n_hold_cnt); + ASSERT_EQ(0, f->b.n_hold_cnt); + + m = stream_sdpmedia(audio_strm(call_audio(ua_call(f->b.ua)))); + ASSERT_EQ(SDP_SENDONLY, sdp_media_ldir(m)); + ASSERT_EQ(SDP_SENDONLY, sdp_media_rdir(m)); + + m = stream_sdpmedia(audio_strm(call_audio(ua_call(f->a.ua)))); + ASSERT_EQ(SDP_SENDRECV, sdp_media_ldir(m)); + ASSERT_EQ(SDP_RECVONLY, sdp_media_rdir(m)); + ASSERT_TRUE(!call_ack_pending(ua_call(f->a.ua))); + + /* B resumes by: setting media direction to sendrecv, then + * sending a re-INVITE without SDP. + * A will create an SDP offer in 200 OK (sendrecv). + * B will send the SDP answer in the ACK (sendrecv). */ + cancel_rule_new(BEVENT_CALL_REMOTE_SDP, f->b.ua, 1, 0, 1); + cr->prm = "offer"; + cancel_rule_and(BEVENT_CALL_REMOTE_SDP, f->a.ua, 0, 0, 1); + cr->prm = "answer"; + + call_set_media_direction(ua_call(f->b.ua), SDP_SENDRECV, SDP_SENDRECV); + + err = call_modify_nosdp(ua_call(f->b.ua)); + TEST_ERR(err); + err = re_main_timeout(10000); + TEST_ERR(err); + TEST_ERR(fix.err); + + err = agent_wait_for_ack(&f->a, -1, -1, 1); + TEST_ERR(err); + + /* A should have emitted resume after receiving the SDP answer + * in the ACK (stream goes from recvonly back to sendrecv) */ + ASSERT_EQ(1, f->a.n_resume_cnt); + + m = stream_sdpmedia(audio_strm(call_audio(ua_call(f->a.ua)))); + ASSERT_EQ(SDP_SENDRECV, sdp_media_ldir(m)); + ASSERT_EQ(SDP_SENDRECV, sdp_media_rdir(m)); + + m = stream_sdpmedia(audio_strm(call_audio(ua_call(f->b.ua)))); + ASSERT_EQ(SDP_SENDRECV, sdp_media_ldir(m)); + ASSERT_EQ(SDP_SENDRECV, sdp_media_rdir(m)); + + out: + if (err) + failure_debug(f, false); + + fixture_close(f); + module_unload("aufile"); + module_unload("ausine"); + + return err; +} + + +/* + * Test: A calls B (sendrecv), then A puts B on hold by sending a re-INVITE + * with inactive SDP direction (B responds with inactive). + * Expectation: B emits ON_HOLD event, A does not. + * Then A resumes by sending a re-INVITE with sendrecv. + * Expectation: B emits RESUME event, A does not. + * + * This tests the scenario where the caller (A) puts the callee (B) on hold + * using inactive direction, and verifies that only B receives hold/resume + * events (since A is the one initiating the hold). + */ +static int test_call_hold_resume_inactive(void) +{ + struct fixture fix, *f = &fix; + struct cancel_rule *cr; + int err = 0; + + fixture_init(f); + cancel_rule_new(BEVENT_CALL_RTPESTAB, f->a.ua, 0, 0, 1); + cr->n_audio_estab = 1; + cancel_rule_and(BEVENT_CALL_RTPESTAB, f->b.ua, 1, 0, 1); + cr->n_audio_estab = 1; + + err = module_load(".", "ausine"); + TEST_ERR(err); + err = module_load(".", "aufile"); + TEST_ERR(err); + + f->behaviour = BEHAVIOUR_ANSWER; + f->estab_action = ACTION_NOTHING; + + /* Make a call from A to B */ + err = ua_connect(f->a.ua, 0, NULL, f->buri, VIDMODE_ON); + TEST_ERR(err); + + /* wait for RTP audio */ + err = re_main_timeout(10000); + TEST_ERR(err); + TEST_ERR(fix.err); + + /* verify that audio was enabled and bi-directional */ + ASSERT_TRUE(call_has_audio(ua_call(f->a.ua))); + ASSERT_TRUE(call_has_audio(ua_call(f->b.ua))); + + struct sdp_media *m; + m = stream_sdpmedia(audio_strm(call_audio(ua_call(f->a.ua)))); + ASSERT_EQ(SDP_SENDRECV, sdp_media_ldir(m)); + ASSERT_EQ(SDP_SENDRECV, sdp_media_rdir(m)); + + m = stream_sdpmedia(audio_strm(call_audio(ua_call(f->b.ua)))); + ASSERT_EQ(SDP_SENDRECV, sdp_media_ldir(m)); + ASSERT_EQ(SDP_SENDRECV, sdp_media_rdir(m)); + + /* A puts B on hold with inactive direction */ + cancel_rule_new(BEVENT_CALL_REMOTE_SDP, f->b.ua, 1, 0, 1); + cr->prm = "offer"; + cancel_rule_and(BEVENT_CALL_REMOTE_SDP, f->a.ua, 0, 0, 1); + cr->prm = "answer"; + + /* Set media direction to inactive and send re-INVITE */ + call_set_media_direction(ua_call(f->a.ua), SDP_INACTIVE, SDP_INACTIVE); + err = call_modify(ua_call(f->a.ua)); + TEST_ERR(err); + err = re_main_timeout(10000); + TEST_ERR(err); + TEST_ERR(fix.err); + + err = agent_wait_for_ack(&f->b, -1, -1, 1); + TEST_ERR(err); + + /* Verify: B should receive ON_HOLD event, A should not */ + ASSERT_EQ(0, f->a.n_hold_cnt); + ASSERT_EQ(1, f->b.n_hold_cnt); + + /* Verify SDP directions */ + m = stream_sdpmedia(audio_strm(call_audio(ua_call(f->a.ua)))); + ASSERT_EQ(SDP_INACTIVE, sdp_media_ldir(m)); + ASSERT_EQ(SDP_INACTIVE, sdp_media_rdir(m)); + + m = stream_sdpmedia(audio_strm(call_audio(ua_call(f->b.ua)))); + ASSERT_EQ(SDP_SENDRECV, sdp_media_ldir(m)); + ASSERT_EQ(SDP_INACTIVE, sdp_media_rdir(m)); + ASSERT_TRUE(!call_ack_pending(ua_call(f->b.ua))); + + /* A resumes the call with sendrecv */ + call_set_media_direction(ua_call(f->a.ua), SDP_SENDRECV, SDP_SENDRECV); + err = call_modify(ua_call(f->a.ua)); + TEST_ERR(err); + tmr_start(&f->b.tmr_ack, 1, check_ack, &f->b); + err = re_main_timeout(10000); + TEST_ERR(err); + + err = agent_wait_for_ack(&f->b, -1, -1, 1); + TEST_ERR(err); + + /* Verify: B should receive RESUME event, A should not */ + ASSERT_EQ(0, f->a.n_resume_cnt); + ASSERT_EQ(1, f->b.n_resume_cnt); + + /* Verify SDP directions are back to sendrecv */ + m = stream_sdpmedia(audio_strm(call_audio(ua_call(f->a.ua)))); + ASSERT_EQ(SDP_SENDRECV, sdp_media_ldir(m)); + ASSERT_EQ(SDP_SENDRECV, sdp_media_rdir(m)); + + m = stream_sdpmedia(audio_strm(call_audio(ua_call(f->b.ua)))); + ASSERT_EQ(SDP_SENDRECV, sdp_media_ldir(m)); + ASSERT_EQ(SDP_SENDRECV, sdp_media_rdir(m)); + ASSERT_TRUE(!call_ack_pending(ua_call(f->b.ua))); + + out: + if (err) + failure_debug(f, false); + + fixture_close(f); + module_unload("aufile"); + module_unload("ausine"); + + return err; +} + + int test_call_hold_resume(void) { int err; @@ -2570,6 +2835,12 @@ err = test_call_hold_resume_base(true); TEST_ERR(err); + err = test_call_hold_resume_nosdp(); + TEST_ERR(err); + + err = test_call_hold_resume_inactive(); + TEST_ERR(err); + out: return err; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/baresip-4.6.0/test/call_fixture.c new/baresip-4.7.0/test/call_fixture.c --- old/baresip-4.6.0/test/call_fixture.c 2026-03-04 08:20:45.000000000 +0100 +++ new/baresip-4.7.0/test/call_fixture.c 2026-04-07 07:20:03.000000000 +0200 @@ -27,6 +27,26 @@ } +static int check_bevent(const struct bevent *event) +{ + struct odict *od = NULL; + enum { HASH_SIZE = 16 }; + + int err = odict_alloc(&od, HASH_SIZE); + if (err) + return err; + + err = bevent_odict_encode(od, event); + TEST_ERR(err); + + ASSERT_TRUE(odict_count(od, true) >= 1); + + out: + mem_deref(od); + return err; +} + + static void event_handler(enum bevent_ev ev, struct bevent *event, void *arg) { struct fixture *f = arg; @@ -48,6 +68,9 @@ ASSERT_TRUE(f != NULL); ASSERT_EQ(MAGIC, f->magic); + err = check_bevent(event); + TEST_ERR(err); + if (ev == BEVENT_CREATE) return; @@ -407,8 +430,42 @@ } +static int check_ua_debug(const struct ua *ua) +{ + if (!ua) + return 0; + + char *debug_str = NULL; + int err = re_sdprintf(&debug_str, "%H", ua_print_calls, ua); + TEST_ERR(err); + ASSERT_TRUE(str_isset(debug_str)); + debug_str = mem_deref(debug_str); + + const struct call *call = ua_call(ua); + if (call) { + err = re_sdprintf(&debug_str, "%H%H", + call_debug, call, + call_status, call); + TEST_ERR(err); + ASSERT_TRUE(str_isset(debug_str)); + } + + out: + mem_deref(debug_str); + + return err; +} + + void fixture_close(struct fixture *f) { + if (!f) + return; + + check_ua_debug(f->a.ua); + check_ua_debug(f->b.ua); + check_ua_debug(f->c.ua); + tmr_cancel(&f->a.tmr_ack); tmr_cancel(&f->b.tmr_ack); tmr_cancel(&f->c.tmr_ack); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/baresip-4.6.0/test/cmd.c new/baresip-4.7.0/test/cmd.c --- old/baresip-4.6.0/test/cmd.c 2026-03-04 08:20:45.000000000 +0100 +++ new/baresip-4.7.0/test/cmd.c 2026-04-07 07:20:03.000000000 +0200 @@ -31,7 +31,10 @@ static const struct cmd cmdv[] = { - {NULL, '@', 0, "Test command", cmd_test}, + {NULL, '@', 0, "Test command", cmd_test}, + {NULL, ' ', 0, "Test command", cmd_test}, + {NULL, '\n', 0, "Test command", cmd_test}, + {NULL, KEYCODE_ESC, 0, "Test command", cmd_test}, }; @@ -103,7 +106,8 @@ static const struct cmd longcmdv[] = { - { "test", 0, 0, "Test Command", long_handler}, + { "test", 0, CMD_PRM, "Test Command", long_handler}, + { "test2", 0, CMD_PRM, "Test Command", long_handler}, }; @@ -112,7 +116,7 @@ struct commands *commands = NULL; struct test test; const struct cmd *cmd; - static const char *input_str = "/test 123\n"; + static const char *input_str = "\t/test 123\n"; struct cmd_ctx *ctx = NULL; size_t i; int err; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/baresip-4.6.0/test/peerconn.c new/baresip-4.7.0/test/peerconn.c --- old/baresip-4.6.0/test/peerconn.c 2026-03-04 08:20:45.000000000 +0100 +++ new/baresip-4.7.0/test/peerconn.c 2026-04-07 07:20:03.000000000 +0200 @@ -474,10 +474,30 @@ } +static bool if_handler(const char *ifname, const struct sa *sa, void *arg) +{ + bool *have_real_ip = arg; + (void)ifname; + + if (sa_is_loopback(sa) || sa_is_linklocal(sa)) + return false; + + *have_real_ip = true; + + return true; +} + + int test_peerconn(void) { + bool have_real_ip = false; int err; + /* skip the test if no other interface than loopback is available */ + net_laddr_apply(baresip_network(), if_handler, &have_real_ip); + if (!have_real_ip) + return 0; + if (conf_config()->avt.rxmode == RECEIVE_MODE_THREAD) return 0;
