Ignore:
Timestamp:
Nov 11, 2006 4:16:04 PM (17 years ago)
Author:
bennylp
Message:

Attended call transfer implementation. The changes involves:

  • Added support for SIP Replaces extension (RFC 3891)
  • Added pjsua_call_xfer_replaces() to perform attended call transfer.
  • PJSUA checks and process Replaces header in incoming calls
  • Added pjsip_ua_find_dialog() API.
  • Added pjsip_endpt_has_capability() API.
  • Added pjsip_endpt_send_response2() API.
  • etc.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • pjproject/trunk/pjsip/src/pjsua-lib/pjsua_call.c

    r782 r797  
    373373    pj_str_t contact; 
    374374    pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata); 
     375    pjsip_dialog *replaced_dlg = NULL; 
    375376    pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata); 
    376377    pjsip_msg *msg = rdata->msg_info.msg; 
     
    420421    pj_gettimeofday(&call->start_time); 
    421422 
     423    /* Check INVITE request for Replaces header. If Replaces header is 
     424     * present, the function will make sure that we can handle the request. 
     425     */ 
     426    status = pjsip_replaces_verify_request(rdata, &replaced_dlg, PJ_FALSE, 
     427                                           &response); 
     428    if (status != PJ_SUCCESS) { 
     429        /* 
     430         * Something wrong with the Replaces header. 
     431         */ 
     432        if (response) { 
     433            pjsip_response_addr res_addr; 
     434 
     435            pjsip_get_response_addr(response->pool, rdata, &res_addr); 
     436            pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,  
     437                                      NULL, NULL); 
     438 
     439        } else { 
     440 
     441            /* Respond with 500 (Internal Server Error) */ 
     442            pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL, 
     443                                          NULL, NULL); 
     444        } 
     445 
     446        PJSUA_UNLOCK(); 
     447        return PJ_TRUE; 
     448    } 
     449 
     450    /* If this INVITE request contains Replaces header, notify application 
     451     * about the request so that application can do subsequent checking 
     452     * if it wants to. 
     453     */ 
     454    if (replaced_dlg != NULL && pjsua_var.ua_cfg.cb.on_call_replace_request) { 
     455        pjsua_call *replaced_call; 
     456        int st_code = 200; 
     457        pj_str_t st_text = { "OK", 2 }; 
     458 
     459        /* Get the replaced call instance */ 
     460        replaced_call = replaced_dlg->mod_data[pjsua_var.mod.id]; 
     461 
     462        /* Notify application */ 
     463        pjsua_var.ua_cfg.cb.on_call_replace_request(replaced_call->index, 
     464                                                    rdata, &st_code, &st_text); 
     465 
     466        /* Must specify final response */ 
     467        PJ_ASSERT_ON_FAIL(st_code >= 200, st_code = 200); 
     468 
     469        /* Check if application rejects this request. */ 
     470        if (st_code >= 300) { 
     471 
     472            if (st_text.slen == 2) 
     473                st_text = *pjsip_get_status_text(st_code); 
     474 
     475            pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,  
     476                                st_code, &st_text, NULL, NULL, NULL); 
     477            PJSUA_UNLOCK(); 
     478            return PJ_TRUE; 
     479        } 
     480    } 
     481 
     482 
    422483    /* Get media capability from media endpoint: */ 
    423484    status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt,  
     
    546607 
    547608 
    548     /* Notify application */ 
    549     if (pjsua_var.ua_cfg.cb.on_incoming_call) 
    550         pjsua_var.ua_cfg.cb.on_incoming_call(acc_id, call_id, rdata); 
     609    /* Check if this request should replace existing call */ 
     610    if (replaced_dlg) { 
     611        pjsip_inv_session *replaced_inv; 
     612        struct pjsua_call *replaced_call; 
     613        pjsip_tx_data *tdata; 
     614 
     615        /* Get the invite session in the dialog */ 
     616        replaced_inv = pjsip_dlg_get_inv_session(replaced_dlg); 
     617 
     618        /* Get the replaced call instance */ 
     619        replaced_call = replaced_dlg->mod_data[pjsua_var.mod.id]; 
     620 
     621        /* Notify application */ 
     622        if (pjsua_var.ua_cfg.cb.on_call_replaced) 
     623            pjsua_var.ua_cfg.cb.on_call_replaced(replaced_call->index, 
     624                                                 call_id); 
     625 
     626        PJ_LOG(4,(THIS_FILE, "Answering replacement call %d with 200/OK", 
     627                             call_id)); 
     628 
     629        /* Answer the new call with 200 response */ 
     630        status = pjsip_inv_answer(inv, 200, NULL, NULL, &tdata); 
     631        if (status == PJ_SUCCESS) 
     632            status = pjsip_inv_send_msg(inv, tdata); 
     633 
     634        if (status != PJ_SUCCESS) 
     635            pjsua_perror(THIS_FILE, "Error answering session", status); 
     636 
     637 
     638        PJ_LOG(4,(THIS_FILE, "Disconnecting replaced call %d", 
     639                             replaced_call->index)); 
     640 
     641        /* Disconnect replaced invite session */ 
     642        status = pjsip_inv_end_session(replaced_inv, PJSIP_SC_GONE, NULL, 
     643                                       &tdata); 
     644        if (status == PJ_SUCCESS && tdata) 
     645            status = pjsip_inv_send_msg(replaced_inv, tdata); 
     646 
     647        if (status != PJ_SUCCESS) 
     648            pjsua_perror(THIS_FILE, "Error terminating session", status); 
     649 
     650 
     651    } else { 
     652 
     653        /* Notify application */ 
     654        if (pjsua_var.ua_cfg.cb.on_incoming_call) 
     655            pjsua_var.ua_cfg.cb.on_incoming_call(acc_id, call_id, rdata); 
     656 
     657    } 
     658 
    551659 
    552660    /* This INVITE request has been handled. */ 
     
    10661174    pjsua_call *call; 
    10671175    pjsip_dialog *dlg; 
     1176    pjsip_generic_string_hdr *gs_hdr; 
     1177    const pj_str_t str_ref_by = { "Referred-By", 11 }; 
    10681178    struct pjsip_evsub_user xfer_cb; 
    10691179    pj_status_t status; 
     
    11021212    } 
    11031213 
     1214    /* Add Referred-By header */ 
     1215    gs_hdr = pjsip_generic_string_hdr_create(tdata->pool, &str_ref_by, 
     1216                                             &dlg->local.info_str); 
     1217    pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)gs_hdr); 
     1218 
     1219 
    11041220    /* Add additional headers etc */ 
    11051221    pjsua_process_msg_data( tdata, msg_data); 
     
    11221238    return PJ_SUCCESS; 
    11231239 
     1240} 
     1241 
     1242 
     1243/* 
     1244 * Initiate attended call transfer to the specified address. 
     1245 */ 
     1246PJ_DEF(pj_status_t) pjsua_call_xfer_replaces( pjsua_call_id call_id,  
     1247                                              pjsua_call_id dest_call_id, 
     1248                                              unsigned options, 
     1249                                              const pjsua_msg_data *msg_data) 
     1250{ 
     1251    pjsua_call *dest_call; 
     1252    pjsip_dialog *dest_dlg; 
     1253    char str_dest_buf[512]; 
     1254    pj_str_t str_dest; 
     1255    int len; 
     1256    pjsip_uri *uri; 
     1257    pj_status_t status; 
     1258     
     1259 
     1260    PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls, 
     1261                     PJ_EINVAL); 
     1262    PJ_ASSERT_RETURN(dest_call_id>=0 &&  
     1263                      dest_call_id<(int)pjsua_var.ua_cfg.max_calls, 
     1264                     PJ_EINVAL); 
     1265     
     1266    status = acquire_call("pjsua_call_xfer_replaces()", dest_call_id,  
     1267                          &dest_call, &dest_dlg); 
     1268    if (status != PJ_SUCCESS) 
     1269        return status; 
     1270         
     1271    /*  
     1272     * Create REFER destination URI with Replaces field. 
     1273     */ 
     1274 
     1275    /* Make sure we have sufficient buffer's length */ 
     1276    PJ_ASSERT_RETURN( dest_dlg->remote.info_str.slen + 
     1277                      dest_dlg->call_id->id.slen + 
     1278                      dest_dlg->remote.info->tag.slen + 
     1279                      dest_dlg->local.info->tag.slen + 32  
     1280                      < sizeof(str_dest_buf), PJSIP_EURITOOLONG); 
     1281 
     1282    /* Print URI */ 
     1283    str_dest_buf[0] = '<'; 
     1284    str_dest.slen = 1; 
     1285 
     1286    uri = pjsip_uri_get_uri(dest_dlg->remote.info->uri); 
     1287    len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri,  
     1288                          str_dest_buf+1, sizeof(str_dest_buf)-1); 
     1289    if (len < 0) 
     1290        return PJSIP_EURITOOLONG; 
     1291 
     1292    str_dest.slen += len; 
     1293 
     1294 
     1295    /* Build the URI */ 
     1296    len = pj_ansi_snprintf(str_dest_buf + str_dest.slen,  
     1297                           sizeof(str_dest_buf) - str_dest.slen, 
     1298                           "?%s" 
     1299                           "Replaces=%.*s" 
     1300                           "%%3Bto-tag%%3D%.*s" 
     1301                           "%%3Bfrom-tag%%3D%.*s>", 
     1302                           ((options&PJSUA_XFER_NO_REQUIRE_REPLACES) ? 
     1303                            "" : "Require=replaces&"), 
     1304                           (int)dest_dlg->call_id->id.slen, 
     1305                           dest_dlg->call_id->id.ptr, 
     1306                           (int)dest_dlg->remote.info->tag.slen, 
     1307                           dest_dlg->remote.info->tag.ptr, 
     1308                           (int)dest_dlg->local.info->tag.slen, 
     1309                           dest_dlg->local.info->tag.ptr); 
     1310 
     1311    PJ_ASSERT_RETURN(len > 0 && len <= (int)sizeof(str_dest_buf)-str_dest.slen, 
     1312                     PJSIP_EURITOOLONG); 
     1313     
     1314    str_dest.ptr = str_dest_buf; 
     1315    str_dest.slen += len; 
     1316 
     1317    pjsip_dlg_dec_lock(dest_dlg); 
     1318     
     1319    return pjsua_call_xfer(call_id, &str_dest, msg_data); 
    11241320} 
    11251321 
     
    23412537    const pj_str_t str_refer_to = { "Refer-To", 8}; 
    23422538    const pj_str_t str_refer_sub = { "Refer-Sub", 9 }; 
     2539    const pj_str_t str_ref_by = { "Referred-By", 11 }; 
    23432540    pjsip_generic_string_hdr *refer_to; 
    23442541    pjsip_generic_string_hdr *refer_sub; 
     2542    pjsip_hdr *ref_by_hdr; 
    23452543    pj_bool_t no_refer_sub = PJ_FALSE; 
    23462544    char *uri; 
     2545    pjsua_msg_data msg_data; 
    23472546    pj_str_t tmp; 
    23482547    pjsip_status_code code; 
     
    23732572    } 
    23742573 
     2574    /* Find optional Referred-By header (to be copied onto outgoing INVITE 
     2575     * request. 
     2576     */ 
     2577    ref_by_hdr = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_ref_by,  
     2578                                            NULL); 
    23752579 
    23762580    /* Notify callback */ 
     
    24912695    uri[refer_to->hvalue.slen] = '\0'; 
    24922696 
     2697    /* Init msg_data */ 
     2698    pjsua_msg_data_init(&msg_data); 
     2699 
     2700    /* If Referred-By header is present in the REFER request, copy this 
     2701     * to the outgoing INVITE request. 
     2702     */ 
     2703    if (ref_by_hdr != NULL) { 
     2704        pjsip_hdr *dup = pjsip_hdr_clone(rdata->tp_info.pool, ref_by_hdr); 
     2705        pj_list_push_back(&msg_data.hdr_list, dup); 
     2706    } 
     2707 
    24932708    /* Now make the outgoing call. */ 
    24942709    tmp = pj_str(uri); 
    24952710    status = pjsua_call_make_call(existing_call->acc_id, &tmp, 0, 
    2496                                   existing_call->user_data, NULL,  
     2711                                  existing_call->user_data, &msg_data,  
    24972712                                  &new_call); 
    24982713    if (status != PJ_SUCCESS) { 
Note: See TracChangeset for help on using the changeset viewer.