summaryrefslogtreecommitdiffstats
path: root/src/transaction_layer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/transaction_layer.cpp')
-rw-r--r--src/transaction_layer.cpp315
1 files changed, 315 insertions, 0 deletions
diff --git a/src/transaction_layer.cpp b/src/transaction_layer.cpp
new file mode 100644
index 0000000..d298261
--- /dev/null
+++ b/src/transaction_layer.cpp
@@ -0,0 +1,315 @@
+/*
+ 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 <assert.h>
+#include <iostream>
+#include "events.h"
+#include "transaction_layer.h"
+#include "userintf.h"
+#include "util.h"
+#include "audits/memman.h"
+
+extern t_event_queue *evq_trans_mgr;
+extern t_event_queue *evq_trans_layer;
+extern bool end_app;
+
+void t_transaction_layer::recvd_response(t_response *r, t_tuid tuid,
+ t_tid tid)
+{
+ lock();
+
+ switch(r->get_class()) {
+ case R_1XX:
+ recvd_provisional(r, tuid, tid);
+ break;
+ case R_2XX:
+ recvd_success(r, tuid, tid);
+ break;
+ case R_3XX:
+ recvd_redirect(r, tuid, tid);
+ break;
+ case R_4XX:
+ recvd_client_error(r, tuid, tid);
+ break;
+ case R_5XX:
+ recvd_server_error(r, tuid, tid);
+ break;
+ case R_6XX:
+ recvd_global_error(r, tuid, tid);
+ break;
+ default:
+ assert(false);
+ break;
+ }
+
+ post_process_response(r, tuid, tid);
+
+ unlock();
+}
+
+void t_transaction_layer::recvd_request(t_request *r, t_tid tid,
+ t_tid tid_cancel_target)
+{
+ bool fatal;
+ string reason;
+ t_response *resp;
+
+ lock();
+
+ // Return a 400 response if the SIP headers are wrong
+ if (!r->is_valid(fatal, reason)) {
+ resp = r->create_response(R_400_BAD_REQUEST, reason);
+ send_response(resp, 0, tid);
+ MEMMAN_DELETE(resp);
+ delete resp;
+ unlock();
+ return;
+ }
+
+ // Return a 400 response if the SIP body contained a parse error
+ if (r->body && r->body->invalid) {
+ resp = r->create_response(R_400_BAD_REQUEST, "Invalid SIP body.");
+ send_response(resp, 0, tid);
+ MEMMAN_DELETE(resp);
+ delete resp;
+ unlock();
+ return;
+ }
+
+ // If a message exceeded the maximum message size, than the body
+ // is not parsed by the listener.
+ if (r->hdr_content_length.is_populated() &&
+ r->hdr_content_length.length > 0 &&
+ !r->body)
+ {
+ resp = r->create_response(R_513_MESSAGE_TOO_LARGE);
+ send_response(resp, 0, tid);
+ MEMMAN_DELETE(resp);
+ delete resp;
+ unlock();
+ return;
+ }
+
+ // RFC 3261 8.2.3
+ // Return a 415 response if content encoding is not supported
+ if (r->body && r->hdr_content_encoding.is_populated()) {
+ for (list<t_coding>::iterator it = r->hdr_content_encoding.coding_list.begin();
+ it != r->hdr_content_encoding.coding_list.end(); ++it)
+ {
+ if (!CONTENT_ENCODING_SUPPORTED(it->content_coding)) {
+ resp = r->create_response(R_415_UNSUPPORTED_MEDIA_TYPE);
+ SET_HDR_ACCEPT_ENCODING(resp->hdr_accept_encoding);
+ send_response(resp, 0, tid);
+ MEMMAN_DELETE(resp);
+ delete resp;
+ unlock();
+ return;
+ }
+ }
+ }
+
+ // Check if URI scheme is supported
+ if (r->uri.get_scheme() != "sip") {
+ resp = r->create_response(R_416_UNSUPPORTED_URI_SCHEME);
+ send_response(resp, 0, tid);
+ MEMMAN_DELETE(resp);
+ delete resp;
+ unlock();
+ return;
+ }
+
+ switch(r->method) {
+ case INVITE:
+ recvd_invite(r, tid);
+ break;
+ case ACK:
+ recvd_ack(r, tid);
+ break;
+ case CANCEL:
+ recvd_cancel(r, tid, tid_cancel_target);
+ break;
+ case BYE:
+ recvd_bye(r, tid);
+ break;
+ case OPTIONS:
+ recvd_options(r, tid);
+ break;
+ case REGISTER:
+ recvd_register(r, tid);
+ break;
+ case PRACK:
+ recvd_prack(r, tid);
+ break;
+ case SUBSCRIBE:
+ recvd_subscribe(r, tid);
+ break;
+ case NOTIFY:
+ recvd_notify(r, tid);
+ break;
+ case REFER:
+ recvd_refer(r, tid);
+ break;
+ case INFO:
+ recvd_info(r, tid);
+ break;
+ case MESSAGE:
+ recvd_message(r, tid);
+ break;
+ default:
+ resp = r->create_response(R_501_NOT_IMPLEMENTED);
+ send_response(resp, 0, tid);
+ MEMMAN_DELETE(resp);
+ delete resp;
+ break;
+ }
+
+ post_process_request(r, tid, tid_cancel_target);
+
+ unlock();
+}
+
+void t_transaction_layer::recvd_async_response(t_event_async_response *event) {
+ lock();
+
+ switch (event->get_response_type()) {
+ case t_event_async_response::RESP_REFER_PERMISSION:
+ recvd_refer_permission(event->get_bool_response());
+ break;
+ default:
+ // Ignore other responses
+ break;
+ }
+
+ unlock();
+}
+
+void t_transaction_layer::send_request(t_user *user_config, t_request *r, t_tuid tuid) {
+ evq_trans_mgr->push_user(user_config, (t_sip_message *)r, tuid, 0);
+}
+
+void t_transaction_layer::send_request(t_user *user_config, StunMessage *r, t_tuid tuid) {
+ // The transaction manager will determine the destination IP and port,
+ // so they can be left to zero in the event.
+ evq_trans_mgr->push_stun_request(user_config, r, TYPE_STUN_SIP, tuid, 0, 0, 0);
+}
+
+void t_transaction_layer::send_response(t_response *r, t_tuid tuid,
+ t_tid tid)
+{
+ evq_trans_mgr->push_user((t_sip_message *)r, tuid, tid);
+}
+
+void t_transaction_layer::run(void) {
+ t_event *event;
+ t_event_user *ev_user;
+ t_event_timeout *ev_timeout;
+ t_event_failure *ev_failure;
+ t_event_stun_response *ev_stun_resp;
+ t_event_async_response *ev_async_resp;
+ t_event_broken_connection *ev_broken_connection;
+ t_sip_message *msg;
+ StunMessage *stun_msg;
+ t_tid tid;
+ t_tid tid_cancel;
+ t_tuid tuid;
+
+ bool quit = false;
+ while (!quit) {
+ event = evq_trans_layer->pop();
+
+ switch (event->get_type()) {
+ case EV_USER:
+ ev_user = (t_event_user *)event;
+ tid = ev_user->get_tid();
+ tuid = ev_user->get_tuid();
+ tid_cancel = ev_user->get_tid_cancel_target();
+ msg = ev_user->get_msg();
+
+ switch(msg->get_type()) {
+ case MSG_REQUEST:
+ recvd_request((t_request *)msg, tid,
+ tid_cancel);
+ break;
+ case MSG_RESPONSE:
+ recvd_response((t_response *)msg, tuid, tid);
+ break;
+ default:
+ assert(false);
+ break;
+ }
+
+ break;
+ case EV_TIMEOUT:
+ ev_timeout = dynamic_cast<t_event_timeout *>(event);
+ handle_event_timeout(ev_timeout);
+ break;
+ case EV_FAILURE:
+ ev_failure = (t_event_failure *)event;
+ tid = ev_failure->get_tid();
+ lock();
+ failure(ev_failure->get_failure(), tid);
+ unlock();
+ break;
+ case EV_STUN_RESPONSE:
+ ev_stun_resp = (t_event_stun_response *)event;
+ tid = ev_stun_resp->get_tid();
+ tuid = ev_stun_resp->get_tuid();
+ stun_msg = ev_stun_resp->get_msg();
+ recvd_stun_resp(stun_msg, tuid, tid);
+ break;
+ case EV_ASYNC_RESPONSE:
+ ev_async_resp = dynamic_cast<t_event_async_response *>(event);
+ recvd_async_response(ev_async_resp);
+ break;
+ case EV_BROKEN_CONNECTION:
+ ev_broken_connection = dynamic_cast<t_event_broken_connection *>(event);
+ handle_broken_connection(ev_broken_connection);
+ break;
+ case EV_QUIT:
+ quit = true;
+ break;
+ default:
+ // other types of event are not expected
+ assert(false);
+ break;
+ }
+
+ MEMMAN_DELETE(event);
+ delete event;
+ }
+}
+
+void t_transaction_layer::lock(void) const {
+ // Prohibited threads may not lock the transaction layer
+ assert(!is_prohibited_thread());
+
+ // The user interface and transaction layer threads both call
+ // functions on the transaction layer. By locking the UI mutex
+ // first, a deadlock can never occur as the UI also takes the
+ // UI lock first and then the transaction layer lock.
+ // During shutdown of Twinkle the GUI has exited already and
+ // a lock on an exited QApplication causes a segmentation fault.
+ // Therefore the lock on the UI should not be taken during shutdown.
+ if (!end_app) ui->lock();
+ tl_mutex.lock();
+}
+
+void t_transaction_layer::unlock(void) const {
+ tl_mutex.unlock();
+ if (!end_app) ui->unlock();
+}