/*=============================================================================

FILE: OEMProvision.c

SERVICES: OEM-Specific Provisioning Interfaces

GENERAL DESCRIPTION:
  
PUBLIC CLASSES AND STATIC FUNCTIONS:

        Copyright ?1999-2002 QUALCOMM Incorporated.
               All Rights Reserved.
            QUALCOMM Proprietary/GTDR
=============================================================================*/
#include <stdlib.h>
#include "OEMFeatures.h"
#if defined(OEMPROVISION)

#if !defined(AEE_SIMULATOR)
#include <string.h>
#include "cmph.h"
#include "bit.h"
#include "mcsys.h"
#include "prl.h"
#include "mccdma.h"
#include "mcsyspr.h"    
#include "AEEComdef.h"
#endif

#include "OEMNotify.h"
#include "OEMClassIDs.h"
#include "AEEStdLib.h"
#include "AEEShell.h"
#include "AEEFile.h"
#include "AEEDownload.h"
#include "AEEProvision.h"
#include "OEMConfig.h"
#include "OEMProvisionItems.h"
#include "AEEBridle.h"

#ifdef AEE_SIMULATOR
#include "OEMSimNV.h"
#endif //AEE_SIMULATOR

OBJECT(COEMProv)
{
   DECLARE_VTBL(IProvisionItem)

   IConfig        *        m_pConfig;
   int32                   m_nRefs;
   AEECLSID                m_cls;
   IShell *                m_pShell;
   nv_cdmach_type   *      m_pPrimary;
   int                     m_nPrimary;
   nv_cdmach_type *        m_pSecondary;
   int                     m_nSecondary;
   nv_home_sid_nid_type *  m_psn;
   int                     m_nsn;
};

#define PROV_BUFF_SIZE      0x1000

#define FARF_LOGO       1
#define FARF_BOTAP      1


#ifdef FARF
#undef FARF
#endif /* #ifdef FARF */

#define FARF(x, p)  if (1 == FARF_##x) DBGPRINTF p 

static uint32  Prov_AddRef(IProvisionItem *po);
static uint32  Prov_Release(IProvisionItem * po);
static int     Prov_Filter(IProvisionItem * po, const char * pszFilterData);
static int     Prov_Get(IProvisionItem * po, IAStream ** ppStream);
static int     Prov_Set(IProvisionItem * po,const char * pszFile, IAStream * ps,boolean * pbNeedReset);

static void GetAreaCode(IConfig *po, char * pszDest);

static int     COEMProv_SetBanner(COEMProv * pme, int nNAM,const char * pszTag, const char * pszVal);
static int     COEMProv_SetNAMName(COEMProv * pme, int nNAM, const char * pszTag, const char * pszVal);
static int     COEMProv_SetAMPSPaging(COEMProv * pme, int nNAM, const char * pszTag, const char * pszVal);
static int     COEMProv_SetAMPSReg(COEMProv * pme, int nNAM, const char * pszTag, const char * pszVal);
static int     COEMProv_SetAMPSSID(COEMProv * pme, int nNAM, const char * pszTag, const char * pszVal);
static int     COEMProv_SetEVRC(COEMProv * pme, int nNAM, const char * pszTag, const char * pszVal);
static int     COEMProv_SetCDMAPrimary(COEMProv * pme, int nNAM, const char * pszTag, const char * pszVal);
static int     COEMProv_SetCDMASecondary(COEMProv * pme, int nNAM, const char * pszTag, const char * pszVal);
static int     COEMProv_SetPrefSys(COEMProv * pme, int nNAM, const char * pszTag, const char * pszVal);
static int     COEMProv_SetPrefMode(COEMProv * pme, int nNAM, const char * pszTag, const char * pszVal);
static int     COEMProv_SetSIDNID(COEMProv * pme, int nNAM, const char * pszTag, const char * pszVal);

static byte *  COEMProv_GetNAM(COEMProv * pme);
static int     COEMProv_SetNAM(COEMProv * pme, IAStream * ps, boolean * pbNeedReset);
static int     COEMProv_SetCLOGO(COEMProv * pme,const char * pszFile, IAStream * ps);

static int              ReadItem(IConfig * po,int nNAM, AEEConfigItem nItem, byte * pDest, int nSize);
static nv_cdmach_type * FindChannel(IConfig * po,int nNAM, AEEConfigItem nItem, nv_cdmach_type ** ppList, int * pnCount);

static const VTBL(IProvisionItem) gPRLProvisionFuncs = { Prov_AddRef,
                                                         Prov_Release,
                                                         Prov_Filter,
                                                         Prov_Get,
                                                         Prov_Set};

typedef int (*PFNOEMTAG)(COEMProv * pme, int nNAM,const char * pszTag,const char * pszVal);

typedef struct _OEMTag
{
   const char *   pszTag;
   PFNOEMTAG      pfn;
} OEMTag;

typedef struct _OEMLookup
{
   const char *   psz;
   word           t;
} OEMLookup;

static const OEMLookup gRegLookup[] = {{"Yes"      ,NV_WHEREABOUTS_KNOWN},
                                       {"Disable"  ,NV_DISABLED},
                                       {"Hide"     ,NV_WHEREABOUTS_UNKNOWN},
                                       {NULL       ,0}};   

static const OEMLookup gSysLookup[] = {{"A"        ,NV_SP_A_ONLY},
                                       {"B"        ,NV_SP_B_ONLY},
                                       {"Home"     ,NV_SP_HOME_ONLY},
                                       {"Standard" ,NV_SP_STANDARD},
                                       {"AutoA"    ,NV_SP_AUTO_A},
                                       {"AutoB"    ,NV_SP_AUTO_B},
                                       {NULL,0}};

static const OEMLookup gModeLookup[] ={{"DigitalPref" ,NV_MODE_DIGITAL_PREF},
                                       {"Digital"     ,NV_MODE_DIGITAL_ONLY},
                                       {"AnalogPref"  ,NV_MODE_ANALOG_PREF},
                                       {"Analog"      ,NV_MODE_ANALOG_ONLY},
                                       {"Auto"        ,NV_MODE_AUTOMATIC},
                                       {"E911"        ,NV_MODE_E911},
                                       {"Home"        ,NV_MODE_HOME_ONLY},
                                       {"CELL"        ,NV_MODE_CELL_CDMA_ONLY},
                                       {"PCS"         ,NV_MODE_PCS_CDMA_ONLY},
                                       {NULL,0}};


#define BANNER_STR         "Banner"
#define NAMENAM_STR        "NAMName"
#define AMPSPAGING_STR     "AMPSPaging"
#define AMPSREG_STR        "AMPSReg"
#define AMPSSID_STR        "AMPSSID"
#define EVRC_STR           "EVRC"
#define CDMAPRIMARYA_STR   "CDMAPrimaryA"
#define CDMAPRIMARYB_STR   "CDMAPrimaryB"
#define CDMASECONDARYA_STR "CDMASecondaryA"
#define CDMASECONDARYB_STR "CDMASecondaryB"
#define PREFSYS_STR        "PrefSys"
#define PREFMODE_STR       "PrefMode"
#define SIDNID_STR         "SIDNID"

static const OEMTag     gTags[] = { {BANNER_STR          ,COEMProv_SetBanner},
                                    {NAMENAM_STR         ,COEMProv_SetNAMName},
                                    {AMPSPAGING_STR      ,COEMProv_SetAMPSPaging},
                                    {AMPSREG_STR         ,COEMProv_SetAMPSReg},
                                    {AMPSSID_STR         ,COEMProv_SetAMPSSID},
                                    {EVRC_STR            ,COEMProv_SetEVRC},
                                    {CDMAPRIMARYA_STR    ,COEMProv_SetCDMAPrimary},
                                    {CDMAPRIMARYB_STR    ,COEMProv_SetCDMAPrimary},
                                    {CDMASECONDARYA_STR  ,COEMProv_SetCDMASecondary},
                                    {CDMASECONDARYB_STR  ,COEMProv_SetCDMASecondary},
                                    {PREFSYS_STR         ,COEMProv_SetPrefSys},
                                    {PREFMODE_STR        ,COEMProv_SetPrefMode},
                                    {SIDNID_STR"1"       ,COEMProv_SetSIDNID},
                                    {SIDNID_STR"2"       ,COEMProv_SetSIDNID},
                                    {SIDNID_STR"3"       ,COEMProv_SetSIDNID},
                                    {SIDNID_STR"4"       ,COEMProv_SetSIDNID},
                                    {SIDNID_STR"5"       ,COEMProv_SetSIDNID},
                                    {SIDNID_STR"6"       ,COEMProv_SetSIDNID},
                                    {SIDNID_STR"7"       ,COEMProv_SetSIDNID},
                                    {SIDNID_STR"8"       ,COEMProv_SetSIDNID},
                                    {SIDNID_STR"9"       ,COEMProv_SetSIDNID},
                                    {SIDNID_STR"10"      ,COEMProv_SetSIDNID},
                                    {SIDNID_STR"11"      ,COEMProv_SetSIDNID},
                                    {SIDNID_STR"12"      ,COEMProv_SetSIDNID},
                                    {SIDNID_STR"13"      ,COEMProv_SetSIDNID},
                                    {SIDNID_STR"14"      ,COEMProv_SetSIDNID},
                                    {SIDNID_STR"15"      ,COEMProv_SetSIDNID},
                                    {SIDNID_STR"16"      ,COEMProv_SetSIDNID},
                                    {SIDNID_STR"17"      ,COEMProv_SetSIDNID},
                                    {SIDNID_STR"18"      ,COEMProv_SetSIDNID},
                                    {SIDNID_STR"19"      ,COEMProv_SetSIDNID},
                                    {SIDNID_STR"20"      ,COEMProv_SetSIDNID},
                                    {NULL,NULL}};

static char const       gMINNums[10] = { '1', '2', '3', '4', '5', '6', '7', '8', '9', '0' };

/*===========================================================================

===========================================================================*/
void OEMProvision_Init(IShell * ps)
{
   ISHELL_RegisterHandler(ps, AEECLSID_PROVISIONITEM, "application/prl",   AEECLSID_PROVISION_PRL);
   ISHELL_RegisterHandler(ps, AEECLSID_PROVISIONITEM, "application/nam",   AEECLSID_PROVISION_NAM);
   ISHELL_RegisterHandler(ps, AEECLSID_PROVISIONITEM, "application/clogo", AEECLSID_PROVISION_CLOGO);
}

/*===========================================================================

=======================================================================*/
int OEMProvision_New(IShell * pIShell,AEECLSID ClsId,void ** ppObj)
{
   COEMProv *  pme;
   int         nErr;

   switch(ClsId){
      case AEECLSID_PROVISION_CLOGO:
      case AEECLSID_PROVISION_PRL:
      case AEECLSID_PROVISION_NAM:
         pme = (COEMProv *)MALLOC(sizeof(COEMProv));
         if(!pme)
            return(ENOMEMORY);

         INIT_VTBL(pme, IProvisionItem, gPRLProvisionFuncs);
         pme->m_nRefs   = 1;
      
         if(ClsId != AEECLSID_PROVISION_CLOGO){
            nErr = ISHELL_CreateInstance(pIShell, AEECLSID_CONFIG, (void **)(&pme->m_pConfig));
            if(nErr){
               FREE(pme);
               return(nErr);
            }
         }

         pme->m_cls     = ClsId;
         pme->m_pShell  = pIShell;
         *ppObj         = pme;
         return(0);
   }
   *ppObj = NULL;
   return(ECLASSNOTSUPPORT);
}

/*===========================================================================

===========================================================================*/
static uint32 Prov_AddRef(IProvisionItem *po)
{
   COEMProv * pme = (COEMProv *)po;

   return(++pme->m_nRefs);
}

/*===========================================================================

===========================================================================*/
static uint32 Prov_Release(IProvisionItem * po)
{
   COEMProv * pme = (COEMProv *)po;

   if(pme->m_nRefs){
      if(--pme->m_nRefs)
         return(pme->m_nRefs);
      if(pme->m_pConfig)
         ICONFIG_Release(pme->m_pConfig);
      FREE(pme);
   }
   return(0);
}


/*===========================================================================

===========================================================================*/
static int Prov_Filter(IProvisionItem * po, const char * pszFilterData)
{
   COEMProv *     pme = (COEMProv *)po;
   AEEMobileInfo  mi;
   int            nMatchLen,i;
   char           *psz,*pszNext;

   if(!pszFilterData)
      return(0);

   if(pme->m_cls == AEECLSID_PROVISION_CLOGO)
      return(0);

   GetAreaCode(pme->m_pConfig,mi.szMobileID);
   
   psz = (char *)pszFilterData;

   // Look for a matching area code in the list...

   nMatchLen = STRLEN(mi.szMobileID);

   while(psz){
      pszNext = STRCHR(psz,',');
      if(pszNext)
         i = (int)(pszNext - psz);
      else
         i = STRLEN(psz);
      if(i == nMatchLen && !STRNCMP(psz,mi.szMobileID,i)){
         FARF(BOTAP,("OP Match %s",psz));
         return(0);
      }
      psz = pszNext;
      if(psz)
         psz++;
   }

   FARF(BOTAP,("OP Bad (%s)",mi.szMobileID));

   return(EBADPARM);
}

/*===========================================================================

===========================================================================*/
static void FreeBuff(void * p)
{
   FREE(p);
}

/*===========================================================================

===========================================================================*/
static int Prov_Get(IProvisionItem * po, IAStream ** ppStream)
{
   COEMProv *     pme = (COEMProv *)po;
   byte     *     pBuff;
   int            nSize;
   int            nErr = 0;
   IMemAStream *  ps;

   FARF(BOTAP,("OP Prov_Get"));

   if(!ppStream)
      return(EBADPARM);

   *ppStream = NULL;

   switch(pme->m_cls){

      case AEECLSID_PROVISION_NAM:
         pBuff = COEMProv_GetNAM(pme);
         if(!pBuff)
            return(ENOMEMORY);
         nSize = STRLEN((char *)pBuff);
         break;

      case AEECLSID_PROVISION_PRL:
         pBuff = (byte *)MALLOC(sizeof(nv_ram_roaming_list_type));
         if(!pBuff)
            return(ENOMEMORY);
         nSize = sizeof(nv_ram_roaming_list_type);
         nErr = ReadItem(pme->m_pConfig,0,CFGI_ROAMING_LIST,(byte *)pBuff,nSize);
         break;

   // Restoration of the old logo is just blowing it away...

      case AEECLSID_PROVISION_CLOGO:
         return(0);

      default:
         return(EBADPARM);
   }

   if(!nErr){
      nErr = ISHELL_CreateInstance(pme->m_pShell,AEECLSID_MEMASTREAM,(void **)(&ps));
      if(ps){
         IMEMASTREAM_SetEx(ps,pBuff,nSize,0,FreeBuff,(void *)pBuff);
         *ppStream = (IAStream *)ps;
      }
   }

   if(nErr)
      FREE(pBuff);

   return(nErr);
}            

   
/*===========================================================================

===========================================================================*/
static int Prov_Set(IProvisionItem * po, const char * pszFile, IAStream * ps, boolean * pbNeedReset)
{
   COEMProv *                 pme = (COEMProv *)po;
   int                        nErr = 0;
   nv_ram_roaming_list_type * prl;
   int                        nBytes,nSize;
   byte                       b;
   byte *                     pDest;

   FARF(BOTAP,("OP Prov_Set"));

   if(!ps)
      return(EBADPARM);

   if(pbNeedReset)
      *pbNeedReset = FALSE;

   if(pme->m_cls == AEECLSID_PROVISION_CLOGO)
      return(COEMProv_SetCLOGO(pme,pszFile,ps));

   if(pme->m_cls == AEECLSID_PROVISION_NAM)
      return(COEMProv_SetNAM(pme,ps,pbNeedReset));

   prl = (nv_ram_roaming_list_type *)MALLOC(sizeof(nv_ram_roaming_list_type));
   if(!prl)
      return(ENOMEMORY);

   pDest = (byte *)prl;
   nSize = sizeof(nv_ram_roaming_list_type);

   FARF(BOTAP,("OP Reading PRL..."));

   while((nBytes = IASTREAM_Read(ps,pDest,nSize)) > 0){
      nSize -= nBytes;
      pDest += nBytes;

   // See if the PRL is too large!

      if(!nSize && IASTREAM_Read(ps,&b,1)){
         nErr = EBADPARM;
         break;
      }
   }

   // Open and read the file into a buffer...

   if(!nErr){

   // See if this looks like a valid roaming list...

      FARF(BOTAP,("OP Validating PRL"));

      if(mcsys_validate_roaming_list((byte *)prl->roaming_list) == PRL_VALID){
         prl->prl_version  = mcsys_get_prl_version();
         prl->nam          = cdma.curr_nam;     
         prl->valid        = TRUE;           // At this point, PRL is already validated

   // Save the roaming list...

         FARF(BOTAP,("OP Valid"));

         if(!ICONFIG_SetItem(pme->m_pConfig,CFGI_ROAMING_LIST,(void *)prl,sizeof(nv_ram_roaming_list_type))){
            FARF(BOTAP,("OP Telling MCSYS"));
            mcsyspr_read_roaming_list(mcsyspr_current_nam);
            if(pbNeedReset)
               *pbNeedReset = TRUE;
         }
         else{
            FARF(BOTAP,("OP Cannot Store"));
            nErr = ENOPERSISTMEMORY;
         }
      }
      else{
         FARF(BOTAP,("OP PRL Invalid"));
         nErr = EBADPARM;
      }
   }
   FARF(BOTAP,("OP Err = %d",nErr));
   FREE((void *)prl);
   return(nErr);
}

/*===========================================================================

===========================================================================*/
static void GetAreaCode(IConfig *po, char * pszDest)
{
#if !defined(AEE_SIMULATOR)
   nv_item_type * pnv;
   uint16         wMin;

   pnv = (nv_item_type *)MALLOC(sizeof(nv_item_type));
   if(!pnv)
      return;

   // Get the Area Code and fill the next 3 digites...

   ReadItem(po, 0,CFGI_MIN2,(byte *)pnv,sizeof(nv_item_type));
   wMin = pnv->min2.min2[1];
   if(!wMin)
      return;

   pszDest[0] = gMINNums[wMin / 100];
   wMin %= 100;
   pszDest[1] = gMINNums[wMin / 10];
   pszDest[2] = gMINNums[wMin % 10];
   pszDest[3] = 0;

   FREE((void *)pnv);

#else
   STRCPY(pszDest,"858");
#endif
}


/*===========================================================================



         NAM Provisioning 



===========================================================================*/

static int Lookup(const OEMLookup * plu, const char * psz)
{
   for(; plu->psz; plu++){
      if(!STRICMP(plu->psz,psz))
         return(plu->t + 1);
   }

   FARF(BOTAP,("WARNING %s == 0", psz));
   return(0);
}

static const char * Unlookup(const OEMLookup * plu, int t)
{
   for(; plu->psz; plu++){
      if(t == plu->t)
         return(plu->psz);
   }
   FARF(BOTAP,("WARNING %d == NULL",t));
   return(NULL);
}

/*===========================================================================

===========================================================================*/
static int COEMProv_SetBanner(COEMProv * pme, int nNAM, const char * pszTag, const char * pszVal)
{
   int            i;
   nv_banner_type nv;

   MEMSET((void *)&nv,0,sizeof(nv));
   
   FARF(BOTAP,("OP Banner (%s)",pszVal));

   if(pszVal){
      i = STRLEN(pszVal);
      if(i > sizeof(nv.letters))
         i = sizeof(nv.letters);
      MEMCPY((void *)nv.letters,pszVal,i);
      return(ICONFIG_SetItem(pme->m_pConfig,CFGI_BANNER, (void *)&nv,sizeof(nv)));
   }
   return(EFAILED);
}

/*===========================================================================

===========================================================================*/
static int COEMProv_SetNAMName(COEMProv * pme, int nNAM, const char * pszTag, const char * pszVal)
{
   int               i;
   nv_name_nam_type  nv;

   FARF(BOTAP,("OP NAM Name (%s)",pszVal));

   MEMSET((void *)&nv,0,sizeof(nv));
   
   if(pszVal){
      i = STRLEN(pszVal);
      if(i > sizeof(nv.name))
         i = sizeof(nv.name);
      MEMSET((void *)nv.name,' ',sizeof(nv.name));
      MEMCPY((void *)nv.name,pszVal,i);
      nv.nam = (byte)nNAM;
      return(ICONFIG_SetItem(pme->m_pConfig,CFGI_NAME_NAM, (void *) &nv,sizeof(nv)));
   }
   return(EFAILED);
}


/*===========================================================================

===========================================================================*/
static int COEMProv_SetAMPSPaging(COEMProv * pme, int nNAM, const char * pszTag, const char * pszVal)
{
   nv_firstchp_type nv;
   
   FARF(BOTAP,("OP AMPSPaging (%s)",pszVal));

   nv.nam      = (byte)nNAM;
   nv.channel  = (word)STRTOUL(pszVal,NULL,10);
   return(ICONFIG_SetItem(pme->m_pConfig,CFGI_ANALOG_FIRSTCHP,(void *)&nv,sizeof(nv)));
}

/*===========================================================================

===========================================================================*/
static int COEMProv_SetAMPSReg(COEMProv * pme, int nNAM, const char * pszTag, const char * pszVal)
{
   nv_analog_reg_type nv;

   FARF(BOTAP,("OP AMPSReg (%s)",pszVal));

   nv.nam      = (byte)nNAM;
   nv.kind     = Lookup(gRegLookup,pszVal);
   if(!nv.kind)
      return(EBADPARM);
   nv.kind--;
   return(ICONFIG_SetItem(pme->m_pConfig,CFGI_ANALOG_REG,(void *)&nv,sizeof(nv)));
}

/*===========================================================================

===========================================================================*/
static int COEMProv_SetAMPSSID(COEMProv * pme, int nNAM, const char * pszTag, const char * pszVal)
{
   nv_sid_type nv;

   FARF(BOTAP,("OP AMPSSID (%s)",pszVal));

   nv.nam = (byte)nNAM;
   nv.sid = (word)STRTOUL(pszVal,NULL,10);
   return(ICONFIG_SetItem(pme->m_pConfig,CFGI_ANALOG_HOME_SID,(void *)&nv,sizeof(nv)));
}

/*===========================================================================

===========================================================================*/
static int COEMProv_SetEVRC(COEMProv * pme, int nNAM, const char * pszTag, const char * pszVal)
{
   boolean  bOn = (boolean)(pszVal && !STRICMP(pszVal,"ON"));

   return(ICONFIG_SetItem(pme->m_pConfig,CFGI_EVRC,(void *)&bOn,sizeof(bOn)));
}

/*===========================================================================

===========================================================================*/
static int COEMProv_SetCDMAPrimary(COEMProv * pme, int nNAM, const char * pszTag, const char * pszVal)
{
   nv_cdmach_type *  pch;

   FARF(BOTAP,("OP CDMAPrimary (%s)",pszVal));

   pch = FindChannel(pme->m_pConfig,nNAM,CFGI_PCDMACH,&pme->m_pPrimary,&pme->m_nPrimary);
   if(!pch)
      return(ENOMEMORY);

   if(pszTag[STRLEN(pszTag) - 1] == 'A')
      pch->channel_a = (word)STRTOUL(pszVal,NULL,10);
   else
      pch->channel_b = (word)STRTOUL(pszVal,NULL,10);
   return(0);
}

/*===========================================================================

===========================================================================*/
static int COEMProv_SetCDMASecondary(COEMProv * pme, int nNAM, const char * pszTag, const char * pszVal)
{
   nv_cdmach_type *  pch;

   FARF(BOTAP,("OP CDMASecondary (%s)",pszVal));

   pch = FindChannel(pme->m_pConfig,nNAM,CFGI_SCDMACH,&pme->m_pSecondary,&pme->m_nSecondary);
   if(!pch)
      return(ENOMEMORY);

   if(pszTag[STRLEN(pszTag) - 1] == 'A')
      pch->channel_a = (word)STRTOUL(pszVal,NULL,10);
   else
      pch->channel_b = (word)STRTOUL(pszVal,NULL,10);
   return(0);
}


/*===========================================================================

===========================================================================*/
static int COEMProv_SetPrefSys(COEMProv * pme, int nNAM, const char * pszTag, const char * pszVal)
{
   nv_sys_pref_type  nv;

   FARF(BOTAP,("OP PrefSys (%s)",pszVal));

   nv.nam   = nNAM;
   nv.sys   = Lookup(gSysLookup,pszVal);
   if(!nv.sys)
      return(EBADPARM);
   nv.sys--;
   return(ICONFIG_SetItem(pme->m_pConfig,CFGI_SYSTEM_PREF,(void *)&nv,sizeof(nv)));
}

/*===========================================================================

===========================================================================*/
static int COEMProv_SetPrefMode(COEMProv * pme, int nNAM, const char * pszTag, const char * pszVal)
{ 
   nv_pref_mode_type  nv;

   FARF(BOTAP,("OP PrefMode (%s)",pszVal));

   nv.nam = nNAM;
   nv.mode = Lookup(gModeLookup,pszVal);
   if(!nv.mode)
      return(EBADPARM);
   nv.mode--;
   return(ICONFIG_SetItem(pme->m_pConfig,CFGI_PREF_MODE,(void *)&nv,sizeof(nv)));
}

/*===========================================================================

===========================================================================*/
static int COEMProv_SetSIDNID(COEMProv * pme, int nNAM, const char * pszTag, const char * pszVal)
{
   char *                  pszNum,ch;
   char *                  pszNID;
   int                     nIndex,i,nErr;
   nv_home_sid_nid_type *  psn;

   FARF(BOTAP,("OP SIDNID (%s)",pszVal));

   pszNum = (char *)pszTag + STRLEN(pszTag) - 1;
   while(pszNum > pszTag){
      ch = *pszNum;
      if(ch < '0' || ch > '9'){
         pszNum++;
         break;
      }
      pszNum--;
   }
   nIndex = STRTOUL(pszNum,NULL,10);

   if(nIndex < 1 || nIndex > NV_MAX_HOME_SID_NID)
      return(EBADPARM);

   nIndex--;

   pszNID = STRCHR(pszVal,'/');
   if(pszNID){
      *pszNID = 0;
      for(psn = pme->m_psn,i = 0; i < pme->m_nsn; i++,psn++){
         if(psn->nam == (byte)nNAM)
            break;
      }
      if(i == pme->m_nsn){
         psn = (nv_home_sid_nid_type *)REALLOC((void *)pme->m_psn,sizeof(nv_home_sid_nid_type) * (pme->m_nsn + 1));
         if(!psn)
            return(ENOMEMORY);
         pme->m_psn = psn;
         psn = &psn[pme->m_nsn];
         pme->m_nsn++;
         psn->nam = nNAM;
         
   // Read the current value into the buffer...

         nErr = ReadItem(pme->m_pConfig,nNAM,CFGI_HOME_SID_NID,(byte *)psn,sizeof(nv_home_sid_nid_type));
         if(nErr){
            pme->m_nsn--;
            return(nErr);
         }
      }
      psn->pair[nIndex].sid = (word)STRTOUL(pszVal,NULL,10);
      psn->pair[nIndex].nid = (word)STRTOUL(pszNID + 1,NULL,10);
      return(0);
   }
   return(EBADPARM);
}

/*===========================================================================

===========================================================================*/
static char * AppendString(char * pszIn, char * pszLine)
{
   int      i;
   char *   pszNew;

   if(pszIn)
      i = STRLEN(pszIn);
   else
      i = 0;
   i += STRLEN(pszLine) + 2;
   pszNew = (char *)REALLOC((void *)pszIn,i);
   if(pszNew){
      STRCAT(pszNew,pszLine);
      STRCAT(pszNew,"\n");
      pszIn = pszNew;
   }
   return(pszIn);
}

/*===========================================================================

===========================================================================*/
static byte * COEMProv_GetNAM(COEMProv * pme)
{
   int            i,n;
   char *         pBuff = NULL;
   char           szTemp[32];
   char *         pszLine;
   nv_item_type * pnv;

   pnv = (nv_item_type *)MALLOC(sizeof(nv_item_type));
   if(!pnv)
      return(NULL);

   pszLine = (char *)MALLOC(256);
   if(!pszLine){
      FREE((void *)pnv);
      return(NULL);
   }

   if(!ReadItem(pme->m_pConfig,0,CFGI_BANNER,(byte *)pnv,sizeof(nv_banner_type))){
      MEMSET(szTemp,0,sizeof(szTemp));
      MEMCPY(szTemp,(void *)((nv_banner_type *)pnv)->letters,sizeof(((nv_banner_type *)pnv)->letters));
      SPRINTF(pszLine,BANNER_STR"=%s",szTemp);
      pBuff = AppendString(pBuff,pszLine);
   }
   
   for(i = 0; i < NV_MAX_NAMS; i++){
      SPRINTF(pszLine,"#\n# NAM %d\n#",i);
      pBuff = AppendString(pBuff,pszLine);

   // Read the NAM Name

      if(!ReadItem(pme->m_pConfig,i,CFGI_NAME_NAM,(byte *)pnv,sizeof(nv_name_nam_type))){
         MEMSET(szTemp,0,sizeof(szTemp));
         MEMCPY(szTemp,(void *)((nv_name_nam_type *)pnv)->name,sizeof(((nv_name_nam_type *)pnv)->name));
         SPRINTF(pszLine,NAMENAM_STR":%d=%s",i,szTemp);
         pBuff = AppendString(pBuff,pszLine);
      }

   // Read the AMPS Paging setting

      if(!ReadItem(pme->m_pConfig,i,CFGI_ANALOG_FIRSTCHP,(byte *)pnv,sizeof(nv_firstchp_type))){
         SPRINTF(pszLine,AMPSPAGING_STR":%d=%lu",i,((nv_firstchp_type *)pnv)->channel);
         pBuff = AppendString(pBuff,pszLine);
      }

   // Read the AMPS Reg setting...
   
      if(!ReadItem(pme->m_pConfig,i,CFGI_ANALOG_REG,(byte *)pnv,sizeof(nv_analog_reg_type))){
         SPRINTF(pszLine,AMPSREG_STR":%d=%s",i,Unlookup(gRegLookup,((nv_analog_reg_type *)pnv)->kind));
         pBuff = AppendString(pBuff,pszLine);
      }

   // Read the AMPS SID setting

      if(!ReadItem(pme->m_pConfig,i,CFGI_ANALOG_HOME_SID,(byte *)pnv,sizeof(nv_sid_type))){
         SPRINTF(pszLine,AMPSSID_STR":%d=%lu",i,((nv_sid_type *)pnv)->sid);
         pBuff = AppendString(pBuff,pszLine);
      }

   // TO DO - EVRC

   // Read the CDMA Primary Channels...

      if(!ReadItem(pme->m_pConfig,i,CFGI_PCDMACH,(byte *)pnv,sizeof(nv_cdmach_type))){
         SPRINTF(pszLine,CDMAPRIMARYA_STR":%d=%lu\n"CDMAPRIMARYB_STR":%d=%lu",i,((nv_cdmach_type *)pnv)->channel_a,i,((nv_cdmach_type *)pnv)->channel_b);
         pBuff = AppendString(pBuff,pszLine);
      }
   
   // Read the CDMA 2ndary Channels...

      if(!ReadItem(pme->m_pConfig,i,CFGI_SCDMACH,(byte *)pnv,sizeof(nv_cdmach_type))){
         SPRINTF(pszLine,CDMASECONDARYA_STR":%d=%lu\n"CDMASECONDARYB_STR":%d=%lu",i,((nv_cdmach_type *)pnv)->channel_a,i,((nv_cdmach_type *)pnv)->channel_b);
         pBuff = AppendString(pBuff,pszLine);
      }

   // Read the preferred system

      if(!ReadItem(pme->m_pConfig,i,CFGI_SYSTEM_PREF,(byte *)pnv,sizeof(nv_sys_pref_type))){
         SPRINTF(pszLine,PREFSYS_STR":%d=%s",i,Unlookup(gSysLookup,((nv_sys_pref_type *)pnv)->sys));
         pBuff = AppendString(pBuff,pszLine);
      }

   // Read the preferred mode

      if(!ReadItem(pme->m_pConfig,i,CFGI_PREF_MODE,(byte *)pnv,sizeof(nv_pref_mode_type))){
         SPRINTF(pszLine,PREFMODE_STR":%d=%s",i,Unlookup(gModeLookup,((nv_pref_mode_type *)pnv)->mode));
         pBuff = AppendString(pBuff,pszLine);
      }

   // Read the preferred mode

      if(!ReadItem(pme->m_pConfig,i,CFGI_HOME_SID_NID,(byte *)pnv,sizeof(nv_home_sid_nid_type))){
         for(n = 0; n < NV_MAX_HOME_SID_NID; n++){
            SPRINTF(pszLine,SIDNID_STR"%d:%d=%lu/%lu",n + 1,i,((nv_home_sid_nid_type *)pnv)->pair[n].sid,((nv_home_sid_nid_type *)pnv)->pair[n].nid);
            pBuff = AppendString(pBuff,pszLine);
         }
      }
   }

   FREE(pszLine);
   FREE((void *)pnv);
   return((byte *)pBuff);
}

/*===========================================================================

===========================================================================*/
static int COEMProv_SetNAM(COEMProv * pme, IAStream * ps, boolean * pbNeedReset)
{
   char *               pszNextLine;
   char *               pszLine;
   char *               pszVal;
   char *               pszNAM;
   byte *               pBuff,*pNew;
   int                  nLeft,nSize,nOffset,nRead,nNAM,i,n;
   OEMTag *             pt;
   boolean              bNAMS[NV_MAX_NAMS];
   nv_min_chksum_type   nv;         
   boolean              bUpdated = FALSE;

   // Read the entire stream into a buffer...

   MEMSET(bNAMS,0,sizeof(bNAMS));

   FARF(BOTAP,("OP Reading NAM..."));

   pBuff = NULL;
   nLeft = nSize = nOffset = 0;
   while(1){
      if(!nLeft){
         pNew = (byte *)REALLOC(pBuff,nSize + PROV_BUFF_SIZE);
         if(!pNew){
            if(pBuff)
               FREE(pBuff);
            return(ENOMEMORY);
         }
         pBuff = pNew;
         nLeft = PROV_BUFF_SIZE;
         nSize += PROV_BUFF_SIZE;
      }
      nRead = IASTREAM_Read(ps,pBuff + nOffset,nLeft);
      if(!nRead)
         break;
      nOffset += nRead;
      nLeft -= nRead;
   }

   FARF(BOTAP,("OP Parsing NAM"));

   pszLine = (char *)pBuff;
   while(pszLine){
      pszNextLine = STRCHR(pszLine,'\r');
      if(!pszNextLine)
         pszNextLine = STRCHR(pszLine,'\n');
      if(pszNextLine)
         *pszNextLine = 0;

   // If the line begins with a comment, skip it...

      if(*pszLine && *pszLine != '#'){
         pszVal = STRCHR(pszLine,'=');
         if(pszVal){
            *pszVal = 0;
            pszVal++;

   // Clip any additional comments...

            pszNAM = STRCHR(pszLine,'#');
            if(pszNAM)
               *pszNAM = 0;

   // Determine the NAM...

            nNAM = 0;

            pszNAM = STRCHR(pszLine,':');
            if(pszNAM){
               *pszNAM = 0;
               nNAM = (int)STRTOUL(pszNAM + 1,NULL,10);
            }

   // If it's a valid NAM look for the handler...

            if(nNAM < NV_MAX_NAMS){
               for(pt = (OEMTag *)gTags; pt->pszTag; pt++){
                  if(!STRICMP(pt->pszTag,pszLine)){
                     bNAMS[nNAM] = TRUE;
                     BRIDLE_ASSERT_SAFE_PFN(pt->pfn);
                     if(!pt->pfn(pme,nNAM,pszLine,pszVal))
                        bUpdated = TRUE;
                     break;
                  }
               }
            }
         }
      }
      pszLine = pszNextLine;
      if(pszLine){
         pszLine++;
         if(!*pszLine)
            break;
         while(*pszLine == '\r' || *pszLine == '\n')
            pszLine++;
      }
   }

   // See if the primary CDMA channel was updated...

   if(pme->m_pPrimary){
      for(i = 0; i < pme->m_nPrimary; i++){
         FARF(BOTAP,("OP Primary %d",i));
         if(!ICONFIG_SetItem(pme->m_pConfig,CFGI_PCDMACH, (void *)&pme->m_pPrimary[i],sizeof(nv_cdmach_type)))
            bUpdated = TRUE;
      }
      FREE((void *)pme->m_pPrimary);
   }

   // See if the primary CDMA channel was updated...

   if(pme->m_pSecondary){
      for(i = 0; i < pme->m_nSecondary; i++){
         FARF(BOTAP,("OP Secondary %d",i));
         if(!ICONFIG_SetItem(pme->m_pConfig,CFGI_SCDMACH, (void *) &pme->m_pSecondary[i],sizeof(nv_cdmach_type)))
            bUpdated = TRUE;
      }
      FREE((void *)pme->m_pSecondary);
   }

   // See if the SID/NID list was updated...

   if(pme->m_psn){
      for(i = 0; i < pme->m_nsn; i++){
         FARF(BOTAP,("OP SIDNID %d",i));
         if(!ICONFIG_SetItem(pme->m_pConfig,CFGI_NAME_NAM, (void *) &pme->m_psn[i],sizeof(nv_name_nam_type)))
            bUpdated = TRUE;
      }
      FREE((void *)pme->m_psn);
   }

   // Update the checksum...

   if(bUpdated){
      for(i = 0; i < NV_MAX_NAMS; i++){
         if(bNAMS[i]){
            FARF(BOTAP,("OP Checksum %d",i));
            nv.nam      = (byte)i;
            for(n = 0; n < NV_MAX_MINS; n++)
               nv.chksum[n] = 0xffff;
            ICONFIG_SetItem(pme->m_pConfig,CFGI_MIN_CHKSUM,(void *)&nv,sizeof(nv));
         }
      }
   }      
      
   if(pbNeedReset)
      *pbNeedReset = bUpdated;

   if(pBuff)
      FREE(pBuff);

   return(0);
}

/*===========================================================================

===========================================================================*/
static int ReadItem(IConfig * po, int nNAM, AEEConfigItem nItem, byte * pDest, int nSize)
{
   ProvisionItem  i;

   i.nNAM = nNAM;
   i.nSize = nSize;
   i.pDest = pDest;
   return(ICONFIG_GetItem(po,nItem,(void *)&i,sizeof(i)));
}

/*===========================================================================

===========================================================================*/
static nv_cdmach_type * FindChannel(IConfig * po,int nNAM, AEEConfigItem nItem, nv_cdmach_type ** ppList, int * pnCount)
{
   nv_cdmach_type *  pch;
   int               i,nCount;
   
   nCount = *pnCount;

   for(pch = *ppList, i = 0; i < nCount; i++, pch++){
      if(pch->nam == nNAM)
         return(pch);
   }
   pch = (nv_cdmach_type *)REALLOC((void *)*ppList,sizeof(nv_cdmach_type) * (nCount + 1));
   if(!pch)
      return(NULL);

   pch->nam = nNAM;
   *ppList = pch;
   pch += nCount;
   pch->nam = (byte)nNAM;
   *pnCount = nCount + 1;

   ReadItem(po,nNAM,nItem,(byte *)pch,sizeof(nv_cdmach_type));

   return(pch);
}

/*===========================================================================



   CARRIER LOGO STUFF


===========================================================================*/
static int COEMProv_SetCLOGO(COEMProv * pme, const char * pszFile, IAStream * ps)
{
   byte *      pBuff;
   int         nBytes;
   IFileMgr *  pfm;
   IFile *     pf;
   int         nErr = 0;
   char        szPath[MAX_FILE_NAME];
   uint32      dwRead = 0;

   // We need the input file name...

   FARF(LOGO,("Logo Update"));

   if(!pszFile)
      return(EBADPARM);

   {
      int nLen = ARRAY_SIZE(szPath);
      MAKEPATH(AEEFS_SYS_DIR"logo",pszFile, szPath, &nLen);
   } 

   pBuff = (byte *)MALLOC(PROV_BUFF_SIZE);
   if(!pBuff)
      return(ENOMEMORY);

   nErr = ISHELL_CreateInstance(pme->m_pShell, AEECLSID_FILEMGR, (void **)(&pfm));
   if(!nErr){

   // Tell the native code to close the file.

      OEM_Notify(OEMNTF_CLOSE_FILE,(uint32)szPath);

      nErr = IFILEMGR_Remove(pfm,szPath);
      FARF(LOGO,("Logo-> Remove (%d)",nErr));

   // Open and write the file.  If the total size we write == 0, then just remove it again.  This
   // will be the case on a restore.

      pf = IFILEMGR_OpenFile(pfm,szPath,_OFM_CREATE);
      if(pf){
         while((nBytes = IASTREAM_Read(ps,pBuff,PROV_BUFF_SIZE)) > 0){
            FARF(LOGO,("Logo-> Write %d",nBytes));
            if(IFILE_Write(pf,pBuff,nBytes) != (uint32)nBytes){
               dwRead = 0;
               break;
            }
            dwRead += nBytes;
         }
         IFILE_Release(pf);
         if(!dwRead){
            nErr = IFILEMGR_Remove(pfm,szPath);
            FARF(LOGO,("Logo-> Remove %s (%d)",szPath,nErr));
         }
         else
            nErr = 0;
      }
      else{
         nErr = IFILEMGR_GetLastError(pfm);
         FARF(LOGO,("Logo-> Bad Open (%d)",nErr));
      }
      IFILEMGR_Release(pfm);
   }
   FREE(pBuff);
   return(nErr);
}
   
#endif   // OEMPROVISION



