Back to index

lightning-sunbird  0.9+nobinonly
rdi.c
Go to the documentation of this file.
00001 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla Navigator.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corp.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Sean Su <ssu@netscape.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "extern.h"
00040 #include "parser.h"
00041 #include "extra.h"
00042 #include "ifuncns.h"
00043 
00044 HKEY hkUnreadMailRootKey = HKEY_CURRENT_USER;
00045 char szUnreadMailKey[] = "Software\\Microsoft\\Windows\\CurrentVersion\\UnreadMail";
00046 char szMozillaDesktopKey[] = "Software\\Mozilla\\Desktop";
00047 char szRDISection[] = "Restore Desktop Integration";
00048 
00049 /* structure for use with UnreadMail registry keys */
00050 typedef struct sKeyNode skn;
00051 struct sKeyNode
00052 {
00053   char  szKey[MAX_BUF];
00054   skn   *Next;
00055   skn   *Prev;
00056 };
00057 
00058 /* Function that creates an instance of skn */
00059 skn *CreateSknNode()
00060 {
00061   skn *sknNode;
00062 
00063   if((sknNode = NS_GlobalAlloc(sizeof(struct sKeyNode))) == NULL)
00064     exit(1);
00065 
00066   sknNode->Next      = sknNode;
00067   sknNode->Prev      = sknNode;
00068 
00069   return(sknNode);
00070 }
00071 
00072 /* Function that inserts a skn structure into a linked list */
00073 void SknNodeInsert(skn **sknHead, skn *sknTemp)
00074 {
00075   if(*sknHead == NULL)
00076   {
00077     *sknHead          = sknTemp;
00078     (*sknHead)->Next  = *sknHead;
00079     (*sknHead)->Prev  = *sknHead;
00080   }
00081   else
00082   {
00083     sknTemp->Next           = *sknHead;
00084     sknTemp->Prev           = (*sknHead)->Prev;
00085     (*sknHead)->Prev->Next  = sknTemp;
00086     (*sknHead)->Prev        = sknTemp;
00087   }
00088 }
00089 
00090 /* Function that removes a skn structure from a skn linked list.
00091  * and frees memory. */
00092 void SknNodeDelete(skn *sknTemp)
00093 {
00094   if(sknTemp != NULL)
00095   {
00096     sknTemp->Next->Prev = sknTemp->Prev;
00097     sknTemp->Prev->Next = sknTemp->Next;
00098     sknTemp->Next       = NULL;
00099     sknTemp->Prev       = NULL;
00100 
00101     FreeMemory(&sknTemp);
00102   }
00103 }
00104 
00105 /* Function that traverses a skn linked list and deletes each node. */
00106 void DeInitSknList(skn **sknHeadNode)
00107 {
00108   skn *sknTemp;
00109   
00110   if(*sknHeadNode == NULL)
00111     return;
00112 
00113   sknTemp = (*sknHeadNode)->Prev;
00114 
00115   while(sknTemp != *sknHeadNode)
00116   {
00117     SknNodeDelete(sknTemp);
00118     sknTemp = (*sknHeadNode)->Prev;
00119   }
00120 
00121   SknNodeDelete(sknTemp);
00122   *sknHeadNode = NULL;
00123 }
00124 
00125 /* Function to check if [windir]\mapi32.dll belongs to mozilla or not */
00126 int IsMapiMozMapi(BOOL *bIsMozMapi)
00127 {
00128   HINSTANCE hLib;
00129   char szMapiFilePath[MAX_BUF];
00130   int iRv = WIZ_ERROR_UNDEFINED;
00131   int (PASCAL *GetMapiDllVersion)(void);
00132   char szMapiVersionKey[] = "MAPI version installed";
00133   char szBuf[MAX_BUF];
00134   int  iMapiVersionInstalled;
00135 
00136   /* Get the Mapi version that we installed from uninstall.ini.
00137    * If there is none set, then return WIZ_ERROR_UNDEFINED. */
00138   GetPrivateProfileString(szRDISection, szMapiVersionKey, "", szBuf, sizeof(szBuf), szFileIniUninstall);
00139   if(*szBuf == '\0')
00140     return(iRv);
00141 
00142   iMapiVersionInstalled = atoi(szBuf);
00143   if(GetSystemDirectory(szMapiFilePath, sizeof(szMapiFilePath)) == 0)
00144     return(iRv);
00145 
00146   AppendBackSlash(szMapiFilePath, sizeof(szMapiFilePath));
00147   lstrcat(szMapiFilePath, "Mapi32.dll");
00148   if(!FileExists(szMapiFilePath))
00149     iRv = WIZ_FILE_NOT_FOUND;
00150   else if((hLib = LoadLibrary(szMapiFilePath)) != NULL)
00151   {
00152     iRv = WIZ_OK;
00153     *bIsMozMapi = FALSE;
00154     if(((FARPROC)GetMapiDllVersion = GetProcAddress(hLib, "GetMapiDllVersion")) != NULL)
00155     {
00156       if(iMapiVersionInstalled == GetMapiDllVersion())
00157         *bIsMozMapi = TRUE;
00158     }
00159     FreeLibrary(hLib);
00160   }
00161   return(iRv);
00162 }
00163 
00164 /* This function parses takes as input a registry key path string beginning
00165  * with HKEY_XXXX (root key) and parses out the sub key path and the root
00166  * key.
00167  *
00168  * It returns the root key as HKEY and the sub key path as char* */
00169 HKEY GetRootKeyAndSubKeyPath(char *szInKeyPath, char *szOutSubKeyPath, DWORD dwOutSubKeyPathSize)
00170 {
00171   char *ptr      = szInKeyPath;
00172   HKEY hkRootKey = HKEY_CLASSES_ROOT;
00173 
00174   ZeroMemory(szOutSubKeyPath, dwOutSubKeyPathSize);
00175   if(ptr == NULL)
00176     return(hkRootKey);
00177 
00178   /* search for the first '\' char */
00179   while(*ptr && (*ptr != '\\'))
00180     ++ptr;
00181 
00182   if((*ptr == '\0') ||
00183      (*ptr == '\\'))
00184   {
00185     BOOL bPtrModified = FALSE;
00186 
00187     if(*ptr == '\\')
00188     {
00189       *ptr = '\0';
00190       bPtrModified = TRUE;
00191     }
00192     hkRootKey = ParseRootKey(szInKeyPath);
00193 
00194     if(bPtrModified)
00195       *ptr = '\\';
00196 
00197     if((*ptr != '\0') &&
00198        ((unsigned)lstrlen(ptr + 1) + 1 <= dwOutSubKeyPathSize))
00199       /* copy only the sub key path after the root key string */
00200       lstrcpy(szOutSubKeyPath, ptr + 1);
00201   }
00202   return(hkRootKey);
00203 }
00204 
00205 
00206 /* This function checks for nonprintable characters.
00207  * If at least one is found in the input string, it will return TRUE,
00208  * else FALSE */
00209 BOOL CheckForNonPrintableChars(char *szInString)
00210 {
00211   int i;
00212   int iLen;
00213   BOOL bFoundNonPrintableChar = FALSE;
00214 
00215   if(!szInString)
00216     return(TRUE);
00217 
00218   iLen = lstrlen(szInString);
00219 
00220   for(i = 0; i < iLen; i++)
00221   {
00222     if(!isprint(szInString[i]))
00223     {
00224       bFoundNonPrintableChar = TRUE;
00225       break;
00226     }
00227   }
00228 
00229   return(bFoundNonPrintableChar);
00230 }
00231 
00232 /* This function checks to see if the key path is a ddeexec path.  If so,
00233  * it then checks for non printable chars */
00234 BOOL DdeexecCheck(char *szKey, char *szValue)
00235 {
00236   char szKddeexec[] = "shell\\open\\ddeexec";
00237   char szKeyLower[MAX_BUF];
00238   BOOL bPass = TRUE;
00239 
00240   lstrcpy(szKeyLower, szKey);
00241   CharLowerBuff(szKeyLower, sizeof(szKeyLower));
00242   if(strstr(szKeyLower, szKddeexec) && CheckForNonPrintableChars(szValue))
00243     bPass = FALSE;
00244 
00245   return(bPass);
00246 }
00247 
00248 /* This function enumerates HKEY_LOCAL_MACHINE\Sofware\Mozilla\Desktop for
00249  * variable information on what desktop integration was done by the
00250  * browser/mail client.
00251  *
00252  * These variables found cannot be deleted or modified until the enumeration
00253  * is complete, or else this function will fail! */
00254 void RestoreDesktopIntegration()
00255 {
00256   char      szVarName[MAX_BUF];
00257   char      szValue[MAX_BUF];
00258   char      szSubKey[MAX_BUF];
00259   HKEY      hkHandle;
00260   DWORD     dwIndex;
00261   DWORD     dwSubKeySize;
00262   DWORD     dwTotalValues;
00263   char      szKHKEY[]               = "HKEY";
00264   char      szKisHandling[]         = "isHandling";
00265 
00266   if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, szMozillaDesktopKey, 0, KEY_READ|KEY_WRITE, &hkHandle) != ERROR_SUCCESS)
00267     return;
00268 
00269   dwTotalValues  = 0;
00270   RegQueryInfoKey(hkHandle, NULL, NULL, NULL, NULL, NULL, NULL, &dwTotalValues, NULL, NULL, NULL, NULL);
00271   for(dwIndex = 0; dwIndex < dwTotalValues; dwIndex++)
00272   {
00273     /* Enumerate thru all the vars found within the Mozilla Desktop key */
00274     dwSubKeySize = sizeof(szVarName);
00275     if(RegEnumValue(hkHandle, dwIndex, szVarName, &dwSubKeySize, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
00276     {
00277       if(strnicmp(szVarName, szKHKEY, lstrlen(szKHKEY)) == 0)
00278       {
00279         HKEY hkRootKey;
00280 
00281         hkRootKey = GetRootKeyAndSubKeyPath(szVarName, szSubKey, sizeof(szSubKey));
00282         if(*szSubKey != '\0')
00283         {
00284           GetWinReg(HKEY_LOCAL_MACHINE, szMozillaDesktopKey, szVarName, szValue, sizeof(szValue));
00285           if(*szValue != '\0')
00286           {
00287             /* Due to a bug in the browser code that saves the previous HKEY
00288              * value it's trying to replace as garbage chars, we need to try
00289              * to detect it.  If found, do not restore it. This bug only
00290              * happens for the saved ddeexec keys. */
00291             if(DdeexecCheck(szSubKey, szValue))
00292             {
00293               /* Restore the previous saved setting here */
00294               SetWinReg(hkRootKey,
00295                         szSubKey,
00296                         NULL,
00297                         REG_SZ,
00298                         szValue,
00299                         lstrlen(szValue));
00300             }
00301           }
00302           else
00303             /* if the saved value is an empty string, then
00304              * delete the default var for this key */
00305             DeleteWinRegValue(hkRootKey,
00306                               szSubKey,
00307                               szValue);
00308         }
00309       }
00310     }
00311   }
00312   RegCloseKey(hkHandle);
00313   return;
00314 }
00315 
00316 void RestoreMozMapi()
00317 {
00318   char szMozMapiBackupFile[MAX_BUF];
00319   BOOL bFileIsMozMapi = FALSE;
00320 
00321   GetWinReg(HKEY_LOCAL_MACHINE,
00322             szMozillaDesktopKey,
00323             "Mapi_backup_dll",
00324             szMozMapiBackupFile,
00325             sizeof(szMozMapiBackupFile));
00326 
00327   /* If the windows registry Mapi_backup_dll var name does not exist,
00328    * then we're not the default mail handler for the system.
00329    *
00330    * If the backup mapi file does not exist for some reason, then
00331    * there's no way to restore the previous saved mapi32.dll file. */
00332   if((*szMozMapiBackupFile == '\0') || !FileExists(szMozMapiBackupFile))
00333     return;
00334 
00335   /* A TRUE for bFileIsMozMapi indicates that we need to restore
00336    * the backed up mapi32.dll (if one was backed up).
00337    *
00338    * bFileIsMozMapi is TRUE in the following conditions:
00339    *   * mapi32.dll is not found
00340    *   * mapi32.dll loads and GetMapiDllVersion() exists
00341    *     _and_ returns the same version indicated in the uninstall.ini file:
00342    *
00343    *       [Restore Desktop Integration]
00344    *       Mapi version installed=94
00345    *
00346    *     94 indicates version 0.9.4 */
00347   if(IsMapiMozMapi(&bFileIsMozMapi) == WIZ_FILE_NOT_FOUND)
00348     bFileIsMozMapi = TRUE;
00349 
00350   if(bFileIsMozMapi)
00351   {
00352     char szDestinationFilename[MAX_BUF];
00353 
00354     /* Get the Windows System (or System32 under NT) directory */
00355     if(GetSystemDirectory(szDestinationFilename, sizeof(szDestinationFilename)))
00356     {
00357       /* Copy the backup filename into the normal Mapi32.dll filename */
00358       AppendBackSlash(szDestinationFilename, sizeof(szDestinationFilename));
00359       lstrcat(szDestinationFilename, "Mapi32.dll");
00360       CopyFile(szMozMapiBackupFile, szDestinationFilename, FALSE);
00361     }
00362   }
00363   /* Delete the backup Mapi filename */
00364   FileDelete(szMozMapiBackupFile);
00365 }
00366 
00367 BOOL UndoDesktopIntegration(void)
00368 {
00369   char szMozillaKey[] = "Software\\Mozilla";
00370   char szBuf[MAX_BUF];
00371 
00372   /* Check to see if uninstall.ini has indicated to restore
00373    * the destktop integration performed by the browser/mail */
00374   GetPrivateProfileString(szRDISection, "Enabled", "", szBuf, sizeof(szBuf), szFileIniUninstall);
00375   if(lstrcmpi(szBuf, "TRUE") == 0)
00376   {
00377     RestoreDesktopIntegration();
00378     RestoreMozMapi();
00379 
00380     DeleteWinRegKey(HKEY_LOCAL_MACHINE, szMozillaDesktopKey, TRUE);
00381     DeleteWinRegKey(HKEY_LOCAL_MACHINE, szMozillaKey, FALSE);
00382   }
00383 
00384   return(0);
00385 }
00386 
00387 
00388 /* Function that retrieves the app name (including path) that is going to be
00389  * uninstalled.  The return string is in upper case. */
00390 int GetUninstallAppPathName(char *szAppPathName, DWORD dwAppPathNameSize)
00391 {
00392   char szKey[MAX_BUF];
00393   HKEY hkRoot;
00394 
00395   if(*ugUninstall.szUserAgent != '\0')
00396   {
00397     hkRoot = ugUninstall.hWrMainRoot;
00398     lstrcpy(szKey, ugUninstall.szWrMainKey);
00399     AppendBackSlash(szKey, sizeof(szKey));
00400     lstrcat(szKey, ugUninstall.szUserAgent);
00401     AppendBackSlash(szKey, sizeof(szKey));
00402     lstrcat(szKey, "Main");
00403   }
00404   else
00405   {
00406     return(CMI_APP_PATHNAME_NOT_FOUND);
00407   }
00408 
00409   GetWinReg(hkRoot, szKey, "PathToExe", szAppPathName, dwAppPathNameSize);
00410   CharUpperBuff(szAppPathName, dwAppPathNameSize);
00411   return(CMI_OK);
00412 }
00413 
00414 /* Function to delete the UnreadMail keys that belong to the app that is being
00415  * uninstalled.  The skn list that is passed in should only contain UnreadMail
00416  * subkeys to be deleted. */
00417 void DeleteUnreadMailKeys(skn *sknHeadNode)
00418 {
00419   skn   *sknTempNode;
00420   char  szUnreadMailDeleteKey[MAX_BUF];
00421   DWORD dwLength;
00422   
00423   if(sknHeadNode == NULL)
00424     return;
00425 
00426   sknTempNode = sknHeadNode;
00427 
00428   do
00429   {
00430     /* build the full UnreadMail key to be deleted */
00431     dwLength = sizeof(szUnreadMailDeleteKey) > lstrlen(szUnreadMailKey) ?
00432                       lstrlen(szUnreadMailKey) + 1: sizeof(szUnreadMailDeleteKey);
00433     lstrcpyn(szUnreadMailDeleteKey, szUnreadMailKey, dwLength);
00434     AppendBackSlash(szUnreadMailDeleteKey, sizeof(szUnreadMailDeleteKey));
00435     if((unsigned)(lstrlen(sknTempNode->szKey) + 1) <
00436        (sizeof(szUnreadMailDeleteKey) - lstrlen(szUnreadMailKey) + 1))
00437     {
00438       lstrcat(szUnreadMailDeleteKey, sknTempNode->szKey);
00439 
00440       /* delete the UnreadMail key (even if it has subkeys) */
00441       DeleteWinRegKey(hkUnreadMailRootKey, szUnreadMailDeleteKey, TRUE);
00442     }
00443 
00444     /* get the next key to delete */
00445     sknTempNode = sknTempNode->Next;
00446 
00447   }while(sknTempNode != sknHeadNode);
00448 }
00449 
00450 /* Function that builds a list of UnreadMail subkey names that only belong to
00451  * the app that is being uninstalled.  The list is a linked list of skn
00452  * structures. */
00453 BOOL GetUnreadMailKeyList(char *szUninstallAppPathName, skn **sknWinRegKeyList)
00454 {
00455   HKEY  hkSubKeyHandle;
00456   HKEY  hkHandle;
00457   DWORD dwErr = ERROR_SUCCESS;
00458   DWORD dwTotalSubKeys;
00459   DWORD dwSubKeySize;
00460   DWORD dwIndex;
00461   DWORD dwBufSize;
00462   BOOL  bFoundAtLeastOne = FALSE;
00463   char  szSubKey[MAX_BUF];
00464   char  szBuf[MAX_BUF];
00465   char  szNewKey[MAX_BUF];
00466   skn   *sknTempNode;
00467 
00468   /* open the UnreadMail key so we can enumerate its subkeys */
00469   dwErr = RegOpenKeyEx(hkUnreadMailRootKey,
00470                        szUnreadMailKey,
00471                        0,
00472                        KEY_READ|KEY_QUERY_VALUE,
00473                        &hkHandle);
00474   if(dwErr == ERROR_SUCCESS)
00475   {
00476     dwTotalSubKeys = 0;
00477     RegQueryInfoKey(hkHandle,
00478                     NULL,
00479                     NULL,
00480                     NULL,
00481                     &dwTotalSubKeys,
00482                     NULL,
00483                     NULL,
00484                     NULL,
00485                     NULL,
00486                     NULL,
00487                     NULL,
00488                     NULL);
00489 
00490     if(dwTotalSubKeys != 0)
00491     {
00492       dwIndex = 0;
00493       do
00494       {
00495         /* For each UnreadMail subkey, parse it's 'Application' var name for
00496          * the app we're uninstalling (full path).  Compare against both long
00497          * path name and short path name.  If match found, add to linked list
00498          * of skn structures.
00499          *
00500          * No key deletion is performed at this time! */
00501 
00502         sknTempNode = NULL; /* reset temporaty node pointer */
00503         dwSubKeySize = sizeof(szSubKey);
00504         if((dwErr = RegEnumKeyEx(hkHandle,
00505                                  dwIndex,
00506                                  szSubKey,
00507                                  &dwSubKeySize,
00508                                  NULL,
00509                                  NULL,
00510                                  NULL,
00511                                  NULL)) == ERROR_SUCCESS)
00512         {
00513           lstrcpy(szNewKey, szUnreadMailKey);
00514           AppendBackSlash(szNewKey, sizeof(szNewKey));
00515           lstrcat(szNewKey, szSubKey);
00516 
00517           if(RegOpenKeyEx(hkUnreadMailRootKey,
00518                           szNewKey,
00519                           0,
00520                           KEY_READ,
00521                           &hkSubKeyHandle) == ERROR_SUCCESS)
00522           {
00523             dwBufSize = sizeof(szBuf);
00524             if(RegQueryValueEx(hkSubKeyHandle,
00525                                "Application",
00526                                NULL,
00527                                NULL,
00528                                szBuf,
00529                                &dwBufSize) == ERROR_SUCCESS)
00530             {
00531               CharUpperBuff(szBuf, sizeof(szBuf));
00532               if(strstr(szBuf, szUninstallAppPathName) != NULL)
00533               {
00534                 bFoundAtLeastOne = TRUE;
00535                 sknTempNode = CreateSknNode();
00536                 lstrcpyn(sknTempNode->szKey, szSubKey, dwSubKeySize + 1);
00537               }
00538               else
00539               {
00540                 char szUninstallAppPathNameShort[MAX_BUF];
00541 
00542                 GetShortPathName(szUninstallAppPathName,
00543                                  szUninstallAppPathNameShort,
00544                                  sizeof(szUninstallAppPathNameShort));
00545                 if(strstr(szBuf, szUninstallAppPathNameShort) != NULL)
00546                 {
00547                   bFoundAtLeastOne = TRUE;
00548                   sknTempNode = CreateSknNode();
00549                   lstrcpyn(sknTempNode->szKey, szSubKey, dwSubKeySize + 1);
00550                 }
00551               }
00552             }
00553             RegCloseKey(hkSubKeyHandle);
00554           }
00555         }
00556 
00557         if(sknTempNode)
00558           SknNodeInsert(sknWinRegKeyList, sknTempNode);
00559 
00560         ++dwIndex;
00561       } while(dwErr != ERROR_NO_MORE_ITEMS);
00562     }
00563     RegCloseKey(hkHandle);
00564   }
00565   return(bFoundAtLeastOne);
00566 }
00567 
00568 /* Main function that deals with cleaning up the UnreadMail subkeys belonging
00569  * to the app were currently uninstalling. */
00570 int CleanupMailIntegration(void)
00571 {
00572   char szCMISection[] = "Cleanup Mail Integration";
00573   char szUninstallApp[MAX_BUF];
00574   char szBuf[MAX_BUF];
00575   skn  *sknWinRegKeyList = NULL;
00576 
00577   /* Check to see if uninstall.ini has indicated to cleanup
00578    * the mail integration performed by mail */
00579   GetPrivateProfileString(szCMISection,
00580                           "Enabled",
00581                           "",
00582                           szBuf,
00583                           sizeof(szBuf),
00584                           szFileIniUninstall);
00585   if(lstrcmpi(szBuf, "TRUE") != 0)
00586     return(CMI_OK);
00587 
00588   /* Get the full app name we're going to uninstall */
00589   if(GetUninstallAppPathName(szUninstallApp, sizeof(szUninstallApp)) != CMI_OK)
00590     return(CMI_APP_PATHNAME_NOT_FOUND);
00591 
00592   /* Build a list of UnreadMail subkeys that needs to be deleted */
00593   if(GetUnreadMailKeyList(szUninstallApp, &sknWinRegKeyList))
00594     /* Delete the UnreadMail subkeys using the list built by
00595      * GetUnreadMailKeyList() */
00596     DeleteUnreadMailKeys(sknWinRegKeyList);
00597 
00598   /* Clean up the linked list */
00599   DeInitSknList(&sknWinRegKeyList);
00600   return(CMI_OK);
00601 }
00602