1035 lines
34 KiB
HTML
1035 lines
34 KiB
HTML
<%+header%>
|
|
<script>
|
|
var pduParser = {};
|
|
|
|
pduParser.parse = function(pdu) {
|
|
//Cursor points to the last octet we've read.
|
|
var cursor = 0;
|
|
|
|
var buffer = new Buffer(pdu.slice(0,4), 'hex');
|
|
var smscSize = buffer[0];
|
|
var smscType = buffer[1].toString(16);
|
|
cursor = (smscSize*2+2);
|
|
var smscNum = pduParser.deSwapNibbles(pdu.slice(4, cursor));
|
|
|
|
var buffer = new Buffer(pdu.slice(cursor,cursor+6), 'hex');
|
|
cursor += 6;
|
|
var smsDeliver = buffer[0];
|
|
|
|
var smsDeliverBits = ("00000000"+parseInt(smsDeliver).toString(2)).slice(-8);
|
|
var udhi = smsDeliverBits.slice(1,2) === "1";
|
|
|
|
var senderSize = buffer[1];
|
|
if(senderSize % 2 === 1)
|
|
senderSize++;
|
|
|
|
var senderType = parseInt(buffer[2]).toString(16)
|
|
|
|
var encodedSender = pdu.slice(cursor, cursor + senderSize);
|
|
var senderNum;
|
|
if (senderType === '91') {
|
|
senderNum = pduParser.deSwapNibbles(encodedSender);
|
|
} else if (senderType === 'd0') {
|
|
senderNum = this.decode7Bit(encodedSender).replace(/\0/g, '');
|
|
} else {
|
|
console.error('unsupported sender type.');
|
|
}
|
|
|
|
cursor += senderSize;
|
|
|
|
var protocolIdentifier = pdu.slice(cursor, cursor+2);
|
|
cursor += 2;
|
|
|
|
var dataCodingScheme = pdu.slice(cursor, cursor+2);
|
|
cursor = cursor+2;
|
|
|
|
var encoding = pduParser.detectEncoding(dataCodingScheme);
|
|
|
|
var timestamp = pduParser.deSwapNibbles(pdu.slice(cursor, cursor+14));
|
|
|
|
|
|
var time = new Date;
|
|
time.setUTCFullYear('20'+timestamp.slice(0,2));
|
|
time.setUTCMonth(timestamp.slice(2,4)-1);
|
|
time.setUTCDate(timestamp.slice(4,6));
|
|
time.setUTCHours(timestamp.slice(6,8));
|
|
time.setUTCMinutes(timestamp.slice(8,10));
|
|
time.setUTCSeconds(timestamp.slice(10,12));
|
|
|
|
var firstTimezoneOctet = parseInt(timestamp.slice(12,13));
|
|
var binary = ("0000"+firstTimezoneOctet.toString(2)).slice(-4);
|
|
var factor = binary.slice(0,1) === '1' ? 1 : -1;
|
|
var binary = '0'+binary.slice(1, 4);
|
|
var firstTimezoneOctet = parseInt(binary, 2).toString(10);
|
|
var timezoneDiff = parseInt(firstTimezoneOctet + timestamp.slice(13, 14));
|
|
var time = new Date(time.getTime() + (timezoneDiff * 15 * 1000 * 60 * factor));
|
|
|
|
cursor += 14;
|
|
|
|
var dataLength = parseInt(pdu.slice(cursor, cursor+2), 16).toString(10);
|
|
cursor += 2;
|
|
|
|
if(udhi) { //User-Data-Header-Indicator: means there's some User-Data-Header.
|
|
var udhLength = pdu.slice(cursor, cursor+2);
|
|
var iei = pdu.slice(cursor+2, cursor+4);
|
|
if(iei == "00") { //Concatenated sms.
|
|
var headerLength = pdu.slice(cursor+4, cursor+6);
|
|
var referenceNumber = pdu.slice(cursor+6, cursor+8);
|
|
var parts = pdu.slice(cursor+8, cursor+10);
|
|
var currentPart = pdu.slice(cursor+10, cursor+12);
|
|
}
|
|
|
|
if(iei == "08") { //Concatenaded sms with a two-bytes reference number
|
|
var headerLength = pdu.slice(cursor+4, cursor+6);
|
|
var referenceNumber = pdu.slice(cursor+6, cursor+10);
|
|
var parts = pdu.slice(cursor+10, cursor+12);
|
|
var currentPart = pdu.slice(cursor+12, cursor+14);
|
|
}
|
|
|
|
if(encoding === '16bit')
|
|
if(iei == '00')
|
|
cursor += (udhLength-2)*4;
|
|
else if(iei == '08')
|
|
cursor += ((udhLength-2)*4)+2;
|
|
else
|
|
cursor += (udhLength-2)*2;
|
|
}
|
|
|
|
if(encoding === '16bit')
|
|
var text = pduParser.decode16Bit(pdu.slice(cursor), dataLength);
|
|
else if(encoding === '7bit')
|
|
var text = pduParser.decode7Bit(pdu.slice(cursor), dataLength);
|
|
else if(encoding === '8bit')
|
|
var text = ''; //TODO
|
|
|
|
var data = {
|
|
'smsc' : smscNum,
|
|
'smsc_type' : smscType,
|
|
'sender' : senderNum,
|
|
'sender_type' : senderType,
|
|
'encoding' : encoding,
|
|
'time' : time,
|
|
'text' : text
|
|
};
|
|
|
|
if(udhi) {
|
|
data['udh'] = {
|
|
'length' : udhLength,
|
|
'iei' : iei,
|
|
};
|
|
|
|
if(iei == '00' || iei == '08') {
|
|
data['udh']['reference_number'] = referenceNumber;
|
|
data['udh']['parts'] = parseInt(parts);
|
|
data['udh']['current_part'] = parseInt(currentPart);
|
|
}
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
pduParser.detectEncoding = function(dataCodingScheme) {
|
|
var binary = ('00000000'+(parseInt(dataCodingScheme, 16).toString(2))).slice(-8);
|
|
|
|
if(binary == '00000000')
|
|
return '7bit';
|
|
|
|
if(binary.slice(0, 2) === '00') {
|
|
var compressed = binary.slice(2, 1) === '1';
|
|
var bitsHaveMeaning = binary.slice(3, 1) === '1';
|
|
|
|
if(binary.slice(4,6) === '00')
|
|
return '7bit';
|
|
|
|
if(binary.slice(4,6) === '01')
|
|
return '8bit';
|
|
|
|
if(binary.slice(4,6) === '10')
|
|
return '16bit';
|
|
}
|
|
}
|
|
|
|
pduParser.decode16Bit = function(data, length) {
|
|
//We are getting ucs2 characters.
|
|
var ucs2 = '';
|
|
for(var i = 0;i<=data.length;i=i+4) {
|
|
ucs2 += String.fromCharCode("0x"+data[i]+data[i+1]+data[i+2]+data[i+3]);
|
|
}
|
|
|
|
return ucs2;
|
|
}
|
|
|
|
pduParser.deSwapNibbles = function(nibbles) {
|
|
var out = '';
|
|
for(var i = 0; i< nibbles.length; i=i+2) {
|
|
if(nibbles[i] === 'F') //Dont consider trailing F.
|
|
out += parseInt(nibbles[i+1], 16).toString(10);
|
|
else
|
|
out += parseInt(nibbles[i+1], 16).toString(10)+parseInt(nibbles[i], 16).toString(10);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
pduParser.decode7Bit = function(code, count) {
|
|
//We are getting 'septeps'. We should decode them.
|
|
var binary = '';
|
|
for(var i = 0; i<code.length;i++)
|
|
binary += ('0000'+parseInt(code.slice(i,i+1), 16).toString(2)).slice(-4);
|
|
|
|
var bin = Array();
|
|
var cursor = 0;
|
|
var fromPrevious = '';
|
|
var i = 0;
|
|
while(binary[i]) {
|
|
var remaining = 7 - fromPrevious.length;
|
|
var toNext = 8 - remaining;
|
|
bin[i] = binary.slice(cursor+toNext, cursor+toNext+remaining) + fromPrevious;
|
|
var fromPrevious = binary.slice(cursor, cursor+toNext);
|
|
if(toNext === 8)
|
|
fromPrevious = '';
|
|
else
|
|
cursor += 8;
|
|
i++;
|
|
}
|
|
|
|
var ascii = '';
|
|
for(i in bin)
|
|
ascii += String.fromCharCode(parseInt(bin[i], 2));
|
|
|
|
return ascii;
|
|
}
|
|
|
|
pduParser.encode7Bit = function(ascii) {
|
|
//We should create septeps now.
|
|
var octets = new Array();
|
|
for(var i = 0; i<ascii.length; i++)
|
|
octets.push(('0000000'+(ascii.charCodeAt(i).toString(2))).slice(-7));
|
|
|
|
for(var i in octets) {
|
|
var i = parseInt(i);
|
|
var freeSpace = 8 - octets[i].length;
|
|
|
|
if(octets[i+1] && freeSpace !== 8) {
|
|
octets[i] = octets[i+1].slice(7-freeSpace) + octets[i];
|
|
octets[i+1] = octets[i+1].slice(0, 7-freeSpace);
|
|
}
|
|
}
|
|
|
|
var hex = '';
|
|
for(i in octets)
|
|
if(octets[i].length > 0)
|
|
hex += ('00'+(parseInt(octets[i], 2).toString(16))).slice(-2);
|
|
return hex;
|
|
}
|
|
|
|
//TODO: TP-Validity-Period (Delivery)
|
|
pduParser.generate = function(message) {
|
|
var pdu = '00';
|
|
|
|
var parts = 1;
|
|
if(message.encoding === '16bit' && message.text.length > 70)
|
|
parts = message.text.length / 66;
|
|
|
|
else if(message.encoding === '7bit' && message.text.length > 160)
|
|
parts = message.text.length / 153;
|
|
|
|
parts = Math.ceil(parts);
|
|
|
|
TPMTI = 1;
|
|
TPRD = 4;
|
|
TPVPF = 8;
|
|
TPSRR = 32;
|
|
TPUDHI = 64;
|
|
TPRP = 128;
|
|
|
|
var submit = TPMTI;
|
|
|
|
if(parts > 1) //UDHI
|
|
submit = submit | TPUDHI;
|
|
|
|
submit = submit | TPSRR;
|
|
|
|
pdu += submit.toString(16);
|
|
|
|
pdu += '00'; //TODO: Reference Number;
|
|
|
|
var receiverSize = ('00'+(parseInt(message.receiver.length, 10).toString(16))).slice(-2);
|
|
var receiver = pduParser.swapNibbles(message.receiver);
|
|
var receiverType = 81; //TODO: NOT-Hardcoded PDU generation. Please note that Hamrah1 doesnt work if we set it to 91 (International).
|
|
|
|
pdu += receiverSize.toString(16) + receiverType + receiver;
|
|
|
|
pdu += '00'; //TODO TP-PID
|
|
|
|
if(message.encoding === '16bit')
|
|
pdu += '08';
|
|
else if(message.encoding === '7bit')
|
|
pdu += '00';
|
|
|
|
var pdus = new Array();
|
|
|
|
var csms = randomHexa(2); // CSMS allows to give a reference to a concatenated message
|
|
|
|
for(var i=0; i< parts; i++) {
|
|
pdus[i] = pdu;
|
|
|
|
if(message.encoding === '16bit') {
|
|
/* If there are more than one messages to be sent, we are going to have to put some UDH. Then, we would have space only
|
|
* for 66 UCS2 characters instead of 70 */
|
|
if(parts === 1)
|
|
var length = 70;
|
|
else
|
|
var length = 66;
|
|
|
|
} else if(message.encoding === '7bit') {
|
|
/* If there are more than one messages to be sent, we are going to have to put some UDH. Then, we would have space only
|
|
* for 153 ASCII characters instead of 160 */
|
|
if(parts === 1)
|
|
var length = 160;
|
|
else
|
|
var length = 153;
|
|
}
|
|
var text = message.text.slice(i*length, (i*length)+length);
|
|
|
|
if(message.encoding === '16bit') {
|
|
user_data = pduParser.encode16Bit(text);
|
|
var size = (user_data.length / 2);
|
|
|
|
if(parts > 1)
|
|
size += 6; //6 is the number of data headers we append.
|
|
|
|
} else if(message.encoding === '7bit') {
|
|
user_data = pduParser.encode7Bit(text);
|
|
var size = user_data.length / 2;
|
|
}
|
|
|
|
pdus[i] += ('00'+parseInt(size).toString(16)).slice(-2);
|
|
|
|
if(parts > 1) {
|
|
pdus[i] += '05';
|
|
pdus[i] += '00';
|
|
pdus[i] += '03';
|
|
pdus[i] += csms;
|
|
pdus[i] += ('00'+parts.toString(16)).slice(-2);
|
|
pdus[i] += ('00'+(i+1).toString(16)).slice(-2);
|
|
}
|
|
pdus[i] += user_data;
|
|
}
|
|
|
|
return pdus;
|
|
}
|
|
|
|
|
|
pduParser.encode16Bit = function(text) {
|
|
var out = '';
|
|
for(var i = 0; i<text.length;i++) {
|
|
out += ('0000'+(parseInt(text.charCodeAt(i), 10).toString(16))).slice(-4);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
pduParser.swapNibbles = function(nibbles) {
|
|
var out = '';
|
|
for(var i = 0; i< nibbles.length; i=i+2) {
|
|
if(typeof(nibbles[i+1]) === 'undefined') // Add a trailing F.
|
|
out += 'F'+parseInt(nibbles[i], 16).toString(10);
|
|
else
|
|
out += parseInt(nibbles[i+1], 16).toString(10)+parseInt(nibbles[i], 16).toString(10);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
pduParser.parseStatusReport = function(pdu) {
|
|
//Cursor points to the last octet we've read.
|
|
var cursor = 0;
|
|
|
|
var smscSize = parseInt(pdu.slice(0, 2), 16);
|
|
cursor += 2;
|
|
|
|
var smscType = parseInt(pdu.slice(cursor, cursor+2), 16);
|
|
cursor += 2;
|
|
|
|
var smscNum = pduParser.deSwapNibbles(pdu.slice(cursor, (smscSize*2)+2));
|
|
cursor = (smscSize*2+2);
|
|
|
|
var header = parseInt(pdu.slice(cursor,cursor+2));
|
|
cursor += 2;
|
|
|
|
var reference = parseInt(pdu.slice(cursor,cursor+2), 16);
|
|
cursor += 2;
|
|
|
|
var senderSize = parseInt(pdu.slice(cursor,cursor+2), 16);
|
|
if(senderSize % 2 === 1)
|
|
senderSize++;
|
|
cursor += 2;
|
|
|
|
var senderType = parseInt(pdu.slice(cursor,cursor+2));
|
|
cursor += 2;
|
|
|
|
var sender = pduParser.deSwapNibbles(pdu.slice(cursor, cursor+senderSize));
|
|
|
|
var status = pdu.slice(-2);
|
|
|
|
return {
|
|
smsc:smscNum,
|
|
reference:reference,
|
|
sender:sender,
|
|
status:status
|
|
}
|
|
}
|
|
|
|
function randomHexa(size)
|
|
{
|
|
var text = "";
|
|
var possible = "0123456789ABCDEF";
|
|
for( var i=0; i < size; i++ )
|
|
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
|
return text;
|
|
}
|
|
|
|
|
|
class LuciTable{
|
|
constructor(){
|
|
this.rows = [];
|
|
this.tbody;
|
|
this.fieldset;
|
|
this.init_table();
|
|
}
|
|
|
|
init_table(){
|
|
//create a luci fieldset (class cbi-section)
|
|
var fieldset = document.createElement('fieldset');
|
|
fieldset.className="cbi-section";
|
|
//set fieldset Header name
|
|
var legend = document.createElement('legend');
|
|
var title_span = document.createElement('span');
|
|
title_span.className="panel-title"
|
|
//init table
|
|
var table = document.createElement('table');
|
|
var tbody = document.createElement('tbody');
|
|
table.className="table"
|
|
//save
|
|
this.fieldset = fieldset;
|
|
this.tbody = tbody
|
|
this.title_span = title_span
|
|
this.legend = legend
|
|
|
|
fieldset.appendChild(legend);
|
|
fieldset.appendChild(title_span);
|
|
table.appendChild(tbody)
|
|
fieldset.appendChild(table)
|
|
}
|
|
|
|
new_tr(data,index){
|
|
var type = data.type;
|
|
var col = data.col;
|
|
var left = data.left;
|
|
var right = data.right;
|
|
//clear the row
|
|
this.rows[index].left.innerHTML = "";
|
|
this.rows[index].right.innerHTML = "";
|
|
//set the row
|
|
this.rows[index].left.appendChild(left);
|
|
this.rows[index].right.appendChild(right);
|
|
if (right == null || right == "") {
|
|
this.rows[index].row.style.display = "none";
|
|
}
|
|
else{
|
|
this.rows[index].row.style.display = "";
|
|
}
|
|
}
|
|
|
|
|
|
set title(value){
|
|
this.legend.innerHTML = value;
|
|
this.title_span.innerHTML = value;
|
|
}
|
|
|
|
set object_data(value){
|
|
var row_length = this.rows.length;
|
|
var value_length = Object.keys(value).length;
|
|
if (row_length < value_length) {
|
|
for ( let i = row_length; i < value_length; i++) {
|
|
let row = document.createElement('tr');
|
|
row.className = "tr"
|
|
let cell_left = document.createElement('td');
|
|
cell_left.classList.add("td")
|
|
cell_left.setAttribute("width","33%")
|
|
let cell_right = document.createElement('td');
|
|
cell_right.classList.add("td");
|
|
row.appendChild(cell_left);
|
|
row.appendChild(cell_right);
|
|
this.tbody.appendChild(row);
|
|
var row_dict = {
|
|
"row":row,
|
|
"left":cell_left,
|
|
"right":cell_right,
|
|
}
|
|
this.rows.push(row_dict);
|
|
}
|
|
}
|
|
else if(row_length > value_length){
|
|
for (let i = value_length; i < row_length; i++) {
|
|
this.tbody.removeChild(this.rows[i].row);
|
|
}
|
|
this.rows = this.rows.slice(0,value_length);
|
|
}
|
|
var index = 0;
|
|
for (var key in value) {
|
|
this.rows[index].left.innerHTML = key;
|
|
this.rows[index].right.innerHTML = value[key];
|
|
index++;
|
|
}
|
|
}
|
|
|
|
set array_data(value){
|
|
var row_length = this.rows.length;
|
|
var value_length = value.length;
|
|
if (row_length < value_length) {
|
|
for ( let i = row_length; i < value_length; i++) {
|
|
let row = document.createElement('tr');
|
|
row.className = "tr"
|
|
let cell_left = document.createElement('td');
|
|
cell_left.classList.add("td")
|
|
cell_left.setAttribute("width","33%")
|
|
let cell_right = document.createElement('td');
|
|
cell_right.classList.add("sms_recv_content")
|
|
cell_right.classList.add("td")
|
|
row.appendChild(cell_left);
|
|
row.appendChild(cell_right);
|
|
this.tbody.appendChild(row);
|
|
var row_dict = {
|
|
"row":row,
|
|
"left":cell_left,
|
|
"right":cell_right,
|
|
}
|
|
this.rows.push(row_dict);
|
|
}
|
|
}
|
|
else if(row_length > value_length){
|
|
for (let i = value_length; i < row_length; i++) {
|
|
this.tbody.removeChild(this.rows[i].row);
|
|
}
|
|
this.rows = this.rows.slice(0,value_length);
|
|
}
|
|
for (let i = 0; i < value.length; i++) {
|
|
this.new_tr(value[i],i);
|
|
}
|
|
}
|
|
|
|
set data(value){
|
|
if (value == null) {
|
|
return;
|
|
}
|
|
if (Array.isArray(value)) {
|
|
this.array_data = value;
|
|
}
|
|
else{
|
|
this.object_data = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
class ModemSMS {
|
|
constructor() {
|
|
this.data = null;
|
|
this.cfg_id = null;
|
|
this.modem_cfg_list = [];
|
|
this.sms_recvbox_table = new LuciTable();
|
|
this.sms_send_table = new LuciTable();
|
|
this.sms_storage_table = new LuciTable();
|
|
this.sms_send_table.title = "<%:Send SMS%>";
|
|
this.cbi_map = document.querySelector('.cbi-map');
|
|
this.cbi_map.appendChild(this.sms_storage_table.fieldset);
|
|
this.cbi_map.appendChild(this.sms_recvbox_table.fieldset);
|
|
this.cbi_map.appendChild(this.sms_send_table.fieldset);
|
|
|
|
this.modem_selector = document.getElementById('modem_selector');
|
|
this.create_modem_cfg_selector();
|
|
this.update_modem_cfg_list();
|
|
this.init_send_table_view();
|
|
this.init_msg_box();
|
|
this.init_sms_storage_table();
|
|
}
|
|
|
|
init_sms_storage_table() {
|
|
const createOption = (value, text, disabled = false, selected = false) => {
|
|
const opt = document.createElement('option');
|
|
opt.value = value;
|
|
opt.innerHTML = text;
|
|
opt.disabled = disabled;
|
|
opt.selected = selected;
|
|
return opt;
|
|
};
|
|
|
|
const reading_storage = document.createElement('select');
|
|
const writing_storage = document.createElement('select');
|
|
const etc_storage = document.createElement('select');
|
|
const set_storage_btn = document.createElement('input');
|
|
set_storage_btn.type = "button";
|
|
set_storage_btn.value = "Set";
|
|
set_storage_btn.onclick = () => this.set_sms_storage();
|
|
|
|
const opt_mt = createOption("ME", "<%: Mobile equipment message storage %>");
|
|
const opt_sm = createOption("SM", "<%: (U)SIM message storage %>");
|
|
const opt_loading = createOption("Loading", "<%: Loading... %>", true, true);
|
|
|
|
const storages = [reading_storage, writing_storage, etc_storage];
|
|
storages.forEach(storage => {
|
|
storage.appendChild(opt_mt.cloneNode(true));
|
|
storage.appendChild(opt_sm.cloneNode(true));
|
|
storage.appendChild(opt_loading.cloneNode(true));
|
|
storage.options[2].selected = true;
|
|
});
|
|
|
|
const data = [
|
|
{ "col": 2, "left": document.createTextNode("<%: Reading Storage%>"), "right": reading_storage },
|
|
{ "col": 2, "left": document.createTextNode("<%: Writing Storage%>"), "right": writing_storage },
|
|
{ "col": 2, "left": document.createTextNode("<%: ETC Storage%>"), "right": etc_storage },
|
|
{ "col": 2, "left": document.createTextNode("<%: Set Storage%>"), "right": set_storage_btn }
|
|
];
|
|
|
|
this.sms_storage_table.title = "<%:SMS Storage%>";
|
|
this.sms_storage_table.data = data;
|
|
this.reading_storage = reading_storage;
|
|
this.writing_storage = writing_storage;
|
|
this.etc_storage = etc_storage;
|
|
this.opt_mt1 = storages[0].querySelector('option[value="ME"]');
|
|
this.opt_sm1 = storages[0].querySelector('option[value="SM"]');
|
|
this.opt_mt2 = storages[1].querySelector('option[value="ME"]');
|
|
this.opt_sm2 = storages[1].querySelector('option[value="SM"]');
|
|
this.opt_mt3 = storages[2].querySelector('option[value="ME"]');
|
|
this.opt_sm3 = storages[2].querySelector('option[value="SM"]');
|
|
}
|
|
|
|
init_msg_box()
|
|
{
|
|
var warn_msg_box = document.createElement('div');
|
|
warn_msg_box.className = "cbi-section";
|
|
var warn_msg = document.createElement('div');
|
|
warn_msg.className = "alert";
|
|
warn_msg_box.appendChild(warn_msg);
|
|
this.cbi_map.appendChild(warn_msg_box);
|
|
this.warn_msg_box = warn_msg_box;
|
|
//hide
|
|
this.warn_msg_box.style.display = "none";
|
|
}
|
|
|
|
warn_msg(msg,timeout){
|
|
this.warn_msg_box.style.display = "";
|
|
this.warn_msg_box.classList.add("alert-warning");
|
|
this.warn_msg_box.firstChild.innerHTML = msg;
|
|
setTimeout(()=>{
|
|
this.warn_msg_box.style.display = "none";
|
|
this.warn_msg_box.classList.remove("alert-warning");
|
|
},timeout);
|
|
}
|
|
|
|
log_msg(msg,timeout){
|
|
this.warn_msg_box.style.display = "";
|
|
this.warn_msg_box.classList.add("alert-info");
|
|
this.warn_msg_box.firstChild.innerHTML = msg;
|
|
setTimeout(()=>{
|
|
this.warn_msg_box.style.display = "none";
|
|
this.warn_msg_box.classList.remove("alert-info");
|
|
},timeout);
|
|
}
|
|
|
|
create_modem_cfg_selector(){
|
|
var selector = document.createElement('select');
|
|
selector.addEventListener('change', (event) => {
|
|
this.update();
|
|
this.cfg_id = event.target.value;
|
|
console.log(this.cfg_id);
|
|
});
|
|
this.modem_selector.appendChild(selector);
|
|
this.selector = selector;
|
|
this.poll_info();
|
|
}
|
|
|
|
send(){
|
|
//if content contain non-ascii char, use raw pdu
|
|
var content = this.message_content.value;
|
|
var is_ascii = /^[\x00-\x7F]*$/.test(content);
|
|
if (is_ascii) {
|
|
this.send_raw_pdu();
|
|
}
|
|
else{
|
|
this.send_raw_pdu();
|
|
}
|
|
}
|
|
|
|
send_gsm(){
|
|
var phone_number = this.phone_number.value;
|
|
var message_content = this.message_content.value;
|
|
XHR.get('<%=luci.dispatcher.build_url("admin", "modem", "qmodem", "send_sms")%>',{
|
|
"cfg": this.cfg_id,
|
|
"phone_number": phone_number,
|
|
"message_content": message_content,
|
|
},(x,data)=>{
|
|
this.update();
|
|
data.result.status == 1 ? this.log_msg("<%:Send SMS Success%>",3000) : this.warn_msg("<%:Send SMS Failed%>",3000);
|
|
});
|
|
}
|
|
|
|
send_raw_pdu(){
|
|
var pdu = pduParser.generate(
|
|
{
|
|
text: this.message_content.value,
|
|
encoding: "16bit",
|
|
receiver: this.phone_number.value,
|
|
}
|
|
)
|
|
//if pdu is array,send first pdu
|
|
if (Array.isArray(pdu)) {
|
|
pdu = pdu[0];
|
|
}
|
|
XHR.get('<%=luci.dispatcher.build_url("admin", "modem", "qmodem", "send_sms")%>',{
|
|
"cfg": this.cfg_id,
|
|
"pdu": pdu,
|
|
},(x,data)=>{
|
|
this.update();
|
|
data.result.status == 1 ? this.log_msg("<%:Send SMS Success%>",3000) : this.warn_msg("<%:Send SMS Failed%>",3000);
|
|
});
|
|
}
|
|
|
|
set_sms_storage(){
|
|
let payload;
|
|
let mem1,mem2,mem3;
|
|
mem1 = this.reading_storage.value;
|
|
mem2 = this.writing_storage.value;
|
|
mem3 = this.etc_storage.value;
|
|
payload = {
|
|
"mem1": mem1,
|
|
"mem2": mem2,
|
|
"mem3": mem3,
|
|
}
|
|
XHR.get('<%=luci.dispatcher.build_url("admin", "modem", "qmodem", "modem_ctrl")%>', {
|
|
"cfg": this.cfg_id,
|
|
"action":"set_sms_storage",
|
|
"params": JSON.stringify(JSON.stringify(payload))
|
|
},(x,data)=>{
|
|
this.update();
|
|
});
|
|
}
|
|
|
|
lock(){
|
|
var delete_btns = document.querySelectorAll('input[value="<%:Delete%>"]');
|
|
for (let btn of delete_btns) {
|
|
console.log(btn);
|
|
btn.disabled = true;
|
|
}
|
|
}
|
|
|
|
release(){
|
|
var delete_btns = document.querySelectorAll('input[value="<%:Delete%>"]');
|
|
for (let btn of delete_btns) {
|
|
btn.disabled = false;
|
|
}
|
|
}
|
|
|
|
|
|
init_send_table_view(){
|
|
let phone_number,message_content,send_button,send_raw_pdu_button;
|
|
phone_number = document.createElement('input');
|
|
phone_number.type = "text";
|
|
message_content = document.createElement('textarea');
|
|
message_content.rows = 5;
|
|
message_content.cols = 50;
|
|
send_button = document.createElement('input');
|
|
send_button.type = "button";
|
|
send_button.value = "<%:Send%>";
|
|
|
|
send_button.addEventListener('click',()=>{
|
|
this.send();
|
|
});
|
|
this.phone_number = phone_number;
|
|
this.message_content = message_content;
|
|
this.sms_send_table.data = [
|
|
{
|
|
"col": 2,
|
|
"left": document.createTextNode("<%:Phone Number%>"),
|
|
"right": phone_number,
|
|
},
|
|
{
|
|
"col": 2,
|
|
"left": document.createTextNode("<%:Message Content%>"),
|
|
"right": message_content,
|
|
},
|
|
{
|
|
"col": 2,
|
|
"left": document.createTextNode("<%:Send%>"),
|
|
"right": send_button,
|
|
}
|
|
];
|
|
}
|
|
|
|
poll_info(){
|
|
if (this.cfg_id == null){
|
|
return;
|
|
}
|
|
XHR.poll(10,'<%=luci.dispatcher.build_url("admin", "modem", "qmodem", "get_sms")%>',{
|
|
"cfg": this.cfg_id,
|
|
}, (x,data) => {
|
|
this.combine_messages(data);
|
|
});
|
|
}
|
|
|
|
update(){
|
|
XHR.get('<%=luci.dispatcher.build_url("admin", "modem", "qmodem", "get_sms")%>',{
|
|
"cfg": this.cfg_id,
|
|
}, (x,data) => {
|
|
this.combine_messages(data);
|
|
this.update_sms_capabilities(data);
|
|
});
|
|
}
|
|
|
|
update_modem_cfg_list(){
|
|
XHR.poll(5,'<%=luci.dispatcher.build_url("admin", "modem", "qmodem", "get_modem_cfg")%>',{},(x,data)=>{
|
|
var new_cfg_list = [];
|
|
var cfgs = data.cfgs;
|
|
for (let i = 0; i < cfgs.length; i++) {
|
|
var cfg = cfgs[i];
|
|
var name = cfg.name;
|
|
var value = cfg.cfg;
|
|
new_cfg_list.push({"value":value,"name":name});
|
|
}
|
|
if (new_cfg_list != this.modem_cfg_list) {
|
|
this.cfg_options = new_cfg_list;
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
combine_messages(data){
|
|
var messages,reference_table,msgs;
|
|
messages = []
|
|
reference_table = {}
|
|
msgs = data.msg;
|
|
for ( let msg of msgs){
|
|
let part,total,index,reference,sender,timestamp,content;
|
|
if (msg.reference){
|
|
reference = `${msg.reference}.${msg.sender}`;
|
|
if (reference in reference_table){
|
|
reference_table[reference].push(msg);
|
|
}
|
|
else{
|
|
reference_table[reference] = [msg];
|
|
}
|
|
}
|
|
else{
|
|
msg.index = [msg.index]
|
|
messages.push(msg);
|
|
}
|
|
|
|
|
|
}
|
|
//combile the messages in reference_table
|
|
for (let key in reference_table){
|
|
let reference_msgs = reference_table[key];
|
|
let total = reference_msgs[0].total;
|
|
let part = [];
|
|
let content = "";
|
|
let sender = reference_msgs[0].sender;
|
|
let timestamp = reference_msgs[0].timestamp;
|
|
let index = [];
|
|
reference_msgs.sort((a,b)=>{
|
|
return a.part - b.part;
|
|
});
|
|
for (let reference_msg of reference_msgs){
|
|
content += reference_msg.content;
|
|
part.push(reference_msg.part);
|
|
index.push(reference_msg.index);
|
|
}
|
|
messages.push({
|
|
"sender":sender,
|
|
"timestamp":timestamp,
|
|
"content":content,
|
|
"part":part,
|
|
"total":total,
|
|
"index":index,
|
|
});
|
|
}
|
|
messages.sort((a,b)=>{
|
|
//filter timestamp space and / :
|
|
let at,bt
|
|
at = a.timestamp;
|
|
bt = b.timestamp;
|
|
if (typeof at == "string") {
|
|
at = at.replace(/ /g,"");
|
|
bt = bt.replace(/ /g,"");
|
|
at = at.replace(/\//g,"");
|
|
bt = bt.replace(/\//g,"");
|
|
at = at.replace(/:/g,"");
|
|
bt = bt.replace(/:/g,"");
|
|
}
|
|
|
|
return bt - at;
|
|
});
|
|
this.view = messages;
|
|
|
|
}
|
|
|
|
update_sms_capabilities(data) {
|
|
const sms_capabilities = data.sms_capabilities;
|
|
const storages = [
|
|
{ mem: sms_capabilities.mem1, storage: this.reading_storage, opts: ['opt_mt1', 'opt_sm1'] },
|
|
{ mem: sms_capabilities.mem2, storage: this.writing_storage, opts: ['opt_mt2', 'opt_sm2'] },
|
|
{ mem: sms_capabilities.mem3, storage: this.etc_storage, opts: ['opt_mt3', 'opt_sm3'] }
|
|
];
|
|
|
|
const updateStorage = (mem, storage, mtOpt, smOpt) => {
|
|
let mt = "", sm = "";
|
|
if (mem === "MT" || mem === "ME") {
|
|
if (storage.value === "Loading") storage.value = "ME";
|
|
mt = "[<%:Using%>]";
|
|
} else if (mem === "SM") {
|
|
if (storage.value === "Loading") storage.value = "SM";
|
|
sm = "[<%:Using%>]";
|
|
} else {
|
|
storage.value = "Loading";
|
|
}
|
|
this[mtOpt].innerHTML = mt + me_message_text;
|
|
this[smOpt].innerHTML = sm + sm_message_text;
|
|
};
|
|
|
|
const me_message_text = sms_capabilities.ME.used
|
|
? `<%: Mobile equipment message storage %> <%: (Used/Total) %>(${sms_capabilities.ME.used}/${sms_capabilities.ME.total})`
|
|
: "<%: Mobile equipment message storage %>";
|
|
|
|
const sm_message_text = sms_capabilities.SM.used
|
|
? `<%: (U)SIM message storage %> <%: (Used/Total) %>(${sms_capabilities.SM.used}/${sms_capabilities.SM.total})`
|
|
: "<%: (U)SIM message storage %>";
|
|
|
|
storages.forEach(({ mem, storage, opts }) => updateStorage(mem, storage, opts[0], opts[1]));
|
|
}
|
|
|
|
set cfg_options(value){
|
|
var longger = this.modem_cfg_list.length > value.length ? this.modem_cfg_list : value;
|
|
if (longger.length == 0) {
|
|
return;
|
|
}
|
|
for (let i = 0; i < longger.length; i++) {
|
|
var option = this.selector.options[i];
|
|
if (i < value.length) {
|
|
if (i >= this.selector.options.length) {
|
|
option = document.createElement('option');
|
|
this.selector.appendChild(option);
|
|
}
|
|
option.value = value[i].value;
|
|
option.innerHTML = value[i].name;
|
|
|
|
}
|
|
else{
|
|
this.selector.removeChild(option);
|
|
}
|
|
}
|
|
this.cfg_id = this.selector.value;
|
|
this.modem_cfg_list = value;
|
|
this.update();
|
|
}
|
|
|
|
set view(data){
|
|
this.data = data;
|
|
if (data == null) {
|
|
return;
|
|
}
|
|
var enties = []
|
|
|
|
|
|
var msgs = data;
|
|
for (let msg of msgs){
|
|
let sender,timestamp,content,part,total,index;
|
|
sender = msg.sender;
|
|
if (typeof(msg.timestamp) == "number") {
|
|
timestamp = new Date(msg.timestamp*1000).toISOString()
|
|
}
|
|
else{
|
|
timestamp = msg.timestamp;
|
|
}
|
|
content = msg.content;
|
|
part = msg.part;
|
|
total = msg.total;
|
|
index = msg.index;
|
|
let sender_strong,timestamp_strong,content_strong,part_strong,total_strong,delete_btn;
|
|
let left,right;
|
|
left = document.createElement('div');
|
|
right = document.createElement('div');
|
|
delete_btn = document.createElement('input');
|
|
delete_btn.type = "button";
|
|
delete_btn.value = "<%:Delete%>";
|
|
delete_btn.addEventListener('click',()=>{
|
|
this.lock();
|
|
XHR.get('<%=luci.dispatcher.build_url("admin", "modem", "qmodem", "delete_sms")%>',{
|
|
"cfg": this.cfg_id,
|
|
"index": index.sort((a,b)=>{return b-a}).join(" "),
|
|
},(x,data)=>{
|
|
this.update();
|
|
this.release
|
|
});
|
|
});
|
|
sender_strong = document.createElement('strong');
|
|
sender_strong.innerHTML = `<%:Sender%>: ${sender}`;
|
|
timestamp_strong = document.createElement('strong');
|
|
timestamp_strong.innerHTML = `<%:Timestamp%>: ${timestamp}`;
|
|
content_strong = document.createElement('strong');
|
|
content_strong.innerHTML = `<%:Content%>: ${content}`;
|
|
left.appendChild(sender_strong);
|
|
left.appendChild(document.createElement('br'));
|
|
left.appendChild(timestamp_strong);
|
|
left.appendChild(document.createElement('br'));
|
|
left.appendChild(delete_btn);
|
|
right.appendChild(content_strong);
|
|
if (part != null) {
|
|
part_strong = document.createElement('strong');
|
|
part_strong.innerHTML = `(${part}/${total})`;
|
|
right.appendChild(document.createElement('br'));
|
|
right.appendChild(part_strong);
|
|
}
|
|
enties.push({
|
|
"col" : 2,
|
|
"left" : left,
|
|
"right" : right
|
|
});
|
|
}
|
|
|
|
|
|
this.sms_recvbox_table.data = enties;
|
|
this.sms_recvbox_table.title = "SMS";
|
|
}
|
|
}
|
|
|
|
|
|
|
|
window.onload = function(){
|
|
const getSMS = new ModemSMS();
|
|
}
|
|
</script>
|
|
|
|
<style>
|
|
.sms_recv_content {
|
|
text-align: left;
|
|
word-wrap: break-word;
|
|
white-space: normal;
|
|
overflow-wrap: break-word;
|
|
}
|
|
</style>
|
|
<div>
|
|
<div class="cbi-map">
|
|
<fieldset class="cbi-section">
|
|
|
|
<table class="table">
|
|
<tbody>
|
|
<tr class="tr">
|
|
<td class="td" width="33%"><%:Modem Name%></td>
|
|
<td class="td" id="modem_selector">
|
|
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</fieldset>
|
|
</div>
|
|
</div>
|
|
<%+footer%>
|