﻿// ================================================================================================================ 
// =========================================== модуль для работы с ЭЦП  =========================================== 
// ================================================================================================================ 

// -------------------------------------------------------------------------------------------------------
// 										ВНЕШНИЕ ПАРАМЕТРЫ
//-------------------------------------------------------------------------------------------------------

////// путь к архиву с апплетом
//var applet_url = "/MBNKWeb/Scripts/java/modest_signer.jar";
//// путь к апплету
//var applet_codebase = "/MBNKWeb/Scripts/java/";
//// путь к обработчику
//var controller_url = "/MBNKWeb/Home/SignDocument";
//// путь к скрипту, выдающему метку времени
//var timeserver_url = "/MBNKWeb/Home/TimestampPHP";

// путь к архиву с апплетом
var applet_url = "/Scripts/java/modest_signer.jar";
// путь к апплету
var applet_codebase = "/Scripts/java/";
// путь к обработчику
var controller_url = "/Login/SignSerial";
// путь к скрипту, выдающему метку времени
var timeserver_url = "/Login/GetTimestamp";
// для отключения запуска апплета во внешнем скрипте должна быть установлена переменная global_use_ecp = 0 
// -------------------------------------------------------------------------------------------------------
// 										ВНЕШНИЕ ФУНКЦИИ
// -------------------------------------------------------------------------------------------------------

//	Подписать данные и отправить на сервер.					
// параметры: форма, модуль-получатель формы, функция обработки формы, флаг использования ЭЦП (1-подписывать, 0 - не подписывать) 
function processForm(sendform, mod, func, useECP) {
	src_form = sendform;
	dest_module = mod;
	dest_func = func;
	ECP = useECP; 
	if (useECP && is_applet_ready==0) {
		outError(0, "Ошибка: апплет еще не готов !");
	} else {
		// после получения таймстемпа запускается sendDocument()
		GetTimestamp(timeserver_url);
	}
}


// Загрузить сертификат открытого ключа, активного на компьютере пользователя, и информацию о нем
function loadCertInfo() {
	startTimer();
	funcOrder += "document.getElementById('signapp').readCert()\n";
	document.getElementById('signapp').readCert();
}

// -------------------------------------------------------------------------------------------------------
// 										ВНЕШНИЕ ОБРАБОТЧИКИ СОБЫТИЙ
// -------------------------------------------------------------------------------------------------------

// вывод и обработка прочитанного сертификата
function setCertLoaded(certstr,info, sn) {
	try{onCertLoaded(certstr,info, sn);}catch(e){}
}

//	вывод и обработка ответа сервера (результат # код ошибки # расшифровка ошибки)		
function setResult(res) {
	try{onResult(res);}catch(e){}
}

// вывод сообщений об ошибке
function outError(errorcode, errormsg) {
	try{onError(errorcode, errormsg);}catch(e){}
}

// вывод информации о процессе подписи/ отправки
function outStatus(stat) {
	try{onStatus(stat);}catch(e){}
}

function setAppletPreReady() {
	try{onAppletPreReady();}catch(e){}
}

function setAppletReady() {
	is_applet_ready = 1;
	try{onAppletReady();}catch(e){}
}




// -------------------------------------------------------------------------------------------------------
// 										ВНУТРЕННИЕ ФУНКЦИИ
// -------------------------------------------------------------------------------------------------------

// локальные переменные
var start = new Date();
var src_form = "";
var dest_module = ""; 
var dest_func = "";	
var timestamp = 0;
var timestamp_try_count = 0;
var ECP = -1; // внутренняя переменная , устанавливается при вызове функции обработки формы
var timeout;
var loading_timeout;
var is_applet_ready = 0;
var is_applet_preready = 0;
var current_state = -1;
var timer_enabled = 1;
var timer_counter = 0;

var funcOrder="";

//setTimeout( function(){ initApplet(); }, 500);
// инициализация апплета

function initApplet() {
	if (document.getElementById('signapp')) {
		return document.getElementById('signapp');
	}
	try{
		if (global_use_ecp==0) {
			setAppletReady();
			return null;
		} 
	} catch(e){}
	
	outStatus("Загрузка...");
	appletEl = document.createElement('DIV');
    document.body.appendChild(appletEl);
	appletEl.style.position = 'absolute';
	appletEl.style.overflow = 'hidden';
	appletEl.style.height = '1px';
	appletEl.style.width = '1px';
	appletEl.id = 'appletblock';
	appletcode = '<applet mayscript="mayscript" code = "ru.eoil.es.signer.Main" name = "Signer1" id = "signapp" codebase = "' + applet_codebase + '" archive="' + applet_url + '" hspace=0 vspace=0>';
	
	try{
		if (silent_mode == 1) {
			appletcode+='<param name=silent value=true>';
		}
	}catch(e){}

	appletcode+='</applet>';
	if (!$('#resultdiv')) {
		appletcode += '<div id="resultdiv" style="display:none;">';
	}
	appletcode += '</div>';
	appletEl.innerHTML = appletcode;
	
	outStatus("Поиск ключей...");
	loading_timeout = setTimeout( function(){ outError(1, "Ошибка: не установлена Java-машина"); }, 60000);
	startTimer();
	return document.getElementById('signapp');
}



function stopTimer() {
	timer_enabled = 0;
//	alert('timer stops');

}

function startTimer() {
	timer_enabled = 1;
	timer_counter = 0;
//	alert('timer starts');
	doOnTimer();
}


function doOnTimer() {
	timer_counter++;
	checkAppletStatus();
	if (timer_counter<3000 && timer_enabled==1) {
		window.setTimeout('doOnTimer();', 100);
	} 
}

function checkAppletStatus() {
	if (!document.getElementById('signapp')) {
		outError("Апплет не запущен");
	} else {
		
		var applet_state = -1;
		try {
		    funcOrder += "document.getElementById('signapp').getState()\n";
			applet_state = document.getElementById('signapp').getState();
			clearTimeout(loading_timeout);
//			alert(applet_state);
		} catch(err) {
			outError("Невозможно получить состояние апплета");
			stopTimer();
		}
		if(applet_state!=current_state) {
			//outStatus(timer_counter + " stat:"+applet_state);
			current_state = applet_state;
			//alert(applet_state);
			switch (applet_state) {
				case 1: setAppletPreReady(); break;
				case 2: break;
				case 3: setAppletReady(); stopTimer(); break;
				case 4: 
					try {
					    outStatus("Документ успешно подписан");

					    funcOrder += "document.getElementById('signapp').getSignedDocumentDoc()\n";
					    var doc = document.getElementById('signapp').getSignedDocumentDoc();
					    funcOrder += "document.getElementById('signapp').getSignedDocumentSign()\n";
					    var sign = document.getElementById('signapp').getSignedDocumentSign();
//						document.write('<pre>'+doc+'</pre>');
//						document.write('<pre>'+sign+'</pre>');
						stopTimer(); 
						SigVerify(doc, sign);
					} catch (err) {
						outError("Невозможно получить подписанный документ.  Апплет не отвечает");
					}; 
				break;
				case 5: 
					try { 
						outStatus("Сертификат успешно прочитан");
						funcOrder += "document.getElementById('signapp').getSignedDocumentDoc()\n";
						var cert = document.getElementById('signapp').getPublicKeyCertificate();
						funcOrder += "document.getElementById('signapp').getPublicKeyCertificateInfo()\n";
						var info = document.getElementById('signapp').getPublicKeyCertificateInfo();
						funcOrder += "document.getElementById('signapp').getPublicKeyCertificateSerialNumber()\n";
						var sn = document.getElementById('signapp').getPublicKeyCertificateSerialNumber();
						stopTimer(); 
						setCertLoaded(cert, info, sn);
					} catch (err) {
						outError("Невозможно получить сертификат откртого ключа. Апплет не отвечает");
					}; 
				break;		
				case -1: outError("Криптопровайдер JCP не установлен"); 	stopTimer();  break;
				case -2: outError("Ошибка чтения хранилища ключевой пары"); stopTimer(); break;
				case -3: outError("Ошибка загрузки закрытого ключа"); stopTimer(); break;
				case -4: outError("Закрытый ключ не заружен"); stopTimer(); break;
				case -5: outError("Пустой документ"); stopTimer(); break;
				case -6: outError("Ошибка формирования ЭЦП"); stopTimer(); break;
				case -7: outError("Ошибка загрузки открытого ключа"); stopTimer(); break;
				case -8: outError("Не удается сформировать сертификат открытого ключа"); stopTimer(); break;
				default:	
			}
		}
	}
}











// -------------------------------------------------------------------------------------------------------
// 										ФУНКЦИИ ДЛЯ РАБОТЫ С СЕРВЕРОМ
// -------------------------------------------------------------------------------------------------------

//// Подписать данные/ или отправить на сервер без подписи
//function sendDocument() {
//	XMLdoc = parsetree(src_form); // преобразование формы в XML
//	if (ECP==1) {
//		outStatus("Создание цифровой подписи...");
//		timeout = setTimeout( function(){ outError(0, "Ошибка: Ошибка загрузки апплета, перезагрузите страницу") }, 5000);
//		startTimer();
//		funcOrder += "document.getElementById('signapp').sign(XMLdoc)\n";
//		document.getElementById('signapp').sign(XMLdoc); // вызов внутренней функции апплета, передача XML
//	} else {
//		outStatus("Отправка данных...");
//		var s = makestring(XMLdoc,"0",dest_module,dest_func,"0");
//		transfer(s, controller_url);
//	}
//}

// Подписать данные/ или отправить на сервер без подписи
function sendDocument() {
    var DataHash = src_form; // преобразование формы в XML
    if (ECP == 1) {
        outStatus("Создание цифровой подписи...");
        timeout = setTimeout(function () { outError(0, "Ошибка: Ошибка загрузки апплета, перезагрузите страницу") }, 5000);
        startTimer();
        funcOrder += "document.getElementById('signapp').sign(XMLdoc)\n";
        document.getElementById('signapp').sign(DataHash); // вызов внутренней функции апплета, передача XML
    } else {
        outStatus("Отправка данных...");
        var s = makestring(DataHash, "0", dest_module, dest_func, "0");
        transfer(s, controller_url);
    }
}

// Отправить подписанные данные на сервер	
function SigVerify(doctext, signature) {
	clearTimeout(timeout);
	outStatus("Идет проверка электронной цифровой подписи ...");
	var s = makestring(doctext, signature, dest_module, dest_func, "1");
	//	отправка данных модулю проверки подписи
	transfer(s, controller_url);
}


var xmlhttp=false;
/*@cc_on @*/
/*@if (@_jscript_version >= 5)
try{xmlhttp=new ActiveXObject("Msxml2.XMLHTTP");}catch(e){try{xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");}catch(E){xmlhttp=false;}}
@end @*/
if (!xmlhttp && typeof XMLHttpRequest!='undefined') {xmlhttp = new XMLHttpRequest();}


//									Отправка на сервер 						
function transfer(str,addr) {
	var timeout1 = setTimeout( function(){ xmlhttp.abort(); outError(0, "Ошибка: Нет ответа от сервера") }, 30000);
	var rand = Math.floor(Math.random()*100000)+1;
	try {
		xmlhttp.abort();
		xmlhttp.open("POST", addr+"?uniqueid="+rand, true);
		xmlhttp.onreadystatechange=function() {
			if (xmlhttp.readyState==4) {
				clearTimeout(timeout1);
				if (xmlhttp.status==200) {
					try {
						setResult(xmlhttp.responseText);
					} catch(err) {outError(0, err);}
				} else outError(0, "Ошибка: Модуль обработки недоступен"); 
			} 
		}
		xmlhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
		xmlhttp.setRequestHeader('Content-length', str.length);
		xmlhttp.setRequestHeader('Connection', 'close');
		xmlhttp.send(str);
		
	} catch (err) {outError(0, err);}
	xmlhttp.close;
}


var xmlhttp2=false;
/*@cc_on @*/
/*@if (@_jscript_version >= 5)
try{xmlhttp2=new ActiveXObject("Msxml2.XMLHTTP");}catch(e){try{xmlhttp2=new ActiveXObject("Microsoft.XMLHTTP");}catch(E){xmlhttp2=false;}}
@end @*/
if (!xmlhttp2 && typeof XMLHttpRequest!='undefined') {xmlhttp2 = new XMLHttpRequest();}


// получение метки времени с сервера
function GetTimestamp(addr) {
	var timeout2 = setTimeout( function(){ xmlhttp.abort(); outError(0, "Ошибка: Сервер времени не отвечает") }, 10000);
	var str = " ";
	var rand = Math.floor(Math.random()*100000)+1;
	try {
		xmlhttp2.abort();
		xmlhttp2.open("POST", addr, true);
		xmlhttp2.onreadystatechange=function() {
			if (xmlhttp2.readyState==4) {
				clearTimeout(timeout2);
				if (xmlhttp2.status==200) {
					try {
						timestamp = xmlhttp2.responseText;
						sendDocument();
					} catch(err) {outError(0, err);}
				} else outError(0, "Ошибка: Модуль выдачи метки времени недоступен"); 
			}
		}
		xmlhttp2.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
		xmlhttp2.setRequestHeader('Content-length', str.length);
		xmlhttp2.setRequestHeader('Connection', 'close');
		xmlhttp2.send(str);
		
	} catch (err) {outError(0, err);}
	xmlhttp2.close;
}


// -------------------------------------------------------------------------------------------------------
// 										СЛУЖЕБНЫЕ ФУНКЦИИ
// -------------------------------------------------------------------------------------------------------

// вызов элемента страницы
//function $(v) {return(document.getElementById(v));}


// вывод в блок вывода результата
//function out(t) {
//	document.getElementById('resultdiv').innerHTML = document.getElementById('resultdiv').innerHTML + '<br>' + t;
//}

// получение времени, прошедшего с начала загрузки страницы
//function getTimeout(message) {
//	var currTime = new Date();
//	var workTime = currTime.getTime() - start.getTime();
	//out("Time:" + workTime + " мс , " + message); 
	//alert("Time:" + workTime + " мс , " + message); 
//}


//						Формирование строки запроса
// входные данные: 
//	документ, 
//	подпись, 
//	модуль назначения, 
//	функция назначения,
//	флаг использвания эцп    						
function makestring(str_doc, str_sig, str_mod, str_func, use_ecp) {
    var _makestring = 'serial=' + encodestr(str_doc.toString().replace(/\r/g, "<br>").replace(/\n/g, "<br>"));
    _makestring += '&sig=' + encodestr(str_sig.toString().replace(/\r/g, "<br>").replace(/\n/g, "<br>"));
    _makestring += '&mod=' + encodestr(str_mod.toString().replace(/\r/g, "<br>").replace(/\n/g, "<br>"));
    _makestring += '&func=' + encodestr(str_func.toString().replace(/\r/g, "<br>").replace(/\n/g, "<br>"));
    _makestring += '&useecp=' + use_ecp;
    return _makestring;
}

//						Проебразование строки для пересылки						
function encodestr(str) {
	var trans = [];
	for (var i = 0x410; i <= 0x44F; i++)
	trans[i] = i - 0x350; // А-Яа-я
	trans[0x401] = 0xA8;    // Ё
	trans[0x451] = 0xB8;    // ё
	
	var ret = [];
	// Составляем массив кодов символов, попутно переводим кириллицу
	for (var i = 0; i < str.length; i++) {
		var n = str.charCodeAt(i);
		if (typeof trans[n] != 'undefined')
			n = trans[n];
		if (n <= 0xFF)
			ret.push(n);
	}
	return escape(String.fromCharCode.apply(null, ret));
}


//						Преобразование формы в формат XML 						
function parsetree(root){
	var XML = "<"+"?xml version='1.0' encoding='WINDOWS-1251'?>"+"<form>";
	var inputs=document.getElementsByTagName('INPUT');
	var atts;
	for(i=0;i<inputs.length;i++) {
		if (inputs[i].type == "text" || inputs[i].type == "hidden") {
			atts = {name:inputs[i].name, value: inputs[i].value};
			XML += element('INPUT', '', atts);
		} else if (inputs[i].type == "radio" || inputs[i].type == "checkbox") {
			if (inputs[i].checked) {
				atts = {name:inputs[i].name, value: inputs[i].value};
				XML += element('INPUT', '', atts);
			}
		} 
	}
	var selects=document.getElementsByTagName('SELECT');
	for(i=0;i<selects.length;i++) {
		atts = {name:selects[i].name, value: selects[i].value};
		XML += element('SELECT', '', atts);
	}
	atts = {name:'timestamp', value: timestamp};
	XML += element('INPUT', '', atts)
	XML += "</form>";
	return XML;
}




//						Добавление элемента дерева в XML
function element(name,content,attributes){
	var att_str = '';
	if (attributes) {
		att_str = formatAttributes(attributes);
	}
	var xml;
	if (!content){
		xml='<' + name + att_str + '/>';
	}
	else {
		xml='&lt;' + name + att_str + '&gt;' + content + '&lt;/'+name+'&gt;';
	}
	return xml;
}
var APOS = "'"; QUOTE = '"';
var ESCAPED_QUOTE = {  };
ESCAPED_QUOTE[QUOTE] = '&quot;';
ESCAPED_QUOTE[APOS] = '&apos;';
   

//						Форматирует атрибуты в тэгах XML 						    
function formatAttributes(attributes) {
	var att_value;
	var apos_pos, quot_pos;
	var use_quote, escape, quote_to_escape;
	var att_str;
	var re;
	var result = '';
	
	for (var att in attributes) {
		att_value = attributes[att];
		
		// находит первые каывычки (одинарные и двойные)
		apos_pos = att_value.indexOf(APOS);
		quot_pos = att_value.indexOf(QUOTE);
		
		// определение вида кавычек вокруг атрибута
		if (apos_pos == -1 && quot_pos == -1) {
			att_str = ' ' + att + "='" + att_value +  "'";
			result += att_str;
			continue;
		}
		
		if (quot_pos != -1 && quot_pos < apos_pos) {
			use_quote = APOS;
		} else {
			use_quote = QUOTE;
		}
	
		// Выбираем, какие кавычки оставить, вместо if-else используем словарик
		escape = ESCAPED_QUOTE[use_quote];
		
		// оставляем только правильные кавычки
		re = new RegExp(use_quote,'g')
		att_str = ' ' + att + '=' + use_quote + 
			att_value.replace(re, escape) + use_quote;
		result += att_str;
	}
	return result
}



