~ubuntu-branches/debian/sid/sflphone/sid

« back to all changes in this revision

Viewing changes to daemon/libs/pjproject-2.0.1/pjsip/src/pjsip-simple/presence.c

  • Committer: Package Import Robot
  • Author(s): Mark Purcell
  • Date: 2014-01-28 18:23:36 UTC
  • mfrom: (1.1.11)
  • Revision ID: package-import@ubuntu.com-20140128182336-3xenud1kbnwmf3mz
Tags: 1.3.0-1
* New upstream release 
  - Fixes "New Upstream Release" (Closes: #735846)
  - Fixes "Ringtone does not stop" (Closes: #727164)
  - Fixes "[sflphone-kde] crash on startup" (Closes: #718178)
  - Fixes "sflphone GUI crashes when call is hung up" (Closes: #736583)
* Build-Depends: ensure GnuTLS 2.6
  - libucommon-dev (>= 6.0.7-1.1), libccrtp-dev (>= 2.0.6-3)
  - Fixes "FTBFS Build-Depends libgnutls{26,28}-dev" (Closes: #722040)
* Fix "boost 1.49 is going away" unversioned Build-Depends: (Closes: #736746)
* Add Build-Depends: libsndfile-dev, nepomuk-core-dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* $Id: presence.c 3553 2011-05-05 06:14:19Z nanang $ */
2
 
/*
3
 
 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4
 
 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5
 
 *
6
 
 * This program is free software; you can redistribute it and/or modify
7
 
 * it under the terms of the GNU General Public License as published by
8
 
 * the Free Software Foundation; either version 2 of the License, or
9
 
 * (at your option) any later version.
10
 
 *
11
 
 * This program is distributed in the hope that it will be useful,
12
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 
 * GNU General Public License for more details.
15
 
 *
16
 
 * You should have received a copy of the GNU General Public License
17
 
 * along with this program; if not, write to the Free Software
18
 
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
 
 */
20
 
#include <pjsip-simple/presence.h>
21
 
#include <pjsip-simple/errno.h>
22
 
#include <pjsip-simple/evsub_msg.h>
23
 
#include <pjsip/sip_module.h>
24
 
#include <pjsip/sip_multipart.h>
25
 
#include <pjsip/sip_endpoint.h>
26
 
#include <pjsip/sip_dialog.h>
27
 
#include <pj/assert.h>
28
 
#include <pj/guid.h>
29
 
#include <pj/log.h>
30
 
#include <pj/os.h>
31
 
#include <pj/pool.h>
32
 
#include <pj/string.h>
33
 
 
34
 
 
35
 
#define THIS_FILE                   "presence.c"
36
 
#define PRES_DEFAULT_EXPIRES        PJSIP_PRES_DEFAULT_EXPIRES
37
 
 
38
 
#if PJSIP_PRES_BAD_CONTENT_RESPONSE < 200 || \
39
 
    PJSIP_PRES_BAD_CONTENT_RESPONSE > 699 || \
40
 
    PJSIP_PRES_BAD_CONTENT_RESPONSE/100 == 3
41
 
# error Invalid PJSIP_PRES_BAD_CONTENT_RESPONSE value
42
 
#endif
43
 
 
44
 
/*
45
 
 * Presence module (mod-presence)
46
 
 */
47
 
static struct pjsip_module mod_presence =
48
 
{
49
 
    NULL, NULL,                     /* prev, next.                      */
50
 
    { "mod-presence", 12 },         /* Name.                            */
51
 
    -1,                             /* Id                               */
52
 
    PJSIP_MOD_PRIORITY_DIALOG_USAGE,/* Priority                         */
53
 
    NULL,                           /* load()                           */
54
 
    NULL,                           /* start()                          */
55
 
    NULL,                           /* stop()                           */
56
 
    NULL,                           /* unload()                         */
57
 
    NULL,                           /* on_rx_request()                  */
58
 
    NULL,                           /* on_rx_response()                 */
59
 
    NULL,                           /* on_tx_request.                   */
60
 
    NULL,                           /* on_tx_response()                 */
61
 
    NULL,                           /* on_tsx_state()                   */
62
 
};
63
 
 
64
 
 
65
 
/*
66
 
 * Presence message body type.
67
 
 */
68
 
typedef enum content_type_e
69
 
{
70
 
    CONTENT_TYPE_NONE,
71
 
    CONTENT_TYPE_PIDF,
72
 
    CONTENT_TYPE_XPIDF,
73
 
} content_type_e;
74
 
 
75
 
/*
76
 
 * This structure describe a presentity, for both subscriber and notifier.
77
 
 */
78
 
struct pjsip_pres
79
 
{
80
 
    pjsip_evsub         *sub;           /**< Event subscribtion record.     */
81
 
    pjsip_dialog        *dlg;           /**< The dialog.                    */
82
 
    content_type_e       content_type;  /**< Content-Type.                  */
83
 
    pj_pool_t           *status_pool;   /**< Pool for pres_status           */
84
 
    pjsip_pres_status    status;        /**< Presence status.               */
85
 
    pj_pool_t           *tmp_pool;      /**< Pool for tmp_status            */
86
 
    pjsip_pres_status    tmp_status;    /**< Temp, before NOTIFY is answred.*/
87
 
    pjsip_evsub_user     user_cb;       /**< The user callback.             */
88
 
};
89
 
 
90
 
 
91
 
typedef struct pjsip_pres pjsip_pres;
92
 
 
93
 
 
94
 
/*
95
 
 * Forward decl for evsub callback.
96
 
 */
97
 
static void pres_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
98
 
static void pres_on_evsub_tsx_state( pjsip_evsub *sub, pjsip_transaction *tsx,
99
 
                                     pjsip_event *event);
100
 
static void pres_on_evsub_rx_refresh( pjsip_evsub *sub,
101
 
                                      pjsip_rx_data *rdata,
102
 
                                      int *p_st_code,
103
 
                                      pj_str_t **p_st_text,
104
 
                                      pjsip_hdr *res_hdr,
105
 
                                      pjsip_msg_body **p_body);
106
 
static void pres_on_evsub_rx_notify( pjsip_evsub *sub,
107
 
                                     pjsip_rx_data *rdata,
108
 
                                     int *p_st_code,
109
 
                                     pj_str_t **p_st_text,
110
 
                                     pjsip_hdr *res_hdr,
111
 
                                     pjsip_msg_body **p_body);
112
 
static void pres_on_evsub_client_refresh(pjsip_evsub *sub);
113
 
static void pres_on_evsub_server_timeout(pjsip_evsub *sub);
114
 
 
115
 
 
116
 
/*
117
 
 * Event subscription callback for presence.
118
 
 */
119
 
static pjsip_evsub_user pres_user =
120
 
{
121
 
    &pres_on_evsub_state,
122
 
    &pres_on_evsub_tsx_state,
123
 
    &pres_on_evsub_rx_refresh,
124
 
    &pres_on_evsub_rx_notify,
125
 
    &pres_on_evsub_client_refresh,
126
 
    &pres_on_evsub_server_timeout,
127
 
};
128
 
 
129
 
 
130
 
/*
131
 
 * Some static constants.
132
 
 */
133
 
const pj_str_t STR_EVENT            = { "Event", 5 };
134
 
const pj_str_t STR_PRESENCE         = { "presence", 8 };
135
 
const pj_str_t STR_APPLICATION      = { "application", 11 };
136
 
const pj_str_t STR_PIDF_XML         = { "pidf+xml", 8};
137
 
const pj_str_t STR_XPIDF_XML        = { "xpidf+xml", 9};
138
 
const pj_str_t STR_APP_PIDF_XML     = { "application/pidf+xml", 20 };
139
 
const pj_str_t STR_APP_XPIDF_XML    = { "application/xpidf+xml", 21 };
140
 
 
141
 
 
142
 
/*
143
 
 * Init presence module.
144
 
 */
145
 
PJ_DEF(pj_status_t) pjsip_pres_init_module( pjsip_endpoint *endpt,
146
 
                                            pjsip_module *mod_evsub)
147
 
{
148
 
    pj_status_t status;
149
 
    pj_str_t accept[2];
150
 
 
151
 
    /* Check arguments. */
152
 
    PJ_ASSERT_RETURN(endpt && mod_evsub, PJ_EINVAL);
153
 
 
154
 
    /* Must have not been registered */
155
 
    PJ_ASSERT_RETURN(mod_presence.id == -1, PJ_EINVALIDOP);
156
 
 
157
 
    /* Register to endpoint */
158
 
    status = pjsip_endpt_register_module(endpt, &mod_presence);
159
 
    if (status != PJ_SUCCESS)
160
 
        return status;
161
 
 
162
 
    accept[0] = STR_APP_PIDF_XML;
163
 
    accept[1] = STR_APP_XPIDF_XML;
164
 
 
165
 
    /* Register event package to event module. */
166
 
    status = pjsip_evsub_register_pkg( &mod_presence, &STR_PRESENCE,
167
 
                                       PRES_DEFAULT_EXPIRES,
168
 
                                       PJ_ARRAY_SIZE(accept), accept);
169
 
    if (status != PJ_SUCCESS) {
170
 
        pjsip_endpt_unregister_module(endpt, &mod_presence);
171
 
        return status;
172
 
    }
173
 
 
174
 
    return PJ_SUCCESS;
175
 
}
176
 
 
177
 
 
178
 
/*
179
 
 * Get presence module instance.
180
 
 */
181
 
PJ_DEF(pjsip_module*) pjsip_pres_instance(void)
182
 
{
183
 
    return &mod_presence;
184
 
}
185
 
 
186
 
 
187
 
/*
188
 
 * Create client subscription.
189
 
 */
190
 
PJ_DEF(pj_status_t) pjsip_pres_create_uac( pjsip_dialog *dlg,
191
 
                                           const pjsip_evsub_user *user_cb,
192
 
                                           unsigned options,
193
 
                                           pjsip_evsub **p_evsub )
194
 
{
195
 
    pj_status_t status;
196
 
    pjsip_pres *pres;
197
 
    char obj_name[PJ_MAX_OBJ_NAME];
198
 
    pjsip_evsub *sub;
199
 
 
200
 
    PJ_ASSERT_RETURN(dlg && p_evsub, PJ_EINVAL);
201
 
 
202
 
    pjsip_dlg_inc_lock(dlg);
203
 
 
204
 
    /* Create event subscription */
205
 
    status = pjsip_evsub_create_uac( dlg,  &pres_user, &STR_PRESENCE,
206
 
                                     options, &sub);
207
 
    if (status != PJ_SUCCESS)
208
 
        goto on_return;
209
 
 
210
 
    /* Create presence */
211
 
    pres = PJ_POOL_ZALLOC_T(dlg->pool, pjsip_pres);
212
 
    pres->dlg = dlg;
213
 
    pres->sub = sub;
214
 
    if (user_cb)
215
 
        pj_memcpy(&pres->user_cb, user_cb, sizeof(pjsip_evsub_user));
216
 
 
217
 
    pj_ansi_snprintf(obj_name, PJ_MAX_OBJ_NAME, "pres%p", dlg->pool);
218
 
    pres->status_pool = pj_pool_create(dlg->pool->factory, obj_name,
219
 
                                       512, 512, NULL);
220
 
    pj_ansi_snprintf(obj_name, PJ_MAX_OBJ_NAME, "tmpres%p", dlg->pool);
221
 
    pres->tmp_pool = pj_pool_create(dlg->pool->factory, obj_name,
222
 
                                    512, 512, NULL);
223
 
 
224
 
    /* Attach to evsub */
225
 
    pjsip_evsub_set_mod_data(sub, mod_presence.id, pres);
226
 
 
227
 
    *p_evsub = sub;
228
 
 
229
 
on_return:
230
 
    pjsip_dlg_dec_lock(dlg);
231
 
    return status;
232
 
}
233
 
 
234
 
 
235
 
/*
236
 
 * Create server subscription.
237
 
 */
238
 
PJ_DEF(pj_status_t) pjsip_pres_create_uas( pjsip_dialog *dlg,
239
 
                                           const pjsip_evsub_user *user_cb,
240
 
                                           pjsip_rx_data *rdata,
241
 
                                           pjsip_evsub **p_evsub )
242
 
{
243
 
    pjsip_accept_hdr *accept;
244
 
    pjsip_event_hdr *event;
245
 
    content_type_e content_type = CONTENT_TYPE_NONE;
246
 
    pjsip_evsub *sub;
247
 
    pjsip_pres *pres;
248
 
    char obj_name[PJ_MAX_OBJ_NAME];
249
 
    pj_status_t status;
250
 
 
251
 
    /* Check arguments */
252
 
    PJ_ASSERT_RETURN(dlg && rdata && p_evsub, PJ_EINVAL);
253
 
 
254
 
    /* Must be request message */
255
 
    PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG,
256
 
                     PJSIP_ENOTREQUESTMSG);
257
 
 
258
 
    /* Check that request is SUBSCRIBE */
259
 
    PJ_ASSERT_RETURN(pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
260
 
                                      &pjsip_subscribe_method)==0,
261
 
                     PJSIP_SIMPLE_ENOTSUBSCRIBE);
262
 
 
263
 
    /* Check that Event header contains "presence" */
264
 
    event = (pjsip_event_hdr*)
265
 
            pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_EVENT, NULL);
266
 
    if (!event) {
267
 
        return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST);
268
 
    }
269
 
    if (pj_stricmp(&event->event_type, &STR_PRESENCE) != 0) {
270
 
        return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_EVENT);
271
 
    }
272
 
 
273
 
    /* Check that request contains compatible Accept header. */
274
 
    accept = (pjsip_accept_hdr*)
275
 
             pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, NULL);
276
 
    if (accept) {
277
 
        unsigned i;
278
 
        for (i=0; i<accept->count; ++i) {
279
 
            if (pj_stricmp(&accept->values[i], &STR_APP_PIDF_XML)==0) {
280
 
                content_type = CONTENT_TYPE_PIDF;
281
 
                break;
282
 
            } else
283
 
            if (pj_stricmp(&accept->values[i], &STR_APP_XPIDF_XML)==0) {
284
 
                content_type = CONTENT_TYPE_XPIDF;
285
 
                break;
286
 
            }
287
 
        }
288
 
 
289
 
        if (i==accept->count) {
290
 
            /* Nothing is acceptable */
291
 
            return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE);
292
 
        }
293
 
 
294
 
    } else {
295
 
        /* No Accept header.
296
 
         * Treat as "application/pidf+xml"
297
 
         */
298
 
        content_type = CONTENT_TYPE_PIDF;
299
 
    }
300
 
 
301
 
    /* Lock dialog */
302
 
    pjsip_dlg_inc_lock(dlg);
303
 
 
304
 
 
305
 
    /* Create server subscription */
306
 
    status = pjsip_evsub_create_uas( dlg, &pres_user, rdata, 0, &sub);
307
 
    if (status != PJ_SUCCESS)
308
 
        goto on_return;
309
 
 
310
 
    /* Create server presence subscription */
311
 
    pres = PJ_POOL_ZALLOC_T(dlg->pool, pjsip_pres);
312
 
    pres->dlg = dlg;
313
 
    pres->sub = sub;
314
 
    pres->content_type = content_type;
315
 
    if (user_cb)
316
 
        pj_memcpy(&pres->user_cb, user_cb, sizeof(pjsip_evsub_user));
317
 
 
318
 
    pj_ansi_snprintf(obj_name, PJ_MAX_OBJ_NAME, "pres%p", dlg->pool);
319
 
    pres->status_pool = pj_pool_create(dlg->pool->factory, obj_name,
320
 
                                       512, 512, NULL);
321
 
    pj_ansi_snprintf(obj_name, PJ_MAX_OBJ_NAME, "tmpres%p", dlg->pool);
322
 
    pres->tmp_pool = pj_pool_create(dlg->pool->factory, obj_name,
323
 
                                    512, 512, NULL);
324
 
 
325
 
    /* Attach to evsub */
326
 
    pjsip_evsub_set_mod_data(sub, mod_presence.id, pres);
327
 
 
328
 
    /* Done: */
329
 
    *p_evsub = sub;
330
 
 
331
 
on_return:
332
 
    pjsip_dlg_dec_lock(dlg);
333
 
    return status;
334
 
}
335
 
 
336
 
 
337
 
/*
338
 
 * Forcefully terminate presence.
339
 
 */
340
 
PJ_DEF(pj_status_t) pjsip_pres_terminate( pjsip_evsub *sub,
341
 
                                          pj_bool_t notify )
342
 
{
343
 
    return pjsip_evsub_terminate(sub, notify);
344
 
}
345
 
 
346
 
/*
347
 
 * Create SUBSCRIBE
348
 
 */
349
 
PJ_DEF(pj_status_t) pjsip_pres_initiate( pjsip_evsub *sub,
350
 
                                         pj_int32_t expires,
351
 
                                         pjsip_tx_data **p_tdata)
352
 
{
353
 
    return pjsip_evsub_initiate(sub, &pjsip_subscribe_method, expires,
354
 
                                p_tdata);
355
 
}
356
 
 
357
 
 
358
 
/*
359
 
 * Add custom headers.
360
 
 */
361
 
PJ_DEF(pj_status_t) pjsip_pres_add_header( pjsip_evsub *sub,
362
 
                                           const pjsip_hdr *hdr_list )
363
 
{
364
 
    return pjsip_evsub_add_header( sub, hdr_list );
365
 
}
366
 
 
367
 
 
368
 
/*
369
 
 * Accept incoming subscription.
370
 
 */
371
 
PJ_DEF(pj_status_t) pjsip_pres_accept( pjsip_evsub *sub,
372
 
                                       pjsip_rx_data *rdata,
373
 
                                       int st_code,
374
 
                                       const pjsip_hdr *hdr_list )
375
 
{
376
 
    return pjsip_evsub_accept( sub, rdata, st_code, hdr_list );
377
 
}
378
 
 
379
 
 
380
 
/*
381
 
 * Get presence status.
382
 
 */
383
 
PJ_DEF(pj_status_t) pjsip_pres_get_status( pjsip_evsub *sub,
384
 
                                           pjsip_pres_status *status )
385
 
{
386
 
    pjsip_pres *pres;
387
 
 
388
 
    PJ_ASSERT_RETURN(sub && status, PJ_EINVAL);
389
 
 
390
 
    pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
391
 
    PJ_ASSERT_RETURN(pres!=NULL, PJSIP_SIMPLE_ENOPRESENCE);
392
 
 
393
 
    if (pres->tmp_status._is_valid) {
394
 
        PJ_ASSERT_RETURN(pres->tmp_pool!=NULL, PJSIP_SIMPLE_ENOPRESENCE);
395
 
        pj_memcpy(status, &pres->tmp_status, sizeof(pjsip_pres_status));
396
 
    } else {
397
 
        PJ_ASSERT_RETURN(pres->status_pool!=NULL, PJSIP_SIMPLE_ENOPRESENCE);
398
 
        pj_memcpy(status, &pres->status, sizeof(pjsip_pres_status));
399
 
    }
400
 
 
401
 
    return PJ_SUCCESS;
402
 
}
403
 
 
404
 
 
405
 
/*
406
 
 * Set presence status.
407
 
 */
408
 
PJ_DEF(pj_status_t) pjsip_pres_set_status( pjsip_evsub *sub,
409
 
                                           const pjsip_pres_status *status )
410
 
{
411
 
    unsigned i;
412
 
    pj_pool_t *tmp;
413
 
    pjsip_pres *pres;
414
 
 
415
 
    PJ_ASSERT_RETURN(sub && status, PJ_EINVAL);
416
 
 
417
 
    pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
418
 
    PJ_ASSERT_RETURN(pres!=NULL, PJSIP_SIMPLE_ENOPRESENCE);
419
 
 
420
 
    for (i=0; i<status->info_cnt; ++i) {
421
 
        pres->status.info[i].basic_open = status->info[i].basic_open;
422
 
        if (pres->status.info[i].id.slen) {
423
 
            /* Id already set */
424
 
        } else if (status->info[i].id.slen == 0) {
425
 
            pj_create_unique_string(pres->dlg->pool,
426
 
                                    &pres->status.info[i].id);
427
 
        } else {
428
 
            pj_strdup(pres->dlg->pool,
429
 
                      &pres->status.info[i].id,
430
 
                      &status->info[i].id);
431
 
        }
432
 
        pj_strdup(pres->tmp_pool,
433
 
                  &pres->status.info[i].contact,
434
 
                  &status->info[i].contact);
435
 
 
436
 
        /* Duplicate <person> */
437
 
        pres->status.info[i].rpid.activity =
438
 
            status->info[i].rpid.activity;
439
 
        pj_strdup(pres->tmp_pool,
440
 
                  &pres->status.info[i].rpid.id,
441
 
                  &status->info[i].rpid.id);
442
 
        pj_strdup(pres->tmp_pool,
443
 
                  &pres->status.info[i].rpid.note,
444
 
                  &status->info[i].rpid.note);
445
 
 
446
 
    }
447
 
 
448
 
    pres->status.info_cnt = status->info_cnt;
449
 
 
450
 
    /* Swap pools */
451
 
    tmp = pres->tmp_pool;
452
 
    pres->tmp_pool = pres->status_pool;
453
 
    pres->status_pool = tmp;
454
 
    pj_pool_reset(pres->tmp_pool);
455
 
 
456
 
    return PJ_SUCCESS;
457
 
}
458
 
 
459
 
 
460
 
/*
461
 
 * Create message body.
462
 
 */
463
 
static pj_status_t pres_create_msg_body( pjsip_pres *pres,
464
 
                                         pjsip_tx_data *tdata)
465
 
{
466
 
    pj_str_t entity;
467
 
 
468
 
    /* Get publisher URI */
469
 
    entity.ptr = (char*) pj_pool_alloc(tdata->pool, PJSIP_MAX_URL_SIZE);
470
 
    entity.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI,
471
 
                                  pres->dlg->local.info->uri,
472
 
                                  entity.ptr, PJSIP_MAX_URL_SIZE);
473
 
    if (entity.slen < 1)
474
 
        return PJ_ENOMEM;
475
 
 
476
 
    if (pres->content_type == CONTENT_TYPE_PIDF) {
477
 
 
478
 
        return pjsip_pres_create_pidf(tdata->pool, &pres->status,
479
 
                                      &entity, &tdata->msg->body);
480
 
 
481
 
    } else if (pres->content_type == CONTENT_TYPE_XPIDF) {
482
 
 
483
 
        return pjsip_pres_create_xpidf(tdata->pool, &pres->status,
484
 
                                       &entity, &tdata->msg->body);
485
 
 
486
 
    } else {
487
 
        return PJSIP_SIMPLE_EBADCONTENT;
488
 
    }
489
 
}
490
 
 
491
 
 
492
 
/*
493
 
 * Create NOTIFY
494
 
 */
495
 
PJ_DEF(pj_status_t) pjsip_pres_notify( pjsip_evsub *sub,
496
 
                                       pjsip_evsub_state state,
497
 
                                       const pj_str_t *state_str,
498
 
                                       const pj_str_t *reason,
499
 
                                       pjsip_tx_data **p_tdata)
500
 
{
501
 
    pjsip_pres *pres;
502
 
    pjsip_tx_data *tdata;
503
 
    pj_status_t status;
504
 
 
505
 
    /* Check arguments. */
506
 
    PJ_ASSERT_RETURN(sub, PJ_EINVAL);
507
 
 
508
 
    /* Get the presence object. */
509
 
    pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
510
 
    PJ_ASSERT_RETURN(pres != NULL, PJSIP_SIMPLE_ENOPRESENCE);
511
 
 
512
 
    /* Must have at least one presence info, unless state is
513
 
     * PJSIP_EVSUB_STATE_TERMINATED. This could happen if subscription
514
 
     * has not been active (e.g. we're waiting for user authorization)
515
 
     * and remote cancels the subscription.
516
 
     */
517
 
    PJ_ASSERT_RETURN(state==PJSIP_EVSUB_STATE_TERMINATED ||
518
 
                     pres->status.info_cnt > 0, PJSIP_SIMPLE_ENOPRESENCEINFO);
519
 
 
520
 
 
521
 
    /* Lock object. */
522
 
    pjsip_dlg_inc_lock(pres->dlg);
523
 
 
524
 
    /* Create the NOTIFY request. */
525
 
    status = pjsip_evsub_notify( sub, state, state_str, reason, &tdata);
526
 
    if (status != PJ_SUCCESS)
527
 
        goto on_return;
528
 
 
529
 
 
530
 
    /* Create message body to reflect the presence status.
531
 
     * Only do this if we have presence status info to send (see above).
532
 
     */
533
 
    if (pres->status.info_cnt > 0) {
534
 
        status = pres_create_msg_body( pres, tdata );
535
 
        if (status != PJ_SUCCESS)
536
 
            goto on_return;
537
 
    }
538
 
 
539
 
    /* Done. */
540
 
    *p_tdata = tdata;
541
 
 
542
 
 
543
 
on_return:
544
 
    pjsip_dlg_dec_lock(pres->dlg);
545
 
    return status;
546
 
}
547
 
 
548
 
 
549
 
/*
550
 
 * Create NOTIFY that reflect current state.
551
 
 */
552
 
PJ_DEF(pj_status_t) pjsip_pres_current_notify( pjsip_evsub *sub,
553
 
                                               pjsip_tx_data **p_tdata )
554
 
{
555
 
    pjsip_pres *pres;
556
 
    pjsip_tx_data *tdata;
557
 
    pj_status_t status;
558
 
 
559
 
    /* Check arguments. */
560
 
    PJ_ASSERT_RETURN(sub, PJ_EINVAL);
561
 
 
562
 
    /* Get the presence object. */
563
 
    pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
564
 
    PJ_ASSERT_RETURN(pres != NULL, PJSIP_SIMPLE_ENOPRESENCE);
565
 
 
566
 
    /* We may not have a presence info yet, e.g. when we receive SUBSCRIBE
567
 
     * to refresh subscription while we're waiting for user authorization.
568
 
     */
569
 
    //PJ_ASSERT_RETURN(pres->status.info_cnt > 0,
570
 
    //                 PJSIP_SIMPLE_ENOPRESENCEINFO);
571
 
 
572
 
 
573
 
    /* Lock object. */
574
 
    pjsip_dlg_inc_lock(pres->dlg);
575
 
 
576
 
    /* Create the NOTIFY request. */
577
 
    status = pjsip_evsub_current_notify( sub, &tdata);
578
 
    if (status != PJ_SUCCESS)
579
 
        goto on_return;
580
 
 
581
 
 
582
 
    /* Create message body to reflect the presence status. */
583
 
    if (pres->status.info_cnt > 0) {
584
 
        status = pres_create_msg_body( pres, tdata );
585
 
        if (status != PJ_SUCCESS)
586
 
            goto on_return;
587
 
    }
588
 
 
589
 
    /* Done. */
590
 
    *p_tdata = tdata;
591
 
 
592
 
 
593
 
on_return:
594
 
    pjsip_dlg_dec_lock(pres->dlg);
595
 
    return status;
596
 
}
597
 
 
598
 
 
599
 
/*
600
 
 * Send request.
601
 
 */
602
 
PJ_DEF(pj_status_t) pjsip_pres_send_request( pjsip_evsub *sub,
603
 
                                             pjsip_tx_data *tdata )
604
 
{
605
 
    return pjsip_evsub_send_request(sub, tdata);
606
 
}
607
 
 
608
 
 
609
 
/*
610
 
 * This callback is called by event subscription when subscription
611
 
 * state has changed.
612
 
 */
613
 
static void pres_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
614
 
{
615
 
    pjsip_pres *pres;
616
 
 
617
 
    pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
618
 
    PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
619
 
 
620
 
    if (pres->user_cb.on_evsub_state)
621
 
        (*pres->user_cb.on_evsub_state)(sub, event);
622
 
 
623
 
    if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
624
 
        if (pres->status_pool) {
625
 
            pj_pool_release(pres->status_pool);
626
 
            pres->status_pool = NULL;
627
 
        }
628
 
        if (pres->tmp_pool) {
629
 
            pj_pool_release(pres->tmp_pool);
630
 
            pres->tmp_pool = NULL;
631
 
        }
632
 
    }
633
 
}
634
 
 
635
 
/*
636
 
 * Called when transaction state has changed.
637
 
 */
638
 
static void pres_on_evsub_tsx_state( pjsip_evsub *sub, pjsip_transaction *tsx,
639
 
                                     pjsip_event *event)
640
 
{
641
 
    pjsip_pres *pres;
642
 
 
643
 
    pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
644
 
    PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
645
 
 
646
 
    if (pres->user_cb.on_tsx_state)
647
 
        (*pres->user_cb.on_tsx_state)(sub, tsx, event);
648
 
}
649
 
 
650
 
 
651
 
/*
652
 
 * Called when SUBSCRIBE is received.
653
 
 */
654
 
static void pres_on_evsub_rx_refresh( pjsip_evsub *sub,
655
 
                                      pjsip_rx_data *rdata,
656
 
                                      int *p_st_code,
657
 
                                      pj_str_t **p_st_text,
658
 
                                      pjsip_hdr *res_hdr,
659
 
                                      pjsip_msg_body **p_body)
660
 
{
661
 
    pjsip_pres *pres;
662
 
 
663
 
    pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
664
 
    PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
665
 
 
666
 
    if (pres->user_cb.on_rx_refresh) {
667
 
        (*pres->user_cb.on_rx_refresh)(sub, rdata, p_st_code, p_st_text,
668
 
                                       res_hdr, p_body);
669
 
 
670
 
    } else {
671
 
        /* Implementors MUST send NOTIFY if it implements on_rx_refresh */
672
 
        pjsip_tx_data *tdata;
673
 
        pj_str_t timeout = { "timeout", 7};
674
 
        pj_status_t status;
675
 
 
676
 
        if (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED) {
677
 
            status = pjsip_pres_notify( sub, PJSIP_EVSUB_STATE_TERMINATED,
678
 
                                        NULL, &timeout, &tdata);
679
 
        } else {
680
 
            status = pjsip_pres_current_notify(sub, &tdata);
681
 
        }
682
 
 
683
 
        if (status == PJ_SUCCESS)
684
 
            pjsip_pres_send_request(sub, tdata);
685
 
    }
686
 
}
687
 
 
688
 
 
689
 
/*
690
 
 * Process the content of incoming NOTIFY request and update temporary
691
 
 * status.
692
 
 *
693
 
 * return PJ_SUCCESS if incoming request is acceptable. If return value
694
 
 *        is not PJ_SUCCESS, res_hdr may be added with Warning header.
695
 
 */
696
 
static pj_status_t pres_process_rx_notify( pjsip_pres *pres,
697
 
                                           pjsip_rx_data *rdata,
698
 
                                           int *p_st_code,
699
 
                                           pj_str_t **p_st_text,
700
 
                                           pjsip_hdr *res_hdr)
701
 
{
702
 
    const pj_str_t STR_MULTIPART = { "multipart", 9 };
703
 
    pjsip_ctype_hdr *ctype_hdr;
704
 
    pj_status_t status = PJ_SUCCESS;
705
 
 
706
 
    *p_st_text = NULL;
707
 
 
708
 
    /* Check Content-Type and msg body are present. */
709
 
    ctype_hdr = rdata->msg_info.ctype;
710
 
 
711
 
    if (ctype_hdr==NULL || rdata->msg_info.msg->body==NULL) {
712
 
 
713
 
        pjsip_warning_hdr *warn_hdr;
714
 
        pj_str_t warn_text;
715
 
 
716
 
        *p_st_code = PJSIP_SC_BAD_REQUEST;
717
 
 
718
 
        warn_text = pj_str("Message body is not present");
719
 
        warn_hdr = pjsip_warning_hdr_create(rdata->tp_info.pool, 399,
720
 
                                            pjsip_endpt_name(pres->dlg->endpt),
721
 
                                            &warn_text);
722
 
        pj_list_push_back(res_hdr, warn_hdr);
723
 
 
724
 
        return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST);
725
 
    }
726
 
 
727
 
    /* Parse content. */
728
 
    if (pj_stricmp(&ctype_hdr->media.type, &STR_MULTIPART)==0) {
729
 
        pjsip_multipart_part *mpart;
730
 
        pjsip_media_type ctype;
731
 
 
732
 
        pjsip_media_type_init(&ctype, (pj_str_t*)&STR_APPLICATION,
733
 
                              (pj_str_t*)&STR_PIDF_XML);
734
 
        mpart = pjsip_multipart_find_part(rdata->msg_info.msg->body,
735
 
                                          &ctype, NULL);
736
 
        if (mpart) {
737
 
            status = pjsip_pres_parse_pidf2((char*)mpart->body->data,
738
 
                                            mpart->body->len, pres->tmp_pool,
739
 
                                            &pres->tmp_status);
740
 
        }
741
 
 
742
 
        if (mpart==NULL) {
743
 
            pjsip_media_type_init(&ctype, (pj_str_t*)&STR_APPLICATION,
744
 
                                  (pj_str_t*)&STR_XPIDF_XML);
745
 
            mpart = pjsip_multipart_find_part(rdata->msg_info.msg->body,
746
 
                                              &ctype, NULL);
747
 
            if (mpart) {
748
 
                status = pjsip_pres_parse_xpidf2((char*)mpart->body->data,
749
 
                                                 mpart->body->len,
750
 
                                                 pres->tmp_pool,
751
 
                                                 &pres->tmp_status);
752
 
            } else {
753
 
                status = PJSIP_SIMPLE_EBADCONTENT;
754
 
            }
755
 
        }
756
 
    }
757
 
    else
758
 
    if (pj_stricmp(&ctype_hdr->media.type, &STR_APPLICATION)==0 &&
759
 
        pj_stricmp(&ctype_hdr->media.subtype, &STR_PIDF_XML)==0)
760
 
    {
761
 
        status = pjsip_pres_parse_pidf( rdata, pres->tmp_pool,
762
 
                                        &pres->tmp_status);
763
 
    }
764
 
    else
765
 
    if (pj_stricmp(&ctype_hdr->media.type, &STR_APPLICATION)==0 &&
766
 
        pj_stricmp(&ctype_hdr->media.subtype, &STR_XPIDF_XML)==0)
767
 
    {
768
 
        status = pjsip_pres_parse_xpidf( rdata, pres->tmp_pool,
769
 
                                         &pres->tmp_status);
770
 
    }
771
 
    else
772
 
    {
773
 
        status = PJSIP_SIMPLE_EBADCONTENT;
774
 
    }
775
 
 
776
 
    if (status != PJ_SUCCESS) {
777
 
        /* Unsupported or bad Content-Type */
778
 
        if (PJSIP_PRES_BAD_CONTENT_RESPONSE >= 300) {
779
 
            pjsip_accept_hdr *accept_hdr;
780
 
            pjsip_warning_hdr *warn_hdr;
781
 
 
782
 
            *p_st_code = PJSIP_PRES_BAD_CONTENT_RESPONSE;
783
 
 
784
 
            /* Add Accept header */
785
 
            accept_hdr = pjsip_accept_hdr_create(rdata->tp_info.pool);
786
 
            accept_hdr->values[accept_hdr->count++] = STR_APP_PIDF_XML;
787
 
            accept_hdr->values[accept_hdr->count++] = STR_APP_XPIDF_XML;
788
 
            pj_list_push_back(res_hdr, accept_hdr);
789
 
 
790
 
            /* Add Warning header */
791
 
            warn_hdr = pjsip_warning_hdr_create_from_status(
792
 
                                        rdata->tp_info.pool,
793
 
                                        pjsip_endpt_name(pres->dlg->endpt),
794
 
                                        status);
795
 
            pj_list_push_back(res_hdr, warn_hdr);
796
 
 
797
 
            return status;
798
 
        } else {
799
 
            pj_assert(PJSIP_PRES_BAD_CONTENT_RESPONSE/100 == 2);
800
 
            PJ_PERROR(4,(THIS_FILE, status,
801
 
                         "Ignoring presence error due to "
802
 
                         "PJSIP_PRES_BAD_CONTENT_RESPONSE setting [%d]",
803
 
                         PJSIP_PRES_BAD_CONTENT_RESPONSE));
804
 
            *p_st_code = PJSIP_PRES_BAD_CONTENT_RESPONSE;
805
 
            status = PJ_SUCCESS;
806
 
        }
807
 
    }
808
 
 
809
 
    /* If application calls pres_get_status(), redirect the call to
810
 
     * retrieve the temporary status.
811
 
     */
812
 
    pres->tmp_status._is_valid = PJ_TRUE;
813
 
 
814
 
    return PJ_SUCCESS;
815
 
}
816
 
 
817
 
 
818
 
/*
819
 
 * Called when NOTIFY is received.
820
 
 */
821
 
static void pres_on_evsub_rx_notify( pjsip_evsub *sub,
822
 
                                     pjsip_rx_data *rdata,
823
 
                                     int *p_st_code,
824
 
                                     pj_str_t **p_st_text,
825
 
                                     pjsip_hdr *res_hdr,
826
 
                                     pjsip_msg_body **p_body)
827
 
{
828
 
    pjsip_pres *pres;
829
 
    pj_status_t status;
830
 
 
831
 
    pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
832
 
    PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
833
 
 
834
 
    if (rdata->msg_info.msg->body) {
835
 
        status = pres_process_rx_notify( pres, rdata, p_st_code, p_st_text,
836
 
                                         res_hdr );
837
 
        if (status != PJ_SUCCESS)
838
 
            return;
839
 
 
840
 
    } else {
841
 
#if 1
842
 
        /* This is the newest change, http://trac.pjsip.org/repos/ticket/873
843
 
         * Some app want to be notified about the empty NOTIFY, e.g. to
844
 
         * decide whether it should consider the buddy as offline.
845
 
         * In this case, leave the buddy state unchanged, but set the
846
 
         * "tuple_node" in pjsip_pres_status to NULL.
847
 
         */
848
 
        unsigned i;
849
 
        for (i=0; i<pres->status.info_cnt; ++i) {
850
 
            pres->status.info[i].tuple_node = NULL;
851
 
        }
852
 
 
853
 
#elif 0
854
 
        /* This has just been changed. Previously, we treat incoming NOTIFY
855
 
         * with no message body as having the presence subscription closed.
856
 
         * Now we treat it as no change in presence status (ref: EyeBeam).
857
 
         */
858
 
        *p_st_code = 200;
859
 
        return;
860
 
#else
861
 
        unsigned i;
862
 
        /* Subscription is terminated. Consider contact is offline */
863
 
        pres->tmp_status._is_valid = PJ_TRUE;
864
 
        for (i=0; i<pres->tmp_status.info_cnt; ++i)
865
 
            pres->tmp_status.info[i].basic_open = PJ_FALSE;
866
 
#endif
867
 
    }
868
 
 
869
 
    /* Notify application. */
870
 
    if (pres->user_cb.on_rx_notify) {
871
 
        (*pres->user_cb.on_rx_notify)(sub, rdata, p_st_code, p_st_text,
872
 
                                      res_hdr, p_body);
873
 
    }
874
 
 
875
 
 
876
 
    /* If application responded NOTIFY with 2xx, copy temporary status
877
 
     * to main status, and mark the temporary status as invalid.
878
 
     */
879
 
    if ((*p_st_code)/100 == 2) {
880
 
        pj_pool_t *tmp;
881
 
 
882
 
        pj_memcpy(&pres->status, &pres->tmp_status, sizeof(pjsip_pres_status));
883
 
 
884
 
        /* Swap the pool */
885
 
        tmp = pres->tmp_pool;
886
 
        pres->tmp_pool = pres->status_pool;
887
 
        pres->status_pool = tmp;
888
 
    }
889
 
 
890
 
    pres->tmp_status._is_valid = PJ_FALSE;
891
 
    pj_pool_reset(pres->tmp_pool);
892
 
 
893
 
    /* Done */
894
 
}
895
 
 
896
 
/*
897
 
 * Called when it's time to send SUBSCRIBE.
898
 
 */
899
 
static void pres_on_evsub_client_refresh(pjsip_evsub *sub)
900
 
{
901
 
    pjsip_pres *pres;
902
 
 
903
 
    pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
904
 
    PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
905
 
 
906
 
    if (pres->user_cb.on_client_refresh) {
907
 
        (*pres->user_cb.on_client_refresh)(sub);
908
 
    } else {
909
 
        pj_status_t status;
910
 
        pjsip_tx_data *tdata;
911
 
 
912
 
        status = pjsip_pres_initiate(sub, -1, &tdata);
913
 
        if (status == PJ_SUCCESS)
914
 
            pjsip_pres_send_request(sub, tdata);
915
 
    }
916
 
}
917
 
 
918
 
/*
919
 
 * Called when no refresh is received after the interval.
920
 
 */
921
 
static void pres_on_evsub_server_timeout(pjsip_evsub *sub)
922
 
{
923
 
    pjsip_pres *pres;
924
 
 
925
 
    pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
926
 
    PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
927
 
 
928
 
    if (pres->user_cb.on_server_timeout) {
929
 
        (*pres->user_cb.on_server_timeout)(sub);
930
 
    } else {
931
 
        pj_status_t status;
932
 
        pjsip_tx_data *tdata;
933
 
        pj_str_t reason = { "timeout", 7 };
934
 
 
935
 
        status = pjsip_pres_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
936
 
                                   NULL, &reason, &tdata);
937
 
        if (status == PJ_SUCCESS)
938
 
            pjsip_pres_send_request(sub, tdata);
939
 
    }
940
 
}