Ignore:
Timestamp:
Nov 27, 2008 12:06:46 AM (15 years ago)
Author:
bennylp
Message:

Ticket #10: handle redirection response in the invite session

File:
1 edited

Legend:

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

    r2362 r2370  
    226226            inv->last_ack = NULL; 
    227227        } 
     228        if (inv->invite_req) { 
     229            pjsip_tx_data_dec_ref(inv->invite_req); 
     230            inv->invite_req = NULL; 
     231        } 
    228232        pjsip_100rel_end_session(inv); 
    229233        pjsip_dlg_dec_session(inv->dlg, &mod_inv.mod); 
     
    12201224 
    12211225 
     1226/* 
     1227 * Restart UAC session, possibly because app or us wants to re-send the  
     1228 * INVITE request due to 401/407 challenge or 3xx response. 
     1229 */ 
     1230PJ_DEF(pj_status_t) pjsip_inv_uac_restart(pjsip_inv_session *inv, 
     1231                                          pj_bool_t new_offer) 
     1232{ 
     1233    PJ_ASSERT_RETURN(inv, PJ_EINVAL); 
     1234 
     1235    inv->state = PJSIP_INV_STATE_NULL; 
     1236    inv->invite_tsx = NULL; 
     1237    if (inv->last_answer) { 
     1238        pjsip_tx_data_dec_ref(inv->last_answer); 
     1239        inv->last_answer = NULL; 
     1240    } 
     1241 
     1242    if (new_offer && inv->neg) { 
     1243        pjmedia_sdp_neg_state neg_state; 
     1244 
     1245        neg_state = pjmedia_sdp_neg_get_state(inv->neg); 
     1246        if (neg_state == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER) { 
     1247            pjmedia_sdp_neg_cancel_offer(inv->neg); 
     1248        } 
     1249    } 
     1250 
     1251    return PJ_SUCCESS; 
     1252} 
     1253 
     1254 
    12221255static void *clone_sdp(pj_pool_t *pool, const void *data, unsigned len) 
    12231256{ 
     
    18901923 
    18911924    return PJ_SUCCESS; 
     1925} 
     1926 
     1927/* Following redirection recursion, get next target from the target set and 
     1928 * notify user. 
     1929 * 
     1930 * Returns PJ_FALSE if recursion fails (either because there's no more target 
     1931 * or user rejects the recursion). If we return PJ_FALSE, caller should 
     1932 * disconnect the session. 
     1933 * 
     1934 * Note: 
     1935 *   the event 'e' argument may be NULL. 
     1936 */ 
     1937static pj_bool_t inv_uac_recurse(pjsip_inv_session *inv, int code, 
     1938                                 const pj_str_t *reason, pjsip_event *e) 
     1939{ 
     1940    pjsip_redirect_op op = PJSIP_REDIRECT_ACCEPT; 
     1941    pjsip_target *target; 
     1942 
     1943    /* Won't redirect if the callback is not implemented. */ 
     1944    if (mod_inv.cb.on_redirected == NULL) 
     1945        return PJ_FALSE; 
     1946 
     1947    if (reason == NULL) 
     1948        reason = pjsip_get_status_text(code); 
     1949 
     1950    /* Set status of current target */ 
     1951    pjsip_target_assign_status(inv->dlg->target_set.current, inv->dlg->pool, 
     1952                               code, reason); 
     1953 
     1954    /* Fetch next target from the target set. We only want to 
     1955     * process SIP/SIPS URI for now. 
     1956     */ 
     1957    for (;;) { 
     1958        target = pjsip_target_set_get_next(&inv->dlg->target_set); 
     1959        if (target == NULL) { 
     1960            /* No more target. */ 
     1961            return PJ_FALSE; 
     1962        } 
     1963 
     1964        if (!PJSIP_URI_SCHEME_IS_SIP(target->uri) && 
     1965            !PJSIP_URI_SCHEME_IS_SIPS(target->uri)) 
     1966        { 
     1967            code = PJSIP_SC_UNSUPPORTED_URI_SCHEME; 
     1968            reason = pjsip_get_status_text(code); 
     1969 
     1970            /* Mark this target as unusable and fetch next target. */ 
     1971            pjsip_target_assign_status(target, inv->dlg->pool, code, reason); 
     1972        } else { 
     1973            /* Found a target */ 
     1974            break; 
     1975        } 
     1976    } 
     1977 
     1978    /* We have target in 'target'. Set this target as current target 
     1979     * and notify callback.  
     1980     */ 
     1981    pjsip_target_set_set_current(&inv->dlg->target_set, target); 
     1982 
     1983    (*mod_inv.cb.on_redirected)(inv, target->uri, &op, e); 
     1984 
     1985 
     1986    /* Check what the application wants to do now */ 
     1987    switch (op) { 
     1988    case PJSIP_REDIRECT_ACCEPT: 
     1989    case PJSIP_REDIRECT_STOP: 
     1990        /* Must increment session counter, that's the convention of the  
     1991         * pjsip_inv_process_redirect(). 
     1992         */ 
     1993        pjsip_dlg_inc_session(inv->dlg, &mod_inv.mod); 
     1994 
     1995        /* Act on the recursion */ 
     1996        pjsip_inv_process_redirect(inv, op, e); 
     1997        return PJ_TRUE; 
     1998 
     1999    case PJSIP_REDIRECT_PENDING: 
     2000        /* Increment session so that the dialog/session is not destroyed  
     2001         * while we're waiting for user confirmation. 
     2002         */ 
     2003        pjsip_dlg_inc_session(inv->dlg, &mod_inv.mod); 
     2004 
     2005        /* Also clear the invite_tsx variable, otherwise when this tsx is 
     2006         * terminated, it will also terminate the session. 
     2007         */ 
     2008        inv->invite_tsx = NULL; 
     2009 
     2010        /* Done. The processing will continue once the application calls 
     2011         * pjsip_inv_process_redirect(). 
     2012         */ 
     2013        return PJ_TRUE; 
     2014 
     2015    case PJSIP_REDIRECT_REJECT: 
     2016        /* Recursively call  this function again to fetch next target, if any. 
     2017         */ 
     2018        return inv_uac_recurse(inv, PJSIP_SC_REQUEST_TERMINATED, NULL, e); 
     2019 
     2020    } 
     2021 
     2022    pj_assert(!"Should not reach here"); 
     2023    return PJ_FALSE; 
     2024} 
     2025 
     2026 
     2027/* Process redirection/recursion */ 
     2028PJ_DEF(pj_status_t) pjsip_inv_process_redirect( pjsip_inv_session *inv, 
     2029                                                pjsip_redirect_op op, 
     2030                                                pjsip_event *e) 
     2031{ 
     2032    const pjsip_status_code cancel_code = PJSIP_SC_REQUEST_TERMINATED; 
     2033    pjsip_event usr_event; 
     2034    pj_status_t status = PJ_SUCCESS; 
     2035 
     2036    PJ_ASSERT_RETURN(inv && op != PJSIP_REDIRECT_PENDING, PJ_EINVAL); 
     2037 
     2038    if (e == NULL) { 
     2039        PJSIP_EVENT_INIT_USER(usr_event, NULL, NULL, NULL, NULL); 
     2040        e = &usr_event; 
     2041    } 
     2042 
     2043    pjsip_dlg_inc_lock(inv->dlg); 
     2044 
     2045    /* Decrement session. That's the convention here to prevent the dialog  
     2046     * or session from being destroyed while we're waiting for user 
     2047     * confirmation. 
     2048     */ 
     2049    pjsip_dlg_dec_session(inv->dlg, &mod_inv.mod); 
     2050 
     2051    /* See what the application wants to do now */ 
     2052    switch (op) { 
     2053    case PJSIP_REDIRECT_ACCEPT: 
     2054        /* User accept the redirection. Reset the session and resend the  
     2055         * INVITE request. 
     2056         */ 
     2057        { 
     2058            pjsip_tx_data *tdata; 
     2059            pjsip_via_hdr *via; 
     2060 
     2061            /* Get the original INVITE request. */ 
     2062            tdata = inv->invite_req; 
     2063            pjsip_tx_data_add_ref(tdata); 
     2064 
     2065            /* Restore strict route set. 
     2066             * See http://trac.pjsip.org/repos/ticket/492 
     2067             */ 
     2068            pjsip_restore_strict_route_set(tdata); 
     2069 
     2070            /* Set target */ 
     2071            tdata->msg->line.req.uri =  
     2072               pjsip_uri_clone(tdata->pool, inv->dlg->target_set.current->uri); 
     2073 
     2074            /* Remove branch param in Via header. */ 
     2075            via = (pjsip_via_hdr*)  
     2076                  pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL); 
     2077            via->branch_param.slen = 0; 
     2078 
     2079            /* Must invalidate the message! */ 
     2080            pjsip_tx_data_invalidate_msg(tdata); 
     2081 
     2082            /* Reset the session */ 
     2083            pjsip_inv_uac_restart(inv, PJ_FALSE); 
     2084 
     2085            /* (re)Send the INVITE request */ 
     2086            status = pjsip_inv_send_msg(inv, tdata); 
     2087        } 
     2088        break; 
     2089 
     2090    case PJSIP_REDIRECT_STOP: 
     2091        /* User doesn't want the redirection. Disconnect the session now. */ 
     2092        inv_set_cause(inv, cancel_code, pjsip_get_status_text(cancel_code)); 
     2093        inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e); 
     2094 
     2095        /* Caller should expect that the invite session is gone now, so 
     2096         * we don't need to set status to PJSIP_ESESSIONTERMINATED here. 
     2097         */ 
     2098        break; 
     2099 
     2100    case PJSIP_REDIRECT_REJECT: 
     2101        /* Current target is rejected. Fetch next target if any. */ 
     2102        if (inv_uac_recurse(inv, cancel_code, NULL, NULL) == PJ_FALSE) { 
     2103            inv_set_cause(inv, cancel_code,  
     2104                          pjsip_get_status_text(cancel_code)); 
     2105            inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e); 
     2106 
     2107            /* Tell caller that the invite session is gone now */ 
     2108            status = PJSIP_ESESSIONTERMINATED; 
     2109        } 
     2110        break; 
     2111 
     2112 
     2113    case PJSIP_REDIRECT_PENDING: 
     2114        pj_assert(!"Should not happen"); 
     2115        break; 
     2116    } 
     2117 
     2118 
     2119    pjsip_dlg_dec_lock(inv->dlg); 
     2120 
     2121    return status; 
    18922122} 
    18932123 
     
    25532783        if (dlg->role == PJSIP_ROLE_UAC) { 
    25542784 
     2785            /* Save the original INVITE request, if on_redirected() callback 
     2786             * is implemented. We may need to resend the INVITE if we receive 
     2787             * redirection response. 
     2788             */ 
     2789            if (mod_inv.cb.on_redirected) { 
     2790                if (inv->invite_req) { 
     2791                    pjsip_tx_data_dec_ref(inv->invite_req); 
     2792                    inv->invite_req = NULL; 
     2793                } 
     2794                inv->invite_req = tsx->last_tx; 
     2795                pjsip_tx_data_add_ref(inv->invite_req); 
     2796            } 
     2797 
    25552798            switch (tsx->state) { 
    25562799            case PJSIP_TSX_STATE_CALLING: 
     
    26742917 
    26752918 
     2919/* Handle call rejection, especially with regard to processing call 
     2920 * redirection. We need to handle the following scenarios: 
     2921 *  - 3xx response is received -- see if on_redirected() callback is 
     2922 *    implemented. If so, add the Contact URIs in the response to the 
     2923 *    target set and notify user. 
     2924 *  - 4xx - 6xx resposne is received -- see if we're currently recursing, 
     2925 *    if so fetch the next target if any and notify the on_redirected() 
     2926 *    callback. 
     2927 *  - for other cases -- disconnect the session. 
     2928 */ 
     2929static void handle_uac_call_rejection(pjsip_inv_session *inv, pjsip_event *e) 
     2930{ 
     2931    pjsip_transaction *tsx = e->body.tsx_state.tsx; 
     2932    pj_status_t status; 
     2933     
     2934    if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 300)) { 
     2935 
     2936        if (mod_inv.cb.on_redirected == NULL) { 
     2937 
     2938            /* Redirection callback is not implemented, disconnect the 
     2939             * call. 
     2940             */ 
     2941            goto terminate_session; 
     2942 
     2943        } else { 
     2944            const pjsip_msg *res_msg; 
     2945 
     2946            res_msg = e->body.tsx_state.src.rdata->msg_info.msg; 
     2947 
     2948            /* Gather all Contact URI's in the response and add them 
     2949             * to target set. The function will take care of removing 
     2950             * duplicate URI's. 
     2951             */ 
     2952            pjsip_target_set_add_from_msg(&inv->dlg->target_set,  
     2953                                          inv->dlg->pool, res_msg); 
     2954 
     2955            /* Recurse to alternate targets if application allows us */ 
     2956            if (!inv_uac_recurse(inv, tsx->status_code, &tsx->status_text, e)) 
     2957            { 
     2958                /* Recursion fails, terminate session now */ 
     2959                goto terminate_session; 
     2960            } 
     2961 
     2962            /* Done */ 
     2963        } 
     2964 
     2965    } else if ((tsx->status_code==401 || tsx->status_code==407) && 
     2966                !inv->cancelling)  
     2967    { 
     2968 
     2969        /* Handle authentication failure: 
     2970         * Resend the request with Authorization header. 
     2971         */ 
     2972        pjsip_tx_data *tdata; 
     2973 
     2974        status = pjsip_auth_clt_reinit_req(&inv->dlg->auth_sess,  
     2975                                           e->body.tsx_state.src.rdata, 
     2976                                           tsx->last_tx, 
     2977                                           &tdata); 
     2978 
     2979        if (status != PJ_SUCCESS) { 
     2980 
     2981            /* Does not have proper credentials. If we are currently  
     2982             * recursing, try the next target. Otherwise end the session. 
     2983             */ 
     2984            if (!inv_uac_recurse(inv, tsx->status_code, &tsx->status_text, e)) 
     2985            { 
     2986                /* Recursion fails, terminate session now */ 
     2987                goto terminate_session; 
     2988            } 
     2989 
     2990        } else { 
     2991 
     2992            /* Restart session. */ 
     2993            pjsip_inv_uac_restart(inv, PJ_FALSE); 
     2994 
     2995            /* Send the request. */ 
     2996            status = pjsip_inv_send_msg(inv, tdata); 
     2997        } 
     2998 
     2999    } else if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 600)) { 
     3000        /* Global error */ 
     3001        goto terminate_session; 
     3002 
     3003    } else { 
     3004        /* See if we have alternate target to try */ 
     3005        if (!inv_uac_recurse(inv, tsx->status_code, &tsx->status_text, e)) { 
     3006            /* Recursion fails, terminate session now */ 
     3007            goto terminate_session; 
     3008        } 
     3009    } 
     3010    return; 
     3011 
     3012terminate_session: 
     3013    inv_set_cause(inv, tsx->status_code, &tsx->status_text); 
     3014    inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e); 
     3015} 
     3016 
     3017 
    26763018/* 
    26773019 * State CALLING is after sending initial INVITE request but before 
     
    27363078                                              e->body.tsx_state.src.rdata); 
    27373079 
    2738             } else if ((tsx->status_code==401 || tsx->status_code==407) && 
    2739                         !inv->cancelling)  
    2740             { 
    2741  
    2742                 /* Handle authentication failure: 
    2743                  * Resend the request with Authorization header. 
    2744                  */ 
    2745                 pjsip_tx_data *tdata; 
    2746  
    2747                 status = pjsip_auth_clt_reinit_req(&inv->dlg->auth_sess,  
    2748                                                    e->body.tsx_state.src.rdata, 
    2749                                                    tsx->last_tx, 
    2750                                                    &tdata); 
    2751  
    2752                 if (status != PJ_SUCCESS) { 
    2753  
    2754                     /* Does not have proper credentials.  
    2755                      * End the session. 
    2756                      */ 
    2757                     inv_set_cause(inv, tsx->status_code, &tsx->status_text); 
    2758                     inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e); 
    2759  
    2760                 } else { 
    2761  
    2762                     /* Restart session. */ 
    2763                     inv->state = PJSIP_INV_STATE_NULL; 
    2764                     inv->invite_tsx = NULL; 
    2765                     if (inv->last_answer) { 
    2766                         pjsip_tx_data_dec_ref(inv->last_answer); 
    2767                         inv->last_answer = NULL; 
    2768                     } 
    2769  
    2770                     /* Send the request. */ 
    2771                     status = pjsip_inv_send_msg(inv, tdata); 
    2772                 } 
    2773  
    27743080            } else { 
    2775  
    2776                 inv_set_cause(inv, tsx->status_code, &tsx->status_text); 
    2777                 inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e); 
    2778  
     3081                handle_uac_call_rejection(inv, e); 
    27793082            } 
    27803083            break; 
     
    29423245                                                  e->body.tsx_state.src.rdata); 
    29433246                } 
     3247 
     3248            } else if (tsx->role == PJSIP_ROLE_UAC) { 
     3249 
     3250                handle_uac_call_rejection(inv, e); 
    29443251 
    29453252            } else { 
Note: See TracChangeset for help on using the changeset viewer.