//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//
//  Copyright (C) Microsoft Corporation, 1999 - 1999
//
//  File:       dumpcert.cxx
//
//--------------------------------------------------------------------------

#include "precomp.hxx"
#include <wincrypt.h>
#include <rpcssl.h>

#if 0

stuff clobbered by Unicode -> ANSI port:

wchar_t -> char
WCHAR   -> char
L""     -> ""
%S      -> %s
W suffix-> A suffix
wcscmp  -> strcmp
wmain   -> main
LPWSTR  -> LPSTR

#endif


#define ByteSwapShort(Value) \
    Value = (  (((Value) & 0x00FF) << 8) \
             | (((Value) & 0xFF00) >> 8))

#define INITIAL_NAME_LENGTH 100

#define     MSSTD_PREFIX_LENGTH 6
const char MSSTD_PREFIX[]    = "msstd:";

#define     FULLPATH_PREFIX_LENGTH 8
const char FULLPATH_PREFIX[] = "fullsic:";

//------------------------------------------------------------------------

void DumpCertInfo( PCERT_INFO Info );
void RecordErrorAndExit( char * Action, unsigned long Error );

extern "C"
{
int __cdecl
main( int argc,
       char * argv[],
       char * envp[]
       );
}



int __cdecl
main(  int argc,
       char * argv[],
       char * envp[]
       )
{
    HCERTSTORE Store;
    PCCERT_CONTEXT Context = 0;
    unsigned Criterion  = CERT_FIND_ANY;
    void *   SearchData = 0;
    char * StoreName = "My";
    int i;
    char * MatchString = 0;
    BOOL fMatch = FALSE;
    BOOL fOutput = FALSE;
    unsigned long OutputFlags = 0;
    DWORD StoreFlags = CERT_SYSTEM_STORE_CURRENT_USER;

    for (i=1; i < argc; ++i)
        {
        if (0 == strcmp(argv[i], "-subject"))
            {
            Criterion = CERT_FIND_SUBJECT_STR_W;
            ++i;
            if (i >= argc)
                {
                printf("-subject must be followed by a subject name or substring\n");
                return 1;
                }

            SearchData = argv[i];
            }
        else if (0 == strcmp(argv[i], "-issuer"))
            {
            Criterion = CERT_FIND_ISSUER_STR_W;
            ++i;
            if (i >= argc)
                {
                printf("-issuer must be followed by an issuer name or substring\n");
                return 1;
                }
            SearchData = argv[i];
            }
        else if (0 == strcmp(argv[i], "-store"))
            {
            ++i;
            if (i >= argc)
                {
                printf("-store must be followed by a store name, such as 'Root' or 'My'\n");
                return 1;
                }
            StoreName = argv[i];
            }
        else if (0 == strcmp(argv[i], "-location"))
            {
            ++i;

            if (i >= argc)
                {
                printf("-location must be followed by 'USER:' or by 'HKLM'\n");
                return 1;
                }

            if (0 == strcmp(argv[i], "USER:"))
                {
                StoreFlags = CERT_SYSTEM_STORE_CURRENT_USER;
                }
            else if (0 == strcmp(argv[i], "HKLM"))
                {
                StoreFlags = CERT_SYSTEM_STORE_LOCAL_MACHINE;
                }
            else
                {
                printf("-location must be followed by 'USER:' or by 'HKLM'\n");
                return 1;
                }
            }
        else if (0 == strcmp(argv[i], "-output"))
            {
            fOutput = TRUE;
            ++i;

            if(i >= argc)
                {
                printf("-output must be followed by 'msstd' or by 'fullsic'\n");
                return 1;
                }

            if (0 == strcmp(argv[i], "fullsic"))
                {
                OutputFlags = RPC_C_FULL_CERT_CHAIN;
                }
            else if (0 == strcmp(argv[i], "msstd"))
                {
                OutputFlags = 0;
                }
            else
                {
                printf("-output must be followed by 'msstd' or by 'fullsic'\n");
                return 1;
                }
            }
        else if (0 == strcmp(argv[i], "-match"))
            {
            fMatch = TRUE;

            ++i;
            if(i >= argc)
                {
                printf("-match must be followed by '@filename', listing the name of the file containing the match string\n");
                return 1;
                }

            if (argv[i][0] == '@')
                {
                DWORD BytesRead;
                HANDLE hMatchFile;

                hMatchFile = CreateFileA( argv[i]+1,
                                          GENERIC_READ,
                                          FILE_SHARE_READ,
                                          0,                 // no security
                                          OPEN_EXISTING,     // must already exist
                                          FILE_ATTRIBUTE_NORMAL,
                                          NULL               // no template file
                                          );

                if (hMatchFile == ULongToHandle(0xffffffff))
                    {
                    printf("can't open file %s\n", argv[i]+1);
                    return 1;
                    }

                DWORD Size = GetFileSize( hMatchFile, 0);
                if (Size == 0xffffffff)
                    {
                    printf("GetFileSize failed with 0x%x\n", GetLastError());
                    return 1;
                    }

                if (Size % 2)
                    {
                    printf("the match-string file must be in Unicode.\n");
                    return 1;
                    }

                MatchString = new char[ Size/sizeof(char) + sizeof(char) ];
                if (!MatchString)
                    {
                    printf("can't allocate memory\n");
                    return 1;
                    }

                if (!ReadFile( hMatchFile,
                               MatchString,
                               Size,
                               &BytesRead,
                               0            // not overlapped
                               )
                    || BytesRead != Size)
                    {
                    printf("can't read file data: 0x%x\n", GetLastError());
                    return 1;
                    }

                MatchString[ Size/sizeof(char) ] = 0;

                if (MatchString[0] == 0xfeff)
                    {
                    ++MatchString;
                    }
                else if (MatchString[0] == 0xfffe)
                    {
                    char * pc;

                    for (pc = MatchString; *pc; ++pc)
                        {
                        ByteSwapShort(*pc);
                        }

                    ++MatchString;
                    }
                }
            else
                {
                MatchString = argv[i];
                }

            printf("string to match is '%s'\n", MatchString);
            }
        else if (0 == strcmp(argv[i], "-?") ||
                 0 == strcmp(argv[i], "-help"))
            {
            printf("usage:\n"
                   "    dumpcert \n"
                   "        [-subject subject-substring]\n"
                   "        [-issuer issuer-substring]\n"
                   "        [-store store-name]\n"
                   "        [-output ('fullsic' | 'msstd')\n"
                   "        [-location ('HKLM' | 'USER:')\n"
                   "        [-match @filename]\n"
                   );
            return 1;
            }
        else
            {
            printf("unknown option '%s'\n", argv[i]);
            return 1;
            }
        }

    Store = CertOpenStore( CERT_STORE_PROV_SYSTEM_A,
                           0,
                           0,
                           StoreFlags,
                           StoreName
                           );

    if (!Store)
        {
        RecordErrorAndExit("opening the store", GetLastError());
        }

    for(;;)
        {
        Context = CertFindCertificateInStore( Store,
                                              X509_ASN_ENCODING,
                                              0,
                                              Criterion,
                                              SearchData,
                                              Context
                                              );
        if (!Context)
            {
            break;
            }

        DumpCertInfo( Context->pCertInfo );
        if (fOutput)
            {
            unsigned char * OutputBuffer = 0;
            DWORD Status = RpcCertGeneratePrincipalNameA( Context, OutputFlags, &OutputBuffer );
            if (Status)
                {
                printf("GeneratePrincName returned %d = 0x%x\n", Status, Status);
                }
            else
                {
                printf("    generated name = '%s'\n", OutputBuffer);
                }
            }

        if (fMatch)
            {
            printf("matching is not implemented because RpcCertMatchPrincipalName is not exported\n");
#if 0
            DWORD Status = RpcCertMatchPrincipalName( Context, MatchString );
            if (Status)
                {
                printf("MatchPrincipalName returned %d = 0x%x\n", Status, Status);
                }
            else
                {
                printf("The names matched.\n");
                }
#endif
            }
        }

    if (GetLastError() != CRYPT_E_NOT_FOUND)
        {
        RecordErrorAndExit("getting certificates", GetLastError());
        }

    return 0;
}

void DumpCertInfo( PCERT_INFO Info )
{
    SYSTEMTIME NotBeforeTime;
    SYSTEMTIME NotAfterTime;
    char      SubjectName[1000];
    char      IssuerName[1000];

    if (!FileTimeToSystemTime( &Info->NotBefore, &NotBeforeTime ))
        {
        RecordErrorAndExit("translating not-before time", GetLastError());
        }

    if (!FileTimeToSystemTime( &Info->NotAfter, &NotAfterTime ))
        {
        RecordErrorAndExit("translating not-after time",  GetLastError());
        }

    if (!CertNameToStrA( X509_ASN_ENCODING,
                        &Info->Subject,
                        CERT_X500_NAME_STR | CERT_NAME_STR_SEMICOLON_FLAG,
                        SubjectName,
                        sizeof(SubjectName)
                        ))
        {
        RecordErrorAndExit("unpacking subject name", GetLastError());
        }

    if (!CertNameToStrA( X509_ASN_ENCODING,
                        &Info->Issuer,
                        CERT_X500_NAME_STR | CERT_NAME_STR_SEMICOLON_FLAG,
                        IssuerName,
                        sizeof(IssuerName)
                        ))
        {
        RecordErrorAndExit("unpacking issuer name", GetLastError());
        }

    printf("----------------------------------------------------\n\n");

    printf("    subject: %s\n", SubjectName);
    printf("    issuer:  %s\n", IssuerName);
    printf("    version %d\n", Info->dwVersion );
    printf("    valid from %02d:%02d:%02d on %d-%02d-%d\n",
           NotBeforeTime.wHour,
           NotBeforeTime.wMinute,
           NotBeforeTime.wSecond,
           NotBeforeTime.wMonth,
           NotBeforeTime.wDay,
           NotBeforeTime.wYear
           );

    printf("          to   %02d:%02d:%02d on %d-%02d-%d\n",
           NotAfterTime.wHour,
           NotAfterTime.wMinute,
           NotAfterTime.wSecond,
           NotAfterTime.wMonth,
           NotAfterTime.wDay,
           NotAfterTime.wYear
           );
}


void RecordErrorAndExit( char * Action, unsigned long Error )
{
    char * lpMsgBuf = "";

    FormatMessageA( FORMAT_MESSAGE_ALLOCATE_BUFFER |
                   FORMAT_MESSAGE_FROM_SYSTEM |
                   FORMAT_MESSAGE_IGNORE_INSERTS,
                   NULL,
                   Error,
                   MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
                   (LPSTR) &lpMsgBuf,
                   0,
                   NULL
                   );

    printf("error while %s: %x (%d) \"%s\"", Action, Error, Error, lpMsgBuf);
}