/*
* Presence Agent, subscribe handling
*
* $Id: subscribe.c 30 2005-06-16 12:42:16Z bogdan_iancu $
*
* Copyright (C) 2001-2003 FhG Fokus
*
* This file is part of openser, a free SIP server.
*
* openser 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 2 of the License, or
* (at your option) any later version
*
* openser 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* History:
* ---------
* 2003-02-29 scratchpad compatibility abandoned
* 2003-01-27 next baby-step to removing ZT - PRESERVE_ZT (jiri)
*/
#include <string.h>
#include "../../str.h"
#include "../../dprint.h"
#include "../../mem/mem.h"
#include "../../parser/parse_uri.h"
#include "../../parser/parse_from.h"
#include "../../parser/parse_expires.h"
#include "../../parser/parse_event.h"
#include "../../parser/parse_content.h"
#include "presentity.h"
#include "watcher.h"
#include "pstate.h"
#include "notify.h"
#include "paerrno.h"
#include "pdomain.h"
#include "pa_mod.h"
#include "ptime.h"
#include "reply.h"
#include "subscribe.h"
#define DOCUMENT_TYPE "application/cpim-pidf+xml"
#define DOCUMENT_TYPE_L (sizeof(DOCUMENT_TYPE) - 1)
static struct {
int event_type;
int mimes[MAX_MIMES_NR];
} event_package_mimetypes[] = {
{ EVENT_PRESENCE, { MIMETYPE(APPLICATION,PIDFXML),
#ifdef SUBTYPE_XML_MSRTC_PIDF
MIMETYPE(APPLICATION,XML_MSRTC_PIDF),
#endif
MIMETYPE(APPLICATION,XPIDFXML), MIMETYPE(APPLICATION,LPIDFXML), 0 } },
{ EVENT_PRESENCE_WINFO, { MIMETYPE(APPLICATION,WATCHERINFOXML), 0 }},
#ifdef EVENT_SIP_PROFILE
{ EVENT_SIP_PROFILE, { MIMETYPE(MESSAGE,EXTERNAL_BODY), 0 }},
#endif
// { EVENT_XCAP_CHANGE, { MIMETYPE(APPLICATION,WINFO+XML), 0 } },
{ -1, { 0 }},
};
/*
* contact will be NULL if user is offline
* fixme:locking
*/
void callback(str* _user, str *_contact, int state, void* data)
{
presentity_t *presentity;
get_act_time();
presentity = (struct presentity*)data;
if (presentity && callback_update_db) {
presence_tuple_t *tuple = NULL;
int orig;
LOG(L_ERR, "callback: uri=%.*s contact=%.*s state=%d\n",
presentity->uri.len, presentity->uri.s, (_contact ? _contact->len : 0), (_contact ? _contact->s : ""), state);
if (_contact) {
if (callback_lock_pdomain)
lock_pdomain(presentity->pdomain);
find_presence_tuple(_contact, presentity, &tuple);
if (!tuple) {
new_presence_tuple(_contact, act_time + default_expires, presentity, &tuple);
add_presence_tuple(presentity, tuple);
};
orig = tuple->state;
if (state == 0) {
tuple->state = PS_OFFLINE;
} else {
tuple->state = PS_ONLINE;
}
tuple->expires = act_time + default_expires;
db_update_presentity(presentity);
if (orig != state) {
presentity->flags |= PFLAG_PRESENCE_CHANGED;
}
if (callback_lock_pdomain)
unlock_pdomain(presentity->pdomain);
}
}
}
/*
* Extract plain uri -- return URI without parameters
* The uri will be in form username@domain
*
*/
static int extract_plain_uri(str* _uri)
{
struct sip_uri puri;
if (parse_uri(_uri->s, _uri->len, &puri) < 0) {
paerrno = PA_URI_PARSE;
LOG(L_ERR, "extract_plain_uri(): Error while parsing URI\n");
return -1;
}
_uri->s = puri.user.s;
_uri->len = puri.host.s + puri.host.len - _uri->s;
return 0;
}
/*
* Get presentity URI, which is stored in R-URI
*/
int get_pres_uri(struct sip_msg* _m, str* _puri)
{
if (_m->new_uri.s) {
_puri->s = _m->new_uri.s;
_puri->len = _m->new_uri.len;
} else {
_puri->s = _m->first_line.u.request.uri.s;
_puri->len = _m->first_line.u.request.uri.len;
}
LOG(L_ERR, "get_pres_uri: _puri=%.*s\n", _puri->len, _puri->s);
if (extract_plain_uri(_puri) < 0) {
LOG(L_ERR, "get_pres_uri(): Error while extracting plain URI\n");
return -1;
}
return 0;
}
static int get_watch_uri(struct sip_msg* _m, str* _wuri, str *_dn)
{
_wuri->s = get_from(_m)->uri.s;
_wuri->len = get_from(_m)->uri.len;
_dn->s = get_from(_m)->body.s;
_dn->len = get_from(_m)->body.len;
if (extract_plain_uri(_wuri) < 0) {
LOG(L_ERR, "get_watch_uri(): Error while extracting plain URI\n");
return -1;
}
return 0;
}
/*
* Parse all header fields that will be needed
* to handle a SUBSCRIBE request
*/
static int parse_hfs(struct sip_msg* _m, int accept_header_required)
{
int rc = 0;
if ( ((rc = parse_headers(_m, HDR_FROM_F | HDR_EVENT_F | HDR_EXPIRES_F | HDR_ACCEPT_F, 0)) == -1)
|| (_m->from==0) || (_m->event==0) ) {
paerrno = PA_PARSE_ERR;
LOG(L_ERR, "parse_hfs(): Error while parsing headers: rc=%d\n", rc);
return -1;
}
if (parse_from_header(_m) < 0) {
paerrno = PA_FROM_ERR;
LOG(L_ERR, "parse_hfs(): From malformed or missing\n");
return -6;
}
if (_m->event) {
if (parse_event(_m->event) < 0) {
paerrno = PA_EVENT_PARSE;
LOG(L_ERR, "parse_hfs(): Error while parsing Event header field\n");
return -8;
}
}
if (_m->expires) {
if (parse_expires(_m->expires) < 0) {
paerrno = PA_EXPIRES_PARSE;
LOG(L_ERR, "parse_hfs(): Error while parsing Expires header field\n");
return -9;
}
}
/* now look for Accept header */
if (_m->accept) {
LOG(L_ERR, "parsing accept header\n");
if (parse_accept_hdr(_m) < 0) {
paerrno = PA_ACCEPT_PARSE;
LOG(L_ERR, "parse_hfs(): Error while parsing Accept header field\n");
return -10;
}
} else if (accept_header_required) {
LOG(L_ERR, "no accept header\n");
return -11;
}
return 0;
}
/*
* Check if a message received has been constructed properly
*/
int check_message(struct sip_msg* _m)
{
LOG(L_ERR, "check_message -0- _m=%p\n", _m);
if (_m->event) {
event_t *parsed_event;
int *accepts_mimes = NULL;
LOG(L_ERR, "check_message -1-");
if (_m->accept) {
accepts_mimes = get_accept(_m);
if (accepts_mimes) {
char buf[100];
int offset = 0;
int *a = accepts_mimes;
buf[0] = '0';
while (*a) {
offset += sprintf(buf+offset, ":%#06x", *a);
a++;
}
LOG(L_ERR, "pa check_message: accept=%.*s parsed=%s\n",
_m->accept->body.len, _m->accept->body.s, buf);
}
}
LOG(L_ERR, "check_message -2- accepts_mimes=%p\n", accepts_mimes);
if (!_m->event->parsed)
parse_event(_m->event);
LOG(L_ERR, "check_message -3-\n");
parsed_event = (event_t*)(_m->event->parsed);
LOG(L_ERR, "check_message -4- parsed_event=%p\n", parsed_event);
if (parsed_event && accepts_mimes) {
int i = 0;
int eventtype = parsed_event->parsed;
LOG(L_ERR, "check_message -4- eventtype=%#06x\n", eventtype);
while (event_package_mimetypes[i].event_type != -1) {
LOG(L_ERR, "check_message -4a- eventtype=%#x epm[i].event_type=%#x",
eventtype, event_package_mimetypes[i].event_type);
if (eventtype == event_package_mimetypes[i].event_type) {
int j = 0;
int mimetype;
while ((mimetype = event_package_mimetypes[i].mimes[j]) != 0) {
int k = 0;
while (accepts_mimes[k]) {
LOG(L_ERR, "check_message -4c- eventtype=%#x mimetype=%#x accepts_mimes[k]=%#x\n", eventtype, mimetype, accepts_mimes[k]);
if (accepts_mimes[k] == mimetype) {
int am0 = accepts_mimes[0];
/* we have a match */
LOG(L_ERR, "check_message -4b- eventtype=%#x accepts_mime=%#x\n", eventtype, mimetype);
/* move it to front for later */
accepts_mimes[0] = mimetype;
accepts_mimes[k] = am0;
return 0;
}
k++;
}
j++;
}
}
i++;
}
/* else, none of the mimetypes accepted are generated for this event package */
{
char *accept_s = NULL;
int accept_len = 0;
if (_m->accept && _m->accept->body.len) {
accept_s = _m->accept->body.s;
accept_len = _m->accept->body.len;
}
LOG(L_ERR, "check_message(): Accepts %.*s not valid for event package et=%.*s\n",
_m->accept->body.len, _m->accept->body.s, _m->event->body.len, _m->event->body.s);
return -1;
}
}
LOG(L_ERR, "check_message -5-\n");
}
return 0;
}
int get_preferred_event_mimetype(struct sip_msg *_m, int et)
{
int acc = 0;
if (_m->accept) {
//LOG(L_ERR, "%s: has accept header\n", __FUNCTION__);
int *accepts_mimes = get_accept(_m);
acc = accepts_mimes[0];
} else {
int i = 0;
//LOG(L_ERR, "%s: no accept header\n", __FUNCTION__);
while (event_package_mimetypes[i].event_type != -1) {
if (event_package_mimetypes[i].event_type == et) {
acc = event_package_mimetypes[i].mimes[0];
LOG(L_ERR, "%s: defaulting to mimetype %x for event_type=%d\n", __FUNCTION__, acc, et);
break;
}
i++;
}
}
return acc;
}
/*
* Create a new presentity and corresponding watcher list
*/
int create_presentity(struct sip_msg* _m, struct pdomain* _d, str* _puri,
struct presentity** _p, struct watcher** _w)
{
time_t e;
dlg_t* dialog;
str watch_uri;
str watch_dn;
event_t *event = NULL;
int et = 0;
int acc = 0;
if (_m->event) {
event = (event_t*)(_m->event->parsed);
et = event->parsed;
} else {
et = EVENT_PRESENCE;
}
acc = get_preferred_event_mimetype(_m, et);
if (_m->expires) {
e = ((exp_body_t*)_m->expires->parsed)->val;
} else {
e = default_expires;
}
if (e == 0) {
*_p = 0;
*_w = 0;
DBG("create_presentity(): expires = 0\n");
return 0;
}
/* Convert to absolute time */
e += act_time;
if (get_watch_uri(_m, &watch_uri, &watch_dn) < 0) {
LOG(L_ERR, "create_presentity(): Error while extracting watcher URI\n");
return -1;
}
if (new_presentity(_d, _puri, _p) < 0) {
LOG(L_ERR, "create_presentity(): Error while creating presentity\n");
return -2;
}
if (tmb.new_dlg_uas(_m, 200, &dialog) < 0) {
paerrno = PA_DIALOG_ERR;
LOG(L_ERR, "create_presentity(): Error while creating dialog state\n");
free_presentity(*_p);
return -3;
}
if (et != EVENT_PRESENCE_WINFO) {
if (add_watcher(*_p, &watch_uri, e, et, acc, dialog, &watch_dn, _w) < 0) {
LOG(L_ERR, "create_presentity(): Error while adding a watcher\n");
tmb.free_dlg(dialog);
free_presentity(*_p);
return -4;
}
} else if (et == EVENT_PRESENCE_WINFO) {
if (add_winfo_watcher(*_p, &watch_uri, e, et, acc, dialog, &watch_dn, _w) < 0) {
LOG(L_ERR, "create_presentity(): Error while adding a winfo watcher\n");
tmb.free_dlg(dialog);
free_presentity(*_p);
return -5;
}
}
add_presentity(_d, *_p);
_d->reg(&watch_uri, _puri, (void*)callback, *_p);
return 0;
}
/*
* Update existing presentity and watcher list
*/
static int update_presentity(struct sip_msg* _m, struct pdomain* _d,
struct presentity* _p, struct watcher** _w)
{
time_t e;
dlg_t* dialog;
str watch_uri;
str watch_dn;
event_t *event = NULL;
int et = 0;
int acc = 0;
if (_m->event) {
event = (event_t*)(_m->event->parsed);
et = event->parsed;
} else {
LOG(L_ERR, "update_presentity defaulting to EVENT_PRESENCE\n");
et = EVENT_PRESENCE;
}
acc = get_preferred_event_mimetype(_m, et);
if (_m->expires) {
e = ((exp_body_t*)_m->expires->parsed)->val;
} else {
e = default_expires;
}
if (get_watch_uri(_m, &watch_uri, &watch_dn) < 0) {
LOG(L_ERR, "update_presentity(): Error while extracting watcher URI\n");
return -1;
}
if (find_watcher(_p, &watch_uri, et, _w) == 0) {
LOG(L_ERR, "update_presentity() found watcher\n");
if (e == 0) {
if (et != EVENT_PRESENCE_WINFO) {
if (remove_watcher(_p, *_w) < 0) {
LOG(L_ERR, "update_presentity(): Error while deleting winfo watcher\n");
return -2;
}
} else {
if (remove_winfo_watcher(_p, *_w) < 0) {
LOG(L_ERR, "update_presentity(): Error while deleting winfo watcher\n");
return -2;
}
}
(*_w)->expires = 0; /* The watcher will be freed after NOTIFY is sent */
if (!_p->watchers && !_p->winfo_watchers) {
remove_presentity(_d, _p);
}
} else {
e += act_time;
if (update_watcher(*_w, e) < 0) {
LOG(L_ERR, "update_presentity(): Error while updating watcher\n");
return -3;
}
}
} else {
if (e) {
e += act_time;
if (tmb.new_dlg_uas(_m, 200, &dialog) < 0) {
paerrno = PA_DIALOG_ERR;
LOG(L_ERR, "update_presentity(): Error while creating dialog state\n");
return -4;
}
if (et != EVENT_PRESENCE_WINFO) {
if (add_watcher(_p, &watch_uri, e, et, acc, dialog, &watch_dn, _w) < 0) {
LOG(L_ERR, "update_presentity(): Error while creating presentity\n");
tmb.free_dlg(dialog);
return -5;
}
} else {
if (add_winfo_watcher(_p, &watch_uri, e, et, acc, dialog, &watch_dn, _w) < 0) {
LOG(L_ERR, "update_presentity(): Error while creating winfo watcher\n");
tmb.free_dlg(dialog);
return -5;
}
}
} else {
DBG("update_presentity(): expires = 0 but no watcher found\n");
*_w = 0;
}
}
return 0;
}
/*
* Handle a registration request -- make sure aor exists in presentity table
*/
/*
* Extract Address of Record
*/
#define MAX_AOR_LEN 256
int pa_extract_aor(str* _uri, str* _a)
{
static char aor_buf[MAX_AOR_LEN];
struct sip_uri puri;
int user_len;
if (parse_uri(_uri->s, _uri->len, &puri) < 0) {
LOG(L_ERR, "pa_extract_aor(): Error while parsing Address of Record\n");
return -1;
}
if ((puri.user.len + puri.host.len + 1) > MAX_AOR_LEN) {
LOG(L_ERR, "pa_extract_aor(): Address Of Record too long\n");
return -2;
}
_a->s = aor_buf;
_a->len = puri.user.len;
user_len = _a->len;
memcpy(aor_buf, puri.user.s, puri.user.len);
aor_buf[_a->len] = '@';
memcpy(aor_buf + _a->len + 1, puri.host.s, puri.host.len);
_a->len += 1 + puri.host.len;
#if 0
if (case_sensitive) {
tmp.s = _a->s + user_len + 1;
tmp.len = puri.host.len;
strlower(&tmp);
} else {
strlower(_a);
}
#endif
return 0;
}
int pa_handle_registration(struct sip_msg* _m, char* _domain, char* _s2)
{
struct pdomain* d = (struct pdomain*)_domain;
struct presentity *presentity;
str p_uri;
struct to_body *from = NULL;
int e = 0;
// LOG(L_ERR, "pa_handle_registration() entered\n");
paerrno = PA_OK;
d = (struct pdomain*)_domain;
if (parse_hfs(_m, 0) < 0) {
paerrno = PA_PARSE_ERR;
LOG(L_ERR, "pa_handle_registration(): Error while parsing headers\n");
return -1;
}
from = get_from(_m);
if (!from || (pa_extract_aor(&from->uri, &p_uri) < 0)) {
LOG(L_ERR, "pa_handle_registration(): Error while extracting Address Of Record\n");
goto error;
}
if (_m->expires) {
e = ((exp_body_t*)_m->expires->parsed)->val;
} else {
e = default_expires;
}
if (from)
LOG(L_ERR, "pa_handle_registration: from=%.*s p_uri=%.*s expires=%d\n",
from->uri.len, from->uri.s, p_uri.len, p_uri.s, e);
lock_pdomain(d);
if (find_presentity(d, &p_uri, &presentity) > 0) {
LOG(L_ERR, "pa_handle_registration: find_presentity did not find presentity\n");
if (e > 0) {
if (create_presentity_only(_m, d, &p_uri, &presentity) < 0) {
LOG(L_ERR, "pa_handle_registration(): Error while creating new presentity\n");
goto error2;
}
}
#if 0
else {
presence_tuple_t *tuple = NULL;
if (_m->contact) {
struct hdr_field* ptr = _m->contact;
while (ptr) {
if (ptr->type == HDR_CONTACT_T) {
if (!ptr->parsed && (parse_contact(ptr) < 0)) {
goto next;
}
}
if (find_presence_tuple(contact, presentity, &tuple) == 0) {
tuple->state = PS_OFFLINE;
}
next:
ptr = ptr->next;
}
}
db_update_presentity(presentity);
}
#endif
}
if (presentity && e > 0) {
LOG(L_ERR, "pa_handle_registration about to call d->reg p=%p expires=%d", presentity, e);
d->reg(&presentity->uri, &presentity->uri, (void*)callback, presentity);
}
LOG(L_ERR, "pa_handle_registration about to return 1");
unlock_pdomain(d);
return 1;
error2:
LOG(L_ERR, "pa_handle_registration about to return -1\n");
unlock_pdomain(d);
return -1;
error:
LOG(L_ERR, "pa_handle_registration about to return -2\n");
return -1;
}
/*
* Handle a subscribe Request
*/
int handle_subscription(struct sip_msg* _m, char* _domain, char* _s2)
{
struct pdomain* d;
struct presentity *p;
struct watcher* w;
str p_uri;
LOG(L_ERR, "handle_subscription() entered\n");
get_act_time();
paerrno = PA_OK;
if (parse_hfs(_m, 0) < 0) {
LOG(L_ERR, "handle_subscription(): Error while parsing message header\n");
goto error;
}
if (check_message(_m) < 0) {
LOG(L_ERR, "handle_subscription(): Error while checking message\n");
goto error;
}
d = (struct pdomain*)_domain;
if (get_pres_uri(_m, &p_uri) < 0) {
LOG(L_ERR, "handle_subscription(): Error while extracting presentity URI\n");
goto error;
}
lock_pdomain(d);
LOG(L_ERR, "handle_subscription(): -1-\n");
if (find_presentity(d, &p_uri, &p) > 0) {
LOG(L_ERR, "handle_subscription(): -2-\n");
if (create_presentity(_m, d, &p_uri, &p, &w) < 0) {
LOG(L_ERR, "handle_subscription(): Error while creating new presentity\n");
goto error2;
}
} else {
LOG(L_ERR, "handle_subscription(): -3-\n");
if (update_presentity(_m, d, p, &w) < 0) {
LOG(L_ERR, "handle_subscription(): Error while updating presentity\n");
goto error2;
}
}
if (send_reply(_m) < 0) {
LOG(L_ERR, "handle_subscription(): Error while sending reply\n");
goto error2;
}
if (p) {
p->flags |= PFLAG_WATCHERINFO_CHANGED;
}
if (w) {
w->flags |= WFLAG_SUBSCRIPTION_CHANGED;
}
LOG(L_ERR, "handle_subscription about to return 1: w->event_package=%d w->accept=%d p->flags=%x w->flags=%x w=%p\n",
(w ? w->event_package : -1), (w ? w->preferred_mimetype : -1), (p ? p->flags : -1), (w ? w->flags : -1), w);
unlock_pdomain(d);
return 1;
error2:
LOG(L_ERR, "handle_subscription about to return -1\n");
unlock_pdomain(d);
return -1;
error:
LOG(L_ERR, "handle_subscription about to send_reply and return -2\n");
send_reply(_m);
return -1;
}
/*
* Returns 1 if subscription exists and -1 if not
*/
int existing_subscription(struct sip_msg* _m, char* _domain, char* _s2)
{
struct pdomain* d;
struct presentity* p;
struct watcher* w;
str p_uri, w_uri;
str w_dn;
int et = 0;
if (_m->event) {
event_t *event = (event_t*)(_m->event->parsed);
et = event->parsed;
} else {
LOG(L_ERR, "existing_subscription defaulting to EVENT_PRESENCE\n");
et = EVENT_PRESENCE;
}
paerrno = PA_OK;
if (parse_from_header(_m) < 0) {
paerrno = PA_PARSE_ERR;
LOG(L_ERR, "existing_subscription(): Error while parsing From header field\n");
goto error;
}
d = (struct pdomain*)_domain;
if (get_pres_uri(_m, &p_uri) < 0) {
LOG(L_ERR, "existing_subscription(): Error while extracting presentity URI\n");
goto error;
}
if (get_watch_uri(_m, &w_uri, &w_dn) < 0) {
LOG(L_ERR, "existing_subscription(): Error while extracting watcher URI\n");
goto error;
}
lock_pdomain(d);
if (find_presentity(d, &p_uri, &p) == 0) {
if (find_watcher(p, &w_uri, et, &w) == 0) {
LOG(L_ERR, "existing_subscription() found watcher\n");
unlock_pdomain(d);
return 1;
}
}
unlock_pdomain(d);
return -1;
error:
send_reply(_m);
return 0;
}
/*
* Returns 1 if possibly a user agent can handle SUBSCRIBE
* itself, 0 if it cannot for sure
*/
int pua_exists(struct sip_msg* _m, char* _domain, char* _s2)
{
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1