sippresence.cpp 8.78 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
/*
 *  Copyright (C) 2004-2013 Savoir-Faire Linux Inc.
 *
 *  Author: Patrick Keroulas  <patrick.keroulas@savoirfairelinux.com>
 *
 *  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 3 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *  Additional permission under GNU GPL version 3 section 7:
 *
 *  If you modify this program, or any covered work, by linking or
 *  combining it with the OpenSSL project's OpenSSL library (or a
 *  modified version of that library), containing parts covered by the
 *  terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
 *  grants you additional permission to convey the resulting work.
 *  Corresponding Source for a non-source form of such a combination
 *  shall include the source code for the parts of OpenSSL used as well
 *  as that of the covered work.
 */


#include "logger.h"
#include "manager.h"
#include "client/client.h"
#include "client/callmanager.h"
37
#include "client/presencemanager.h"
38 39 40
#include "sipaccount.h"
#include "sippublish.h"
#include "sippresence.h"
41 42
#include "pres_sub_server.h"
#include "pres_sub_client.h"
43 44
#include "sipvoiplink.h"

45 46
#define MAX_N_PRES_SUB_SERVER 20
#define MAX_N_PRES_SUB_CLIENT 20
47 48

SIPPresence::SIPPresence(SIPAccount *acc)
49 50
    : publish_sess()
    , pres_status_data_()
Tristan Matthews's avatar
Tristan Matthews committed
51
    , enabled_(true)
52
    , acc_(acc)
Tristan Matthews's avatar
Tristan Matthews committed
53 54
    , pres_sub_server_list_()  //IP2IP context
    , pres_sub_client_list_()
55 56 57 58
    , mutex_()
    , mutex_nesting_level_()
    , mutex_owner_()
    , cp_()
59
    , pool_()
60
{
61
    /* init default status */
Tristan Matthews's avatar
Tristan Matthews committed
62
    updateStatus(true, "Available");
63

64 65 66 67
    /* init pool */
    pj_caching_pool_init(&cp_, &pj_pool_factory_default_policy, 0);
    pool_ = pj_pool_create(&cp_.factory, "pres", 1000, 1000, NULL);

68
    /* Create mutex */
Tristan Matthews's avatar
Tristan Matthews committed
69 70
    if (pj_mutex_create_recursive(pool_, "pres", &mutex_) != PJ_SUCCESS)
        ERROR("Unable to create mutex");
71 72 73
}


Tristan Matthews's avatar
Tristan Matthews committed
74 75
SIPPresence::~SIPPresence()
{
76
    /* Flush the lists */
77
    for (const auto &c : pres_sub_client_list_)
78
        removePresSubClient(c) ;
Tristan Matthews's avatar
Tristan Matthews committed
79

80
    for (const auto &s : pres_sub_server_list_)
81
        removePresSubServer(s);
82
}
83

Tristan Matthews's avatar
Tristan Matthews committed
84 85
SIPAccount * SIPPresence::getAccount() const
{
86 87 88
    return acc_;
}

Tristan Matthews's avatar
Tristan Matthews committed
89 90
pjsip_pres_status * SIPPresence::getStatus()
{
91
    return &pres_status_data_;
92 93
}

Tristan Matthews's avatar
Tristan Matthews committed
94 95 96
int SIPPresence::getModId() const
{
    return ((SIPVoIPLink*)(acc_->getVoIPLink()))->getModId();
97 98
}

Tristan Matthews's avatar
Tristan Matthews committed
99 100
pj_pool_t*  SIPPresence::getPool() const
{
101 102 103
    return pool_;
}

104
void SIPPresence::enable(bool flag)
Tristan Matthews's avatar
Tristan Matthews committed
105 106
{
    enabled_ = flag;
107 108
}

109
void SIPPresence::updateStatus(bool status, const std::string &note)
Tristan Matthews's avatar
Tristan Matthews committed
110
{
111 112
    //char* pj_note  = (char*) pj_pool_alloc(pool_, "50");

113
    pjrpid_element rpid = {
Tristan Matthews's avatar
Tristan Matthews committed
114 115 116 117 118
        PJRPID_ELEMENT_TYPE_PERSON,
        pj_str("20"),
        PJRPID_ACTIVITY_UNKNOWN,
        pj_str((char *) note.c_str())
    };
119 120

    /* fill activity if user not available. */
Tristan Matthews's avatar
Tristan Matthews committed
121
    if (note == "away")
122
        rpid.activity = PJRPID_ACTIVITY_AWAY;
Tristan Matthews's avatar
Tristan Matthews committed
123
    else if (note == "busy")
124
        rpid.activity = PJRPID_ACTIVITY_BUSY;
125
    else // TODO: is there any other possibilities
126 127
        DEBUG("Presence : no activity");

128 129 130 131 132
    pj_bzero(&pres_status_data_, sizeof(pres_status_data_));
    pres_status_data_.info_cnt = 1;
    pres_status_data_.info[0].basic_open = status;
    pres_status_data_.info[0].id = pj_str("0"); /* todo: tuplie_id*/
    pj_memcpy(&pres_status_data_.info[0].rpid, &rpid, sizeof(pjrpid_element));
133 134 135
    /* "contact" field is optionnal */
}

136
void SIPPresence::sendPresence(bool status, const std::string &note)
Tristan Matthews's avatar
Tristan Matthews committed
137 138 139 140 141 142 143 144 145 146
{
    updateStatus(status, note);

    if (not enabled_)
        return;

    if (acc_->isIP2IP())
        notifyPresSubServer(); // to each subscribers
    else
        pres_publish(this); // to the PBX server
147 148 149
}


Tristan Matthews's avatar
Tristan Matthews committed
150 151
void SIPPresence::reportPresSubClientNotification(const std::string& uri, pjsip_pres_status * status)
{
152 153
    /* Update our info. See pjsua_buddy_get_info() for additionnal ideas*/
    const std::string basic(status->info[0].basic_open ? "open" : "closed");
Tristan Matthews's avatar
Tristan Matthews committed
154 155
    const std::string note(status->info[0].rpid.note.ptr, status->info[0].rpid.note.slen);
    DEBUG(" Received status of PresSubClient  %s: status=%s note=%s", uri.c_str(), (status->info[0].basic_open ? "open" : "closed"), note.c_str());
156
    /* report status to client signal */
157
    Manager::instance().getClient()->getPresenceManager()->newBuddySubscription(uri, status->info[0].basic_open, note);
158 159
}

160
void SIPPresence::subscribeClient(const std::string& uri, bool flag)
Tristan Matthews's avatar
Tristan Matthews committed
161
{
162
    /* Check if the buddy was already subscribed */
163
    for (const auto &c : pres_sub_client_list_)
Tristan Matthews's avatar
Tristan Matthews committed
164 165
        if (c->getURI() == uri) {
            DEBUG("-PresSubClient:%s exists in the list. Replace it.", uri.c_str());
166 167
            delete c;
            removePresSubClient(c);
168 169 170
            break;
        }

Tristan Matthews's avatar
Tristan Matthews committed
171
    if (pres_sub_client_list_.size() >= MAX_N_PRES_SUB_CLIENT) {
172 173 174 175
        WARN("Can't add PresSubClient, max number reached.");
        return;
    }

Tristan Matthews's avatar
Tristan Matthews committed
176 177 178 179
    if (flag) {
        PresSubClient *c = new PresSubClient(uri, this);

        if (!(c->subscribe())) {
180
            WARN("Failed send subscribe.");
181
            delete c;
182
        }
Tristan Matthews's avatar
Tristan Matthews committed
183

184 185
        // the buddy has to be accepted before being added in the list
    }
186 187
}

Tristan Matthews's avatar
Tristan Matthews committed
188 189 190
void SIPPresence::addPresSubClient(PresSubClient *c)
{
    if (pres_sub_client_list_.size() < MAX_N_PRES_SUB_CLIENT) {
191
        pres_sub_client_list_.push_back(c);
Tristan Matthews's avatar
Tristan Matthews committed
192 193
        DEBUG("New Presence_subscription_client client added in the list[l=%i].", pres_sub_client_list_.size());
    } else {
194
        WARN("Max Presence_subscription_client is reach.");
195
        // let the client alive //delete c;
196
    }
197
}
198

Tristan Matthews's avatar
Tristan Matthews committed
199 200
void SIPPresence::removePresSubClient(PresSubClient *c)
{
201
    DEBUG("Presence_subscription_client removed from the buddy list.");
202
    pres_sub_client_list_.remove(c);
203 204
}

205

Tristan Matthews's avatar
Tristan Matthews committed
206 207
void SIPPresence::reportnewServerSubscriptionRequest(PresSubServer *s)
{
208
    Manager::instance().getClient()->getPresenceManager()->newServerSubscriptionRequest(s->remote);
209 210
}

211
void SIPPresence::approvePresSubServer(const std::string& uri, bool flag)
Tristan Matthews's avatar
Tristan Matthews committed
212
{
213
    for (const auto &s : pres_sub_server_list_)
Tristan Matthews's avatar
Tristan Matthews committed
214 215 216 217 218
        if (s->matches((char *) uri.c_str())) {
            DEBUG("Approve Presence_subscription_server for %s: %s.", s->remote, flag ? "true" : "false");
            s->approve(flag);
            // return; // 'return' would prevent multiple-time subscribers from spam
        }
219 220 221
}


Tristan Matthews's avatar
Tristan Matthews committed
222 223 224 225
void SIPPresence::addPresSubServer(PresSubServer *s)
{
    if (pres_sub_server_list_.size() < MAX_N_PRES_SUB_SERVER) {
        DEBUG("Presence_subscription_server added: %s.", s->remote);
226
        pres_sub_server_list_.push_back(s);
Tristan Matthews's avatar
Tristan Matthews committed
227
    } else {
228
        WARN("Max Presence_subscription_server is reach.");
229
        // let de server alive // delete s;
230
    }
231 232
}

Tristan Matthews's avatar
Tristan Matthews committed
233 234
void SIPPresence::removePresSubServer(PresSubServer *s)
{
235
    pres_sub_server_list_.remove(s);
236
    DEBUG("Presence_subscription_server removed");
237 238
}

Tristan Matthews's avatar
Tristan Matthews committed
239 240
void SIPPresence::notifyPresSubServer()
{
241
    DEBUG("Iterating through Presence_subscription_server:");
Tristan Matthews's avatar
Tristan Matthews committed
242

243
    for (const auto &s : pres_sub_server_list_)
244
        s->notify();
245
}
246 247 248 249 250 251 252 253 254 255 256

void SIPPresence::lock()
{
    pj_mutex_lock(mutex_);
    mutex_owner_ = pj_thread_this();
    ++mutex_nesting_level_;
}

void SIPPresence::unlock()
{
    if (--mutex_nesting_level_ == 0)
Tristan Matthews's avatar
Tristan Matthews committed
257 258
        mutex_owner_ = NULL;

259 260 261
    pj_mutex_unlock(mutex_);
}

262 263 264 265 266 267 268 269 270 271 272
void SIPPresence::fillDoc(pjsip_tx_data *tdata, const pres_msg_data *msg_data)
{

    if (tdata->msg->type == PJSIP_REQUEST_MSG) {
        const pj_str_t STR_USER_AGENT = pj_str("User-Agent");
        std::string useragent(acc_->getUserAgentName());
        pj_str_t pJuseragent = pj_str((char*) useragent.c_str());
        pjsip_hdr *h = (pjsip_hdr*) pjsip_generic_string_hdr_create(tdata->pool, &STR_USER_AGENT, &pJuseragent);
        pjsip_msg_add_hdr(tdata->msg, h);
    }

Tristan Matthews's avatar
Tristan Matthews committed
273
    if (msg_data == NULL)
274 275 276 277
        return;

    const pjsip_hdr *hdr;
    hdr = msg_data->hdr_list.next;
Tristan Matthews's avatar
Tristan Matthews committed
278

279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
    while (hdr && hdr != &msg_data->hdr_list) {
        pjsip_hdr *new_hdr;
        new_hdr = (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, hdr);
        DEBUG("adding header", new_hdr->name.ptr);
        pjsip_msg_add_hdr(tdata->msg, new_hdr);
        hdr = hdr->next;
    }

    if (msg_data->content_type.slen && msg_data->msg_body.slen) {
        pjsip_msg_body *body;
        pj_str_t type = pj_str("application");
        pj_str_t subtype = pj_str("pidf+xml");
        body = pjsip_msg_body_create(tdata->pool, &type, &subtype, &msg_data->msg_body);
        tdata->msg->body = body;
    }
}