On 10/21/2022 7:53 AM, Joshua C. Colp wrote:
On Fri, Oct 21, 2022 at 8:29 AM <aster...@phreaknet.org <mailto:aster...@phreaknet.org>> wrote:

    On 10/21/2022 5:34 AM, Joshua C. Colp wrote:
    > On Thu, Oct 20, 2022 at 8:23 PM <aster...@phreaknet.org
    <mailto:aster...@phreaknet.org>
    > <mailto:aster...@phreaknet.org <mailto:aster...@phreaknet.org>>>
    wrote:
    >
    >     Hi, all,
    >
    >          Something I have been working on recently is adding
    support to
    >     PJSIP for device feature key synchronization (the
    as-feature-event
    >     Broadworks spec that many common IP phones, e.g. Polycom
    support) to
    >     control server-side features from endpoints. It's using the
    PJSIP
    >     pub/sub capabilities; I had to add the ability to execute a
    custom
    >     module callback when a SUBSCRIBE is refreshed, but with that
    >     addition,
    >     it works as it's supposed to.
    >
    >
    > You should further elaborate on all of the server-side features you
    > expect to implement, if it extends beyond DND.

    The other one is Call Forwarding (Always/Busy/No Answer, with
    number of
    rings)

    >
    >     I wanted to solicit some input on what an ideal way of
    triggering
    >     updates should be. Currently we have hints, which actually work
    >     reasonably all right for Do Not Disturb, which is simply a
    boolean
    >     on/off, easily represented with a hint and custom device
    state for
    >     DND.
    >     The PJSIP module emits an AMI event, the user can process it and
    >     change
    >     the device state if needed, which will trigger a NOTIFY to
    go out
    >     to the
    >     endpoint.
    >
    >
    > What user? An outside AMI application? An internal consumer in
    Asterisk?

    The administrator of the Asterisk system, who can add AMI logic to
    receive the event and then do something with it.
    The reason this is needed is the phone isn't turning DND on directly,
    for example. It's merely a request. The server can decide to not
    allow
    it, for example if that phone isn't allowed to toggle DND. The server
    will process it, and send it the updated status. This usually
    reflects
    what the phone wanted, but not necessarily.
    (I've elaborated on this more below)


Okay, that's not the administrator of an Asterisk system. That's a developer using Asterisk who may be the administrator. Those are two separate things. As soon as you bring AMI into the mix then it's making a solution using Asterisk. That's not a wrong thing, but it's important to be clear in the audience. Many average users of Asterisk doesn't know or care about AMI, 'nor do they code for it. They may use additional solutions that utilize AMI but they themselves haven't the foggiest of the details.


    >
    >     This sort of came up about 12 years ago[1]. The actual SIP stuff
    >     is not
    >     complicated; it's the user interface to it that requires more
    >     thought.
    >     For call forwarding, there are more moving pieces and abusing
    >     hints/custom device state for that is super clunky. You can't
    >     communicate the call forwarding target, # of rings, etc. in
    a device
    >     state, so additional hints are then needed for that. It
    works but
    >     it's
    >     super clunky and I don't think this is a great pipeline.
    >
    >
    > Okay, so this covers call forwarding as well.
    Yes.
    >
    >
    >     I'm wondering if people have thoughts on what an ideal mechanism
    >     would
    >     be for users, once they process a request to enable/disable
    a feature
    >     from the phone, to communicate that to the PJSIP module. The
    problem
    >     with abusing hints, especially for call forwarding, is it's
    not a
    >     good
    >     way to communicate details into the module. One option
    perhaps is to
    >     have dialplan extensions, setup in a manner similar to use with
    >     EVAL_EXTEN, where it returns the current value needed, as any
    >     relevant
    >     function, DB, ODBC, CURL, custom function, etc. could be used to
    >     retrieve the current feature value. The clunky part is more
    >     signaling to
    >     the PJSIP module that it needs to send the phone the updated
    >     status (by
    >     checking those extensions, for example). The device state
    callback
    >     happens to be convenient for this kind of signaling but not
    really
    >     appropriate here. It would be better to push the info into
    the module
    >     directly rather than the signaling it and making it retrieve the
    >     updated
    >     data in some arbitrary way.
    >
    >     So with this in mind, I'm currently leaning in the direction
    of a
    >     dialplan function/AMI action that could be used to set the
    >     appropriate
    >     info for a subscription, which would trigger the NOTIFY, and
    then
    >     nothing would actually need to be added to the dialplan at all
    >     (inasmuch
    >     as hints and things of that nature). One d I think starting
    purely
    >     from that perspective
    >
    >     isadvantage of this is that
    >     for every single update, unlike callbacks, we have to
    traverse the
    >     entire list of subscriptions (though maybe that's not a big
    deal).
    >     The
    >     bigger problem is this is push only and the PJSIP module still
    >     needs to
    >     be able to "pull" feature statuses on demand, which is where the
    >     hint/lookup model is useful. A potential middle ground
    solution is
    >     use
    >     the dialplan function/AMI action to push only, but cache all of
    >     this in
    >     AstDB (as subscriptions themselves are), so that we can
    retrieve the
    >     latest/most current value at any point if needed. Then we don't
    >     need to
    >     be concerned at all with how the user is managing state as
    that is
    >     fully
    >     decoupled, although obviously this would lead to a little
    >     duplication/redundancy. Any thoughts?
    >
    >
    > You've thrown a lot of lower level implementation details at us
    and to
    > be quite honest it's overwhelming. There's no full user facing
    > examples of how it would all work, beyond the bits and pieces in
    your
    > text that we'd then have to deduce and after reading a few times it
    > doesn't feel very friendly. To start off with: Is this a
    developer API
    > and interface, or is this also meant for the common everyday
    user? I
    > would hope it's for the common everyday user, in which case it
    should
    > be approached from that perspective first with implementation
    details
    > following.

    Yes, it's for the common every day end user. A callback mechanism if
    used would be more of a development one but that would be more a
    means
    to an end.

    Here's an example of what I have in my dialplan right now, in the
    subscribe_context for the endpoint:
    exten => dnd_Polycom5,hint,Custom:${EXTEN}
    exten => callfwd_Polycom5,hint,Custom:${EXTEN}
    exten => callfwdalways_Polycom5,1,${FOOBAR(callforward,2135)}
    exten => callfwdbusy_Polycom5,1,${FOOBAR(callforwardbusy,2135)}
    exten =>
    callfwdnoanswer_Polycom5,1,${FOOBAR(callforwardnoanswer,2135)}

    The user gets the AMI event, processes it with whatever processing is
    needed (e.g. checking that the DND feature is available for that
    line,
    setting it in AstDB, ODBC, or whatever is the source of truth for
    feature statuses), and then updates the relevant hints above.

    The module is currently hardcoded to use these extensions in the
    subscribe context: the prefix + the endpoint name. Obviously
    that's also
    inflexible.
    Right now, the first 2 extensions the user will set to signal the
    module
    to send an updated NOTIFY. The first hint by virtue of being binary
    contains the DND status itself, and the 3 bottom extensions are
    needed
    to retrieve the call forwarding numbers from the source of truth for
    these features. (Here, FOOBAR is a custom - but any arbitrary -
    dialplan
    function I have that retrieves the status).

    I bring this up only to show the current implementation and how
    hacky it
    is; I don't like this at all or think it's appropriate (except for
    DND,
    possibly). It was more a proof of concept of testing the
    underlying SIP
    technology.

    A better implementation might look like:

    User gets the AMI event and processes it as usual, and then simply
    does
    Set(PJSIP_DEVICE_FEATURE_STATE(PJSIP/Polycom1,donotdisturb)=enabled)
    or
    
Set(PJSIP_DEVICE_FEATURE_STATE(PJSIP/Polycom1,callforwardingnoanswer)=8675309,4)

    (forward on no answer to 8675309 after 4 rings).

    Internally, PJSIP_DEVICE_FEATURE_STATE could also persist input to
    AstDB
    so it's available to the module.

    > As an everyday user I'd expect not to have to deal with AMI or
    complex
    > dialplan. I'd expect to be able to set and get the information from
    > the dialplan using dialplan functions (or a single function) so I
    > could use that in the dialplan, and have it "just work" with my
    phones
    > that support the feature. I'd expect it to persist across Asterisk
    > restarts. For example ${EXTENSION_DND(alice)} for retrieving DND
    > status of Alice, and if Asterisk restarted then that should stay
    the same.

    That's a good point. The problem is that means that Asterisk
    internally
    is the source of truth of DND, and that may not necessarily be what
    people want. For instance, that wouldn't meet my own requirements.
    The
    way that Broadworks works is the phone is merely requesting a certain
    disposition, but the server isn't under any obligation to carry it
    out.
    So I think there needs to be some mechanism for the user to be
    involved
    in that pipeline, to be able to deny something that a phone wants.
    Maybe
    the user doesn't have DND, maybe certain phones aren't allowed to
    toggle, whatever. Lots of people store their feature states in MySQL
    databases and use them for Asterisk clusters. Some systems might have
    specific requirements for that. So users should have flexibility to
    reject it. On some systems, maybe the same DND status is used for
    several lines and Asterisk internally would not have any idea
    about this.
    There are lots of different scenarios that are beyond what I think
    Asterisk itself should handle, hence the "two-part" process described
    above: the user (system admin) can do whatever needs to be done, and
    then just tell PJSIP what the new state is. PJSIP doesn't need to
    know
    or care about where feature states are actually stored or what the
    logic
    is or how they are mapped to endpoints.

    I do see your point though and I think it would be nice to have a
    "simple, default mode" where Asterisk will internally just "approve
    everything" that the phone wants, and users can use that if that
    suits
    their needs, but fundamentally I think users should be able to be
    involved in the decision pipeline if they want/need to. I'm not
    yet sure
    what that would look like: maybe a pjsip.conf option to emit AMI
    events
    rather than auto-handling them? And then the
    PJSIP_DEVICE_FEATURE_STATE
    function would have to be used to tell PJSIP what to do, and Asterisk
    itself would not be the source of truth for feature statuses in this
    case (but it would cache them as described above) (though in this
    case
    reading the PJSIP_DEVICE_FEATURE_STATE could still return the cached
    disposition)


Okay, YOUR usage is based on an API interface for developers to allow external source of truth and logic. That's what it is. My response did not approach it from that perspective.

I don't have thoughts on that aspect currently. It requires more time.

The new architecture proposed (not using hints) has been working pretty well for a few weeks. I'm using it in the more "developer" mode but the other mode exists as well. The last hurdle is being able to send a multipart XML response when necessary, since if more than one feature needs to be sent to the device, the Broadworks spec outlines a multipart response devices will accept.

Right now I'm using a body_generator module in order to generate the actual content for the NOTIFY, similar to how res_pjsip_exten_state and res_pjsip_mwi do things. However the body generator only gets a pointer to an ast_str, so there's no way for it to do anything PJSIP related like multipart. I was thinking that one elegant option would be to have a separate body generator for each of the features, and then res_pjsip_pubsub would handle creating the multipart body.

There is some stuff in res_pjsip_pubsub currently to handle multipart subscriptions, for subscriptions that have "children". The individual features are not "child subscriptions" in the right sense, but if they were, then it might work within the constraints of the pubsub interface. However, I know you suggested it would be preferable to handle any multipart stuff in the module itself using the PJSIP multipart APIs, as opposed to changing anything in res_pjsip_pubsub.

Looking at the existing support for multipart in res_pjsip_pubsub, it seems this is limited to subscriptions as discussed in RFC 4662, which require a "Supported: eventlist" header in them, even if you define a resource list in pjsip.conf. The Broadworks device feature sync subscriptions do not include such a header in the SUBSCRIBE. So, the subscription in question seems to be a single, childless subscription, that nonetheless needs to send a multipart response, potentially.

So, a couple questions, assuming my understanding of the above is correct:

 * Since it doesn't appear that res_pjsip_pubsub currently supports
   multipart in the way that's needed here, do you think there's any
   approach we could take that retains being able to use body generator
   modules? I don't see how we can, unless res_pjsip_pubsub itself
   supported it. The support it has is close, but not really what it
   would need to be. Would it be reasonable to extend such
   functionality to support this kind of subscription? i.e. not require
   "Supported: eventlist" in order to be considered a resource list?
 * Alternately, if we didn't want to modify res_pjsip_pubsub to support
   this unconventional usage, perhaps the module itself could directly
   add some "dummy" child subscriptions to itself, one for each
   feature, so that one body generator could then be used for each of
   them. ast_sip_subscription is an opaque structure though, so this
   would require breaking some abstraction and using the internals of
   ast_sip_subscription in the module, in order to be able to add
   children to it.
 * Assuming we don't want to do anything with pubsub children, would it
   be fine/preferred to ditch the body generator altogether and do
   everything in a single module? It just feels a bit clunkier this
   way, since it'll likely duplicate a lot of what res_pjsip_pubsub is
   doing internally for building NOTIFYs, in fact this would likely
   require bypassing much of what res_pjsip_pubsub does in order to
   generate the entire response itself.
 * Is it even worth trying to support multipart responses for non
   resource list subscriptions? Would sending up to 4 NOTIFYs instead
   of a single multipart one be an "acceptable kludge", given the
   requirements of res_pjsip_pubsub?

All of the approaches listed seem a bit hacky to me, though in different ways. Wondering if you think a particular approach is more promising than the others...

Thanks!

NA


--
_____________________________________________________________________
-- Bandwidth and Colocation Provided by http://www.api-digital.com --

asterisk-dev mailing list
To UNSUBSCRIBE or update options visit:
  http://lists.digium.com/mailman/listinfo/asterisk-dev

Reply via email to