summaryrefslogtreecommitdiffstats
path: root/src/sub_refer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/sub_refer.cpp')
-rw-r--r--src/sub_refer.cpp324
1 files changed, 324 insertions, 0 deletions
diff --git a/src/sub_refer.cpp b/src/sub_refer.cpp
new file mode 100644
index 0000000..f520575
--- /dev/null
+++ b/src/sub_refer.cpp
@@ -0,0 +1,324 @@
+/*
+ Copyright (C) 2005-2009 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(
+ h_ip2str(resp->get_local_ip())));
+ 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) {
+ 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;
+}