/*
 *  ADP (Another Data Processor) www.adp.la
 *  Copyright (C) 2010 Katsuhisa Ohfuji <katsuhisa@ohfuji.name>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 *  MA 02110-1301, USA.
 */
#define ADP_COMMON_CPP
#include "adp.h"
#include "kz_cformat.h"
#include <boost/regex.hpp>

void print_header()
{
	if ( first_print ) {
		if ( !header_string.empty() ) {
			if ( !sessionid.empty() ) {
				// output session id 
				string cookie = "Set-Cookie: ";
				cookie += ADP_SESSION_ID_STRING;
				cookie += "=";
				cookie += sessionid;
				if ( !session_cookie_expires.empty() ) {
					cookie += ";";
					cookie += " expires=";
					cookie += session_cookie_expires;
				}
				if ( !session_cookie_domain.empty() ) {
					cookie += ";";
					cookie += " domain=";
					cookie += session_cookie_domain;
				}
				if ( !session_cookie_path.empty() ) {
					cookie += ";";
					cookie += " path=";
					cookie += session_cookie_path;
				}
				if ( !session_cookie_secure.empty() ) {
					cookie += ";";
					cookie += " ";
					cookie += session_cookie_secure;
				}				
				cookie += ";\r\n";				
				fputs( cookie.c_str(), stdout);
			}
			fputs( header_string.c_str(), stdout);
			fputs( "\r\n", stdout);
			if ( header_string[header_string.size()-1] != '\n' ) {
				fputs( "\r\n", stdout);
			}
		}
		first_print = false;
	}
}

static inline bool checkamp( int(*fnc)(int), size_t max_count, string::iterator &i, string::iterator end, string &tmp)
{
	bool escape = true;
	for (size_t cnt = 0; cnt < max_count; cnt++ ) {
		++i;
		if ( i >= end ) break;
		tmp.push_back(*i);
		if ( fnc(*i) ) {
			;
		} else if ( *i == ';' ) {
			escape = false;
			break; // escapse 処理しない
		} else {
			break; // escapse 処理する
		}
	}
	return escape;
}

static inline bool checkamp( string::iterator &i, string::iterator end, string &tmp) 
{
	// &amp;等の変換は行わない。HTML特殊文字を参照 
	// 11文字(10+;)まで先読みし判定する。 
	// また、&#99999 または &#xFFFF 形式も通す
	string::iterator back_up = i;
	const int max_count = 10;
	bool escape = true;
	++i;
	tmp.push_back(*i);
	if ( isalpha(*i) ) {
		escape = checkamp( isalnum, max_count, i, end, tmp);
	} else if ( *i == '#' ) {
		++i;
		tmp.push_back(*i);
		if ( *i == 'x'  || *i == 'X' ) { 
			escape = checkamp( isxdigit, max_count + 1, i, end, tmp);
		} else {
			escape = checkamp( isdigit, max_count, i, end, tmp);
		}
	}
	if ( escape ) {
		i = back_up;
		tmp.clear();
	}
	return escape;
}

static bool checktag(string::iterator &i, string::iterator end );

#define MAX_ALLOW_TYPE (5)
struct allowelement {
	const char	*element_name;
	int		type;	// 1: 数値のみ、2:文字と数値、3: url(http: / https:)、4:文字と数値と#と_のみ、5:なし
};

#define ALLOWELEMENTS_SIZE	(7+1)
struct allowelements {
	allowelement	elements[ALLOWELEMENTS_SIZE];
};

typedef map<string, allowelements>	allowtagmap;
typedef pair<string, allowelements>	allowtagpair;

static allowtagmap allowtags;

void init_awpescapetags(void)
{
	allowelements	end = {{0,0}};
	allowelements	classelement = { {{"class", 2}, {"id", 2}, {0,0}} };
	allowtags.insert( allowtagpair( string("table"), classelement ) );
	allowtags.insert( allowtagpair( string("/table"), end ) );
	allowtags.insert( allowtagpair( string("caption"), classelement ) );
	allowtags.insert( allowtagpair( string("/caption"), end ) );
	allowtags.insert( allowtagpair( string("tr"), classelement ) );
	allowtags.insert( allowtagpair( string("/tr"), end ) );

	allowelements	tdtag = { {{"rowspan", 1}, {"colspan", 1}, {"nowrap", 5}, {"class", 2}, {"id", 2}, {0,0}} };
	allowtags.insert( allowtagpair( string("td"), tdtag ) );
	allowtags.insert( allowtagpair( string("/td"), end ) );
	allowtags.insert( allowtagpair( string("th"), tdtag ) );
	allowtags.insert( allowtagpair( string("/th"), end ) );
	allowtags.insert( allowtagpair( string("pre"), classelement ) );
	allowtags.insert( allowtagpair( string("/pre"), end ) );
	allowtags.insert( allowtagpair( string("code"), classelement ) );
	allowtags.insert( allowtagpair( string("/code"), end ) );
	allowtags.insert( allowtagpair( string("b"), end ) );
	allowtags.insert( allowtagpair( string("/b"), end ) );
	allowtags.insert( allowtagpair( string("i"), end ) );
	allowtags.insert( allowtagpair( string("/i"), end ) );
	allowtags.insert( allowtagpair( string("em"), end ) );
	allowtags.insert( allowtagpair( string("/em"), end ) );
	allowtags.insert( allowtagpair( string("h1"), classelement ) );
	allowtags.insert( allowtagpair( string("/h1"), end ) );
	allowtags.insert( allowtagpair( string("h2"), classelement ) );
	allowtags.insert( allowtagpair( string("/h2"), end ) );
	allowtags.insert( allowtagpair( string("h3"), classelement ) );
	allowtags.insert( allowtagpair( string("/h3"), end ) );
	allowtags.insert( allowtagpair( string("h4"), classelement ) );
	allowtags.insert( allowtagpair( string("/h4"), end ) );
	allowtags.insert( allowtagpair( string("h5"), classelement ) );
	allowtags.insert( allowtagpair( string("/h5"), end ) );
	allowtags.insert( allowtagpair( string("h6"), classelement ) );
	allowtags.insert( allowtagpair( string("/h6"), end ) );
	allowtags.insert( allowtagpair( string("br"), end ) );
	allowtags.insert( allowtagpair( string("p"), classelement ) );
	allowtags.insert( allowtagpair( string("/p"), end ) );
	allowtags.insert( allowtagpair( string("blockquote"), classelement ) );
	allowtags.insert( allowtagpair( string("/blockquote"), end ) );
	allowtags.insert( allowtagpair( string("q"), classelement ) );
	allowtags.insert( allowtagpair( string("/q"), end ) );
	allowtags.insert( allowtagpair( string("ul"), classelement ) );
	allowtags.insert( allowtagpair( string("/ul"), end ) );
	allowtags.insert( allowtagpair( string("ol"), classelement ) );
	allowtags.insert( allowtagpair( string("/ol"), end ) );
	allowtags.insert( allowtagpair( string("li"), classelement ) );
	allowtags.insert( allowtagpair( string("/li"), end ) );

	allowelements	atag = { {{"href", 3}, {"name", 4}, {"class", 2}, {"id", 2}, {"target", 4}, {0,0}} };
	allowtags.insert( allowtagpair( string("a"), atag ) );
	allowtags.insert( allowtagpair( string("/a"), end ) );

	allowelements	imgtag = { {{"src", 3}, {"border", 1}, {"class", 2}, {"id", 2}, {0,0}} };
	allowtags.insert( allowtagpair( string("img"), imgtag ) );

	allowtags.insert( allowtagpair( string("div"), classelement ) );
	allowtags.insert( allowtagpair( string("/div"), end ) );
	allowtags.insert( allowtagpair( string("span"), classelement ) );
	allowtags.insert( allowtagpair( string("/span"), end ) );
}

bool add_allowtags(const string &tag, const char *el1, int tp1, const char *el2, int tp2,  const char *el3, int tp3, const char *el4, int tp4, const char *el5, int tp5, const char *el6, int tp6, const char *el7, int tp7 )
{
	if ( el1 && (tp1 < 1 || MAX_ALLOW_TYPE < tp1) ) return false;
	if ( el2 && (tp2 < 1 || MAX_ALLOW_TYPE < tp2) ) return false;
	if ( el3 && (tp3 < 1 || MAX_ALLOW_TYPE < tp3) ) return false;
	if ( el4 && (tp4 < 1 || MAX_ALLOW_TYPE < tp4) ) return false;
	if ( el5 && (tp5 < 1 || MAX_ALLOW_TYPE < tp5) ) return false;
	if ( el6 && (tp6 < 1 || MAX_ALLOW_TYPE < tp6) ) return false;
	if ( el7 && (tp7 < 1 || MAX_ALLOW_TYPE < tp7) ) return false;

	remove_allowtags(tag);
	allowelements	classelement = { {{el1, tp1}, {el2, tp2}, {el3, tp3}, {el4, tp4}, {el5, tp5}, {el6, tp6}, {el7, tp7}, {0,0}} };
	allowtags.insert(allowtagpair( tag, classelement ));
	return true;
}

void remove_allowtags(const string &tag)
{
	allowtags.erase(tag);
}

static inline bool checklt( bool awpescape, string::iterator &i, string::iterator end, string &tmp) 
{
	bool escape = true;
	allowtagmap::iterator f;
	string name;
	name.reserve(20);
	string element;
	element.reserve(64);

	string::iterator back_up = i;
	i++;	// <の読み飛ばし 
	if ( i >= end ) goto return_out; // 文字列が終了したのでエスケープ終了 
	if ( !awpescape ) goto return_out; // AWP出力モードでない  
	tmp.push_back(*i);

	// 名前の読み込み 
	while ( *i != ' ' && *i != '>' ) {
		char c = *i++;
		if ( i >= end ) goto return_out; // 文字列が終了したのでエスケープ終了 
		tmp.push_back(*i);
		if ( c != '/' || *i != '>' ) name.push_back(c); // />で終わる場合は/はタグ名に含めない 
	}

	// 許可テーブルの検索 
	f = allowtags.find(name);
	if ( f == allowtags.end() ) goto return_out; // なかったのでエスケープ終了 
	
	// elementのチェック 
	while ( *i != '>' ) {
		// 必ずスペースで区切られる 
		if ( *i != ' ' ) goto return_out; // エスケープ終了 
		i++;
		if ( i >= end ) goto return_out; // 文字列が終了したのでエスケープ終了 
		tmp.push_back(*i);
		element.clear();
		while ( *i != ' ' && *i != '>' && *i != '='  ) {
			char c = *i++;
			if ( i >= end ) goto return_out; // 文字列が終了したのでエスケープ終了 
			tmp.push_back(*i);
			if ( c != '/' || *i != '>' ) element.push_back(c); // />で終わる場合は/は名に含めない  
		}
		if ( i >= end ) return true; // 途中で終了している 
		
		int idx = 0;
		while ( idx < ALLOWELEMENTS_SIZE ) {
			if ( f->second.elements[idx].element_name == 0 ) goto return_out; // エスケープ終了 
			if ( strcmp( f->second.elements[idx].element_name, element.c_str()) == 0 ) break;
			idx++;
		}
		if ( idx >= ALLOWELEMENTS_SIZE ) goto return_out; // エスケープ終了 
		
		if ( f->second.elements[idx].type == 5 ) { // 5: 引数がないもののチェック 
			if ( *i != ' ' && *i != '>' ) goto return_out; // エスケープ終了 
		} else {
			// 引数があるもののチェック 厳密に ="...." この形式でなければならない 
			if ( *i != '=' ) goto return_out; // エスケープ終了 
			i++;
			if ( i >= end ) goto return_out; // 文字列が終了したのでエスケープ終了 
			tmp.push_back(*i);
			if ( *i != '"' ) goto return_out; // エスケープ終了 
			i++;
			if ( i >= end ) goto return_out; // 文字列が終了したのでエスケープ終了 
			tmp.push_back(*i);
			string url;
			while ( *i != '"' ) {
				char c = *i++;
				if ( i >= end ) goto return_out; // 文字列が終了したのでエスケープ終了 
				tmp.push_back(*i);
				bool flg = false;
				switch ( f->second.elements[idx].type ) {
					case 1 : // 1: 数値のみ 
						flg =  isdigit(c) != 0;
						break;
					case 2 : // 2:文字と数値 
						flg =  isalnum(c) != 0;
						break;
					case 3 : // 3: url(http: / https:)
						url.push_back(c);
						flg = true;	// ここのチェックはとりあえずスルー ↓で確認処理を行う 
						break;
					case 4 : // 、4:文字と数値と#のみ 　
						flg =  isalnum(c)  != 0 || c == '#' || c == '_';
						break;
					case 5 : // 5: ↑21行付近で処理をしている 
					default :
						if ( i >= end ) goto return_out; // ありえないので終了 
				}
				if ( !flg ) goto return_out; // エスケープ終了 
			}
			if ( f->second.elements[idx].type == 3 ) { // 3: url(http: / https:)
				if ( url.size() < 6 ) goto return_out; // エスケープ終了 
				if ( memcmp( url.c_str(), "http:", 5) != 0 && memcmp( url.c_str(), "https:", 6) != 0 ) goto return_out; // エスケープ終了 
			}
			i++;
			if ( i >= end ) goto return_out; // 文字列が終了したのでエスケープ終了 
			tmp.push_back(*i);
		}
	}

	escape = false; // 全てのチェックを通ったので許可タグとする（エスケープしない） 

return_out:
	if ( escape ) {
		// <をエスケープする場合は再度文字列をパースしなおす 
		i = back_up;
		tmp.clear();
	}
	return escape;
}


void htmlescape( string &txt, bool awpescape)
{
	string out;
	string tmp;
	tmp.reserve(16);
	bool escape = true;
	for ( string::iterator i = txt.begin(); i < txt.end(); i++ ) {
		switch ( *i ) {
		case '&' :
			tmp.clear();
			out += checkamp( i, txt.end(), tmp ) ? "&amp;" : "&";
			out += tmp;
			break;
		case '<' :
			tmp.clear();
			out += checklt(awpescape, i, txt.end(), tmp) ? "&lt;" : "<";
			out += tmp;
			break;
		case '>' :
			out += "&gt;";
			break;
		case '"' :
			out += "&quot;";
			break;
		case '\'' :
			out += "&#39;";
			break;
		default :
			out += *i;
			break;
		}
	}

	if ( awpescape ) {
		txt.clear();
		char prevchr = 0;
		bool rbrace = false;	// false:タグ内 
		int precnt = 0;
		int tablecnt = 0;
		int thtdcnt = 0;
		int ulolcnt = 0;
		int licnt = 0;
		for ( string::iterator i = out.begin(); i < out.end(); i++ ) {
			if ( revcmp3( txt, "<pre>" , "<Pre>" , "<PRE>" , 5) ) { precnt++; }
			if ( revcmp3( txt, "<pre " , "<Pre " , "<PRE " , 5) ) { precnt++; rbrace = true; }
			if ( revcmp3( txt, "</pre>", "</Pre>", "</PRE>", 6) ) { precnt--; }

			/* table タグ関係 */
			if ( revcmp3( txt, "<table>" , "<Table>" , "<TABLE>" , 7) ) { tablecnt++; }
			if ( revcmp3( txt, "<table " , "<Table " , "<TABLE " , 7) ) { tablecnt++; rbrace = true; }
			if ( revcmp3( txt, "</table>", "</Table>", "</TABLE>", 8) ) { tablecnt--; }
			if ( revcmp3( txt, "<th>" , "<Th>" , "<TH>" , 4) ) { thtdcnt++; }
			if ( revcmp3( txt, "<th " , "<Th " , "<TH " , 4) ) { thtdcnt++; rbrace = true; }
			if ( revcmp3( txt, "</th>", "</Th>", "</TH>", 5) ) { thtdcnt--; }
			if ( revcmp3( txt, "<td>" , "<Td>" , "<TD>" , 4) ) { thtdcnt++; }
			if ( revcmp3( txt, "<td " , "<Td " , "<TD " , 4) ) { thtdcnt++; rbrace = true;  }
			if ( revcmp3( txt, "</td>", "</Td>", "</TD>", 5) ) { thtdcnt--; }
			if ( revcmp3( txt, "<caption>" , "<Caption>" , "<CAPTION>" , 9) ) { thtdcnt++; }
			if ( revcmp3( txt, "<caption " , "<Caption " , "<CAPTION " , 9) ) { thtdcnt++; rbrace = true; }
			if ( revcmp3( txt, "</caption>", "</Caption>", "</CAPTION>", 10) ) { thtdcnt--; }

			/* ul ol タグ関係 */
			if ( revcmp3( txt, "<ul>" , "<Ul>" , "<UL>" , 4) ) { ulolcnt++; }
			if ( revcmp3( txt, "<ul " , "<Ul " , "<UL " , 4) ) { ulolcnt++; rbrace = true; }
			if ( revcmp3( txt, "</ul>", "</Ul>", "</UL>", 5) ) { ulolcnt--; }
			if ( revcmp3( txt, "<ol>" , "<Ol>" , "<OL>" , 4) ) { ulolcnt++; }
			if ( revcmp3( txt, "<ol " , "<Ol " , "<OL " , 4) ) { ulolcnt++; rbrace = true; }
			if ( revcmp3( txt, "</ol>", "</Ol>", "</OL>", 5) ) { ulolcnt--; }
			if ( revcmp3( txt, "<li>" , "<Li>" , "<LI>" , 4) ) { licnt++; }
			if ( revcmp3( txt, "<li " , "<Li " , "<LI " , 4) ) { licnt++; rbrace = true; }
			if ( revcmp3( txt, "</li>", "</Li>", "</LI>", 5) ) { licnt--; }

			if ( *i == '>' ) { rbrace = false; }	// タグの閉じ括弧 

			if ( precnt == 0 && tablecnt <= thtdcnt && ulolcnt <= licnt && rbrace == false && prevchr != '>' ) {
				if ( (*i == '\r') ||
					 (*i == '\n' && prevchr != '\r') ) {
					txt += "<br>";
				}
			}
			prevchr = *i;
			if ( *i ) txt += *i;
		}
#if 0
		// 改行の処理（改行は<br>に置換）
		replace_regex_all( txt, "([^>])\r\n", "$1<br>", txt);	// タグの直ぐ後の\r\nは<br>に置換しない
		replace_regex_all( txt, "([^>])\r", "$1<br>", txt);
		replace_regex_all( txt, "([^>\r])\n", "$1<br>", txt);
		replace_regex_all( txt, "<br>\r\n", "<br><br>", txt);	// 改行が続く場合、<br>タグの後の改行は置換する。
		replace_regex_all( txt, "<br>\r", "<br><br>", txt);
		replace_regex_all( txt, "<br>\n", "<br><br>", txt);
#endif
	} else {
		txt = out;
	}
}


void formatNumber(PINTEGER num, string &fmtnum)
{
    vector<int>    sepnum;
    PINTEGER       number = num >= 0 ? num : -num;
    int            sgn = num >= 0 ? 1 : -1;

    while ( number / 1000 ) {
        sepnum.push_back(static_cast<int>(number % 1000));
        number /= 1000;
    }

    stringstream  ss;
    ss << number * sgn;
    for ( vector<int>::reverse_iterator i = sepnum.rbegin(); i < sepnum.rend(); i++ ) {
		ss << "," << std::setfill('0') << std::setw(3) << *i;
    }
    fmtnum = ss.str();
}

void ej2sj( const unsigned char ej[2], unsigned char sj[3])
{
	//// 半角カナ
	//if ( ej[0] == 0x8e ) {
	//	sj[0] = ej[1];
	//	sj[1] = 0;
	//	return;
	//}

	// JIS漢字
	sj[0] = (ej[0] & 0x7f) - 0x21;
	sj[1] = (ej[1] & 0x7f);
	if ( sj[0] % 2 ) {
		sj[1] += 0x7e;
	} else {
		sj[1] += 0x1f;
		if ( sj[1] >= 0x7f )  sj[1]++;
	}
	sj[0] >>= 1;
	if ( sj[0] >= 0x1f ) {
		sj[0] += 0xc1;
	} else {
		sj[0] += 0x81;
	}
	sj[2] = 0;
	return;
}


void unicode_to_utf8(int unicode, string &dst)
{
	// 現在のコードはunicode は１６ビットのみ対応
	if ( unicode < 0x80 ) {
		dst += (unsigned char)unicode;
	} else if ( unicode < 0x800 ) {
		dst += 0xc0 | ((unicode >> 6) & 0x3f);
		dst += 0x80 | (unicode & 0x3f);
	} else {
		dst += 0xe0 | ((unicode >> 12) & 0x3f);
		dst += 0x80 | ((unicode >> 6) & 0x3f);
		dst += 0x80 | (unicode & 0x3f);
	}
}

void utf8to(const string &src, string &dst)
{
	// ロケールの保存と設定
	char *lp = setlocale(LC_CTYPE,NULL);
	char *save = new char[strlen(lp) + 1];
	strcpy( save, lp);
	setlocale(LC_CTYPE,"");
	
	dst.clear();
	dst.reserve(src.length());
	char	*mc = new char[MB_CUR_MAX+1];
	mbstate_t ps;
	mbsinit(&ps);
	for ( string::const_iterator i = src.begin(); i < src.end(); i++ ) {
		wchar_t	wc = 0;
		if ( (0x80 & *i) == 0 ) {
			wc = *i;
		} else if ( (0xe0 & *i) == 0xc0 ) {
			if ( i + 1 < src.end() && (0xc0 & i[1]) == 0x80 ) {
				wc = ((i[0] & 0x1f) << 6) | (i[1] & 0x3f);
				i++;
			}
		} else if ( (0xf0 & *i) == 0xe0 ) {
			if ( i + 2 < src.end() && (0xc0 & i[1]) == 0x80 && (0xc0 & i[2]) == 0x80 ) {
				wc = ((i[0] & 0x0f) << 12) | ((i[1] & 0x3f) << 6) | (i[2] & 0x3f);
				i += 2;
			}	
		}
		size_t	len = wcrtomb( mc, wc, &ps);
		if ( len != (size_t)(-1) ) {
			mc[len] = '\0';
			dst.insert( dst.end(), mc, mc + len);
		}
	}
	
	// ロケールを戻す
	setlocale(LC_CTYPE,save);
	delete mc;
	delete save;
}

void list2ssmap( const PList *ovalues, ssmap &values)
{
	const PList *fvalue;
	while ( ovalues && (fvalue = dynamic_cast<const PList*>(ovalues->lval())) != 0 ) {
		string	name;
		string  value;
		name = fvalue->lval()->c_str();
		if ( fvalue->rval()->c_str() ) {
			value = fvalue->rval()->c_str();
		} else {
			const PList *fvvalue = dynamic_cast<const PList*>(fvalue->rval());
			if ( fvvalue ) {
				value = fvvalue->lval()->c_str();
			}
		}
		values.insert( sspair(name, value) );
		ovalues = dynamic_cast<const PList*>(ovalues->rval());
	}
}

PList *ssmap2list( ssmap &values, ExecContextRoot *ec)
{
	PObject *list = &pnil;
	for ( ssmap::reverse_iterator ri = values.rbegin(); ri != values.rend(); ri++ ) {
		PString *pn = pmm.newPString(ec->pobjs, ri->first.c_str());
		PString *pv = pmm.newPString(ec->pobjs, ri->second.c_str());
		PList *vlist = pmm.newPList(ec->pobjs, pn, pv);
		PList *nlist = pmm.newPList(ec->pobjs, vlist, list);
		list = nlist;
	}
	return dynamic_cast<PList*>(list);
}

PList *stringstring2list( string_array &keys, string_array &values, ExecContextRoot *ec)
{
	PObject *list = &pnil;
	if ( keys.size() != values.size() ) return 0;
	for (size_t i = keys.size() - 1; i < keys.size(); i-- ) {
		PString *pn = pmm.newPString(ec->pobjs, keys[i]);
		PString *pv = pmm.newPString(ec->pobjs, values[i]);
		PList *vlist = pmm.newPList(ec->pobjs,  pn, pv);
		PList *nlist = pmm.newPList(ec->pobjs,  vlist, list);
		list = nlist;
	}
	return dynamic_cast<PList*>(list);
}


void convertstringvector( char **src, vector<string>&dest)
{
	while ( *src ) {
		dest.push_back( string(*src++) );
	}
}



kz_odbc	*db_connect(const char *constr)
{
	kz_odbc_map::iterator db;
	if ( ( db = dbmap.find(constr)) == dbmap.end() ) {
		kz_odbc	*dbcon	= new kz_odbc(constr, true);
		dbvector.push_back(dbcon);
		dbmap.insert( kz_odbc_pair( constr, dbcon ) );
		db = dbmap.find(constr);
	}
	return db->second;
}

void db_disconnect()
{
	for_each( dbvector.rbegin(), dbvector.rend(), MyDeleteObject());
}

static bool make_tempdir(string &tempdir)
{
#ifdef _WIN32
	char	tempdir_[MAX_PATH +1];
	size_t	len = GetTempPath( MAX_PATH , tempdir_);
	if ( len == 0 || len >= MAX_PATH  ) return false;
	tempdir = tempdir_;
#else
	tempdir.clear();
	char	path[FILENAME_MAX+1];
	
	if ( getenv("TMPDIR") ) {
		tempdir = getenv("TMPDIR");
		if ( tempdir[tempdir.size()-1] != '/' ) {
			tempdir += '/';
		}
	} else {
		tempdir = "/tmp/";
	}
#endif
	return true;
}

bool make_temppath(const string &head, string &temppath)
{
#ifdef _WIN32
	char	tempdir[MAX_PATH +1];
	char	tempfilename[MAX_PATH +1]; // GetTempFileNameがMAX_PATH以上を要求
	size_t	len = GetTempPath( MAX_PATH , tempdir);
	if ( len == 0 || len >= MAX_PATH  ) return false;
	if ( GetTempFileName( tempdir, head.c_str(), 0, tempfilename) == 0 ) return false;
	temppath = tempfilename;
#else
	string	work;
	char	path[FILENAME_MAX+1];
	
	if ( getenv("TMPDIR") ) {
		work = getenv("TMPDIR");
		if ( work[work.size()-1] != '/' ) {
			work += '/';
		}
	} else {
		work = "/tmp/";
	}
	work += head;
	work += "XXXXXX";
	if ( work.size() >= FILENAME_MAX ) return false;
	strcpy( path, work.c_str());	
	int d = mkstemp(path);
	if ( d == -1 ) return false;
	close(d);
	temppath = path;
#endif
	return true;
}

static  const char *hex_table = "0123456789ABCDEF";

bool createHash( const string &str, string &hash)
{
#ifdef _WIN32
    HCRYPTPROV hCryptProv = 0;
    HCRYPTHASH hHash = 0;
    HCRYPTKEY hKey = 0;
	char hash_value[20] = { 0 };
	unsigned long ds = 20;
	bool ret = false;

    if ( !CryptAcquireContext( &hCryptProv, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT) ) goto error_out;
	if ( !CryptCreateHash( hCryptProv, CALG_SHA1, 0, 0, &hHash) ) goto error_out;
	if ( !CryptHashData( hHash, (BYTE*)str.c_str(), (DWORD)str.size(), CRYPT_USERDATA) ) goto error_out;
	if( !CryptGetHashParam( hHash, HP_HASHVAL, (BYTE*)hash_value, &ds, 0 ) ) goto error_out;
    for ( unsigned long i = 0 ; i < ds ; ++i ) {
		hash.push_back( hex_table[hash_value[i] >> 4] );
		hash.push_back( hex_table[hash_value[i] & 0xf] );
    }
	
	ret = true;
error_out:
    if(hHash) CryptDestroyHash(hHash);
    if(hCryptProv) CryptReleaseContext(hCryptProv,0);

	return ret;
#else
	unsigned char	hash_value[SHA_DIGEST_LENGTH];	
	SHA1((const unsigned char *)str.c_str(), str.size(), hash_value);
	for ( int i = 0; i < SHA_DIGEST_LENGTH; i++ ) {
		hash.push_back( hex_table[hash_value[i] >> 4] );
		hash.push_back( hex_table[hash_value[i] & 0xf] );
	}	
	return true;
#endif
}

extern void init_by_array(unsigned long init_key[], int key_length);
extern unsigned long genrand_int32(void);
void mysrand()
{
#if _WIN32
	unsigned long init[]={time(0) >> 32, time(0) & 0xffffffff, clock()};
#else
	unsigned long init[]={ time(0) & 0xffffffff, clock()};
#endif
	init_by_array( init, sizeof(init));
}

extern void init_genrand(unsigned long s);
void mysrand(int seed)
{
	init_genrand((unsigned long)seed);
}

int myrand()
{
	return (int)genrand_int32();
}

bool make_rkey(string &base, string &rkey)
{
	string sessionwork = base;
	for ( int i = 0; i < 160; i++ ) {
		int value = myrand();
		sessionwork += hex_table[(value >> 28) & 0xf];
		sessionwork += hex_table[(value >> 24) & 0xf];
		sessionwork += hex_table[(value >> 20) & 0xf];
		sessionwork += hex_table[(value >> 16) & 0xf];
		sessionwork += hex_table[(value >> 12) & 0xf];
		sessionwork += hex_table[(value >>  8) & 0xf];
		sessionwork += hex_table[(value >>  4) & 0xf];
		sessionwork += hex_table[value & 0xf];
	}
	rkey.clear();		
	return createHash( sessionwork, rkey);
}

bool createSessionID( string &sessionid, string &sessionpath, ssmap &env)
{
	string sessionid_base = cformat("%s%d%d", env["REMOTE_ADDR"].c_str(), time(0), clock());
	string	tempdir;
	if ( !make_tempdir(tempdir) ) return false;

	mysrand();
	for ( int i = 0; i < 50; i++ ) { // リトライ50回

		if ( !make_rkey( sessionid_base, sessionid) ) continue;
	
		sessionpath = tempdir + sessionid + ".awpsf";

#ifdef _WIN32
		// 以下、vine linuxだと動作しなかった・・・
		fstream	sessionf(sessionpath.c_str(), ios_base::out);
		if ( sessionf.is_open() ) return true;
#else
		// のでこっちで実装
		int fh = open( sessionpath.c_str(), O_CREAT | O_EXCL, S_IREAD | S_IWRITE );
		if ( fh != -1 ) {
			close(fh);
			return true;
		}
#endif

		mySleep(10*1000*1000);		// 10ms待つ

	}
	
	return false;
}

PArray *getSession(const string &sessionid, string &sessionpath)
{
	if ( sessionid.empty() ) return 0;
	string	tempdir;
	if ( !make_tempdir(tempdir) ) return 0;
	sessionpath = tempdir + sessionid + ".awpsf";

	basic_ifstream<char> inputFile( sessionpath.c_str(), ios_base::in | ios::binary);
	istreambuf_iterator<char> sin(inputFile);
	PObject *po = fromBinary< istreambuf_iterator<char> >( sin, istreambuf_iterator< char >(), poFactory.pobjs());
	return dynamic_cast<PArray*>(po);
}


bool setSessionItem(const PObject *item, const string &key, ssmap &env, PArray* &session, string &sessionid, string &sessionpath)
{
	PObject *po = item->clone( poFactory.pobjs());

	if ( session == 0 ) {
		if ( sessionid.empty() ) {
			if ( !createSessionID( sessionid, sessionpath, env) ) return false;
		}
		session = poFactory.createObject<PArray>();
		session->push_back( po, key);
	} else {
		int idx = -1;
		if ( (idx = session->find_key(key)) != -1 ) {
			poFactory.DeleteObject( const_cast<PObject*>((*session)[idx]) );
			session->set( idx, po, &key);
		} else {
			session->push_back( po, key);
		}
	}
	return true;
}

bool setGlobalItem(const PObject *item, const string &key, PArray* &global)
{
	PObject *po = item->clone( poFactory.pobjs());

	if ( global == 0 ) {
		global = poFactory.createObject<PArray>();
		global->push_back( po, key);
	} else {
		int idx = -1;
		if ( (idx = global->find_key(key)) != -1 ) {
			poFactory.DeleteObject( const_cast<PObject*>((*global)[idx]) );
			global->set( idx, po, &key);
		} else {
			global->push_back( po, key);
		}
	}
	return true;
}



bool toBinary( const PObject *obj, string &cvobj, string &header)
{
	if ( !obj->Bwrite(cvobj) ) {
		return false;
	}
	header = cformat( BINARY_OBJECT_HEADER_1"%-10d%10.10s", cvobj.size(), "" );
	return true;
}

void mySleep(long long nanosec)
{
#ifdef _WIN32
		 
		Sleep((int)(nanosec/1000/1000));
#else
		timespec	req;
		timespec	rem;
		req.tv_sec = nanosec / (1000 * 1000 * 1000);
		req.tv_nsec = nanosec % (1000 * 1000 * 1000);
		nanosleep( &req, &rem);
#endif
}

