Artifact Content

Not logged in

Artifact e18cd844860064552a8fbae9d03e328a12aa4bdb



#include "stdafx.h"
#include "resource.h"



//----------------------------------------------------------------------------
// ユーティリティー
//----------------------------------------------------------------------------

#ifndef ListView_SetCheckState
#define ListView_SetCheckState(_hwndLV, _i, _fCheck) \
	ListView_SetItemState(_hwndLV, _i, \
	INDEXTOSTATEIMAGEMASK((_fCheck)+1), LVIS_STATEIMAGEMASK)
#endif


// 挿入caldixF

// なんか長いし分り難いんで(w
#define IsDblChar(x) IsDBCSLeadByte((BYTE)(x))

// 一番前=TRUE(後ろ=FALSE)の何か=cのインデックスを探して返す
int GetcX(char *buf,char c,BOOL fl)
{
	int rt;
	int i,len;

	len = lstrlen(buf);
	rt = 0;
	for (i = 0;i < len;i++){
		// 2バイト文字なら飛ばす
		if (IsDblChar(buf[i])){
			i++;
			continue;
		}
		else{
			if (buf[i] == c){
				rt = i;
				// 最初の奴を探すのなら
				if (fl)
					return(rt);
			}
		}
	}
	// 最後まで無かった
	if (!rt && (i == len))
		rt = len;

	return(rt);
}

// ファイル名取得
char *GetFileName(char *buf)
{
	int ly;
	
	// 最後の¥
	ly = GetcX(buf,'\\',FALSE);

	// そのまま返す
	if (ly == lstrlen(buf))
		return(buf);
	
	// \の次のポインタを返す
	return(&buf[ly+1]);
}
// 挿入ここまでcaldixF

static void move_later( const char* from, const char* to )
{
	// 移動先と同じディレクトリへ動かしておく
	kiPath tmp( to );
	::CopyFile( from, tmp+=".tmp", FALSE );
	::DeleteFile( from );

	// NT系ならMoveFileExが成功するはず
	if( ::MoveFileEx( tmp, to, MOVEFILE_REPLACE_EXISTING|MOVEFILE_DELAY_UNTIL_REBOOT ) )
		return;

	// 9x系ならWININIT.INIをいじる
	char inifile[MAX_PATH];
	::GetWindowsDirectory( inifile, sizeof(inifile) );
	::lstrcat( inifile, "\\WININIT.INI" );

	// ショート名前に変換
	char sfrom[MAX_PATH], sto[MAX_PATH];
	::GetShortPathName(  tmp, sfrom, MAX_PATH );
	::GetShortPathName(   to,   sto, MAX_PATH );

	// Renameセクションに追加
	static char buf[30000];
	::GetPrivateProfileSection( "Rename", buf, 30000, inifile ); 
	char* p = buf; 
	while(*p)while(*p++);
	::lstrcpy( p, "NUL=" );
	::lstrcat( p, sto );
	::lstrcat( p, "\r\n" );
	::lstrcat( p, sto );
	::lstrcat( p, "=" );
	::lstrcat( p, sfrom );
	while(*p++);
	*p='\0'; 

	// 2発打って確実に書き込む
	::WritePrivateProfileSection( "Rename", buf, inifile );
	::WritePrivateProfileString( NULL, NULL, NULL, inifile );
}

static void rebootWindows()
{
	if( app()->osver().dwPlatformId == VER_PLATFORM_WIN32_NT )
	{
		// NT系では特権を取得しなくてはいけない
		HANDLE hToken;
		TOKEN_PRIVILEGES tkp;
		::OpenProcessToken( ::GetCurrentProcess(),
			TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &hToken );
		::LookupPrivilegeValue(
			NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid );
		tkp.PrivilegeCount = 1;
		tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
		::AdjustTokenPrivileges( hToken, FALSE, &tkp, 0, NULL, 0 );
	}
	::ExitWindowsEx( EWX_REBOOT, 0 );
}

static bool cancelShiteIidesuka()
{
	return IDYES == app()->msgBox( kiStr().loadRsrc( IDS_CANCELOK ),
		"caldix", MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON2 );
}

template <class T> static inline bool show( const T& dlg )
{
	(const_cast<T&>(dlg)).doModal();
	return IDOK == (const_cast<T&>(dlg)).getEndCode();
}



//----------------------------------------------------------------------------
// 解凍関係
//----------------------------------------------------------------------------

#include "kiutil.h"
#include "LzhTool.h"
#include "CabTool.h"
#include "ZipTool.h"
#define is_lzh(xx) (xx[2]=='-' && xx[3]=='l' && xx[4]=='h' && xx[6]=='-' && '0'<=xx[5] && xx[5]<='9')
#define is_zip(xx) (xx[2]=='P' && xx[3]=='K' && xx[4]==0x3 && xx[6]==0x4)
#define is_cab(xx) (xx[0]=='M' && xx[1]=='S' && xx[2]=='C' && xx[3]=='F')

enum arctype{ e_lzh, e_zip, e_cab, unknown };

static arctype get_archive_type( const char* arcname )
{
	// 手抜き判別
	arctype ans = unknown;
	kiFile fp;
	if( fp.open(arcname) )
	{
		unsigned long asize = 128 << 10;
		unsigned char *buf = new unsigned char[asize];
		unsigned char *p=buf, *end=buf+fp.read( buf, asize-6 );

		while( p!=end )
		{
				 if( is_lzh(p) ) { ans=e_lzh; break; }
			else if( is_zip(p) ) { ans=e_zip; break; }
			else if( is_cab(p) ) { ans=e_cab; break; }
			p++;
		}

		delete [] buf;
	}
	return ans;
}

static void melt_it( const char* arcname, const char* dllname, kiPath& dll_rel_path )
{
	const char* ext = kiPath::ext( arcname );
	arctype type = unknown;

	// 判別
		 if( 0==stricmp( ext, "lzh" ) ) type = e_lzh;
	else if( 0==stricmp( ext, "zip" ) ) type = e_zip;
	else if( 0==stricmp( ext, "cab" ) ) type = e_cab;
	else type = get_archive_type(arcname);

	// 展開
	switch( type )
	{
	case e_lzh:{ CLzhTool().Extract( arcname, dllname, dll_rel_path ); }break;
	case e_zip:{ CZipTool().Extract( arcname, dllname, dll_rel_path ); }break;
	case e_cab:{ CCabTool().Extract( arcname, dllname, dll_rel_path ); }break;
	}
}



//----------------------------------------------------------------------------
// DLL情報管理:Meta-WWWC-Contentの解析など
//----------------------------------------------------------------------------

class DLLInfo
{
public:

	DLLInfo( const char* _name, const char* _html, int _wwwc )
		: name( _name ), html( _html ), wwwc( _wwwc ), size( 0 ) {}

	bool  ParseHTML( char* str );
	const kiStr& htmlName() const { return html; }
	bool  needToUpdate( kiPath& dlldir );
	const kiStr& getName() const { return name; }
	const kiStr& getState() const { return stat; }
	const int getSize() const { return size; }
	const kiStr& getArchiveName() const { return arch; }

private:

	char* Get_WWWC_Content( char* str );
	bool  ParseWWWC( char* str );
	bool  CheckDateTime( const char* localDLL );
	bool  VersionCheckLogic(
		const kiStr& dllName, const kiStr& htmlVer, int Ver, int subVer );

private:

	// 固定情報
	const kiStr name; // DLLの名前
	const kiStr html; // チェック先HTMLの名前
	const int   wwwc; // 何番目のWWWCタグを利用するか

private:

	// HTMLから取得する情報
	kiStr       arch; // 書庫名
	kiStr       vers; // バージョン
	kiStr       dttm; // 日付&時刻
	int         size; // サイズ

	// バージョン照合の結果
	kiStr       stat; // 更新状況
};

char* DLLInfo::Get_WWWC_Content( char* x )
{
	// もーれつに雑。
	while( *x==' ' || *x=='\t' || *x=='\r' || *x=='\n' ) x++;
	if( (x[0]=='m' || x[0]=='M')
	 && (x[1]=='e' || x[1]=='E')
	 && (x[2]=='t' || x[2]=='T')
	 && (x[3]=='a' || x[3]=='A') )
	{
		x+=4;
		while( *x==' ' || *x=='\t' || *x=='\r' || *x=='\n' ) x++;
		if( (x[0]=='n' || x[0]=='N')
		 && (x[1]=='a' || x[1]=='A')
		 && (x[2]=='m' || x[2]=='M')
		 && (x[3]=='e' || x[3]=='E')
		 &&       (x[4]=='=')
		 &&       (x[5]=='"')
		 && (x[6]=='w' || x[6]=='W')
		 && (x[7]=='w' || x[7]=='W')
		 && (x[8]=='w' || x[8]=='W')
		 && (x[9]=='c' || x[9]=='C')
		 &&       (x[10]=='"') )
		{
			x+=11;
			while( *x==' ' || *x=='\t' || *x=='\r' || *x=='\n' ) x++;
			if( (x[0]=='c' || x[0]=='C')
			 && (x[1]=='o' || x[1]=='O')
			 && (x[2]=='n' || x[2]=='N')
			 && (x[3]=='t' || x[3]=='T')
			 && (x[4]=='e' || x[4]=='E')
			 && (x[5]=='n' || x[5]=='N')
			 && (x[6]=='t' || x[6]=='T')
			 &&       (x[7]=='=')
			 &&       (x[8]=='"') )
			{
				x+=9;
				char* p=x;
				while( *p!='\0' && *p!='"' ) p++;
				*p='\0';
				return x;
			}
		}
	}
	return NULL;
}

bool DLLInfo::ParseHTML( char* str )
{
	int ct = wwwc;
	for( char* p=str; *p; ++p )
		if( *p == '<' )
		{
			for( char* x=p+1; *x; ++x )
				if( *x == '>' )
					break;
			*x = '\0';

			char* w = Get_WWWC_Content( p+1 );
			if( w && (--ct)==0 )
				return ParseWWWC( w );

			p = x;
		}
	return false;
}

bool DLLInfo::ParseWWWC( char* str )
{
	char *x,*k,*t,*p=str;

	// 2個目のスペースまでが更新日時
	for( int spc=2; *p; ++p )
		if( *p==' ' )
			if( (--spc)==0 )
				break;
	if( *p=='\0' )
		return false;
	*(p++) = '\0';
	dttm = str;

	// [xxx: ---] 部分
	while( true )
	{
		while( *p!='\0' && *p!='[' ) ++p;
		if( *p!='\0' ) ++p;
		if( *p=='\0' )break;

		t=x=k=p;
		while( *x!='\0' && *x!=']' ) x++;
		while( *k!='\0' && *k!=':' ) k++;
		if( *x=='\0' || *k=='\0' || k>=x )break;
		*k=*x='\0';
		p=k+1;

		if( 0==lstrcmpi( t, "File" ) )
			arch = p;
		else if( 0==lstrcmpi( t, "Ver" ) )
		{
			while( *p && (*p<'0' || '9'<*p) )
				++p;
			vers = p;
		}
		else if( 0==lstrcmpi( t, "Size" ) )
		{
			// atoi
			for( int n=0; *p; p++ )
				if( '0'<=*p && *p<='9' )
					n = (10*n) + (*p - '0');
			size = n;
		}
		p=x+1;
	}

	return ( size!=0 && arch.len()!=0 );
}

//----------------------------------------------------------------------------
// caldixの設定項目管理
//----------------------------------------------------------------------------

enum {
	UNLHA, UNZIP, ZIP, CAB, TAR, UNRAR, UNGCA,
	UNARJ, YZ1, BGA, JACK, AISH, ISH, UNBEL, SvZIP,
	UNIMP, BH, YZ2,
DLLID_NUM };

struct CldxConfig
{
	kiStr   UA;            // ユーザーエージェント名

	bool    SuperAutoMode; // (利用しない)
	bool    AutoMode;      // 全自動ならtrue, カスタムならfalse

	kiPath  InstallTo;     // インストール先
	int     CheckServer;   // 更新チェックに使うサーバ 窓辺=0, CSD=1, 惑星=2

	int     ProxyPort;     // プロキシのポート番号
	kiStr   ProxyServer;   // プロキシサーバ名
	kiStr   ProxyUser;     // プロキシユーザ名(利用しない)
	kiStr   ProxyPwd;      // プロキシパスワード(利用しない)

	bool    George;        // 常時接続モードならtrue, 汎用モードならfalse
	bool    UseCustomDir;  // 全自動でも、カスタムと同じディレクトリを用いる
	bool    ShowReadMe;    // 最後にcldxフォルダを開くか否か

// 挿入caldixF
	kiStr   Filer;         // フォルダを開くファイラー
	kiStr   F_Prefix;      // ファイラに渡すオプション前
	kiStr   F_Suffix;      // ファイラに渡すオプション後ろ
	
// 挿入ここまでcaldixF

	bool    DoCheck[DLLID_NUM]; // 各DLLをチェック対象にするかどうか
	int     InstMode;           // ダウンロードしたDLLの扱い
	int     Expire;             // 日時チェックで更新対象とする範囲(単位:Day)

	kiArray<DLLInfo*> List;     // チェック対象DLLのリスト

	CldxConfig()
		: UA( "caldix/1.21" )
		, SuperAutoMode( false )
		, AutoMode( true )
		, InstallTo( kiPath::Sys )
	{
		kiIniFile ini;
		ini.setFileName( "caldix.ini" );
		ini.setSection( "conf" );

		InstallTo    = ini.getStr ( "dll",       InstallTo );
		CheckServer  = ini.getInt ( "checkserv", 0 ) % 3;
		ProxyServer  = ini.getStr ( "proxy",     "" );
		ProxyPort    = ini.getInt ( "proxyport", -80 );
		George       =!ini.getBool( "askhang",   false ); // [1.21] デフォルトを false に変更。流石に今時…
		UseCustomDir = ini.getBool( "custdir",   false );
		ShowReadMe   = ini.getBool( "readme",    true );
		InstMode     = ini.getInt ( "mode",      1 );
		Expire       = ini.getInt ( "expire",    7 );
		int version  = ini.getInt ( "ver",     110 );

// 挿入caldixF
		Filer        = ini.getStr ( "Filer","explorer");
		F_Prefix     = ini.getStr ( "F_Prefix","");
		F_Suffix     = ini.getStr ( "F_Suffix","");
		if( Filer=="" || Filer.isSame("explorer") )
			Filer = kiPath(kiPath::Win,true), Filer += "explorer.exe";
// 挿入ここまでcaldixF

		for( int i=0; i<DLLID_NUM; ++i )
			DoCheck[i] = false;
		const char* ptr = ini.getStr( "DLLList", "LZzCTRGAYBJaIb7iH" );
		while( *ptr )
			switch( 0x7f & (*(ptr++)) )
			{
			case 'L': DoCheck[UNLHA] = true; break;
			case 'Z': DoCheck[UNZIP] = true; break;
			case 'z': DoCheck[ZIP]   = true; break;
			case 'C': DoCheck[CAB]   = true; break;
			case 'T': DoCheck[TAR]   = true; break;
			case 'R': DoCheck[UNRAR] = true; break;
			case 'G': DoCheck[UNGCA] = true; break;
			case 'A': DoCheck[UNARJ] = true; break;
			case 'Y': DoCheck[YZ1]   = true; break;
			case 'B': DoCheck[BGA]   = true; break;
			case 'J': DoCheck[JACK]  = true; break;
			case 'a': DoCheck[AISH]  = true; break;
			case 'I': DoCheck[ISH]   = true; break;
			case 'b': DoCheck[UNBEL] = true; break;
			case '7': DoCheck[SvZIP] = true; break;
			case 'i': DoCheck[UNIMP] = true; break;
			case 'H': DoCheck[BH]    = true; break;
			case '2': DoCheck[YZ2]   = true; break;
			}
		if( version<111 )
			DoCheck[SvZIP] = true; // ver1.11より前には7-zip32.dllは
			                       // 存在しなかったのでここで強制ON
		if( version<112 )
			DoCheck[UNIMP] = true; // ver1.12より前にはUnImp32.dllは
			                       // 存在しなかったのでここで強制ON
		if( version<118 )
			DoCheck[BH]    = true; // ver1.18より前にはBh32.dllは
			                       // 存在しなかったのでここで強制ON
		if( version<120 )
			DoCheck[YZ2]   = true; // ver1.20より前にはYz2.dllは
			                       // 存在しなかったのでここで強制ON
	}

	void GenerateDLLList()
	{
		if( DoCheck[UNLHA] )
			List.add( new DLLInfo(  "Unlha32.dll",  "unlha32.html", 1 ) );
		if( DoCheck[UNZIP] )
			List.add( new DLLInfo(  "UnZip32.dll",  "unzip32.html", 1 ) );
		if( DoCheck[ZIP]   )
			List.add( new DLLInfo(   "Zip32j.dll",   "zip32j.html", 1 ) ),
			List.add( new DLLInfo(    "Zip32.dll",   "zip32j.html", 2 ) ),
			List.add( new DLLInfo( "Sfx32gui.dat", "sfx32gui.html", 1 ) );
		if( DoCheck[CAB]   )
			List.add( new DLLInfo(    "Cab32.dll",    "cab32.html", 1 ) );
		if( DoCheck[TAR]   )
			List.add( new DLLInfo(    "Tar32.dll",    "tar32.html", 1 ) );
		if( DoCheck[UNRAR] )
			List.add( new DLLInfo(  "Unrar32.dll",  "unrar32.html", 1 ) );
		if( DoCheck[UNGCA] )
			List.add( new DLLInfo(  "UnGCA32.dll",  "ungca32.html", 1 ) );
		if( DoCheck[UNARJ] )
			List.add( new DLLInfo( "Unarj32j.dll",  "unarj32.html", 1 ) );
		if( DoCheck[YZ1]   )
			List.add( new DLLInfo(      "Yz1.dll",      "yz1.html", 1 ) );
		if( DoCheck[BGA]   )
			List.add( new DLLInfo(    "Bga32.dll",    "bga32.html", 1 ) );
		if( DoCheck[JACK]  )
			List.add( new DLLInfo(   "Jack32.dll",   "jack32.html", 1 ) );
		if( DoCheck[AISH]  )
			List.add( new DLLInfo(   "Aish32.dll",   "aish32.html", 1 ) );
		if( DoCheck[ISH]   )
			List.add( new DLLInfo(    "Ish32.dll",    "ish32.html", 1 ) );
		if( DoCheck[UNBEL] )
			List.add( new DLLInfo(  "Unbel32.dll",  "unbel32.html", 1 ) );
		if( DoCheck[SvZIP] )
			List.add( new DLLInfo(  "7-zip32.dll",  "7-zip32.html", 1 ) );
		if( DoCheck[UNIMP] )
			List.add( new DLLInfo(  "UnImp32.dll",  "unimp32.html", 1 ) );
		if( DoCheck[BH] )
			List.add( new DLLInfo(     "Bh32.dll",     "bh32.html", 1 ) );
		if( DoCheck[YZ2] )
			List.add( new DLLInfo(      "Yz2.dll",      "yz2.html", 1 ) );
	}

	~CldxConfig()
	{
		if( !AutoMode )
		{
			kiIniFile ini;
			ini.setFileName( "caldix.ini" );
			ini.setSection( "conf" );

			ini.putStr ( "dll",       InstallTo );
			ini.putInt ( "checkserv", CheckServer );
			ini.putStr ( "proxy",     ProxyServer );
			ini.putInt ( "proxyport", ProxyPort );
			ini.putBool( "askhang",  !George );
			ini.putBool( "custdir",   UseCustomDir );
			ini.putBool( "readme",    ShowReadMe );
			ini.putInt ( "mode",      InstMode );
			ini.putInt ( "expire",    Expire );
			ini.putInt ( "ver",       121 );
// 挿入caldixF
			ini.putStr ( "Filer",     Filer );
			ini.putStr ( "F_Prefix",  F_Prefix );
			ini.putStr ( "F_Suffix",  F_Suffix );
// 挿入ここまでcaldixF

			kiStr chk;
			for( int i=0; i<DLLID_NUM; ++i )
				if( DoCheck[i] )
					chk += "LZzCTRGAYBJaIb7iH2"[i];
			ini.putStr( "DLLList", chk );
		}

		for( unsigned int i=0; i<List.len(); ++i )
			delete List[i];
	}
};

CldxConfig* cfg = NULL;



//----------------------------------------------------------------------------
// DLLのバージョンチェック
//----------------------------------------------------------------------------

struct _zip_version_type
{
	unsigned char major;
	unsigned char minor;
	unsigned char patchlevel;
	unsigned char not_used;
};

struct ZpVer
{
	unsigned long structlen;
	unsigned long flag;
	char betalevel[10];
	char date[20];
	char zlib_version[10];
	_zip_version_type zip;
	_zip_version_type os2dll;
	_zip_version_type windll;
};

static WORD Zip32DLLGetVersion( const char* dll )
{
	WORD ans=0;
	HINSTANCE inst = kiutil::safepathLoadLibrary( dll ); // dll はフルパスなので要らないはずだけど念のため
	if( inst )
	{
		typedef int (WINAPI * ZPVR)(ZpVer*);
		ZPVR v = (ZPVR)::GetProcAddress( inst, "ZpVersion" );
		if( v )
		{
			ZpVer x;
			v( &x );
			ans = x.zip.major*100 + x.zip.minor*10 + x.zip.patchlevel;
		}
		::FreeLibrary( inst );
	}
	return ans;
}

bool DLLInfo::CheckDateTime( const char* localDLL )
{
	// ローカルのDLLの日付を取得
	FILETIME ft;
	SYSTEMTIME StLocal;
	HANDLE h = ::CreateFile(
		localDLL, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
		FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL );
	if( h == INVALID_HANDLE_VALUE )
		return true;
	::GetFileTime( h, NULL, NULL, &ft );
	::FileTimeToSystemTime( &ft, &StLocal );
	::CloseHandle( h );

	// Webの方のの日付を取得
	const char* p = dttm;
	SYSTEMTIME StWeb = StLocal;
	for( StWeb.wYear=0; *p && *p!='/'; ++p )
		StWeb.wYear = StWeb.wYear*10 + (*p-'0');
	if(*p)++p;
	for( StWeb.wMonth=0; *p && *p!='/'; ++p )
		StWeb.wMonth = StWeb.wMonth*10 + (*p-'0');
	if(*p)++p;
	for( StWeb.wDay=0; *p && *p!=' '; ++p )
		StWeb.wDay = StWeb.wDay*10 + (*p-'0');

	// Webの方が1週間以上新しければ更新とかそんな感じ

	if( (StLocal.wYear> StWeb.wYear)
	 || (StLocal.wYear==StWeb.wYear && StLocal.wMonth>StWeb.wMonth)
	 || (StLocal.wYear==StWeb.wYear && StLocal.wMonth==StWeb.wMonth && StLocal.wDay>=StWeb.wDay) )
		return false; // ローカルの方が新しいので更新しない

	if( StLocal.wYear+2 < StWeb.wYear )
		return true; // 確実に1年以上新しいので更新
	if( StLocal.wYear+1 == StWeb.wYear )
		StWeb.wMonth += 12;

	// cfg->Expire 日以上新しければ更新
	StWeb.wDay += 30 * (StWeb.wMonth - StLocal.wMonth);
	return ( StLocal.wDay+cfg->Expire < StWeb.wDay );
}

//-------------------------------------------------------------------------------
// バージョンチェック for caldix/1.15 or older + caldixF fix
//-------------------------------------------------------------------------------
/*
bool DLLInfo::VersionCheckLogic(
	const kiStr& dllName,
	const kiStr& htmlVer,
	int ver, int sub )
{
	// 文字列に変換
	char verstr[100];
	if( dllName=="Unimp32.dll" )
		if( sub==0 )
			::wsprintf( verstr, "%d.%02d", ver/100, ver%100 );
		else if( sub >= 100 )
			::wsprintf( verstr, "%d.%02d%c", ver/100, ver%100, sub/100+'a'-1 );
		else
			::wsprintf( verstr, "%d.%02d%c", ver/100, ver%100, sub+'a'-1 );
	else if( dllName=="7-zip32.dll" ) // 7-zipだけは無条件でSubVersionも数値で入れる
		::wsprintf( verstr, "%d.%02d.%02d.%02d", ver/100, ver%100, sub/100,sub%100 );
	else if( sub < 100 )
		::wsprintf( verstr, "%d.%02d", ver/100, ver%100 );
	else
		::wsprintf( verstr, "%d.%02d%c", ver/100, ver%100, sub/100+'a'-1 );

	// 比較(htmlVerの方がVer.subVerより新しければtrue)
	return 
		(CSTR_GREATER_THAN == ::CompareString(LOCALE_USER_DEFAULT,SORT_STRINGSORT,vers,-1,verstr,-1));
}
*/
//-------------------------------------------------------------------------------
// バージョンチェック for caldix/1.17 or newer
//-------------------------------------------------------------------------------

const char* find_first_non_digit( const char* p )
{
	while( '0'<=*p && *p<='9' ) ++p;
	return p;
}

bool DLLInfo::VersionCheckLogic(
	const kiStr& dllName,    // DLL名
	const kiStr& htmVerStr,  // HTML上のバージョン文字列
	int locVer, int locSub ) // ローカルDLLのバージョン
{
	const char* vs = htmVerStr;
	if( dllName == "Unimp32.dll" && locSub<100 )
		locSub *= 100;

	// バージョン文字列を解析。以下の4つの形式を認識する
	//("%d.%d.%d.%d", ver/100, ver%100, sub/100, sub%100)
	//("%d.%d",       ver/100, ver%100) && sub<100
	//("%d.%d%c",     ver/100, ver%100, sub/100+'a'-1)
	//("%d.%d.%d",    ver/100, ver%100, sub)

	int htmVer=0, htmSub=0;
	{
		// [%d][.]
		const int d1 = atoi(vs);
		vs = find_first_non_digit(vs);
		if( *vs!='.' ) return false; ++vs;
		// [%d]
		const int d2 = atoi(vs);
		const char* vs2 = find_first_non_digit(vs);
		if( vs2-vs==1 && dllName=="Zip32.dll" ) // adhoc対処
			htmVer = d1*100 + d2*10;
		else
			htmVer = d1*100 + d2;
		vs = vs2;
		if( *vs=='.' ) // [.]
		{
			++vs;
			// [%d]
			const int d3 = atoi(vs);
			vs = find_first_non_digit(vs);
			if( *vs=='.' ) // [.]
			{
				++vs;
				// [%d]
				const int d4 = atoi(vs);
				htmSub = d3*100 + d4;
			}
			else
				htmSub = d3;
		}
		else if( 'a'<=*vs && *vs<='z' ) // [a-z]
			htmSub = (*vs-'a'+1) * 100;
		else if( 'A'<=*vs && *vs<='Z' ) // [A-Z]
			htmSub = (*vs-'A'+1) * 100;
	}
	return (locVer < htmVer) || (locVer==htmVer && locSub<htmSub);
}

bool DLLInfo::needToUpdate( kiPath& dlldir )
{
	// 情報取得できていなかった場合は更新しない
	if( vers.len() == 0 )
		return false;

	// 対象DLLのフルパス
	kiPath dllPath( dlldir );
	dllPath += name;

	// DLLが存在しなかった場合は新しくインストール
	if( !kiSUtil::exist( dllPath ) )
	{
		stat.loadRsrc( IDS_NEWINST );
		return true;
	}

	// 存在した場合…
	bool updatable = false;

	if( name=="Sfx32gui.dat" )
	{
		// 特別処理1:Sfx32gui.datの場合は日付チェック
		updatable = CheckDateTime( dllPath );
	}
	else
	{
		// それ以外は、バージョン番号でチェック
		WORD ver=1, sub=0;
		if( name == "Zip32.dll" )
		{
			ver = Zip32DLLGetVersion( dllPath );
		}
		else if( name != "Sfx32gui.dat" )
		{
			kiArcDLLRaw dll( dllPath );
			ver = dll.getVer();
			sub = dll.getVerSub();
			if( name=="Unimp32.dll" && !dll.isVerSubAvail() )
				ver=6, sub=0; // 特殊処理2:古いunimpは0.0.6.0として扱う
		}

		// バージョンチェックをここで実行
		updatable = VersionCheckLogic( name, vers, ver, sub );

		// 特殊処理3:Unrar32.dllで、違いがSubVersionだけのときは日付チェック
		if( updatable && name=="Unrar32.dll" )
		{
			char verstr[100];
			::wsprintf( verstr, "%d.%02dz", ver/100, ver%100 );
			if( CSTR_GREATER_THAN == ::CompareString(LOCALE_USER_DEFAULT,SORT_STRINGSORT,verstr,-1,vers,-1) )
				updatable = CheckDateTime( kiPath(dlldir)+="Unrar.dll" );
		}
	}

	// 更新可能
	if( updatable )
	{
		char str[100];
		::wsprintf( str, stat.loadRsrc( IDS_UPDINST ), (const char*)vers );
		stat = str;
	}
	return updatable;
}



//----------------------------------------------------------------------------
// startDlg : 自動|カスタムの選択
//----------------------------------------------------------------------------

class startDlg : public kiDialog
{
public:
	startDlg() : kiDialog( IDD_START ) {}

private:
	virtual BOOL onInit()
	{
		// 二重起動禁止
		char cls[256], wnd[256];
		::GetClassName(  hwnd(), cls, sizeof(cls)-1 );
		::GetWindowText( hwnd(), wnd, sizeof(wnd)-1 );
		if( HWND h = ::FindWindow(cls,wnd) )
			if( h!=hwnd() || (h=::FindWindowEx(NULL,h,cls,wnd)) )
			{
				end( IDCANCEL );
				setFront( h );
				return FALSE;
			}

		app()->setMainWnd( this );
		UINT md = (cfg->AutoMode ? IDC_WORKTYPE1 : IDC_WORKTYPE2);
		sendMsgToItem( md, BM_SETCHECK, BST_CHECKED );
		return FALSE;
	}

	virtual bool onOK()
	{
		cfg->AutoMode =
			(BST_CHECKED == sendMsgToItem( IDC_WORKTYPE1, BM_GETCHECK ));
		return true;
	}
};



//----------------------------------------------------------------------------
// customDlg : 接続先/インストール先の選択 プロキシ|詳細への分岐
//----------------------------------------------------------------------------

class customDlg : public kiDialog
{
public:
	customDlg() : kiDialog( IDD_CUSTOM1 ) {}

private:
	virtual BOOL onInit()
	{
		app()->setMainWnd( this );
		sendMsgToItem( IDC_DLLDIR, WM_SETTEXT, 0, (LPARAM)(const char*)(cfg->InstallTo) );
		sendMsgToItem( IDC_CONNECTTO1+cfg->CheckServer, BM_SETCHECK, BST_CHECKED );
		return FALSE;
	}

	virtual BOOL CALLBACK proc( UINT msg, WPARAM wp, LPARAM lp )
	{
		if( msg==WM_COMMAND )
			if( LOWORD(wp)==IDC_REF )
			{
				kiSUtil::getFolderDlgOfEditBox( item(IDC_DLLDIR), hwnd(), NULL );
				return TRUE;
			}
			else if(  LOWORD(wp)==IDC_PROXY )
			{
				show( proxyDlg() );
				return TRUE;
			}
			else if(  LOWORD(wp)==IDC_DETAIL )
			{
				show( detailDlg() );
				return TRUE;
			}
		return FALSE;
	}

	virtual bool onOK()
	{
		char str[MAX_PATH];
		sendMsgToItem( IDC_DLLDIR, WM_GETTEXT, sizeof(str)-1, (LPARAM)str );
		cfg->InstallTo=str, cfg->InstallTo.beBackSlash( true );
		for(int i=0; i<=2; ++i)
			if( BST_CHECKED == sendMsgToItem( IDC_CONNECTTO1+i, BM_GETCHECK ) )
				cfg->CheckServer = i;
		return true;
	}

	virtual bool onCancel()
	{
		return cancelShiteIidesuka();
	}

private:
	class detailDlg : public kiDialog
	{
	public:
		detailDlg() : kiDialog( IDD_DETAIL ) {}
	private:
		virtual BOOL onInit()
		{
			if( cfg->George )
				sendMsgToItem( IDC_CONNECT_ALWAYS, BM_SETCHECK, BST_CHECKED );
			if( cfg->ShowReadMe )
				sendMsgToItem( IDC_SHOW_README, BM_SETCHECK, BST_CHECKED );
			if( cfg->UseCustomDir )
				sendMsgToItem( IDC_SAVE_DESTDIR, BM_SETCHECK, BST_CHECKED );
			for( int i=0; i<DLLID_NUM; ++i )
				if( cfg->DoCheck[i] )
					sendMsgToItem( IDC_DLHA+i, BM_SETCHECK, BST_CHECKED );
// 挿入caldixF
			sendMsgToItem( IDC_FILERPATH, WM_SETTEXT, 0, (LPARAM)(const char*)(cfg->Filer) );
			sendMsgToItem( IDC_FILEROPT, WM_SETTEXT, 0, (LPARAM)(const char*)(cfg->F_Prefix) );
			sendMsgToItem( IDC_FILEROPT2, WM_SETTEXT, 0, (LPARAM)(const char*)(cfg->F_Suffix) );
			
// 挿入ここまでcaldixF
			return FALSE;
		}

// 挿入caldixF
		virtual BOOL CALLBACK proc( UINT msg, WPARAM wp, LPARAM lp )
		{
			// 指定ボタン
			if((msg==WM_COMMAND) && (LOWORD(wp)==IDC_REF )){
				kiSUtil::getOpenFileNameDlgOfEditBox(item(IDC_FILERPATH),hwnd());
				return TRUE;
			}
			return FALSE;
		}
// 挿入ここまでcaldixF
		
		virtual bool onOK()
		{
			cfg->George =
				(BST_CHECKED==sendMsgToItem( IDC_CONNECT_ALWAYS, BM_GETCHECK ));
			cfg->ShowReadMe =
				(BST_CHECKED==sendMsgToItem( IDC_SHOW_README, BM_GETCHECK ));
			cfg->UseCustomDir =
				(BST_CHECKED==sendMsgToItem( IDC_SAVE_DESTDIR, BM_GETCHECK ));
			for( int i=0; i<DLLID_NUM; ++i )
				cfg->DoCheck[i] =
					(BST_CHECKED==sendMsgToItem( IDC_DLHA+i, BM_GETCHECK ));
// 挿入caldixF
			char str[500];

			sendMsgToItem( IDC_FILERPATH, WM_GETTEXT, sizeof(str)-1, (LPARAM)str );
			cfg->Filer = str;
			sendMsgToItem( IDC_FILEROPT, WM_GETTEXT, sizeof(str)-1, (LPARAM)str );
			cfg->F_Prefix = str;
			sendMsgToItem( IDC_FILEROPT2, WM_GETTEXT, sizeof(str)-1, (LPARAM)str );
			cfg->F_Suffix = str;
// 挿入ここまでcaldixF
			return true;
		}
	};

	class proxyDlg : public kiDialog
	{
	public:
		proxyDlg() : kiDialog( IDD_PROXY ) {}
	private:
		virtual BOOL onInit()
		{
			if( cfg->ProxyPort>0 )
			{
				sendMsgToItem( IDC_USEPROXY, BM_SETCHECK, BST_CHECKED );
				::EnableWindow( item(IDC_HOST_N), TRUE );
				::EnableWindow( item(IDC_PORT_N), TRUE );
				::EnableWindow( item(IDC_HOST),   TRUE );
				::EnableWindow( item(IDC_PORT),   TRUE );
				::SetDlgItemInt( hwnd(), IDC_PORT, cfg->ProxyPort, FALSE );
			}
			else
				::SetDlgItemInt( hwnd(), IDC_PORT, -cfg->ProxyPort, FALSE );
			sendMsgToItem( IDC_HOST, WM_SETTEXT, 0, (LPARAM)(const char*)cfg->ProxyServer );
			return FALSE;
		}

		virtual BOOL CALLBACK proc( UINT msg, WPARAM wp, LPARAM lp )
		{
			if((msg==WM_COMMAND) && (LOWORD(wp)==IDC_USEPROXY)){
					BOOL x = ( BST_CHECKED==sendMsgToItem( IDC_USEPROXY, BM_GETCHECK ) );
					::EnableWindow( item(IDC_HOST_N), x );
					::EnableWindow( item(IDC_PORT_N), x );
					::EnableWindow( item(IDC_HOST),   x );
					::EnableWindow( item(IDC_PORT),   x );
					return TRUE;
			}
			return FALSE;
		}

		virtual bool onOK()
		{
			char str[500];
			sendMsgToItem( IDC_HOST, WM_GETTEXT, sizeof(str)-1, (LPARAM)str );
			cfg->ProxyServer = str;
			cfg->ProxyPort = ::GetDlgItemInt( hwnd(), IDC_PORT, NULL, FALSE );
			if( BST_CHECKED!=sendMsgToItem( IDC_USEPROXY, BM_GETCHECK ) )
				cfg->ProxyPort *= -1;
			return true;
		}
	};
};

//----------------------------------------------------------------------------
// mirrorMan : バージョンチェック先サーバ情報管理
//----------------------------------------------------------------------------

class mirrorInfo
{
private:
	enum { MAD, CSD, WAK, SERVER_NUM };
	static const char* c_serv[SERVER_NUM];
	static const char* c_path[SERVER_NUM];
	static const char* c_nick[SERVER_NUM];

	int mirrors[SERVER_NUM+1];

public:
	mirrorInfo()
	{
		for(int i=0; i!=SERVER_NUM+1; ++i)
			mirrors[i] = -1;
	}

	// どのサーバを先に見に行くか選択。
	void setPreferedServer( int p )
	{
		switch( p )
		{
		default:
		case MAD:
			mirrors[0] = MAD;
			mirrors[1] = CSD;
			mirrors[2] = WAK;
			break;
		case CSD:
			mirrors[0] = CSD;
			mirrors[1] = MAD;
			mirrors[2] = WAK;
			break;
		case WAK:
			mirrors[0] = WAK;
			mirrors[1] = MAD;
			mirrors[2] = CSD;
			break;
		}
	}

	// m番目のミラーサーバがDownしてるっぽい場合、候補から消去
	void server_is_down( int m )
	{
		for(int i=m; i!=SERVER_NUM; ++i)
			mirrors[i] = mirrors[i+1];
	}

	// t番目のミラーサーバの情報を取得
	bool isMirrorLeft( int m ) const { return mirrors[m] >= 0; }
	kiStr serverName ( int m ) const { return c_serv[mirrors[m]]; }
	kiStr basePath   ( int m ) const { return c_path[mirrors[m]]; }
	kiStr nickname   ( int m ) const { return c_nick[mirrors[m]]; }
	kiStr baseURL    ( int m ) const {
		return (kiStr("http://")+=c_serv[mirrors[m]])+=c_path[mirrors[m]];
	}
};

// static constants

const char* mirrorInfo::c_serv[mirrorInfo::SERVER_NUM] = {
	"www.madobe.net",
	"www.csdinc.co.jp",
	"archiver.wakusei.ne.jp",
};
const char* mirrorInfo::c_path[mirrorInfo::SERVER_NUM] = {
	"/archiver/lib/",
	"/archiver/lib/",
	"/lib/",
};
const char* mirrorInfo::c_nick[mirrorInfo::SERVER_NUM] = {
	"Madobe",
	"CSD",
	"Wakusei",
};
static mirrorInfo mirrorMan;

//----------------------------------------------------------------------------
// verDlg : 接続して実際のバージョンチェックを行う
//----------------------------------------------------------------------------

class verDlg : public kiDialog
{
public:
	verDlg( HINTERNET hi )
		: kiDialog( IDD_VERCHECK ), h(hi), ss(NULL), http(NULL) {}

	~verDlg()
	{
		if( ss )
			::InternetCloseHandle( ss );
		if( http )
			::InternetCloseHandle( http );
	}

private:

	HANDLE        hT;
	HINTERNET     h, http, ss;
	volatile bool m_endFlag;

	BOOL onInit()
	{
		app()->setMainWnd( this );
		sendMsgToItem( IDC_BAR, PBM_SETSTEP, 1 );
		sendMsgToItem( IDC_BAR, PBM_SETRANGE, 0, MAKELPARAM(0,cfg->List.len()*3) );

		m_endFlag = false;
		DWORD id;
		if( !(hT = ::CreateThread( NULL, 0, threadEntry, this, 0, &id )) )
			end( IDCANCEL );
		return FALSE;
	}

	bool onCancel()
	{
		m_endFlag = true;
		DWORD ex;
		while( ::GetExitCodeThread( hT, &ex ) && ex==STILL_ACTIVE )
			::Sleep(0);
		::CloseHandle( hT );
		return true;
	}

	static DWORD WINAPI threadEntry( void* prm )
	{
		((verDlg*)prm)->threadWorker();
		return 0;
	}

private:

	// 作業用マクロ
	#define CHECK_CANCEL()      if(m_endFlag) break
	#define STEP_GRAPH()	    CHECK_CANCEL(); sendMsgToItem( IDC_BAR, PBM_STEPIT )
	#define RETRY_()            ::InternetCloseHandle( http ); http=NULL; goto retry;
	#define RETRY_error()       do{ ++Mir; RETRY_() }while(0)
	#define RETRY_serverdown()  do{ mirrorMan.server_is_down(Mir); RETRY_() }while(0)
	#define INET_CONNECT( __s ) ::InternetConnect( h, __s, INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0 )
	#define HTTP_REQ( __n )     ::HttpOpenRequest( http, "GET", __n, "HTTP/1.0", NULL, NULL, 0, 0 )
	#define HTTP_SEND()         status=0,::HttpSendRequest( ss, NULL, 0, NULL, 0 ), ::HttpQueryInfoA( ss, HTTP_QUERY_STATUS_CODE|HTTP_QUERY_FLAG_NUMBER,&status,&size,&dw),status;

	void threadWorker()
	{
		// 作業変数
		DWORD dw = 0, size = sizeof(DWORD), status;
		kiStr name;
		char buf[4000];

		// メインループ
		for( unsigned int i=0; i<cfg->List.len(); ++i )
		{
			if( !mirrorMan.isMirrorLeft(0) )
				break; // どのサーバにも繋げない場合、終了
			STEP_GRAPH();

			//
			// 未接続ならサーバへ繋ぐ
			//
			int Mir = 0;
		retry:
			if( !http )
			{
				if( !mirrorMan.isMirrorLeft(Mir) )
					continue; // このDLLの情報取得は無理でした
				http = INET_CONNECT( mirrorMan.serverName(Mir) );
				if( !http )
					RETRY_serverdown(); // 次のミラーを試す
			}

			//
			// HTTPリクエスト作成
			//
			name = mirrorMan.basePath(Mir);
			name+= cfg->List[i]->htmlName();
			ss   = HTTP_REQ( name );
			if( !ss )
				RETRY_serverdown();
			CHECK_CANCEL();

			//
			// HTTPリクエスト送信
			//
			bool succeeded = false;
			int try_auth = 3;
			while( !succeeded && (try_auth-->0) )
			{
				status = HTTP_SEND();

				if( status/100 == 2 )  // 200など。成功
				{
					succeeded = true;
					break;
				}
				else if( status == 0 ) // タイムアウト。別サーバをtry
				{
					::InternetCloseHandle( ss );
					RETRY_serverdown();
				}
				else if( status == 407 ) // 407 Proxy Authentication Required
				{
					// パスワードを打たせてもう一度
					DWORD dwRet = ::InternetErrorDlg(
						hwnd(), ss, GetLastError(),
						FLAGS_ERROR_UI_FILTER_FOR_ERRORS |
						FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS |
						FLAGS_ERROR_UI_FLAGS_GENERATE_DATA,
						NULL );
					// 認証失敗したら終了
					if( dwRet != ERROR_INTERNET_FORCE_RETRY )
					{
						::InternetCloseHandle( ss );
						end( IDOK ); return;
					}
					continue;
				}
				else // その他のエラー。あきらめて次のDLLに進む
				{
					::InternetCloseHandle( ss );
					break;
				}
			}

			//
			// 読込
			//
			STEP_GRAPH();
			if( succeeded )
			{
				DWORD read;
				::InternetReadFile( ss, buf, sizeof(buf)-1, &read );
				::InternetCloseHandle( ss );
				buf[ read ] = '\0';
			}

			//
			// 解析
			//
			STEP_GRAPH();
			if( succeeded )
				cfg->List[i]->ParseHTML( buf );
		}

		if( !m_endFlag )
			end( IDOK );
	}
};



//----------------------------------------------------------------------------
// dllDlg: ダウンロードするDLLの選択
//----------------------------------------------------------------------------

class dllDlg : public kiDialog
{
public:
	dllDlg( kiArray<DLLInfo*>& upd ) : kiDialog( IDD_CUSTOM2 ), m_upd(upd) {}

private:
	kiArray<DLLInfo*>& m_upd;

	BOOL onInit()
	{
		app()->setMainWnd( this );

		SHFILEINFO fi;
		HIMAGELIST hI = (HIMAGELIST)::SHGetFileInfo(
			kiPath(kiPath::Sys)+="KERNEL32.DLL", 0, &fi, sizeof(fi),
			SHGFI_SYSICONINDEX | SHGFI_ICON | SHGFI_SMALLICON );
		int iDLL = fi.iIcon;
		int iEXE = kiSUtil::getSysIcon( "exe" );

		kiStr str;
		kiListView list( this, IDC_UPDATELIST );
		list.setImageList( NULL, hI );
		list.insertColumn( 0, str.loadRsrc( IDS_LV_DLLNAME ), 120 );
		list.insertColumn( 1, str.loadRsrc( IDS_LV_STATE ), 110 );
		for( unsigned int i=0; i<m_upd.len(); i++ )
		{
			list.insertItem( i, m_upd[i]->getName(), 0, strcmpi( "dll", kiPath::ext(m_upd[i]->getName()) ) ? iEXE : iDLL );
			list.setSubItem( i, 1, m_upd[i]->getState() );
		}
		sendMsgToItem( IDC_UPDATELIST,LVM_SETEXTENDEDLISTVIEWSTYLE,LVS_EX_CHECKBOXES,LVS_EX_CHECKBOXES );
		for( i=0; i!=m_upd.len(); i++ )
			ListView_SetCheckState( item(IDC_UPDATELIST), i, TRUE );
		for( int j=0; j!=3; j++ )
			sendMsgToItem( IDC_INSTMODE1+j,BM_SETCHECK,j==cfg->InstMode ? BST_CHECKED : BST_UNCHECKED );
		return FALSE;
	}
	bool onCancel()
	{
		if( !cancelShiteIidesuka() )
			return false;
		kiListView( this, IDC_UPDATELIST ).setImageList( NULL, NULL );
		return true;
	}
	bool onOK()
	{
		for( unsigned int i=0; i!=3; i++ )
			if( BST_CHECKED == sendMsgToItem( IDC_INSTMODE1+i, BM_GETCHECK ) )
			{
				cfg->InstMode = i;
				break;
			}

		kiArray<DLLInfo*> res;
		res = m_upd, m_upd.empty();
		for( i=0; i<res.len(); i++ )
			if( ListView_GetCheckState( item(IDC_UPDATELIST), i ) )
				m_upd.add( res[i] );

		kiListView( this, IDC_UPDATELIST ).setImageList( NULL, NULL );
		return true;
	}
};



//----------------------------------------------------------------------------
// downloadDlg : ダウンロード
//----------------------------------------------------------------------------

class downloadDlg : public kiDialog, IBindStatusCallback
{
public:
	downloadDlg( const kiArray<DLLInfo*>& lst, const kiPath& pth )
		: kiDialog( IDD_VERCHECK ), m_lst( lst ), m_pth( pth ), m_cRef( 1 ){}

private:
	const kiArray<DLLInfo*>& m_lst;
	const kiPath& m_pth;
	unsigned long m_cRef;
	unsigned long m_total;
	unsigned long m_step;
	unsigned long m_startTime, m_lastTime;
	bool m_stateCancel;

	BOOL onInit()
	{
		app()->setMainWnd( this );

		m_startTime = m_lastTime = ::GetTickCount();
		m_stateCancel = false;
		m_step = m_total = 0;
		for( unsigned int i=0; i<m_lst.len(); i++ )
			m_total += m_lst[i]->getSize();
		sendMsgToItem( IDC_BAR, PBM_SETRANGE,0, MAKELPARAM(0,32768) );
		setTimeInfo( 0 );
		::ShowWindow( hwnd(), SW_SHOW );
		::UpdateWindow( hwnd() );

		kiPath url, fil;

		// メインループ
		for( i=0; i<m_lst.len(); i++ )
		{
			int Mir = 0;
		retry:
			if( !mirrorMan.isMirrorLeft(Mir) )
			{
				app()->msgBox( kiStr(1000).loadRsrc( IDS_DLERROR ), "caldix", MB_ICONINFORMATION|MB_SYSTEMMODAL );
				break;
			}

			// "○○をダウンロード中..." のテキストを更新
			char msg[300];
			::wsprintf( msg, kiStr().loadRsrc(IDS_DOWNLOADING),
						(const char*)m_lst[i]->getName(), (const char*)mirrorMan.nickname(Mir) );
			sendMsgToItem( IDC_MSG, WM_SETTEXT, 0, (LPARAM)msg );
			::EnableWindow( item(IDC_MSG), FALSE );
			::EnableWindow( item(IDC_MSG), TRUE );

			// ダウンロード元とダウンロード先
			url = mirrorMan.baseURL(Mir), url += m_lst[i]->getArchiveName();
			fil = m_pth,                  fil += m_lst[i]->getArchiveName();
			if( S_OK != ::URLDownloadToFile( NULL, url, fil, 0, this ) )
			{
				++Mir;
				goto retry;
			}
			m_step += m_lst[i]->getSize();
		}
		end( i==m_lst.len() ? IDOK : IDCANCEL );
		return FALSE;
	}
	bool onCancel()
	{
		if( cancelShiteIidesuka() )
			m_stateCancel = true;
		return false;
	}

public:
	void setTimeInfo( unsigned long bytesread )
	{
		int sec = (bytesread==0 || m_lastTime==m_startTime)
		  ? (int)((double)(m_total) / 7000)
		  : (int)((double)((m_total-bytesread)) * (m_lastTime - m_startTime) / 1000 / bytesread);

		static char str[300];
		::wsprintf( str, (const char*)(kiStr().loadRsrc(IDS_REST)),
			(m_total-bytesread)/1000, sec/60, sec%60 );

		sendMsgToItem( IDC_RESTTIME, WM_SETTEXT, 0, (LPARAM)str );
	}

	STDMETHODIMP_(ULONG) AddRef()	{ return (++m_cRef); }
	STDMETHODIMP_(ULONG) Release()	{ return (m_cRef ? --m_cRef : 0L); }
	STDMETHODIMP GetBindInfo( DWORD*, BINDINFO* ) { return E_NOTIMPL; }
	STDMETHODIMP GetPriority( LONG* ) { return E_NOTIMPL; }
	STDMETHODIMP OnLowResource( DWORD ) { return E_NOTIMPL; }
	STDMETHODIMP OnDataAvailable( DWORD, DWORD, FORMATETC*, STGMEDIUM* ) { return E_NOTIMPL; }
	STDMETHODIMP OnObjectAvailable( REFIID, IUnknown* ) { return E_NOTIMPL; }
	STDMETHODIMP OnStartBinding( DWORD, IBinding* ) { return E_NOTIMPL; }
	STDMETHODIMP OnStopBinding( HRESULT, LPCWSTR ) { return E_NOTIMPL; }
	STDMETHODIMP QueryInterface( REFIID riid, void** ppv )
	{
		*ppv = NULL;
		AddRef();
		if( IsEqualIID( riid, IID_IUnknown ) || IsEqualIID( riid, IID_IBindStatusCallback ) )
			*ppv = (IBindStatusCallback*)this;
		else
		{
			Release();
			return E_NOINTERFACE;
		}
		return NOERROR;
	}
	STDMETHODIMP OnProgress( ULONG cur, ULONG max, ULONG status, LPCWSTR txt )
	{
		msgLoop( PEEK );

		if( m_stateCancel )
		{
			end( IDCANCEL );
			return E_ABORT;
		}

		DWORD time = ::GetTickCount();
		if( time-m_lastTime >= 5000 )
		{
			m_lastTime = time;
			setTimeInfo( m_step+cur );
		}

		sendMsgToItem( IDC_BAR, PBM_SETPOS, (int)(((double)(m_step+cur))/m_total*32768) );
		return S_OK;
	}
};



//----------------------------------------------------------------------------
// instDlg : system等へインストール
//----------------------------------------------------------------------------

class instDlg : public kiDialog
{
public:
	instDlg( kiArray<DLLInfo*>& upd, const kiPath& arcdir, const kiPath& work, const kiPath& to )
		: kiDialog( IDD_INSTALL ), m_upd( upd ), m_arcdir( arcdir ), m_workdir( work ), m_dlldir( to ) {}

private:
	kiArray<DLLInfo*>& m_upd;
	const kiPath& m_arcdir;
	const kiPath& m_workdir;
	const kiPath& m_dlldir;
	bool onCancel()	{ return false; }
	bool onOK()		{ return false; }

	BOOL onInit()
	{
		bool needreboot=false;
		app()->setMainWnd( this );
		::ShowWindow( hwnd(), SW_SHOW );
		::UpdateWindow( hwnd() );
		//-- 解凍&インストール開始〜 ---------------

		char tmp[MAX_PATH];
		kiPath arc, dll, path;
		for( unsigned int i=0; i<m_upd.len(); i++ )
		{
			// 書庫名:フルパス
			arc = m_arcdir, arc += m_upd[i]->getArchiveName();
			// ドキュメント保存先:フルパス
			dll = m_workdir, ::lstrcpyn(tmp,m_upd[i]->getName(),m_upd[i]->getName().len()-3), dll += tmp, dll.remove();
			dll += "\\", dll.mkdir(), ::SetCurrentDirectory( dll );
			// 解凍( path=書庫内のDLLの相対パス )
			melt_it( arc, m_upd[i]->getName(), path );
			// インストール
			if( !install_it( dll, path ) )
			{
				needreboot = true;
			}
			if( path.isSame( "aish32.dll" ) )
			{
				if( !install_it( dll, "Aishmv32.dll" ) )
					needreboot = true;
			}
			else if( path.isSame( "unrar32.dll" ) )
			{
				if( !install_it( dll, "unrar.dll" ) )
					needreboot = true;
			}
			else if( path.isSame( "yz1.dll" ) )
			{
				if( !install_it( dll, "yzdec.exe" ) )
					needreboot = true;
				kiPath uyz( m_dlldir );
				if( !kiSUtil::exist( uyz+="UnYz1.dll" ) )
					if( !install_it( dll, "UnYz1.dll" ) )
						needreboot = true;
			}
			else if( path.isSame( "bh32.dll" ) )
			{
				if( !install_it( dll, "bhsfx.exe" ) )
					needreboot = true;
			}
			// メッセージ処理
			msg();
		}

		//-- 解凍&インストール終了〜 ---------------
		end( needreboot ? IDCANCEL : IDOK );
		return FALSE;
	}

private:
	bool install_it( const char* position, const char* relpath )
	{
		kiPath from( position ); from += relpath;
		if( ::GetFileAttributes(from) != 0xffffffff )
		{
			kiPath to( m_dlldir ); to += kiPath::name(relpath);
			if( ::CopyFile( from, to, FALSE ) )
				::DeleteFile( from );
			else
			{
				// 再起動後コピー準備
				move_later( from, to );
				return false;
			}
		}
		return true;
	}
};



//----------------------------------------------------------------------------
// caldix : メインルーチン
//----------------------------------------------------------------------------

#include <ras.h>
#include <raserror.h>

class caldix : public kiApp
{
	caldix()
	{
		shellInit();
		kiutil::pathInit();

		cfg = new CldxConfig;
	}

	void run( kiCmdParser& cmd )
	{
		// ダイアログ [全自動/カスタム]
		if( !cfg->SuperAutoMode && !show( startDlg() ) )
			return;

		if( cfg->AutoMode )
		{
			// 全自動なら、インストール先をシステムにするかもしれない
			if( !cfg->UseCustomDir )
				cfg->InstallTo.beSpecialPath( kiPath::Sys );
		}
		else
		{
			// ダイアログ [インストール先とか]
			if( !show( customDlg() ) )
				return;
		}

		// 設定の調整
		cfg->InstallTo.beBackSlash( true );
		cfg->GenerateDLLList();

		// 常時接続モードでない場合、接続を確立
		if( !cfg->George )
			if( ERROR_SUCCESS != ::InternetAttemptConnect(0) )
			{
				msgBox( kiStr().loadRsrc( IDS_NOINTERNET ) );
				return;
			}

		// WinInet開始…
		HINTERNET h = ::InternetOpen( cfg->UA, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0 );
		if( !h )
		{
			msgBox( kiStr().loadRsrc( IDS_NOINTERNET ) );
			return;
		}

		// プロクシの設定
		if( cfg->ProxyServer.len() && cfg->ProxyPort>0 )
		{
			kiStr pr( "http=http://" );
			pr += cfg->ProxyServer;
			pr += ':';
			pr += kiStr().setInt(cfg->ProxyPort);
			INTERNET_PROXY_INFO pi = {INTERNET_OPEN_TYPE_PROXY,(const char*)pr,"<local>"};
			::InternetSetOption(  h, INTERNET_OPTION_PROXY, (void*)&pi, sizeof(pi) );
			::UrlMkSetSessionOption( INTERNET_OPTION_PROXY, (void*)&pi, sizeof(pi), 0 );
		}

		// タイムアウトは5秒
		DWORD TimeOut = 5*1000;
		::InternetSetOption( h, INTERNET_OPTION_RECEIVE_TIMEOUT, &TimeOut, sizeof(TimeOut) );

		// 接続先サーバ情報を設定から取得
		mirrorMan.setPreferedServer( cfg->CheckServer );

		// ダイアログ [WWWC META CHECK]
		if( !show( verDlg(h) ) )
		{
			::InternetCloseHandle( h );
			askRasHangUp();
			return;
		}

		// バージョン比較
		kiStr tmp;
		kiArray<DLLInfo*> upd;
		for( unsigned int i=0; i<cfg->List.len(); ++i )
			if( cfg->List[i]->needToUpdate( cfg->InstallTo ) )
				upd.add( cfg->List[i] );

		if( upd.len()==0 )
		{
			::InternetCloseHandle( h );
			msgBox( kiStr().loadRsrc( IDS_NONEED ), "caldix", MB_ICONINFORMATION );
			askRasHangUp();
			return;
		}

		if( ! cfg->AutoMode )
		{
			// ダイアログ [インストールするDLLとか]
			if( !show(dllDlg(upd)) || upd.len()==0 )
			{
				::InternetCloseHandle( h );
				askRasHangUp();
				return;
			}
		}

		// ダウンロード
		char buf[MAX_PATH];
		::GetTempFileName( kiPath( kiPath::Tmp ), "cld", 0, buf );
		::DeleteFile( buf );
		kiPath dlto( buf );
		dlto.beBackSlash( true );
		dlto.mkdir();

		if( !show( downloadDlg(upd, dlto) ) )
		{
			dlto.remove();
			::InternetCloseHandle( h );
			askRasHangUp();
			return;
		}

		// インストール準備
		bool needreboot = false;
		cfg->InstallTo.mkdir();
		kiPath workDir( kiPath::Exe ); workDir.lower();
		UINT drv = workDir.getDriveType();
		if( strstr(workDir,"temporary internet files")!=NULL
		 || (drv!=DRIVE_FIXED && drv!=DRIVE_REMOTE && drv!=DRIVE_UNKNOWN) )
			workDir.beSpecialPath( kiPath::Dsk ), workDir.beBackSlash( true );
		workDir += "cldx\\";
		workDir.mkdir();

		if( cfg->InstMode != 1 )
		{
			// 書庫コピー
			kiPath from, to;
			for( unsigned int i=0; i<upd.len(); i++ )
			{
				from = dlto, from += upd[i]->getArchiveName();
				to = workDir,  to += upd[i]->getArchiveName();
				::CopyFile( from, to, FALSE );
			}
		}
		if( cfg->InstMode != 0 )
		{
			// インストール
			if( !show( instDlg( upd, dlto, workDir, cfg->InstallTo ) ) )
				needreboot = true;
		}

		// 閉じ処理
		dlto.remove();
		::InternetCloseHandle( h );

		if( !needreboot )
		{
			if( ! cfg->SuperAutoMode )
				msgBox( kiStr(500).loadRsrc( IDS_FINISHED ), "caldix", MB_ICONINFORMATION|MB_SYSTEMMODAL );
			askRasHangUp();
		}
		else
			if( IDYES!=msgBox( kiStr(1000).loadRsrc( IDS_REBOOT ), "caldix", MB_ICONINFORMATION|MB_YESNO|MB_SYSTEMMODAL ) )
				needreboot = false;

		// 簡易説明書出力
		if( ::GetACP() == 932 )
		{
			kiFile fp;
			if( fp.open( kiPath(workDir)+="読んでね.txt", false ) )
			{
				static const char * text =
"このフォルダには、caldixでダウンロードされた\r\n"
"DLLの説明書を格納してあります。よくお読み下さい。\r\n"
"\r\n"
"★ UNZIP32.DLL を企業内等で利用する場合には、必ず\r\n"
"★ ライセンス契約が必要です。詳細は下記ページにて。\r\n"
"★ http://www.csdinc.co.jp/archiver/lib/unzip32.html\r\n"
"\r\n"
"それぞれのDLLの作者様のサイトは、\r\n"
" 『統合アーカイバプロジェクト』\r\n"
"   http://www.csdinc.co.jp/archiver/ \r\n"
"からリンクをたどって訪れることが出来ます。\r\n"
"是非一度は行ってみることをお勧めします。\r\n"
"\r\n"
"DLLのアンインストールは uncaldix が便利。\r\n"
"     http://www6.plala.or.jp/amasoft/soft/uncaldix.html\r\n"
"\r\n"
"caldixに関するご質問等は( http://www.kmonos.net/ )へ\r\n"
				; fp.write( text, ki_strlen(text) );
			}
		}

		// 出力先開く
		if( cfg->ShowReadMe )
		{
			char cmdline[1000];
// コメントアウトcaldixF
/*
			::wsprintf( cmdline, "explorer \"%s\"", (const char*)workDir );
			::WinExec( cmdline, SW_SHOWDEFAULT );
*/
// コメントアウトここまでcaldixF

// 挿入caldixF
			char wdir[MAX_PATH*3];

			// caldixのパスを取得
			GetModuleFileName(NULL,wdir,MAX_PATH);
			// caldix.exe→cldxにする
			lstrcpy(GetFileName(wdir),"cldx");

			// prefixとpostfixをつける( modified by k.inaba )
			bool bNeedQuote = false;
			for(int i=0; i!=cfg->Filer.len(); ++i)
				if(cfg->Filer[i]==' ') { bNeedQuote=true; break; }
			::wsprintf( cmdline,
				bNeedQuote ? "\"%s\" %s\"%s\"%s" : "%s %s\"%s\"%s",
				(const char*)cfg->Filer,
				(const char*)cfg->F_Prefix, wdir, (const char*)cfg->F_Suffix);

			// ファイラ起動( modified by k.inaba )
			STARTUPINFO si = {sizeof(si)};
			PROCESS_INFORMATION pi;
			{
				kiPath original_cur(kiPath::Cur), sys(kiPath::Sys); // 大丈夫なはずだけど念のためのカレント移動
				::SetCurrentDirectory(sys);
				::CreateProcess( NULL, cmdline, NULL, NULL, FALSE, 0, NULL, wdir, &si, &pi );
				::SetCurrentDirectory(original_cur); 
			}
			::CloseHandle(pi.hProcess);
			::CloseHandle(pi.hThread);
// 挿入ここまでcaldixF
		}

		if( needreboot )
			rebootWindows(); // 再起動でーす
	}

	void askRasHangUp()
	{
		// 訊かないモード
		if( cfg->George )
			return;

		// DLL,及び必要な関数をロード
		HINSTANCE rasdll = kiutil::safepathLoadLibrary( "RASAPI32.DLL" );
		if( rasdll == NULL )
			return;
		typedef DWORD (WINAPI * rE)( LPRASCONN,LPDWORD,LPDWORD );
		typedef DWORD (WINAPI * rH)( HRASCONN );
		typedef BOOL  (WINAPI * rS)( HRASCONN,LPRASCONNSTATUS );
		rE RasEnumConnections = (rE)::GetProcAddress( rasdll, "RasEnumConnectionsA" );
		rH RasHangUp          = (rH)::GetProcAddress( rasdll, "RasHangUpA" );
		rS RasGetConnectStatus= (rS)::GetProcAddress( rasdll, "RasGetConnectStatusA" );
		if( RasEnumConnections!=NULL && RasHangUp!=NULL && RasGetConnectStatus!=NULL )
		{
			RASCONN* RasConn = new RASCONN[1];
			DWORD Num, Size = sizeof(RASCONN);
			RasConn->dwSize = sizeof(RASCONN);
			int Sts = RasEnumConnections( RasConn, &Size, &Num );
			if( (Sts==ERROR_BUFFER_TOO_SMALL) || (Sts==ERROR_NOT_ENOUGH_MEMORY) )
			{
				delete [] RasConn;
				RasConn = new RASCONN[Size/sizeof(RASCONN)];
				Sts = RasEnumConnections( RasConn, &Size, &Num );
			}
			if( Sts==0 && Num>=1 )
			{
				// 訊く。
				if( IDYES==msgBox( kiStr(1000).loadRsrc( IDS_RASHANGUP ), "caldix", MB_ICONINFORMATION|MB_YESNO|MB_SYSTEMMODAL ) )
				{
					// 切る
					RASCONNSTATUS RasSts;
					RasSts.dwSize = sizeof(RASCONNSTATUS);
					for( DWORD i=0; i<Num; i++ )
					{
						RasHangUp( RasConn->hrasconn );
						while( RasGetConnectStatus( RasConn->hrasconn, &RasSts ) != ERROR_INVALID_HANDLE )
							::Sleep( 10 );
					}
				}
			}
			delete [] RasConn;
		}
		::FreeLibrary( rasdll );
	}

	~caldix()
	{
		delete cfg;
	}

	friend void kilib_create_new_app();
};

void kilib_create_new_app()
{
	new caldix;
}