//***************************************************************************
//
//  getparams.cpp
//
//  Module: WMI Instance provider code for boot parameters
//
//  Purpose: Extracting boot parameters.  
//
//  Copyright (c) 1997-1999 Microsoft Corporation
//
//***************************************************************************

#include "bootini.h"

SCODE ParseLine(IWbemClassObject *pNewOSInst,
                PCHAR line,
                PCHAR options
                )
{
    PCHAR rest; // the rest of the options cannot be bigger than this.
    int size = strlen(line);
    int len;
    SCODE sc;
    VARIANT v;
    BOOL found=FALSE;

    rest = (PCHAR) BPAlloc(size);
    if (!rest) {
        return WBEM_E_FAILED;
    }
    PWCHAR wstr;
    wstr = (PWCHAR) BPAlloc(size*sizeof(WCHAR));
    if (!wstr) {
        BPFree(rest);
        return WBEM_E_FAILED;
    }
    
    *options = 0; //Later fill in the '=' 
    len = MultiByteToWideChar(CP_ACP,
                              0,
                              line,
                              strlen(line),
                              wstr,
                              size
                              );
    wstr[len] = (WCHAR) 0;
    v.vt = VT_BSTR;
    v.bstrVal = SysAllocString(wstr);
    sc = pNewOSInst->Put(L"Directory", 0,&v, 0);
    VariantClear(&v);
    PCHAR temp = options + 1;
    *options = '=';
    PCHAR temp1;
    // Rest of the stuff is filled in during initialization
    while(*temp){ // We know line ends with a null
        while(*temp && *temp == ' '){
            temp ++;
        }
        if(*temp == 0) break;
        // Get the new string 
        temp1 = temp;
        if(*temp == '"'){
            // could be the name of the OS
            do {
                temp1++;
            }while(*temp1 && (*temp1 != '"'));
            if(*temp1){
                temp1++;
            }
            else{
                BPFree(rest);
                BPFree(wstr);
                return WBEM_E_FAILED;
            }
            len = MultiByteToWideChar(CP_ACP,
                                      0,
                                      temp,
                                      temp1-temp,
                                      wstr,
                                      size
                                      );
            wstr[len] = (WCHAR) 0;
            v.vt = VT_BSTR;
            v.bstrVal = SysAllocString(wstr);
            sc = pNewOSInst->Put(L"OperatingSystem", 0,&v, 0);
            VariantClear(&v);
            temp = temp1;
            continue;
        }
        do{
            temp1++;
        }while((*temp1) && (*temp1 != ' ') && (*temp1 != '/'));
                 // Now we have the option between temp1 and temp2.
        if(strncmp(temp,"/redirect", strlen("/redirect")) == 0){
            v.vt = VT_BOOL;
            v.boolVal = TRUE;
            sc = pNewOSInst->Put(L"Redirect", 0,&v, 0);
            VariantClear(&v);
            temp = temp1;
            continue;
        }
        if(strncmp(temp,"/debug", strlen("/debug")) == 0){
            // fill in the redirect flag.
            v.vt = VT_BOOL;
            v.boolVal = TRUE;
            sc = pNewOSInst->Put(L"Debug", 0,&v, 0);
            VariantClear(&v);
            temp = temp1;
            continue;
        }

        if(strncmp(temp,"/fastdetect", strlen("/fastdetect")) == 0){
            // fill in the redirect flag.
            v.vt = VT_BOOL;
            v.boolVal = TRUE;
            sc = pNewOSInst->Put(L"Fastdetect", 0,&v, 0);
            VariantClear(&v);
            temp = temp1;
            continue;
        }
        strncat(rest,temp, temp1-temp);
        strcat(rest," ");
        temp = temp1;
    }
    len = MultiByteToWideChar(CP_ACP,
                              0,
                              rest,
                              strlen(rest),
                              wstr,
                              size
                              );
    wstr[len] = (WCHAR) 0;
    v.vt=VT_BSTR;
    v.bstrVal = SysAllocString(wstr);
    sc = pNewOSInst->Put(L"Rest", 0,&v, 0);
    VariantClear(&v);
    BPFree(rest);
    BPFree(wstr);
    return sc;
}


SCODE
ParseBootFile(IWbemClassObject *pClass,
              PCHAR data, 
              PWCHAR *wdef, 
              PCHAR red,
              PLONG pdelay,
              SAFEARRAY **psa
              )
{
    IWbemClassObject FAR* pNewOSInst;
    HRESULT ret;
    int dwRet;
    SCODE sc;
    SAFEARRAYBOUND bound[1];
    long index;
    PCHAR def=NULL;
    PCHAR pChar;
    VARIANT v;
    HRESULT hret;
    CIMTYPE type;
    
    // Ok, start string manipulation.

    // Read each line and glean the required information
    CHAR sep[] = "\r\n";
    PCHAR temp1;

    PCHAR temp = strtok(data,sep);
    int i = 0;
    strcpy(red,"no"); // Put in the default values for these.
    *pdelay = 30;
    while(temp){
        // Ignore spaces
        while(*temp && *temp == ' '){
            temp++;
        }
        if(*temp == ';'){// comment line
            temp = strtok(NULL,sep);
            continue;
        }
        if(strncmp(temp,"[boot loader]",strlen("[boot loader]"))==0){
            do{
                temp1 = strchr(temp,'=');
                if(!temp1){
                    // weird stuff is going on
                    // could be a comment line or some such thing
                    temp = strtok(NULL,sep);
                    continue;
                }
                else{
                    temp1++;
                }
                while(*temp1 && *temp1 == ' ' ){
                    temp1++;
                }
                if(strncmp(temp,"default",strlen("default"))==0){
                    def= temp1;
                    temp = strtok(NULL,sep);
                    continue;
                }
                if(strncmp(temp,"redirect",strlen("redirect"))==0){
                    sscanf(temp1, "%s",red);
                    temp = strtok(NULL,sep);
                    continue;
                }
                if(strncmp(temp,"timeout=",strlen("timeout="))==0){
                    sscanf(temp1, "%d",pdelay);
                }
                temp = strtok(NULL,sep);
            }while(temp && (*temp != '[')); // next section has begun
            continue;
        }
        if(strncmp(temp,"[operating systems]",strlen("[operating systems]")) == 0){
            bound[0].lLbound = 0;
            bound[0].cElements = 0;
            *psa = SafeArrayCreate(VT_UNKNOWN,
                                   1,
                                   bound
                                   );  

            if(*psa == NULL){
                return WBEM_E_FAILED;
            }
            do{

                // Trim leading spaces
                while (*temp == ' '){
                    temp ++;
                }
                // Skip comment lines
                if ( *temp != ';' ){
                    // pChar will point at the directory

                    PCHAR pChar = strchr(temp,'=');

                    // We must have an = sign or this is an invalid string

                    if (pChar){
                        // Punch in a null
                        // Increase the number of elements
                        index = (long) bound[0].cElements;
                        bound[0].cElements += 1;
                        ret = SafeArrayRedim(*psa,
                                             bound
                                             );
                        if(ret != S_OK){
                            SafeArrayDestroy(*psa);
                            return WBEM_E_FAILED;
                        }
                        sc = pClass->SpawnInstance(0,&pNewOSInst);
                        // Start filling in the new instance
                        if(FAILED(sc)){
                            SafeArrayDestroy(*psa);
                            return sc;
                        }
                        sc = ParseLine(pNewOSInst,temp,pChar);
                        if (sc != S_OK) {
                            SafeArrayDestroy(*psa);
                            return sc;
                        }
                        ret = SafeArrayPutElement(*psa,
                                                  &index,
                                                  pNewOSInst
                                                  );
                        if(ret != S_OK){
                            SafeArrayDestroy(*psa);
                            return WBEM_E_FAILED;
                        }
                    }
                }
                temp = strtok(NULL,sep);
            }while(temp && (*temp != '['));
        }
    }

    // Now find out if the default operating system is in one of the
    // Convert the default string to a proper displayable value.
    if(def){
        int size = strlen(def);
        int len;
        *wdef = (PWCHAR) BPAlloc((size+1)*sizeof(WCHAR));
        
        if(*wdef == NULL){
            SafeArrayDestroy(*psa);
            return WBEM_E_FAILED;
        }
        len = MultiByteToWideChar(CP_ACP,
                                  0,
                                  def,
                                  size,
                                  *wdef,
                                  size
                                  );
        (*wdef)[len] = (WCHAR) 0;
        LONG uBound;
        IWbemClassObject *pOSInst;
        hret = SafeArrayGetUBound(*psa,
                                  1,
                                  &uBound
                                  );
        LONG i;
        for(i = 0; i<=uBound; i++){
            hret = SafeArrayGetElement(*psa,
                                       &i,
                                       &pOSInst
                                       );
            if(hret != S_OK){
                pOSInst->Release();
                SafeArrayDestroy(*psa);
                BPFree(*wdef);
                return WBEM_E_FAILED;
            }
            hret = pOSInst->Get(L"Directory",
                                0,
                                &v,
                                &type,
                                NULL
                                );
            if(hret != WBEM_S_NO_ERROR){
                SafeArrayDestroy(*psa);
                pOSInst->Release();
                BPFree(*wdef);
                return -1;
            }
            if(v.vt != VT_BSTR){
                SafeArrayDestroy(*psa);
                pOSInst->Release();
                BPFree(*wdef);
                return -1;
            }
            if(wcscmp(v.bstrVal,*wdef) == 0){
                VariantClear(&v);
                break;
            }
        }
        BPFree(*wdef);
        if(i > uBound){
            SafeArrayDestroy(*psa);
            return WBEM_E_FAILED;
        }
        hret=pOSInst->Get(L"OperatingSystem",
                          0,
                          &v,
                          &type,
                          NULL
                          );
        pOSInst->Release();
        if(hret != WBEM_S_NO_ERROR){
            SafeArrayDestroy(*psa);
            return WBEM_E_FAILED;
        }
        if(v.vt != VT_BSTR){
            SafeArrayDestroy(*psa);
            return WBEM_E_FAILED;
        }
        *wdef = (PWCHAR) BPAlloc(wcslen(v.bstrVal) + sizeof(WCHAR));
        if(*wdef == NULL){
            return -1;
        }
        wcscpy(*wdef,v.bstrVal);
        VariantClear(&v);
    }
    return S_OK;
}

SCODE
GetLoaderParameters(HANDLE BootFile,
                    IWbemClassObject *pNewInst,
                    IWbemClassObject *pClass
                    )
{
    // Read the entire file into memory if you can otherwise forget about it. 
    VARIANT v;
    LONG dwret;
    SCODE sc;
    DWORD dwlen;


    DWORD dwsize = GetFileSize(BootFile,
                               NULL
                               );
    if(dwsize == -1){
        return WBEM_E_FAILED;
    }
    PCHAR data =(PCHAR)  BPAlloc(dwsize + sizeof(CHAR));
    if(!data){
        return WBEM_E_FAILED;
    }
    dwret = ReadFile(BootFile,
                     (LPVOID) data,
                     dwsize,
                     &dwlen,
                     NULL
                     );

    if(dwret == 0){
        BPFree(data);
        return GetLastError();
    }
    
    // Parse the code and return the answers in two arrays, and a safe array
    SAFEARRAY *psa;
    CHAR red[32];
    LONG delay;
    PWCHAR wdef=NULL;
    sc = ParseBootFile(pClass,
                       data, 
                       &wdef, 
                       red,
                       &delay,
                       &psa
                       );
    
    BPFree(data);
    if (sc != S_OK) {
        return sc;
    }

    // fill in the New Instance

    // Fill in the default OS.
    v.vt = VT_BSTR;
    int len;
    v.bstrVal = SysAllocString(wdef);
    sc = pNewInst->Put(L"Default", 0,&v, 0);
    VariantClear(&v);
    BPFree(wdef);
    
    //Fill in the redirect parameter
    WCHAR wred[32];
    len = MultiByteToWideChar(CP_ACP,
                              0,
                              red,
                              strlen(red),
                              wred,
                              32
                              );
    wred[len] = (WCHAR) 0;
    v.vt = VT_BSTR;
    v.bstrVal = SysAllocString(wred);
    sc = pNewInst->Put(L"Redirect", 0, &v, 0);
    VariantClear(&v);

    // Fill in the delay

    v.vt = VT_I4;
    v.lVal = delay;
    sc = pNewInst->Put(L"Delay", 0, &v, 0);
    VariantClear(&v);

    // Fill in the OS in the file
    v.vt = VT_ARRAY|VT_UNKNOWN;
    v.parray = psa;
    sc = pNewInst->Put(L"operating_systems", 0, &v, 0);
    VariantClear(&v);
    return S_OK;
}

//BOOLEAN first=TRUE;

SCODE
GetBootLoaderParameters(IWbemServices * m_pNamespace,
                        IWbemClassObject *pNewInst,
                        IWbemContext *pCtx
                        )
{
    HANDLE BootFile;
    SCODE sc;
    IWbemClassObject *pClass;
    IWbemObjectTextSrc *pSrc;
    BSTR strText;
    HRESULT hr;
/*
    if (first) {
        first = FALSE;
        return WBEM_E_FAILED;
    }
*/
    // Read the file and set in the values.
    if(pNewInst == NULL){
        return WBEM_E_INVALID_PARAMETER;
    }

    // Get a handle to the boot file.
    PCHAR data = GetBootFileName();
    if(!data){
        return WBEM_E_FAILED;
    }
    BootFile = GetFileHandle(data,OPEN_EXISTING,GENERIC_READ);
    BPFree(data);
    if(BootFile == INVALID_HANDLE_VALUE){
        return WBEM_E_FAILED;
    }
    sc = m_pNamespace->GetObject(L"OSParameters", 0, pCtx, &pClass, NULL);
    if (sc != S_OK) {
        return WBEM_E_FAILED;
    }
    sc = GetLoaderParameters(BootFile, pNewInst, pClass);
    CloseHandle(BootFile);
    pClass->Release();
    if (sc != S_OK) {
        return WBEM_E_FAILED;
    }

    pSrc = NULL;
    IWbemClassObject *pInstance;

    if(SUCCEEDED(hr = CoCreateInstance (CLSID_WbemObjectTextSrc, NULL, CLSCTX_INPROC_SERVER,                            
                                        IID_IWbemObjectTextSrc, (void**) &pSrc))) {
        if (pSrc) {
            if(SUCCEEDED(hr = pSrc->GetText(0, pNewInst, WMI_OBJ_TEXT_WMI_DTD_2_0, pCtx, &strText))) {
                if( SUCCEEDED( hr = pSrc->CreateFromText( 0, strText, WMI_OBJ_TEXT_WMI_DTD_2_0, 
                                                            NULL, &pInstance) ) ) {
                    pInstance->Release();
                    sc = 0;
                } else {
                    sc = hr;
                }
                SysFreeString(strText);
            }
            else {
                printf("GetText failed with %x\n", hr);
            }
            pSrc->Release();
        }

    }
    else
        printf("CoCreateInstance on WbemObjectTextSrc failed with %x\n", hr);

    return sc;
}