|
413 | 413 | \verbatim
|
414 | 414 | {
|
415 | 415 | "request" : "message",
|
| 416 | + "call_id" : "<user-defined value of Call-ID SIP header used to send the message; optional>", |
416 | 417 | "content_type" : "<content type; optional>"
|
417 | 418 | "content" : "<text to send>",
|
418 | 419 | "uri" : "<SIP URI of the peer; optional; if set, the message will be sent out of dialog>",
|
|
435 | 436 | "headers" : "<object with key/value strings; custom headers extracted from SIP event based on incoming_header_prefix defined in register request; optional>"
|
436 | 437 | }
|
437 | 438 | }
|
| 439 | +\endverbatim |
| 440 | + * |
| 441 | + * After delivery a \c messagedelivery event will be sent back with the SIP server response. |
| 442 | + * Used to track the delivery status of the message. |
| 443 | + * |
| 444 | +\verbatim |
| 445 | +{ |
| 446 | + "sip" : "event", |
| 447 | + "call_id" : "<value of SIP Call-ID header for related message>", |
| 448 | + "result" : { |
| 449 | + "event" : "messagedelivery", |
| 450 | + "code" : "<SIP error code>", |
| 451 | + "reason" : "<SIP error reason>", |
| 452 | + } |
| 453 | +} |
438 | 454 | \endverbatim
|
439 | 455 | *
|
440 | 456 | * SIP INFO works pretty much the same way, except that you use an \c info
|
@@ -1049,6 +1065,7 @@ typedef struct janus_sip_session {
|
1049 | 1065 | static GHashTable *sessions;
|
1050 | 1066 | static GHashTable *identities;
|
1051 | 1067 | static GHashTable *callids;
|
| 1068 | +static GHashTable *messageids; |
1052 | 1069 | static GHashTable *masters;
|
1053 | 1070 | static GHashTable *transfers;
|
1054 | 1071 | static janus_mutex sessions_mutex = JANUS_MUTEX_INITIALIZER;
|
@@ -1076,6 +1093,11 @@ static void janus_sip_session_destroy(janus_sip_session *session) {
|
1076 | 1093 | janus_refcount_decrease(&session->ref);
|
1077 | 1094 | }
|
1078 | 1095 |
|
| 1096 | +static void janus_sip_session_dereference(janus_sip_session *session) { |
| 1097 | + /* This is used to decrease the reference when removing to the messageids hashtable. janus_refcount_increase(&session->ref) must be called before inserting into messageids hashtable */ |
| 1098 | + janus_refcount_decrease(&session->ref); |
| 1099 | +} |
| 1100 | + |
1079 | 1101 | static void janus_sip_session_free(const janus_refcount *session_ref) {
|
1080 | 1102 | janus_sip_session *session = janus_refcount_containerof(session_ref, janus_sip_session, ref);
|
1081 | 1103 | /* Remove the reference to the core plugin session */
|
@@ -1936,6 +1958,7 @@ int janus_sip_init(janus_callbacks *callback, const char *config_path) {
|
1936 | 1958 | sessions = g_hash_table_new_full(NULL, NULL, NULL, (GDestroyNotify)janus_sip_session_destroy);
|
1937 | 1959 | identities = g_hash_table_new(g_str_hash, g_str_equal);
|
1938 | 1960 | callids = g_hash_table_new(g_str_hash, g_str_equal);
|
| 1961 | + messageids = g_hash_table_new_full(NULL, NULL, (GDestroyNotify)g_free, (GDestroyNotify)janus_sip_session_dereference); |
1939 | 1962 | masters = g_hash_table_new(NULL, NULL);
|
1940 | 1963 | transfers = g_hash_table_new_full(NULL, NULL, NULL, (GDestroyNotify)janus_sip_transfer_destroy);
|
1941 | 1964 | messages = g_async_queue_new_full((GDestroyNotify) janus_sip_message_free);
|
@@ -1972,11 +1995,13 @@ void janus_sip_destroy(void) {
|
1972 | 1995 | janus_mutex_lock(&sessions_mutex);
|
1973 | 1996 | g_hash_table_destroy(sessions);
|
1974 | 1997 | g_hash_table_destroy(callids);
|
| 1998 | + g_hash_table_destroy(messageids); |
1975 | 1999 | g_hash_table_destroy(identities);
|
1976 | 2000 | g_hash_table_destroy(masters);
|
1977 | 2001 | g_hash_table_destroy(transfers);
|
1978 | 2002 | sessions = NULL;
|
1979 | 2003 | callids = NULL;
|
| 2004 | + messageids = NULL; |
1980 | 2005 | identities = NULL;
|
1981 | 2006 | masters = NULL;
|
1982 | 2007 | transfers = NULL;
|
@@ -4637,37 +4662,76 @@ static void *janus_sip_handler(void *data) {
|
4637 | 4662 | char custom_headers[2048];
|
4638 | 4663 | janus_sip_parse_custom_headers(root, (char *)&custom_headers, sizeof(custom_headers));
|
4639 | 4664 |
|
| 4665 | + char *message_callid = NULL; |
4640 | 4666 | if(in_dialog_message) {
|
| 4667 | + /* Take Call-ID, later used to report delivery status */ |
| 4668 | + message_callid = g_strdup(session->callid) ; |
4641 | 4669 | nua_message(session->stack->s_nh_i,
|
4642 | 4670 | SIPTAG_CONTENT_TYPE_STR(content_type),
|
4643 | 4671 | SIPTAG_PAYLOAD_STR(msg_content),
|
4644 | 4672 | TAG_IF(strlen(custom_headers) > 0, SIPTAG_HEADER_STR(custom_headers)),
|
4645 | 4673 | TAG_END());
|
4646 | 4674 | } else {
|
4647 |
| - janus_mutex_lock(&session->stack->smutex); |
4648 |
| - if(session->stack->s_nh_m == NULL) { |
4649 |
| - if (session->stack->s_nua == NULL) { |
| 4675 | + /* Get appropriate handle */ |
| 4676 | + nua_handle_t *nh = NULL; |
| 4677 | + if(!session->helper) { |
| 4678 | + janus_mutex_lock(&session->stack->smutex); |
| 4679 | + if(session->stack->s_nua == NULL) { |
4650 | 4680 | janus_mutex_unlock(&session->stack->smutex);
|
4651 | 4681 | JANUS_LOG(LOG_ERR, "NUA destroyed while sending message?\n");
|
4652 | 4682 | error_code = JANUS_SIP_ERROR_LIBSOFIA_ERROR;
|
4653 | 4683 | g_snprintf(error_cause, 512, "Invalid NUA");
|
4654 | 4684 | goto error;
|
4655 | 4685 | }
|
4656 |
| - session->stack->s_nh_m = nua_handle(session->stack->s_nua, session, TAG_END()); |
| 4686 | + nh = nua_handle(session->stack->s_nua, session, TAG_END()); |
| 4687 | + janus_mutex_unlock(&session->stack->smutex); |
| 4688 | + } else { |
| 4689 | + /* This is a helper, we need to use the master's SIP stack */ |
| 4690 | + if(session->master == NULL || session->master->stack == NULL) { |
| 4691 | + error_code = JANUS_SIP_ERROR_HELPER_ERROR; |
| 4692 | + g_snprintf(error_cause, 512, "Invalid master SIP stack"); |
| 4693 | + goto error; |
| 4694 | + } |
| 4695 | + janus_mutex_lock(&session->master->stack->smutex); |
| 4696 | + if(session->master->stack->s_nua == NULL) { |
| 4697 | + janus_mutex_unlock(&session->master->stack->smutex); |
| 4698 | + JANUS_LOG(LOG_ERR, "NUA destroyed while sending message?\n"); |
| 4699 | + error_code = JANUS_SIP_ERROR_LIBSOFIA_ERROR; |
| 4700 | + g_snprintf(error_cause, 512, "Invalid NUA"); |
| 4701 | + goto error; |
| 4702 | + } |
| 4703 | + nh = nua_handle(session->master->stack->s_nua, session, TAG_END()); |
| 4704 | + janus_mutex_unlock(&session->master->stack->smutex); |
4657 | 4705 | }
|
4658 |
| - janus_mutex_unlock(&session->stack->smutex); |
4659 |
| - nua_message(session->stack->s_nh_m, |
| 4706 | + json_t *request_callid = json_object_get(root, "call_id"); |
| 4707 | + /* Use call-id from the request, if it exists */ |
| 4708 | + if(request_callid) { |
| 4709 | + message_callid = g_strdup(json_string_value(request_callid)); |
| 4710 | + } else { |
| 4711 | + /* If call-id does not exist in request, create a random one */ |
| 4712 | + message_callid = g_malloc0(24); |
| 4713 | + janus_sip_random_string(24, message_callid); |
| 4714 | + } |
| 4715 | + nua_message(nh, |
4660 | 4716 | SIPTAG_TO_STR(uri_text),
|
4661 | 4717 | SIPTAG_CONTENT_TYPE_STR(content_type),
|
4662 | 4718 | SIPTAG_PAYLOAD_STR(msg_content),
|
4663 | 4719 | NUTAG_PROXY(session->helper && session->master ?
|
4664 | 4720 | session->master->account.outbound_proxy : session->account.outbound_proxy),
|
4665 | 4721 | TAG_IF(strlen(custom_headers) > 0, SIPTAG_HEADER_STR(custom_headers)),
|
| 4722 | + SIPTAG_CALL_ID_STR(message_callid), |
4666 | 4723 | TAG_END());
|
4667 | 4724 | }
|
4668 |
| - /* Notify the operation */ |
| 4725 | + /* Notify the application */ |
4669 | 4726 | result = json_object();
|
4670 | 4727 | json_object_set_new(result, "event", json_string("messagesent"));
|
| 4728 | + json_object_set_new(result, "call_id", json_string(message_callid)); |
| 4729 | + /* Store message id and session */ |
| 4730 | + janus_mutex_lock(&sessions_mutex); |
| 4731 | + janus_refcount_increase(&session->ref); |
| 4732 | + g_hash_table_insert(messageids, g_strdup(message_callid), session); |
| 4733 | + janus_mutex_unlock(&sessions_mutex); |
| 4734 | + g_free(message_callid); |
4671 | 4735 | } else if(!strcasecmp(request_text, "dtmf_info")) {
|
4672 | 4736 | /* Send DMTF tones using SIP INFO
|
4673 | 4737 | * (https://tools.ietf.org/html/draft-kaplan-dispatch-info-dtmf-package-00)
|
@@ -5419,7 +5483,88 @@ void janus_sip_sofia_callback(nua_event_t event, int status, char const *phrase,
|
5419 | 5483 | break;
|
5420 | 5484 | case nua_r_message:
|
5421 | 5485 | JANUS_LOG(LOG_VERB, "[%s][%s]: %d %s\n", session->account.username, nua_event_name(event), status, phrase ? phrase : "??");
|
5422 |
| - /* FIXME Should we notify the user, in case the SIP MESSAGE returned an error? */ |
| 5486 | + /* Handle authetntication for SIP MESSAGE - eg. SippySoft Softswitch requires 401 authentication even if SIP user is registerered */ |
| 5487 | + if(status == 401 || status == 407) { |
| 5488 | + const char *scheme = NULL; |
| 5489 | + const char *realm = NULL; |
| 5490 | + if(status == 401) { |
| 5491 | + /* Get scheme/realm from 401 error */ |
| 5492 | + sip_www_authenticate_t const* www_auth = sip->sip_www_authenticate; |
| 5493 | + scheme = www_auth->au_scheme; |
| 5494 | + realm = msg_params_find(www_auth->au_params, "realm="); |
| 5495 | + } else { |
| 5496 | + /* Get scheme/realm from 407 error, proxy-auth */ |
| 5497 | + sip_proxy_authenticate_t const* proxy_auth = sip->sip_proxy_authenticate; |
| 5498 | + scheme = proxy_auth->au_scheme; |
| 5499 | + realm = msg_params_find(proxy_auth->au_params, "realm="); |
| 5500 | + } |
| 5501 | + char authuser[100], secret[100]; |
| 5502 | + memset(authuser, 0, sizeof(authuser)); |
| 5503 | + memset(secret, 0, sizeof(secret)); |
| 5504 | + if(session->helper) { |
| 5505 | + /* This is an helper session, we'll need the credentials from the master */ |
| 5506 | + if(session->master == NULL) { |
| 5507 | + JANUS_LOG(LOG_WARN, "No master session for this helper, authentication will fail...\n"); |
| 5508 | + } else { |
| 5509 | + session = session->master; |
| 5510 | + } |
| 5511 | + } |
| 5512 | + if(session->account.authuser && strchr(session->account.authuser, ':')) { |
| 5513 | + /* The authuser contains a colon: wrap it in quotes */ |
| 5514 | + g_snprintf(authuser, sizeof(authuser), "\"%s\"", session->account.authuser); |
| 5515 | + } else { |
| 5516 | + g_snprintf(authuser, sizeof(authuser), "%s", session->account.authuser); |
| 5517 | + } |
| 5518 | + if(session->account.secret && strchr(session->account.secret, ':')) { |
| 5519 | + /* The secret contains a colon: wrap it in quotes */ |
| 5520 | + g_snprintf(secret, sizeof(secret), "\"%s\"", session->account.secret); |
| 5521 | + } else { |
| 5522 | + g_snprintf(secret, sizeof(secret), "%s", session->account.secret); |
| 5523 | + } |
| 5524 | + char auth[256]; |
| 5525 | + memset(auth, 0, sizeof(auth)); |
| 5526 | + g_snprintf(auth, sizeof(auth), "%s%s:%s:%s:%s%s", |
| 5527 | + session->account.secret_type == janus_sip_secret_type_hashed ? "HA1+" : "", |
| 5528 | + scheme, |
| 5529 | + realm, |
| 5530 | + authuser, |
| 5531 | + session->account.secret_type == janus_sip_secret_type_hashed ? "HA1+" : "", |
| 5532 | + secret); |
| 5533 | + JANUS_LOG(LOG_VERB, "\t%s\n", auth); |
| 5534 | + /* Authenticate */ |
| 5535 | + nua_authenticate(nh, |
| 5536 | + NUTAG_AUTH(auth), |
| 5537 | + TAG_END()); |
| 5538 | + } else { |
| 5539 | + char *messageid = g_strdup(sip->sip_call_id->i_id); |
| 5540 | + /* Find session associated with the message */ |
| 5541 | + janus_mutex_lock(&sessions_mutex); |
| 5542 | + janus_sip_session *message_session = g_hash_table_lookup(messageids, messageid); |
| 5543 | + if (!message_session) { |
| 5544 | + message_session = session; |
| 5545 | + JANUS_LOG(LOG_VERB, "Message (%s) not associated with any session, event will be reported to master\n", messageid); |
| 5546 | + } |
| 5547 | + janus_mutex_unlock(&sessions_mutex); |
| 5548 | + /* MESSAGE response, notify the application */ |
| 5549 | + json_t *result = json_object(); |
| 5550 | + /* SIP code and reason */ |
| 5551 | + json_object_set_new(result, "event", json_string("messagedelivery")); |
| 5552 | + json_object_set_new(result, "code", json_integer(status)); |
| 5553 | + json_object_set_new(result, "reason", json_string(phrase)); |
| 5554 | + /* Build the delivery receipt */ |
| 5555 | + json_t *dr = json_object(); |
| 5556 | + json_object_set_new(dr, "sip", json_string("event")); |
| 5557 | + json_object_set_new(dr, "result", result); |
| 5558 | + json_object_set_new(dr, "call_id", json_string(messageid)); |
| 5559 | + /* Report delivery */ |
| 5560 | + int ret = gateway->push_event(message_session->handle, &janus_sip_plugin, message_session->transaction, dr, NULL); |
| 5561 | + JANUS_LOG(LOG_VERB, " >> Pushing event to peer: %d (%s)\n", ret, janus_get_api_error(ret)); |
| 5562 | + json_decref(dr); |
| 5563 | + janus_mutex_lock(&sessions_mutex); |
| 5564 | + g_hash_table_remove(messageids, messageid); |
| 5565 | + janus_mutex_unlock(&sessions_mutex); |
| 5566 | + g_free(messageid); |
| 5567 | + } |
5423 | 5568 | break;
|
5424 | 5569 | case nua_r_refer: {
|
5425 | 5570 | JANUS_LOG(LOG_VERB, "[%s][%s]: %d %s\n", session->account.username, nua_event_name(event), status, phrase ? phrase : "??");
|
|
0 commit comments