/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_xmlsecurity.hxx"

#include <rtl/locale.h>
#include <osl/nlsupport.h> 
#include <osl/process.h>

#include <util.hxx>

#include <stdio.h>

#include <com/sun/star/registry/XImplementationRegistration.hpp>
#include <com/sun/star/security/KeyUsage.hpp>
#include <cppuhelper/bootstrap.hxx>
#include <xmlsecurity/biginteger.hxx>
#include <comphelper/processfactory.hxx>
#include <unotools/streamhelper.hxx>

#include <rtl/ustrbuf.hxx>
#include <tools/string.hxx>

namespace cssu = com::sun::star::uno;
namespace cssl = com::sun::star::lang;
namespace cssxc = com::sun::star::xml::crypto;
namespace cssi = com::sun::star::io;

using namespace ::com::sun::star;

/** convert util::DateTime to ISO Date String */
void convertDateTime( ::rtl::OUStringBuffer& rBuffer,
	const com::sun::star::util::DateTime& rDateTime )
{
    String aString( String::CreateFromInt32( rDateTime.Year ) );
    aString += '-';
    if( rDateTime.Month < 10 )
        aString += '0';
    aString += String::CreateFromInt32( rDateTime.Month );
    aString += '-';
    if( rDateTime.Day < 10 )
        aString += '0';
    aString += String::CreateFromInt32( rDateTime.Day );

    if( rDateTime.Seconds != 0 ||
        rDateTime.Minutes != 0 ||
        rDateTime.Hours   != 0 )
    {
        aString += 'T';
        if( rDateTime.Hours < 10 )
            aString += '0';
        aString += String::CreateFromInt32( rDateTime.Hours );
        aString += ':';
        if( rDateTime.Minutes < 10 )
            aString += '0';
        aString += String::CreateFromInt32( rDateTime.Minutes );
        aString += ':';
        if( rDateTime.Seconds < 10 )
            aString += '0';
        aString += String::CreateFromInt32( rDateTime.Seconds );
		if ( rDateTime.HundredthSeconds > 0)
		{
	        aString += ',';
			if (rDateTime.HundredthSeconds < 10)
				aString += '0';
			aString += String::CreateFromInt32( rDateTime.HundredthSeconds );
		}
    }

    rBuffer.append( aString );
}

::rtl::OUString printHexString(cssu::Sequence< sal_Int8 > data)
{
	int length = data.getLength();
	::rtl::OUString result;
	
	char number[4];
	for (int j=0; j<length; j++)
	{
		sprintf(number, "%02X ", (unsigned char)data[j]);
		result += rtl::OUString::createFromAscii( number );
	}
	
	return result;
}


::rtl::OUString getSignatureInformation( 
	const SignatureInformation& infor, 
	cssu::Reference< ::com::sun::star::xml::crypto::XSecurityEnvironment >& xSecurityEnvironment )
{
	char* status[50] = {
		"STATUS_UNKNOWN",
		"OPERATION_SUCCEEDED",
		"RUNTIMEERROR_FAILED",
		"ENGINE_FAILED",
		"MALLOC_FAILED",
		"STRDUP_FAILED",
		"CRYPTO_FAILED",
		"XML_FAILED",
		"XSLT_FAILED",
		"IO_FAILED",
		"DISABLED",
		"NOT_IMPLEMENTED",
		"INVALID_SIZE",
		"INVALID_DATA",
		"INVALID_RESULT",
		"INVALID_TYPE",
		"INVALID_OPERATION",
		"INVALID_STATUS",
		"INVALID_FORMAT",
		"DATA_NOT_MATCH",
		"INVALID_NODE",
		"INVALID_NODE_CONTENT",
		"INVALID_NODE_ATTRIBUTE",
		"MISSING_NODE_ATTRIBUTE",
		"NODE_ALREADY_PRESENT",
		"UNEXPECTED_NODE",
		"NODE_NOT_FOUND",
		"INVALID_TRANSFORM",
		"INVALID_TRANSFORM_KEY",
		"INVALID_URI_TYPE",
		"TRANSFORM_SAME_DOCUMENT_REQUIRED",
		"TRANSFORM_DISABLED",
		"INVALID_KEY_DATA",
		"KEY_DATA_NOT_FOUND",
		"KEY_DATA_ALREADY_EXIST",
		"INVALID_KEY_DATA_SIZE",
		"KEY_NOT_FOUND",
		"KEYDATA_DISABLED",
		"MAX_RETRIEVALS_LEVEL",
		"MAX_RETRIEVAL_TYPE_MISMATCH",
		"MAX_ENCKEY_LEVEL",
		"CERT_VERIFY_FAILED",
		"CERT_NOT_FOUND",
		"CERT_REVOKED",
		"CERT_ISSUER_FAILED",
		"CERT_NOT_YET_VALID",
		"CERT_HAS_EXPIRED",
		"DSIG_NO_REFERENCES",
		"DSIG_INVALID_REFERENCE",
		"ASSERTION"};
	
	rtl::OUString result;
	
	result += rtl::OUString::createFromAscii( "Security Id : " )
		+rtl::OUString::valueOf(infor.nSecurityId)
		+rtl::OUString::createFromAscii( "\n" );
	result += rtl::OUString::createFromAscii( "Status : [" )
		+rtl::OUString::valueOf((sal_Int32)(infor.nStatus))
		+rtl::OUString::createFromAscii( "] " )
		+rtl::OUString::createFromAscii(status[infor.nStatus])
		+rtl::OUString::createFromAscii( "\n" );
	
	const SignatureReferenceInformations& rInfors = infor.vSignatureReferenceInfors;
	int i;
	int size = rInfors.size();
	
	result += rtl::OUString::createFromAscii( "--References :\n" );
	for (i=0; i<size; i++)
	{
        	result += rtl::OUString::createFromAscii( "---URI : " );
		result += rInfors[i].ouURI;
		result += rtl::OUString::createFromAscii( "\n" );
        	result += rtl::OUString::createFromAscii( "---DigestValue : " );
		result += rInfors[i].ouDigestValue;
		result += rtl::OUString::createFromAscii( "\n" );
	}

        if (infor.ouX509IssuerName.getLength()>0)
        {
        	result += rtl::OUString::createFromAscii( "--X509IssuerName :\n" );
        	result += infor.ouX509IssuerName;
        	result += rtl::OUString::createFromAscii( "\n" );
        }

        if (infor.ouX509SerialNumber.getLength()>0)
        {
        	result += rtl::OUString::createFromAscii( "--X509SerialNumber :\n" );
        	result += infor.ouX509SerialNumber;
        	result += rtl::OUString::createFromAscii( "\n" );
        }
        
        if (infor.ouX509Certificate.getLength()>0)
        {
        	result += rtl::OUString::createFromAscii( "--X509Certificate :\n" );
        	result += infor.ouX509Certificate;
        	result += rtl::OUString::createFromAscii( "\n" );
        }

        if (infor.ouSignatureValue.getLength()>0)
        {
        	result += rtl::OUString::createFromAscii( "--SignatureValue :\n" );
        	result += infor.ouSignatureValue;
        	result += rtl::OUString::createFromAscii( "\n" );
        }

       	result += rtl::OUString::createFromAscii( "--Date :\n" );
       	
	::rtl::OUStringBuffer buffer;
	convertDateTime( buffer, infor.stDateTime );
	result += buffer.makeStringAndClear();
       	result += rtl::OUString::createFromAscii( "\n" );

        if (infor.ouX509IssuerName.getLength()>0 && infor.ouX509SerialNumber.getLength()>0 && xSecurityEnvironment.is())
        {
        	result += rtl::OUString::createFromAscii( "--Certificate Path :\n" );
        	cssu::Reference< ::com::sun::star::security::XCertificate > xCert = xSecurityEnvironment->getCertificate( infor.ouX509IssuerName, numericStringToBigInteger(infor.ouX509SerialNumber) );
        	cssu::Sequence < cssu::Reference< ::com::sun::star::security::XCertificate > > xCertPath;
			if(! xCert.is() ) 
			{
				fprintf(stdout , " xCert is NULL , so can not buildCertificatePath\n");
				return result ;
			}
			else
			{
				xCertPath = xSecurityEnvironment->buildCertificatePath( xCert ) ;
			}
			
		for( int i = 0; i < xCertPath.getLength(); i++ ) 
		{
			result += xCertPath[i]->getSubjectName();
                	result += rtl::OUString::createFromAscii( "\n    Subject public key algorithm : " );
                	result += xCertPath[i]->getSubjectPublicKeyAlgorithm();
                	result += rtl::OUString::createFromAscii( "\n    Signature algorithm : " );
                	result += xCertPath[i]->getSignatureAlgorithm();

                	result += rtl::OUString::createFromAscii( "\n    Subject public key value : " );
                	cssu::Sequence< sal_Int8 > keyValue = xCertPath[i]->getSubjectPublicKeyValue();
                	result += printHexString(keyValue);

                	result += rtl::OUString::createFromAscii( "\n    Thumbprint (SHA1) : " );
                	cssu::Sequence< sal_Int8 > SHA1Thumbprint = xCertPath[i]->getSHA1Thumbprint();
                	result += printHexString(SHA1Thumbprint);

                	result += rtl::OUString::createFromAscii( "\n    Thumbprint (MD5) : " );
                	cssu::Sequence< sal_Int8 > MD5Thumbprint = xCertPath[i]->getMD5Thumbprint();
                	result += printHexString(MD5Thumbprint);

                	result += rtl::OUString::createFromAscii( "\n  <<\n" );
		}
		
               	result += rtl::OUString::createFromAscii( "\n    Key Usage : " );
               	sal_Int32 usage = xCert->getCertificateUsage();
               	
               	if (usage & ::com::sun::star::security::KeyUsage::DIGITAL_SIGNATURE)
               	{
               		result += rtl::OUString::createFromAscii( "DIGITAL_SIGNATURE " );
               	}

               	if (usage & ::com::sun::star::security::KeyUsage::NON_REPUDIATION)
               	{
               		result += rtl::OUString::createFromAscii( "NON_REPUDIATION " );
               	}
               	
               	if (usage & ::com::sun::star::security::KeyUsage::KEY_ENCIPHERMENT)
               	{
               		result += rtl::OUString::createFromAscii( "KEY_ENCIPHERMENT " );
               	}
               	
               	if (usage & ::com::sun::star::security::KeyUsage::DATA_ENCIPHERMENT)
               	{
               		result += rtl::OUString::createFromAscii( "DATA_ENCIPHERMENT " );
               	}
               	
               	if (usage & ::com::sun::star::security::KeyUsage::KEY_AGREEMENT)
               	{
               		result += rtl::OUString::createFromAscii( "KEY_AGREEMENT " );
               	}
               	
               	if (usage & ::com::sun::star::security::KeyUsage::KEY_CERT_SIGN)
               	{
               		result += rtl::OUString::createFromAscii( "KEY_CERT_SIGN " );
               	}
               	
               	if (usage & ::com::sun::star::security::KeyUsage::CRL_SIGN)
               	{
               		result += rtl::OUString::createFromAscii( "CRL_SIGN " );
               	}

               	result += rtl::OUString::createFromAscii( "\n" );
        }
        
	result += rtl::OUString::createFromAscii( "\n" );
	return result;
}

::rtl::OUString getSignatureInformations( 
	const SignatureInformations& SignatureInformations,
	cssu::Reference< ::com::sun::star::xml::crypto::XSecurityEnvironment > xSecurityEnvironment ) 
{
	rtl::OUString result;
	int i;
	int size = SignatureInformations.size();
	
	for (i=0; i<size; i++)
	{
		const SignatureInformation& infor = SignatureInformations[i];
		result += getSignatureInformation( infor, xSecurityEnvironment );
	}
	
	result += rtl::OUString::createFromAscii( "\n" );
	
	return result;
}

::com::sun::star::uno::Reference< ::com::sun::star::security::XCertificate > 
	getCertificateFromEnvironment( ::com::sun::star::uno::Reference< ::com::sun::star::xml::crypto::XSecurityEnvironment >	xSecurityEnvironment , sal_Bool nType)
{
	cssu::Sequence< cssu::Reference< ::com::sun::star::security::XCertificate > > xPersonalCerts ;
	int length = 0;
	int i;

	// add By CP
	sal_uInt16 encoding ;
	rtl_Locale *pLocale = NULL ;
	osl_getProcessLocale( &pLocale ) ;
	encoding = osl_getTextEncodingFromLocale( pLocale ) ;
	// CP end

	if( nType != sal_False )
		xPersonalCerts = xSecurityEnvironment->getPersonalCertificates() ;
	else 
		return NULL; // not support then;

	length = xPersonalCerts.getLength();
	if(length == 0) 
	{
		fprintf( stdout, "\nNo certificate found!\n" ) ;
		return NULL;
	}

	fprintf( stdout, "\nSelect a certificate:\n" ) ;
	for( i = 0; i < length; i ++ ) 
	{
		rtl::OUString xxxIssuer;
		rtl::OUString xxxSubject;
		rtl::OString yyyIssuer;
		rtl::OString yyySubject;

		xxxIssuer=xPersonalCerts[i]->getIssuerName();
		yyyIssuer=rtl::OUStringToOString( xxxIssuer, encoding );

		xxxSubject=xPersonalCerts[i]->getSubjectName();
		yyySubject=rtl::OUStringToOString( xxxSubject, encoding );

		fprintf( stdout, "\n%d:\nsubject=[%s]\nissuer=[%s]\n",
			i+1,
			yyySubject.getStr(),
			yyyIssuer.getStr());
	}
	
	int sel = QuerySelectNumber( 1, length ) -1;
	return xPersonalCerts[sel] ;
}

void QueryPrintSignatureDetails( const SignatureInformations& SignatureInformations, ::com::sun::star::uno::Reference< ::com::sun::star::xml::crypto::XSecurityEnvironment > rSecEnv )
{
	char cShowDetails;
	fprintf( stdout, "\nDisplay details (y/n) [y]?" );
	fflush( stdout );
	fscanf( stdin, "%c", &cShowDetails);
	if ( cShowDetails == 'y' )
	{
		rtl_Locale *pLocale = NULL ;
		osl_getProcessLocale( &pLocale ) ;
		sal_uInt16 encoding = osl_getTextEncodingFromLocale( pLocale ) ;

		fprintf( stdout, "------------- Signature details START -------------\n" );
		fprintf( stdout, "%s", 
			rtl::OUStringToOString(
				getSignatureInformations( SignatureInformations, rSecEnv),
				encoding).getStr());

		fprintf( stdout, "------------- Signature details END -------------\n" );
	}	
}

int QuerySelectNumber( int nMin, int nMax )
{
	fprintf( stdout, "\n" ) ;
	int sel = 0;
	do
	{
		fprintf( stdout, "\nSelect <%d-%d>:", nMin, nMax ) ;
		fflush( stdout );
		fscanf( stdin, "%d", &sel ) ;
	} while( ( sel < nMin ) || ( sel > nMax ) );

	return sel;
}

long QueryVerifySignature()
{
	char answer;
	fprintf( stdout, "\nFound a signature - verify this one (y/n) [y]?" );
	fflush( stdout );
	fscanf( stdin, "%c", &answer);
	return  (answer == 'n')?0:1;
}
