diff options
Diffstat (limited to 'pjsip/src/pjsua')
-rw-r--r-- | pjsip/src/pjsua/main.c | 277 | ||||
-rw-r--r-- | pjsip/src/pjsua/pjsua.h | 70 | ||||
-rw-r--r-- | pjsip/src/pjsua/pjsua_core.c | 89 | ||||
-rw-r--r-- | pjsip/src/pjsua/pjsua_inv.c | 24 | ||||
-rw-r--r-- | pjsip/src/pjsua/pjsua_opt.c | 13 | ||||
-rw-r--r-- | pjsip/src/pjsua/pjsua_pres.c | 471 |
6 files changed, 863 insertions, 81 deletions
diff --git a/pjsip/src/pjsua/main.c b/pjsip/src/pjsua/main.c index 37fde773..4a40d324 100644 --- a/pjsip/src/pjsua/main.c +++ b/pjsip/src/pjsua/main.c @@ -22,7 +22,8 @@ #define THIS_FILE "main.c" -static pjsip_inv_session *inv_session; +/* Current dialog */ +static struct pjsua_inv_data *inv_session; /* * Notify UI when invite state has changed. @@ -35,12 +36,16 @@ void pjsua_ui_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e) pjsua_inv_state_names[inv->state])); if (inv->state == PJSIP_INV_STATE_DISCONNECTED) { - if (inv == inv_session) - inv_session = NULL; + if (inv == inv_session->inv) { + inv_session = inv_session->next; + if (inv_session == &pjsua.inv_list) + inv_session = pjsua.inv_list.next; + } } else { - inv_session = inv; + if (inv_session == &pjsua.inv_list || inv_session == NULL) + inv_session = inv->mod_data[pjsua.mod.id]; } } @@ -56,24 +61,71 @@ void pjsua_ui_regc_on_state_changed(int code) } +/* + * Print buddy list. + */ +static void print_buddy_list(void) +{ + unsigned i; + + puts("Buddy list:"); + //puts("-------------------------------------------------------------------------------"); + if (pjsua.buddy_cnt == 0) + puts(" -none-"); + else { + for (i=0; i<pjsua.buddy_cnt; ++i) { + const char *status; + + if (pjsua.buddies[i].sub == NULL || + pjsua.buddies[i].status.info_cnt==0) + { + status = " ? "; + } + else if (pjsua.buddies[i].status.info[0].basic_open) + status = " Online"; + else + status = "Offline"; + + printf(" [%2d] <%s> %s\n", + i+1, status, pjsua.buddies[i].uri.ptr); + } + } + puts(""); +} /* * Show a bit of help. */ -static void ui_help(void) +static void keystroke_help(void) { - puts(""); - puts("Console keys:"); - puts(" m Make a call/another call"); - puts(" d Dump application states"); - puts(" a Answer incoming call"); - puts(" h Hangup current call"); - puts(" q Quit"); - puts(""); + + printf(">>>>\nOnline status: %s\n", + (pjsua.online_status ? "Online" : "Invisible")); + print_buddy_list(); + + //puts("Commands:"); + puts("+=============================================================================+"); + puts("| Call Commands: | IM & Presence: | Misc: |"); + puts("| | | |"); + puts("| m Make new call | i Send IM | o Send OPTIONS |"); + puts("| a Answer call | s Subscribe presence | d Dump status |"); + puts("| h Hangup call | u Unsubscribe presence | d1 Dump detailed |"); + puts("| ] Select next dialog | t Toggle Online status | |"); + puts("| [ Select previous dialog | | |"); + puts("+-----------------------------------------------------------------------------+"); + puts("| q QUIT |"); + puts("+=============================================================================+"); + printf(">>> "); + + fflush(stdout); } -static pj_bool_t input(const char *title, char *buf, pj_size_t len) + +/* + * Input simple string + */ +static pj_bool_t simple_input(const char *title, char *buf, pj_size_t len) { char *p; @@ -92,48 +144,131 @@ static pj_bool_t input(const char *title, char *buf, pj_size_t len) return PJ_TRUE; } + +#define NO_NB -2 +struct input_result +{ + int nb_result; + char *uri_result; +}; + + +/* + * Input URL. + */ +static void ui_input_url(const char *title, char *buf, int len, + struct input_result *result) +{ + result->nb_result = 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.buddy_cnt, pjsua.buddy_cnt); + printf("%s: ", title); + + fflush(stdout); + fgets(buf, len, stdin); + len = strlen(buf); + + /* Left trim */ + while (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 (isdigit(*buf) || *buf=='-') { + + int i; + + if (*buf=='-') + i = 1; + else + i = 0; + + for (; i<len; ++i) { + if (!isdigit(buf[i])) { + puts("Invalid input"); + return; + } + } + + result->nb_result = atoi(buf); + + if (result->nb_result > 0 && result->nb_result <= (int)pjsua.buddy_cnt) { + --result->nb_result; + return; + } + if (result->nb_result == -1) + return; + + puts("Invalid input"); + result->nb_result = NO_NB; + return; + + } else { + pj_status_t status; + + if ((status=pjsua_verify_sip_url(buf)) != PJ_SUCCESS) { + pjsua_perror("Invalid URL", status); + return; + } + + result->uri_result = buf; + } +} + static void ui_console_main(void) { + char menuin[10]; char buf[128]; pjsip_inv_session *inv; + struct input_result result; - //ui_help(); + //keystroke_help(); for (;;) { - ui_help(); - fgets(buf, sizeof(buf), stdin); + keystroke_help(); + fgets(menuin, sizeof(menuin), stdin); - switch (buf[0]) { + switch (menuin[0]) { case 'm': - if (inv_session != NULL) { - puts("Can not make call while another one is in progress"); - fflush(stdout); - continue; - } - -#if 1 /* Make call! : */ - if (!input("Enter URL to call", buf, sizeof(buf))) - continue; - pjsua_invite(buf, &inv); - -#else - - pjsua_invite("sip:localhost:5061", &inv); -#endif + if (pj_list_size(&pjsua.inv_list)) + printf("(You have %d calls)\n", pj_list_size(&pjsua.inv_list)); + + ui_input_url("Make call", buf, sizeof(buf), &result); + if (result.nb_result != NO_NB) { + if (result.nb_result == -1) + puts("You can't do that with make call!"); + else + pjsua_invite(pjsua.buddies[result.nb_result].uri.ptr, &inv); + } else if (result.uri_result) + pjsua_invite(result.uri_result, &inv); + break; - case 'd': - pjsua_dump(); - break; - case 'a': - if (inv_session == NULL || inv_session->role != PJSIP_ROLE_UAS || - inv_session->state >= PJSIP_INV_STATE_CONNECTING) + if (inv_session == &pjsua.inv_list || + inv_session->inv->role != PJSIP_ROLE_UAS || + inv_session->inv->state >= PJSIP_INV_STATE_CONNECTING) { puts("No pending incoming call"); fflush(stdout); @@ -143,16 +278,16 @@ static void ui_console_main(void) pj_status_t status; pjsip_tx_data *tdata; - if (!input("Answer with code (100-699)", buf, sizeof(buf))) + if (!simple_input("Answer with code (100-699)", buf, sizeof(buf))) continue; if (atoi(buf) < 100) continue; - status = pjsip_inv_answer(inv_session, atoi(buf), NULL, NULL, - &tdata); + status = pjsip_inv_answer(inv_session->inv, atoi(buf), + NULL, NULL, &tdata); if (status == PJ_SUCCESS) - status = pjsip_inv_send_msg(inv_session, tdata, NULL); + status = pjsip_inv_send_msg(inv_session->inv, tdata, NULL); if (status != PJ_SUCCESS) pjsua_perror("Unable to create/send response", status); @@ -160,9 +295,10 @@ static void ui_console_main(void) break; + case 'h': - if (inv_session == NULL) { + if (inv_session == &pjsua.inv_list) { puts("No current call"); fflush(stdout); continue; @@ -171,22 +307,62 @@ static void ui_console_main(void) pj_status_t status; pjsip_tx_data *tdata; - status = pjsip_inv_end_session(inv_session, PJSIP_SC_DECLINE, - NULL, &tdata); + status = pjsip_inv_end_session(inv_session->inv, + PJSIP_SC_DECLINE, NULL, &tdata); if (status != PJ_SUCCESS) { pjsua_perror("Failed to create end session message", status); continue; } - status = pjsip_inv_send_msg(inv_session, tdata, NULL); + status = pjsip_inv_send_msg(inv_session->inv, tdata, NULL); if (status != PJ_SUCCESS) { pjsua_perror("Failed to send end session message", status); continue; } } + break; + + case ']': + inv_session = inv_session->next; + if (inv_session == &pjsua.inv_list) + inv_session = pjsua.inv_list.next; + break; + + case '[': + inv_session = inv_session->prev; + if (inv_session == &pjsua.inv_list) + inv_session = pjsua.inv_list.prev; + break; + + case 's': + case 'u': + ui_input_url("Subscribe presence of", buf, sizeof(buf), &result); + if (result.nb_result != NO_NB) { + if (result.nb_result == -1) { + unsigned i; + for (i=0; i<pjsua.buddy_cnt; ++i) + pjsua.buddies[i].monitor = (menuin[0]=='s'); + } else { + pjsua.buddies[result.nb_result].monitor = (menuin[0]=='s'); + } + + pjsua_pres_refresh(); + + } else if (result.uri_result) { + puts("Sorry, can only subscribe to buddy's presence, not arbitrary URL (for now)"); + } break; + case 't': + pjsua.online_status = !pjsua.online_status; + pjsua_pres_refresh(); + break; + + case 'd': + pjsua_dump(); + break; + case 'q': goto on_exit; } @@ -254,7 +430,7 @@ static pj_status_t console_on_tx_msg(pjsip_tx_data *tdata) static pjsip_module console_msg_logger = { NULL, NULL, /* prev, next. */ - { "mod-console-msg-logger", 22 }, /* Name. */ + { "mod-pjsua-log", 13 }, /* Name. */ -1, /* Id */ PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,/* Priority */ NULL, /* User data. */ @@ -385,6 +561,11 @@ int main(int argc, char *argv[]) pj_thread_sleep(500); + /* No current call initially: */ + + inv_session = &pjsua.inv_list; + + /* Start UI console main loop: */ ui_console_main(); diff --git a/pjsip/src/pjsua/pjsua.h b/pjsip/src/pjsua/pjsua.h index ebb97a0d..202c2840 100644 --- a/pjsip/src/pjsua/pjsua.h +++ b/pjsip/src/pjsua/pjsua.h @@ -31,6 +31,9 @@ /* Include all PJSIP-UA headers */ #include <pjsip_ua.h> +/* Include all PJSIP-SIMPLE headers */ +#include <pjsip_simple.h> + /* Include all PJLIB-UTIL headers. */ #include <pjlib-util.h> @@ -61,6 +64,33 @@ struct pjsua_inv_data }; +/** + * Buddy data. + */ +struct pjsua_buddy +{ + pj_str_t uri; /**< Buddy URI */ + pj_bool_t monitor; /**< Should we monitor? */ + pjsip_evsub *sub; /**< Buddy presence subscription */ + pjsip_pres_status status; /**< Buddy presence status. */ +}; + +typedef struct pjsua_buddy pjsua_buddy; + + +/** + * Server presence subscription list head. + */ +struct pjsua_srv_pres +{ + PJ_DECL_LIST_MEMBER(struct pjsua_srv_pres); + pjsip_evsub *sub; + char *remote; +}; + +typedef struct pjsua_srv_pres pjsua_srv_pres; + + /* PJSUA application variables. */ struct pjsua @@ -141,10 +171,13 @@ struct pjsua struct pjsua_inv_data inv_list; - /* Buddy list: */ + /* SIMPLE and buddy status: */ + + pj_bool_t online_status; /**< Out online status. */ + pjsua_srv_pres pres_srv_list; /**< Server subscription list. */ unsigned buddy_cnt; - pj_str_t buddies[PJSUA_MAX_BUDDIES]; + pjsua_buddy buddies[PJSUA_MAX_BUDDIES]; }; @@ -235,6 +268,12 @@ void pjsua_inv_on_new_session(pjsip_inv_session *inv, pjsip_event *e); void pjsua_inv_on_media_update(pjsip_inv_session *inv, pj_status_t status); +/** + * Terminate all calls. + */ +void pjsua_inv_shutdown(void); + + /***************************************************************************** * PJSUA Client Registration API (defined in pjsua_reg.c). */ @@ -253,6 +292,33 @@ pj_status_t pjsua_regc_init(void); void pjsua_regc_update(pj_bool_t renew); + + +/***************************************************************************** + * PJSUA Presence (pjsua_pres.c) + */ + +/** + * Init presence. + */ +pj_status_t pjsua_pres_init(); + +/** + * Refresh both presence client and server subscriptions. + */ +void pjsua_pres_refresh(void); + +/** + * Terminate all subscriptions + */ +void pjsua_pres_shutdown(void); + +/** + * Dump presence subscriptions. + */ +void pjsua_pres_dump(void); + + /***************************************************************************** * User Interface API. * diff --git a/pjsip/src/pjsua/pjsua_core.c b/pjsip/src/pjsua/pjsua_core.c index f97c8f43..2d059d92 100644 --- a/pjsip/src/pjsua/pjsua_core.c +++ b/pjsip/src/pjsua/pjsua_core.c @@ -79,6 +79,11 @@ void pjsua_default(void) /* Init invite session list: */ pj_list_init(&pjsua.inv_list); + + /* Init server presence subscription list: */ + + pj_list_init(&pjsua.pres_srv_list); + } @@ -391,14 +396,14 @@ on_error: } -static int PJ_THREAD_FUNC pjsua_worker_thread(void *arg) +static int PJ_THREAD_FUNC pjsua_poll(void *arg) { PJ_UNUSED_ARG(arg); - while (!pjsua.quit_flag) { + do { pj_time_val timeout = { 0, 10 }; pjsip_endpt_handle_events (pjsua.endpt, &timeout); - } + } while (!pjsua.quit_flag); return 0; } @@ -435,7 +440,7 @@ pj_status_t pjsua_init(void) pjsua.pool = pj_pool_create(&pjsua.cp.factory, "pjsua", 4000, 4000, NULL); - /* Init PJSIP and all the modules: */ + /* Init PJSIP : */ status = init_stack(); if (status != PJ_SUCCESS) { @@ -445,6 +450,20 @@ pj_status_t pjsua_init(void) } + /* Init core SIMPLE module : */ + + pjsip_evsub_init_module(pjsua.endpt); + + /* Init presence module: */ + + pjsip_pres_init_module( pjsua.endpt, pjsip_evsub_instance()); + + + /* Init pjsua presence handler: */ + + pjsua_pres_init(); + + /* Init media endpoint: */ status = pjmedia_endpt_create(&pjsua.cp.factory, &pjsua.med_endpt); @@ -609,7 +628,7 @@ pj_status_t pjsua_start(void) /* Create worker thread(s), if required: */ for (i=0; i<pjsua.thread_cnt; ++i) { - status = pj_thread_create( pjsua.pool, "pjsua", &pjsua_worker_thread, + status = pj_thread_create( pjsua.pool, "pjsua", &pjsua_poll, NULL, 0, 0, &pjsua.threads[i]); if (status != PJ_SUCCESS) { pjsua.quit_flag = 1; @@ -635,11 +654,26 @@ pj_status_t pjsua_start(void) } - + PJ_LOG(3,(THIS_FILE, "PJSUA version %s started", PJ_VERSION)); return PJ_SUCCESS; } +/* Sleep with polling */ +static void busy_sleep(unsigned msec) +{ + pj_time_val timeout, now; + + pj_gettimeofday(&timeout); + timeout.msec += msec; + pj_time_val_normalize(&timeout); + + do { + pjsua_poll(NULL); + pj_gettimeofday(&now); + } while (PJ_TIME_VAL_LT(now, timeout)); +} + /* * Destroy pjsua. */ @@ -647,42 +681,43 @@ pj_status_t pjsua_destroy(void) { int i; - /* Unregister, if required: */ - if (pjsua.regc) { + /* Signal threads to quit: */ + pjsua.quit_flag = 1; - pjsua_regc_update(0); + /* Wait worker threads to quit: */ + for (i=0; i<pjsua.thread_cnt; ++i) { + + if (pjsua.threads[i]) { + pj_thread_join(pjsua.threads[i]); + pj_thread_destroy(pjsua.threads[i]); + pjsua.threads[i] = NULL; + } + } - /* Wait for some time to allow unregistration to complete: */ - pj_thread_sleep(500); - } + /* Terminate all calls. */ + pjsua_inv_shutdown(); - /* Signal threads to quit: */ + /* Terminate all presence subscriptions. */ + pjsua_pres_shutdown(); - pjsua.quit_flag = 1; + /* Unregister, if required: */ + if (pjsua.regc) { + pjsua_regc_update(0); + } + /* Wait for some time to allow unregistration to complete: */ + PJ_LOG(4,(THIS_FILE, "Shutting down...")); + busy_sleep(1000); /* Shutdown pjmedia-codec: */ - pjmedia_codec_deinit(); - /* Destroy sound framework: * (this should be done in pjmedia_shutdown()) */ pj_snd_deinit(); - /* Wait worker threads to quit: */ - - for (i=0; i<pjsua.thread_cnt; ++i) { - - if (pjsua.threads[i]) { - pj_thread_join(pjsua.threads[i]); - pj_thread_destroy(pjsua.threads[i]); - pjsua.threads[i] = NULL; - } - } - /* Destroy endpoint. */ pjsip_endpt_destroy(pjsua.endpt); diff --git a/pjsip/src/pjsua/pjsua_inv.c b/pjsip/src/pjsua/pjsua_inv.c index 7c6dbf26..6f9607b5 100644 --- a/pjsip/src/pjsua/pjsua_inv.c +++ b/pjsip/src/pjsua/pjsua_inv.c @@ -80,6 +80,7 @@ pj_status_t pjsua_invite(const char *cstr_dest_uri, inv_data = pj_pool_zalloc( dlg->pool, sizeof(struct pjsua_inv_data)); inv_data->inv = inv; dlg->mod_data[pjsua.mod.id] = inv_data; + inv->mod_data[pjsua.mod.id] = inv_data; /* Set dialog Route-Set: */ @@ -221,6 +222,7 @@ pj_bool_t pjsua_inv_on_incoming(pjsip_rx_data *rdata) inv_data = pj_pool_zalloc(dlg->pool, sizeof(struct pjsua_inv_data)); inv_data->inv = inv; dlg->mod_data[pjsua.mod.id] = inv_data; + inv->mod_data[pjsua.mod.id] = inv_data; pj_list_push_back(&pjsua.inv_list, inv_data); @@ -345,3 +347,25 @@ void pjsua_inv_on_media_update(pjsip_inv_session *inv, pj_status_t status) PJ_LOG(3,(THIS_FILE,"Media has been started successfully")); } } + + +/* + * Terminate all calls. + */ +void pjsua_inv_shutdown() +{ + struct pjsua_inv_data *inv_data, *next; + + inv_data = pjsua.inv_list.next; + while (inv_data != &pjsua.inv_list) { + pjsip_tx_data *tdata; + + next = inv_data->next; + + if (pjsip_inv_end_session(inv_data->inv, 410, NULL, &tdata)==0) + pjsip_inv_send_msg(inv_data->inv, tdata, NULL); + + inv_data = next; + } +} + diff --git a/pjsip/src/pjsua/pjsua_opt.c b/pjsip/src/pjsua/pjsua_opt.c index 61e5adba..5ec1a56a 100644 --- a/pjsip/src/pjsua/pjsua_opt.c +++ b/pjsip/src/pjsua/pjsua_opt.c @@ -403,7 +403,7 @@ pj_status_t pjsua_parse_args(int argc, char *argv[]) printf("Error: too many buddies in buddy list.\n"); return -1; } - pjsua.buddies[pjsua.buddy_cnt++] = pj_str(optarg); + pjsua.buddies[pjsua.buddy_cnt++].uri = pj_str(optarg); break; } } @@ -510,6 +510,7 @@ void pjsua_dump(void) pjsip_endpt_dump(pjsua.endpt, 1); pjmedia_endpt_dump(pjsua.med_endpt); + pjsip_tsx_layer_dump(); pjsip_ua_dump(); @@ -536,6 +537,9 @@ void pjsua_dump(void) } } + /* Dump presence status */ + pjsua_pres_dump(); + pj_log_set_decor(old_decor); PJ_LOG(3,(THIS_FILE, "Dump complete")); } @@ -547,8 +551,9 @@ void pjsua_dump(void) pj_status_t pjsua_load_settings(const char *filename) { int argc = 3; - char *argv[] = { "pjsua", "--config-file", (char*)filename, NULL}; + char *argv[4] = { "pjsua", "--config-file", NULL, NULL}; + argv[3] = (char*)filename; return pjsua_parse_args(argc, argv); } @@ -654,8 +659,8 @@ pj_status_t pjsua_save_settings(const char *filename) /* Add buddies. */ for (i=0; i<pjsua.buddy_cnt; ++i) { pj_ansi_sprintf(line, "--add-buddy %.*s\n", - (int)pjsua.buddies[i].slen, - pjsua.buddies[i].ptr); + (int)pjsua.buddies[i].uri.slen, + pjsua.buddies[i].uri.ptr); pj_strcat2(&cfg, line); } diff --git a/pjsip/src/pjsua/pjsua_pres.c b/pjsip/src/pjsua/pjsua_pres.c new file mode 100644 index 00000000..db009eed --- /dev/null +++ b/pjsip/src/pjsua/pjsua_pres.c @@ -0,0 +1,471 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "pjsua.h" + +/* + * pjsua_pres.c + * + * Presence related stuffs. + */ + +#define THIS_FILE "pjsua_pres.c" + + + +/* ************************************************************************** + * THE FOLLOWING PART HANDLES SERVER SUBSCRIPTION + * ************************************************************************** + */ + +/* Proto */ +static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata); + +/* The module instance. */ +static pjsip_module mod_pjsua_pres = +{ + NULL, NULL, /* prev, next. */ + { "mod-pjsua-pres", 14 }, /* Name. */ + -1, /* Id */ + PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */ + NULL, /* User data. */ + NULL, /* load() */ + NULL, /* start() */ + NULL, /* stop() */ + NULL, /* unload() */ + &pres_on_rx_request, /* on_rx_request() */ + NULL, /* on_rx_response() */ + NULL, /* on_tx_request. */ + NULL, /* on_tx_response() */ + NULL, /* on_tsx_state() */ + +}; + + +/* Callback called when *server* subscription state has changed. */ +static void pres_evsub_on_srv_state( pjsip_evsub *sub, pjsip_event *event) +{ + pjsua_srv_pres *uapres = pjsip_evsub_get_mod_data(sub, pjsua.mod.id); + + PJ_UNUSED_ARG(event); + + if (uapres) { + PJ_LOG(3,(THIS_FILE, "Server subscription to %s is %s", + uapres->remote, pjsip_evsub_get_state_name(sub))); + + if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) { + pjsip_evsub_set_mod_data(sub, pjsua.mod.id, NULL); + pj_list_erase(uapres); + } + } +} + +/* 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) +{ + pjsip_method *req_method = &rdata->msg_info.msg->line.req.method; + pjsua_srv_pres *uapres; + pjsip_evsub *sub; + pjsip_evsub_user pres_cb; + pjsip_tx_data *tdata; + pjsip_pres_status pres_status; + pjsip_dialog *dlg; + pj_status_t status; + + if (pjsip_method_cmp(req_method, &pjsip_subscribe_method) != 0) + return PJ_FALSE; + + /* Incoming SUBSCRIBE: */ + + /* Create UAS dialog: */ + status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata, + &pjsua.contact_uri, &dlg); + if (status != PJ_SUCCESS) { + pjsua_perror("Unable to create UAS dialog for subscription", status); + return PJ_FALSE; + } + + /* Init callback: */ + pj_memset(&pres_cb, 0, 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) { + PJ_TODO(DESTROY_DIALOG); + pjsua_perror("Unable to create server subscription", status); + return PJ_FALSE; + } + + /* Attach our data to the subscription: */ + uapres = pj_pool_alloc(dlg->pool, sizeof(pjsua_srv_pres)); + uapres->sub = sub; + uapres->remote = pj_pool_alloc(dlg->pool, PJSIP_MAX_URL_SIZE); + 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.mod.id, uapres); + + /* Add server subscription to the list: */ + pj_list_push_back(&pjsua.pres_srv_list, uapres); + + + /* Create and send 200 (OK) to the SUBSCRIBE request: */ + status = pjsip_pres_accept(sub, rdata, 200, NULL); + if (status != PJ_SUCCESS) { + pjsua_perror("Unable to accept presence subscription", status); + pj_list_erase(uapres); + return PJ_FALSE; + } + + + /* Set our online status: */ + pj_memset(&pres_status, 0, sizeof(pres_status)); + pres_status.info_cnt = 1; + pres_status.info[0].basic_open = pjsua.online_status; + //Both pjsua.local_uri and pjsua.contact_uri are enclosed in "<" and ">" + //causing XML parsing to fail. + //pres_status.info[0].contact = pjsua.local_uri; + + pjsip_pres_set_status(sub, &pres_status); + + /* Create and send the first NOTIFY to active subscription: */ + status = pjsip_pres_notify( sub, PJSIP_EVSUB_STATE_ACTIVE, NULL, + NULL, &tdata); + if (status == PJ_SUCCESS) + status = pjsip_pres_send_request( sub, tdata); + + if (status != PJ_SUCCESS) { + pjsua_perror("Unable to create/send NOTIFY", status); + pj_list_erase(uapres); + return PJ_FALSE; + } + + + /* Done: */ + + return PJ_TRUE; +} + + +/* Refresh subscription (e.g. when our online status has changed) */ +static void refresh_server_subscription() +{ + pjsua_srv_pres *uapres; + + uapres = pjsua.pres_srv_list.next; + + while (uapres != &pjsua.pres_srv_list) { + + pjsip_pres_status pres_status; + pjsip_tx_data *tdata; + + pjsip_pres_get_status(uapres->sub, &pres_status); + if (pres_status.info[0].basic_open != pjsua.online_status) { + pres_status.info[0].basic_open = pjsua.online_status; + pjsip_pres_set_status(uapres->sub, &pres_status); + + if (pjsua.quit_flag) { + pj_str_t reason = { "noresource", 10 }; + if (pjsip_pres_notify(uapres->sub, + PJSIP_EVSUB_STATE_TERMINATED, NULL, + &reason, &tdata)==PJ_SUCCESS) + { + pjsip_pres_send_request(uapres->sub, tdata); + } + } else { + if (pjsip_pres_current_notify(uapres->sub, &tdata)==PJ_SUCCESS) + pjsip_pres_send_request(uapres->sub, tdata); + } + } + + uapres = uapres->next; + } +} + + + +/* ************************************************************************** + * THE FOLLOWING PART HANDLES CLIENT SUBSCRIPTION + * ************************************************************************** + */ + +/* Callback called when *client* subscription state has changed. */ +static void pjsua_evsub_on_state( pjsip_evsub *sub, pjsip_event *event) +{ + pjsua_buddy *buddy; + + PJ_UNUSED_ARG(event); + + buddy = pjsip_evsub_get_mod_data(sub, pjsua.mod.id); + if (buddy) { + PJ_LOG(3,(THIS_FILE, + "Presence subscription to %s is %s", + buddy->uri.ptr, + pjsip_evsub_get_state_name(sub))); + + if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) { + buddy->sub = NULL; + buddy->status.info_cnt = 0; + pjsip_evsub_set_mod_data(sub, pjsua.mod.id, NULL); + } + } +} + +/* Callback called when we receive NOTIFY */ +static void pjsua_evsub_on_rx_notify(pjsip_evsub *sub, + pjsip_rx_data *rdata, + int *p_st_code, + pj_str_t **p_st_text, + pjsip_hdr *res_hdr, + pjsip_msg_body **p_body) +{ + pjsua_buddy *buddy; + + buddy = pjsip_evsub_get_mod_data(sub, pjsua.mod.id); + if (buddy) { + /* Update our info. */ + pjsip_pres_get_status(sub, &buddy->status); + + if (buddy->status.info_cnt) { + PJ_LOG(3,(THIS_FILE, "%s is %s", + buddy->uri.ptr, + (buddy->status.info[0].basic_open?"online":"offline"))); + } else { + PJ_LOG(3,(THIS_FILE, "No presence info for %s", + buddy->uri.ptr)); + } + } + + /* The default is to send 200 response to NOTIFY. + * Just leave it there.. + */ + PJ_UNUSED_ARG(rdata); + PJ_UNUSED_ARG(p_st_code); + PJ_UNUSED_ARG(p_st_text); + PJ_UNUSED_ARG(res_hdr); + PJ_UNUSED_ARG(p_body); +} + + +/* Event subscription callback. */ +static pjsip_evsub_user pres_callback = +{ + &pjsua_evsub_on_state, + + NULL, /* on_tsx_state: don't care about transaction state. */ + + NULL, /* on_rx_refresh: don't care about SUBSCRIBE refresh, unless + * we want to authenticate + */ + + &pjsua_evsub_on_rx_notify, + + NULL, /* on_client_refresh: Use default behaviour, which is to + * refresh client subscription. */ + + NULL, /* on_server_timeout: Use default behaviour, which is to send + * NOTIFY to terminate. + */ +}; + + +/* It does what it says.. */ +static void subscribe_buddy_presence(unsigned index) +{ + pjsip_dialog *dlg; + pjsip_tx_data *tdata; + pj_status_t status; + + status = pjsip_dlg_create_uac( pjsip_ua_instance(), + &pjsua.local_uri, + &pjsua.contact_uri, + &pjsua.buddies[index].uri, + NULL, &dlg); + if (status != PJ_SUCCESS) { + pjsua_perror("Unable to create dialog", status); + return; + } + + status = pjsip_pres_create_uac( dlg, &pres_callback, + &pjsua.buddies[index].sub); + if (status != PJ_SUCCESS) { + pjsua.buddies[index].sub = NULL; + pjsua_perror("Unable to create presence client", status); + return; + } + + pjsip_evsub_set_mod_data(pjsua.buddies[index].sub, pjsua.mod.id, + &pjsua.buddies[index]); + + status = pjsip_pres_initiate(pjsua.buddies[index].sub, 60, &tdata); + if (status != PJ_SUCCESS) { + pjsua.buddies[index].sub = NULL; + pjsua_perror("Unable to create initial SUBSCRIBE", status); + return; + } + + status = pjsip_pres_send_request(pjsua.buddies[index].sub, tdata); + if (status != PJ_SUCCESS) { + pjsua.buddies[index].sub = NULL; + pjsua_perror("Unable to send initial SUBSCRIBE", status); + return; + } + + PJ_TODO(DESTROY_DIALOG_ON_ERROR); +} + + +/* It does what it says... */ +static void unsubscribe_buddy_presence(unsigned index) +{ + pjsip_tx_data *tdata; + pj_status_t status; + + if (pjsua.buddies[index].sub == NULL) + return; + + if (pjsip_evsub_get_state(pjsua.buddies[index].sub) == + PJSIP_EVSUB_STATE_TERMINATED) + { + pjsua.buddies[index].sub = NULL; + return; + } + + status = pjsip_pres_initiate( pjsua.buddies[index].sub, 0, &tdata); + if (status == PJ_SUCCESS) + status = pjsip_pres_send_request( pjsua.buddies[index].sub, tdata ); + + if (status == PJ_SUCCESS) { + + //pjsip_evsub_set_mod_data(pjsua.buddies[index].sub, pjsua.mod.id, + // NULL); + //pjsua.buddies[index].sub = NULL; + + } else { + pjsua_perror("Unable to unsubscribe presence", status); + } +} + + +/* It does what it says.. */ +static void refresh_client_subscription(void) +{ + unsigned i; + + for (i=0; i<pjsua.buddy_cnt; ++i) { + + if (pjsua.buddies[i].monitor && !pjsua.buddies[i].sub) { + subscribe_buddy_presence(i); + + } else if (!pjsua.buddies[i].monitor && pjsua.buddies[i].sub) { + unsubscribe_buddy_presence(i); + + } + } +} + + +/* + * Init presence + */ +pj_status_t pjsua_pres_init() +{ + pj_status_t status; + + status = pjsip_endpt_register_module( pjsua.endpt, &mod_pjsua_pres); + if (status != PJ_SUCCESS) { + pjsua_perror("Unable to register pjsua presence module", status); + } + + return status; +} + +/* + * Refresh presence + */ +void pjsua_pres_refresh(void) +{ + refresh_client_subscription(); + refresh_server_subscription(); +} + + +/* + * Shutdown presence. + */ +void pjsua_pres_shutdown(void) +{ + unsigned i; + + pjsua.online_status = 0; + for (i=0; i<pjsua.buddy_cnt; ++i) { + pjsua.buddies[i].monitor = 0; + } + pjsua_pres_refresh(); +} + +/* + * Dump presence status. + */ +void pjsua_pres_dump(void) +{ + unsigned i; + + PJ_LOG(3,(THIS_FILE, "Dumping pjsua server subscriptions:")); + if (pj_list_empty(&pjsua.pres_srv_list)) { + PJ_LOG(3,(THIS_FILE, " - none - ")); + } else { + struct pjsua_srv_pres *uapres; + + uapres = pjsua.pres_srv_list.next; + while (uapres != &pjsua.pres_srv_list) { + + PJ_LOG(3,(THIS_FILE, " %10s %s", + pjsip_evsub_get_state_name(uapres->sub), + uapres->remote)); + + uapres = uapres->next; + } + } + + PJ_LOG(3,(THIS_FILE, "Dumping pjsua client subscriptions:")); + if (pjsua.buddy_cnt == 0) { + PJ_LOG(3,(THIS_FILE, " - no buddy list - ")); + } else { + for (i=0; i<pjsua.buddy_cnt; ++i) { + + if (pjsua.buddies[i].sub) { + PJ_LOG(3,(THIS_FILE, " %10s %s", + pjsip_evsub_get_state_name(pjsua.buddies[i].sub), + pjsua.buddies[i].uri.ptr)); + } else { + PJ_LOG(3,(THIS_FILE, " %10s %s", + "(null)", + pjsua.buddies[i].uri.ptr)); + } + } + } +} + |