/* *@@sourcefile bs_config.cpp: * this has the code for the classes which manipulate CONFIG.SYS, * the WPS class list, create WPS objects, or do other * WarpIN system configuration. * * These classes are designed to work independently of WarpIN * and could be used in any program. * I have attempted to design these classes for easy, yet flexible use. * * All system config classes are derived from BSConfigBase. * This allows for maintaining a list of all system changes * with each WarpIN package, which have a somewhat uniform * interface. Each config class has in turn a special method * which will actually perform the system configuration change. * * This special method will usually take a logger instance * as a parameter, which logs the changes that were actually * made to the system. This logger can then be used later to * undo the change. * See BSCfgSysManip::BSCfgSysManip for an example usage. * * The loggers are all direct, unmodified descendants of the * BSMemLoggerBase class (bs_logger.cpp) and only defined to * use C++ type checking. * * Some config methods throw instances of the BSConfigExcpt * class def'd in bs_config.h, which you should handle. * * These are the classes implemented here: * * -- BSCfgSysManip: * a "manipulator" object of this class holds information * for a CONFIG.SYS change, which is passed to BSConfigSys::Manipulate. * See BSCfgSysManip::BSCfgSysManip for a description. * * -- BSConfigSys: * the actual class which holds the CONFIG.SYS text file * and allows changes to be made. * This class does not have a corresponding "Undo" class. * Instead, use BSCfgSysManip::AddToUndoList. * * -- BSRegisterClass: * this allows registering classes with the WPS. * See BSRegisterClass::Register for an example usage. * * -- BSDeregisterClass: * the "Undo" class to the previous. * * -- BSReplaceClass: * this allows WPS classes to be replaced. * See BSReplaceClass::Replace for an example usage. * * -- BSUnreplaceClass: * the "Undo" class to the previous. * * -- BSCreateWPSObject: * for creating a WPS object. * * -- BSDeleteWPSObject: * the "Undo" class for the previous, to delete a WPS object again. * * See bs_config.h for the declarations of these classes. * *@@header "cppbase\bs_config.h" *@@header "cppbase\bs_config_impl.h" */ /* * This file Copyright (C) 1999-2008 Ulrich M”ller. * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, in version 2 as it comes in the COPYING * file of this distribution. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #define OS2EMX_PLAIN_CHAR // this is needed for "os2emx.h"; if this is defined, // emx will define PSZ as _signed_ char, otherwise // as unsigned char #define INCL_DOSSESMGR #define INCL_DOSSEMAPHORES #define INCL_DOSERRORS #define INCL_WINWINDOWMGR #define INCL_WINMESSAGEMGR #define INCL_WINDIALOGS #define INCL_WINSTDCNR #define INCL_WININPUT #define INCL_WINSYS #define INCL_WINSHELLDATA #define INCL_WINWORKPLACE #include #include #include #include #include #include "setup.h" // include's from helpers #include "helpers\dosh.h" #include "helpers\winh.h" #include "helpers\prfh.h" #include "helpers\stringh.h" #include "helpers\xprf.h" #include "helpers\xstring.h" #include "helpers\configsys.h" // front-end includes #include "cppbase\bs_base.h" #include "cppbase\bs_list.h" #include "cppbase\bs_string.h" #include "cppbase\bs_errors.h" #include "cppbase\bs_logger.h" #include "cppbase\bs_config.h" #include "cppbase\bs_config_impl.h" #pragma hdrstop DEFINE_CLASS(BSConfigBase, BSRoot); DEFINE_CLASS(BSCfgSysManip, BSConfigBase); DEFINE_CLASS(BSConfigSys, BSRoot); DEFINE_CLASS(BSRegisterClass, BSConfigBase); DEFINE_CLASS(BSDeregisterClass, BSConfigBase); DEFINE_CLASS(BSReplaceClassBase, BSConfigBase); DEFINE_CLASS(BSReplaceClass, BSReplaceClassBase); DEFINE_CLASS(BSUnreplaceClass, BSReplaceClassBase); DEFINE_CLASS(BSCreateWPSObject, BSConfigBase); DEFINE_CLASS(BSDeleteWPSObject, BSConfigBase); DEFINE_CLASS(BSProfileBase, BSConfigBase); DEFINE_CLASS(BSClearProfile, BSProfileBase); DEFINE_CLASS(BSWriteProfile, BSProfileBase); DEFINE_CLASS(BSExecute, BSConfigBase); DEFINE_CLASS(BSDeExecute, BSExecute); DEFINE_CLASS(BSKillProcess, BSConfigBase); #define CONVERT(c, s) string str ## s(c, _ustr ## s); \ PCSZ pcsz ## s = str ## s.c_str() /* ****************************************************************** * * BSCfgSysManip class * ********************************************************************/ /* *@@ BSCfgSysManip(char* pszConfigSys): * this constructor translates a CONFIGSYS attribute (as used * in the PCK tag and stored in the database) into a CONFIGSYSITEM * structure. * * The BSCfgSysManip class is designed for use with the * BSConfigSys class and describes manipulations to be done upon * the CONFIG.SYS file (represented by the BSConfigSys class). * * Manipulating CONFIG.SYS works just as follows: * * 1) Create an instance of BSConfigSys: * + BSConfigSys *pConfigSys = new BSConfigSys; * * This will load your CONFIG.SYS file into the instance's memory. * * 2) Create an instance of BSCfgSysManip and specify the manipulation * in the constructor: * + BSCfgSysManip *pManip = new BSCfgSysManip("SET TEST=YES | UNIQUE"); * * The BSCfgSysManip class has a single constructor which takes a PSZ * (char*) as input. This input string has exactly the format like * with the CONFIGSYS attribute to the PCK tag in WarpIN installation * scripts, like this (see the WarpIN Programmer's Reference for details): * + statement [| modifiers] * * "modifiers" can be one of the following: + [UNIQUE[(xxx)]] [vertical] + ADDRIGHT [vertical] + ADDLEFT [vertical] + REMOVELINE + REMOVEPART * with "xxx" being a search string. * * "vertical" can be one of the following: + ADDTOP + ADDAFTER(xxx) + ADDBEFORE(xxx) * with "xxx" being a search string. * * Examples: + "BASEDEV=NEWDRIVR.SYS /WHATEVER | UNIQUE ADDAFTER(IBM1S506.ADD)" + "SET PATH=C:\BLAH | ADDRIGHT" * * After this constructor has successfully converted pszConfigSys, * all the instance data is valid (see cfgsys.h). * * However, this does _not_ handle macro resultion like in WarpIn * scripts (which is done in warpin.cpp before calling the Manipulate * method), because in this file scope we know nothing about the * PackageInfo instances. * * 3) Create a CfgSysDone logger instance: + BSCfgSysDoneLogger logger; * * 4) Invoke the BSConfigSys::Manipulate method with the BSCfgSysManip * instance (this will add data to the logger): + pConfigSys->Manipulate(logger, pManip); * * 5) Write CONFIG.SYS back to disk and clean up. + pConfigSys->Flush(TRUE); + delete pBSCfgSysManip; + delete pConfigSys; * * Now, if you want to undo the changes later, call the static * BSCfgSysManip::AddToUndoList method with the logger passed to * to the various constructor calls (above), which will create * a list of BSCfgSysManip instance which will be able to undo * the changes again: * + list UndoList; + BSCfgSysManip::AddToUndoList(UndoList, logger); * * and iterate over the list and call BSConfigSys::Manipulate with * the objects on that list to undo the changes. * * Throws: * -- BSConfigExcpt. * *@@changed V0.9.1 (2000-01-06) [umoeller]: added UNIQUE(xxx) support *@@changed V0.9.5 (2000-08-26) [umoeller]: fixed UNIQUE(xxx) and REMOVELINE, which deleted wrong lines */ BSCfgSysManip::BSCfgSysManip(const ustring &ustrConfigSys) : BSConfigBase(CFGT_CFGSYS, tBSCfgSysManip) { PCSZ pcszConfigSys; if (!(pcszConfigSys = ustrConfigSys.GetBuffer())) throw BSConfigExcpt(CFGEXCPT_SYNTAX, 0); // initialize all fields to zero // memset(&_ConfigManip, 0, sizeof(_ConfigManip)); // V0.9.12 (2001-05-22) [umoeller] _iReplaceMode = CFGRPL_ADD; // V1.0.4 (2004-12-18) [pr] _iVertical = CFGVRT_BOTTOM; // Moved above statements here from the else{} block below. If there wasn't a modifier, // this was uninitialised garbage and CONFIG.SYS never got modified. // now check if we have modifiers PSZ pSep; if (!(pSep = strchr(pcszConfigSys, '|'))) // no modifiers: just copy _ustrNewLine = ustrConfigSys; else { // we do have modifiers: BOOL fVerticalsAllowed = TRUE; // get rid of spaces before '|' PSZ pSep2 = pSep-1; while (*pSep2 == ' ') pSep2--; // get the "statement" part _ustrNewLine.assignUtf8(pcszConfigSys, pSep2 + 1); // remember the modifiers position PSZ pModifiers = pSep + 1; BOOL fReplaceModeFound = FALSE; // now check for the replace mode; // the default is 0 (CFGRPL_ADD) fVerticalsAllowed = TRUE; PSZ pszUnique; if ((pszUnique = strhistr(pModifiers, "UNIQUE"))) { _iReplaceMode = CFGRPL_UNIQUE; fVerticalsAllowed = TRUE; fReplaceModeFound = TRUE; // block future replacement modifiers // check if we have a "UNIQUE(xxx)" syntax PSZ psz; if (psz = strhExtract(pszUnique, '(', ')', NULL)) { // if found, this extracts the stuff between ( and ); // if not, this returns NULL _ustrUniqueSearchString2.assignUtf8(psz); free(psz); } } if (strhistr(pModifiers, "ADDRIGHT")) { if (!fReplaceModeFound) { _iReplaceMode = CFGRPL_ADDRIGHT; fVerticalsAllowed = TRUE; fReplaceModeFound = TRUE; // block future replacement modifiers } else { // double replace mode found: throw BSConfigExcpt(CFGEXCPT_DOUBLEREPLACEMODE, ustrConfigSys); } } if (strhistr(pModifiers, "ADDLEFT")) { if (!fReplaceModeFound) { _iReplaceMode = CFGRPL_ADDLEFT; fVerticalsAllowed = TRUE; fReplaceModeFound = TRUE; // block future replacement modifiers } else // double replace mode found: throw BSConfigExcpt(CFGEXCPT_DOUBLEREPLACEMODE, ustrConfigSys); } if (strhistr(pModifiers, "REMOVELINE")) { if (!fReplaceModeFound) { _iReplaceMode = CFGRPL_REMOVELINE; fVerticalsAllowed = FALSE; fReplaceModeFound = TRUE; // block future replacement modifiers // check also for stuff after "=" PSZ p; if (p = strchr(_ustrNewLine.GetBuffer(), '=')) _ustrUniqueSearchString2.assignUtf8(p + 1); } else // double replace mode found: throw BSConfigExcpt(CFGEXCPT_DOUBLEREPLACEMODE, ustrConfigSys); } if (strhistr(pModifiers, "REMOVEPART")) { if (!fReplaceModeFound) { _iReplaceMode = CFGRPL_REMOVEPART; fVerticalsAllowed = FALSE; fReplaceModeFound = TRUE; } else // double replace mode found: throw BSConfigExcpt(CFGEXCPT_DOUBLEREPLACEMODE, ustrConfigSys); } // now parse vertical modifiers BOOL fVerticalFound = FALSE; if (strhistr(pModifiers, "ADDTOP")) { _iVertical = CFGVRT_TOP; fVerticalFound = TRUE; } PSZ p2; if ((p2 = strhistr(pModifiers, "ADDAFTER("))) { if (!fVerticalFound) { PSZ pEndOfSearch; if (!(pEndOfSearch = strchr(p2, ')'))) { ustring ustr; ustr.assignUtf8(p2); throw BSConfigExcpt(CFGEXCPT_INVALIDSEARCH, ustr); } _ustrVerticalSearchString.assignUtf8( p2 + 9, // strlen("ADDAFTER(") pEndOfSearch); // excluding that char _iVertical = CFGVRT_AFTER; fVerticalFound = TRUE; } else { ustring ustr; ustr.assignUtf8(p2); throw BSConfigExcpt(CFGEXCPT_INVALIDVERTICAL, ustr); } } if ((p2 = strhistr(pModifiers, "ADDBEFORE("))) { if (!fVerticalFound) { PSZ pEndOfSearch; if (!(pEndOfSearch = strchr(p2, ')'))) { ustring ustr; ustr.assignUtf8(p2); throw BSConfigExcpt(CFGEXCPT_INVALIDSEARCH, ustr); } _ustrVerticalSearchString.assignUtf8(p2 + 10, // strlen("ADDBEFORE(") pEndOfSearch); // excluding that char _iVertical = CFGVRT_BEFORE; fVerticalFound = TRUE; } else { ustring ustr; ustr.assignUtf8(p2); throw BSConfigExcpt(CFGEXCPT_INVALIDVERTICAL, ustr); } } // finally check if vertical modifier is allowed at all if ( (fVerticalFound) && (!fVerticalsAllowed) ) throw BSConfigExcpt(CFGEXCPT_INVALIDVERTICAL, ustrConfigSys); } // end elseif (!pSep) } /* *@@ DescribeType: * describes the current manipulator type to the GUI. * *@@added V0.9.2 (2000-02-19) [umoeller] */ const char* BSCfgSysManip::DescribeType() { return ("CONFIG.SYS manipulation"); } /* *@@ DescribeData: * describes the current manipulator data to the GUI. * *@@added V0.9.2 (2000-02-19) [umoeller] *@@changed V0.9.5 (2000-08-26) [umoeller]: UNIQUE wasn't reported right *@@changed V0.9.18 (2002-03-08) [umoeller]: now returning ustring */ ustring BSCfgSysManip::DescribeData() { ustring str; BOOL fAddSpace = TRUE; switch (_iReplaceMode) { case CFGRPL_UNIQUE: str.appendUtf8("UNIQUE"); if (_ustrUniqueSearchString2()) { str.appendUtf8("("); str.append(_ustrUniqueSearchString2); str.appendUtf8(")"); } break; case CFGRPL_ADDLEFT: str.appendUtf8("ADDLEFT"); break; case CFGRPL_ADDRIGHT: str.appendUtf8("ADDRIGHT"); break; case CFGRPL_REMOVELINE: str.appendUtf8("REMOVELINE"); break; case CFGRPL_REMOVEPART: str.appendUtf8("REMOVEPART"); break; default: fAddSpace = FALSE; break; } if (fAddSpace) str.appendUtf8(" "); switch (_iVertical) { case CFGVRT_TOP: str.appendUtf8("ADDTOP "); break; case CFGVRT_BEFORE: str.appendUtf8("BEFORE("); str.append(_ustrVerticalSearchString); str.appendUtf8(") "); break; case CFGVRT_AFTER: str.appendUtf8("AFTER "); str.append(_ustrVerticalSearchString); str.appendUtf8(") "); break; } str.append(_ustrNewLine); return (str); } /* *@@ AddToUndoList: * static helper method to create BSCfgSysManip * instances from the logger instance that was * previously used with BSConfigSys::Manipulate. * * The new BSCfgSysManip objects are appended to * the specified list and are exact opposites * to the BSCfgSysManip objects that were stored * in the logger. That is, if the logger registered * that something was added to CONFIG.SYS, we * create an object which removes that text again, * and vice versa. * * Use this method to undo CONFIG.SYS changes * and pass the objects on the list to BSConfigSys::Manipulate, * since there is no corresponding "undo" class * for BSConfigSys. * * See BSCfgSysManip::BSCfgSysManip for an example usage. * * This returns the number of items created. * * Throws: * -- BSConfigExcpt. */ int BSCfgSysManip::AddToUndoList(list &List, BSCfgSysDoneLogger &logger) { // the logger in this case has a list of special PSZs, // where the first three characters of each entry // signify to us what was changed in CONFIG.SYS PSZ pszLogStart = logger._pabLogString, pszLogThis = pszLogStart; int iCount = 0; while (pszLogThis < pszLogStart + logger._cbLogString) { ULONG cbLogThis = strlen(pszLogThis); string strNewModifiers; // now check what we have in the log; the // first three characters (followed by a // space) signify what happened: // -- "DLL": deleted line // -- "DLP": deleted part of line // -- "NWL": added an all new line // -- "NWP": added a new part to an existing line if (memicmp(pszLogThis, "DLL ", 4) == 0) { // line was deleted: re-insert that line (UNIQUE mode) // strNewModifiers = "UNIQUE"; } else if (memicmp(pszLogThis, "DLP ", 4) == 0) { // part of line was deleted: re-insert that line (ADDRIGHT mode) strNewModifiers = "ADDRIGHT"; } else if (memicmp(pszLogThis, "NWL ", 4) == 0) { // line was added: remove that whole line strNewModifiers = "REMOVELINE"; // #### no, no, no!!! this removes an entire line... } else if (memicmp(pszLogThis, "NWP ", 4) == 0) // part of line was added: remove that part strNewModifiers = "REMOVEPART"; else { // none of the above: error ustring ustr; ustr.assignUtf8(pszLogThis); throw BSConfigExcpt(CFGEXCPT_PARSELOG, ustr); } // something found: compose attribute string for manipulator PSZ pszNewAttrs = (PSZ)malloc(strlen(pszLogThis) + strNewModifiers.size() + 30); sprintf(pszNewAttrs, "%s | %s", pszLogThis + 4, // stuff after "DLL " or whatever strNewModifiers.c_str()); // add the undo manipulator to the _front_ of the // list so that items are undone in reverse order // (because if a line was replaced originally, // we first have a "delete line" and then an // "add line" entry in the log) ustring ustr; ustr.assignUtf8(pszNewAttrs); List.push_front(new BSCfgSysManip(ustr)); free(pszNewAttrs); pszLogThis += cbLogThis + 1; // go beyond null byte iCount++; } return (iCount); } /* ****************************************************************** * * BSConfigSys class * ********************************************************************/ /* *@@ BSConfigSys: * the constructor, which loads the current CONFIG.SYS * file from OS/2 boot drive into instance memory. * * Throws: * -- BSConfigExcpt. * *@@changed V0.9.7 (2001-01-15) [umoeller]: now using csysLoadConfigSys *@@changed WarpIN V1.0.9 (2006-02-16) [pr]: added _pszContentOld */ BSConfigSys::BSConfigSys() : BSRoot(tBSConfigSys) { _pszContent = _pszContentOld = NULL; /* sprintf(_szFilename, "%c:\\config.sys", doshQueryBootDrive()); // now read CONFIG.SYS file to initialize the dlg items APIRET arc = doshLoadTextFile(_szFilename, &_pszContent); */ APIRET arc = csysLoadConfigSys(NULL, // default CONFIG.SYS &_pszContent); if (arc != NO_ERROR) throw BSConfigExcpt(CFGEXCPT_OPEN, arc); else _pszContentOld = strhdup(_pszContent, NULL); _fDirty = FALSE; } /* *@@ ~ConfigSys: * the destructor. * * This does _not_ write the file. Use BSConfigSys::Flush() * before deleting an instance of this. * *@@changed WarpIN V1.0.9 (2006-02-16) [pr]: added _pszContentOld */ BSConfigSys::~BSConfigSys() { if (_pszContent) free(_pszContent); if (_pszContentOld) free(_pszContentOld); } /* *@@ Manipulate: * this monster method changes the data in our memory copy of * CONFIG.SYS according to the BSCfgSysManip object, which you must * have created before. See BSCfgSysManip::BSCfgSysManip for a * description of this usage. * * This also takes a BSCfgSysDoneLogger as input where all the * changes made to the CONFIG.SYS memory copy are logged. This * logger can then be passed to BSConfigSys::Undo to have the * changes undone again. * * Call BSConfigSys::Flush to have CONFIG.SYS written back to disk. * * Returns NO_ERROR if everything was OK or an error code. * *@@changed V0.9.18 (2002-03-08) [umoeller]: adjusted for Unicode; added codec */ int BSConfigSys::Manipulate(BSUniCodec &codecProcess, // in: codec for process codepage BSCfgSysManip &Manip, // in: manipulator object BSCfgSysDoneLogger *pLogger, // in: logger object to append items to (can be NULL) BSFileLogger *pLogFile) // in: file logger (log file), can be NULL { int irc = 0; XSTRING strLogger; xstrInit(&strLogger, 0); CONFIGMANIP cm; memset(&cm, 0, sizeof(cm)); cm.iReplaceMode = Manip._iReplaceMode; string strUniqueSearchString2(&codecProcess, Manip._ustrUniqueSearchString2); cm.pszUniqueSearchString2 = strUniqueSearchString2.c_str(); cm.iVertical = Manip._iVertical; string strVerticalSearchString(&codecProcess, Manip._ustrVerticalSearchString); cm.pszVerticalSearchString = strVerticalSearchString.c_str(); string strNewLine(&codecProcess, Manip._ustrNewLine); cm.pszNewLine = strNewLine.c_str(); irc = csysManipulate(&_pszContent, &cm, &_fDirty, &strLogger); // now parse the logger... if (strLogger.ulLength) { // something to be logged: PSZ p = strLogger.psz; while (p) { PSZ pEOL; if ( (pEOL = strhFindEOL(p, NULL)) && (*pEOL) ) { string strTemp(p, pEOL); if (strTemp()) { if (pLogFile) // write to log file pLogFile->Write("Updated CONFIG.SYS: \"%s\"", strTemp.c_str()); if (pLogger) { // write to "done" logger: ustring ustr(&codecProcess, strTemp); pLogger->Append(ustr); } } else break; p = pEOL + 1; } else break; } } xstrClear(&strLogger); return (irc); } /* *@@ Flush: * this rewrites the CONFIG.SYS file on disk with the * data we have in memory. * * This makes a backup copy in "CONFIG.003" style if * (pszBackup != NULL). See doshCreateBackupFileName * for details. * * Returns: * * -- 0: contents were dirty, written back to disk. * * -- 1: no error, but contents were clean, so no write-back * was needed. * * Throws: * -- BSConfigExcpt(CFGEXCPT_WRITE, APIRET). * *@@changed V0.9.3 (2000-05-03) [umoeller]: added more error checking *@@changed V0.9.3 (2000-05-12) [umoeller]: added pszBackup *@@changed V0.9.6 (2000-10-27) [umoeller]: added check if contents are dirty *@@changed WarpIN V1.0.9 (2006-02-16) [pr]: added _pszContentOld */ int BSConfigSys::Flush(string *pstrBackup, // in/out: create backup? BSFileLogger *pLogFile) // in: file logger (log file), can be NULL const { int irc = 0; // WarpIN V1.0.9 (2006-02-16) [pr]: Check for content modification @@fixes 269 if ( _fDirty && strhcmp (_pszContent, _pszContentOld) ) { PSZ pszBackup = NULL; CHAR szBackup[CCHMAXPATH]; if (pstrBackup) pszBackup = szBackup; APIRET arc = csysWriteConfigSys(NULL, // default _szFilename, _pszContent, pszBackup); // create backup if (arc) { if (pLogFile) pLogFile->Write("Error %d occurred writing CONFIG.SYS back to disk", arc); throw BSConfigExcpt(CFGEXCPT_WRITE, arc); } if (pLogFile) pLogFile->Write("CONFIG.SYS file written back to disk, backup is \"%s\"", pszBackup); if (pstrBackup) pstrBackup->assign(szBackup); } else irc = 1; return (irc); } /* ****************************************************************** * * BSRegisterClass class * ********************************************************************/ /* *@@ BSRegisterClass: * this constructor translates a REGISTERCLASS attribute (as used * in the PCK tag and stored in the database) into the BSRegisterClass * instance data. * * Syntax: * + REGISTERCLASS="classname|dllpath" * * Throws: * -- BSConfigExcpt. */ BSRegisterClass::BSRegisterClass(const ustring &ustrRegisterClass) : BSConfigBase(CFGT_REGISTERCLASS, tBSRegisterClass) { // find separator PCSZ pcszRegisterClass = ustrRegisterClass.GetBuffer(); PCSZ pBegin; if (!(pBegin = pcszRegisterClass)) throw BSConfigExcpt(REGEXCPT_SYNTAX, ustrRegisterClass); PCSZ pEnd; if (!(pEnd = strchr(pBegin, '|'))) throw BSConfigExcpt(REGEXCPT_SYNTAX, ustrRegisterClass); // strip trailing spaces PCSZ pEnd2 = pEnd; while ((*pEnd2 == ' ') && (pEnd2 > pcszRegisterClass)) pEnd2--; _ustrClassName.assignUtf8(pBegin, pEnd2); if (!_ustrClassName()) throw BSConfigExcpt(REGEXCPT_SYNTAX, ustrRegisterClass); pBegin = pEnd + 1; // strip leading spaces while ((*pBegin) && (*pBegin == ' ')) pBegin++; _ustrModule.assignUtf8(pBegin); if (!_ustrModule()) throw BSConfigExcpt(REGEXCPT_SYNTAX, ustrRegisterClass); } /* *@@ DescribeType: * describes the current manipulator type to the GUI. * *@@added V0.9.9 (2001-03-27) [umoeller] */ const char* BSRegisterClass::DescribeType() { return ("WPS class registration"); } /* *@@ DescribeData: * describes the current manipulator data to the GUI. * *@@added V0.9.9 (2001-03-27) [umoeller] *@@changed V0.9.18 (2002-03-08) [umoeller]: now returning ustring */ ustring BSRegisterClass::DescribeData() { ustring str = _ustrClassName; str.appendUtf8(" in "); str += _ustrModule; return (str); } /* *@@ IsRegistered: * this returns TRUE if a class of the same WPS class name * as this instance is already registered with the WPS. * In that case, the module of the registered class is * copied into strModule. * * This throws a BSConfigExcpt with REGEXCPT_QUERYCLASSLIST * if the class list could not be queried. * *@@changed V0.9.18 (2002-03-08) [umoeller]: now using string for buffer, added codec */ bool BSRegisterClass::IsRegistered(BSUniCodec &codecProcess, // in: codec for process codepage ustring &ustrModule) const { BOOL fInstalled = FALSE; PBYTE pClassList, pClassThis = 0; if (pClassList = winhQueryWPSClassList()) { string strClassName(&codecProcess, _ustrClassName); if ((pClassThis = winhQueryWPSClass(pClassList, strClassName.c_str()))) { fInstalled = TRUE; ustrModule.assignCP(&codecProcess, ((POBJCLASS)pClassThis)->pszModName); } free(pClassList); } else throw BSConfigExcpt(REGEXCPT_QUERYCLASSLIST, 0); return (fInstalled); } /* *@@ Register: * this attempts to register the class. * * If (fReplace == TRUE), we do not call * BSRegisterClass::IsRegistered before registering * this, i.e. we will always register the class, * even if it's already registered. * * If (fReplace == FALSE) and BSRegisterClass::IsRegistered * returned TRUE, we throw a BSConfigExcpt with * REGEXCPT_ALREADYREGISTERED and pszSubstr set to the * file name of the registered DLL. Note that IsRegistered might * in turn throw BSConfigExcpt with REGEXCPT_QUERYCLASSLIST. * * If registering the class failed, this throws a * BSConfigExcpt with REGEXCPT_REGISTER and iData set * to the APIRET of winhRegisterClass. * * This method can take a BSDoneLoggerBase object as input, which * can later be used with the BSDeregisterClass::AddToUndoList * static class method to easily create a list of BSDeregisterClass * objects to undo the changes made. * * Example usage (exception handling omitted): + BSRegisterDoneLogger logger; + // create logger instance + BSRegisterClass RegClass("XFolder|C:\XFOLDER\XFLDR.DLL"); + // create BSRegisterClass instance: + RegClass.Register(TRUE, logger); + ... // register more classes with the same logger + + // now create undo list + list List; + BSDeregisterClass::AddToUndoList(List, logger); + list::iterator DeregStart = List.begin(), + DeregEnd = List.end(); + for (; DeregStart != DeregEnd; DeregStart++) + (**DeregStart).Deregister; * *@@changed V0.9.18 (2002-03-08) [umoeller]: added codec *@@changed V0.9.18 (2002-03-08) [umoeller]: added missing log file entry */ int BSRegisterClass::Register(BSUniCodec &codecProcess, // in: codec for process codepage bool fReplace, BSRegisterDoneLogger *pLogger, // in: logger object to append items to (can be NULL) BSFileLogger *pLogFile) // in: file logger (log file), can be NULL const { int irc = 0; if (fReplace == FALSE) { ustring ustrModule; if (IsRegistered(codecProcess, ustrModule)) throw BSConfigExcpt(REGEXCPT_ALREADYREGISTERED, ustrModule); } CONVERT(&codecProcess, ClassName); CONVERT(&codecProcess, Module); CHAR szBuf[1000]; APIRET arc = winhRegisterClass(pcszClassName, pcszModule, szBuf, sizeof(szBuf)); // always record in logger, even if failed, // because the class is in the class list anyway if (pLogger) pLogger->Append(_ustrClassName); if (arc != NO_ERROR) { // this was missing V0.9.18 (2002-03-08) [umoeller] if (pLogFile) pLogFile->Write("Error %d registering WPS class \"%s\"", arc, pcszClassName); throw BSConfigExcpt(REGEXCPT_REGISTER, arc); } // this was missing V0.9.18 (2002-03-08) [umoeller] if (pLogFile) pLogFile->Write("Registered WPS class \"%s\"", pcszClassName); return (irc); } /* ****************************************************************** * * BSDeregisterClass class * ********************************************************************/ /* *@@ BSDeregisterClass: * the constructor, which in this case takes a simple * PSZ class name as input. */ BSDeregisterClass::BSDeregisterClass(const ustring &ustrDeregisterClass) : BSConfigBase(CFGT_DEREGISTERCLASS, tBSDeregisterClass) { _ustrClassName = ustrDeregisterClass; } /* *@@ DescribeType: * describes the current manipulator type to the GUI. * *@@added V0.9.2 (2000-02-19) [umoeller] */ const char* BSDeregisterClass::DescribeType() { return ("WPS class deregistration"); } /* *@@ DescribeData: * describes the current manipulator data to the GUI. * *@@added V0.9.2 (2000-02-19) [umoeller] *@@changed V0.9.18 (2002-03-08) [umoeller]: now returning ustring */ ustring BSDeregisterClass::DescribeData() { return (_ustrClassName); } /* *@@ Deregister: * this attempts to deregister the class. * * Throws: * -- BSConfigExcpt. * *@@changed V0.9.18 (2002-03-08) [umoeller]: added codec */ int BSDeregisterClass::Deregister(BSUniCodec &codecProcess, // in: codec for process codepage BSFileLogger *pLogFile) // in: file logger (log file), can be NULL const { int irc = 0; CONVERT(&codecProcess, ClassName); // creates local string strXXX, PCSZ pcszXXX if (!WinDeregisterObjectClass(pcszClassName)) { if (pLogFile) pLogFile->Write("An error occurred deregistering WPS class \"%s\"", pcszClassName); throw BSConfigExcpt(REGEXCPT_DEREGISTER, _ustrClassName); } if (pLogFile) pLogFile->Write("Deregistered WPS class \"%s\"", pcszClassName); return (irc); } /* *@@ AddToUndoList: * static helper method to create BSDeregisterClass * instances from the logger instance that was * previously used with BSRegisterClass::Register. * * The new BSDeregisterClass objects are appended to * the specified list. * * See BSRegisterClass::Register for an example usage. * * This returns the number of items created. */ int BSDeregisterClass::AddToUndoList(list &List, BSRegisterDoneLogger &logger) { // the logger in this case has a simple list of PSZs with all // the class names that were registered, each of which is terminated // with a null character PSZ pszLogStart = logger._pabLogString, pszLogThis = pszLogStart; int iCount = 0; while (pszLogThis < pszLogStart + logger._cbLogString) { ULONG cbLogThis = strlen(pszLogThis); ustring ustr; ustr.assignUtf8(pszLogThis); List.push_back(new BSDeregisterClass(ustr)); pszLogThis += cbLogThis + 1; // go beyond null byte iCount++; } return (iCount); } /* ****************************************************************** * * BSReplaceClass class * ********************************************************************/ /* *@@ BSReplaceClassBase: * this constructor translates a REPLACECLASS attribute (as used * in the PCK tag and stored in the database) into the BSReplaceClass * instance data. * * Syntax: * + REPLACECLASS="oldclassname|newclassname" * *@@changed V0.9.19 (2002-05-07) [umoeller]: changed inheritance hierarchy here */ BSReplaceClassBase::BSReplaceClassBase(const ustring &ustrReplaceClass, ULONG cfgt, BSClassID &Class) : BSConfigBase(cfgt, Class) { PCSZ pcszReplaceClass = ustrReplaceClass.GetBuffer(); // find separator PCSZ pBegin; if (!(pBegin = pcszReplaceClass)) throw BSConfigExcpt(REGEXCPT_SYNTAX, ustrReplaceClass); PCSZ pEnd; if (!(pEnd = strchr(pBegin, '|'))) throw BSConfigExcpt(REGEXCPT_SYNTAX, ustrReplaceClass); // strip trailing spaces PCSZ pEnd2 = pEnd; while ((*pEnd2 == ' ') && (pEnd2 > pcszReplaceClass)) pEnd2--; _ustrOldClassName.assignUtf8(pBegin, pEnd2); if (!_ustrOldClassName()) throw BSConfigExcpt(REGEXCPT_SYNTAX, ustrReplaceClass); pBegin = pEnd + 1; // strip leading spaces while ((*pBegin) && (*pBegin == ' ')) pBegin++; _ustrNewClassName.assignUtf8(pBegin); if (!_ustrNewClassName()) throw BSConfigExcpt(REGEXCPT_SYNTAX, ustrReplaceClass); } /* *@@ DescribeType: * describes the current manipulator type to the GUI. * *@@added V0.9.2 (2000-02-19) [umoeller] */ const char* BSReplaceClass::DescribeType() { return ("WPS class replacement"); } /* *@@ DescribeData: * describes the current manipulator data to the GUI. * *@@added V0.9.2 (2000-02-19) [umoeller] *@@changed V0.9.5 (2000-08-26) [umoeller]: UNIQUE wasn't reported right *@@changed V0.9.18 (2002-03-08) [umoeller]: now returning ustring */ ustring BSReplaceClass::DescribeData() { ustring str = _ustrOldClassName; str.appendUtf8(" with "); str += _ustrNewClassName; return (str); } /* *@@ Replace: * this attempts to replace the class or undo * an existing replacement (if fReplace == FALSE). * * If replacing the class failed, this throws a * BSConfigExcpt with REGEXCPT_REPLACE. * * Example usage (exception handling omitted): + BSReplaceDoneLogger logger; + // create logger instance + BSReplaceClass ReplClass("WPFolder;XFolder"); + // create BSReplaceClass instance: + ReplClass.Replace(TRUE, logger); + ... // replace more classes with the same logger + + // now create undo list + list List; + BSUnreplaceClass::AddToUndoList(List, logger); + list::iterator UnrplStart = List.begin(), + UnrplEnd = List.end(); + for (; UnrplStart != UnrplEnd; UnrplStart++) + (**UnrplStart).Unreplace; * *@@changed V0.9.1 (2000-01-05) [umoeller]: finally added logging, which was missing *@@changed V0.9.9 (2001-03-30) [umoeller]: fixed wrong logging if replace failed *@@changed V0.9.18 (2002-03-08) [umoeller]: added codec */ int BSReplaceClass::Replace(BSUniCodec &codecProcess, // in: codec for process codepage BSReplaceDoneLogger *pLogger, // in: logger object to append items to (can be NULL) BSFileLogger *pLogFile) // in: file logger (log file), can be NULL const { int irc = 0; CONVERT(&codecProcess, OldClassName); CONVERT(&codecProcess, NewClassName); BOOL brc = WinReplaceObjectClass(pcszOldClassName, pcszNewClassName, TRUE); // replace // always record in logger, even if failed, // because the class is in the class list anyway // V0.9.9 (2001-03-30) [umoeller] if (pLogger) { ustring ustr2Append(_ustrOldClassName); ustr2Append.appendUtf8("|"); ustr2Append += _ustrNewClassName; pLogger->Append(ustr2Append); } if (!brc) { if (pLogFile) pLogFile->Write("An error occurred replacing WPS class \"%s\" with \"%s\"", pcszOldClassName, pcszNewClassName); throw BSConfigExcpt(REGEXCPT_REPLACE, 0); } // success: if (pLogFile) pLogFile->Write("Replaced WPS class \"%s\" with \"%s\"", pcszOldClassName, pcszNewClassName); return (irc); } /* ****************************************************************** * * BSUnreplaceClass class * ********************************************************************/ /* *@@ DescribeType: * describes the current manipulator type to the GUI. * *@@added V0.9.2 (2000-02-19) [umoeller] */ const char* BSUnreplaceClass::DescribeType() { return ("WPS class un-replacement"); } /* *@@ DescribeData: * describes the current manipulator data to the GUI. * *@@added V0.9.2 (2000-02-19) [umoeller] *@@changed V0.9.18 (2002-03-08) [umoeller]: now returning ustring */ ustring BSUnreplaceClass::DescribeData() { ustring ustr = _ustrOldClassName; ustr.appendUtf8(" with "); ustr += _ustrNewClassName; return (ustr); } /* *@@ Unreplace: * this attempts to unreplace the class. * Throws a BSConfigExcpt upon failure. * *@@changed V0.9.18 (2002-03-08) [umoeller]: added codec */ int BSUnreplaceClass::Unreplace(BSUniCodec &codecProcess, // in: codec for process codepage, BSFileLogger *pLogFile) // in: file logger (log file), can be NULL const { int irc = 0; CONVERT(&codecProcess, OldClassName); CONVERT(&codecProcess, NewClassName); if (!WinReplaceObjectClass(pcszOldClassName, pcszNewClassName, FALSE)) { if (pLogFile) pLogFile->Write("Error undoing WPS class replacement of \"%s\" with \"%s\"", pcszOldClassName, pcszNewClassName); throw BSConfigExcpt(REGEXCPT_UNREPLACE, 0); } // success: if (pLogFile) pLogFile->Write("Undid WPS class replacement of \"%s\" with \"%s\"", pcszOldClassName, pcszNewClassName); return (irc); } /* *@@ AddToUndoList: * static helper method to create BSUnreplaceClass * instances from the logger instance that was * previously used with BSRegisterClass::Register. * * The new BSUnreplaceClass objects are appended to * the specified list. * * See BSReplaceClass::Replace for an example usage. * * This returns the number of items created. */ int BSUnreplaceClass::AddToUndoList(list &List, BSReplaceDoneLogger &logger) { // the logger in this case has a simple list of PSZs with all // the class names that were registered, each of which is terminated // with a null character PSZ pszLogStart = logger._pabLogString, pszLogThis = pszLogStart; int iCount = 0; while (pszLogThis < pszLogStart + logger._cbLogString) { ULONG cbLogThis = strlen(pszLogThis); ustring ustr; ustr.assignUtf8(pszLogThis); List.push_back(new BSUnreplaceClass(ustr)); // this calls the inherited BSReplaceClass // constructor, which will parse the thing pszLogThis += cbLogThis + 1; // go beyond null byte iCount++; } return (iCount); } /* ****************************************************************** * * BSCreateWPSObject class * ********************************************************************/ /* *@@ BSCreateWPSObject: * this constructor translates a CREATEOBJECT attribute (as used * in the PCK tag and stored in the database) into the BSCreateWPSObject * instance data. * * Syntax: * + CREATEOBJECT="[REPLACE] classname|title|location[|config]]" + CREATEOBJECT="[FAIL] classname|title|location[|config]]" * * Throws: * -- BSConfigExcpt. * *@@changed V0.9.3 (2000-04-08) [umoeller]: "REPLACE" wasn't evaluated; fixed. Thanks, Cornelis Bockemhl. *@@changed V0.9.15 (2001-08-26) [umoeller]: added WPOEXCPT_INVALIDLOCATION checks *@@changed V0.9.19 (2002-04-14) [umoeller]: REPLACE was never detected, fixed *@@changed V1.0.14 (2006-12-03) [pr]: added FAIL mode @@fixes 893 */ BSCreateWPSObject::BSCreateWPSObject(const ustring &ustrCreateObject) : BSConfigBase(CFGT_CREATEOBJECT, tBSCreateWPSObject) { PCSZ pcszCreateObject = ustrCreateObject.GetBuffer(); // extract class name PCSZ pBegin; if (!(pBegin = pcszCreateObject)) throw BSConfigExcpt(WPOEXCPT_NOCLASS, ustrCreateObject); if (!memcmp(pBegin, "REPLACE ", 8)) { _ulFlags = CO_REPLACEIFEXISTS; // V1.0.14 (2006-12-03) [pr] pBegin += 8; while (*pBegin == ' ') pBegin++; } else if (!memcmp(pBegin, "FAIL ", 5)) // V1.0.14 (2006-12-03) [pr] { _ulFlags = CO_FAILIFEXISTS; pBegin += 5; while (*pBegin == ' ') pBegin++; } else _ulFlags = CO_UPDATEIFEXISTS; // V1.0.14 (2006-12-03) [pr] PSZ pEnd; if (pEnd = strchr(pBegin, '|')) { _ustrClassName.assignUtf8(pBegin, pEnd); // extract title pBegin = pEnd + 1; if (pEnd = strchr(pBegin, '|')) { _ustrTitle.assignUtf8(pBegin, pEnd); // extract location pBegin = pEnd + 1; if (pEnd = strchr(pBegin, '|')) { // yet another separator found: we then have a config parameter _ustrLocation.assignUtf8(pBegin, pEnd); // extract config (rest of pszCreateObject) pBegin = pEnd + 1; _ustrSetupString.assignUtf8(pBegin); } else // no separator after "location" found: // duplicate whole string _ustrLocation.assignUtf8(pBegin); if (!_ustrLocation()) throw BSConfigExcpt(WPOEXCPT_NOLOCATION, ustrCreateObject); // check if the location is correctly in <> brackets // V0.9.15 (2001-08-26) [umoeller] PCSZ pcszLocation = _ustrLocation.GetBuffer(); if ( (pcszLocation[0] == '<') && (pcszLocation[_ustrLocation.length() - 1] != '>') ) throw BSConfigExcpt(WPOEXCPT_INVALIDLOCATIONSTRING, ustrCreateObject); } else throw BSConfigExcpt(WPOEXCPT_NOTITLE, ustrCreateObject); } else throw BSConfigExcpt(WPOEXCPT_NOCLASS, ustrCreateObject); } /* *@@ DescribeType: * describes the current manipulator type to the GUI. * *@@added V0.9.9 (2001-03-27) [umoeller] */ const char* BSCreateWPSObject::DescribeType() { return ("WPS object"); } /* *@@ DescribeData: * describes the current manipulator data to the GUI. * *@@added V0.9.9 (2001-03-27) [umoeller] *@@changed V0.9.18 (2002-03-08) [umoeller]: now returning ustring */ ustring BSCreateWPSObject::DescribeData() { ustring ustr = _ustrClassName; ustr.appendUtf8(" "); ustr += _ustrTitle; ustr.appendUtf8(" "); ustr += _ustrLocation; ustr.appendUtf8(" "); ustr += _ustrSetupString; return (ustr); } /* *@@ CreateObject: * this attempts to create the WPS object * with the instance data. * * Throws a BSConfigExcpt with WPOEXCPT_CREATE * if this fails. * *@@changed V0.9.18 (2002-03-08) [umoeller]: added codec *@@changed V1.0.14 (2006-12-03) [pr]: added FAIL mode @@fixes 893 */ void BSCreateWPSObject::CreateObject(BSUniCodec &codecProcess, // in: codec for process codepage, BSWPSObjectsDoneLogger *pLogger, // in: logger object to append items to (can be NULL) BSFileLogger *pLogFile) // in: file logger (log file), can be NULL { CONVERT(&codecProcess, ClassName); // creates local string strXXX, PCSZ pcszXXX CONVERT(&codecProcess, Title); // creates local string strXXX, PCSZ pcszXXX CONVERT(&codecProcess, SetupString); // creates local string strXXX, PCSZ pcszXXX CONVERT(&codecProcess, Location); // creates local string strXXX, PCSZ pcszXXX // check if the target folder exists, this is the most common problem // if WinCreateObject fails // V0.9.19 (2002-06-15) [umoeller] if (!WinQueryObject(pcszLocation)) { if (pLogFile) pLogFile->Write("Error creating WPS object \"%s\", class \"%s\", location \"%s\", setup \"%s\": Invalid target folder", pcszTitle, pcszClassName, pcszLocation, pcszSetupString); throw BSConfigExcpt(WPOEXCPT_CREATE_BADLOCATION, _ustrTitle); } if (!(_hobj = WinCreateObject(pcszClassName, pcszTitle, pcszSetupString, pcszLocation, _ulFlags))) { if (_ulFlags == CO_FAILIFEXISTS) // V1.0.14 (2006-12-03) [pr] { if (pLogFile) pLogFile->Write("WPS object already exists \"%s\", class \"%s\", location \"%s\", setup \"%s\"", pcszTitle, pcszClassName, pcszLocation, pcszSetupString); } else { if (pLogFile) pLogFile->Write("Error creating WPS object \"%s\", class \"%s\", location \"%s\", setup \"%s\"", pcszTitle, pcszClassName, pcszLocation, pcszSetupString); throw BSConfigExcpt(WPOEXCPT_CREATE, _ustrTitle); } } else { // V1.0.5 (2005-02-17) [pr] // We now save the object synchronously to prevent objects getting lost if we // restart the WPS imminently due to a class registration for example. @@fixes 634. WinSaveObject (_hobj, FALSE); if (pLogFile) { pLogFile->Write("Created WPS object \"%s\", class \"%s\", location \"%s\", setup \"%s\"", pcszTitle, pcszClassName, pcszLocation, pcszSetupString); pLogFile->Write("HOBJECT is 0x%lX", _hobj); } } // store the object ID in the logger PCSZ pObjectID; if ( // do we have a setup string at all? (pObjectID = _ustrSetupString.GetBuffer()) // does it contain an object ID? && (pObjectID = strhistr(pObjectID, "OBJECTID=<")) ) { PCSZ pBegin = pObjectID + 9; // points to '<' now PCSZ pEnd; if (pEnd = strchr(pBegin, '>')) { ustring ustrObjectID; ustrObjectID.assignUtf8(pBegin, pEnd + 1); if (pLogger) pLogger->Append(ustrObjectID); } } } /* ****************************************************************** * * BSDeleteWPSObject class * ********************************************************************/ /* *@@ BSDeleteWPSObject: * the constructor, which in this case takes a simple * PSZ object ID as input. */ BSDeleteWPSObject::BSDeleteWPSObject(const ustring &ustrID2Delete) : BSConfigBase(CFGT_DELETEOBJECT, tBSDeleteWPSObject) { _ustrObjectID = ustrID2Delete; } /* *@@ DescribeType: * describes the current manipulator type to the GUI. * *@@added V0.9.2 (2000-02-19) [umoeller] */ const char* BSDeleteWPSObject::DescribeType() { return ("WPS object deletion"); } /* *@@ DescribeData: * describes the current manipulator data to the GUI. * *@@added V0.9.2 (2000-02-19) [umoeller] *@@changed V0.9.18 (2002-03-08) [umoeller]: now returning ustring */ ustring BSDeleteWPSObject::DescribeData() { return (_ustrObjectID); } /* *@@ Delete: * this attempts to delete the object. * Throws a BSConfigExcpt upon failure. * *@@changed V0.9.18 (2002-03-08) [umoeller]: added codec *@@changed V1.0.18 (2008-09-27) [pr]: no error if object no longer exists @@fixes 1062 */ int BSDeleteWPSObject::Delete(BSUniCodec &codecProcess, BSFileLogger *pLogFile) // in: file logger (log file), can be NULL const { int irc = 0; CONVERT(&codecProcess, ObjectID); // creates local string strXXX, PCSZ pcszXXX HOBJECT hobj; if (!(hobj = WinQueryObject(pcszObjectID))) { if (pLogFile) pLogFile->Write("Error locating WPS object \"%s\"", pcszObjectID); } else if (!WinDestroyObject(hobj)) { if (pLogFile) pLogFile->Write("Error deleting WPS object \"%s\", HOBJECT was 0x%lX", pcszObjectID, hobj); throw BSConfigExcpt(WPOEXCPT_DELETEOBJECT, 0); } return (irc); } /* *@@ AddToUndoList: * static helper method to create BSDeleteWPSObject * instances from the logger instance that was * previously used with BSRegisterClass::Register. * * The new BSDeleteWPSObject objects are appended to * the specified list. * * This returns the number of items created. */ int BSDeleteWPSObject::AddToUndoList(list &List, BSWPSObjectsDoneLogger &logger) { // the logger in this case has a simple list of PSZs with all // the object IDs that were created, each of which is terminated // with a null character PSZ pszLogStart = logger._pabLogString, pszLogThis = pszLogStart; int iCount = 0; while (pszLogThis < pszLogStart + logger._cbLogString) { ULONG cbLogThis = strlen(pszLogThis); ustring ustr; ustr.assignUtf8(pszLogThis); List.push_back(new BSDeleteWPSObject(ustr)); pszLogThis += cbLogThis + 1; // go beyond null byte iCount++; } return (iCount); } /* ****************************************************************** * * BSClearProfile class * ********************************************************************/ /* *@@ DescribeType: * describes the current manipulator type to the GUI. * *@@added V0.9.2 (2000-02-19) [umoeller] */ const char* BSProfileBase::DescribeType() { return ("Profile data"); } /* *@@ DescribeData: * describes the current manipulator data to the GUI. * *@@added V0.9.2 (2000-02-19) [umoeller] *@@changed V0.9.18 (2002-03-08) [umoeller]: now returning ustring */ ustring BSProfileBase::DescribeData() { return (DescribePrfKey()); } /* *@@ DescribePrfKey: * returns a descriptive string describing the * member fields, a la "USER\app\key". * *@@added V0.9.1 (2000-02-12) [umoeller] */ ustring BSProfileBase::DescribePrfKey() const { ustring str(_ustrProfile); if (_ustrApplication()) { str.appendUtf8("\\"); str += _ustrApplication; if (_ustrKey()) { str.appendUtf8("\\"); str += _ustrKey; } } return (str); } /* *@@ DescribePrfKey: * *@@added V0.9.18 (2002-03-08) [umoeller] */ string BSProfileBase::DescribePrfKey(BSUniCodec &codecProcess) const { string str(&codecProcess, DescribePrfKey()); return str; } /* *@@ BSClearProfile: * * Syntax: * + CLEARPROFILE="profile\application[\key]" * *@@added V0.9.1 (2000-02-07) [umoeller] *@@changed V1.0.5 (2005-03-06) [pr]: Improved parsing of profiles with path names. @@fixes 633 */ BSClearProfile::BSClearProfile(const ustring &ustrClearProfile) : BSProfileBase(CFGT_CLEARPROFILE, tBSClearProfile) { PCSZ pcszClearProfile = ustrClearProfile.GetBuffer(); // get profile PCSZ pFirstBackslash; if (pFirstBackslash = strchr(pcszClearProfile, '\\')) { // extract profile _ustrProfile.assignUtf8(pcszClearProfile, pFirstBackslash); // V1.0.7 (2005-05-15) [pr]: Changed to case-insensitive compare as there are // a few broken packages out there if ( _ustrProfile.compareUtf8I("USER") && _ustrProfile.compareUtf8I("SYSTEM") ) { while (pFirstBackslash) { ULONG cbProfile = pFirstBackslash - pcszClearProfile; if ( (cbProfile > 4) && (!strnicmp(pFirstBackslash - 4, ".INI", 4)) ) { _ustrProfile.assignUtf8(pcszClearProfile, pFirstBackslash); break; } pFirstBackslash = strchr(pFirstBackslash + 1, '\\'); } } } if (pFirstBackslash) { // get application PCSZ pSecondBackslash; if (pSecondBackslash = strchr(pFirstBackslash + 2, '\\')) { // key specified: // extract application up to key _ustrApplication.assignUtf8(pFirstBackslash + 1, pSecondBackslash); // extract key (all the rest) _ustrKey.assignUtf8(pSecondBackslash + 1); } else // key not specified: _ustrApplication.assignUtf8(pFirstBackslash + 1); } if (!_ustrApplication()) // any error: throw BSConfigExcpt(PRFEXCPT_SYNTAX, ustrClearProfile); } /* *@@ Clear: * deletes the corresponding profile entry. * *@@added V0.9.5 (2000-08-26) [umoeller] *@@changed V0.9.18 (2002-03-08) [umoeller]: added codec *@@changed WarpIN V1.0.11 (2006-08-29) [pr]: fix entire app. delete @@fixes 833 */ int BSClearProfile::Clear(BSUniCodec &codecProcess, // in: codec for process codepage, HAB hab, BSFileLogger *pLogFile) // in: file logger (log file), can be NULL const { BOOL fOK = FALSE; HINI hini = NULLHANDLE, hini2Close = NULLHANDLE; if (!_ustrProfile.compareUtf8("USER")) hini = HINI_USER; else if (!_ustrProfile.compareUtf8("SYSTEM")) hini = HINI_SYSTEM; else { CONVERT(&codecProcess, Profile); hini = PrfOpenProfile(hab, pcszProfile); hini2Close = hini; } if (hini) { CONVERT(&codecProcess, Application); CONVERT(&codecProcess, Key); ULONG cb = 0; // WarpIN V1.0.11 (2006-08-29) [pr]: fix entire app. delete @@fixes 833 if (strlen(pcszKey) == 0) pcszKey = NULL; // does key exist? if (PrfQueryProfileSize(hini, pcszApplication, pcszKey, // can be NULL &cb)) { // key exists: if (PrfWriteProfileString(hini, pcszApplication, pcszKey, // can be NULL NULL)) fOK = TRUE; } else // key does not exist: fOK = TRUE; } if (!fOK) { string strClear = DescribePrfKey(codecProcess); if (pLogFile) pLogFile->Write("Error deleting profile key \"%s\"", strClear.c_str()); // error: throw BSConfigExcpt(PRFEXCPT_PRFERROR, DescribePrfKey()); } if (pLogFile) { string strClear = DescribePrfKey(codecProcess); pLogFile->Write("Deleted profile key \"%s\"", strClear.c_str()); } return (1); } /* *@@ AddToUndoList: * static helper method to create BSClearProfile * instances from the specified logger instance. * Note that this class method will be used both * for "CLEARPROFILE" tags specified in the script * as well as "WRITEPROFILE" tags which were actually * executed (written to a profile). * * The new BSClearProfile objects are appended to * the specified list. * * This returns the number of items created. */ int BSClearProfile::AddToUndoList(list &List, BSClearPrfAttrsLogger &logger) { // the logger in this case has a simple list of PSZs with all // the profile keys that were created, each of which is terminated // with a null character PSZ pszLogStart = logger._pabLogString, pszLogThis = pszLogStart; int iCount = 0; while (pszLogThis < pszLogStart + logger._cbLogString) { ULONG cbLogThis = strlen(pszLogThis); ustring ustr; ustr.assignUtf8(pszLogThis); List.push_back(new BSClearProfile(ustr)); pszLogThis += cbLogThis + 1; // go beyond null byte iCount++; } return (iCount); } /* ****************************************************************** * * BSWriteProfile class * ********************************************************************/ /* *@@ BSWriteProfile: * * Syntax: * + WRITEPROFILE="profile\application\key|string" * *@@added V0.9.1 (2000-02-07) [umoeller] *@@changed V0.9.19 (2002-05-07) [umoeller]: added missing class init, now writeprofiles work again *@@changed V1.0.5 (2005-03-06) [pr]: Improved parsing of profiles with path names. @@fixes 633 */ BSWriteProfile::BSWriteProfile(const ustring &ustrWriteProfile) : BSProfileBase(CFGT_WRITEPROFILE, tBSWriteProfile) { PCSZ pcszWriteProfile = ustrWriteProfile.GetBuffer(); // override class, this was still BSClearProfile due // to the constructor; this is also why writeprofiles // never worked V0.9.19 (2002-05-07) [umoeller] _Class = tBSWriteProfile; PCSZ pSlash; if (pSlash = strchr(pcszWriteProfile, '|')) { // copy write data (after '|') _ustrWriteString.assignUtf8(pSlash + 1); // get profile PCSZ pFirstBackslash; if (pFirstBackslash = strchr(pcszWriteProfile, '\\')) { // extract profile _ustrProfile.assignUtf8(pcszWriteProfile, pFirstBackslash); if ( _ustrProfile.compareUtf8("USER") && _ustrProfile.compareUtf8("SYSTEM") ) { while (pFirstBackslash) { ULONG cbProfile = pFirstBackslash - pcszWriteProfile; if ( (cbProfile > 4) && (!strnicmp(pFirstBackslash - 4, ".INI", 4)) && (pFirstBackslash < pSlash) ) { _ustrProfile.assignUtf8(pcszWriteProfile, pFirstBackslash); break; } pFirstBackslash = strchr(pFirstBackslash + 1, '\\'); } } } if (pFirstBackslash) { // get application PCSZ pSecondBackslash; if (pSecondBackslash = strchr(pFirstBackslash + 2, '\\')) { // extract application _ustrApplication.assignUtf8(pFirstBackslash + 1, pSecondBackslash); // extract key (up to '|') _ustrKey.assignUtf8(pSecondBackslash + 1, pSlash); } } } if (!_ustrKey()) // any error: throw BSConfigExcpt(PRFEXCPT_SYNTAX, ustrWriteProfile); } /* *@@ Write: * this actually writes data to a profile, all * as specified by the member variables. * * Note that this stores the "done" stuff in * a BSClearPrfAttrsLogger for undoing later. * * Throws: * -- BSConfigExcpt. * *@@added V0.9.1 (2000-02-07) [umoeller] *@@changed V0.9.18 (2002-03-08) [umoeller]: added codec */ int BSWriteProfile::Write(BSUniCodec &codecProcess, // in: codec for process codepage, HAB hab, // in: anchor block (for PrfOpenProfile) BSClearPrfAttrsLogger *pLogger, BSFileLogger *pLogFile) // in: file logger (log file), can be NULL const { BOOL fOK = FALSE; HINI hini = NULLHANDLE, hini2Close = NULLHANDLE; if (!_ustrProfile.compareUtf8("USER")) hini = HINI_USER; else if (!_ustrProfile.compareUtf8("SYSTEM")) hini = HINI_SYSTEM; else { CONVERT(&codecProcess, Profile); if (!(hini = PrfOpenProfile(hab, pcszProfile))) throw BSConfigExcpt(PRFEXCPT_PRFOPENPROFILE, _ustrProfile); hini2Close = hini; } if (hini) { CONVERT(&codecProcess, Application); CONVERT(&codecProcess, Key); CONVERT(&codecProcess, WriteString); if (PrfWriteProfileString(hini, pcszApplication, pcszKey, pcszWriteString)) { fOK = TRUE; } } // recompose string for BSClearProfile ustring ustrClear = DescribePrfKey(); string strClear = DescribePrfKey(codecProcess); if (fOK) { if (pLogFile) pLogFile->Write("Wrote profile key \"%s\"", strClear.c_str()); if (pLogger) pLogger->Append(ustrClear); } else { // error: if (pLogFile) pLogFile->Write("Error writing profile key \"%s\"", strClear.c_str()); throw BSConfigExcpt(PRFEXCPT_PRFERROR, DescribePrfKey()); } return 1; } /* ****************************************************************** * * BSExecute class * ********************************************************************/ /* *@@ BSExecute: * this constructor takes an EXECUTE attribute (of the PCK tag) * as input and sets up the instance data accordingly. * * Syntax: * + EXECUTE="[ context ] execfile params" * * Throws: * -- BSConfigExcpt. * *@@added V0.9.1 (2000-02-07) [umoeller] *@@changed V0.9.3 (2000-04-08) [umoeller]: "|" was a requirement, contradicting the docs; changed. Thanks, Cornelis Bockemhl. *@@changed V0.9.4 (2000-07-10) [umoeller]: _ulExecType wasn't always initialized; thanks Yuri Dario *@@changed V0.9.4 (2000-07-26) [umoeller]: now ORing the flags *@@changed V0.9.9 (2001-03-27) [umoeller]: fixed EXECUTE parameters */ BSExecute::BSExecute(const ustring &ustrExecute, BSClassID &Class) : BSConfigBase(CFGT_EXECUTE, Class) { BOOL fContextFound = FALSE, fContinue = TRUE; PCSZ pcszExecute = ustrExecute.GetBuffer(); PCSZ pSearch = (PSZ)pcszExecute; _ulExecType = 0; _arc = 0; // NO_ERROR while (fContinue) { // skip leading spaces while ( (*pSearch) && (*pSearch == ' ') ) pSearch++; if (strncmp(pSearch, "CONFIGSYS", 9) == 0) { _ulExecType |= CFGT_CFGSYS; fContextFound = TRUE; pSearch += 9; // strlen("CONFIGSYS"); } else if (strncmp(pSearch, "REGISTERCLASS", 13) == 0) { _ulExecType |= CFGT_REGISTERCLASS; fContextFound = TRUE; pSearch += 13; // strlen("REGISTERCLASS"); } else if (strncmp(pSearch, "CREATEOBJECT", 12) == 0) { _ulExecType |= CFGT_CREATEOBJECT; fContextFound = TRUE; pSearch += 12; // strlen("CREATEOBJECT"); } else // other: stop looping fContinue = FALSE; } // end while (fContinue) if (!(*pSearch)) throw BSConfigExcpt(EXEEXCPT_SYNTAX, ustrExecute); // pSearch now points to the first non-space // character which does not introduces a keyword; // check if this is a "|" separator if (*pSearch == '|') // skip '|' pSearch++; // skip following spaces while ( (*pSearch) && (*pSearch == ' ') ) pSearch++; // NOW we finally point to the executable if (!(*pSearch)) throw BSConfigExcpt(EXEEXCPT_SYNTAX, ustrExecute); // separate executable and parameters: // This can be separated by another '|', // or a space. // NOTE: The '|' separator is used by the "resolved" // logger, from which BSDeExecute instances are // created on de-install. But the documentation states // spaces should be used... as long as a user doesn't // use spaces in the (unresolved) filename, we're fine. // For example we get: // 1) on install: $(1)\install.cmd PARAMETER // works nicely, and $(1) may contain spaces. // 2) Logger stores C:\PATH1 WITH SPACES\install.cmd|PARAMETER // in database. // 3) on deinstall, BSDeExecutes get created, and // we now have nice separation and get PARAMETER // properly separated. PCSZ p; if (!(p = strchr(pSearch, '|'))) p = strchr(pSearch, ' '); if (p) { const char *pSep = p++; // V1.0.5 (2005-01-20) [pr]: @@fixes 624 // we have a space --> parameters: while ( (*p) && (*p == ' ') ) p++; if (*p) { _ustrParams.assignUtf8(p); _ustrExecutable.assignUtf8(pSearch, pSep); } else _ustrExecutable.assignUtf8(pSearch); } else _ustrExecutable.assignUtf8(pSearch); } /* *@@ DescribeType: * describes the current manipulator type to the GUI. * *@@added V0.9.9 (2001-03-27) [umoeller] */ const char* BSExecute::DescribeType() { return ("Execute program"); } /* *@@ DescribeData: * describes the current manipulator data to the GUI. * *@@added V0.9.9 (2001-03-27) [umoeller] *@@changed V0.9.18 (2002-03-08) [umoeller]: now returning ustring */ ustring BSExecute::DescribeData() { ustring ustr; ustr._printf("\"%s\"", _ustrExecutable.GetBuffer()); if (_ustrParams()) { ustr.appendUtf8(" \""); ustr += _ustrParams; ustr.appendUtf8("\""); } if (_ulExecType == 0) ustr.appendUtf8(" (no flags)"); else { if (_ulExecType & CFGT_CFGSYS) ustr.appendUtf8(" CONFIGSYS"); if (_ulExecType & CFGT_REGISTERCLASS) ustr.appendUtf8(" REGISTERCLASS"); if (_ulExecType & CFGT_CREATEOBJECT) ustr.appendUtf8(" CREATEOBJECT"); } return (ustr); } /* *@@ Execute: * executes the member program. This does not return * until the program terminates. As a result, do not * call this on a PM thread, because this gets blocked. * * Throws: * -- BSConfigExcpt. * *@@added V0.9.1 (2000-02-07) [umoeller] *@@changed V0.9.18 (2002-03-08) [umoeller]: added codec *@@changed V1.0.5 (2005-01-25) [pr]: Change directory to that of executable *@@changed WarpIN V1.0.10 (2006-04-01) [pr]: Set title in doshStartSession call */ int BSExecute::Execute(BSUniCodec &codecProcess, // in: codec for process codepage, BSFileLogger *pLogFile) // in: file logger (log file), can be NULL { string strTitle; strTitle.assignUtf8(&codecProcess, _ustrExecutable); string strParams = "/c "; strParams.appendUtf8(&codecProcess, _ustrExecutable); if (_ustrParams()) { strParams += " "; strParams.appendUtf8(&codecProcess, _ustrParams); } // V1.0.7 (2005-05-30) [pr]: This doesn't work. Some packages rely on the current // directory not being changed, so you cannot win whatever you do. In any case, the // code below assumes it is always a path'd executable, which is not always the case. // Perhaps we should just set BEGINLIBPATH here instead which will allow newly // installed EXEs to find their DLLs. // // V1.0.5 (2005-01-26) [pr]: Change directory to that of executable. @@fixes 626. // We remove any leading quotes and just leave the directory name, excluding a // trailing slash, unless it is a root directory. #if 0 string strDir; strDir.assignUtf8(&codecProcess, _ustrExecutable); strDir = strDir.substr(strDir.find_first_not_of("\"'")); size_type pos = strDir.rfind('\\', 0); if (pos != string::npos) { strDir.erase(pos); if (strDir.size() == 2 && strDir[1] == ':') strDir += '\\'; doshSetCurrentDir(strDir.c_str()); } #endif ULONG sid = 0; PID pid = 0; if (_arc = doshQuickStartSession("cmd.exe", (PSZ)strParams.c_str(), (PSZ)strTitle.c_str(), SSF_TYPE_DEFAULT, // session type FALSE, // foreground 0, // auto-close, visible TRUE, // wait &sid, &pid, NULL)) { if (pLogFile) pLogFile->Write("Error %d executing \"CMD.EXE %s\"", _arc, strParams.c_str()); throw BSConfigExcpt(EXEEXCPT_DOSERROR, _arc); } if (pLogFile) pLogFile->Write("Executed \"CMD.EXE %s\"", strParams.c_str()); return (0); } /* ****************************************************************** * * BSDeExecute class * ********************************************************************/ /* *@@ BSDeExecute: * constructor. * *@@added V0.9.16 (2001-12-08) [umoeller] */ BSDeExecute::BSDeExecute(const ustring &ustrDeExecute) : BSExecute(ustrDeExecute, tBSDeExecute) { }; /* *@@ DescribeType: * describes the current manipulator type to the GUI. * *@@added V0.9.2 (2000-02-19) [umoeller] */ const char* BSDeExecute::DescribeType() { return ("External program for deinstall"); } /* *@@ AddToUndoList: * static helper method to create BSDeExecute instances from * the specified logger instance. * * The new BSKillProcess objects are appended to * the specified list. * * This returns the number of items created. * * This isn't really an "Undo" method like the other * static AddToUndoList methods (for the other classes), * but has been named like this for conformity. This * is during package de-install to create the same * BSDeExecute list which was initially created from * the script so that processes can be killed during * de-install as well. * *@@added V0.9.9 (2001-03-27) [umoeller] */ int BSDeExecute::AddToUndoList(list &List, BSDeExecuteResolvedLogger &logger) { PSZ pszLogStart = logger._pabLogString, pszLogThis = pszLogStart; int iCount = 0; while (pszLogThis < pszLogStart + logger._cbLogString) { ULONG cbLogThis = strlen(pszLogThis); ustring ustr; ustr.assignUtf8(pszLogThis); List.push_back(new BSDeExecute(ustr)); pszLogThis += cbLogThis + 1; // go beyond null byte iCount++; } return (iCount); } /* ****************************************************************** * * BSKillProcess class * ********************************************************************/ /* *@@ BSKillProcess: * * Syntax: * + KILLPROCESS="filename" * * Throws: * -- BSConfigExcpt. * *@@added V0.9.1 (2000-02-12) [umoeller] */ BSKillProcess::BSKillProcess(const ustring &ustrKillProcess) : BSConfigBase(CFGT_KILLPROCESS, tBSKillProcess) { if (!ustrKillProcess()) throw BSConfigExcpt(KILLEXCPT_SYNTAX, ustrKillProcess); _ustrKillProcess = ustrKillProcess; } /* *@@ DescribeType: * describes the current manipulator type to the GUI. * *@@added V0.9.4 (2000-07-01) [umoeller] */ const char* BSKillProcess::DescribeType() { return ("Kill process"); } /* *@@ DescribeData: * describes the current manipulator data to the GUI. * *@@added V0.9.4 (2000-07-01) [umoeller] *@@changed V0.9.18 (2002-03-08) [umoeller]: now returning ustring */ ustring BSKillProcess::DescribeData() { return _ustrKillProcess; } /* *@@ AddToUndoList: * static helper method to create BSKillProcess * instances from the specified logger instance. * * The new BSKillProcess objects are appended to * the specified list. * * This returns the number of items created. * * This isn't really an "Undo" method like the other * static AddToUndoList methods (for the other classes), * but has been named like this for conformity. This * is during package de-install to create the same * BSKillProcess list which was initially created from * the script so that processes can be killed during * de-install as well. * *@@added V0.9.4 (2000-07-01) [umoeller] */ int BSKillProcess::AddToUndoList(list &List, BSKillProcessAttrsLogger &logger) { // the logger in this case has a simple list of PSZs with all // the executable names that were created, each of which is // terminated with a null character PSZ pszLogStart = logger._pabLogString, pszLogThis = pszLogStart; int iCount = 0; while (pszLogThis < pszLogStart + logger._cbLogString) { ULONG cbLogThis = strlen(pszLogThis); ustring ustr; ustr.assignUtf8(pszLogThis); List.push_back(new BSKillProcess(ustr)); pszLogThis += cbLogThis + 1; // go beyond null byte iCount++; } return (iCount); }