/* It does what it says... */ static void unsubscribe_buddy_presence(unsigned index) { pjsua_buddy *buddy; pjsip_tx_data *tdata; pj_status_t status; buddy = &pjsua_var.buddy[index]; if (buddy->sub == NULL) return; if (pjsip_evsub_get_state(buddy->sub) == PJSIP_EVSUB_STATE_TERMINATED) { pjsua_var.buddy[index].sub = NULL; return; } status = pjsip_pres_initiate( buddy->sub, 0, &tdata); if (status == PJ_SUCCESS) { pjsua_process_msg_data(tdata, NULL); status = pjsip_pres_send_request( buddy->sub, tdata ); } if (status != PJ_SUCCESS && buddy->sub) { pjsip_pres_terminate(buddy->sub, PJ_FALSE); buddy->sub = NULL; pjsua_perror(THIS_FILE, "Unable to unsubscribe presence", status); } }
static void ui_write_settings() { char settings[2000]; int len; char buf[128]; len = write_settings(&app_config, settings, sizeof(settings)); if (len < 1) PJ_LOG(1,(THIS_FILE, "Error: not enough buffer")); else { pj_oshandle_t fd; pj_status_t status; status = pj_file_open(app_config.pool, buf, PJ_O_WRONLY, &fd); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to open file", status); } else { pj_ssize_t size = len; pj_file_write(fd, settings, &size); pj_file_close(fd); printf("Settings successfully written to '%s'\n", buf); } } }
static void ui_send_dtmf_2833() { if (current_call == -1) { PJ_LOG(3,(THIS_FILE, "No current call")); } else if (!pjsua_call_has_media(current_call)) { PJ_LOG(3,(THIS_FILE, "Media is not established yet!")); } else { pj_str_t digits; int call = current_call; pj_status_t status; char buf[128]; if (!simple_input("DTMF strings to send (0-9*R#A-B)", buf, sizeof(buf))) { return; } if (call != current_call) { puts("Call has been disconnected"); return; } digits = pj_str(buf); status = pjsua_call_dial_dtmf(current_call, &digits); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to send DTMF", status); } else { puts("DTMF digits enqueued for transmission"); } } }
static void ui_add_account(pjsua_transport_config *rtp_cfg) { char id[80], registrar[80], realm[80], uname[80], passwd[30]; pjsua_acc_config acc_cfg; pj_status_t status; if (!simple_input("Your SIP URL:", id, sizeof(id))) return; if (!simple_input("URL of the registrar:", registrar, sizeof(registrar))) return; if (!simple_input("Auth Realm:", realm, sizeof(realm))) return; if (!simple_input("Auth Username:"******"Auth Password:"******"Digest"); acc_cfg.cred_info[0].realm = pj_str(realm); acc_cfg.cred_info[0].username = pj_str(uname); acc_cfg.cred_info[0].data_type = 0; acc_cfg.cred_info[0].data = pj_str(passwd); acc_cfg.rtp_cfg = *rtp_cfg; app_config_init_video(&acc_cfg); status = pjsua_acc_add(&acc_cfg, PJ_TRUE, NULL); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error adding new account", status); } }
/* Display error and exit application */ static void error_exit (const char *title, pj_status_t status) { VFVW_LOG("pjsip error [%s, %d]", title, status); pjsua_perror ("voice app", title, status); pjsua_destroy (); exit (1); }
/* * Init presence */ pj_status_t pjsua_pres_init() { unsigned i; pj_status_t status; status = pjsip_endpt_register_module( pjsua_var.endpt, &mod_pjsua_pres); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to register pjsua presence module", status); } for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) { reset_buddy(i); } return status; }
// display error and exit application static void error_exit(const char *title, pj_status_t status) { if (!app_exiting) { app_exiting = 1; pjsua_perror("SIP Call", title, status); // check if player/recorder is active and stop them if (play_id != -1) pjsua_player_destroy(play_id); if (rec_id != -1) pjsua_recorder_destroy(rec_id); // hangup open calls and stop pjsua pjsua_call_hangup_all(); pjsua_destroy(); exit(1); } }
/* * Send arbitrary request to remote host */ void send_request(char *cstr_method, const pj_str_t *dst_uri) { pj_str_t str_method; pjsip_method method; pjsip_tx_data *tdata; pjsip_endpoint *endpt; pj_status_t status; endpt = pjsua_get_pjsip_endpt(); str_method = pj_str(cstr_method); pjsip_method_init_np(&method, &str_method); status = pjsua_acc_create_request(current_acc, &method, dst_uri, &tdata); status = pjsip_endpt_send_request(endpt, tdata, -1, NULL, NULL); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to send request", status); return; } }
void PjsipCallFront::run() { pj_thread_desc initdec; pj_thread_t* thread = 0; pj_status_t status; int evt_cnt = 0; Q_ASSERT(::globalPjCallback == NULL); ::globalPjCallback = new PjCallback(); status = pjsua_create(); Q_ASSERT(status == PJ_SUCCESS); PJSipEventThread *ethread = new PJSipEventThread(); ethread->start(); while (!ethread->isRunning()) { msleep(5); } PjCallback *myCb = (PjCallback*) globalPjCallback; QObject::connect(this, SIGNAL(invoke_make_call_fwd(int, pjsua_acc_id, const QString&)), myCb, SLOT(on_make_call_impl(int, pjsua_acc_id, const QString&))); QObject::connect(myCb, SIGNAL(sig_make_call_done(int, pj_status_t, pjsua_call_id)), this, SIGNAL(invoke_make_call_result(int, pj_status_t, pjsua_call_id))); status = pjsua_init(m_ua_cfg, m_log_cfg, m_media_cfg); Q_ASSERT(status == PJ_SUCCESS); pjsua_var.mconf_cfg.samples_per_frame = 8000; // pjsua_var from /* Add UDP transport. */ { pjsua_transport_config cfg; pjsua_transport_config rtp_cfg; pjsua_transport_id udp_tp_id; pjsua_transport_id tcp_tp_id; // 创建指定端口的RTP/RTCP层media后端 pjsua_transport_config_default(&rtp_cfg); rtp_cfg.port = 8050; status = pjsua_media_transports_create(&rtp_cfg); // SIP 层初始化,可指定端口 pjsua_transport_config_default(&cfg); cfg.port = 15678; // if not set , use random big port // cfg.public_addr = pj_str("123.1.2.3"); // 与上面的port一起可用于穿透,指定特定的公共端口!!! status = pjsua_transport_create(PJSIP_TRANSPORT_UDP, &cfg, m_udp_tp_id); if (status != PJ_SUCCESS) { pjsua_perror(__FILE__, "Error creating udp transport", status); // error_exit("Error creating transport", status); } status = pjsua_transport_set_enable(*m_udp_tp_id, PJ_TRUE); // TCP transport pjsua_transport_config_default(&cfg); cfg.port = 56789; status = pjsua_transport_create(PJSIP_TRANSPORT_TCP, &cfg, m_tcp_tp_id); if (status != PJ_SUCCESS) { pjsua_perror(__FILE__, "Error creating tcp transport", status); // error_exit("Error creating transport", status); } status = pjsua_transport_set_enable(*m_tcp_tp_id, PJ_TRUE); status = pjsua_transport_set_enable(*m_udp_tp_id, PJ_TRUE); } status = pjsua_start(); Q_ASSERT(status == PJ_SUCCESS); qLogx()<<""; emit this->realStarted(status); this->exec(); }
/* Display error and exit application */ static void error_exit(const char *title, pj_status_t status) { pjsua_perror(THIS_FILE, title, status); pjsua_destroy(); exit(1); }
int main() { /* system variables */ pj_status_t status; pjsua_config ua_cfg; pjsua_acc_config acc_cfg; pjsua_logging_config log_cfg; pjsua_media_config media_cfg; pjsua_transport_config udp_cfg; pjsua_transport_id transport_id; pjsua_acc_id local_aid; pjsua_acc_id user_aid; // used to send message pj_str_t to; pj_str_t mime_type; pj_str_t content; bShutdown = 0; pthread_cond_init(&_cond, NULL); pthread_mutex_init(&_mutex, NULL); if ( signal( SIGINT, signalHandler ) == SIG_ERR ) { exit( -1 ); } if ( signal( SIGTERM, signalHandler ) == SIG_ERR ) { exit( -1 ); } /** * create pjsua first */ status = pjsua_create(); if(status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error initializing pjsua", status); return status; } /** * init pjsua framework */ /* configuration of ua */ pjsua_config_default(&(ua_cfg)); /** * init callback * set callback functions you need * all the prototype of call are declared in pjsua.h */ // /* call related handle */ // ua_cfg.cb.on_call_state = on_call_state; // ua_cfg.cb.on_incoming_call = on_incoming_call; // ua_cfg.cb.on_call_tsx_state = on_call_tsx_state; // ua_cfg.cb.on_call_media_state = on_call_media_state; // ua_cfg.cb.on_call_sdp_created = on_call_sdp_created; // ua_cfg.cb.on_stream_created = on_stream_created; // ua_cfg.cb.on_stream_destroyed = on_stream_destroyed; // ua_cfg.cb.on_dtmf_digit = on_dtmf_digit; // ua_cfg.cb.on_call_transfer_request = on_call_transfer_request; // ua_cfg.cb.on_call_transfer_request2 = on_call_transfer_request2; // ua_cfg.cb.on_call_transfer_status = on_call_transfer_status; // ua_cfg.cb.on_call_replace_request = on_call_replace_request; // ua_cfg.cb.on_call_replace_request2 = on_call_replace_request2; // ua_cfg.cb.on_call_replaced = on_call_replaced; // ua_cfg.cb.on_call_rx_offer = on_call_rx_offer; // /* register handle */ ua_cfg.cb.on_reg_started = on_reg_started; ua_cfg.cb.on_reg_state = on_reg_state; ua_cfg.cb.on_reg_state2 = on_reg_state2; // /* subscirbe handle */ // ua_cfg.cb.on_incoming_subscribe = on_incoming_subscribe; // ua_cfg.cb.on_srv_subscribe_state = on_srv_subscribe_state; // ua_cfg.cb.on_buddy_state = on_buddy_state; // ua_cfg.cb.on_buddy_evsub_state = on_buddy_evsub_state; // /* message handle */ // ua_cfg.cb.on_pager = on_pager; ua_cfg.cb.on_pager2 = on_pager2; // ua_cfg.cb.on_pager_status = on_pager_status; // ua_cfg.cb.on_pager_status2 = on_pager_status2; // ua_cfg.cb.on_typing = on_typing; // ua_cfg.cb.on_typing2 = on_typing2; // /* network handle */ // ua_cfg.cb.on_nat_detect = on_nat_detect; ua_cfg.cb.on_call_redirected = on_call_redirected; // ua_cfg.cb.on_mwi_state = on_mwi_state; // ua_cfg.cb.on_mwi_info = on_mwi_info; // ua_cfg.cb.on_transport_state = on_transport_state; // ua_cfg.cb.on_call_media_transport_state = on_call_media_transport_state; // ua_cfg.cb.on_ice_transport_error = on_ice_transport_error; // ua_cfg.cb.on_snd_dev_operation = on_snd_dev_operation; // /* media module handle */ // ua_cfg.cb.on_call_media_event = on_call_media_event; // ua_cfg.cb.on_create_media_transport = on_create_media_transport; /* configure logging */ pjsua_logging_config_default(&(log_cfg)); log_cfg.msg_logging = PJ_FALSE; /* configure media */ pjsua_media_config_default(&(media_cfg)); status = pjsua_init(&(ua_cfg), &(log_cfg), NULL); // &(media_cfg)); if(status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error initializing pjsua", status); return status; } status = pjsua_start(); if(status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error starting pjsua", status); return status; } /** * Setup transport */ pjsua_transport_config_default(&udp_cfg); udp_cfg.port = 5091; status = pjsua_transport_create(PJSIP_TRANSPORT_UDP, &udp_cfg, &transport_id); if(status != PJ_SUCCESS) return -1; /** * add local account (sip:domain) which handle sip messages that don't matches any * other accounts */ pjsua_acc_add_local(transport_id, PJ_TRUE, &local_aid); /** * add user account and set it to default account */ pjsua_acc_config_default(&acc_cfg); acc_cfg.id = pj_str("sip:[email protected]"); acc_cfg.reg_uri = pj_str("sip:192.168.1.197"); acc_cfg.reg_timeout = 300; acc_cfg.cred_count = 1; acc_cfg.cred_info[0].realm = pj_str("192.168.1.197"); acc_cfg.cred_info[0].scheme = pj_str("digest"); acc_cfg.cred_info[0].username = pj_str("Server_301007"); acc_cfg.cred_info[0].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD; acc_cfg.cred_info[0].data = pj_str("Server"); acc_cfg.proxy_cnt = 1; acc_cfg.proxy[0] = pj_str("sip:192.168.1.197;lr"); /** * register_on_acc_add is true by default, add: * * acc_cfg.register_on_acc_add = PJ_FALSE; * * to disable register on add.add later you can call the following * code to send a REGISTER. * * pjsua_acc_set_registration(id_of_the_account, PJ_TRUE); * */ status = pjsua_acc_add(&acc_cfg, PJ_TRUE, &user_aid); sleep(2); /** * unregister account before exit */ to = pj_str("sip:[email protected]"); to = pj_str("sip:[email protected]"); mime_type = pj_str("text/plain"); content = pj_str("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" "<SIP_XML EventType=\"TimeSync\">" "<Item operation=\"query\" time=\"0-0-0 0:0:0\"/>" "</SIP_XML>"); content = pj_str("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" "<SIP_XML EventType=\"MediaPlay\">" "<User uri=\"sip:[email protected]\"/>" "<Media type=\"1\" id=\"000001-001\"/>" "</SIP_XML>"); content = pj_str("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" "<SIP_XML EventType=\"MediaTeardown\">" "<User uri=\"sip:[email protected]\"/>" "<Media type=\"1\" id=\"000001-001\"/>" "</SIP_XML>"); { pthread_t tid; // pthread_create(&tid, NULL, test_thread, &user_aid); } pjsua_im_send(user_aid, &to, &mime_type, &content, NULL, NULL); sleep(1); printf("*******************************\n"); printf("pjsua_im_send ok @ main\n"); printf("*******************************\n"); pthread_mutex_lock(&_mutex); pthread_cond_signal(&_cond); pthread_mutex_unlock(&_mutex); // pjsua_call_setting setting; // pjsua_call_setting_default(&setting); // setting.aud_cnt = 0; // setting.vid_cnt = 0; // pjsua_call_id cid; // pjsua_msg_data msg_data; // pjsua_msg_data_init(&msg_data); // msg_data.content_type = pj_str("application/sdp"); // msg_data.msg_body = pj_str( // "v=0\r\n" // "o=adapter 0 0 IN IP4 0.0.0.0\r\n" // "s=-\r\n" // "c=IN IP4 192.168.1.147\r\n" // "t=1371053400 1371057000\r\n" // "m=video 6000 TCP 96\r\n" // "a=rtpmap:98 L16/16000/2\r\n" // "a=ResID:301002-0005\r\n" // "a=WndIndex:1\r\n" // "a=recvonly\r\n"); // sleep(2); // if(pjsua_call_make_call(user_aid, &to, &setting, NULL, NULL, &cid) != PJ_SUCCESS) // PJ_LOG(1, (__FILE__, "failed to make call\n")); while(!bShutdown) { sleep(1); } pjsua_acc_set_registration(user_aid, PJ_FALSE); sleep(1); return status; }
/** * Create ipv6 transport */ PJ_DECL(pj_status_t) media_transports_create_ipv6(pjsua_transport_config rtp_cfg) { pjsua_media_transport tp[PJSUA_MAX_CALLS]; pj_status_t status; int port = rtp_cfg.port; unsigned i; //TODO : here should be get from config for (i=0; i<PJSUA_MAX_CALLS; ++i) { enum { MAX_RETRY = 10 }; pj_sock_t sock[2]; pjmedia_sock_info si; unsigned j; /* Get rid of uninitialized var compiler warning with MSVC */ status = PJ_SUCCESS; for (j=0; j<MAX_RETRY; ++j) { unsigned k; for (k=0; k<2; ++k) { pj_sockaddr bound_addr; status = pj_sock_socket(pj_AF_INET6(), pj_SOCK_DGRAM(), 0, &sock[k]); if (status != PJ_SUCCESS) break; status = pj_sockaddr_init(pj_AF_INET6(), &bound_addr, &rtp_cfg.bound_addr, (unsigned short)(port+k)); if (status != PJ_SUCCESS) break; status = pj_sock_bind(sock[k], &bound_addr, pj_sockaddr_get_len(&bound_addr)); if (status != PJ_SUCCESS) break; } if (status != PJ_SUCCESS) { if (k==1) pj_sock_close(sock[0]); if (port != 0) port += 10; else break; continue; } pj_bzero(&si, sizeof(si)); si.rtp_sock = sock[0]; si.rtcp_sock = sock[1]; pj_sockaddr_init(pj_AF_INET6(), &si.rtp_addr_name, &rtp_cfg.public_addr, (unsigned short)(port)); pj_sockaddr_init(pj_AF_INET6(), &si.rtcp_addr_name, &rtp_cfg.public_addr, (unsigned short)(port+1)); status = pjmedia_transport_udp_attach(pjsua_get_pjmedia_endpt(), NULL, &si, 0, &tp[i].transport); if (port != 0) port += 10; else break; if (status == PJ_SUCCESS) break; } if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error creating IPv6 UDP media transport", status); for (j=0; j<i; ++j) { pjmedia_transport_close(tp[j].transport); } return status; } } return pjsua_media_transports_attach(tp, i, PJ_TRUE); }
/* * Send typing indication outside dialog. */ PJ_DEF(pj_status_t) pjsua_im_typing( pjsua_acc_id acc_id, const pj_str_t *to, pj_bool_t is_typing, const pjsua_msg_data *msg_data) { pjsua_im_data *im_data; pjsip_tx_data *tdata; pjsua_acc *acc; pj_status_t status; acc = &pjsua_var.acc[acc_id]; /* Create request. */ status = pjsip_endpt_create_request( pjsua_var.endpt, &pjsip_message_method, to, &acc->cfg.id, to, NULL, NULL, -1, NULL, &tdata); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to create request", status); return status; } /* If account is locked to specific transport, then set transport to * the request. */ if (acc->cfg.transport_id != PJSUA_INVALID_ID) { pjsip_tpselector tp_sel; pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel); pjsip_tx_data_set_transport(tdata, &tp_sel); } /* Add accept header. */ pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)pjsua_im_create_accept(tdata->pool)); /* Create suitable Contact header unless a Contact header has been * set in the account. */ /* Ticket #1632: According to RFC 3428: * MESSAGE requests do not initiate dialogs. * User Agents MUST NOT insert Contact header fields into MESSAGE requests */ /* if (acc->contact.slen) { contact = acc->contact; } else { status = pjsua_acc_create_uac_contact(tdata->pool, &contact, acc_id, to); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to generate Contact header", status); pjsip_tx_data_dec_ref(tdata); return status; } } pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*) pjsip_generic_string_hdr_create(tdata->pool, &STR_CONTACT, &contact)); */ /* Create "application/im-iscomposing+xml" msg body. */ tdata->msg->body = pjsip_iscomposing_create_body( tdata->pool, is_typing, NULL, NULL, -1); /* Add additional headers etc. */ pjsua_process_msg_data(tdata, msg_data); /* Add route set */ pjsua_set_msg_route_set(tdata, &acc->route_set); /* If via_addr is set, use this address for the Via header. */ if (acc->cfg.allow_via_rewrite && acc->via_addr.host.slen > 0) { tdata->via_addr = acc->via_addr; tdata->via_tp = acc->via_tp; } /* Create data to reauthenticate */ im_data = PJ_POOL_ZALLOC_T(tdata->pool, pjsua_im_data); im_data->acc_id = acc_id; /* Send request (statefully) */ status = pjsip_endpt_send_request( pjsua_var.endpt, tdata, -1, im_data, &typing_callback); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to send request", status); return status; } return PJ_SUCCESS; }
/* * Change codec priorities. */ static void ui_manage_codec_prio() { pjsua_codec_info c[32]; unsigned i, count = PJ_ARRAY_SIZE(c); char input[32]; char *codec, *prio; pj_str_t id; int new_prio; pj_status_t status; printf("List of audio codecs:\n"); pjsua_enum_codecs(c, &count); for (i=0; i<count; ++i) { printf(" %d\t%.*s\n", c[i].priority, (int)c[i].codec_id.slen, c[i].codec_id.ptr); } #if PJSUA_HAS_VIDEO puts(""); printf("List of video codecs:\n"); pjsua_vid_enum_codecs(c, &count); for (i=0; i<count; ++i) { printf(" %d\t%.*s%s%.*s\n", c[i].priority, (int)c[i].codec_id.slen, c[i].codec_id.ptr, c[i].desc.slen? " - ":"", (int)c[i].desc.slen, c[i].desc.ptr); } #endif puts(""); puts("Enter codec id and its new priority (e.g. \"speex/16000 200\", " """\"H263 200\"),"); puts("or empty to cancel."); printf("Codec name (\"*\" for all) and priority: "); if (fgets(input, sizeof(input), stdin) == NULL) return; if (input[0]=='\r' || input[0]=='\n') { puts("Done"); return; } codec = strtok(input, " \t\r\n"); prio = strtok(NULL, " \r\n"); if (!codec || !prio) { puts("Invalid input"); return; } new_prio = atoi(prio); if (new_prio < 0) new_prio = 0; else if (new_prio > PJMEDIA_CODEC_PRIO_HIGHEST) new_prio = PJMEDIA_CODEC_PRIO_HIGHEST; status = pjsua_codec_set_priority(pj_cstr(&id, codec), (pj_uint8_t)new_prio); #if PJSUA_HAS_VIDEO if (status != PJ_SUCCESS) { status = pjsua_vid_codec_set_priority(pj_cstr(&id, codec), (pj_uint8_t)new_prio); } #endif if (status != PJ_SUCCESS) pjsua_perror(THIS_FILE, "Error setting codec priority", status); }
/* * Add new buddy. */ PJ_DEF(pj_status_t) pjsua_buddy_add( const pjsua_buddy_config *cfg, pjsua_buddy_id *p_buddy_id) { pjsip_name_addr *url; pjsua_buddy *buddy; pjsip_sip_uri *sip_uri; int index; pj_str_t tmp; PJ_ASSERT_RETURN(pjsua_var.buddy_cnt <= PJ_ARRAY_SIZE(pjsua_var.buddy), PJ_ETOOMANY); PJSUA_LOCK(); /* Find empty slot */ for (index=0; index<(int)PJ_ARRAY_SIZE(pjsua_var.buddy); ++index) { if (pjsua_var.buddy[index].uri.slen == 0) break; } /* Expect to find an empty slot */ if (index == PJ_ARRAY_SIZE(pjsua_var.buddy)) { PJSUA_UNLOCK(); /* This shouldn't happen */ pj_assert(!"index < PJ_ARRAY_SIZE(pjsua_var.buddy)"); return PJ_ETOOMANY; } buddy = &pjsua_var.buddy[index]; /* Create pool for this buddy */ if (buddy->pool) { pj_pool_reset(buddy->pool); } else { char name[PJ_MAX_OBJ_NAME]; pj_ansi_snprintf(name, sizeof(name), "buddy%03d", index); buddy->pool = pjsua_pool_create(name, 512, 256); } /* Init buffers for presence subscription status */ buddy->term_reason.ptr = (char*) pj_pool_alloc(buddy->pool, PJSUA_BUDDY_SUB_TERM_REASON_LEN); /* Get name and display name for buddy */ pj_strdup_with_null(buddy->pool, &tmp, &cfg->uri); url = (pjsip_name_addr*)pjsip_parse_uri(buddy->pool, tmp.ptr, tmp.slen, PJSIP_PARSE_URI_AS_NAMEADDR); if (url == NULL) { pjsua_perror(THIS_FILE, "Unable to add buddy", PJSIP_EINVALIDURI); pj_pool_release(buddy->pool); buddy->pool = NULL; PJSUA_UNLOCK(); return PJSIP_EINVALIDURI; } /* Only support SIP schemes */ if (!PJSIP_URI_SCHEME_IS_SIP(url) && !PJSIP_URI_SCHEME_IS_SIPS(url)) { pj_pool_release(buddy->pool); buddy->pool = NULL; PJSUA_UNLOCK(); return PJSIP_EINVALIDSCHEME; } /* Reset buddy, to make sure everything is cleared with default * values */ reset_buddy(index); /* Save URI */ pjsua_var.buddy[index].uri = tmp; sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(url->uri); pjsua_var.buddy[index].name = sip_uri->user; pjsua_var.buddy[index].display = url->display; pjsua_var.buddy[index].host = sip_uri->host; pjsua_var.buddy[index].port = sip_uri->port; pjsua_var.buddy[index].monitor = cfg->subscribe; if (pjsua_var.buddy[index].port == 0) pjsua_var.buddy[index].port = 5060; /* Save user data */ pjsua_var.buddy[index].user_data = (void*)cfg->user_data; if (p_buddy_id) *p_buddy_id = index; pjsua_var.buddy_cnt++; PJSUA_UNLOCK(); pjsua_buddy_subscribe_pres(index, cfg->subscribe); return PJ_SUCCESS; }
/* This is called when request is received. * We need to check for incoming SUBSCRIBE request. */ static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata) { int acc_id; pjsua_acc *acc; pj_str_t contact; pjsip_method *req_method = &rdata->msg_info.msg->line.req.method; pjsua_srv_pres *uapres; pjsip_evsub *sub; pjsip_evsub_user pres_cb; pjsip_dialog *dlg; pjsip_status_code st_code; pj_str_t reason; pjsip_expires_hdr *expires_hdr; pjsua_msg_data msg_data; pj_status_t status; if (pjsip_method_cmp(req_method, pjsip_get_subscribe_method()) != 0) return PJ_FALSE; /* Incoming SUBSCRIBE: */ PJSUA_LOCK(); /* Find which account for the incoming request. */ acc_id = pjsua_acc_find_for_incoming(rdata); acc = &pjsua_var.acc[acc_id]; PJ_LOG(4,(THIS_FILE, "Creating server subscription, using account %d", acc_id)); /* Create suitable Contact header */ if (acc->contact.slen) { contact = acc->contact; } else { status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact, acc_id, rdata); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to generate Contact header", status); PJSUA_UNLOCK(); pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 400, NULL, NULL, NULL); return PJ_TRUE; } } /* Create UAS dialog: */ status = pjsip_dlg_create_uas(pjsip_ua_instance(), rdata, &contact, &dlg); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to create UAS dialog for subscription", status); PJSUA_UNLOCK(); pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 400, NULL, NULL, NULL); return PJ_TRUE; } /* Set credentials and preference. */ pjsip_auth_clt_set_credentials(&dlg->auth_sess, acc->cred_cnt, acc->cred); pjsip_auth_clt_set_prefs(&dlg->auth_sess, &acc->cfg.auth_pref); /* Init callback: */ pj_bzero(&pres_cb, sizeof(pres_cb)); pres_cb.on_evsub_state = &pres_evsub_on_srv_state; /* Create server presence subscription: */ status = pjsip_pres_create_uas( dlg, &pres_cb, rdata, &sub); if (status != PJ_SUCCESS) { int code = PJSIP_ERRNO_TO_SIP_STATUS(status); pjsip_tx_data *tdata; pjsua_perror(THIS_FILE, "Unable to create server subscription", status); if (code==599 || code > 699 || code < 300) { code = 400; } status = pjsip_dlg_create_response(dlg, rdata, code, NULL, &tdata); if (status == PJ_SUCCESS) { status = pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata), tdata); } PJSUA_UNLOCK(); return PJ_TRUE; } /* If account is locked to specific transport, then lock dialog * to this transport too. */ if (acc->cfg.transport_id != PJSUA_INVALID_ID) { pjsip_tpselector tp_sel; pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel); pjsip_dlg_set_transport(dlg, &tp_sel); } /* Attach our data to the subscription: */ uapres = PJ_POOL_ALLOC_T(dlg->pool, pjsua_srv_pres); uapres->sub = sub; uapres->remote = (char*) pj_pool_alloc(dlg->pool, PJSIP_MAX_URL_SIZE); uapres->acc_id = acc_id; uapres->dlg = dlg; status = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, dlg->remote.info->uri, uapres->remote, PJSIP_MAX_URL_SIZE); if (status < 1) pj_ansi_strcpy(uapres->remote, "<-- url is too long-->"); else uapres->remote[status] = '\0'; pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, uapres); /* Add server subscription to the list: */ pj_list_push_back(&pjsua_var.acc[acc_id].pres_srv_list, uapres); /* Capture the value of Expires header. */ expires_hdr = (pjsip_expires_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL); if (expires_hdr) uapres->expires = expires_hdr->ivalue; else uapres->expires = -1; st_code = (pjsip_status_code)200; reason = pj_str("OK"); pjsua_msg_data_init(&msg_data); /* Notify application callback, if any */ if (pjsua_var.ua_cfg.cb.on_incoming_subscribe) { pjsua_buddy_id buddy_id; buddy_id = pjsua_find_buddy(rdata->msg_info.from->uri); (*pjsua_var.ua_cfg.cb.on_incoming_subscribe)(acc_id, uapres, buddy_id, &dlg->remote.info_str, rdata, &st_code, &reason, &msg_data); } /* Handle rejection case */ if (st_code >= 300) { pjsip_tx_data *tdata; /* Create response */ status = pjsip_dlg_create_response(dlg, rdata, st_code, &reason, &tdata); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error creating response", status); pj_list_erase(uapres); pjsip_pres_terminate(sub, PJ_FALSE); PJSUA_UNLOCK(); return PJ_FALSE; } /* Add header list, if any */ pjsua_process_msg_data(tdata, &msg_data); /* Send the response */ status = pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata), tdata); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error sending response", status); /* This is not fatal */ } /* Terminate presence subscription */ pj_list_erase(uapres); pjsip_pres_terminate(sub, PJ_FALSE); PJSUA_UNLOCK(); return PJ_TRUE; } /* Create and send 2xx response to the SUBSCRIBE request: */ status = pjsip_pres_accept(sub, rdata, st_code, &msg_data.hdr_list); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to accept presence subscription", status); pj_list_erase(uapres); pjsip_pres_terminate(sub, PJ_FALSE); PJSUA_UNLOCK(); return PJ_FALSE; } /* If code is 200, send NOTIFY now */ if (st_code == 200) { pjsua_pres_notify(acc_id, uapres, PJSIP_EVSUB_STATE_ACTIVE, NULL, NULL, PJ_TRUE, &msg_data); } /* Done: */ PJSUA_UNLOCK(); return PJ_TRUE; }
/* * Input URL. */ static void ui_input_url(const char *title, char *buf, pj_size_t len, input_result *result) { result->nb_result = PJSUA_APP_NO_NB; result->uri_result = NULL; print_buddy_list(); printf("Choices:\n" " 0 For current dialog.\n" " -1 All %d buddies in buddy list\n" " [1 -%2d] Select from buddy list\n" " URL An URL\n" " <Enter> Empty input (or 'q') to cancel\n" , pjsua_get_buddy_count(), pjsua_get_buddy_count()); printf("%s: ", title); fflush(stdout); if (fgets(buf, (int)len, stdin) == NULL) return; len = strlen(buf); /* Left trim */ while (pj_isspace(*buf)) { ++buf; --len; } /* Remove trailing newlines */ while (len && (buf[len-1] == '\r' || buf[len-1] == '\n')) buf[--len] = '\0'; if (len == 0 || buf[0]=='q') return; if (pj_isdigit(*buf) || *buf=='-') { unsigned i; if (*buf=='-') i = 1; else i = 0; for (; i<len; ++i) { if (!pj_isdigit(buf[i])) { puts("Invalid input"); return; } } result->nb_result = my_atoi(buf); if (result->nb_result >= 0 && result->nb_result <= (int)pjsua_get_buddy_count()) { return; } if (result->nb_result == -1) return; puts("Invalid input"); result->nb_result = PJSUA_APP_NO_NB; return; } else { pj_status_t status; if ((status=pjsua_verify_url(buf)) != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Invalid URL", status); return; } result->uri_result = buf; } }
/* * Initialize a new account (after configuration is set). */ static pj_status_t initialize_acc(unsigned acc_id) { pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg; pjsua_acc *acc = &pjsua_var.acc[acc_id]; pjsip_name_addr *name_addr; pjsip_sip_uri *sip_uri, *sip_reg_uri; pj_status_t status; unsigned i; /* Need to parse local_uri to get the elements: */ name_addr = (pjsip_name_addr*) pjsip_parse_uri(acc->pool, acc_cfg->id.ptr, acc_cfg->id.slen, PJSIP_PARSE_URI_AS_NAMEADDR); if (name_addr == NULL) { pjsua_perror(THIS_FILE, "Invalid local URI", PJSIP_EINVALIDURI); return PJSIP_EINVALIDURI; } /* Local URI MUST be a SIP or SIPS: */ if (!PJSIP_URI_SCHEME_IS_SIP(name_addr) && !PJSIP_URI_SCHEME_IS_SIPS(name_addr)) { pjsua_perror(THIS_FILE, "Invalid local URI", PJSIP_EINVALIDSCHEME); return PJSIP_EINVALIDSCHEME; } /* Get the SIP URI object: */ sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(name_addr); /* Parse registrar URI, if any */ if (acc_cfg->reg_uri.slen) { pjsip_uri *reg_uri; reg_uri = pjsip_parse_uri(acc->pool, acc_cfg->reg_uri.ptr, acc_cfg->reg_uri.slen, 0); if (reg_uri == NULL) { pjsua_perror(THIS_FILE, "Invalid registrar URI", PJSIP_EINVALIDURI); return PJSIP_EINVALIDURI; } /* Registrar URI MUST be a SIP or SIPS: */ if (!PJSIP_URI_SCHEME_IS_SIP(reg_uri) && !PJSIP_URI_SCHEME_IS_SIPS(reg_uri)) { pjsua_perror(THIS_FILE, "Invalid registar URI", PJSIP_EINVALIDSCHEME); return PJSIP_EINVALIDSCHEME; } sip_reg_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(reg_uri); } else { sip_reg_uri = NULL; } /* Save the user and domain part. These will be used when finding an * account for incoming requests. */ acc->display = name_addr->display; acc->user_part = sip_uri->user; acc->srv_domain = sip_uri->host; acc->srv_port = 0; if (sip_reg_uri) { acc->srv_port = sip_reg_uri->port; } /* Create Contact header if not present. */ //if (acc_cfg->contact.slen == 0) { // acc_cfg->contact = acc_cfg->id; //} /* Build account route-set from outbound proxies and route set from * account configuration. */ pj_list_init(&acc->route_set); for (i=0; i<pjsua_var.ua_cfg.outbound_proxy_cnt; ++i) { pj_str_t hname = { "Route", 5}; pjsip_route_hdr *r; pj_str_t tmp; pj_strdup_with_null(acc->pool, &tmp, &pjsua_var.ua_cfg.outbound_proxy[i]); r = (pjsip_route_hdr*) pjsip_parse_hdr(acc->pool, &hname, tmp.ptr, tmp.slen, NULL); if (r == NULL) { pjsua_perror(THIS_FILE, "Invalid outbound proxy URI", PJSIP_EINVALIDURI); return PJSIP_EINVALIDURI; } pj_list_push_back(&acc->route_set, r); } for (i=0; i<acc_cfg->proxy_cnt; ++i) { pj_str_t hname = { "Route", 5}; pjsip_route_hdr *r; pj_str_t tmp; pj_strdup_with_null(acc->pool, &tmp, &acc_cfg->proxy[i]); r = (pjsip_route_hdr*) pjsip_parse_hdr(acc->pool, &hname, tmp.ptr, tmp.slen, NULL); if (r == NULL) { pjsua_perror(THIS_FILE, "Invalid URI in account route set", PJ_EINVAL); return PJ_EINVAL; } pj_list_push_back(&acc->route_set, r); } /* Concatenate credentials from account config and global config */ acc->cred_cnt = 0; for (i=0; i<acc_cfg->cred_count; ++i) { acc->cred[acc->cred_cnt++] = acc_cfg->cred_info[i]; } for (i=0; i<pjsua_var.ua_cfg.cred_count && acc->cred_cnt < PJ_ARRAY_SIZE(acc->cred); ++i) { acc->cred[acc->cred_cnt++] = pjsua_var.ua_cfg.cred_info[i]; } status = pjsua_pres_init_acc(acc_id); if (status != PJ_SUCCESS) return status; /* Mark account as valid */ pjsua_var.acc[acc_id].valid = PJ_TRUE; /* Insert account ID into account ID array, sorted by priority */ for (i=0; i<pjsua_var.acc_cnt; ++i) { if ( pjsua_var.acc[pjsua_var.acc_ids[i]].cfg.priority < pjsua_var.acc[acc_id].cfg.priority) { break; } } pj_array_insert(pjsua_var.acc_ids, sizeof(pjsua_var.acc_ids[0]), pjsua_var.acc_cnt, i, &acc_id); return PJ_SUCCESS; }
/* It does what it says.. */ static void subscribe_buddy_presence(unsigned index) { pj_pool_t *tmp_pool = NULL; pjsua_buddy *buddy; int acc_id; pjsua_acc *acc; pj_str_t contact; pjsip_tx_data *tdata; pj_status_t status; buddy = &pjsua_var.buddy[index]; acc_id = pjsua_acc_find_for_outgoing(&buddy->uri); acc = &pjsua_var.acc[acc_id]; PJ_LOG(4,(THIS_FILE, "Using account %d for buddy %d subscription", acc_id, index)); /* Generate suitable Contact header unless one is already set in * the account */ if (acc->contact.slen) { contact = acc->contact; } else { tmp_pool = pjsua_pool_create("tmpbuddy", 512, 256); status = pjsua_acc_create_uac_contact(tmp_pool, &contact, acc_id, &buddy->uri); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to generate Contact header", status); pj_pool_release(tmp_pool); return; } } /* Create UAC dialog */ status = pjsip_dlg_create_uac( pjsip_ua_instance(), &acc->cfg.id, &contact, &buddy->uri, NULL, &buddy->dlg); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to create dialog", status); if (tmp_pool) pj_pool_release(tmp_pool); return; } /* Increment the dialog's lock otherwise when presence session creation * fails the dialog will be destroyed prematurely. */ pjsip_dlg_inc_lock(buddy->dlg); status = pjsip_pres_create_uac( buddy->dlg, &pres_callback, PJSIP_EVSUB_NO_EVENT_ID, &buddy->sub); if (status != PJ_SUCCESS) { pjsua_var.buddy[index].sub = NULL; pjsua_perror(THIS_FILE, "Unable to create presence client", status); /* This should destroy the dialog since there's no session * referencing it */ pjsip_dlg_dec_lock(buddy->dlg); if (tmp_pool) pj_pool_release(tmp_pool); return; } /* If account is locked to specific transport, then lock dialog * to this transport too. */ if (acc->cfg.transport_id != PJSUA_INVALID_ID) { pjsip_tpselector tp_sel; pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel); pjsip_dlg_set_transport(buddy->dlg, &tp_sel); } /* Set route-set */ if (!pj_list_empty(&acc->route_set)) { pjsip_dlg_set_route_set(buddy->dlg, &acc->route_set); } /* Set credentials */ if (acc->cred_cnt) { pjsip_auth_clt_set_credentials( &buddy->dlg->auth_sess, acc->cred_cnt, acc->cred); } /* Set authentication preference */ pjsip_auth_clt_set_prefs(&buddy->dlg->auth_sess, &acc->cfg.auth_pref); pjsip_evsub_set_mod_data(buddy->sub, pjsua_var.mod.id, buddy); status = pjsip_pres_initiate(buddy->sub, -1, &tdata); if (status != PJ_SUCCESS) { pjsip_dlg_dec_lock(buddy->dlg); if (buddy->sub) { pjsip_pres_terminate(buddy->sub, PJ_FALSE); } buddy->sub = NULL; pjsua_perror(THIS_FILE, "Unable to create initial SUBSCRIBE", status); if (tmp_pool) pj_pool_release(tmp_pool); return; } pjsua_process_msg_data(tdata, NULL); status = pjsip_pres_send_request(buddy->sub, tdata); if (status != PJ_SUCCESS) { pjsip_dlg_dec_lock(buddy->dlg); if (buddy->sub) { pjsip_pres_terminate(buddy->sub, PJ_FALSE); } buddy->sub = NULL; pjsua_perror(THIS_FILE, "Unable to send initial SUBSCRIBE", status); if (tmp_pool) pj_pool_release(tmp_pool); return; } pjsip_dlg_dec_lock(buddy->dlg); if (tmp_pool) pj_pool_release(tmp_pool); }
/* * Send typing indication outside dialog. */ PJ_DEF(pj_status_t) pjsua_im_typing( pjsua_acc_id acc_id, const pj_str_t *to, pj_bool_t is_typing, const pjsua_msg_data *msg_data) { const pj_str_t STR_CONTACT = { "Contact", 7 }; pjsua_im_data *im_data; pjsip_tx_data *tdata; pj_str_t contact; pj_status_t status; /* Create request. */ status = pjsip_endpt_create_request( pjsua_var.endpt, &pjsip_message_method, to, &pjsua_var.acc[acc_id].cfg.id, to, NULL, NULL, -1, NULL, &tdata); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to create request", status); return status; } /* If account is locked to specific transport, then set transport to * the request. */ if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) { pjsip_tpselector tp_sel; pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel); pjsip_tx_data_set_transport(tdata, &tp_sel); } /* Add accept header. */ pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)pjsua_im_create_accept(tdata->pool)); /* Add contact. */ status = pjsua_acc_create_uac_contact(tdata->pool, &contact, acc_id, to); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to generate Contact header", status); pjsip_tx_data_dec_ref(tdata); return status; } pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*) pjsip_generic_string_hdr_create(tdata->pool, &STR_CONTACT, &contact)); /* Create "application/im-iscomposing+xml" msg body. */ tdata->msg->body = pjsip_iscomposing_create_body( tdata->pool, is_typing, NULL, NULL, -1); /* Add additional headers etc. */ pjsua_process_msg_data(tdata, msg_data); /* Add route set */ pjsua_set_msg_route_set(tdata, &pjsua_var.acc[acc_id].route_set); /* Create data to reauthenticate */ im_data = PJ_POOL_ZALLOC_T(tdata->pool, pjsua_im_data); im_data->acc_id = acc_id; /* Send request (statefully) */ status = pjsip_endpt_send_request( pjsua_var.endpt, tdata, -1, im_data, &typing_callback); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to send request", status); return status; } return PJ_SUCCESS; }
/* * Send instant messaging outside dialog, using the specified account for * route set and authentication. */ PJ_DEF(pj_status_t) pjsua_im_send( pjsua_acc_id acc_id, const pj_str_t *to, const pj_str_t *mime_type, const pj_str_t *content, const pjsua_msg_data *msg_data, void *user_data) { pjsip_tx_data *tdata; const pj_str_t mime_text_plain = pj_str("text/plain"); const pj_str_t STR_CONTACT = { "Contact", 7 }; pjsip_media_type media_type; pjsua_im_data *im_data; pj_str_t contact; pj_status_t status; /* To and message body must be specified. */ PJ_ASSERT_RETURN(to && content, PJ_EINVAL); /* Create request. */ status = pjsip_endpt_create_request(pjsua_var.endpt, &pjsip_message_method, to, &pjsua_var.acc[acc_id].cfg.id, to, NULL, NULL, -1, NULL, &tdata); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to create request", status); return status; } /* If account is locked to specific transport, then set transport to * the request. */ if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) { pjsip_tpselector tp_sel; pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel); pjsip_tx_data_set_transport(tdata, &tp_sel); } /* Add accept header. */ pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)pjsua_im_create_accept(tdata->pool)); /* Add contact. */ status = pjsua_acc_create_uac_contact(tdata->pool, &contact, acc_id, to); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to generate Contact header", status); pjsip_tx_data_dec_ref(tdata); return status; } pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*) pjsip_generic_string_hdr_create(tdata->pool, &STR_CONTACT, &contact)); /* Create IM data to keep message details and give it back to * application on the callback */ im_data = PJ_POOL_ZALLOC_T(tdata->pool, pjsua_im_data); im_data->acc_id = acc_id; im_data->call_id = PJSUA_INVALID_ID; pj_strdup_with_null(tdata->pool, &im_data->to, to); pj_strdup_with_null(tdata->pool, &im_data->body, content); im_data->user_data = user_data; /* Set default media type if none is specified */ if (mime_type == NULL) { mime_type = &mime_text_plain; } /* Parse MIME type */ pjsua_parse_media_type(tdata->pool, mime_type, &media_type); /* Add message body */ tdata->msg->body = pjsip_msg_body_create( tdata->pool, &media_type.type, &media_type.subtype, &im_data->body); if (tdata->msg->body == NULL) { pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM); pjsip_tx_data_dec_ref(tdata); return PJ_ENOMEM; } /* Add additional headers etc. */ pjsua_process_msg_data(tdata, msg_data); /* Add route set */ pjsua_set_msg_route_set(tdata, &pjsua_var.acc[acc_id].route_set); /* Send request (statefully) */ status = pjsip_endpt_send_request( pjsua_var.endpt, tdata, -1, im_data, &im_callback); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to send request", status); return status; } return PJ_SUCCESS; }
/** * Private: process pager message. * This may trigger pjsua_ui_on_pager() or pjsua_ui_on_typing(). */ void pjsua_im_process_pager(int call_id, const pj_str_t *from, const pj_str_t *to, pjsip_rx_data *rdata) { pjsip_contact_hdr *contact_hdr; pj_str_t contact; pjsip_msg_body *body = rdata->msg_info.msg->body; #if 0 /* Ticket #693: allow incoming MESSAGE without message body */ /* Body MUST have been checked before */ pj_assert(body != NULL); #endif /* Build remote contact */ contact_hdr = (pjsip_contact_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL); if (contact_hdr && contact_hdr->uri) { contact.ptr = (char*) pj_pool_alloc(rdata->tp_info.pool, PJSIP_MAX_URL_SIZE); contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, contact_hdr->uri, contact.ptr, PJSIP_MAX_URL_SIZE); } else { contact.slen = 0; } if (body && pj_stricmp(&body->content_type.type, &STR_MIME_APP)==0 && pj_stricmp(&body->content_type.subtype, &STR_MIME_ISCOMPOSING)==0) { /* Expecting typing indication */ pj_status_t status; pj_bool_t is_typing; status = pjsip_iscomposing_parse(rdata->tp_info.pool, (char*)body->data, body->len, &is_typing, NULL, NULL, NULL ); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Invalid MESSAGE body", status); return; } if (pjsua_var.ua_cfg.cb.on_typing) { (*pjsua_var.ua_cfg.cb.on_typing)(call_id, from, to, &contact, is_typing); } if (pjsua_var.ua_cfg.cb.on_typing2) { pjsua_acc_id acc_id; if (call_id == PJSUA_INVALID_ID) { acc_id = pjsua_acc_find_for_incoming(rdata); } else { pjsua_call *call = &pjsua_var.calls[call_id]; acc_id = call->acc_id; } (*pjsua_var.ua_cfg.cb.on_typing2)(call_id, from, to, &contact, is_typing, rdata, acc_id); } } else { pj_str_t mime_type; char buf[256]; pjsip_media_type *m; pj_str_t text_body; /* Save text body */ if (body) { text_body.ptr = (char*)rdata->msg_info.msg->body->data; text_body.slen = rdata->msg_info.msg->body->len; /* Get mime type */ m = &rdata->msg_info.msg->body->content_type; mime_type.ptr = buf; mime_type.slen = pj_ansi_snprintf(buf, sizeof(buf), "%.*s/%.*s", (int)m->type.slen, m->type.ptr, (int)m->subtype.slen, m->subtype.ptr); if (mime_type.slen < 1) mime_type.slen = 0; } else { text_body.ptr = mime_type.ptr = ""; text_body.slen = mime_type.slen = 0; } if (pjsua_var.ua_cfg.cb.on_pager) { (*pjsua_var.ua_cfg.cb.on_pager)(call_id, from, to, &contact, &mime_type, &text_body); } if (pjsua_var.ua_cfg.cb.on_pager2) { pjsua_acc_id acc_id; if (call_id == PJSUA_INVALID_ID) { acc_id = pjsua_acc_find_for_incoming(rdata); } else { pjsua_call *call = &pjsua_var.calls[call_id]; acc_id = call->acc_id; } (*pjsua_var.ua_cfg.cb.on_pager2)(call_id, from, to, &contact, &mime_type, &text_body, rdata, acc_id); } } }
/* * Send NOTIFY. */ PJ_DEF(pj_status_t) pjsua_pres_notify( pjsua_acc_id acc_id, pjsua_srv_pres *srv_pres, pjsip_evsub_state ev_state, const pj_str_t *state_str, const pj_str_t *reason, pj_bool_t with_body, const pjsua_msg_data *msg_data) { pjsua_acc *acc; pjsip_pres_status pres_status; pjsua_buddy_id buddy_id; pjsip_tx_data *tdata; pj_status_t status; /* Check parameters */ PJ_ASSERT_RETURN(acc_id!=-1 && srv_pres, PJ_EINVAL); /* Check that account ID is valid */ PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc), PJ_EINVAL); /* Check that account is valid */ PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, PJ_EINVALIDOP); PJSUA_LOCK(); acc = &pjsua_var.acc[acc_id]; /* Check that the server presence subscription is still valid */ if (pj_list_find_node(&acc->pres_srv_list, srv_pres) == NULL) { /* Subscription has been terminated */ PJSUA_UNLOCK(); return PJ_EINVALIDOP; } /* Set our online status: */ pj_bzero(&pres_status, sizeof(pres_status)); pres_status.info_cnt = 1; pres_status.info[0].basic_open = acc->online_status; pres_status.info[0].id = acc->cfg.pidf_tuple_id; //Both pjsua_var.local_uri and pjsua_var.contact_uri are enclosed in "<" and ">" //causing XML parsing to fail. //pres_status.info[0].contact = pjsua_var.local_uri; /* add RPID information */ pj_memcpy(&pres_status.info[0].rpid, &acc->rpid, sizeof(pjrpid_element)); pjsip_pres_set_status(srv_pres->sub, &pres_status); /* Check expires value. If it's zero, send our presense state but * set subscription state to TERMINATED. */ if (srv_pres->expires == 0) ev_state = PJSIP_EVSUB_STATE_TERMINATED; /* Create and send the NOTIFY to active subscription: */ status = pjsip_pres_notify(srv_pres->sub, ev_state, state_str, reason, &tdata); if (status == PJ_SUCCESS) { /* Force removal of message body if msg_body==FALSE */ if (!with_body) { tdata->msg->body = NULL; } pjsua_process_msg_data(tdata, msg_data); status = pjsip_pres_send_request( srv_pres->sub, tdata); } if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to create/send NOTIFY", status); pj_list_erase(srv_pres); pjsip_pres_terminate(srv_pres->sub, PJ_FALSE); PJSUA_UNLOCK(); return status; } /* Subscribe to buddy's presence if we're not subscribed */ buddy_id = pjsua_find_buddy(srv_pres->dlg->remote.info->uri); if (buddy_id != PJSUA_INVALID_ID) { pjsua_buddy *b = &pjsua_var.buddy[buddy_id]; if (b->monitor && b->sub == NULL) { PJ_LOG(4,(THIS_FILE, "Received SUBSCRIBE from buddy %d, " "activating outgoing subscription", buddy_id)); subscribe_buddy_presence(buddy_id); } } PJSUA_UNLOCK(); return PJ_SUCCESS; }
/* * Send instant messaging outside dialog, using the specified account for * route set and authentication. */ PJ_DEF(pj_status_t) pjsua_im_send( pjsua_acc_id acc_id, const pj_str_t *to, const pj_str_t *mime_type, const pj_str_t *content, const pjsua_msg_data *msg_data, void *user_data) { pjsip_tx_data *tdata; const pj_str_t mime_text_plain = pj_str("text/plain"); pjsip_media_type media_type; pjsua_im_data *im_data; pjsua_acc *acc; pj_status_t status; /* To and message body must be specified. */ PJ_ASSERT_RETURN(to && content, PJ_EINVAL); acc = &pjsua_var.acc[acc_id]; /* Create request. */ status = pjsip_endpt_create_request(pjsua_var.endpt, &pjsip_message_method, (msg_data && msg_data->target_uri.slen? &msg_data->target_uri: to), &acc->cfg.id, to, NULL, NULL, -1, NULL, &tdata); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to create request", status); return status; } /* If account is locked to specific transport, then set transport to * the request. */ if (acc->cfg.transport_id != PJSUA_INVALID_ID) { pjsip_tpselector tp_sel; pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel); pjsip_tx_data_set_transport(tdata, &tp_sel); } /* Add accept header. */ pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)pjsua_im_create_accept(tdata->pool)); /* Create suitable Contact header unless a Contact header has been * set in the account. */ /* Ticket #1632: According to RFC 3428: * MESSAGE requests do not initiate dialogs. * User Agents MUST NOT insert Contact header fields into MESSAGE requests */ /* if (acc->contact.slen) { contact = acc->contact; } else { status = pjsua_acc_create_uac_contact(tdata->pool, &contact, acc_id, to); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to generate Contact header", status); pjsip_tx_data_dec_ref(tdata); return status; } } pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*) pjsip_generic_string_hdr_create(tdata->pool, &STR_CONTACT, &contact)); */ /* Create IM data to keep message details and give it back to * application on the callback */ im_data = PJ_POOL_ZALLOC_T(tdata->pool, pjsua_im_data); im_data->acc_id = acc_id; im_data->call_id = PJSUA_INVALID_ID; pj_strdup_with_null(tdata->pool, &im_data->to, to); pj_strdup_with_null(tdata->pool, &im_data->body, content); im_data->user_data = user_data; /* Set default media type if none is specified */ if (mime_type == NULL) { mime_type = &mime_text_plain; } /* Parse MIME type */ pjsua_parse_media_type(tdata->pool, mime_type, &media_type); /* Add message body */ tdata->msg->body = pjsip_msg_body_create( tdata->pool, &media_type.type, &media_type.subtype, &im_data->body); if (tdata->msg->body == NULL) { pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM); pjsip_tx_data_dec_ref(tdata); return PJ_ENOMEM; } /* Add additional headers etc. */ pjsua_process_msg_data(tdata, msg_data); /* Add route set */ pjsua_set_msg_route_set(tdata, &acc->route_set); /* If via_addr is set, use this address for the Via header. */ if (acc->cfg.allow_via_rewrite && acc->via_addr.host.slen > 0) { tdata->via_addr = acc->via_addr; tdata->via_tp = acc->via_tp; } /* Send request (statefully) */ status = pjsip_endpt_send_request( pjsua_var.endpt, tdata, -1, im_data, &im_callback); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to send request", status); return status; } return PJ_SUCCESS; }
/* * Send PUBLISH request. */ static pj_status_t send_publish(int acc_id, pj_bool_t active) { pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg; pjsua_acc *acc = &pjsua_var.acc[acc_id]; pjsip_pres_status pres_status; pjsip_tx_data *tdata; pj_status_t status; /* Create PUBLISH request */ if (active) { char *bpos; pj_str_t entity; status = pjsip_publishc_publish(acc->publish_sess, PJ_TRUE, &tdata); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error creating PUBLISH request", status); goto on_error; } /* Set our online status: */ pj_bzero(&pres_status, sizeof(pres_status)); pres_status.info_cnt = 1; pres_status.info[0].basic_open = acc->online_status; pres_status.info[0].id = acc->cfg.pidf_tuple_id; /* .. including RPID information */ pj_memcpy(&pres_status.info[0].rpid, &acc->rpid, sizeof(pjrpid_element)); /* Be careful not to send PIDF with presence entity ID containing * "<" character. */ if ((bpos=pj_strchr(&acc_cfg->id, '<')) != NULL) { char *epos = pj_strchr(&acc_cfg->id, '>'); if (epos - bpos < 2) { pj_assert(!"Unexpected invalid URI"); status = PJSIP_EINVALIDURI; goto on_error; } entity.ptr = bpos+1; entity.slen = epos - bpos - 1; } else { entity = acc_cfg->id; } /* Create and add PIDF message body */ status = pjsip_pres_create_pidf(tdata->pool, &pres_status, &entity, &tdata->msg->body); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error creating PIDF for PUBLISH request", status); pjsip_tx_data_dec_ref(tdata); goto on_error; } } else { status = pjsip_publishc_unpublish(acc->publish_sess, &tdata); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error creating PUBLISH request", status); goto on_error; } } /* Add headers etc */ pjsua_process_msg_data(tdata, NULL); /* Send the PUBLISH request */ status = pjsip_publishc_send(acc->publish_sess, tdata); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error sending PUBLISH request", status); goto on_error; } acc->publish_state = acc->online_status; return PJ_SUCCESS; on_error: if (acc->publish_sess) { pjsip_publishc_destroy(acc->publish_sess); acc->publish_sess = NULL; } return status; }
/* * Create a file recorder, and automatically connect this recorder to * the conference bridge. */ PJ_DEF(pj_status_t) pjsua_sxs_switcher_create( // const pj_str_t *filename, int direct_type, unsigned enc_type, void *enc_param, unsigned options, pjsua_recorder_id *p_id) { enum Format { FMT_UNKNOWN, FMT_WAV, FMT_MP3, }; unsigned slot, file_id; char path[PJ_MAXPATH]; pj_str_t ext; int file_format; pjmedia_port *port; pj_status_t status; /* Filename must present */ // PJ_ASSERT_RETURN(filename != NULL, PJ_EINVAL); /* Don't support encoding type at present */ PJ_ASSERT_RETURN(enc_type == 0, PJ_EINVAL); if (pjsua_var.rec_cnt >= PJ_ARRAY_SIZE(pjsua_var.recorder)) return PJ_ETOOMANY; file_format = FMT_WAV; PJSUA_LOCK(); for (file_id=0; file_id<PJ_ARRAY_SIZE(pjsua_var.recorder); ++file_id) { if (pjsua_var.recorder[file_id].port == NULL) break; } if (file_id == PJ_ARRAY_SIZE(pjsua_var.recorder)) { /* This is unexpected */ PJSUA_UNLOCK(); pj_assert(0); return PJ_EBUG; } if (file_format == FMT_WAV) { status = pjmedia_sxs_port_server_create( pjsua_var.media_cfg.clock_rate, pjsua_var.mconf_cfg.channel_count, pjsua_var.mconf_cfg.samples_per_frame, pjsua_var.mconf_cfg.bits_per_sample, options, 0, &port); fprintf(stderr, "samples_per_frame=%d, bits_per_samples=%d\n", pjsua_var.mconf_cfg.samples_per_frame, pjsua_var.mconf_cfg.bits_per_sample); } else { PJ_UNUSED_ARG(enc_param); port = NULL; status = PJ_ENOTSUP; } if (status != PJ_SUCCESS) { PJSUA_UNLOCK(); pjsua_perror(THIS_FILE, "Unable to open file for recording", status); return status; } pjmedia_sxs_port_outgoing_server_get_port(port, 0); if (direct_type == SUA_SWITCHER_SERVER) { } else { } // const pj_str_t filename = pj_str(path); assert(g_param->pool); status = pjmedia_conf_add_port(pjsua_var.mconf, g_param->pool, port, 0, &slot); if (status != PJ_SUCCESS) { pjmedia_port_destroy(port); PJSUA_UNLOCK(); return status; } pjsua_var.recorder[file_id].port = port; pjsua_var.recorder[file_id].slot = slot; if (p_id) *p_id = file_id; ++pjsua_var.rec_cnt; PJSUA_UNLOCK(); return PJ_SUCCESS; }
static void ui_detect_nat_type() { int i = pjsua_detect_nat_type(); if (i != PJ_SUCCESS) pjsua_perror(THIS_FILE, "Error", i); }