Changeset 2150


Ignore:
Timestamp:
Jul 17, 2008 2:19:10 PM (16 years ago)
Author:
bennylp
Message:

Ticket #192: Add callback to notify application about incoming SUBSCRIBE request, and add subscription state and termination reason in buddy info

Location:
pjproject/trunk/pjsip
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h

    r2118 r2150  
    394394typedef int pjsua_conf_port_id; 
    395395 
     396/** Opaque declaration for server side presence subscription */ 
     397typedef struct pjsua_srv_pres pjsua_srv_pres; 
     398 
     399/** Forward declaration for pjsua_msg_data */ 
     400typedef struct pjsua_msg_data pjsua_msg_data; 
    396401 
    397402 
     
    792797     */ 
    793798    void (*on_reg_state)(pjsua_acc_id acc_id); 
     799 
     800    /** 
     801     * Notification when incoming SUBSCRIBE request is received. Application 
     802     * may use this callback to authorize the incoming subscribe request 
     803     * (e.g. ask user permission if the request should be granted). 
     804     * 
     805     * If this callback is not implemented, all incoming presence subscription 
     806     * requests will be accepted. 
     807     * 
     808     * If this callback is implemented, application has several choices on 
     809     * what to do with the incoming request: 
     810     *  - it may reject the request immediately by specifying non-200 class 
     811     *    final response in the \a code argument. 
     812     *  - it may immediately accept the request by specifying 200 as the 
     813     *    \a code argument. This is the default value if application doesn't 
     814     *    set any value to the \a code argument. In this case, the library 
     815     *    will automatically send NOTIFY request upon returning from this 
     816     *    callback. 
     817     *  - it may delay the processing of the request, for example to request 
     818     *    user permission whether to accept or reject the request. In this  
     819     *    case, the application MUST set the \a code argument to 202, and  
     820     *    later calls #pjsua_pres_notify() to accept or reject the  
     821     *    subscription request. 
     822     * 
     823     * Any \a code other than 200 and 202 will be treated as 200. 
     824     * 
     825     * Application MUST return from this callback immediately (e.g. it must 
     826     * not block in this callback while waiting for user confirmation). 
     827     * 
     828     * @param srv_pres      Server presence subscription instance. If 
     829     *                      application delays the acceptance of the request, 
     830     *                      it will need to specify this object when calling 
     831     *                      #pjsua_pres_notify(). 
     832     * @param acc_id        Account ID most appropriate for this request. 
     833     * @param buddy_id      ID of the buddy matching the sender of the 
     834     *                      request, if any, or PJSUA_INVALID_ID if no 
     835     *                      matching buddy is found. 
     836     * @param from          The From URI of the request. 
     837     * @param rdata         The incoming request. 
     838     * @param code          The status code to respond to the request. The 
     839     *                      default value is 200. Application may set this 
     840     *                      to other final status code to accept or reject 
     841     *                      the request. 
     842     * @param reason        The reason phrase to respond to the request. 
     843     * @param msg_data      If the application wants to send additional 
     844     *                      headers in the response, it can put it in this 
     845     *                      parameter. 
     846     */ 
     847    void (*on_incoming_subscribe)(pjsua_acc_id acc_id, 
     848                                  pjsua_srv_pres *srv_pres, 
     849                                  pjsua_buddy_id buddy_id, 
     850                                  const pj_str_t *from, 
     851                                  pjsip_rx_data *rdata, 
     852                                  pjsip_status_code *code, 
     853                                  pj_str_t *reason, 
     854                                  pjsua_msg_data *msg_data); 
     855 
     856    /** 
     857     * Notification when server side subscription state has changed. 
     858     * This callback is optional as application normally does not need 
     859     * to do anything to maintain server side presence subscription. 
     860     * 
     861     * @param acc_id        The account ID. 
     862     * @param srv_pres      Server presence subscription object. 
     863     * @param remote_uri    Remote URI string. 
     864     * @param state         New subscription state. 
     865     * @param event         PJSIP event that triggers the state change. 
     866     */ 
     867    void (*on_srv_subscribe_state)(pjsua_acc_id acc_id, 
     868                                   pjsua_srv_pres *srv_pres, 
     869                                   const pj_str_t *remote_uri, 
     870                                   pjsip_evsub_state state, 
     871                                   pjsip_event *event); 
    794872 
    795873    /** 
     
    11941272 * \endcode 
    11951273 */ 
    1196 typedef struct pjsua_msg_data 
     1274struct pjsua_msg_data 
    11971275{ 
    11981276    /** 
     
    12221300    pj_str_t    msg_body; 
    12231301 
    1224 } pjsua_msg_data; 
     1302}; 
    12251303 
    12261304 
     
    24122490PJ_DECL(pj_status_t) pjsua_acc_set_registration(pjsua_acc_id acc_id,  
    24132491                                                pj_bool_t renew); 
    2414  
    24152492 
    24162493/** 
     
    33533430 
    33543431    /** 
     3432     * If \a monitor_pres is enabled, this specifies the last state of the 
     3433     * presence subscription. If presence subscription session is currently 
     3434     * active, the value will be PJSIP_EVSUB_STATE_ACTIVE. If presence 
     3435     * subscription request has been rejected, the value will be 
     3436     * PJSIP_EVSUB_STATE_TERMINATED, and the termination reason will be 
     3437     * specified in \a sub_term_reason. 
     3438     */ 
     3439    pjsip_evsub_state   sub_state; 
     3440 
     3441    /** 
     3442     * Specifies the last presence subscription terminatino reason. If  
     3443     * presence subscription is currently active, the value will be empty. 
     3444     */ 
     3445    pj_str_t            sub_term_reason; 
     3446 
     3447    /** 
    33553448     * Extended RPID information about the person. 
    33563449     */ 
     
    35203613PJ_DECL(pj_status_t) pjsua_buddy_update_pres(pjsua_buddy_id buddy_id); 
    35213614 
     3615 
     3616/** 
     3617 * Send NOTIFY to inform account presence status or to terminate server 
     3618 * side presence subscription. If application wants to reject the incoming 
     3619 * request, it should set the \a state to PJSIP_EVSUB_STATE_TERMINATED. 
     3620 * 
     3621 * @param acc_id        Account ID. 
     3622 * @param srv_pres      Server presence subscription instance. 
     3623 * @param state         New state to set. 
     3624 * @param state_str     Optionally specify the state string name, if state 
     3625 *                      is not "active", "pending", or "terminated". 
     3626 * @param reason        If the new state is PJSIP_EVSUB_STATE_TERMINATED, 
     3627 *                      optionally specify the termination reason.  
     3628 * @param with_body     If the new state is PJSIP_EVSUB_STATE_TERMINATED, 
     3629 *                      this specifies whether the NOTIFY request should 
     3630 *                      contain message body containing account's presence 
     3631 *                      information. 
     3632 * @param msg_data      Optional list of headers to be sent with the NOTIFY 
     3633 *                      request. 
     3634 * 
     3635 * @return              PJ_SUCCESS on success. 
     3636 */ 
     3637PJ_DECL(pj_status_t) pjsua_pres_notify(pjsua_acc_id acc_id, 
     3638                                       pjsua_srv_pres *srv_pres, 
     3639                                       pjsip_evsub_state state, 
     3640                                       const pj_str_t *state_str, 
     3641                                       const pj_str_t *reason, 
     3642                                       pj_bool_t with_body, 
     3643                                       const pjsua_msg_data *msg_data); 
    35223644 
    35233645/** 
  • pjproject/trunk/pjsip/include/pjsua-lib/pjsua_internal.h

    r2130 r2150  
    8686 * Server presence subscription list head. 
    8787 */ 
    88 typedef struct pjsua_srv_pres 
     88struct pjsua_srv_pres 
    8989{ 
    9090    PJ_DECL_LIST_MEMBER(struct pjsua_srv_pres); 
    91     pjsip_evsub     *sub; 
    92     char            *remote; 
    93 } pjsua_srv_pres; 
     91    pjsip_evsub     *sub;           /**< The evsub.                         */ 
     92    char            *remote;        /**< Remote URI.                        */ 
     93    int              acc_id;        /**< Account ID.                        */ 
     94    pjsip_dialog    *dlg;           /**< Dialog.                            */ 
     95    int              expires;       /**< "expires" value in the request.    */ 
     96}; 
    9497 
    9598 
     
    151154} pjsua_transport_data; 
    152155 
     156 
     157/** Maximum length of subscription termination reason. */ 
     158#define PJSUA_BUDDY_SUB_TERM_REASON_LEN     32 
    153159 
    154160/** 
     
    168174    pjsip_dialog        *dlg;       /**< The underlying dialog.         */ 
    169175    pjsip_evsub         *sub;       /**< Buddy presence subscription    */ 
     176    pj_str_t             term_reason;/**< Subscription termination reason */ 
    170177    pjsip_pres_status    status;    /**< Buddy presence status.         */ 
    171178 
  • pjproject/trunk/pjsip/src/pjsua-lib/pjsua_pres.c

    r2130 r2150  
    164164    info->monitor_pres = buddy->monitor; 
    165165 
     166    /* subscription state and termination reason */ 
     167    if (buddy->sub) { 
     168        info->sub_state = pjsip_evsub_get_state(buddy->sub); 
     169        if (info->sub_state == PJSIP_EVSUB_STATE_TERMINATED && 
     170            total < sizeof(info->buf_))  
     171        { 
     172            info->sub_term_reason.ptr = info->buf_ + total; 
     173            pj_strncpy(&info->sub_term_reason, 
     174                       pjsip_evsub_get_termination_reason(buddy->sub), 
     175                       sizeof(info->buf_) - total); 
     176            total += info->sub_term_reason.slen; 
     177        } else { 
     178            info->sub_term_reason = pj_str(""); 
     179        } 
     180    } else if (total < sizeof(info->buf_)) { 
     181        info->sub_term_reason.ptr = info->buf_ + total; 
     182        pj_strncpy(&info->sub_term_reason, &buddy->term_reason, 
     183                   sizeof(info->buf_) - total); 
     184        total += info->sub_term_reason.slen; 
     185    } else { 
     186        info->sub_term_reason = pj_str(""); 
     187    } 
     188 
    166189    PJSUA_UNLOCK(); 
    167190    return PJ_SUCCESS; 
     
    223246        buddy->pool = pjsua_pool_create(name, 512, 256); 
    224247    } 
     248 
     249    /* Init buffers for presence subscription status */ 
     250    buddy->term_reason.ptr = (char*)  
     251                             pj_pool_alloc(buddy->pool,  
     252                                           PJSUA_BUDDY_SUB_TERM_REASON_LEN); 
    225253 
    226254    /* Get name and display name for buddy */ 
     
    525553    uapres = (pjsua_srv_pres*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id); 
    526554    if (uapres) { 
     555        pjsip_evsub_state state; 
     556 
    527557        PJ_LOG(4,(THIS_FILE, "Server subscription to %s is %s", 
    528558                  uapres->remote, pjsip_evsub_get_state_name(sub))); 
    529559 
    530         if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) { 
     560        state = pjsip_evsub_get_state(sub); 
     561 
     562        if (pjsua_var.ua_cfg.cb.on_srv_subscribe_state) { 
     563            pj_str_t from; 
     564 
     565            from = uapres->dlg->remote.info_str; 
     566            (*pjsua_var.ua_cfg.cb.on_srv_subscribe_state)(uapres->acc_id,  
     567                                                          uapres, &from, 
     568                                                          state, event); 
     569        } 
     570 
     571        if (state == PJSIP_EVSUB_STATE_TERMINATED) { 
    531572            pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL); 
    532573            pj_list_erase(uapres); 
     
    549590    pjsip_evsub *sub; 
    550591    pjsip_evsub_user pres_cb; 
    551     pjsip_tx_data *tdata; 
    552     pjsip_pres_status pres_status; 
    553592    pjsip_dialog *dlg; 
     593    pjsip_status_code st_code; 
     594    pj_str_t reason; 
    554595    pjsip_expires_hdr *expires_hdr; 
    555     pjsip_evsub_state ev_state; 
    556     pjsua_buddy_id buddy_id; 
     596    pjsua_msg_data msg_data; 
    557597    pj_status_t status; 
    558598 
     
    628668    uapres->sub = sub; 
    629669    uapres->remote = (char*) pj_pool_alloc(dlg->pool, PJSIP_MAX_URL_SIZE); 
     670    uapres->acc_id = acc_id; 
     671    uapres->dlg = dlg; 
    630672    status = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, dlg->remote.info->uri, 
    631673                             uapres->remote, PJSIP_MAX_URL_SIZE); 
     
    641683 
    642684 
    643     /* Create and send 200 (OK) to the SUBSCRIBE request: */ 
    644     status = pjsip_pres_accept(sub, rdata, 200, NULL); 
     685    /* Capture the value of Expires header. */ 
     686    expires_hdr = (pjsip_expires_hdr*) 
     687                  pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, 
     688                                     NULL); 
     689    if (expires_hdr) 
     690        uapres->expires = expires_hdr->ivalue; 
     691    else 
     692        uapres->expires = -1; 
     693 
     694    st_code = 200; 
     695    reason = pj_str("OK"); 
     696    pjsua_msg_data_init(&msg_data); 
     697 
     698    /* Notify application callback, if any */ 
     699    if (pjsua_var.ua_cfg.cb.on_incoming_subscribe) { 
     700        pjsua_buddy_id buddy_id; 
     701 
     702        buddy_id = pjsua_find_buddy(rdata->msg_info.from->uri); 
     703 
     704        (*pjsua_var.ua_cfg.cb.on_incoming_subscribe)(acc_id, uapres, buddy_id, 
     705                                                     &dlg->remote.info_str,  
     706                                                     rdata, &st_code, &reason, 
     707                                                     &msg_data); 
     708    } 
     709 
     710    /* Handle rejection case */ 
     711    if (st_code >= 300) { 
     712        pjsip_tx_data *tdata; 
     713 
     714        /* Create response */ 
     715        status = pjsip_dlg_create_response(dlg, rdata, st_code,  
     716                                           &reason, &tdata); 
     717        if (status != PJ_SUCCESS) { 
     718            pjsua_perror(THIS_FILE, "Error creating response",  status); 
     719            pj_list_erase(uapres); 
     720            pjsip_pres_terminate(sub, PJ_FALSE); 
     721            PJSUA_UNLOCK(); 
     722            return PJ_FALSE; 
     723        } 
     724 
     725        /* Add header list, if any */ 
     726        pjsua_process_msg_data(tdata, &msg_data); 
     727 
     728        /* Send the response */ 
     729        status = pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata), 
     730                                         tdata); 
     731        if (status != PJ_SUCCESS) { 
     732            pjsua_perror(THIS_FILE, "Error sending response",  status); 
     733            /* This is not fatal */ 
     734        } 
     735 
     736        /* Terminate presence subscription */ 
     737        pj_list_erase(uapres); 
     738        pjsip_pres_terminate(sub, PJ_FALSE); 
     739        PJSUA_UNLOCK(); 
     740        return PJ_TRUE; 
     741    } 
     742 
     743    /* Create and send 2xx response to the SUBSCRIBE request: */ 
     744    status = pjsip_pres_accept(sub, rdata, st_code, &msg_data.hdr_list); 
    645745    if (status != PJ_SUCCESS) { 
    646746        pjsua_perror(THIS_FILE, "Unable to accept presence subscription",  
     
    652752    } 
    653753 
     754    /* If code is 200, send NOTIFY now */ 
     755    if (st_code == 200) { 
     756        pjsua_pres_notify(acc_id, uapres, PJSIP_EVSUB_STATE_ACTIVE,  
     757                          NULL, NULL, PJ_TRUE, &msg_data); 
     758    } 
     759 
     760    /* Done: */ 
     761 
     762    PJSUA_UNLOCK(); 
     763 
     764    return PJ_TRUE; 
     765} 
     766 
     767 
     768/* 
     769 * Send NOTIFY. 
     770 */ 
     771PJ_DEF(pj_status_t) pjsua_pres_notify( pjsua_acc_id acc_id, 
     772                                       pjsua_srv_pres *srv_pres, 
     773                                       pjsip_evsub_state ev_state, 
     774                                       const pj_str_t *state_str, 
     775                                       const pj_str_t *reason, 
     776                                       pj_bool_t with_body, 
     777                                       const pjsua_msg_data *msg_data) 
     778{ 
     779    pjsua_acc *acc; 
     780    pjsip_pres_status pres_status; 
     781    pjsua_buddy_id buddy_id; 
     782    pjsip_tx_data *tdata; 
     783    pj_status_t status; 
     784 
     785    /* Check parameters */ 
     786    PJ_ASSERT_RETURN(acc_id!=-1 && srv_pres, PJ_EINVAL); 
     787 
     788    /* Check that account ID is valid */ 
     789    PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc), 
     790                     PJ_EINVAL); 
     791    /* Check that account is valid */ 
     792    PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, PJ_EINVALIDOP); 
     793 
     794    PJSUA_LOCK(); 
     795 
     796    acc = &pjsua_var.acc[acc_id]; 
     797 
     798    /* Check that the server presence subscription is still valid */ 
     799    if (pj_list_find_node(&acc->pres_srv_list, srv_pres) == NULL) { 
     800        /* Subscription has been terminated */ 
     801        PJSUA_UNLOCK(); 
     802        return PJ_EINVALIDOP; 
     803    } 
    654804 
    655805    /* Set our online status: */ 
    656806    pj_bzero(&pres_status, sizeof(pres_status)); 
    657807    pres_status.info_cnt = 1; 
    658     pres_status.info[0].basic_open = pjsua_var.acc[acc_id].online_status; 
    659     pres_status.info[0].id = pjsua_var.acc[acc_id].cfg.pidf_tuple_id; 
     808    pres_status.info[0].basic_open = acc->online_status; 
     809    pres_status.info[0].id = acc->cfg.pidf_tuple_id; 
    660810    //Both pjsua_var.local_uri and pjsua_var.contact_uri are enclosed in "<" and ">" 
    661811    //causing XML parsing to fail. 
    662812    //pres_status.info[0].contact = pjsua_var.local_uri; 
    663813 
    664     pjsip_pres_set_status(sub, &pres_status); 
     814    pjsip_pres_set_status(srv_pres->sub, &pres_status); 
    665815 
    666816    /* Check expires value. If it's zero, send our presense state but 
    667817     * set subscription state to TERMINATED. 
    668818     */ 
    669     expires_hdr=(pjsip_expires_hdr*) 
    670                 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL); 
    671  
    672     if (expires_hdr && expires_hdr->ivalue == 0) 
     819    if (srv_pres->expires == 0) 
    673820        ev_state = PJSIP_EVSUB_STATE_TERMINATED; 
    674     else 
    675         ev_state = PJSIP_EVSUB_STATE_ACTIVE; 
    676  
    677     /* Create and send the first NOTIFY to active subscription: */ 
    678     status = pjsip_pres_notify( sub, ev_state, NULL, NULL, &tdata); 
     821 
     822    /* Create and send the NOTIFY to active subscription: */ 
     823    status = pjsip_pres_notify(srv_pres->sub, ev_state, state_str,  
     824                               reason, &tdata); 
    679825    if (status == PJ_SUCCESS) { 
    680         pjsua_process_msg_data(tdata, NULL); 
    681         status = pjsip_pres_send_request( sub, tdata); 
     826        /* Force removal of message body if msg_body==FALSE */ 
     827        if (!with_body) { 
     828            tdata->msg->body = NULL; 
     829        } 
     830        pjsua_process_msg_data(tdata, msg_data); 
     831        status = pjsip_pres_send_request( srv_pres->sub, tdata); 
    682832    } 
    683833 
     
    685835        pjsua_perror(THIS_FILE, "Unable to create/send NOTIFY",  
    686836                     status); 
    687         pj_list_erase(uapres); 
    688         pjsip_pres_terminate(sub, PJ_FALSE); 
    689         PJSUA_UNLOCK(); 
    690         return PJ_FALSE; 
     837        pj_list_erase(srv_pres); 
     838        pjsip_pres_terminate(srv_pres->sub, PJ_FALSE); 
     839        PJSUA_UNLOCK(); 
     840        return status; 
    691841    } 
    692842 
    693843 
    694844    /* Subscribe to buddy's presence if we're not subscribed */ 
    695     buddy_id = pjsua_find_buddy(dlg->remote.info->uri); 
     845    buddy_id = pjsua_find_buddy(srv_pres->dlg->remote.info->uri); 
    696846    if (buddy_id != PJSUA_INVALID_ID) { 
    697847        pjsua_buddy *b = &pjsua_var.buddy[buddy_id]; 
     
    703853    } 
    704854 
    705     /* Done: */ 
    706  
    707855    PJSUA_UNLOCK(); 
    708856 
    709     return PJ_TRUE; 
     857    return PJ_SUCCESS; 
    710858} 
    711859 
     
    10101158 
    10111159        if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) { 
     1160            if (buddy->term_reason.ptr == NULL) { 
     1161                buddy->term_reason.ptr = (char*)  
     1162                                         pj_pool_alloc(buddy->pool, 
     1163                                           PJSUA_BUDDY_SUB_TERM_REASON_LEN); 
     1164            } 
     1165            pj_strncpy(&buddy->term_reason,  
     1166                       pjsip_evsub_get_termination_reason(sub),  
     1167                       PJSUA_BUDDY_SUB_TERM_REASON_LEN); 
     1168        } else { 
     1169            buddy->term_reason.slen = 0; 
     1170        } 
     1171 
     1172        /* Call callback */ 
     1173        if (pjsua_var.ua_cfg.cb.on_buddy_state) 
     1174            (*pjsua_var.ua_cfg.cb.on_buddy_state)(buddy->index); 
     1175 
     1176        /* Clear subscription */ 
     1177        if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) { 
    10121178            buddy->sub = NULL; 
    10131179            buddy->status.info_cnt = 0; 
    10141180            pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL); 
    10151181        } 
    1016  
    1017         /* Call callback */ 
    1018         if (pjsua_var.ua_cfg.cb.on_buddy_state) 
    1019             (*pjsua_var.ua_cfg.cb.on_buddy_state)(buddy->index); 
    10201182    } 
    10211183 
Note: See TracChangeset for help on using the changeset viewer.