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;
 

Reply via email to