<div dir="ltr"><div dir="ltr">On Wed, Nov 9, 2022 at 11:38 AM <<a href="mailto:asterisk@phreaknet.org">asterisk@phreaknet.org</a>> wrote:<br></div><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">On 10/21/2022 7:53 AM, Joshua C. Colp wrote:<br>
> On Fri, Oct 21, 2022 at 8:29 AM <<a href="mailto:asterisk@phreaknet.org" target="_blank">asterisk@phreaknet.org</a> <br>
> <mailto:<a href="mailto:asterisk@phreaknet.org" target="_blank">asterisk@phreaknet.org</a>>> wrote:<br>
><br>
> On 10/21/2022 5:34 AM, Joshua C. Colp wrote:<br>
> > On Thu, Oct 20, 2022 at 8:23 PM <<a href="mailto:asterisk@phreaknet.org" target="_blank">asterisk@phreaknet.org</a><br>
> <mailto:<a href="mailto:asterisk@phreaknet.org" target="_blank">asterisk@phreaknet.org</a>><br>
> > <mailto:<a href="mailto:asterisk@phreaknet.org" target="_blank">asterisk@phreaknet.org</a> <mailto:<a href="mailto:asterisk@phreaknet.org" target="_blank">asterisk@phreaknet.org</a>>>><br>
> wrote:<br>
> ><br>
> > Hi, all,<br>
> ><br>
> > Something I have been working on recently is adding<br>
> support to<br>
> > PJSIP for device feature key synchronization (the<br>
> as-feature-event<br>
> > Broadworks spec that many common IP phones, e.g. Polycom<br>
> support) to<br>
> > control server-side features from endpoints. It's using the<br>
> PJSIP<br>
> > pub/sub capabilities; I had to add the ability to execute a<br>
> custom<br>
> > module callback when a SUBSCRIBE is refreshed, but with that<br>
> > addition,<br>
> > it works as it's supposed to.<br>
> ><br>
> ><br>
> > You should further elaborate on all of the server-side features you<br>
> > expect to implement, if it extends beyond DND.<br>
><br>
> The other one is Call Forwarding (Always/Busy/No Answer, with<br>
> number of<br>
> rings)<br>
><br>
> ><br>
> > I wanted to solicit some input on what an ideal way of<br>
> triggering<br>
> > updates should be. Currently we have hints, which actually work<br>
> > reasonably all right for Do Not Disturb, which is simply a<br>
> boolean<br>
> > on/off, easily represented with a hint and custom device<br>
> state for<br>
> > DND.<br>
> > The PJSIP module emits an AMI event, the user can process it and<br>
> > change<br>
> > the device state if needed, which will trigger a NOTIFY to<br>
> go out<br>
> > to the<br>
> > endpoint.<br>
> ><br>
> ><br>
> > What user? An outside AMI application? An internal consumer in<br>
> Asterisk?<br>
><br>
> The administrator of the Asterisk system, who can add AMI logic to<br>
> receive the event and then do something with it.<br>
> The reason this is needed is the phone isn't turning DND on directly,<br>
> for example. It's merely a request. The server can decide to not<br>
> allow<br>
> it, for example if that phone isn't allowed to toggle DND. The server<br>
> will process it, and send it the updated status. This usually<br>
> reflects<br>
> what the phone wanted, but not necessarily.<br>
> (I've elaborated on this more below)<br>
><br>
><br>
> Okay, that's not the administrator of an Asterisk system. That's a <br>
> developer using Asterisk who may be the administrator. Those are two <br>
> separate things. As soon as you bring AMI into the mix then it's <br>
> making a solution using Asterisk. That's not a wrong thing, but it's <br>
> important to be clear in the audience. Many average users of Asterisk <br>
> doesn't know or care about AMI, 'nor do they code for it. They may use <br>
> additional solutions that utilize AMI but they themselves haven't the <br>
> foggiest of the details.<br>
><br>
><br>
> ><br>
> > This sort of came up about 12 years ago[1]. The actual SIP stuff<br>
> > is not<br>
> > complicated; it's the user interface to it that requires more<br>
> > thought.<br>
> > For call forwarding, there are more moving pieces and abusing<br>
> > hints/custom device state for that is super clunky. You can't<br>
> > communicate the call forwarding target, # of rings, etc. in<br>
> a device<br>
> > state, so additional hints are then needed for that. It<br>
> works but<br>
> > it's<br>
> > super clunky and I don't think this is a great pipeline.<br>
> ><br>
> ><br>
> > Okay, so this covers call forwarding as well.<br>
> Yes.<br>
> ><br>
> ><br>
> > I'm wondering if people have thoughts on what an ideal mechanism<br>
> > would<br>
> > be for users, once they process a request to enable/disable<br>
> a feature<br>
> > from the phone, to communicate that to the PJSIP module. The<br>
> problem<br>
> > with abusing hints, especially for call forwarding, is it's<br>
> not a<br>
> > good<br>
> > way to communicate details into the module. One option<br>
> perhaps is to<br>
> > have dialplan extensions, setup in a manner similar to use with<br>
> > EVAL_EXTEN, where it returns the current value needed, as any<br>
> > relevant<br>
> > function, DB, ODBC, CURL, custom function, etc. could be used to<br>
> > retrieve the current feature value. The clunky part is more<br>
> > signaling to<br>
> > the PJSIP module that it needs to send the phone the updated<br>
> > status (by<br>
> > checking those extensions, for example). The device state<br>
> callback<br>
> > happens to be convenient for this kind of signaling but not<br>
> really<br>
> > appropriate here. It would be better to push the info into<br>
> the module<br>
> > directly rather than the signaling it and making it retrieve the<br>
> > updated<br>
> > data in some arbitrary way.<br>
> ><br>
> > So with this in mind, I'm currently leaning in the direction<br>
> of a<br>
> > dialplan function/AMI action that could be used to set the<br>
> > appropriate<br>
> > info for a subscription, which would trigger the NOTIFY, and<br>
> then<br>
> > nothing would actually need to be added to the dialplan at all<br>
> > (inasmuch<br>
> > as hints and things of that nature). One d I think starting<br>
> purely<br>
> > from that perspective<br>
> ><br>
> > isadvantage of this is that<br>
> > for every single update, unlike callbacks, we have to<br>
> traverse the<br>
> > entire list of subscriptions (though maybe that's not a big<br>
> deal).<br>
> > The<br>
> > bigger problem is this is push only and the PJSIP module still<br>
> > needs to<br>
> > be able to "pull" feature statuses on demand, which is where the<br>
> > hint/lookup model is useful. A potential middle ground<br>
> solution is<br>
> > use<br>
> > the dialplan function/AMI action to push only, but cache all of<br>
> > this in<br>
> > AstDB (as subscriptions themselves are), so that we can<br>
> retrieve the<br>
> > latest/most current value at any point if needed. Then we don't<br>
> > need to<br>
> > be concerned at all with how the user is managing state as<br>
> that is<br>
> > fully<br>
> > decoupled, although obviously this would lead to a little<br>
> > duplication/redundancy. Any thoughts?<br>
> ><br>
> ><br>
> > You've thrown a lot of lower level implementation details at us<br>
> and to<br>
> > be quite honest it's overwhelming. There's no full user facing<br>
> > examples of how it would all work, beyond the bits and pieces in<br>
> your<br>
> > text that we'd then have to deduce and after reading a few times it<br>
> > doesn't feel very friendly. To start off with: Is this a<br>
> developer API<br>
> > and interface, or is this also meant for the common everyday<br>
> user? I<br>
> > would hope it's for the common everyday user, in which case it<br>
> should<br>
> > be approached from that perspective first with implementation<br>
> details<br>
> > following.<br>
><br>
> Yes, it's for the common every day end user. A callback mechanism if<br>
> used would be more of a development one but that would be more a<br>
> means<br>
> to an end.<br>
><br>
> Here's an example of what I have in my dialplan right now, in the<br>
> subscribe_context for the endpoint:<br>
> exten => dnd_Polycom5,hint,Custom:${EXTEN}<br>
> exten => callfwd_Polycom5,hint,Custom:${EXTEN}<br>
> exten => callfwdalways_Polycom5,1,${FOOBAR(callforward,2135)}<br>
> exten => callfwdbusy_Polycom5,1,${FOOBAR(callforwardbusy,2135)}<br>
> exten =><br>
> callfwdnoanswer_Polycom5,1,${FOOBAR(callforwardnoanswer,2135)}<br>
><br>
> The user gets the AMI event, processes it with whatever processing is<br>
> needed (e.g. checking that the DND feature is available for that<br>
> line,<br>
> setting it in AstDB, ODBC, or whatever is the source of truth for<br>
> feature statuses), and then updates the relevant hints above.<br>
><br>
> The module is currently hardcoded to use these extensions in the<br>
> subscribe context: the prefix + the endpoint name. Obviously<br>
> that's also<br>
> inflexible.<br>
> Right now, the first 2 extensions the user will set to signal the<br>
> module<br>
> to send an updated NOTIFY. The first hint by virtue of being binary<br>
> contains the DND status itself, and the 3 bottom extensions are<br>
> needed<br>
> to retrieve the call forwarding numbers from the source of truth for<br>
> these features. (Here, FOOBAR is a custom - but any arbitrary -<br>
> dialplan<br>
> function I have that retrieves the status).<br>
><br>
> I bring this up only to show the current implementation and how<br>
> hacky it<br>
> is; I don't like this at all or think it's appropriate (except for<br>
> DND,<br>
> possibly). It was more a proof of concept of testing the<br>
> underlying SIP<br>
> technology.<br>
><br>
> A better implementation might look like:<br>
><br>
> User gets the AMI event and processes it as usual, and then simply<br>
> does<br>
> Set(PJSIP_DEVICE_FEATURE_STATE(PJSIP/Polycom1,donotdisturb)=enabled)<br>
> or<br>
> Set(PJSIP_DEVICE_FEATURE_STATE(PJSIP/Polycom1,callforwardingnoanswer)=8675309,4)<br>
><br>
> (forward on no answer to 8675309 after 4 rings).<br>
><br>
> Internally, PJSIP_DEVICE_FEATURE_STATE could also persist input to<br>
> AstDB<br>
> so it's available to the module.<br>
><br>
> > As an everyday user I'd expect not to have to deal with AMI or<br>
> complex<br>
> > dialplan. I'd expect to be able to set and get the information from<br>
> > the dialplan using dialplan functions (or a single function) so I<br>
> > could use that in the dialplan, and have it "just work" with my<br>
> phones<br>
> > that support the feature. I'd expect it to persist across Asterisk<br>
> > restarts. For example ${EXTENSION_DND(alice)} for retrieving DND<br>
> > status of Alice, and if Asterisk restarted then that should stay<br>
> the same.<br>
><br>
> That's a good point. The problem is that means that Asterisk<br>
> internally<br>
> is the source of truth of DND, and that may not necessarily be what<br>
> people want. For instance, that wouldn't meet my own requirements.<br>
> The<br>
> way that Broadworks works is the phone is merely requesting a certain<br>
> disposition, but the server isn't under any obligation to carry it<br>
> out.<br>
> So I think there needs to be some mechanism for the user to be<br>
> involved<br>
> in that pipeline, to be able to deny something that a phone wants.<br>
> Maybe<br>
> the user doesn't have DND, maybe certain phones aren't allowed to<br>
> toggle, whatever. Lots of people store their feature states in MySQL<br>
> databases and use them for Asterisk clusters. Some systems might have<br>
> specific requirements for that. So users should have flexibility to<br>
> reject it. On some systems, maybe the same DND status is used for<br>
> several lines and Asterisk internally would not have any idea<br>
> about this.<br>
> There are lots of different scenarios that are beyond what I think<br>
> Asterisk itself should handle, hence the "two-part" process described<br>
> above: the user (system admin) can do whatever needs to be done, and<br>
> then just tell PJSIP what the new state is. PJSIP doesn't need to<br>
> know<br>
> or care about where feature states are actually stored or what the<br>
> logic<br>
> is or how they are mapped to endpoints.<br>
><br>
> I do see your point though and I think it would be nice to have a<br>
> "simple, default mode" where Asterisk will internally just "approve<br>
> everything" that the phone wants, and users can use that if that<br>
> suits<br>
> their needs, but fundamentally I think users should be able to be<br>
> involved in the decision pipeline if they want/need to. I'm not<br>
> yet sure<br>
> what that would look like: maybe a pjsip.conf option to emit AMI<br>
> events<br>
> rather than auto-handling them? And then the<br>
> PJSIP_DEVICE_FEATURE_STATE<br>
> function would have to be used to tell PJSIP what to do, and Asterisk<br>
> itself would not be the source of truth for feature statuses in this<br>
> case (but it would cache them as described above) (though in this<br>
> case<br>
> reading the PJSIP_DEVICE_FEATURE_STATE could still return the cached<br>
> disposition)<br>
><br>
><br>
> Okay, YOUR usage is based on an API interface for developers to allow <br>
> external source of truth and logic. That's what it is. My response did <br>
> not approach it from that perspective.<br>
><br>
> I don't have thoughts on that aspect currently. It requires more time.<br>
<br>
The new architecture proposed (not using hints) has been working pretty <br>
well for a few weeks. I'm using it in the more "developer" mode but the <br>
other mode exists as well. The last hurdle is being able to send a <br>
multipart XML response when necessary, since if more than one feature <br>
needs to be sent to the device, the Broadworks spec outlines a multipart <br>
response devices will accept.<br>
<br>
Right now I'm using a body_generator module in order to generate the <br>
actual content for the NOTIFY, similar to how res_pjsip_exten_state and <br>
res_pjsip_mwi do things. However the body generator only gets a pointer <br>
to an ast_str, so there's no way for it to do anything PJSIP related <br>
like multipart. I was thinking that one elegant option would be to have <br>
a separate body generator for each of the features, and then <br>
res_pjsip_pubsub would handle creating the multipart body.<br>
<br>
There is some stuff in res_pjsip_pubsub currently to handle multipart <br>
subscriptions, for subscriptions that have "children". The individual <br>
features are not "child subscriptions" in the right sense, but if they <br>
were, then it might work within the constraints of the pubsub interface. <br>
However, I know you suggested it would be preferable to handle any <br>
multipart stuff in the module itself using the PJSIP multipart APIs, as <br>
opposed to changing anything in res_pjsip_pubsub.<br>
<br>
Looking at the existing support for multipart in res_pjsip_pubsub, it <br>
seems this is limited to subscriptions as discussed in RFC 4662, which <br>
require a "Supported: eventlist" header in them, even if you define a <br>
resource list in pjsip.conf. The Broadworks device feature sync <br>
subscriptions do not include such a header in the SUBSCRIBE. So, the <br>
subscription in question seems to be a single, childless subscription, <br>
that nonetheless needs to send a multipart response, potentially.<br>
<br>
So, a couple questions, assuming my understanding of the above is correct:<br>
<br>
* Since it doesn't appear that res_pjsip_pubsub currently supports<br>
multipart in the way that's needed here, do you think there's any<br>
approach we could take that retains being able to use body generator<br>
modules? I don't see how we can, unless res_pjsip_pubsub itself<br>
supported it. The support it has is close, but not really what it<br>
would need to be. Would it be reasonable to extend such<br>
functionality to support this kind of subscription? i.e. not require<br>
"Supported: eventlist" in order to be considered a resource list?<br>
* Alternately, if we didn't want to modify res_pjsip_pubsub to support<br>
this unconventional usage, perhaps the module itself could directly<br>
add some "dummy" child subscriptions to itself, one for each<br>
feature, so that one body generator could then be used for each of<br>
them. ast_sip_subscription is an opaque structure though, so this<br>
would require breaking some abstraction and using the internals of<br>
ast_sip_subscription in the module, in order to be able to add<br>
children to it.<br>
* Assuming we don't want to do anything with pubsub children, would it<br>
be fine/preferred to ditch the body generator altogether and do<br>
everything in a single module? It just feels a bit clunkier this<br>
way, since it'll likely duplicate a lot of what res_pjsip_pubsub is<br>
doing internally for building NOTIFYs, in fact this would likely<br>
require bypassing much of what res_pjsip_pubsub does in order to<br>
generate the entire response itself.<br>
* Is it even worth trying to support multipart responses for non<br>
resource list subscriptions? Would sending up to 4 NOTIFYs instead<br>
of a single multipart one be an "acceptable kludge", given the<br>
requirements of res_pjsip_pubsub?<br>
<br>
All of the approaches listed seem a bit hacky to me, though in different <br>
ways. Wondering if you think a particular approach is more promising <br>
than the others...<br></blockquote><div><br></div><div>I don't have any further input, and it is unlikely I will have any for quite some time. I'm also not that familiar with the res_pjsip_pubsub RLS implementation which is what you are referring to and haven't looked at the underlying specification for your Broadsoft stuff. Someone else may have input.</div></div><div><br></div>-- <br><div dir="ltr" class="gmail_signature"><div dir="ltr"><div><div dir="ltr"><div><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div style="font-family:tahoma,sans-serif"><font color="#073763">Joshua C. Colp</font></div><div style="font-family:tahoma,sans-serif"><font color="#073763">Asterisk Project Lead</font></div><div style="font-family:tahoma,sans-serif"><font color="#073763">Sangoma Technologies</font></div><div style="font-family:tahoma,sans-serif"><font color="#073763">Check us out at <a href="http://www.sangoma.com" target="_blank">www.sangoma.com</a> and <a href="http://www.asterisk.org" target="_blank">www.asterisk.org</a></font><br></div></div></div></div></div></div></div></div></div></div></div>