Removed some aes/sha256 from dependencies on clientside

This commit is contained in:
Kasper Rynning-Tønnesen
2018-03-13 12:39:24 +01:00
parent dba60cd866
commit 8c1c0011a2
15 changed files with 91 additions and 61 deletions

View File

@@ -6,19 +6,19 @@
'end', {
id: video_id,
channel: channel_name,
pass: AES-CBC-Pkcs7 with Base64 IV(channel_pass)
pass: Base64(channel_pass)
}
// Asks server where in the song it should be
'pos', {
channel: channel_name,
pass: cAES-CBC-Pkcs7 with Base64 IV(hannel_pass)
pass: cBase64(hannel_pass)
}
// Tells the server the client wants the list
'list', {
channel: channel_name,
pass: AES-CBC-Pkcs7 with Base64 IV(channel_pass),
pass: Base64(channel_pass),
version: system_version (can be checked in VERSION.js)
}
@@ -26,13 +26,13 @@
'add', {
id: VIDEO_ID,
title: VIDEO_TITLE,
adminpass: AES-CBC-Pkcs7 with Base64 IV(PASSWORD),
adminpass: Base64(PASSWORD),
duration: VIDEO_DURATION,
list: channel_name,
playlist: true_if_importing_playlist,
num: current_number_of_sending_songs,
total: total_number_of_sending_songs,
pass: AES-CBC-Pkcs7 with Base64 IV(channel_pass)
pass: Base64(channel_pass)
}
// Tells the server to disconnect the user from the current channel, is used for remote controlling on the host side
@@ -50,7 +50,7 @@
'chat',{
channel: channel_name,
data: input,
pass: AES-CBC-Pkcs7 with Base64 IV(channel_pass)
pass: Base64(channel_pass)
}
// Sends info about song the user wants to vote on. If VOTE_TYPE is del, its deleting the song, if its pos, its just voting
@@ -58,29 +58,29 @@
channel: CHANNEL_NAME,
id: VIDEO_ID,
type: VOTE_TYPE,
adminpass: AES-CBC-Pkcs7 with Base64 IV(PASSWORD)
adminpass: Base64(PASSWORD)
}
// Sends shuffle to the server (Only works every 5 seconds per list)
'shuffle', {
adminpass: AES-CBC-Pkcs7 with Base64 IV(PASSWORD),
adminpass: Base64(PASSWORD),
channel: CHANNELNAME,
pass: AES-CBC-Pkcs7 with Base64 IV(USER_PASSWORD)
pass: Base64(USER_PASSWORD)
}
// Sends skip message to server
'skip', {
pass: AES-CBC-Pkcs7 with Base64 IV(PASSWORD),
pass: Base64(PASSWORD),
id:video_id,
channel: chan,
userpass: AES-CBC-Pkcs7 with Base64 IV(channel_pass)
userpass: Base64(channel_pass)
}
// Sends password for instant log in to server
'password', {
password: AES-CBC-Pkcs7 with Base64 IV(PASSWORD),
password: Base64(PASSWORD),
channel: CHANNEL_NAME,
oldpass: AES-CBC-Pkcs7 with Base64 IV(old_pass_if_changing_password)
oldpass: Base64(old_pass_if_changing_password)
}
// Sends message to the host channel for play
@@ -129,7 +129,7 @@
'get_history', {
channel: CHANNEL_NAME,
all: BOOLEAN (if true, it requests for all-chat),
pass: AES-CBC-Pkcs7 with Base64 IV(USERPASS)
pass: Base64(USERPASS)
}
```

View File

@@ -1,4 +1,4 @@
VERSION = 4;
VERSION = 5;
try {
module.exports = VERSION;

View File

@@ -89,7 +89,7 @@ function rndName(seed, len) {
function decrypt_string(socket_id, pw){
try {
var input = pw.split("$");
/*var input = pw.split("$");
pw = input[0];
var testKey = ((new Buffer(socket_id).toString('base64')) + (new Buffer(socket_id).toString('base64'))).substring(0,32);
var keyNew = (new Buffer(testKey)).toString('base64');
@@ -104,8 +104,10 @@ function decrypt_string(socket_id, pw){
padding: CryptoJS.pad.Pkcs7,
iv: iv,
})
);
return decrypted;
);*/
//return atob(pw);
return Buffer.from(pw, 'base64').toString('ascii')
//return decrypted;
} catch(e) {
return "";
}
@@ -132,7 +134,8 @@ function contains(a, obj) {
}
}
function hash_pass(adminpass) {
function hash_pass(adminpass, hex) {
if(hex) return crypto.createHash('sha256').update(adminpass).digest('hex');
return crypto.createHash('sha256').update(adminpass).digest('base64');
}

View File

@@ -181,6 +181,7 @@ module.exports = function() {
if(!msg.hasOwnProperty("channel") || !msg.hasOwnProperty("all") ||
!msg.hasOwnProperty("pass") || typeof(msg.pass) != "string" ||
typeof(msg.channel) != "string" || typeof(msg.all) != "boolean") {
console.log("here");
var result = {
all: {
expected: "boolean",

View File

@@ -152,7 +152,7 @@ function skip(list, guid, coll, offline, socket) {
}
if(adminpass !== undefined && adminpass !== null && adminpass !== "")
hash = Functions.hash_pass(Functions.decrypt_string(socketid, adminpass));
hash = Functions.hash_pass(Functions.hash_pass(Functions.decrypt_string(socketid, adminpass),true));
else
hash = "";

View File

@@ -98,7 +98,7 @@ function add_function(arr, coll, guid, offline, socket) {
var id = arr.id;
var title = arr.title;
var hash = Functions.hash_pass(Functions.decrypt_string(socketid, arr.adminpass));
var hash = Functions.hash_pass(Functions.hash_pass(Functions.decrypt_string(socketid, arr.adminpass), true));
var duration = parseInt(arr.duration);
var full_list = arr.playlist;
var last = arr.num == arr.total - 1;
@@ -288,7 +288,7 @@ function voteUndecided(msg, coll, guid, offline, socket) {
ListChange.del(msg, socket, socketid);
} else {
var id = msg.id;
var hash = Functions.hash_pass(Functions.decrypt_string(socketid, msg.adminpass));
var hash = Functions.hash_pass(Functions.hash_pass(Functions.decrypt_string(socketid, msg.adminpass), true));
if(docs !== null && docs.length !== 0 && ((docs[0].vote === true && (hash == docs[0].adminpass || docs[0].adminpass === "")) ||
docs[0].vote === false)) {
ListChange.vote(coll, id, guid, socket, false, false);
@@ -324,7 +324,7 @@ function shuffle(msg, coll, guid, offline, socket) {
got: msg.hasOwnProperty("channel") ? typeof(msg.channel) : undefined,
},
adminpass: {
expected: "adminpass",
expected: "string",
got: msg.hasOwnProperty("adminpass") ? typeof(msg.adminpass) : undefined,
},
pass: {
@@ -362,7 +362,7 @@ function shuffle(msg, coll, guid, offline, socket) {
Functions.check_inlist(coll, guid, socket, offline);
var hash;
if(msg.adminpass === "") hash = msg.adminpass;
else hash = Functions.hash_pass(Functions.decrypt_string(socketid, msg.adminpass));
else hash = Functions.hash_pass(Functions.hash_pass(Functions.decrypt_string(socketid, msg.adminpass),true));
db.collection(coll + "_settings").find(function(err, docs){
if(docs.length > 0 && (docs[0].userpass == undefined || docs[0].userpass == "" || (msg.hasOwnProperty('pass') && docs[0].userpass == crypto.createHash('sha256').update(Functions.decrypt_string(socketid, msg.pass)).digest("base64")))) {
if(docs !== null && docs.length !== 0 && ((docs[0].adminpass == hash || docs[0].adminpass === "") || docs[0].shuffle === false))
@@ -403,7 +403,7 @@ function del(params, socket, socketid) {
coll = encodeURIComponent(coll).replace(/\W/g, '');
coll = filter.clean(coll);
db.collection(coll + "_settings").find(function(err, docs){
if(docs !== null && docs.length !== 0 && docs[0].adminpass == Functions.hash_pass(Functions.decrypt_string(socketid, params.adminpass)))
if(docs !== null && docs.length !== 0 && docs[0].adminpass == Functions.hash_pass(Functions.hash_pass(Functions.decrypt_string(socketid, params.adminpass),true)))
{
db.collection(coll).find({id:params.id}, function(err, docs){
var dont_increment = false;
@@ -448,7 +448,7 @@ function delete_all(msg, coll, guid, offline, socket) {
return;
}
var hash = Functions.hash_pass(Functions.decrypt_string(socketid, msg.adminpass));
var hash = Functions.hash_pass(Functions.hash_pass(Functions.decrypt_string(socketid, msg.adminpass),true));
var hash_userpass = crypto.createHash('sha256').update(Functions.decrypt_string(socketid, msg.pass)).digest("base64");
db.collection(coll + "_settings").find(function(err, conf) {
if(conf.length == 1 && conf) {

View File

@@ -30,13 +30,13 @@ function password(inp, coll, guid, offline, socket) {
}
uncrypted = pw;
pw = Functions.decrypt_string(socket.zoff_id, pw);
pw = Functions.hash_pass(Functions.decrypt_string(socket.zoff_id, pw), true);
Functions.check_inlist(coll, guid, socket, offline);
if(inp.oldpass)
{
opw = inp.oldpass;
}
opw = Functions.decrypt_string(socket.zoff_id, opw);
opw = Functions.hash_pass(Functions.decrypt_string(socket.zoff_id, opw), true);
db.collection(coll + "_settings").find(function(err, docs){
if(docs !== null && docs.length !== 0)
@@ -153,6 +153,7 @@ function conf_function(params, coll, guid, offline, socket) {
var shuffling = params.shuffling;
var userpass = Functions.decrypt_string(socket.zoff_id, params.userpass);
if((!params.userpass_changed && frontpage) || (params.userpass_changed && userpass == "")) {
userpass = "";
} else if(params.userpass_changed && userpass != "") {
@@ -161,9 +162,8 @@ function conf_function(params, coll, guid, offline, socket) {
var description = "";
var hash;
if(params.description) description = params.description;
if(adminpass !== "") {
hash = Functions.hash_pass(Functions.decrypt_string(socket.zoff_id, adminpass));
hash = Functions.hash_pass(Functions.hash_pass(Functions.decrypt_string(socket.zoff_id, adminpass), true));
} else {
hash = adminpass;
}

View File

@@ -68,9 +68,11 @@ var Admin = {
pass_save: function() {
if(!w_p) {
emit('password', {password: Crypt.crypt_pass(CryptoJS.SHA256(document.getElementById("password").value).toString()), channel: chan.toLowerCase(), oldpass: Crypt.crypt_pass(Crypt.get_pass(chan.toLowerCase()))});
//emit('password', {password: Crypt.crypt_pass(CryptoJS.SHA256(document.getElementById("password").value).toString()), channel: chan.toLowerCase(), oldpass: Crypt.crypt_pass(Crypt.get_pass(chan.toLowerCase()))});
emit('password', {password: Crypt.crypt_pass(document.getElementById("password").value), channel: chan.toLowerCase(), oldpass: Crypt.crypt_pass(Crypt.get_pass(chan.toLowerCase()))});
} else {
emit('password', {password: Crypt.crypt_pass(CryptoJS.SHA256(document.getElementById("password").value).toString()), channel: chan.toLowerCase()});
//emit('password', {password: Crypt.crypt_pass(CryptoJS.SHA256(document.getElementById("password").value).toString()), channel: chan.toLowerCase()});
emit('password', {password: Crypt.crypt_pass(document.getElementById("password").value), channel: chan.toLowerCase()});
}
},
@@ -229,7 +231,9 @@ var Admin = {
shuffle: function() {
if(!offline) {
emit('shuffle', {adminpass: adminpass !== undefined ? Crypt.crypt_pass(adminpass) : "", channel: chan.toLowerCase(), pass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()), true)});
var u = Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()), true);
if(u == undefined) u = "";
emit('shuffle', {adminpass: adminpass !== undefined ? Crypt.crypt_pass(adminpass) : "", channel: chan.toLowerCase(), pass: embed ? '' : u});
} else {
for(var x = 0; x < full_playlist.length; x++){
var num = Math.floor(Math.random()*1000000);

View File

@@ -683,7 +683,9 @@ var Channel = {
function get_history() {
if(socket && socket.id) {
var p = Crypt.get_userpass();
if(p == undefined) p = "";
var c = Crypt.crypt_pass(p, true);
if(c == undefined) c = "";
socket.emit("get_history", {channel: chan.toLowerCase(), all: false, pass: embed ? '' : c});
socket.emit("get_history", {channel: chan.toLowerCase(), all: true, pass: ""});
} else {

View File

@@ -42,7 +42,7 @@ var Crypt = {
if(Crypt.getCookie(name) === undefined) {
cookie = Crypt.create_cookie(name);
}
var key = btoa("0103060703080703080701") + btoa("0103060703080703080701");
/*var key = btoa("0103060703080703080701") + btoa("0103060703080703080701");
key = key.substring(0,32);
key = btoa(key);
var decrypted = CryptoJS.AES.decrypt(
@@ -51,14 +51,15 @@ var Crypt = {
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
}
);
);*/
return $.parseJSON(decrypted.toString(CryptoJS.enc.Utf8));
//return $.parseJSON(decrypted.toString(CryptoJS.enc.Utf8));
return $.parseJSON(atob(cookie));
},
decrypt_pass: function(pass) {
if(socket) {
var key = btoa(socket.id) + btoa(socket.id);
/*var key = btoa(socket.id) + btoa(socket.id);
key = key.substring(0,32);
key = btoa(key);
var decrypted = CryptoJS.AES.decrypt(
@@ -68,13 +69,14 @@ var Crypt = {
padding: CryptoJS.pad.Pkcs7
}
);
return decrypted.toString(CryptoJS.enc.Utf8);
return decrypted.toString(CryptoJS.enc.Utf8);*/
return atob(pass);
} return false;
},
encrypt: function(json_formated, cookie) {
var to_encrypt = JSON.stringify(json_formated);
var key = btoa("0103060703080703080701") + btoa("0103060703080703080701");
/*var key = btoa("0103060703080703080701") + btoa("0103060703080703080701");
key = key.substring(0,32);
key = btoa(key);
var encrypted = CryptoJS.AES.encrypt(
@@ -84,8 +86,8 @@ var Crypt = {
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
}
);
);*/
var encrypted = btoa(to_encrypt);
var CookieDate = new Date();
CookieDate.setFullYear(CookieDate.getFullYear( ) +1);
if (location.protocol != "https:"){
@@ -120,7 +122,7 @@ var Crypt = {
else cookie_object = {passwords: {}};
var string_it = JSON.stringify(cookie_object);
var key = btoa("0103060703080703080701") + btoa("0103060703080703080701");
/*var key = btoa("0103060703080703080701") + btoa("0103060703080703080701");
key = key.substring(0,32);
key = btoa(key);
var encrypted = CryptoJS.AES.encrypt(
@@ -130,7 +132,8 @@ var Crypt = {
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
}
);
);*/
var encrypted = btoa(string_it);
var CookieDate = new Date();
CookieDate.setFullYear(CookieDate.getFullYear( ) +1);
@@ -202,7 +205,7 @@ var Crypt = {
},
crypt_chat_pass: function(pass) {
var key = btoa(socket.id) + btoa(socket.id);
/*var key = btoa(socket.id) + btoa(socket.id);
key = key.substring(0,32);
key = btoa(key);
var iv = btoa(Crypt.makeiv());
@@ -214,9 +217,10 @@ var Crypt = {
padding: CryptoJS.pad.Pkcs7,
iv: CryptoJS.enc.Base64.parse(iv),
}
);
window.encrypted = encrypted;
return encrypted.toString() + "$" + iv;
);*/
//window.encrypted = encrypted;
return btoa(pass);
//return encrypted.toString() + "$" + iv;
},
crypt_pass: function(pass, userpass) {
@@ -225,7 +229,8 @@ var Crypt = {
} else {
Crypt.tmp_pass = pass;
}
return Crypt.crypt_chat_pass(pass);
//return Crypt.crypt_chat_pass(pass);
return btoa(pass);
},
makeiv: function() {

View File

@@ -151,8 +151,10 @@ function start_auth() {
function emit_list() {
var add = "";
if(private_channel) add = Crypt.getCookie("_uI") + "_";
var p = Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()), true);
if(p == undefined) p = "";
if(socket.id) {
socket.emit("list", {version: parseInt(localStorage.getItem("VERSION")), channel: add + chan.toLowerCase(), pass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()), true)});
socket.emit("list", {version: parseInt(localStorage.getItem("VERSION")), channel: add + chan.toLowerCase(), pass: embed ? '' : p});
} else {
setTimeout(function(){
emit_list();
@@ -346,7 +348,9 @@ function get_list_listener(){
socket.on("get_list", function(){
var add = "";
if(private_channel) add = Crypt.getCookie("_uI") + "_";
socket.emit("list", { offline: offline, version: parseInt(localStorage.getItem("VERSION")), channel: add + chan.toLowerCase(), pass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()), true)});
var p = Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()), true);
if(p == undefined) p = "";
socket.emit("list", { offline: offline, version: parseInt(localStorage.getItem("VERSION")), channel: add + chan.toLowerCase(), pass: embed ? '' : p});
});
}

View File

@@ -557,7 +557,9 @@ var List = {
return;
}
if(!offline || (vote == "del" && (hasadmin && (!w_p && adminpass != "")))){
emit('vote', {channel: chan, id: id, type: vote, adminpass: adminpass == "" ? "" : Crypt.crypt_pass(adminpass), pass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()), true)});
var u = Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()), true);
if(u == undefined) u = "";
emit('vote', {channel: chan, id: id, type: vote, adminpass: adminpass == "" ? "" : Crypt.crypt_pass(adminpass), pass: embed ? '' : u});
} else {
if(vote == "pos"){
List.voted_song(id, (new Date()).getTime()/1000);
@@ -570,7 +572,9 @@ var List = {
skip: function(way) {
if(!offline){
emit('skip', {pass: adminpass == "" ? "" : Crypt.crypt_pass(adminpass), id:video_id, channel: chan.toLowerCase(), userpass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()), true)});
var u = Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()), true);
if(u == undefined) u = "";
emit('skip', {pass: adminpass == "" ? "" : Crypt.crypt_pass(adminpass), id:video_id, channel: chan.toLowerCase(), userpass: embed ? '' : u});
} else {
if(way) {
Player.playNext();

View File

@@ -274,7 +274,9 @@ var Player = {
paused = false;
if(!offline) {
socket.emit("end", {id: video_id, channel: chan.toLowerCase(), pass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()), true)});
var u = Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()), true);
if(u == undefined) u = "";
socket.emit("end", {id: video_id, channel: chan.toLowerCase(), pass: embed ? '' : u});
} else {
Player.playNext();
}
@@ -308,7 +310,9 @@ var Player = {
$("#pause").toggleClass("hide");
}
if((paused || was_stopped) && !offline) {
socket.emit('pos', {channel: chan.toLowerCase(), pass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()), true)});
var u = Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()), true);
if(u == undefined) u = "";
socket.emit('pos', {channel: chan.toLowerCase(), pass: embed ? '' : u});
paused = false;
was_stopped = false;
}
@@ -551,7 +555,9 @@ var Player = {
if(!user_auth_started) {
if(newState.data == 5 || newState.data == 100 || newState.data == 101 || newState.data == 150) {
curr_playing = Player.player.getVideoUrl().replace("https://www.youtube.com/watch?v=", "");
emit("skip", {error: newState.data, id: video_id, pass: adminpass == "" ? "" : Crypt.crypt_pass(adminpass), channel: chan.toLowerCase(), userpass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()), true)});
var u = Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()), true);
if(u == undefined) u = "";
emit("skip", {error: newState.data, id: video_id, pass: adminpass == "" ? "" : Crypt.crypt_pass(adminpass), channel: chan.toLowerCase(), userpass: embed ? '' : u});
} else if(video_id !== undefined) {
Player.loadVideoById(video_id, duration);
@@ -748,7 +754,9 @@ var Player = {
if(!offline) {
Player.player.pauseVideo();
socket.emit("end", {id: video_id, channel: chan.toLowerCase(), pass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()), true)});
var u = Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()), true);
if(u == undefined) u = "";
socket.emit("end", {id: video_id, channel: chan.toLowerCase(), pass: embed ? '' : u});
} else {
Player.playNext();
}

View File

@@ -453,7 +453,9 @@ var Search = {
List.vote(id, "pos");
}
} else {
emit("add", {id: id, start: start, end: end, title: title, adminpass: adminpass == "" ? "" : Crypt.crypt_pass(adminpass), list: chan.toLowerCase(), duration: duration, playlist: playlist, num: num, total: full_num, pass: embed ? '' : Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()), true)});
var u = Crypt.crypt_pass(Crypt.get_userpass(chan.toLowerCase()), true);
if(u == undefined) u = "";
emit("add", {id: id, start: start, end: end, title: title, adminpass: adminpass == "" ? "" : Crypt.crypt_pass(adminpass), list: chan.toLowerCase(), duration: duration, playlist: playlist, num: num, total: full_num, pass: embed ? '' : u});
}//[id, decodeURIComponent(title), adminpass, duration, playlist]);
},

View File

@@ -36,7 +36,7 @@
<link rel="stylesheet" href="/assets/css/jquery-ui.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/css/materialize.min.css">
<link rel="stylesheet" type="text/css" href="/assets/css/{{stylesheet}}" title="Default" />
<script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/js/materialize.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.4/socket.io.slim.js"></script>
<script type="text/javascript" src="/assets/dist/lib/jquery-ui.min.js"></script>
@@ -59,9 +59,6 @@
}
}
</script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/color-thief/2.0.1/color-thief.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/sha256.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js"></script>
<script async type="text/javascript" src="https://www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1"></script>
{{/if}}
</head>