/*
Copyright (C) 2005-2007 Michel de Boer <michel@twinklephone.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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "sub_refer.h"
#include "line.h"
#include "log.h"
#include "phone_user.h"
#include "user.h"
#include "userintf.h"
#include "audits/memman.h"
t_dialog *t_sub_refer::get_dialog(void) const {
return dynamic_cast<t_dialog *>(dialog);
}
t_sub_refer::t_sub_refer(t_dialog *_dialog, t_subscription_role _role) :
t_subscription(_dialog, _role, SIP_EVENT_REFER)
{
// A refer subscription is implicitly defined by the REFER
// transaction
state = SS_ESTABLISHED;
// Start the subscription timer only for a notifier.
// The subscriber will start a timer when it receives NOTIFY.
if (role == SR_NOTIFIER) {
unsigned long dur;
if (user_config->get_ask_user_to_refer()) {
dur = DUR_REFER_SUB_INTERACT * 1000;
} else {
dur = DUR_REFER_SUBSCRIPTION * 1000;
}
start_timer(STMR_SUBSCRIPTION, dur);
}
auto_refresh = user_config->get_auto_refresh_refer_sub();
subscription_expiry = DUR_REFER_SUBSCRIPTION;
sr_result = SRR_INPROG;
last_response = NULL;
log_file->write_header("t_sub_refer::t_sub_refer");
log_file->write_raw("Refer ");
if (role == SR_SUBSCRIBER) {
log_file->write_raw("subscriber");
} else {
log_file->write_raw("notifier");
}
log_file->write_raw(" created: event = ");
log_file->write_raw(event_type);
log_file->write_endl();
log_file->write_footer();
}
t_sub_refer::t_sub_refer(t_dialog *_dialog, t_subscription_role _role,
const string &_event_id) :
t_subscription(_dialog, _role, SIP_EVENT_REFER, _event_id)
{
state = SS_ESTABLISHED;
if (role == SR_NOTIFIER) {
unsigned long dur;
if (user_config->get_ask_user_to_refer()) {
dur = DUR_REFER_SUB_INTERACT * 1000;
} else {
dur = DUR_REFER_SUBSCRIPTION * 1000;
}
start_timer(STMR_SUBSCRIPTION, dur);
}
auto_refresh = user_config->get_auto_refresh_refer_sub();
subscription_expiry = DUR_REFER_SUBSCRIPTION;
sr_result = SRR_INPROG;
last_response = NULL;
log_file->write_header("t_sub_refer::t_sub_refer");
log_file->write_raw("Refer ");
if (role == SR_SUBSCRIBER) {
log_file->write_raw("subscriber");
} else {
log_file->write_raw("notifier");
}
log_file->write_raw(" created: event = ");
log_file->write_raw(event_type);
log_file->write_raw(";id=");
log_file->write_raw(event_id);
log_file->write_endl();
log_file->write_footer();
}
t_sub_refer::~t_sub_refer() {
if (last_response) {
MEMMAN_DELETE(last_response);
delete last_response;
}
log_file->write_header("t_sub_refer::~t_sub_refer");
log_file->write_raw("Refer ");
if (role == SR_SUBSCRIBER) {
log_file->write_raw("subscriber");
} else {
log_file->write_raw("notifier");
}
log_file->write_raw(" destroyed: event = ");
log_file->write_raw(event_type);
if (!event_id.empty()) {
log_file->write_raw(";id=");
log_file->write_raw(event_id);
}
log_file->write_endl();
log_file->write_footer();
}
void t_sub_refer::send_notify(t_response *r, const string &substate,
const string reason)
{
t_request *notify;
if (substate == SUBSTATE_TERMINATED) {
// RFC 3515 2.4.7
notify = create_notify(substate, reason);
stop_timer(STMR_SUBSCRIPTION);
} else {
notify = create_notify(substate);
}
// RFC 3515 2.4.4
// Create message/sipfrag body containing only the status line
// of the response.
t_response sipfrag(r->code, r->reason);
notify->body = new t_sip_body_sipfrag(&sipfrag);
MEMMAN_NEW(notify->body);
notify->hdr_content_type.set_media(t_media("message", "sipfrag"));
// If an outgoing NOTIFY is still pending, then store this
// NOTIFY in the queue
if (req_out) {
queue_notify.push(notify);
} else {
// Send NOTIFY
req_out = new t_client_request(user_config, notify,0);
MEMMAN_NEW(req_out);
send_request(user_config, notify, req_out->get_tuid());
MEMMAN_DELETE(notify);
delete notify;
}
// Keep response and state such that it can be resend when
// a SUBSCRIBE is received.
if (last_response && last_response != r) {
MEMMAN_DELETE(last_response);
delete last_response;
last_response = NULL;
}
if (!last_response) last_response = (t_response *)r->copy();
current_substate = substate;
}
bool t_sub_refer::recv_notify(t_request *r, t_tuid tuid, t_tid tid) {
if (t_subscription::recv_notify(r, tuid, tid)) return true;
// RFC 3515 2.4.5.
// NOTIFY must have a sipfrag body
if (!r->body || r->body->get_type() != BODY_SIPFRAG) {
t_response *resp = r->create_response(R_400_BAD_REQUEST,
"message/sipfrag body missing");
send_response(user_config, resp, 0, tid);
MEMMAN_DELETE(resp);
delete resp;
return true;
}
// RFC 3515 2.4.5
// The sipfrag body must start with a Status-Line
if (((t_sip_body_sipfrag *)r->body)->sipfrag->get_type() != MSG_RESPONSE) {
t_response *resp = r->create_response(R_400_BAD_REQUEST,
"sipfrag body does not begin with Status-Line");
send_response(user_config, resp, 0, tid);
MEMMAN_DELETE(resp);
delete resp;
return true;
}
t_response *sipfrag = (t_response *)((t_sip_body_sipfrag *)r->body)->sipfrag;
// Determine state of reference
if (r->hdr_subscription_state.substate == SUBSTATE_TERMINATED) {
if (r->hdr_subscription_state.reason == EV_REASON_REJECTED) {
// Referee rejected to refer
sr_result = SRR_FAILED;
} else if (r->hdr_subscription_state.reason == EV_REASON_NORESOURCE) {
// Reference is finished. The sipfrag body indicates
// success or failure.
if (sipfrag->is_success()) {
sr_result = SRR_SUCCEEDED;
} else {
sr_result = SRR_FAILED;
}
}
}
// Inform user about progress
ui->cb_notify_recvd(get_dialog()->get_line()->get_line_number(), r);
t_response *resp = r->create_response(R_200_OK);
send_response(user_config, resp, 0, tid);
MEMMAN_DELETE(resp);
delete resp;
return true;
}
bool t_sub_refer::recv_subscribe(t_request *r, t_tuid tuid, t_tid tid) {
unsigned long expires;
if (t_subscription::recv_subscribe(r, tuid, tid)) return true;
// Determine value for Expires header
if (!r->hdr_expires.is_populated() ||
r->hdr_expires.time > 2 * DUR_REFER_SUBSCRIPTION)
{
// User did not indicate an expiry time for subscription
// refresh or a time larger then 2 times the default.
// Just use the Twinkle default.
stop_timer(STMR_SUBSCRIPTION);
start_timer(STMR_SUBSCRIPTION, DUR_REFER_SUBSCRIPTION * 1000);
expires = DUR_REFER_SUBSCRIPTION;
} else {
expires = r->hdr_expires.time;
}
t_response *resp = r->create_response(R_200_OK);
// RFC 3265 7.1
// Contact header is mandatory
t_contact_param contact;
contact.uri.set_url(get_dialog()->get_line()->create_user_contact());
resp->hdr_contact.add_contact(contact);
// Expires header is mandatory
resp->hdr_expires.set_time(expires);
send_response(user_config, resp, 0, tid);
MEMMAN_DELETE(resp);
delete resp;
// RFC 3265 3.2.2
// After a succesful SUBSCRIBE the notifier must immediately
// send a NOTIFY.
// If no last response has been kept then this is probably a
// first SUBSCRIBE. The dialog has to send the initial NOTIFY.
if (last_response) {
if (expires == 0) {
send_notify(last_response, SUBSTATE_TERMINATED,
EV_REASON_TIMEOUT);
} else {
send_notify(last_response, current_substate);
}
}
return true;
}
bool t_sub_refer::timeout(t_subscribe_timer timer) {
t_request *r;
if (t_subscription::timeout(timer)) return true;
switch (timer) {
case STMR_SUBSCRIPTION:
switch (role) {
case SR_NOTIFIER:
// RFC 3265 3.1.6.4
// The subscription has expired
// RFC 2.4.5
// Each NOTIFY MUST contain a body
if (last_response) {
// Repeat last response as body
send_notify(last_response, SUBSTATE_TERMINATED,
EV_REASON_TIMEOUT);
} else {
// This should never happen. Create a timeout
// response for the body.
t_response resp(R_408_REQUEST_TIMEOUT);
send_notify(&resp, SUBSTATE_TERMINATED,
EV_REASON_TIMEOUT);
}
log_file->write_report("Refer notifier timed out.",
"t_sub_refer::timeout");
return true;
case SR_SUBSCRIBER:
// Should have been handled by parent class
default:
assert(false);
}
break;
default:
assert(false);
}
return false;
}
t_sub_refer_result t_sub_refer::get_sr_result(void) const {
return sr_result;
}
syntax highlighted by Code2HTML, v. 0.9.1