2
* Copyright (C) 2012, 2013 LOTES TM LLC
3
* Author : Andrey Loukhnov <aol.nnov@gmail.com>
5
* This file is a part of pult5-voip
7
* pult5-voip is free software; you can redistribute it and/or modify
8
* it under the terms of the GNU General Public License as published by
9
* the Free Software Foundation; either version 3 of the License, or
10
* (at your option) any later version.
12
* pult5-voip is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
17
* You should have received a copy of the GNU General Public License
18
* along with this programm. If not, see <http://www.gnu.org/licenses/>.
20
* Additional permission under GNU GPL version 3 section 7:
22
* If you modify pult5-voip, or any covered work, by linking or
23
* combining it with the OpenSSL project's OpenSSL library (or a
24
* modified version of that library), containing parts covered by the
25
* terms of the OpenSSL or SSLeay licenses, LOTES-TM LLC
26
* grants you additional permission to convey the resulting work.
27
* Corresponding Source for a non-source form of such a combination
28
* shall include the source code for the parts of OpenSSL used as well
29
* as that of the covered work.
34
#include <pjsip/sip_module.h>
35
#include <pjsip/sip_types.h>
36
#include <pjsip/sip_event.h>
37
#include <pjsip/sip_transaction.h>
38
#include <pjsip/sip_dialog.h>
39
#include <pjsip/sip_endpoint.h>
43
#include <pjsip/sip_ua_layer.h>
44
#include <pjsip-simple/evsub.h>
47
#include "array_size.h"
48
#include "pres_sub_client.h"
49
#include "client/presencemanager.h"
50
#include "client/configurationmanager.h"
51
#include "sipaccount.h"
52
#include "sippresence.h"
53
#include "sipvoiplink.h"
54
#include "sip_utils.h"
59
#define PRES_TIMER 300 // 5min
61
int PresSubClient::modId_ = 0; // used to extract data structure from event_subscription
64
PresSubClient::pres_client_timer_cb(pj_timer_heap_t * /*th*/, pj_timer_entry *entry)
66
PresSubClient *c = (PresSubClient *) entry->user_data;
67
DEBUG("timeout for %s", c->getURI().c_str());
70
/* Callback called when *client* subscription state has changed. */
72
PresSubClient::pres_client_evsub_on_state(pjsip_evsub *sub, pjsip_event *event)
76
PresSubClient *pres_client = (PresSubClient *) pjsip_evsub_get_mod_data(sub, modId_);
77
/* No need to pres->lock() here since the client has a locked dialog*/
80
WARN("pres_client not found");
84
DEBUG("Subscription for pres_client '%s' is '%s'", pres_client->getURI().c_str(),
85
pjsip_evsub_get_state_name(sub) ? pjsip_evsub_get_state_name(sub) : "null");
87
pjsip_evsub_state state = pjsip_evsub_get_state(sub);
89
SIPPresence * pres = pres_client->getPresence();
91
if (state == PJSIP_EVSUB_STATE_ACCEPTED) {
92
pres_client->enable(true);
93
Manager::instance().getClient()->getPresenceManager()->subscriptionStateChanged(
94
pres->getAccount()->getAccountID(),
95
pres_client->getURI().c_str(),
98
pres->getAccount()->supportPresence(PRESENCE_FUNCTION_SUBSCRIBE, true);
100
} else if (state == PJSIP_EVSUB_STATE_TERMINATED) {
101
int resub_delay = -1;
102
pj_strdup_with_null(pres_client->pool_, &pres_client->term_reason_, pjsip_evsub_get_termination_reason(sub));
104
Manager::instance().getClient()->getPresenceManager()->subscriptionStateChanged(
105
pres->getAccount()->getAccountID(),
106
pres_client->getURI().c_str(),
109
pres_client->term_code_ = 200;
111
/* Determine whether to resubscribe automatically */
112
if (event && event->type == PJSIP_EVENT_TSX_STATE) {
113
const pjsip_transaction *tsx = event->body.tsx_state.tsx;
115
if (pjsip_method_cmp(&tsx->method, &pjsip_subscribe_method) == 0) {
116
pres_client->term_code_ = tsx->status_code;
117
std::ostringstream os;
118
os << pres_client->term_code_;
119
const std::string error = os.str() + "/" +
120
std::string(pres_client->term_reason_.ptr,
121
pres_client->term_reason_.slen);
124
bool subscribe_allowed = PJ_FALSE;
126
switch (tsx->status_code) {
127
case PJSIP_SC_CALL_TSX_DOES_NOT_EXIST:
128
/* 481: we refreshed too late? resubscribe
131
/* But this must only happen when the 481 is received
132
* on subscription refresh request. We MUST NOT try to
133
* resubscribe automatically if the 481 is received
134
* on the initial SUBSCRIBE (if server returns this
135
* response for some reason).
137
if (pres_client->dlg_->remote.contact)
139
msg = "Bad subscribe refresh.";
140
subscribe_allowed = PJ_TRUE;
143
case PJSIP_SC_NOT_FOUND:
144
msg = "Subscribe context not set for this buddy.";
145
subscribe_allowed = PJ_TRUE;
148
case PJSIP_SC_FORBIDDEN:
149
msg = "Subscribe not allowed for this buddy.";
150
subscribe_allowed = PJ_TRUE;
153
case PJSIP_SC_PRECONDITION_FAILURE:
154
msg = "Wrong server.";
159
* 1) send a signal through DBus
160
* 2) change the support field in the account schema if the pres_sub's server
161
* is the same as the account's server
163
Manager::instance().getClient()->getPresenceManager()->serverError(
164
pres_client->getPresence()->getAccount()->getAccountID(),
168
std::string account_host = std::string(pj_gethostname()->ptr, pj_gethostname()->slen);
169
std::string sub_host = sip_utils::getHostFromUri(pres_client->getURI());
171
if((!subscribe_allowed) && (account_host == sub_host))
172
pres_client->getPresence()->getAccount()->supportPresence(PRESENCE_FUNCTION_SUBSCRIBE, false);
174
} else if (pjsip_method_cmp(&tsx->method, &pjsip_notify_method) == 0) {
175
if (pres_client->isTermReason("deactivated") || pres_client->isTermReason("timeout")) {
176
/* deactivated: The subscription has been terminated,
177
* but the subscriber SHOULD retry immediately with
178
* a new subscription.
180
/* timeout: The subscription has been terminated
181
* because it was not refreshed before it expired.
182
* Clients MAY re-subscribe immediately. The
183
* "retry-after" parameter has no semantics for
187
} else if (pres_client->isTermReason("probation") || pres_client->isTermReason("giveup")) {
188
/* probation: The subscription has been terminated,
189
* but the client SHOULD retry at some later time.
190
* If a "retry-after" parameter is also present, the
191
* client SHOULD wait at least the number of seconds
192
* specified by that parameter before attempting to re-
195
/* giveup: The subscription has been terminated because
196
* the notifier could not obtain authorization in a
197
* timely fashion. If a "retry-after" parameter is
198
* also present, the client SHOULD wait at least the
199
* number of seconds specified by that parameter before
200
* attempting to re-subscribe; otherwise, the client
201
* MAY retry immediately, but will likely get put back
202
* into pending state.
204
const pjsip_sub_state_hdr *sub_hdr;
205
pj_str_t sub_state = CONST_PJ_STR("Subscription-State");
206
const pjsip_msg *msg;
208
msg = event->body.tsx_state.src.rdata->msg_info.msg;
209
sub_hdr = (const pjsip_sub_state_hdr*) pjsip_msg_find_hdr_by_name(msg, &sub_state, NULL);
211
if (sub_hdr && sub_hdr->retry_after > 0)
212
resub_delay = sub_hdr->retry_after * 1000;
218
/* For other cases of subscription termination, if resubscribe
219
* timer is not set, schedule with default expiration (plus minus
220
* some random value, to avoid sending SUBSCRIBEs all at once)
222
if (resub_delay == -1) {
223
resub_delay = PRES_TIMER * 1000;
226
pres_client->sub_ = sub;
227
pres_client->rescheduleTimer(PJ_TRUE, resub_delay);
229
} else { //state==ACTIVE ......
230
//This will clear the last termination code/reason
231
pres_client->term_code_ = 0;
232
pres_client->term_reason_.ptr = NULL;
235
/* Clear subscription */
236
if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
237
pjsip_evsub_terminate(pres_client->sub_, PJ_FALSE); // = NULL;
238
pres_client->status_.info_cnt = 0;
239
pres_client->dlg_ = NULL;
240
pres_client->rescheduleTimer(PJ_FALSE, 0);
241
pjsip_evsub_set_mod_data(sub, modId_, NULL);
243
pres_client->enable(false);
247
/* Callback when transaction state has changed. */
249
PresSubClient::pres_client_evsub_on_tsx_state(pjsip_evsub *sub, pjsip_transaction *tsx, pjsip_event *event)
252
PresSubClient *pres_client;
253
pjsip_contact_hdr *contact_hdr;
255
pres_client = (PresSubClient *) pjsip_evsub_get_mod_data(sub, modId_);
256
/* No need to pres->lock() here since the client has a locked dialog*/
259
WARN("Couldn't find pres_client.");
263
/* We only use this to update pres_client's Contact, when it's not
266
if (pres_client->contact_.slen != 0) {
267
/* Contact already set */
271
/* Only care about 2xx response to outgoing SUBSCRIBE */
272
if (tsx->status_code / 100 != 2 || tsx->role != PJSIP_UAC_ROLE || event->type != PJSIP_EVENT_RX_MSG
273
|| pjsip_method_cmp(&tsx->method, pjsip_get_subscribe_method()) != 0) {
277
/* Find contact header. */
278
contact_hdr = (pjsip_contact_hdr*) pjsip_msg_find_hdr(event->body.rx_msg.rdata->msg_info.msg, PJSIP_H_CONTACT,
281
if (!contact_hdr || !contact_hdr->uri) {
285
pres_client->contact_.ptr = (char*) pj_pool_alloc(pres_client->pool_, PJSIP_MAX_URL_SIZE);
286
pres_client->contact_.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, contact_hdr->uri, pres_client->contact_.ptr,
289
if (pres_client->contact_.slen < 0)
290
pres_client->contact_.slen = 0;
294
/* Callback called when we receive NOTIFY */
296
PresSubClient::pres_client_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)
299
PresSubClient *pres_client = (PresSubClient *) pjsip_evsub_get_mod_data(sub, modId_);
302
WARN("Couldn't find pres_client from ev_sub.");
305
/* No need to pres->lock() here since the client has a locked dialog*/
307
pjsip_pres_get_status(sub, &pres_client->status_);
308
pres_client->reportPresence();
310
/* The default is to send 200 response to NOTIFY.
311
* Just leave it there..
313
PJ_UNUSED_ARG(rdata);
314
PJ_UNUSED_ARG(p_st_code);
315
PJ_UNUSED_ARG(p_st_text);
316
PJ_UNUSED_ARG(res_hdr);
317
PJ_UNUSED_ARG(p_body);
320
PresSubClient::PresSubClient(const std::string& uri, SIPPresence *pres) :
339
pj_caching_pool_init(&cp_, &pj_pool_factory_default_policy, 0);
340
pool_ = pj_pool_create(&cp_.factory, "Pres_sub_client", 512, 512, NULL);
341
uri_ = pj_strdup3(pool_, uri.c_str());
342
contact_ = pj_strdup3(pool_, pres_->getAccount()->getFromUri().c_str());
345
PresSubClient::~PresSubClient()
347
DEBUG("Destroying pres_client object with uri %.*s", uri_.slen, uri_.ptr);
348
rescheduleTimer(PJ_FALSE, 0);
350
pj_pool_release(pool_);
353
bool PresSubClient::isSubscribed()
358
std::string PresSubClient::getURI()
360
std::string res(uri_.ptr, uri_.slen);
364
SIPPresence * PresSubClient::getPresence()
369
bool PresSubClient::isPresent()
371
return status_.info[0].basic_open;
374
std::string PresSubClient::getLineStatus()
376
return std::string(status_.info[0].rpid.note.ptr, status_.info[0].rpid.note.slen);
379
bool PresSubClient::isTermReason(const std::string &reason)
381
const std::string myReason(term_reason_.ptr, term_reason_.slen);
382
return not myReason.compare(reason);
385
void PresSubClient::rescheduleTimer(bool reschedule, unsigned msec)
387
SIPAccount * acc = pres_->getAccount();
390
pjsip_endpt_cancel_timer(((SIPVoIPLink*) acc->getVoIPLink())->getEndpoint(), &timer_);
391
timer_.id = PJ_FALSE;
397
WARN("pres_client %.*s will resubscribe in %u ms (reason: %.*s)",
398
uri_.slen, uri_.ptr, msec, (int) term_reason_.slen, term_reason_.ptr);
399
pj_timer_entry_init(&timer_, 0, this, &pres_client_timer_cb);
402
pj_time_val_normalize(&delay);
404
if (pjsip_endpt_schedule_timer(((SIPVoIPLink*) acc->getVoIPLink())->getEndpoint(), &timer_, &delay) == PJ_SUCCESS) {
410
void PresSubClient::enable(bool flag)
412
DEBUG("pres_client %s is %s monitored.",getURI().c_str(), flag? "":"NOT");
413
if(flag && !monitored_)
414
pres_->addPresSubClient(this);
418
void PresSubClient::reportPresence()
421
pres_->reportPresSubClientNotification(getURI(), &status_);
424
bool PresSubClient::lock()
430
if(!(pres_->tryLock())){
431
pj_thread_sleep(i/10);
434
lock_flag_ = PRESENCE_LOCK_FLAG;
439
if (pjsip_dlg_try_inc_lock(dlg_) != PJ_SUCCESS) {
442
pj_thread_sleep(i/10);
446
lock_flag_ = PRESENCE_CLIENT_LOCK_FLAG;
452
DEBUG("pres_client failed to lock : timeout");
458
void PresSubClient::unlock()
460
if (lock_flag_ & PRESENCE_CLIENT_LOCK_FLAG)
461
pjsip_dlg_dec_lock(dlg_);
463
if (lock_flag_ & PRESENCE_LOCK_FLAG)
467
bool PresSubClient::unsubscribe()
474
pjsip_tx_data *tdata;
475
pj_status_t retStatus;
477
if (sub_ == NULL or dlg_ == NULL) {
478
WARN("PresSubClient already unsubscribed.");
483
if (pjsip_evsub_get_state(sub_) == PJSIP_EVSUB_STATE_TERMINATED) {
484
WARN("pres_client already unsubscribed sub=TERMINATED.");
490
/* Unsubscribe means send a subscribe with timeout=0s*/
491
WARN("pres_client %.*s: unsubscribing..", uri_.slen, uri_.ptr);
492
retStatus = pjsip_pres_initiate(sub_, 0, &tdata);
494
if (retStatus == PJ_SUCCESS) {
495
pres_->fillDoc(tdata, NULL);
496
retStatus = pjsip_pres_send_request(sub_, tdata);
499
if (retStatus != PJ_SUCCESS and sub_) {
500
pjsip_pres_terminate(sub_, PJ_FALSE);
502
WARN("Unable to unsubscribe presence", retStatus);
507
//pjsip_evsub_set_mod_data(sub_, modId_, NULL); // Not interested with further events
514
bool PresSubClient::subscribe()
517
if (sub_ and dlg_) { //do not bother if already subscribed
518
pjsip_evsub_terminate(sub_, PJ_FALSE);
519
DEBUG("PreseSubClient %.*s: already subscribed. Refresh it.", uri_.slen, uri_.ptr);
523
pjsip_evsub_user pres_callback;
524
pjsip_tx_data *tdata;
527
/* Event subscription callback. */
528
pj_bzero(&pres_callback, sizeof(pres_callback));
529
pres_callback.on_evsub_state = &pres_client_evsub_on_state;
530
pres_callback.on_tsx_state = &pres_client_evsub_on_tsx_state;
531
pres_callback.on_rx_notify = &pres_client_evsub_on_rx_notify;
533
SIPAccount * acc = pres_->getAccount();
534
DEBUG("PresSubClient %.*s: subscribing ", uri_.slen, uri_.ptr);
537
/* Create UAC dialog */
538
pj_str_t from = pj_strdup3(pool_, acc->getFromUri().c_str());
539
status = pjsip_dlg_create_uac(pjsip_ua_instance(), &from, &contact_, &uri_, NULL, &dlg_);
541
if (status != PJ_SUCCESS) {
542
ERROR("Unable to create dialog \n");
546
/* Add credential for auth. */
547
if (acc->hasCredentials() and pjsip_auth_clt_set_credentials(&dlg_->auth_sess, acc->getCredentialCount(), acc->getCredInfo()) != PJ_SUCCESS) {
548
ERROR("Could not initialize credentials for subscribe session authentication");
551
/* Increment the dialog's lock otherwise when presence session creation
552
* fails the dialog will be destroyed prematurely.
554
pjsip_dlg_inc_lock(dlg_);
556
status = pjsip_pres_create_uac(dlg_, &pres_callback, PJSIP_EVSUB_NO_EVENT_ID, &sub_);
558
if (status != PJ_SUCCESS) {
560
WARN("Unable to create presence client", status);
562
/* This should destroy the dialog since there's no session
566
pjsip_dlg_dec_lock(dlg_);
572
/* Add credential for authentication */
573
if (acc->hasCredentials() and pjsip_auth_clt_set_credentials(&dlg_->auth_sess, acc->getCredentialCount(), acc->getCredInfo()) != PJ_SUCCESS) {
574
ERROR("Could not initialize credentials for invite session authentication");
579
pjsip_regc *regc = acc->getRegistrationInfo();
580
if (regc and acc->hasServiceRoute())
581
pjsip_regc_set_route_set(regc, sip_utils::createRouteSet(acc->getServiceRoute(), pres_->getPool()));
583
// attach the client data to the sub
584
pjsip_evsub_set_mod_data(sub_, modId_, this);
586
status = pjsip_pres_initiate(sub_, -1, &tdata);
587
if (status != PJ_SUCCESS) {
589
pjsip_dlg_dec_lock(dlg_);
591
pjsip_pres_terminate(sub_, PJ_FALSE);
593
WARN("Unable to create initial SUBSCRIBE", status);
597
// pjsua_process_msg_data(tdata, NULL);
599
status = pjsip_pres_send_request(sub_, tdata);
601
if (status != PJ_SUCCESS) {
603
pjsip_dlg_dec_lock(dlg_);
605
pjsip_pres_terminate(sub_, PJ_FALSE);
607
WARN("Unable to send initial SUBSCRIBE", status);
611
pjsip_dlg_dec_lock(dlg_);
615
bool PresSubClient::match(PresSubClient *b)
617
return (b->getURI() == getURI());