Revision 332
Date:
2013/05/03 13:35:48
Author:
ahitrov
Revision Log:
TokBox Plugin
Files:
Legend:
Added
Removed
Modified
utf8/plugins/tokbox/comps/contenido/tokbox/autohandler
1
<%init>
2
3
$r->content_type('text/html');
4
$m->call_next();
5
6
</%init>
utf8/plugins/tokbox/comps/contenido/tokbox/dhandler
1
<& $call, %ARGS &>
2
<%init>
3
4
my $call;
5
if ( $r->uri eq '/contenido/tokbox/' ) {
6
$call = 'index.html';
7
} else {
8
&abort404;
9
}
10
11
</%init>
utf8/plugins/tokbox/comps/contenido/tokbox/index.html
1
<& "/contenido/components/header.msn" &>
2
<& "/contenido/components/naviline.msn" &>
3
4
<p>PLugin [tokbox]</p>
5
6
</body>
7
</html>
utf8/plugins/tokbox/config.proto
1
#############################################################################
2
#
3
# Параметры данного шаблона необходимо ВРУЧНУЮ добавить в config.mk проекта
4
# и привести в соответствие с требованиями проекта
5
#
6
#############################################################################
7
PLUGINS += tokbox
8
9
TOKBOX_API_KEY =
10
TOKBOX_SECRET =
11
12
REWRITE += TOKBOX_API_KEY TOKBOX_SECRET
utf8/plugins/tokbox/lib/tokbox/Apache.pm
1
package tokbox::Apache;
2
3
use strict;
4
use warnings 'all';
5
6
use tokbox::State;
7
use Contenido::Globals;
8
9
10
sub child_init {
11
# встраиваем keeper плагина в keeper проекта
12
$keeper->{tokbox} = tokbox::Keeper->new($state->tokbox);
13
}
14
15
sub request_init {
16
}
17
18
sub child_exit {
19
}
20
21
1;
utf8/plugins/tokbox/lib/tokbox/Init.pm
1
package tokbox::Init;
2
3
use strict;
4
use warnings 'all';
5
6
use Contenido::Globals;
7
use tokbox::Apache;
8
use tokbox::Keeper;
9
use tokbox::Session;
10
11
12
# загрузка всех необходимых плагину классов
13
# tokbox::SQL::SomeTable
14
# tokbox::SomeClass
15
Contenido::Init::load_classes(qw(
16
));
17
18
sub init {
19
0;
20
}
21
22
1;
utf8/plugins/tokbox/lib/tokbox/Keeper.pm
1
package tokbox::Keeper;
2
3
use strict;
4
use warnings 'all';
5
use Time::HiRes;
6
use MIME::Base64;
7
use Digest::SHA1 qw(sha1 sha1_hex);
8
use LWP::UserAgent;
9
use URI;
10
use Data::Dumper;
11
use XML::Fast;
12
13
use base qw(Contenido::Keeper);
14
15
use Contenido::Globals;
16
17
our $SUBSCRIBER = "subscriber";
18
our $PUBLISHER = "publisher";
19
our $MODERATOR = "moderator";
20
21
sub OpenTokSession {
22
my ($self, $sessionId, $properties) = @_;
23
return tokbox::Session->new( sessionId => $sessionId, properties => $properties );
24
}
25
26
27
### - Generate a token
28
#
29
# session_id - If session_id is not blank, this token can only join the call with the specified session_id.
30
# role - One of the constants defined in RoleConstants. Default is publisher, look in the documentation to learn more about roles.
31
# expire_time - Optional timestamp to change when the token expires. See documentation on token for details.
32
# connection_data - Optional string data to pass into the stream. See documentation on token for details.
33
#
34
##########################################################################################################
35
sub generateToken {
36
my ($self, %opts) = @_;
37
38
my $session_id = delete $opts{session_id} || '';
39
my $role = delete $opts{role} || '';
40
my $expire_time = delete $opts{expire_time};
41
my $connection_data = delete $opts{connection_data} || '';
42
43
my $create_time = time;
44
my $nonce = gettimeofday() . rand();
45
46
if ( !$role ) {
47
$role = $PUBLISHER;
48
} elsif ( $role ne $SUBSCRIBER && $role ne $PUBLISHER && $role ne $MODERATOR ) {
49
warn "unknown role $role\n";
50
return;
51
}
52
my $data_string = "session_id=$session_id&create_time=$create_time&role=$role&nonce=$nonce";
53
if ( defined $expire_time ) {
54
if ( $expire_time =~ /\D/ ) {
55
warn "Expire time must be a number\n";
56
return;
57
} elsif ( $expire_time < $create_time ) {
58
warn "Expire time must be in the future\n";
59
return;
60
} elsif ( $expire_time > $create_time + 2592000 ) {
61
warn "Expire time must be in the next 30 days\n";
62
return;
63
}
64
$data_string .= "&expire_time=$expire_time";
65
}
66
if ( $connection_data ) {
67
if ( length $connection_data > 1000 ) {
68
warn "Connection data must be less than 1000 characters\n";
69
return;
70
}
71
$data_string .= "&connection_data=" . Utils::HTML::url_escape($connection_data);
72
}
73
my $sig = $self->_sign_string($data_string, $self->state->{tokbox_secret});
74
my $api_key = $self->state->{tokbox_api_key};
75
76
return "T1==" . encode_base64("partner_id=$api_key&sig=$sig:$data_string");
77
}
78
79
80
###
81
#
82
# Creates a new session.
83
# location - IP address to geolocate the call around.
84
# properties - Optional array, keys are defined in SessionPropertyConstants
85
#
86
###################################################################################
87
sub createSession {
88
my ($self, %opts) = @_;
89
90
my $location = delete $opts{location} || '';
91
my $properties = delete $opts{properties} || {};
92
$properties->{"location"} = $location;
93
$properties->{"api_key"} = $self->state->{tokbox_api_key};
94
95
my $createSessionResult = $self->_do_request("/session/create", $properties);
96
return unless $createSessionResult;
97
my $createSessionXML = xml2hash ($createSessionResult);
98
unless ( ref $createSessionXML ) {
99
warn "Failed to create session: Invalid response from server\n";
100
return;
101
}
102
103
unless( exists $createSessionXML->{sessions}{Session}{session_id} ) {
104
warn "Failed to create session.\n";
105
warn Dumper $createSessionXML;
106
return;
107
}
108
my $sessionId = $createSessionXML->{sessions}{Session}{session_id};
109
110
return $self->OpenTokSession( $sessionId );
111
}
112
113
114
115
########################################################
116
# Inner functions
117
########################################################
118
sub _sign_string {
119
my ($self, $string, $secret) = @_;
120
return sha1_hex($string, $secret);
121
}
122
123
sub _do_request {
124
my ($self, $url, $data, $auth) = @_;
125
126
$auth = {} unless ref $auth;
127
$auth->{'type'} = 'partner' unless exists $auth->{type};
128
129
$url = $self->state->{tokbox_server} . $url;
130
131
my %authHeader;
132
if ( $auth->{type} eq 'token' ) {
133
$authHeader{"X-TB-TOKEN-AUTH"} = $auth->{'token'};
134
} else {
135
$authHeader{"X-TB-PARTNER-AUTH"} = $self->state->{tokbox_api_key}.":".$self->state->{tokbox_secret};
136
}
137
138
my $req = URI->new( $url );
139
my $ua = LWP::UserAgent->new;
140
$ua->timeout(3);
141
$ua->default_header( 'Content-type' => 'application/x-www-form-urlencoded' );
142
$ua->default_header( %authHeader );
143
warn "Post: [$url] params:".Dumper($data) if $DEBUG;
144
my $res = $ua->post( $req, $data );
145
unless ($res->code == 200) {
146
warn "Request failed: ".$res->status_line;
147
return undef;
148
} else {
149
warn "Responce: ".Dumper($res) if $DEBUG;
150
}
151
return $res->content;
152
}
153
154
155
1;
utf8/plugins/tokbox/lib/tokbox/Session.pm
1
package tokbox::Session;
2
3
use strict;
4
use warnings 'all';
5
use Contenido::Globals;
6
use tokbox::Keeper;
7
8
our $P2P_PREFERENCE = 'p2p.preference';
9
10
sub new {
11
my ($proto, %params) = @_;
12
my $class = ref($proto) || $proto;
13
my $self = {};
14
bless $self, $class;
15
16
$self->{sessionId} = $params{sessionId} || '';
17
$self->{sessionProperties} = $params{properties};
18
19
return $self;
20
}
21
22
23
sub sessionId {
24
my $self = shift;
25
return exists $self->{sessionId} && $self->{sessionId} ? $self->{sessionId} : undef;
26
}
27
28
sub sessionProperties {
29
my $self = shift;
30
return exists $self->{sessionProperties} && $self->{sessionProperties} ? $self->{sessionProperties} : undef;
31
}
32
33
sub getSessionId {
34
my $self = shift;
35
return $self->sessionId;
36
}
37
38
sub id {
39
my $self = shift;
40
return $self->sessionId;
41
}
42
43
1;
utf8/plugins/tokbox/lib/tokbox/State.pm.proto
1
package tokbox::State;
2
3
use strict;
4
use warnings 'all';
5
use vars qw($AUTOLOAD);
6
7
8
sub new {
9
my ($proto) = @_;
10
my $class = ref($proto) || $proto;
11
my $self = {};
12
bless $self, $class;
13
14
# configured
15
$self->{debug} = (lc('') eq 'yes');
16
$self->{project} = '';
17
18
# зашитая конфигурация плагина
19
$self->{db_type} = 'none'; ### For REAL database use 'remote'
20
$self->{db_keepalive} = 0;
21
$self->{db_host} = '';
22
$self->{db_name} = '';
23
$self->{db_user} = '';
24
$self->{db_password} = '';
25
$self->{db_port} = '';
26
$self->{store_method} = 'toast';
27
$self->{cascade} = 1;
28
$self->{db_prepare} = 0;
29
30
$self->{memcached_enable} = lc( '' ) eq 'yes' ? 1 : 0;
31
$self->{memcached_enable_compress} = 1;
32
$self->{memcached_backend} = '';
33
$self->{memcached_servers} = [qw()];
34
$self->{memcached_busy_lock} = 60;
35
$self->{memcached_delayed} = lc('') eq 'yes' ? 1 : 0;
36
37
$self->{serialize_with} = 'json'; ### or 'dumper'
38
39
# not implemented really (core compatibility)
40
$self->{binary_directory} = '/nonexistent';
41
$self->{data_directory} = '/nonexistent';
42
$self->{images_directory} = '/nonexistent';
43
$self->{preview} = '0';
44
45
$self->{tokbox_api_key} = '@TOKBOX_API_KEY@';
46
$self->{tokbox_secret} = '@TOKBOX_SECRET@';
47
$self->{tokbox_server} = 'http://api.opentok.com/hl';
48
49
$self->_init_();
50
$self;
51
}
52
53
sub info {
54
my $self = shift;
55
return unless ref $self;
56
57
for (sort keys %{$self->{attributes}}) {
58
my $la = length $_;
59
warn "\t$_".("\t" x (2-int($la/8))).": $self->{$_}\n";
60
}
61
}
62
63
sub _init_ {
64
my $self = shift;
65
66
# зашитая конфигурация плагина
67
$self->{attributes}->{$_} = 'SCALAR' for qw(
68
debug
69
project
70
71
db_type
72
db_keepalive
73
db_host
74
db_port
75
db_name
76
db_user
77
db_password
78
store_method
79
cascade
80
db_prepare
81
db_client_encoding
82
83
memcached_enable
84
memcached_enable_compress
85
memcached_backend
86
memcached_servers
87
memcached_busy_lock
88
memcached_delayed
89
90
binary_directory
91
data_directory
92
images_directory
93
preview
94
);
95
}
96
97
sub AUTOLOAD {
98
my $self = shift;
99
my $attribute = $AUTOLOAD;
100
101
$attribute =~ s/.*:://;
102
return unless $attribute =~ /[^A-Z]/; # Отключаем методы типа DESTROY
103
104
if (!exists $self->{attributes}->{$attribute}) {
105
warn "Contenido Error (tokbox::State): Вызов метода, для которого не существует обрабатываемого свойства: ->$attribute()\n";
106
return;
107
}
108
109
$self->{$attribute} = shift @_ if $#_>=0;
110
$self->{$attribute};
111
}
112
113
1;
Небольшая справка по веткам
cnddist – контейнер, в котором хранятся все дистрибутивы всех библиотек и программных пакетов, которые использовались при построении различных версий Contenido. Если какой-то библиотеки в данном хранилище нет, инсталлятор сделает попытку "подтянуть" ее с веба (например, с CPAN). Если библиотека слишком старая, есть очень большая вероятность, что ее там уже нет. Поэтому мы храним весь хлам от всех сборок. Если какой-то дистрибутив вдруг отсутствует в cnddist - напишите нам, мы положим его туда.
koi8 – отмирающая ветка, чей код, выдача и все внутренние библиотеки заточены на кодировку KOI8-R. Вносятся только те дополнения, которые касаются внешнего вида и функционала админки, баги ядра, обязательные обновления портов и мелочи, которые легко скопипастить. В дальнейшем планируется полная остановка поддержки по данной ветке.
utf8 – актуальная ветка, заточенная под UTF-8.
Внутри каждой ветки: core – исходники ядра; install – скрипт установки инсталляции; plugins – плагины; samples – "готовые к употреблению" проекты, которые можно поставить, запустить и посмотреть, как они работают.