Ignore:
Timestamp:
May 17, 2008 12:45:00 PM (12 years ago)
Author:
bennylp
Message:

Ticket #534: Client register/registration support for various registrar brokenness

File:
1 edited

Legend:

Unmodified
Added
Removed
  • pjproject/trunk/pjsip/src/pjsip-ua/sip_reg.c

    r1946 r1959  
    3636 
    3737#define REFRESH_TIMER           1 
    38 #define DELAY_BEFORE_REFRESH    5 
     38#define DELAY_BEFORE_REFRESH    PJSIP_REGISTER_CLIENT_DELAY_BEFORE_REFRESH 
    3939#define THIS_FILE               "sip_reg.c" 
    4040 
     
    4444#define REGC_TSX_TIMEOUT        33000 
    4545 
     46enum { NOEXP = 0x1FFFFFFF }; 
     47 
     48static const pj_str_t XUID_PARAM_NAME = { "x-uid", 5 }; 
     49 
     50 
     51/* Current/pending operation */ 
     52enum regc_op 
     53{ 
     54    REGC_IDLE, 
     55    REGC_REGISTERING, 
     56    REGC_UNREGISTERING 
     57}; 
    4658 
    4759/** 
     
    5567    pj_bool_t                    has_tsx; 
    5668    pj_int32_t                   busy; 
     69    enum regc_op                 current_op; 
     70 
     71    pj_bool_t                    add_xuid_param; 
    5772 
    5873    void                        *token; 
     
    6681    pjsip_from_hdr              *from_hdr; 
    6782    pjsip_to_hdr                *to_hdr; 
    68     pjsip_hdr                    contact_hdr_list; 
     83    pjsip_contact_hdr            contact_hdr_list; 
     84    pjsip_contact_hdr            removed_contact_hdr_list; 
    6985    pjsip_expires_hdr           *expires_hdr; 
    70     pjsip_contact_hdr           *unreg_contact_hdr; 
    71     pjsip_expires_hdr           *unreg_expires_hdr; 
    7286    pj_uint32_t                  expires; 
    7387    pjsip_route_hdr              route_set; 
     
    93107 
    94108 
    95  
    96109PJ_DEF(pj_status_t) pjsip_regc_create( pjsip_endpoint *endpt, void *token, 
    97110                                       pjsip_regc_cb *cb, 
     
    115128    regc->cb = cb; 
    116129    regc->expires = PJSIP_REGC_EXPIRATION_NOT_SPECIFIED; 
     130    regc->add_xuid_param = pjsip_cfg()->regc.add_xuid_param; 
    117131 
    118132    status = pjsip_auth_clt_init(&regc->auth_sess, endpt, regc->pool, 0); 
     
    123137    pj_list_init(&regc->hdr_list); 
    124138    pj_list_init(&regc->contact_hdr_list); 
     139    pj_list_init(&regc->removed_contact_hdr_list); 
    125140 
    126141    /* Done */ 
     
    142157            pjsip_transport_dec_ref(regc->last_transport); 
    143158            regc->last_transport = NULL; 
     159        } 
     160        if (regc->timer.id != 0) { 
     161            pjsip_endpt_cancel_timer(regc->endpt, &regc->timer); 
     162            regc->timer.id = 0; 
    144163        } 
    145164        pjsip_endpt_release_pool(regc->endpt, regc->pool); 
     
    200219{ 
    201220    const pj_str_t CONTACT = { "Contact", 7 }; 
     221    pjsip_contact_hdr *h; 
    202222    int i; 
    203223     
    204     /* Clear existing contacts */ 
    205     pj_list_init(&regc->contact_hdr_list); 
    206  
     224    /* Save existing contact list to removed_contact_hdr_list and 
     225     * clear contact_hdr_list. 
     226     */ 
     227    pj_list_merge_last(&regc->removed_contact_hdr_list,  
     228                       &regc->contact_hdr_list); 
     229 
     230    /* Set the expiration of Contacts in to removed_contact_hdr_list  
     231     * zero. 
     232     */ 
     233    h = regc->removed_contact_hdr_list.next; 
     234    while (h != &regc->removed_contact_hdr_list) { 
     235        h->expires = 0; 
     236        h = h->next; 
     237    } 
     238 
     239    /* Process new contacts */ 
    207240    for (i=0; i<contact_cnt; ++i) { 
    208         pjsip_hdr *hdr; 
     241        pjsip_contact_hdr *hdr; 
    209242        pj_str_t tmp; 
    210243 
    211244        pj_strdup_with_null(regc->pool, &tmp, &contact[i]); 
    212         hdr = (pjsip_hdr*) 
     245        hdr = (pjsip_contact_hdr*) 
    213246              pjsip_parse_hdr(regc->pool, &CONTACT, tmp.ptr, tmp.slen, NULL); 
    214247        if (hdr == NULL) { 
     
    216249                     (int)tmp.slen, tmp.ptr)); 
    217250            return PJSIP_EINVALIDURI; 
     251        } 
     252 
     253        /* Find the new contact in old contact list. If found, remove 
     254         * the old header from the old header list. 
     255         */ 
     256        h = regc->removed_contact_hdr_list.next; 
     257        while (h != &regc->removed_contact_hdr_list) { 
     258            int rc; 
     259 
     260            rc = pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR,  
     261                               h->uri, hdr->uri); 
     262            if (rc == 0) { 
     263                /* Match */ 
     264                pj_list_erase(h); 
     265                break; 
     266            } 
     267 
     268            h = h->next; 
     269        } 
     270 
     271        /* If add_xuid_param option is enabled and Contact URI is sip/sips, 
     272         * add xuid parameter to assist matching the Contact URI in the  
     273         * REGISTER response later. 
     274         */ 
     275        if (regc->add_xuid_param && (PJSIP_URI_SCHEME_IS_SIP(hdr->uri) || 
     276                                     PJSIP_URI_SCHEME_IS_SIPS(hdr->uri)))  
     277        { 
     278            pjsip_param *xuid_param; 
     279            pjsip_sip_uri *sip_uri; 
     280 
     281            xuid_param = PJ_POOL_ZALLOC_T(regc->pool, pjsip_param); 
     282            xuid_param->name = XUID_PARAM_NAME; 
     283            pj_create_unique_string(regc->pool, &xuid_param->value); 
     284 
     285            sip_uri = pjsip_uri_get_uri(hdr->uri); 
     286            pj_list_push_back(&sip_uri->other_param, xuid_param); 
    218287        } 
    219288 
     
    288357    regc->cseq_hdr->cseq = pj_rand() % 0xFFFF; 
    289358    pjsip_method_set( &regc->cseq_hdr->method, PJSIP_REGISTER_METHOD); 
    290  
    291     /* Create "Contact" header used in unregistration. */ 
    292     regc->unreg_contact_hdr = pjsip_contact_hdr_create(regc->pool); 
    293     regc->unreg_contact_hdr->star = 1; 
    294  
    295     /* Create "Expires" header used in unregistration. */ 
    296     regc->unreg_expires_hdr = pjsip_expires_hdr_create( regc->pool, 0); 
    297359 
    298360    /* Done. */ 
     
    437499{ 
    438500    pjsip_msg *msg; 
    439     pjsip_hdr *hdr; 
     501    pjsip_contact_hdr *hdr; 
    440502    pj_status_t status; 
    441503    pjsip_tx_data *tdata; 
     
    457519    } 
    458520 
     521    /* Also add bindings which are to be removed */ 
     522    while (!pj_list_empty(&regc->removed_contact_hdr_list)) { 
     523        hdr = regc->removed_contact_hdr_list.next; 
     524        pjsip_msg_add_hdr(msg, (pjsip_hdr*) 
     525                               pjsip_hdr_clone(tdata->pool, hdr)); 
     526        pj_list_erase(hdr); 
     527    } 
     528     
     529 
    459530    if (regc->expires_hdr) 
    460531        pjsip_msg_add_hdr(msg, (pjsip_hdr*) 
     
    497568 
    498569    /* Add Contact headers. */ 
    499     hdr = regc->contact_hdr_list.next; 
    500     while (hdr != &regc->contact_hdr_list) { 
     570    hdr = (pjsip_hdr*)regc->contact_hdr_list.next; 
     571    while (hdr != (pjsip_hdr*)&regc->contact_hdr_list) { 
    501572        pjsip_msg_add_hdr(msg, (pjsip_hdr*) 
    502573                               pjsip_hdr_shallow_clone(tdata->pool, hdr)); 
     
    504575    } 
    505576 
    506     pjsip_msg_add_hdr( msg, (pjsip_hdr*)regc->unreg_expires_hdr); 
     577    /* Also add bindings which are to be removed */ 
     578    while (!pj_list_empty(&regc->removed_contact_hdr_list)) { 
     579        hdr = (pjsip_hdr*)regc->removed_contact_hdr_list.next; 
     580        pjsip_msg_add_hdr(msg, (pjsip_hdr*) 
     581                               pjsip_hdr_clone(tdata->pool, hdr)); 
     582        pj_list_erase(hdr); 
     583    } 
     584 
     585    /* Add Expires:0 header */ 
     586    hdr = (pjsip_hdr*) pjsip_expires_hdr_create(tdata->pool, 0); 
     587    pjsip_msg_add_hdr(msg, hdr); 
    507588 
    508589    *p_tdata = tdata; 
     
    514595{ 
    515596    pjsip_tx_data *tdata; 
     597    pjsip_contact_hdr *hcontact; 
     598    pjsip_hdr *hdr; 
    516599    pjsip_msg *msg; 
    517600    pj_status_t status; 
     
    529612 
    530613    msg = tdata->msg; 
    531     pjsip_msg_add_hdr( msg, (pjsip_hdr*)regc->unreg_contact_hdr); 
    532     pjsip_msg_add_hdr( msg, (pjsip_hdr*)regc->unreg_expires_hdr); 
     614 
     615    /* Clear removed_contact_hdr_list */ 
     616    pj_list_init(&regc->removed_contact_hdr_list); 
     617 
     618    /* Add Contact:* header */ 
     619    hcontact = pjsip_contact_hdr_create(tdata->pool); 
     620    hcontact->star = 1; 
     621    pjsip_msg_add_hdr(msg, (pjsip_hdr*)hcontact); 
     622     
     623    /* Add Expires:0 header */ 
     624    hdr = (pjsip_hdr*) pjsip_expires_hdr_create(tdata->pool, 0); 
     625    pjsip_msg_add_hdr(msg, hdr); 
    533626 
    534627    *p_tdata = tdata; 
     
    616709} 
    617710 
     711static pj_int32_t calculate_response_expiration(const pjsip_regc *regc, 
     712                                                const pjsip_rx_data *rdata, 
     713                                                unsigned *contact_cnt, 
     714                                                unsigned max_contact, 
     715                                                pjsip_contact_hdr *contacts[]) 
     716{ 
     717    pj_int32_t expiration = NOEXP; 
     718    const pjsip_msg *msg = rdata->msg_info.msg; 
     719    const pjsip_hdr *hdr; 
     720 
     721    /* Enumerate all Contact headers in the response */ 
     722    *contact_cnt = 0; 
     723    for (hdr=msg->hdr.next; hdr!=&msg->hdr; hdr=hdr->next) { 
     724        if (hdr->type == PJSIP_H_CONTACT &&  
     725            *contact_cnt < max_contact)  
     726        { 
     727            contacts[*contact_cnt] = (pjsip_contact_hdr*)hdr; 
     728            ++(*contact_cnt); 
     729        } 
     730    } 
     731 
     732    if (regc->current_op == REGC_REGISTERING) { 
     733        pj_bool_t has_our_contact = PJ_FALSE; 
     734        const pjsip_expires_hdr *expires; 
     735 
     736        /* Get Expires header */ 
     737        expires = (const pjsip_expires_hdr*) 
     738                  pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL); 
     739 
     740        /* Try to find the Contact URIs that we register, in the response 
     741         * to get the expires value. We'll try both with comparing the URI 
     742         * and comparing the extension param only. 
     743         */ 
     744        if (pjsip_cfg()->regc.check_contact || regc->add_xuid_param) { 
     745            unsigned i; 
     746            for (i=0; i<*contact_cnt; ++i) { 
     747                const pjsip_contact_hdr *our_hdr; 
     748 
     749                our_hdr = (const pjsip_contact_hdr*)  
     750                          regc->contact_hdr_list.next; 
     751 
     752                /* Match with our Contact header(s) */ 
     753                while ((void*)our_hdr != (void*)&regc->contact_hdr_list) { 
     754 
     755                    const pjsip_uri *uri1, *uri2; 
     756                    pj_bool_t matched = PJ_FALSE; 
     757 
     758                    /* Exclude the display name when comparing the URI  
     759                     * since server may not return it. 
     760                     */ 
     761                    uri1 = (const pjsip_uri*) 
     762                           pjsip_uri_get_uri(contacts[i]->uri); 
     763                    uri2 = (const pjsip_uri*) 
     764                           pjsip_uri_get_uri(our_hdr->uri); 
     765 
     766                    /* First try with exact matching, according to RFC 3261 
     767                     * Section 19.1.4 URI Comparison 
     768                     */ 
     769                    if (pjsip_cfg()->regc.check_contact) { 
     770                        matched = pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR, 
     771                                                uri1, uri2)==0; 
     772                    } 
     773 
     774                    /* If no match is found, try with matching the extension 
     775                     * parameter only if extension parameter was added. 
     776                     */ 
     777                    if (!matched && regc->add_xuid_param && 
     778                        (PJSIP_URI_SCHEME_IS_SIP(uri1) || 
     779                         PJSIP_URI_SCHEME_IS_SIPS(uri1)) &&  
     780                        (PJSIP_URI_SCHEME_IS_SIP(uri2) || 
     781                         PJSIP_URI_SCHEME_IS_SIPS(uri2)))  
     782                    { 
     783                        const pjsip_sip_uri *sip_uri1, *sip_uri2; 
     784                        const pjsip_param *p1, *p2; 
     785                 
     786                        sip_uri1 = (const pjsip_sip_uri*)uri1; 
     787                        sip_uri2 = (const pjsip_sip_uri*)uri2; 
     788 
     789                        p1 = pjsip_param_cfind(&sip_uri1->other_param, 
     790                                               &XUID_PARAM_NAME); 
     791                        p2 = pjsip_param_cfind(&sip_uri2->other_param, 
     792                                               &XUID_PARAM_NAME); 
     793                        matched = p1 && p2 && 
     794                                  pj_strcmp(&p1->value, &p2->value)==0; 
     795 
     796                    } 
     797 
     798                    if (matched) { 
     799                        has_our_contact = PJ_TRUE; 
     800 
     801                        if (contacts[i]->expires >= 0 &&  
     802                            contacts[i]->expires < expiration)  
     803                        { 
     804                            /* Get the lowest expiration time. */ 
     805                            expiration = contacts[i]->expires; 
     806                        } 
     807 
     808                        break; 
     809                    } 
     810 
     811                    our_hdr = our_hdr->next; 
     812 
     813                } /* while ((void.. */ 
     814 
     815            }  /* for (i=.. */ 
     816 
     817            /* If matching Contact header(s) are found but the 
     818             * header doesn't contain expires parameter, get the 
     819             * expiration value from the Expires header. And 
     820             * if Expires header is not present, get the expiration 
     821             * value from the request. 
     822             */ 
     823            if (has_our_contact && expiration == NOEXP) { 
     824                if (expires) { 
     825                    expiration = expires->ivalue; 
     826                } else if (regc->expires_hdr) { 
     827                    expiration = regc->expires_hdr->ivalue; 
     828                } else { 
     829                    /* We didn't request explicit expiration value, 
     830                     * and server doesn't specify it either. This  
     831                     * shouldn't happen unless we have a broken 
     832                     * registrar. 
     833                     */ 
     834                    expiration = 3600; 
     835                } 
     836            } 
     837 
     838        } 
     839 
     840        /* If we still couldn't get matching Contact header(s), it means 
     841         * there must be something wrong with the  registrar (e.g. it may 
     842         * have modified the URI's in the response, which is prohibited). 
     843         */ 
     844        if (expiration==NOEXP) { 
     845            /* If the number of Contact headers in the response matches  
     846             * ours, they're all probably ours. Get the expiration 
     847             * from there if this is the case, or from Expires header 
     848             * if we don't have exact Contact header count, or 
     849             * from the request as the last resort. 
     850             */ 
     851            unsigned our_contact_cnt; 
     852 
     853            our_contact_cnt = pj_list_size(&regc->contact_hdr_list); 
     854 
     855            if (*contact_cnt == our_contact_cnt && *contact_cnt && 
     856                contacts[0]->expires >= 0)  
     857            { 
     858                expiration = contacts[0]->expires; 
     859            } else if (expires) 
     860                expiration = expires->ivalue; 
     861            else if (regc->expires_hdr) 
     862                expiration = regc->expires_hdr->ivalue; 
     863            else 
     864                expiration = 3600; 
     865        } 
     866 
     867    } else { 
     868        /* Just assume that the unregistration has been successful. */ 
     869        expiration = 0; 
     870    } 
     871 
     872    /* Must have expiration value by now */ 
     873    pj_assert(expiration != NOEXP); 
     874 
     875    return expiration; 
     876} 
     877 
    618878static void tsx_callback(void *token, pjsip_event *event) 
    619879{ 
     
    645905        pjsip_rx_data *rdata = event->body.tsx_state.src.rdata; 
    646906        pjsip_tx_data *tdata; 
     907 
     908        /* reset current op */ 
     909        regc->current_op = REGC_IDLE; 
    647910 
    648911        status = pjsip_auth_clt_reinit_req( &regc->auth_sess, 
     
    683946         */ 
    684947 
    685         /* Nothing to do */ 
    686         ; 
     948        /* Just reset current op */ 
     949        regc->current_op = REGC_IDLE; 
    687950 
    688951    } else { 
    689         int contact_cnt = 0; 
     952        pjsip_rx_data *rdata; 
     953        pj_int32_t expiration = NOEXP; 
     954        unsigned contact_cnt = 0; 
    690955        pjsip_contact_hdr *contact[PJSIP_REGC_MAX_CONTACT]; 
    691         pjsip_rx_data *rdata; 
    692         enum { NOEXP = 0x1FFFFFFF }; 
    693         pj_int32_t expiration = NOEXP; 
    694956 
    695957        if (tsx->status_code/100 == 2) { 
    696             int i; 
    697             pjsip_contact_hdr *hdr; 
    698             pjsip_msg *msg; 
    699             pj_bool_t has_our_contact = PJ_FALSE; 
    700             pjsip_expires_hdr *expires; 
    701958 
    702959            rdata = event->body.tsx_state.src.rdata; 
    703             msg = rdata->msg_info.msg; 
    704  
    705             /* Record all Contact headers in the response */ 
    706             hdr = (pjsip_contact_hdr*) 
    707                   pjsip_msg_find_hdr( msg, PJSIP_H_CONTACT, NULL); 
    708             while (hdr) { 
    709                 contact[contact_cnt++] = hdr; 
    710                 hdr = hdr->next; 
    711                 if (hdr == (void*)&msg->hdr) 
    712                     break; 
    713                 hdr = (pjsip_contact_hdr*) 
    714                       pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, hdr); 
    715             } 
    716  
    717             /* Set default expiration value to the value of Expires hdr */ 
    718             expires = (pjsip_expires_hdr*) 
    719                       pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL); 
    720  
    721             if (expires) 
    722                 expiration = expires->ivalue; 
    723              
    724             /* Enumerate all Contact headers found in the response and 
    725              * find the Contact(s) that we register. 
    726              * 
    727              * Note:  
    728              *  by default we require that the exact same URI that we 
    729              *  register is returned in the 200/OK response (by exact, 
    730              *  meaning all URI components including transport param), 
    731              *  otherwise if we don't detect that our URI is there, we 
    732              *  treat registration as failed. 
    733              * 
    734              *  If your registrar server couldn't do this, you can 
    735              *  disable this exact URI checking. See the compile time 
    736              *  setting PJSIP_REGISTER_CLIENT_CHECK_CONTACT or the 
    737              *  corresponding run-time setting in pjsip_cfg(). 
    738              */ 
    739             for (i=0; i<contact_cnt && pjsip_cfg()->regc.check_contact; ++i) { 
    740                 pjsip_contact_hdr *our_contact; 
    741  
    742                 our_contact = (pjsip_contact_hdr*) 
    743                               regc->contact_hdr_list.next; 
    744  
    745                 while ((void*)our_contact != (void*)&regc->contact_hdr_list) { 
    746  
    747                     const pjsip_uri *uri1, *uri2; 
    748  
    749                     /* Compare URIs. 
    750                      * Exclude the display name when comparing the URI since 
    751                      * server may not return it. 
    752                      */ 
    753  
    754                     uri1=(const pjsip_uri*)pjsip_uri_get_uri(contact[i]->uri); 
    755                     uri2=(const pjsip_uri*)pjsip_uri_get_uri(our_contact->uri); 
    756                     if (pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR, uri1, uri2)==0) 
    757                     { 
    758                         has_our_contact = PJ_TRUE; 
    759  
    760                         if (contact[i]->expires >= 0 &&  
    761                             contact[i]->expires < expiration)  
    762                         { 
    763                             /* Get the lowest expiration time. */ 
    764                             expiration = contact[i]->expires; 
    765                         } 
    766                     } 
    767  
    768                     our_contact = our_contact->next; 
    769                 } 
    770             } 
    771  
    772             /* If regc.check_contact is disabled, and no Expires header  
    773              * has been found, but the server does return one single 
    774              * Contact header, assumes that the server is broken/unable to 
    775              * return the correct Contact. In this case, get the expiration 
    776              * from the single Contact header in the response. 
    777              */ 
    778             if (expiration==NOEXP && !pjsip_cfg()->regc.check_contact &&  
    779                 contact_cnt==1)  
    780             { 
    781                 if (contact[0]->expires >= 0) 
    782                     expiration = contact[0]->expires; 
    783             } 
    784  
    785             /* When the response doesn't contain our Contact header, that 
    786              * means we have been unregistered. 
    787              */ 
    788             if (pjsip_cfg()->regc.check_contact && !has_our_contact) 
    789                 expiration = 0; 
     960 
     961            /* Calculate expiration */ 
     962            expiration = calculate_response_expiration(regc, rdata,  
     963                                                       &contact_cnt, 
     964                                                       PJSIP_REGC_MAX_CONTACT, 
     965                                                       contact); 
     966 
     967            /* Mark operation as complete */ 
     968            regc->current_op = REGC_IDLE; 
    790969 
    791970            /* Schedule next registration */ 
    792             if (regc->auto_reg && expiration != 0 && expiration != NOEXP) { 
     971            if (regc->auto_reg && expiration > 0) { 
    793972                pj_time_val delay = { 0, 0}; 
    794973 
     
    820999        ++regc->busy; 
    8211000 
     1001        /* Update registration */ 
     1002        if (expiration==NOEXP) expiration=-1; 
     1003        regc->expires = expiration; 
     1004 
    8221005        /* Call callback. */ 
    823         if (expiration == NOEXP) expiration = -1; 
    8241006        call_callback(regc, PJ_SUCCESS, tsx->status_code,  
    8251007                      (rdata ? &rdata->msg_info.msg->line.status.reason  
     
    8421024    pj_status_t status; 
    8431025    pjsip_cseq_hdr *cseq_hdr; 
     1026    pjsip_expires_hdr *expires_hdr; 
    8441027    pj_uint32_t cseq; 
    8451028 
     
    8521035    } 
    8531036 
     1037    pj_assert(regc->current_op == REGC_IDLE); 
     1038 
    8541039    /* Invalidate message buffer. */ 
    8551040    pjsip_tx_data_invalidate_msg(tdata); 
     
    8611046    cseq_hdr->cseq = cseq; 
    8621047 
     1048    /* Find Expires header */ 
     1049    expires_hdr = (pjsip_expires_hdr*) 
     1050                  pjsip_msg_find_hdr(tdata->msg, PJSIP_H_EXPIRES, NULL); 
     1051 
    8631052    /* Bind to transport selector */ 
    8641053    pjsip_tx_data_set_transport(tdata, &regc->tp_sel); 
     
    8691058    regc->has_tsx = PJ_TRUE; 
    8701059    ++regc->busy; 
     1060 
     1061    /* Set current operation based on the value of Expires header */ 
     1062    if (expires_hdr && expires_hdr->ivalue==0) 
     1063        regc->current_op = REGC_UNREGISTERING; 
     1064    else 
     1065        regc->current_op = REGC_REGISTERING; 
     1066 
    8711067    status = pjsip_endpt_send_request(regc->endpt, tdata, REGC_TSX_TIMEOUT, 
    8721068                                      regc, &tsx_callback); 
Note: See TracChangeset for help on using the changeset viewer.