<?
/**
* Equa bank HTTP API
*
* @copyright Martin Zvarík, https://zvarik.cz/cs/equabank-api
*
* @license	Jen pro potřeby vlastníka, zákaz dalšího prodeje nebo sdílení.
*			Only for the needs of the owner, further resell or share is prohibited
*
* @note Funguje jen pro osobní účty!
*
* @version 2016-05-03
*/
class EquaBank
{	
	private $ch; // curl
	private $curl_url;
	
	private $host;
	private $base_url;
	
	private $ucty; // seznam účtů
	
	private $file_encoding = 'WINDOWS-1250';
	
	
	/**
	* CURL_EXEC
	*/
	private function curl_redir_exec($request_url, $post_data = null, $site_encoding = 'UTF-8')
	{
		$ch =& $this->ch;
		$this->curl_url = $request_url;
		
		curl_setopt($ch, CURLOPT_URL, $request_url);
		if (isset($post_data)) {
			curl_setopt($ch, CURLOPT_POST, true);
			if (is_array($post_data)) $post_data = http_build_query($post_data);
			curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
			curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
		} else {
			curl_setopt($ch, CURLOPT_POST, false);
			curl_setopt($ch, CURLOPT_POSTFIELDS, '');
			curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
		}
		
		$res = curl_exec($ch);
		$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
		
		
		//------- UNCOMMENT FOR DEBUGGING
		/*echo "<hr style='height:10px;background:#000'>";
		echo (isset($post_data) ? "POST " : "GET ").$request_url."<br>";
		echo $http_code."<br><pre>";
		echo htmlspecialchars($res)."</pre><br>";
		echo "<hr><br>\n<br>\n";*/
		
		$res = iconv($site_encoding, $this->file_encoding.'//TRANSLIT//IGNORE', $res);
		
		return $res;
	}
	
	/**
	 * CHYBA
	 */
	function err($msg = '')
	{
		if (!$msg) $msg = "Server je nedostupný.<br>Server is unavailable /or library needs update."; // může se ukázat ojediněle a po dalším reloadu v pořádku!! (nutno zjistit co server vrací)
		if (strpos($msg, '<br>') !== false) {
			$msg = str_replace("<br>", "<br><span style='color:#444'>", $msg);
			$msg.= '</span>';
		}
		die("EquaBank API: ".$msg);
	}
	
	/**
	* LOGIN
	*
	* @param (string) Uživatelské jméno
	* @param (string) Heslo
	*/
	function __construct($username, $password)
	{
		$this->host = 'www.equabanking.cz';
		$this->base_url = 'https://'.$this->host.'/IBS';
		
		
		$fp = fopen('cookiefile', 'w');
		fclose($fp);
		
		//--------------------- globální nastavení CURL
		$ch =& $this->ch;
		
		$ch = curl_init();
		
		$header = array();
		$header[] = 'Host: '.$this->host;
		$header[] = 'User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:45.0) Gecko/20100101 Firefox/45.0'; // $_SERVER['HTTP_USER_AGENT']
		$header[] = 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8';
		$header[] = 'Accept-Language: cs,en-us;q=0.7,en;q=0.3';
		$header[] = 'Accept-Encoding: none';
		$header[] = 'Connection: keep-alive';
		
		curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
		
		curl_setopt($ch, CURLOPT_COOKIESESSION, false); // COOKIES ARE NOT NEEDED
		//curl_setopt($ch, CURLOPT_COOKIE, ""); // cookies content
		//curl_setopt($ch, CURLOPT_COOKIEFILE, "cookiefile");
		//curl_setopt($ch, CURLOPT_COOKIEJAR, "cookiefile");
		
		curl_setopt($ch, CURLOPT_HEADER, false);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
		curl_setopt($ch, CURLOPT_TIMEOUT, 5);
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
		//curl_setopt($ch, CURLOPT_SSLVERSION, 3); // v komentarich nekdo psal, ze mu to jinak neslo

		//curl_setopt($ch, CURLOPT_REFERER, $this->base_url); // referer - se nepoužívá
		
		curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); // 10 sekund timeout
		
		
		$res = $this->curl_redir_exec($this->base_url.'/ControllerServlet'); // login main frame
		
		preg_match_all('~src="([^"]+)"~', $res, $mx);
		if (!$mx) err();
		$res = $this->curl_redir_exec($this->base_url.'/'.$mx[1][0]); // session blank page
		$res = $this->curl_redir_exec($this->base_url.'/'.$mx[1][1]); // login page frame
		
		
		$header[] = 'Content-Type: application/x-www-form-urlencoded';
		curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
		
		$data = array(
			'javaVM' => 7,
			'command' => 'auth_loginByPasswordPage',
			'staticCmd' => 'null',
		);
		$res = $this->curl_redir_exec($this->base_url.'/ControllerServlet', $data); // POST to get the login page
		
		preg_match("~'(ControllerServlet;jsessionid=([^']+))'~i", $res, $m);
		$ucontrol = $m[1];
		
		preg_match('~name="OTOKey" value=\'(\d+)\'~', $res, $m);
		
		$otokey = $m[1];
    
		$data = 'command=auth_loginByPassword&back_command=&back_custom1=&back_custom2=&back_custom3=&back_custom4=&back_custom5='
			.'&checkInputs=false&typeOfLogin=mk&username='.$username.'&password='.$password.'&clientusername=&CIF=&GFOSessionID=&appIdent=&langID='
			.'&actChannelID=&actPartnerID=&actPersonID=&suppModule=eTpkcs11&module=eTpkcs11&keyStoreType=eTpkcs11&protocol=pkcs11://'
			.'&OTOKey='.$otokey.'&javaVM=&CurrentTime='.date("H").'&loginByMK_username='.$username.'&loginByMK_password='.$password;
			
		$res = $this->curl_redir_exec($this->base_url.'/'.$ucontrol, $data);
		
		// jsme přihlášeni
		// máme k dispozici seznam účtů, ale přeskočíme
		
		preg_match("~'(ControllerServlet;jsessionid=([^']+))'~i", $res, $m);
		$ucontrol = $m[1];
		$this->ucontrol = $ucontrol;
		
		//------ HISTORIE TRANSAKCÍ
		$data = 'command=mov_filterPage&custom1=0&custom2=&custom3=&custom4=&custom5=&operID=&filterString=&verificationGroupName=&requiredList=&removeCommand='
			.'&back_command=&back_custom1=&back_custom2=&back_custom3=&back_custom4=&back_custom5=&back_operID=&saveTemplate=&paymentTemplateName=';
		
		$res = $this->curl_redir_exec($this->base_url.'/'.$ucontrol, $data);
		
		//--- DOŠLO KE ZMĚNĚ "JSID" !!!
		preg_match("~'(ControllerServlet;jsessionid=([^']+))'~i", $res, $m);
		$ucontrol = $m[1];
		$this->ucontrol = $ucontrol;
		
		
		// zjistíme seznam účtů
		$this->ucty = array();
		
		$dm = new DataMiner($res);
		$dm->setBound('<h2>Moje účty</h2>');
		while ($heading = $dm->get('h3'))
		{
			$total = $dm->get('p');
			$total = $this->format_num($total);
			
			preg_match("~'(\d{3,})','(\d{3,})'~", $heading, $m);
			
			$cislo = strval($m[1]);
			$id = $m[2];
			
			$this->ucty[ $cislo ] = array(
				'nazev' => strip_tags($heading),
				'cislo_uctu' => $cislo,
				'zustatek' => $total,
				'pid' => $id,
			);
		}
		
		
		// HOTOVO
	}
	
	
	/**
	* Seznam účtů
	*/
	function ucty()
	{
		return $this->ucty;
	}
	
	
	/**
	* Odpojení
	*/
	function __destruct()
	{
		curl_close($this->ch);
		unset($this->ch);
	}
	
	
	/**
	* Výpis transakcí
	*
	* @param (string) číslo účtu - null = všechny účty
	* @param (string) od kdy - výchozí je posledních 31 dní, lze zadat YYYY-MM-DD nebo záporné číslo (počet posledních dní)
	* @param (string) do kdy - výchozí je do dnes, jinak YYYY-MM-DD
	
	* @return (array) transakce
	*/
	function transakce($ucet = null, $od = null, $do = null)
	{
		$csv = $this->_transakce('CSV', $ucet, $od, $do);
		
		$rows = explode("\n", $csv);
		
		$data = array();
		
		$mapkeys = array();
		$col2map = array();
		
		$rowi = 0;
		$line = 0;
		foreach ($rows as $row)
		{
			$cols = $this->str_getcsv($row);
			
			$line++;
			if ($line == 1) {
				$mapkeys = array(
					"Číslo účtu klienta" => "klient_ucet",
					"IBAN účtu klienta" => "klient_iban",
					"Číslo účtu protistrany" => "prijemce_ucet",
					"Název účtu protistrany" => "prijemce_nazev",
					"Datum splatnosti" => "datum",
					"Datum vystavení" => "datum_zauctovani",
					"Částka" => "castka",
					"Měna" => "mena",
					"Typ pohybu" => "typ",
					"Popis pohybu" => "popis",
					"Kategorie" => "kategorie",
					"Kód transakce" => "kod",
					"Variabilní symbol" => "VS",
					"Specifický symbol" => "SS",
					"Konstantní symbol" => "KS",
				);
				foreach ($cols as $coli => $col) {
					$col = trim($col);
					if (isset($mapkeys[$col])) {
						$col2map[$coli] = $mapkeys[$col];
					}
				}
				continue; // konec hlavičky
			}
			
			if (!$cols[0]) continue; // prázdný řádek
			
			foreach ($cols as $coli => $col)
			{
				if (!isset($col2map[$coli])) continue;
				
				$col = trim($col);
				$key = $col2map[$coli];
				
				if ($key == 'castka') $col = $this->format_num($col);
				if ($key == 'datum' || $key == 'datum_zauctovani') {
					$arr = explode('.', $col);
					if ($arr[0] <= 31) $arr = array_reverse($arr);
					$col = vsprintf("%04d-%02d-%02d", $arr);
				}
				
				$data[$rowi][ $key ] = $col;
			}
			
			$rowi++;
		}
		
		return $data;
	}
	
	//------------------------ str_getcsv
	function str_getcsv($s)
	{
		$s = preg_replace_callback('/"(.*?)"/s', array($this, 'str_getcsv_cb'), $s);
		$arr = explode(";", $s);
		foreach ($arr as &$str) {
			$str = str_replace("!SEMICOLON!", ";", $str);
		}
		return $arr;
	}
	function str_getcsv_cb($m)
	{
		return str_replace(";", "!SEMICOLON!", $m[1]);
	}
	
	function csv_string($s)
	{	
		$s = trim($s);
		$s = preg_replace('~(^[\'"]|[\'"]$)~', '', $s); // uvozovky
		$s = preg_replace('~\s{2,}~', ' ', $s); // více mezer nahradí jednou	
		$s = trim($s);
		if ($s == '-') $s = '';
		return $s;
	}
	
	/**
	* Z řetězce vybere číslo
	*
	* @param (string) číslo
	* @return (integer) číslo
	*/
	function format_num($s)
	{
		$s = strip_tags($s);
		$s = preg_replace('~[^0-9,-]*~', '', $s);
		$s = str_replace(',', '.', $s);
		$s = (double)$s;
		return $s;
	}
	
	function transakce_csv($ucet = null, $od = null, $do = null) {
		return $this->_transakce('CSV', $ucet, $od, $do);
	}
	function transakce_pdf($ucet = null, $od = null, $do = null) {
		return $this->_transakce('PDF', $ucet, $od, $do);
	}
	function transakce_xml($ucet = null, $od = null, $do = null) {
		return $this->_transakce('XML', $ucet, $od, $do);
	}
	function transactions_csv($ucet = null, $od = null, $do = null) {
		return $this->transakce_csv($ucet, $od, $do);
	}
	function transactions_pdf($ucet = null, $od = null, $do = null) {
		return $this->transakce_pdf($ucet, $od, $do);
	}
	function transactions_xml($ucet = null, $od = null, $do = null) {
		return $this->transactions_xml($ucet, $od, $do);
	}
	
	
	/**
	* Výpis transakcí CSV / ABO / PDF
	*/
	private function _transakce($typ, $ucet, $od, $do)
	{		
		if (isset($ucet)) {
			if (!isset($this->ucty[ $ucet ])) {
				return $this->err("Účet $ucet nenalezen.<br>Account $ucet not found.");
			}
			$cislo_uctu = $ucet;
			$id_uctu = $this->ucty[ $ucet ]['pid'];
		}
		else {
			// všechny účty
			$id_uctu = "";
			$cislo_uctu = "0";
		}
		
		//------ DATUM
		if (!isset($od) || $od < 0) {
			$days_ago = $od < 0 ? (int)$od : -31;
			$od = date("Y-m-d", strtotime(($days_ago+1)." days")); // včetně toho dnešního, proto +1
		}
		if (!isset($do) || strtotime($do) > time()) { // datum nesmí být větší než dnes!
			$do = date("Y-m-d");
		}
		$od = explode('-', $od);
		$do = explode('-', $do);
		
		
		$start_dmY = sprintf('%02d.%02d.%04d', $od[2], $od[1], $od[0]);
		$end_dmY = sprintf('%02d.%02d.%04d', $do[2], $do[1], $do[0]);
		
		// uděláme výběr
		$data = 'command=mov_filterVerify&verificationGroupName=transactionHistoryFilter&useGDMVerification=false&transactionsFilterStatus=open'
			.'&accountDetailID='.$id_uctu.'&accountNumber='.$cislo_uctu.'&custom2=&custom3=&accountID=&period=user'
			.'&startDateRef='.$start_dmY.'&endDateRef='.$end_dmY.'&fromAmount=&toAmount=&creditAccountNumberFull=%25'
			.'&creditAccountPrefix=&creditAccountNumber=&creditBankCode=&variableSymbol=&transactionType=6&kategorie=';
			
		$this->curl_redir_exec($this->base_url.'/'.$this->ucontrol, $data);
		
		$res = null;
		
		if ($typ == 'PDF') {
			$data = 'command=mov_listPrint&custom1=&custom2=&custom3=&custom4=&custom5=&operID=';
			$res = $this->curl_redir_exec($this->base_url.'/'.$this->ucontrol, $data);
		}
		elseif ($typ == 'XML') {
			$data = 'command=mov_exportXML&custom1=&custom2=&custom3=&custom4=&custom5=&operID=&filterString=&verificationGroupName=&requiredList='
				.'&removeCommand=&back_command=&back_custom1=&back_custom2=&back_custom3=&back_custom4=&back_custom5=&back_operID=&saveTemplate=&paymentTemplateName=';
			$res = $this->curl_redir_exec($this->base_url.'/'.$this->ucontrol, $data);
		}
		elseif ($typ == 'CSV') {
			$data = 'command=mov_exportCSV&custom1=&custom2=&custom3=&custom4=&custom5=&operID=&filterString=&verificationGroupName=&requiredList='
				.'&removeCommand=&back_command=&back_custom1=&back_custom2=&back_custom3=&back_custom4=&back_custom5=&back_operID=&saveTemplate=&paymentTemplateName=';
			$res = $this->curl_redir_exec($this->base_url.'/'.$this->ucontrol, $data);
		}
		
		return $res;
	}
}



/**
* Jednoduchý extraktor dat z HTML
*
* @author Martin Zvarík
*/
class DataMiner
{
	public $c;
	public $c2;
	public $c2_bound;
	
	public $cur = 0;
	public $start = 0;
	public $end;
	
	
	function __construct($content)
	{
		$content = trim($content);
		
		$this->c = $content;
		$this->c2 = strtolower($content);
	}
	
	
	function setBound($start, $end = null)
	{
		if (isset($start)) {
			$s = strtolower($start);
			$pos = stripos($this->c2, $s);
			
			if ($pos !== false) {
				$this->start = $pos;
			}
		}
		
		if (isset($end)) {
			$s = strtolower($end);
			$pos = strpos($this->c2, $s, $this->start);
			if ($pos !== false) {
				$this->end = $pos;
			}
		}
		
		if ($this->start > 0 || isset($this->end)) {
			if (!isset($this->end)) $this->end = strlen($this->c2);
			$this->c2_bound = substr($this->c2, $this->start, $this->end - $this->start);
		}
	}
	
	
	function get($tag, $format = false)
	{
		$tag = strtolower($tag);
		
		$c2 = isset($this->c2_bound) ? $this->c2_bound : $this->c2;
		
		$pos = strpos($c2, '<'.$tag, $this->cur);
		
		$this->cur = $pos + strlen($tag);
		
		if ($pos === false) return false;
		
		$end = strpos($c2, '</'.$tag.'>', $pos); // konec tagu - pro naše potřeby stačí takto
		
		
		$entry = substr($this->c, $this->start + $pos, $end - $pos + strlen($tag) + 3);
		
		if ($format == 'number')
		{
			$entry = strip_tags($entry);
			$entry = preg_replace('~[^0-9,-]*~', '', $entry);
			$entry = str_replace(',', '.', $entry);
			$entry = (double)$entry;
		}
		
		return $entry;
	}
}