Revision 336 (by ahitrov, 2013/05/03 19:59:49) Another sha1 algorythm
package tokbox::Keeper;

use strict;
use warnings 'all';
use Time::HiRes qw( gettimeofday );
use MIME::Base64;
use Digest::SHA qw(hmac_sha1 hmac_sha1_hex);
use LWP::UserAgent;
use URI;
use Data::Dumper;
use XML::Fast;

use base qw(Contenido::Keeper);

use Contenido::Globals;

my $SUBSCRIBER = "subscriber";
my $PUBLISHER = "publisher";
my $MODERATOR = "moderator";

sub OpenTokSession {
    my ($self, $sessionId, $properties) = @_;
    return tokbox::Session->new( sessionId => $sessionId, properties => $properties );
}


### - Generate a token
#
# session_id - If session_id is not blank, this token can only join the call with the specified session_id.
# role - One of the constants defined in RoleConstants. Default is publisher, look in the documentation to learn more about roles.
# expire_time - Optional timestamp to change when the token expires. See documentation on token for details.
# connection_data - Optional string data to pass into the stream. See documentation on token for details.
#
##########################################################################################################
sub generateToken {
    my ($self, %opts) = @_;

    my $session_id = delete $opts{session_id} || '';
    my $role = delete $opts{role} || '';
    my $expire_time = delete $opts{expire_time};
    my $connection_data = delete $opts{connection_data} || '';

    my $create_time = time;
    my $nonce = rand();

    if ( !$role ) {
	$role = $PUBLISHER;
    } elsif ( $role ne $SUBSCRIBER && $role ne $PUBLISHER && $role ne $MODERATOR ) {
	warn "unknown role $role\n";
	return;
    }
    my $data_string = "role=$role&session_id=$session_id&create_time=$create_time&nonce=$nonce";
    if ( defined $expire_time ) {
	if ( $expire_time =~ /\D/ ) {
		warn "Expire time must be a number\n";
		return;
	} elsif ( $expire_time < $create_time ) {
		warn "Expire time must be in the future\n";
		return;
	} elsif ( $expire_time > $create_time + 2592000 ) {	
		warn "Expire time must be in the next 30 days\n";
		return;
	}
	$data_string .= "&expire_time=$expire_time";
    }
    if ( $connection_data ) {
	if ( length $connection_data > 1000 ) {
		warn "Connection data must be less than 1000 characters\n";
		return;
	}
	$data_string .= "&connection_data=" . Utils::HTML::url_escape( $connection_data );
    } else {
	$data_string .= "&connection_data=";
    }

    my $sig = $self->_sign_string($data_string, $self->state->{tokbox_secret});
    my $api_key = $self->state->{tokbox_api_key};

    warn "AUTH: [partner_id=$api_key&sig=$sig:$data_string]\n"		if $DEBUG;
    return "T1==" . MIME::Base64::encode_base64("partner_id=$api_key&sig=$sig:$data_string", '');
}


###
#
# Creates a new session.
# location - IP address to geolocate the call around.
# properties - Optional array, keys are defined in SessionPropertyConstants
#
###################################################################################
sub createSession {
    my ($self, %opts) = @_;

    my $location = delete $opts{location} || '';
    my $properties = delete $opts{properties} || {};
    $properties->{"location"} = $location;
    $properties->{"api_key"} = $self->state->{tokbox_api_key};

    my $createSessionResult = $self->_do_request("/session/create", $properties);
    return	unless $createSessionResult;
    my $createSessionXML = xml2hash ($createSessionResult);
    unless ( ref $createSessionXML ) {
	warn "Failed to create session: Invalid response from server\n";
	return;
    }

    unless( exists $createSessionXML->{sessions}{Session}{session_id} ) {
	warn "Failed to create session.\n";
	warn Dumper $createSessionXML;
	return;
    }
    my $sessionId = $createSessionXML->{sessions}{Session}{session_id};

    return $self->OpenTokSession( $sessionId );
}



########################################################
#  Inner functions
########################################################
sub _sign_string {
    my ($self, $string, $secret) = @_;
    return hmac_sha1_hex($string, $secret);
}

sub _do_request {
    my ($self, $url, $data, $auth) = @_;

    $auth = {}				unless ref $auth;
    $auth->{'type'} = 'partner'		unless exists $auth->{type};

    $url = $self->state->{tokbox_server} . $url;

    my %authHeader;
    if ( $auth->{type} eq 'token' ) {
	$authHeader{"X-TB-TOKEN-AUTH"} = $auth->{'token'};
    } else {
	$authHeader{"X-TB-PARTNER-AUTH"} = $self->state->{tokbox_api_key}.":".$self->state->{tokbox_secret};
    }

    my $req = URI->new( $url );
    my $ua = LWP::UserAgent->new;
    $ua->timeout(3);
    $ua->default_header( 'Content-type' => 'application/x-www-form-urlencoded' );
    $ua->default_header( %authHeader );
    warn "Post: [$url] params:".Dumper($data)			if $DEBUG;
    my $res = $ua->post( $req, $data );
    unless ($res->code == 200) {
	warn "Request failed: ".$res->status_line;
	return undef;
    } else {
	warn "Responce: ".Dumper($res)				if $DEBUG;
    }
    return $res->content;
}


1;