projects / voip-6line-twinkle.git / blob
Build Results
 
Summary

Description: Michael Larabel's 6-line VoIP client implementation of Twinkle (normally supports two). Development sponsored by Global Fone.
Last Change: Thu 12/13/07 20:15

 
Files
plain
/*
    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 <cstdlib>
#include <assert.h>
#include <iostream>
#include "abstract_dialog.h"
#include "log.h"
#include "phone.h"
#include "util.h"
#include "userintf.h"
#include "audits/memman.h"

extern string        user_host;
extern t_phone        *phone;

// Private
void t_abstract_dialog::remove_client_request(t_client_request **cr) {
    if ((*cr)->dec_ref_count() == 0) {
        MEMMAN_DELETE(*cr);
        delete *cr;
    }

    *cr = NULL;
}

// Create a request within a dialog
// RFC 3261 12.2.1.1
t_request *t_abstract_dialog::create_request(t_method m) {
    t_request *r = new t_request(m);
    MEMMAN_NEW(r);

    // To header
    r->hdr_to.set_uri(remote_uri);
    r->hdr_to.set_display(remote_display);
    r->hdr_to.set_tag(remote_tag);

    // From header
    r->hdr_from.set_uri(local_uri);
    r->hdr_from.set_display(local_display);
    r->hdr_from.set_tag(local_tag);

    // Call-ID header
    r->hdr_call_id.set_call_id(call_id);

    // CSeq header
    r->hdr_cseq.set_method(m);
    r->hdr_cseq.set_seqnr(++local_seqnr);

    // Via header
    t_via via(USER_HOST(user_config), PUBLIC_SIP_UDP_PORT(user_config));
    r->hdr_via.add_via(via);

    // Set Max-Forwards header
    r->hdr_max_forwards.set_max_forwards(MAX_FORWARDS);

    // User-Agent
    SET_HDR_USER_AGENT(r->hdr_user_agent);

    // RFC 3261 12.2.1.1
    // Request URI and Route header
        if (route_set.empty()) {
                r->uri = remote_target_uri;
        } else {
                if (route_set.front().uri.get_lr()) {
            // Loose routing
                        r->uri = remote_target_uri;
                        for (list<t_route>::iterator i = route_set.begin();
                             i != route_set.end(); i++)
                        {
                                r->hdr_route.add_route(*i);
                        }
                        r->hdr_route.route_to_first_route = true;
                } else {
            // Strict routing
                        r->uri = route_set.front().uri;
                        for (list<t_route>::iterator i = route_set.begin();
                             i != route_set.end(); i++)
                        {
                                if (i != route_set.begin()) {
                                        r->hdr_route.add_route(*i);
                                }
                        }

                        // Add remote target uri to the route list
                        t_route route;
                        route.uri = remote_target_uri;
                        r->hdr_route.add_route(route);
                }
        }

        // Caculate destination set. A DNS request can result in multiple
        // IP address. In failover scenario's the request must be sent to
        // the next IP address in the list. As the request will be copied
        // in various places, the destination set must be calculated now.
        // In previous version the DNS request was done by the transaction
        // manager. This is too late as the transaction manager gets a copy
        // of the request. The destination set should be set in the copy
        // kept by the dialog.
        r->calc_destinations(*user_config);

    return r;
}

void t_abstract_dialog::create_route_set(t_response *r) {
    // Originally the check was this:
    // if (route_set.empty() && r->hdr_record_route.is_populated())
    // This prevented the route set from being altered between a 18X response
    // and a 2XX response. This is allowed per RFC 3261 13.2.2.4
    if (r->hdr_record_route.is_populated())
    {
        route_set = r->hdr_record_route.route_list;
        route_set.reverse();
    } else {
        route_set.clear();
    }
}

void t_abstract_dialog::create_remote_target(t_response *r) {
    if (r->hdr_contact.is_populated()) {
        remote_target_uri = r->hdr_contact.contact_list.front().uri;
        remote_target_display = r->hdr_contact.contact_list.front().display;
    }
}

void t_abstract_dialog::resend_request(t_client_request *cr) {
    t_request *req = cr->get_request();

    // A new sequence number must be assigned
    req->hdr_cseq.set_seqnr(++local_seqnr);

    // Create a new via-header. Otherwise the
    // request will be seen as a retransmission
    req->hdr_via.via_list.clear();
    t_via via(USER_HOST(user_config), PUBLIC_SIP_UDP_PORT(user_config));
    req->hdr_via.add_via(via);

    cr->renew(0);
    send_request(req, cr->get_tuid());
}

bool t_abstract_dialog::resend_request_auth(t_client_request *cr, t_response *resp) {
    t_request *req = cr->get_request();

    // Add authorization header, increment CSeq and create new branch id
    if (phone->authorize(user_config, req, resp)) {
        resend_request(cr);
        return true;
    }

    return false;
}

bool t_abstract_dialog::redirect_request(t_client_request *cr, t_response *resp,
        t_contact_param &contact)
{
    // If the response is a 3XX response then add redirection contacts
    if (resp->get_class() == R_3XX  && resp->hdr_contact.is_populated()) {
        cr->redirector.add_contacts(
                    resp->hdr_contact.contact_list);
    }

    // Get next destination
    if (!cr->redirector.get_next_contact(contact)) {
        // There is no next destination
        return false;
    }

    t_request *req = cr->get_request();

    // Ask user for permission to redirect if indicated by user config
    if (user_config->get_ask_user_to_redirect()) {
        if(!ui->cb_ask_user_to_redirect_request(user_config,
                contact.uri, contact.display, resp->hdr_cseq.method))
        {
            // User did not permit to redirect
            return false;
        }
    }

    // Change the request URI to the new URI.
    // As the URI changes the destination set must be recalculated
    req->uri = contact.uri;
    req->calc_destinations(*user_config);

    resend_request(cr);

    return true;
}

bool t_abstract_dialog::failover_request(t_client_request *cr) {
    log_file->write_report("Failover to next destination.",
                "t_abstract_dialog::failover_request");

    t_request *req = cr->get_request();

    // Get next destination
    if (!req->next_destination()) {
        log_file->write_report("No next destination for failover.",
                "t_abstract_dialog::failover_request");
        return false;
    }

    resend_request(cr);
    return true;
}


////////////
// Public
////////////

t_abstract_dialog::t_abstract_dialog(t_user *user) :
    t_id_object()
{
    assert(user);
    user_config = user;
    call_id_owner = false;

    local_seqnr = 0;
    remote_seqnr = 0;
    remote_seqnr_set = false;

    local_resp_nr = 0;
    remote_resp_nr = 0;

    remote_ipaddr = 0;
    remote_port = 0;

    log_file->write_header("t_abstract_dialog::t_abstract_dialog", LOG_NORMAL, LOG_DEBUG);
    log_file->write_raw("Created dialog, id=");
    log_file->write_raw(get_object_id());
    log_file->write_endl();
    log_file->write_footer();
}

t_abstract_dialog::~t_abstract_dialog() {
    log_file->write_header("t_abstract_dialog::~t_abstract_dialog", LOG_NORMAL, LOG_DEBUG);
    log_file->write_raw("Destroy dialog, id=");
    log_file->write_raw(get_object_id());
    log_file->write_endl();
    log_file->write_footer();
}

t_user *t_abstract_dialog::get_user(void) const {
    return user_config;
}

void t_abstract_dialog::recvd_response(t_response *r, t_tuid tuid, t_tid tid) {
    // The source address and port of a message may be 0 when the
    // message was sent internally.
    if (r->src_ipaddr != 0 && r->src_port != 0) {
        remote_ipaddr = r->src_ipaddr;
        remote_port = r->src_port;
    }
}

void t_abstract_dialog::recvd_request(t_request *r, t_tuid tuid, t_tid tid) {
    // The source address and port of a message may be 0 when the
    // message was sent internally.
    if (r->src_ipaddr != 0 && r->src_port != 0) {
        remote_ipaddr = r->src_ipaddr;
        remote_port = r->src_port;
    }
}

bool t_abstract_dialog::match_response(t_response *r, t_tuid tuid) {
    return (call_id == r->hdr_call_id.call_id &&
        local_tag == r->hdr_from.tag &&
        (remote_tag.size() == 0 || remote_tag == r->hdr_to.tag));
}

bool t_abstract_dialog::match_request(t_request *r) {
    return match(r->hdr_call_id.call_id, r->hdr_to.tag, r->hdr_from.tag);
}

bool t_abstract_dialog::match_partial_request(t_request *r) {
    return (r->hdr_call_id.call_id == call_id &&
            r->hdr_to.tag == local_tag);
}

bool t_abstract_dialog::match(const string &_call_id, const string &to_tag,
        const string &from_tag) const
{
    return (call_id == _call_id &&
        local_tag == to_tag &&
        remote_tag == from_tag);
}

t_url t_abstract_dialog::get_remote_target_uri(void) const {
    return remote_target_uri;
}

string t_abstract_dialog::get_remote_target_display(void) const {
    return remote_target_display;
}

t_url t_abstract_dialog::get_remote_uri(void) const {
    return remote_uri;
}

string t_abstract_dialog::get_remote_display(void) const {
    return remote_display;
}

unsigned long t_abstract_dialog::get_remote_ipaddr(void) const {
    return remote_ipaddr;
}

unsigned short t_abstract_dialog::get_remote_port(void) const {
    return remote_port;
}

string t_abstract_dialog::get_call_id(void) const {
    return call_id;
}

string t_abstract_dialog::get_local_tag(void) const {
    return local_tag;
}

string t_abstract_dialog::get_remote_tag(void) const {
    return remote_tag;
}

bool t_abstract_dialog::remote_extension_supported(const string &extension) const {
    return (remote_extensions.find(extension) != remote_extensions.end());
}

bool t_abstract_dialog::is_call_id_owner(void) const {
    return call_id_owner;
}
 
Phoronix.com
Linux Driver Forums
Copyright © 2013 by Phoronix Media