~ubuntu-branches/ubuntu/trusty/sflphone/trusty

« back to all changes in this revision

Viewing changes to daemon/libs/pjproject-2.1.0/pjsip/src/pjsip-simple/publishc.c

  • Committer: Package Import Robot
  • Author(s): Mark Purcell
  • Date: 2014-01-28 18:23:36 UTC
  • mfrom: (4.3.4 sid)
  • Revision ID: package-import@ubuntu.com-20140128182336-jrsv0k9u6cawc068
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: publishc.c 4206 2012-07-16 02:45:09Z ming $ */
 
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/publish.h>
 
21
#include <pjsip/sip_auth.h>
 
22
#include <pjsip/sip_endpoint.h>
 
23
#include <pjsip/sip_errno.h>
 
24
#include <pjsip/sip_event.h>
 
25
#include <pjsip/sip_msg.h>
 
26
#include <pjsip/sip_transaction.h>
 
27
#include <pjsip/sip_uri.h>
 
28
#include <pjsip/sip_util.h>
 
29
#include <pj/assert.h>
 
30
#include <pj/guid.h>
 
31
#include <pj/log.h>
 
32
#include <pj/os.h>
 
33
#include <pj/pool.h>
 
34
#include <pj/rand.h>
 
35
#include <pj/string.h>
 
36
#include <pj/timer.h>
 
37
 
 
38
 
 
39
#define REFRESH_TIMER           1
 
40
#define DELAY_BEFORE_REFRESH    PJSIP_PUBLISHC_DELAY_BEFORE_REFRESH
 
41
#define THIS_FILE               "publishc.c"
 
42
 
 
43
 
 
44
/* Let's define this enum, so that it'll trigger compilation error
 
45
 * when somebody define the same enum in sip_msg.h
 
46
 */
 
47
enum
 
48
{
 
49
    PJSIP_PUBLISH_METHOD = PJSIP_OTHER_METHOD,
 
50
};
 
51
 
 
52
const pjsip_method pjsip_publish_method = 
 
53
{
 
54
    (pjsip_method_e)PJSIP_PUBLISH_METHOD,
 
55
    { "PUBLISH", 7 }
 
56
};
 
57
 
 
58
 
 
59
/**
 
60
 * Pending request list.
 
61
 */
 
62
typedef struct pending_publish
 
63
{
 
64
    PJ_DECL_LIST_MEMBER(pjsip_tx_data);
 
65
} pending_publish;
 
66
 
 
67
 
 
68
/**
 
69
 * SIP client publication structure.
 
70
 */
 
71
struct pjsip_publishc
 
72
{
 
73
    pj_pool_t                   *pool;
 
74
    pjsip_endpoint              *endpt;
 
75
    pj_bool_t                    _delete_flag;
 
76
    int                          pending_tsx;
 
77
    pj_bool_t                    in_callback;
 
78
    pj_mutex_t                  *mutex;
 
79
 
 
80
    pjsip_publishc_opt           opt;
 
81
    void                        *token;
 
82
    pjsip_publishc_cb           *cb;
 
83
 
 
84
    pj_str_t                     event;
 
85
    pj_str_t                     str_target_uri;
 
86
    pjsip_uri                   *target_uri;
 
87
    pjsip_cid_hdr               *cid_hdr;
 
88
    pjsip_cseq_hdr              *cseq_hdr;
 
89
    pj_str_t                     from_uri;
 
90
    pjsip_from_hdr              *from_hdr;
 
91
    pjsip_to_hdr                *to_hdr;
 
92
    pj_str_t                     etag;
 
93
    pjsip_expires_hdr           *expires_hdr;
 
94
    pj_uint32_t                  expires;
 
95
    pjsip_route_hdr              route_set;
 
96
    pjsip_hdr                    usr_hdr;
 
97
    pjsip_host_port              via_addr;
 
98
    const void                  *via_tp;
 
99
 
 
100
    /* Authorization sessions. */
 
101
    pjsip_auth_clt_sess          auth_sess;
 
102
 
 
103
    /* Auto refresh publication. */
 
104
    pj_bool_t                    auto_refresh;
 
105
    pj_time_val                  last_refresh;
 
106
    pj_time_val                  next_refresh;
 
107
    pj_timer_entry               timer;
 
108
 
 
109
    /* Pending PUBLISH request */
 
110
    pending_publish              pending_reqs;
 
111
};
 
112
 
 
113
 
 
114
PJ_DEF(void) pjsip_publishc_opt_default(pjsip_publishc_opt *opt)
 
115
{
 
116
    pj_bzero(opt, sizeof(*opt));
 
117
    opt->queue_request = PJSIP_PUBLISHC_QUEUE_REQUEST;
 
118
}
 
119
 
 
120
 
 
121
/*
 
122
 * Initialize client publication module.
 
123
 */
 
124
PJ_DEF(pj_status_t) pjsip_publishc_init_module(pjsip_endpoint *endpt)
 
125
{
 
126
    /* Note:
 
127
        Commented out the capability registration below, since it's
 
128
        wrong to include PUBLISH in Allow header of INVITE requests/
 
129
        responses.
 
130
 
 
131
        13.2.1 Creating the Initial INVITE
 
132
          An Allow header field (Section 20.5) SHOULD be present in the 
 
133
          INVITE. It indicates what methods can be invoked within a dialog
 
134
 
 
135
        20.5 Allow
 
136
          The Allow header field lists the set of methods supported by the
 
137
          UA generating the message.
 
138
 
 
139
        While the semantic of Allow header in non-dialog requests is unclear,
 
140
        it's probably best not to include PUBLISH in Allow header for now
 
141
        until we can find out how to customize the inclusion of methods in
 
142
        Allow header for in-dialog vs out-dialog requests.
 
143
 
 
144
    return pjsip_endpt_add_capability( endpt, NULL, PJSIP_H_ALLOW, NULL,
 
145
                                       1, &pjsip_publish_method.name);
 
146
     */
 
147
    PJ_UNUSED_ARG(endpt);
 
148
    return PJ_SUCCESS;
 
149
}
 
150
 
 
151
 
 
152
PJ_DEF(pj_status_t) pjsip_publishc_create( pjsip_endpoint *endpt, 
 
153
                                           const pjsip_publishc_opt *opt,
 
154
                                           void *token,
 
155
                                           pjsip_publishc_cb *cb,       
 
156
                                           pjsip_publishc **p_pubc)
 
157
{
 
158
    pj_pool_t *pool;
 
159
    pjsip_publishc *pubc;
 
160
    pjsip_publishc_opt default_opt;
 
161
    pj_status_t status;
 
162
 
 
163
    /* Verify arguments. */
 
164
    PJ_ASSERT_RETURN(endpt && cb && p_pubc, PJ_EINVAL);
 
165
 
 
166
    pool = pjsip_endpt_create_pool(endpt, "pubc%p", 1024, 1024);
 
167
    PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
 
168
 
 
169
    pubc = PJ_POOL_ZALLOC_T(pool, pjsip_publishc);
 
170
 
 
171
    pubc->pool = pool;
 
172
    pubc->endpt = endpt;
 
173
    pubc->token = token;
 
174
    pubc->cb = cb;
 
175
    pubc->expires = PJSIP_PUBC_EXPIRATION_NOT_SPECIFIED;
 
176
 
 
177
    if (!opt) {
 
178
        pjsip_publishc_opt_default(&default_opt);
 
179
        opt = &default_opt;
 
180
    }
 
181
    pj_memcpy(&pubc->opt, opt, sizeof(*opt));
 
182
    pj_list_init(&pubc->pending_reqs);
 
183
 
 
184
    status = pj_mutex_create_recursive(pubc->pool, "pubc%p", &pubc->mutex);
 
185
    if (status != PJ_SUCCESS) {
 
186
        pj_pool_release(pool);
 
187
        return status;
 
188
    }
 
189
 
 
190
    status = pjsip_auth_clt_init(&pubc->auth_sess, endpt, pubc->pool, 0);
 
191
    if (status != PJ_SUCCESS) {
 
192
        pj_mutex_destroy(pubc->mutex);
 
193
        pj_pool_release(pool);
 
194
        return status;
 
195
    }
 
196
 
 
197
    pj_list_init(&pubc->route_set);
 
198
    pj_list_init(&pubc->usr_hdr);
 
199
 
 
200
    /* Done */
 
201
    *p_pubc = pubc;
 
202
    return PJ_SUCCESS;
 
203
}
 
204
 
 
205
 
 
206
PJ_DEF(pj_status_t) pjsip_publishc_destroy(pjsip_publishc *pubc)
 
207
{
 
208
    PJ_ASSERT_RETURN(pubc, PJ_EINVAL);
 
209
 
 
210
    if (pubc->pending_tsx || pubc->in_callback) {
 
211
        pubc->_delete_flag = 1;
 
212
        pubc->cb = NULL;
 
213
    } else {
 
214
        /* Cancel existing timer, if any */
 
215
        if (pubc->timer.id != 0) {
 
216
            pjsip_endpt_cancel_timer(pubc->endpt, &pubc->timer);
 
217
            pubc->timer.id = 0;
 
218
        }
 
219
 
 
220
        if (pubc->mutex)
 
221
            pj_mutex_destroy(pubc->mutex);
 
222
        pjsip_endpt_release_pool(pubc->endpt, pubc->pool);
 
223
    }
 
224
 
 
225
    return PJ_SUCCESS;
 
226
}
 
227
 
 
228
 
 
229
PJ_DEF(pj_pool_t*) pjsip_publishc_get_pool(pjsip_publishc *pubc)
 
230
{
 
231
    return pubc->pool;
 
232
}
 
233
 
 
234
static void set_expires( pjsip_publishc *pubc, pj_uint32_t expires)
 
235
{
 
236
    if (expires != pubc->expires && 
 
237
        expires != PJSIP_PUBC_EXPIRATION_NOT_SPECIFIED) 
 
238
    {
 
239
        pubc->expires_hdr = pjsip_expires_hdr_create(pubc->pool, expires);
 
240
    } else {
 
241
        pubc->expires_hdr = NULL;
 
242
    }
 
243
}
 
244
 
 
245
 
 
246
PJ_DEF(pj_status_t) pjsip_publishc_init(pjsip_publishc *pubc,
 
247
                                        const pj_str_t *event,
 
248
                                        const pj_str_t *target_uri,
 
249
                                        const pj_str_t *from_uri,
 
250
                                        const pj_str_t *to_uri,
 
251
                                        pj_uint32_t expires)
 
252
{
 
253
    pj_str_t tmp;
 
254
 
 
255
    PJ_ASSERT_RETURN(pubc && event && target_uri && from_uri && to_uri && 
 
256
                     expires, PJ_EINVAL);
 
257
 
 
258
    /* Copy event type */
 
259
    pj_strdup_with_null(pubc->pool, &pubc->event, event);
 
260
 
 
261
    /* Copy server URL. */
 
262
    pj_strdup_with_null(pubc->pool, &pubc->str_target_uri, target_uri);
 
263
 
 
264
    /* Set server URL. */
 
265
    tmp = pubc->str_target_uri;
 
266
    pubc->target_uri = pjsip_parse_uri( pubc->pool, tmp.ptr, tmp.slen, 0);
 
267
    if (pubc->target_uri == NULL) {
 
268
        return PJSIP_EINVALIDURI;
 
269
    }
 
270
 
 
271
    /* Set "From" header. */
 
272
    pj_strdup_with_null(pubc->pool, &pubc->from_uri, from_uri);
 
273
    tmp = pubc->from_uri;
 
274
    pubc->from_hdr = pjsip_from_hdr_create(pubc->pool);
 
275
    pubc->from_hdr->uri = pjsip_parse_uri(pubc->pool, tmp.ptr, tmp.slen, 
 
276
                                          PJSIP_PARSE_URI_AS_NAMEADDR);
 
277
    if (!pubc->from_hdr->uri) {
 
278
        return PJSIP_EINVALIDURI;
 
279
    }
 
280
 
 
281
    /* Set "To" header. */
 
282
    pj_strdup_with_null(pubc->pool, &tmp, to_uri);
 
283
    pubc->to_hdr = pjsip_to_hdr_create(pubc->pool);
 
284
    pubc->to_hdr->uri = pjsip_parse_uri(pubc->pool, tmp.ptr, tmp.slen, 
 
285
                                        PJSIP_PARSE_URI_AS_NAMEADDR);
 
286
    if (!pubc->to_hdr->uri) {
 
287
        return PJSIP_EINVALIDURI;
 
288
    }
 
289
 
 
290
 
 
291
    /* Set "Expires" header, if required. */
 
292
    set_expires( pubc, expires);
 
293
 
 
294
    /* Set "Call-ID" header. */
 
295
    pubc->cid_hdr = pjsip_cid_hdr_create(pubc->pool);
 
296
    pj_create_unique_string(pubc->pool, &pubc->cid_hdr->id);
 
297
 
 
298
    /* Set "CSeq" header. */
 
299
    pubc->cseq_hdr = pjsip_cseq_hdr_create(pubc->pool);
 
300
    pubc->cseq_hdr->cseq = pj_rand() % 0xFFFF;
 
301
    pjsip_method_set( &pubc->cseq_hdr->method, PJSIP_REGISTER_METHOD);
 
302
 
 
303
    /* Done. */
 
304
    return PJ_SUCCESS;
 
305
}
 
306
 
 
307
PJ_DEF(pj_status_t) pjsip_publishc_set_credentials( pjsip_publishc *pubc,
 
308
                                                int count,
 
309
                                                const pjsip_cred_info cred[] )
 
310
{
 
311
    PJ_ASSERT_RETURN(pubc && count && cred, PJ_EINVAL);
 
312
    return pjsip_auth_clt_set_credentials(&pubc->auth_sess, count, cred);
 
313
}
 
314
 
 
315
PJ_DEF(pj_status_t) pjsip_publishc_set_route_set( pjsip_publishc *pubc,
 
316
                                              const pjsip_route_hdr *route_set)
 
317
{
 
318
    const pjsip_route_hdr *chdr;
 
319
 
 
320
    PJ_ASSERT_RETURN(pubc && route_set, PJ_EINVAL);
 
321
 
 
322
    pj_list_init(&pubc->route_set);
 
323
 
 
324
    chdr = route_set->next;
 
325
    while (chdr != route_set) {
 
326
        pj_list_push_back(&pubc->route_set, pjsip_hdr_clone(pubc->pool, chdr));
 
327
        chdr = chdr->next;
 
328
    }
 
329
 
 
330
    return PJ_SUCCESS;
 
331
}
 
332
 
 
333
PJ_DEF(pj_status_t) pjsip_publishc_set_headers( pjsip_publishc *pubc,
 
334
                                                const pjsip_hdr *hdr_list)
 
335
{
 
336
    const pjsip_hdr *h;
 
337
 
 
338
    PJ_ASSERT_RETURN(pubc && hdr_list, PJ_EINVAL);
 
339
 
 
340
    pj_list_init(&pubc->usr_hdr);
 
341
    h = hdr_list->next;
 
342
    while (h != hdr_list) {
 
343
        pj_list_push_back(&pubc->usr_hdr, pjsip_hdr_clone(pubc->pool, h));
 
344
        h = h->next;
 
345
    }
 
346
 
 
347
    return PJ_SUCCESS;
 
348
}
 
349
 
 
350
PJ_DEF(pj_status_t) pjsip_publishc_set_via_sent_by(pjsip_publishc *pubc,
 
351
                                                   pjsip_host_port *via_addr,
 
352
                                                   pjsip_transport *via_tp)
 
353
{
 
354
    PJ_ASSERT_RETURN(pubc, PJ_EINVAL);
 
355
 
 
356
    if (!via_addr)
 
357
        pj_bzero(&pubc->via_addr, sizeof(pubc->via_addr));
 
358
    else {
 
359
        if (pj_strcmp(&pubc->via_addr.host, &via_addr->host))
 
360
            pj_strdup(pubc->pool, &pubc->via_addr.host, &via_addr->host);
 
361
        pubc->via_addr.port = via_addr->port;
 
362
    }
 
363
    pubc->via_tp = via_tp;
 
364
 
 
365
    return PJ_SUCCESS;
 
366
}
 
367
 
 
368
static pj_status_t create_request(pjsip_publishc *pubc, 
 
369
                                  pjsip_tx_data **p_tdata)
 
370
{
 
371
    const pj_str_t STR_EVENT = { "Event", 5 };
 
372
    pj_status_t status;
 
373
    pjsip_generic_string_hdr *hdr;
 
374
    pjsip_tx_data *tdata;
 
375
 
 
376
    PJ_ASSERT_RETURN(pubc && p_tdata, PJ_EINVAL);
 
377
 
 
378
    /* Create the request. */
 
379
    status = pjsip_endpt_create_request_from_hdr( pubc->endpt, 
 
380
                                                  &pjsip_publish_method,
 
381
                                                  pubc->target_uri,
 
382
                                                  pubc->from_hdr,
 
383
                                                  pubc->to_hdr,
 
384
                                                  NULL,
 
385
                                                  pubc->cid_hdr,
 
386
                                                  pubc->cseq_hdr->cseq,
 
387
                                                  NULL,
 
388
                                                  &tdata);
 
389
    if (status != PJ_SUCCESS)
 
390
        return status;
 
391
 
 
392
    /* Add cached authorization headers. */
 
393
    pjsip_auth_clt_init_req( &pubc->auth_sess, tdata );
 
394
 
 
395
    /* Add Route headers from route set, ideally after Via header */
 
396
    if (!pj_list_empty(&pubc->route_set)) {
 
397
        pjsip_hdr *route_pos;
 
398
        const pjsip_route_hdr *route;
 
399
 
 
400
        route_pos = (pjsip_hdr*)
 
401
                    pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
 
402
        if (!route_pos)
 
403
            route_pos = &tdata->msg->hdr;
 
404
 
 
405
        route = pubc->route_set.next;
 
406
        while (route != &pubc->route_set) {
 
407
            pjsip_hdr *new_hdr = (pjsip_hdr*)
 
408
                                 pjsip_hdr_shallow_clone(tdata->pool, route);
 
409
            pj_list_insert_after(route_pos, new_hdr);
 
410
            route_pos = new_hdr;
 
411
            route = route->next;
 
412
        }
 
413
    }
 
414
 
 
415
    /* Add Event header */
 
416
    hdr = pjsip_generic_string_hdr_create(tdata->pool, &STR_EVENT,
 
417
                                          &pubc->event);
 
418
    if (hdr)
 
419
        pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr);
 
420
 
 
421
 
 
422
    /* Add SIP-If-Match if we have etag */
 
423
    if (pubc->etag.slen) {
 
424
        const pj_str_t STR_HNAME = { "SIP-If-Match", 12 };
 
425
 
 
426
        hdr = pjsip_generic_string_hdr_create(tdata->pool, &STR_HNAME,
 
427
                                              &pubc->etag);
 
428
        if (hdr)
 
429
            pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr);
 
430
    }
 
431
 
 
432
    /* Add user headers */
 
433
    if (!pj_list_empty(&pubc->usr_hdr)) {
 
434
        const pjsip_hdr *hdr;
 
435
 
 
436
        hdr = pubc->usr_hdr.next;
 
437
        while (hdr != &pubc->usr_hdr) {
 
438
            pjsip_hdr *new_hdr = (pjsip_hdr*)
 
439
                                 pjsip_hdr_shallow_clone(tdata->pool, hdr);
 
440
            pjsip_msg_add_hdr(tdata->msg, new_hdr);
 
441
            hdr = hdr->next;
 
442
        }
 
443
    }
 
444
 
 
445
 
 
446
    /* Done. */
 
447
    *p_tdata = tdata;
 
448
    return PJ_SUCCESS;
 
449
}
 
450
 
 
451
 
 
452
PJ_DEF(pj_status_t) pjsip_publishc_publish(pjsip_publishc *pubc, 
 
453
                                           pj_bool_t auto_refresh,
 
454
                                           pjsip_tx_data **p_tdata)
 
455
{
 
456
    pj_status_t status;
 
457
    pjsip_tx_data *tdata;
 
458
 
 
459
    PJ_ASSERT_RETURN(pubc && p_tdata, PJ_EINVAL);
 
460
 
 
461
    status = create_request(pubc, &tdata);
 
462
    if (status != PJ_SUCCESS)
 
463
        return status;
 
464
 
 
465
    /* Add Expires header */
 
466
    if (pubc->expires_hdr) {
 
467
        pjsip_hdr *dup;
 
468
 
 
469
        dup = (pjsip_hdr*)
 
470
              pjsip_hdr_shallow_clone(tdata->pool, pubc->expires_hdr);
 
471
        if (dup)
 
472
            pjsip_msg_add_hdr(tdata->msg, dup);
 
473
    }
 
474
 
 
475
    /* Cancel existing timer */
 
476
    if (pubc->timer.id != 0) {
 
477
        pjsip_endpt_cancel_timer(pubc->endpt, &pubc->timer);
 
478
        pubc->timer.id = 0;
 
479
    }
 
480
 
 
481
    pubc->auto_refresh = auto_refresh;
 
482
 
 
483
    /* Done */
 
484
    *p_tdata = tdata;
 
485
    return PJ_SUCCESS;
 
486
}
 
487
 
 
488
 
 
489
PJ_DEF(pj_status_t) pjsip_publishc_unpublish(pjsip_publishc *pubc,
 
490
                                             pjsip_tx_data **p_tdata)
 
491
{
 
492
    pjsip_tx_data *tdata;
 
493
    pjsip_msg *msg;
 
494
    pjsip_expires_hdr *expires;
 
495
    pj_status_t status;
 
496
 
 
497
    PJ_ASSERT_RETURN(pubc && p_tdata, PJ_EINVAL);
 
498
 
 
499
    if (pubc->timer.id != 0) {
 
500
        pjsip_endpt_cancel_timer(pubc->endpt, &pubc->timer);
 
501
        pubc->timer.id = 0;
 
502
    }
 
503
 
 
504
    status = create_request(pubc, &tdata);
 
505
    if (status != PJ_SUCCESS)
 
506
        return status;
 
507
 
 
508
    msg = tdata->msg;
 
509
 
 
510
    /* Add Expires:0 header */
 
511
    expires = pjsip_expires_hdr_create(tdata->pool, 0);
 
512
    pjsip_msg_add_hdr( msg, (pjsip_hdr*)expires);
 
513
 
 
514
    *p_tdata = tdata;
 
515
    return PJ_SUCCESS;
 
516
}
 
517
 
 
518
 
 
519
PJ_DEF(pj_status_t) pjsip_publishc_update_expires( pjsip_publishc *pubc,
 
520
                                                   pj_uint32_t expires )
 
521
{
 
522
    PJ_ASSERT_RETURN(pubc, PJ_EINVAL);
 
523
    set_expires( pubc, expires );
 
524
    return PJ_SUCCESS;
 
525
}
 
526
 
 
527
 
 
528
static void call_callback(pjsip_publishc *pubc, pj_status_t status, 
 
529
                          int st_code, const pj_str_t *reason,
 
530
                          pjsip_rx_data *rdata, pj_int32_t expiration)
 
531
{
 
532
    struct pjsip_publishc_cbparam cbparam;
 
533
 
 
534
 
 
535
    cbparam.pubc = pubc;
 
536
    cbparam.token = pubc->token;
 
537
    cbparam.status = status;
 
538
    cbparam.code = st_code;
 
539
    cbparam.reason = *reason;
 
540
    cbparam.rdata = rdata;
 
541
    cbparam.expiration = expiration;
 
542
 
 
543
    (*pubc->cb)(&cbparam);
 
544
}
 
545
 
 
546
static void pubc_refresh_timer_cb( pj_timer_heap_t *timer_heap,
 
547
                                   struct pj_timer_entry *entry)
 
548
{
 
549
    pjsip_publishc *pubc = (pjsip_publishc*) entry->user_data;
 
550
    pjsip_tx_data *tdata;
 
551
    pj_status_t status;
 
552
    
 
553
    PJ_UNUSED_ARG(timer_heap);
 
554
 
 
555
    entry->id = 0;
 
556
    status = pjsip_publishc_publish(pubc, 1, &tdata);
 
557
    if (status != PJ_SUCCESS) {
 
558
        char errmsg[PJ_ERR_MSG_SIZE];
 
559
        pj_str_t reason = pj_strerror(status, errmsg, sizeof(errmsg));
 
560
        call_callback(pubc, status, 400, &reason, NULL, -1);
 
561
        return;
 
562
    }
 
563
 
 
564
    status = pjsip_publishc_send(pubc, tdata);
 
565
    /* No need to call callback as it should have been called */
 
566
}
 
567
 
 
568
static void tsx_callback(void *token, pjsip_event *event)
 
569
{
 
570
    pj_status_t status;
 
571
    pjsip_publishc *pubc = (pjsip_publishc*) token;
 
572
    pjsip_transaction *tsx = event->body.tsx_state.tsx;
 
573
    
 
574
    /* Decrement pending transaction counter. */
 
575
    pj_assert(pubc->pending_tsx > 0);
 
576
    --pubc->pending_tsx;
 
577
 
 
578
    /* Mark that we're in callback to prevent deletion (#1164) */
 
579
    ++pubc->in_callback;
 
580
 
 
581
    /* If publication data has been deleted by user then remove publication 
 
582
     * data from transaction's callback, and don't call callback.
 
583
     */
 
584
    if (pubc->_delete_flag) {
 
585
 
 
586
        /* Nothing to do */
 
587
        ;
 
588
 
 
589
    } else if (tsx->status_code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED ||
 
590
               tsx->status_code == PJSIP_SC_UNAUTHORIZED)
 
591
    {
 
592
        pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
 
593
        pjsip_tx_data *tdata;
 
594
 
 
595
        status = pjsip_auth_clt_reinit_req( &pubc->auth_sess,
 
596
                                            rdata, 
 
597
                                            tsx->last_tx,  
 
598
                                            &tdata);
 
599
        if (status != PJ_SUCCESS) {
 
600
            call_callback(pubc, status, tsx->status_code, 
 
601
                          &rdata->msg_info.msg->line.status.reason,
 
602
                          rdata, -1);
 
603
        } else {
 
604
            status = pjsip_publishc_send(pubc, tdata);
 
605
        }
 
606
 
 
607
    } else {
 
608
        pjsip_rx_data *rdata;
 
609
        pj_int32_t expiration = 0xFFFF;
 
610
 
 
611
        if (tsx->status_code/100 == 2) {
 
612
            pjsip_msg *msg;
 
613
            pjsip_expires_hdr *expires;
 
614
            pjsip_generic_string_hdr *etag_hdr;
 
615
            const pj_str_t STR_ETAG = { "SIP-ETag", 8 };
 
616
 
 
617
            rdata = event->body.tsx_state.src.rdata;
 
618
            msg = rdata->msg_info.msg;
 
619
 
 
620
            /* Save ETag value */
 
621
            etag_hdr = (pjsip_generic_string_hdr*)
 
622
                       pjsip_msg_find_hdr_by_name(msg, &STR_ETAG, NULL);
 
623
            if (etag_hdr) {
 
624
                pj_strdup(pubc->pool, &pubc->etag, &etag_hdr->hvalue);
 
625
            } else {
 
626
                pubc->etag.slen = 0;
 
627
            }
 
628
 
 
629
            /* Update expires value */
 
630
            expires = (pjsip_expires_hdr*)
 
631
                      pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
 
632
 
 
633
            if (pubc->auto_refresh && expires)
 
634
                expiration = expires->ivalue;
 
635
            
 
636
            if (pubc->auto_refresh && expiration!=0 && expiration!=0xFFFF) {
 
637
                pj_time_val delay = { 0, 0};
 
638
 
 
639
                /* Cancel existing timer, if any */
 
640
                if (pubc->timer.id != 0) {
 
641
                    pjsip_endpt_cancel_timer(pubc->endpt, &pubc->timer);
 
642
                    pubc->timer.id = 0;
 
643
                }
 
644
 
 
645
                delay.sec = expiration - DELAY_BEFORE_REFRESH;
 
646
                if (pubc->expires != PJSIP_PUBC_EXPIRATION_NOT_SPECIFIED && 
 
647
                    delay.sec > (pj_int32_t)pubc->expires) 
 
648
                {
 
649
                    delay.sec = pubc->expires;
 
650
                }
 
651
                if (delay.sec < DELAY_BEFORE_REFRESH) 
 
652
                    delay.sec = DELAY_BEFORE_REFRESH;
 
653
                pubc->timer.cb = &pubc_refresh_timer_cb;
 
654
                pubc->timer.id = REFRESH_TIMER;
 
655
                pubc->timer.user_data = pubc;
 
656
                pjsip_endpt_schedule_timer( pubc->endpt, &pubc->timer, &delay);
 
657
                pj_gettimeofday(&pubc->last_refresh);
 
658
                pubc->next_refresh = pubc->last_refresh;
 
659
                pubc->next_refresh.sec += delay.sec;
 
660
            }
 
661
 
 
662
        } else {
 
663
            rdata = (event->body.tsx_state.type==PJSIP_EVENT_RX_MSG) ? 
 
664
                        event->body.tsx_state.src.rdata : NULL;
 
665
        }
 
666
 
 
667
 
 
668
        /* Call callback. */
 
669
        if (expiration == 0xFFFF) expiration = -1;
 
670
 
 
671
        /* Temporarily increment pending_tsx to prevent callback from
 
672
         * destroying pubc.
 
673
         */
 
674
        ++pubc->pending_tsx;
 
675
 
 
676
        call_callback(pubc, PJ_SUCCESS, tsx->status_code, 
 
677
                      (rdata ? &rdata->msg_info.msg->line.status.reason 
 
678
                        : pjsip_get_status_text(tsx->status_code)),
 
679
                      rdata, expiration);
 
680
 
 
681
        --pubc->pending_tsx;
 
682
 
 
683
        /* If we have pending request(s), send them now */
 
684
        pj_mutex_lock(pubc->mutex);
 
685
        while (!pj_list_empty(&pubc->pending_reqs)) {
 
686
            pjsip_tx_data *tdata = pubc->pending_reqs.next;
 
687
            pj_list_erase(tdata);
 
688
 
 
689
            /* Add SIP-If-Match if we have etag and the request doesn't have
 
690
             * one (http://trac.pjsip.org/repos/ticket/996)
 
691
             */
 
692
            if (pubc->etag.slen) {
 
693
                const pj_str_t STR_HNAME = { "SIP-If-Match", 12 };
 
694
                pjsip_generic_string_hdr *sim_hdr;
 
695
 
 
696
                sim_hdr = (pjsip_generic_string_hdr*)
 
697
                          pjsip_msg_find_hdr_by_name(tdata->msg, &STR_HNAME, NULL);
 
698
                if (!sim_hdr) {
 
699
                    /* Create the header */
 
700
                    sim_hdr = pjsip_generic_string_hdr_create(tdata->pool,
 
701
                                                              &STR_HNAME,
 
702
                                                              &pubc->etag);
 
703
                    pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)sim_hdr);
 
704
 
 
705
                } else {
 
706
                    /* Update */
 
707
                    if (pj_strcmp(&pubc->etag, &sim_hdr->hvalue))
 
708
                        pj_strdup(tdata->pool, &sim_hdr->hvalue, &pubc->etag);
 
709
                }
 
710
            }
 
711
 
 
712
            status = pjsip_publishc_send(pubc, tdata);
 
713
            if (status == PJ_EPENDING) {
 
714
                pj_assert(!"Not expected");
 
715
                pj_list_erase(tdata);
 
716
                pjsip_tx_data_dec_ref(tdata);
 
717
            } else if (status == PJ_SUCCESS) {
 
718
                break;
 
719
            }
 
720
        }
 
721
        pj_mutex_unlock(pubc->mutex);
 
722
    }
 
723
 
 
724
    /* No longer in callback. */
 
725
    --pubc->in_callback;
 
726
 
 
727
    /* Delete the record if user destroy pubc during the callback. */
 
728
    if (pubc->_delete_flag && pubc->pending_tsx==0) {
 
729
        pjsip_publishc_destroy(pubc);
 
730
    }
 
731
}
 
732
 
 
733
 
 
734
PJ_DEF(pj_status_t) pjsip_publishc_send(pjsip_publishc *pubc, 
 
735
                                        pjsip_tx_data *tdata)
 
736
{
 
737
    pj_status_t status;
 
738
    pjsip_cseq_hdr *cseq_hdr;
 
739
    pj_uint32_t cseq;
 
740
 
 
741
    PJ_ASSERT_RETURN(pubc && tdata, PJ_EINVAL);
 
742
 
 
743
    /* Make sure we don't have pending transaction. */
 
744
    pj_mutex_lock(pubc->mutex);
 
745
    if (pubc->pending_tsx) {
 
746
        if (pubc->opt.queue_request) {
 
747
            pj_list_push_back(&pubc->pending_reqs, tdata);
 
748
            pj_mutex_unlock(pubc->mutex);
 
749
            PJ_LOG(4,(THIS_FILE, "Request is queued, pubc has another "
 
750
                                 "transaction pending"));
 
751
            return PJ_EPENDING;
 
752
        } else {
 
753
            pjsip_tx_data_dec_ref(tdata);
 
754
            pj_mutex_unlock(pubc->mutex);
 
755
            PJ_LOG(4,(THIS_FILE, "Unable to send request, pubc has another "
 
756
                                 "transaction pending"));
 
757
            return PJ_EBUSY;
 
758
        }
 
759
    }
 
760
    pj_mutex_unlock(pubc->mutex);
 
761
 
 
762
    /* If via_addr is set, use this address for the Via header. */
 
763
    if (pubc->via_addr.host.slen > 0) {
 
764
        tdata->via_addr = pubc->via_addr;
 
765
        tdata->via_tp = pubc->via_tp;
 
766
    }
 
767
 
 
768
    /* Invalidate message buffer. */
 
769
    pjsip_tx_data_invalidate_msg(tdata);
 
770
 
 
771
    /* Increment CSeq */
 
772
    cseq = ++pubc->cseq_hdr->cseq;
 
773
    cseq_hdr = (pjsip_cseq_hdr*)
 
774
               pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
 
775
    cseq_hdr->cseq = cseq;
 
776
 
 
777
    /* Increment pending transaction first, since transaction callback
 
778
     * may be called even before send_request() returns!
 
779
     */
 
780
    ++pubc->pending_tsx;
 
781
    status = pjsip_endpt_send_request(pubc->endpt, tdata, -1, pubc, 
 
782
                                      &tsx_callback);
 
783
    if (status!=PJ_SUCCESS) {
 
784
        // no need to decrement, callback has been called and it should
 
785
        // already decremented pending_tsx. Decrementing this here may 
 
786
        // cause accessing freed memory location.
 
787
        //--pubc->pending_tsx;
 
788
        PJ_LOG(4,(THIS_FILE, "Error sending request, status=%d", status));
 
789
    }
 
790
 
 
791
    return status;
 
792
}
 
793