/*
 * The Cryptonit security software suite is developped by IDEALX
 * Cryptonit Team (http://IDEALX.org/ and http://cryptonit.org).
 *
 * Copyright 2004 IDEALX
 *
 * 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 St, Fifth Floor, Boston, MA
 * 02110-1301, USA. 
 *
 * In addition, as two special exceptions:
 *
 * 1) IDEALX S.A.S gives permission to:
 *  * link the code of portions of his program with the OpenSSL library under
 *    certain conditions described in each source file
 *  * distribute linked combinations including the two, with respect to the
 *    OpenSSL license and with the GPL
 *
 * You must obey the GNU General Public License in all respects for all of the
 * code used other than OpenSSL. If you modify file(s) with this exception,
 * you may extend this exception to your version of the file(s), but you are
 * not obligated to do so. If you do not wish to do so, delete this exception
 * statement from your version, in all files (this very one along with all
 * source files).

 * 2) IDEALX S.A.S acknowledges that portions of his sourcecode uses (by the
 * way of headers inclusion) some work published by 'RSA Security Inc.'. Those
 * portions are "derived from the RSA Security Inc. PKCS #11Cryptographic
 * Token Interface (Cryptoki)" as described in each individual source file.
 */
#include "P11USBDevices.hh"

namespace Cryptonit {

#ifdef __WIN32__

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <string.h>
#include <vector>

#include <windows.h>
#include <winreg.h>


/** Try to detect *known* USB devices, using the knownUSBDevices list.
 *  Connected devices are detected using the Windows registry in the
 *  subkey defined by REGISTRY_USB_SUBKEY.
 *  Detected devices are added into the usb_devices vector.
 *
 * Return the number of detected devices, or
 *  -1: if the registry cannot be open
 *  -2: cannot get the numbers of entries
 *  -3: not enough memory to allocate the max subkey name
 */
int getUSBDevices( std::vector<P11Device>& usb_devices )
{
    HKEY hKey;
    DWORD nSubKeys, maxSubKeyLen, maxSubKeyLenBuffer;
    DWORD index;
    LPSTR subKeyName;
    LONG ret;
    int count = 0;

    if( RegOpenKeyEx( HKEY_LOCAL_MACHINE, REGISTRY_USB_SUBKEY, 0,
		      KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE, &hKey )
	!= ERROR_SUCCESS ) {
	return -1;
    }

    /* Get the total number of subkeys */
    if( RegQueryInfoKey( hKey,
			 NULL, /* LPTSTR lpClass */
			 NULL, /* LPDWORD lpcClass */
			 NULL, /* LPDWORD lpReserve */
			 &nSubKeys, /* LPDWORD lpcSubKeys */
			 &maxSubKeyLen, /* LPDWORD lpcMaxSubKeyLen */
			 NULL, /* LPDWORD lpcMaxClassLen */
			 NULL, /* LPDWORD lpcValues */
			 NULL, /* LPDWORD lpcMaxValueNameLen */
			 NULL, /* LPDWORD lpcMaxValueLen */
			 NULL, /* LPDWORD lpcbSecurityDescriptor */
			 NULL /* PFILETIME lpftLastWriteTime */ )
	!= ERROR_SUCCESS ) {
	RegCloseKey( hKey );
	return -2;
    }


    /* Add 1 to max size since Windows 95/98/ME do not add
     *  space for the final 0
     */
    maxSubKeyLen = maxSubKeyLen + 1;

    subKeyName = (CHAR*) malloc(maxSubKeyLen * sizeof(CHAR));
    if( subKeyName == NULL ) {
	RegCloseKey( hKey );
	return -3;
    }

    /* Start enumerating from the last subkeys */
    index = nSubKeys - 1;

    maxSubKeyLenBuffer = maxSubKeyLen;
    ret = RegEnumKeyEx( hKey, index, subKeyName, &maxSubKeyLenBuffer,
			NULL, NULL, NULL, NULL );

    while( index != -1 && (ret != ERROR_NO_MORE_ITEMS || ret != ERROR_SUCCESS) ) {
	char *vidIndex, *pidIndex;
	char vid[5], pid[5];
	unsigned int i;

	vidIndex = strchr( subKeyName, '_' );
	pidIndex = strrchr( subKeyName, '_' );
	memcpy( vid, vidIndex+1, 4 ); vid[4] = 0;
	memcpy( pid, pidIndex+1, 4 ); pid[4] = 0;

	/* Adding only known device ID into the list.
	 */
	for( i = 0; i < MAX_KNOWN_USB_DEVICES; i++ ) {
	    if( strnicmp(knownUSBDevices[i].vendorID, vid, 4) == 0
		&& strnicmp(knownUSBDevices[i].productID, pid, 4) == 0 ) {
		fprintf( stderr, "Adding VID:%s - PID:%s\n", vid, pid );
		P11Device device( knownUSBDevices[i].vendor,
				  knownUSBDevices[i].product,
				  knownUSBDevices[i].path,
				  knownUSBDevices[i].filename );
		usb_devices.push_back( device );
		count++;
	    }
	}

	/* Step to the next entry */
	index--;
	maxSubKeyLenBuffer = maxSubKeyLen;
	ret = RegEnumKey( hKey, index, subKeyName, maxSubKeyLen );
    }

    free( subKeyName );
    RegCloseKey( hKey );
    return count;

}

#else

int getUSBDevices( std::vector<P11Device>& usb_devices )
{
    /* insert some grep gimmick in /[proc,sys]/bus/usb/devices */
    return -1;
}

#endif

}
