diago icon indicating copy to clipboard operation
diago copied to clipboard

Hold/Unhold call

Open secca37-oss opened this issue 3 months ago • 1 comments

There are no Hold/Unhold functions in the current Diago release. I suggest adding functions based on RFC6337.

My version of the functions is below. For the unhold function, I had trouble restoring the IP address from the c header of the original SDP request. Maybe you can suggest a better way to get the IP address in SDP.

func (d *DialogClientSession) Hold(ctx context.Context, dig sipgo.DigestAuth) error {
	c := d.UA.Client
	newsdp := d.DialogMedia.Media().MediaSession()
	newsdp.Mode = sdp.ModeSendonly
	newsdp.ExternalIP = net.ParseIP("0.0.0.0")

	req := sip.NewRequest(sip.INVITE, d.InviteRequest.Contact().Address)
	req.AppendHeader(d.InviteRequest.Contact())
	req.AppendHeader(sip.NewHeader("Content-Type", "application/sdp"))
	req.SetBody(newsdp.LocalSDP())

	res, err := d.Do(ctx, req)
	if err != nil {
		return err
	}

	var resDig *sip.Response
	if res.StatusCode == sip.StatusProxyAuthRequired {
		resDig, err = c.DoDigestAuth(ctx, req, res, dig)
		if err != nil {
			return err
		}
	}
	err = d.ReadRequest(req, nil)
	if err != nil {
		return err
	}

	if !resDig.IsSuccess() {
		return sipgo.ErrDialogResponse{
			Res: resDig,
		}
	}
	return d.Ack(ctx)
}

func (d *DialogClientSession) UnHold(ctx context.Context, dig sipgo.DigestAuth) error {

	//restore original IP from c-field in initial SDP
	inreq := d.InviteRequest.Body()
	sd := sdp.SessionDescription{}
	if err := sdp.Unmarshal(inreq, &sd); err != nil {
		return err
	}
	cinfo, err := sd.ConnectionInformation()
	if err != nil {
		return err
	}

	c := d.UA.Client
	newsdp := d.DialogMedia.Media().MediaSession()
	newsdp.Mode = sdp.ModeSendrecv
	newsdp.ExternalIP = cinfo.IP

	req := sip.NewRequest(sip.INVITE, d.InviteRequest.Contact().Address)
	req.AppendHeader(d.InviteRequest.Contact())
	req.AppendHeader(sip.NewHeader("Content-Type", "application/sdp"))
	req.SetBody(newsdp.LocalSDP())

	res, err := d.Do(ctx, req)
	if err != nil {
		return err
	}

	var resDig *sip.Response
	if res.StatusCode == sip.StatusProxyAuthRequired {
		resDig, err = c.DoDigestAuth(ctx, req, res, dig)
		if err != nil {
			return err
		}
	}
	err = d.ReadRequest(req, nil)
	if err != nil {
		return err
	}

	if !resDig.IsSuccess() {
		return sipgo.ErrDialogResponse{
			Res: resDig,
		}
	}
	return d.Ack(ctx)
}

secca37-oss avatar Aug 26 '25 09:08 secca37-oss

Bigger one. But I am glad to see that you can do this without changing lib?

I will keep eye on to add this. Above solution you may have some races, but not to now discuss this, feature is definetely on my roadmap to add.

emiago avatar Aug 30 '25 20:08 emiago