summaryrefslogtreecommitdiff
path: root/miranda-wine/src/modules/database/dbini.c
diff options
context:
space:
mode:
Diffstat (limited to 'miranda-wine/src/modules/database/dbini.c')
-rw-r--r--miranda-wine/src/modules/database/dbini.c495
1 files changed, 495 insertions, 0 deletions
diff --git a/miranda-wine/src/modules/database/dbini.c b/miranda-wine/src/modules/database/dbini.c
new file mode 100644
index 0000000..0717080
--- /dev/null
+++ b/miranda-wine/src/modules/database/dbini.c
@@ -0,0 +1,495 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2006 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+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; either version 2
+of the License, or (at your option) any later version.
+
+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.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "../../core/commonheaders.h"
+//#include "database.h"
+
+static HANDLE hIniChangeNotification;
+extern char mirandabootini[MAX_PATH];
+
+int GetCommandLineDbName(char *szName,int cbName)
+{
+ char *szCmdLine=GetCommandLineA();
+ char *szEndOfParam;
+ char szThisParam[1024];
+ int firstParam=1;
+
+ while(szCmdLine[0]) {
+ if(szCmdLine[0]=='"') {
+ szEndOfParam=strchr(szCmdLine+1,'"');
+ if(szEndOfParam==NULL) break;
+ lstrcpynA(szThisParam,szCmdLine+1,min(sizeof(szThisParam),szEndOfParam-szCmdLine));
+ szCmdLine=szEndOfParam+1;
+ }
+ else {
+ szEndOfParam=szCmdLine+strcspn(szCmdLine," \t");
+ lstrcpynA(szThisParam,szCmdLine,min(sizeof(szThisParam),szEndOfParam-szCmdLine+1));
+ szCmdLine=szEndOfParam;
+ }
+ while(*szCmdLine && *szCmdLine<=' ') szCmdLine++;
+ if(firstParam) {firstParam=0; continue;} //first param is executable name
+ if(szThisParam[0]=='/' || szThisParam[0]=='-') continue; //no switches supported
+ lstrcpynA(szName,szThisParam,cbName);
+ return 0;
+ }
+ return 1;
+}
+
+void GetProfileDirectory(char *szPath,int cbPath)
+{
+ char *str2;
+ char szMirandaDir[MAX_PATH],szProfileDir[MAX_PATH],szExpandedProfileDir[MAX_PATH];
+ DWORD dwAttributes;
+
+ GetModuleFileNameA(GetModuleHandle(NULL),szMirandaDir,sizeof(szMirandaDir));
+ str2=strrchr(szMirandaDir,'\\');
+ if(str2!=NULL) *str2=0;
+ GetPrivateProfileStringA("Database","ProfileDir",".",szProfileDir,sizeof(szProfileDir),mirandabootini);
+ ExpandEnvironmentStringsA(szProfileDir,szExpandedProfileDir,sizeof(szExpandedProfileDir));
+ _chdir(szMirandaDir);
+ if(!_fullpath(szPath,szExpandedProfileDir,cbPath))
+ lstrcpynA(szPath,szMirandaDir,cbPath);
+ if(szPath[lstrlenA(szPath)-1]=='\\') szPath[lstrlenA(szPath)-1]='\0';
+ if((dwAttributes=GetFileAttributesA(szPath))!=0xffffffff&&dwAttributes&FILE_ATTRIBUTE_DIRECTORY) return;
+ CreateDirectoryA(szPath,NULL);
+}
+
+int ShouldAutoCreate(void)
+{
+ char szAutoCreate[4];
+ GetPrivateProfileStringA("Database","AutoCreate","no",szAutoCreate,sizeof(szAutoCreate),mirandabootini);
+ return !lstrcmpiA(szAutoCreate,"yes");
+}
+
+int GetDefaultProfilePath(char *szPath,int cbPath,int *specified)
+{
+ char szProfileDir[MAX_PATH],szDefaultName[MAX_PATH],szExpandedDefaultName[MAX_PATH];
+ HANDLE hFind;
+ char szSearchPath[MAX_PATH],szSingleExistingPath[MAX_PATH];
+ WIN32_FIND_DATAA fd;
+
+ if(specified) *specified=1;
+ GetProfileDirectory(szProfileDir,sizeof(szProfileDir));
+ if(GetCommandLineDbName(szDefaultName,sizeof(szDefaultName))) {
+ if(specified) *specified=0;
+ GetPrivateProfileStringA("Database","DefaultProfile","",szDefaultName,sizeof(szDefaultName),mirandabootini);
+ }
+ ExpandEnvironmentStringsA(szDefaultName,szExpandedDefaultName,sizeof(szExpandedDefaultName));
+
+ _chdir(szProfileDir);
+
+ szSingleExistingPath[0]='\0';
+ lstrcpyA(szSearchPath,szProfileDir);
+ lstrcatA(szSearchPath,"\\*.dat");
+ hFind=FindFirstFileA(szSearchPath,&fd);
+ if(hFind!=INVALID_HANDLE_VALUE) {
+ if(FindNextFileA(hFind,&fd)==0)
+ if(_fullpath(szSingleExistingPath,fd.cFileName,cbPath)==NULL)
+ szSingleExistingPath[0]='\0';
+ FindClose(hFind);
+ }
+
+ if(szExpandedDefaultName[0]) {
+ lstrcatA(szExpandedDefaultName,".dat");
+ if(_fullpath(szPath,szExpandedDefaultName,cbPath)!=NULL) {
+ if(specified && !lstrcmpiA(szSingleExistingPath,szPath)) *specified=1;
+ if(!_access(szPath,0)) return 0;
+ if(ShouldAutoCreate()) {
+ HANDLE hFile;
+ if(specified && szSingleExistingPath[0]=='\0') *specified=1;
+ hFile=CreateFileA(szPath,GENERIC_WRITE,FILE_SHARE_READ,NULL,OPEN_ALWAYS,0,NULL);
+ CloseHandle(hFile);
+ return 0;
+ }
+ }
+ }
+
+ if(szSingleExistingPath[0]) {
+ if(specified && szExpandedDefaultName[0]=='\0') *specified=1;
+ lstrcpynA(szPath,szSingleExistingPath,cbPath);
+ return 0;
+ }
+ return 1;
+}
+
+int ShouldShowProfileManager(void)
+{
+ char szShowValue[7];
+ char szDefaultProfile[MAX_PATH];
+ int defaultProfileSpecified;
+
+ if(GetAsyncKeyState(VK_CONTROL)&0x8000) return 1;
+ GetPrivateProfileStringA("Database","ShowProfileMgr","smart",szShowValue,sizeof(szShowValue),mirandabootini);
+ if(!lstrcmpiA(szShowValue,"always")) return 1;
+ if(!lstrcmpiA(szShowValue,"never")) {
+ return GetDefaultProfilePath(szDefaultProfile,sizeof(szDefaultProfile),NULL);
+ }
+ return GetDefaultProfilePath(szDefaultProfile,sizeof(szDefaultProfile),&defaultProfileSpecified)
+ || !defaultProfileSpecified;
+}
+
+static BOOL CALLBACK InstallIniDlgProc(HWND hwndDlg,UINT message,WPARAM wParam,LPARAM lParam)
+{
+ switch(message) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ SetDlgItemTextA(hwndDlg,IDC_ININAME,(char*)lParam);
+ { char szSecurity[11],*pszSecurityInfo;
+ GetPrivateProfileStringA("AutoExec","Warn","notsafe",szSecurity,sizeof(szSecurity),mirandabootini);
+ if(!lstrcmpiA(szSecurity,"all"))
+ pszSecurityInfo="Security systems to prevent malicious changes are in place and you will be warned before every change that is made.";
+ else if(!lstrcmpiA(szSecurity,"onlyunsafe"))
+ pszSecurityInfo="Security systems to prevent malicious changes are in place and you will be warned before changes that are known to be unsafe.";
+ else if(!lstrcmpiA(szSecurity,"none"))
+ pszSecurityInfo="Security systems to prevent malicious changes have been disabled. You will receive no further warnings.";
+ else pszSecurityInfo=NULL;
+ if(pszSecurityInfo) SetDlgItemTextA(hwndDlg,IDC_SECURITYINFO,ServiceExists(MS_LANGPACK_TRANSLATESTRING)?Translate(pszSecurityInfo):pszSecurityInfo);
+ }
+ return TRUE;
+ case WM_COMMAND:
+ switch(LOWORD(wParam)) {
+ case IDC_VIEWINI:
+ { char szPath[MAX_PATH];
+ GetDlgItemTextA(hwndDlg,IDC_ININAME,szPath,sizeof(szPath));
+ ShellExecuteA(hwndDlg,"open",szPath,NULL,NULL,SW_SHOW);
+ break;
+ }
+ case IDOK:
+ case IDCANCEL:
+ case IDC_NOTOALL:
+ EndDialog(hwndDlg,LOWORD(wParam));
+ break;
+ }
+ break;
+ }
+ return FALSE;
+}
+
+static int IsInSpaceSeparatedList(const char *szWord,const char *szList)
+{
+ char *szItem,*szEnd;
+ int wordLen=lstrlenA(szWord);
+
+ for(szItem=(char*)szList;;) {
+ szEnd=strchr(szItem,' ');
+ if(szEnd==NULL) return !lstrcmpA(szItem,szWord);
+ if(szEnd-szItem==wordLen) {
+ if(!strncmp(szItem,szWord,wordLen)) return 1;
+ }
+ szItem=szEnd+1;
+ }
+ return 0;
+}
+
+struct warnSettingChangeInfo_t {
+ char *szIniPath;
+ char *szSection;
+ char *szSafeSections;
+ char *szUnsafeSections;
+ char *szName;
+ char *szValue;
+ int warnNoMore,cancel;
+};
+
+static BOOL CALLBACK WarnIniChangeDlgProc(HWND hwndDlg,UINT message,WPARAM wParam,LPARAM lParam)
+{
+ static struct warnSettingChangeInfo_t *warnInfo;
+
+ switch(message) {
+ case WM_INITDIALOG:
+ { char szSettingName[256];
+ char *pszSecurityInfo;
+ warnInfo=(struct warnSettingChangeInfo_t*)lParam;
+ TranslateDialogDefault(hwndDlg);
+ SetDlgItemTextA(hwndDlg,IDC_ININAME,warnInfo->szIniPath);
+ lstrcpyA(szSettingName,warnInfo->szSection);
+ lstrcatA(szSettingName," / ");
+ lstrcatA(szSettingName,warnInfo->szName);
+ SetDlgItemTextA(hwndDlg,IDC_SETTINGNAME,szSettingName);
+ SetDlgItemTextA(hwndDlg,IDC_NEWVALUE,warnInfo->szValue);
+ if(IsInSpaceSeparatedList(warnInfo->szSection,warnInfo->szSafeSections))
+ pszSecurityInfo="This change is known to be safe.";
+ else if(IsInSpaceSeparatedList(warnInfo->szSection,warnInfo->szUnsafeSections))
+ pszSecurityInfo="This change is known to be potentially hazardous.";
+ else
+ pszSecurityInfo="This change is not known to be safe.";
+ SetDlgItemTextA(hwndDlg,IDC_SECURITYINFO,ServiceExists(MS_LANGPACK_TRANSLATESTRING)?Translate(pszSecurityInfo):pszSecurityInfo);
+ return TRUE;
+ }
+ case WM_COMMAND:
+ switch(LOWORD(wParam)) {
+ case IDCANCEL:
+ warnInfo->cancel=1;
+ case IDYES:
+ case IDNO:
+ warnInfo->warnNoMore=IsDlgButtonChecked(hwndDlg,IDC_WARNNOMORE);
+ EndDialog(hwndDlg,LOWORD(wParam));
+ break;
+ }
+ break;
+ }
+ return FALSE;
+}
+
+static BOOL CALLBACK IniImportDoneDlgProc(HWND hwndDlg,UINT message,WPARAM wParam,LPARAM lParam)
+{
+ switch(message) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ SetDlgItemTextA(hwndDlg,IDC_ININAME,(char*)lParam);
+ SetDlgItemTextA(hwndDlg,IDC_NEWNAME,(char*)lParam);
+ return TRUE;
+ case WM_COMMAND:
+ { char szIniPath[MAX_PATH];
+ GetDlgItemTextA(hwndDlg,IDC_ININAME,szIniPath,sizeof(szIniPath));
+ switch(LOWORD(wParam)) {
+ case IDC_DELETE:
+ DeleteFileA(szIniPath);
+ case IDC_LEAVE:
+ EndDialog(hwndDlg,LOWORD(wParam));
+ break;
+ case IDC_RECYCLE:
+ { SHFILEOPSTRUCTA shfo={0};
+ shfo.wFunc=FO_DELETE;
+ shfo.pFrom=szIniPath;
+ szIniPath[lstrlenA(szIniPath)+1]='\0';
+ shfo.fFlags=FOF_NOCONFIRMATION|FOF_NOERRORUI|FOF_SILENT;
+ SHFileOperationA(&shfo);
+ }
+ EndDialog(hwndDlg,LOWORD(wParam));
+ break;
+ case IDC_MOVE:
+ { char szNewPath[MAX_PATH];
+ GetDlgItemTextA(hwndDlg,IDC_NEWNAME,szNewPath,sizeof(szNewPath));
+ MoveFileA(szIniPath,szNewPath);
+ }
+ EndDialog(hwndDlg,LOWORD(wParam));
+ break;
+ }
+ break;
+ }
+ }
+ return FALSE;
+}
+
+static void DoAutoExec(void)
+{
+ HANDLE hFind;
+ char szMirandaDir[MAX_PATH],szUse[7],szIniPath[MAX_PATH],szFindPath[MAX_PATH],szExpandedFindPath[MAX_PATH];
+ char szLine[2048];
+ char *str2;
+ WIN32_FIND_DATAA fd;
+ FILE *fp;
+ char szSection[128];
+ int lineLength;
+ char szSafeSections[2048],szUnsafeSections[2048],szSecurity[11],szOverrideSecurityFilename[MAX_PATH];
+ int warnThisSection=0;
+
+ GetPrivateProfileStringA("AutoExec","Use","prompt",szUse,sizeof(szUse),mirandabootini);
+ if(!lstrcmpiA(szUse,"no")) return;
+ GetPrivateProfileStringA("AutoExec","Safe","CLC Icons CLUI CList SkinSounds",szSafeSections,sizeof(szSafeSections),mirandabootini);
+ GetPrivateProfileStringA("AutoExec","Unsafe","ICQ MSN",szUnsafeSections,sizeof(szUnsafeSections),mirandabootini);
+ GetPrivateProfileStringA("AutoExec","Warn","notsafe",szSecurity,sizeof(szSecurity),mirandabootini);
+ GetPrivateProfileStringA("AutoExec","OverrideSecurityFilename","",szOverrideSecurityFilename,sizeof(szOverrideSecurityFilename),mirandabootini);
+ GetModuleFileNameA(GetModuleHandle(NULL),szMirandaDir,sizeof(szMirandaDir));
+ str2=strrchr(szMirandaDir,'\\');
+ if(str2!=NULL) *str2=0;
+ _chdir(szMirandaDir);
+ GetPrivateProfileStringA("AutoExec","Glob","autoexec_*.ini",szFindPath,sizeof(szFindPath),mirandabootini);
+ ExpandEnvironmentStringsA(szFindPath,szExpandedFindPath,sizeof(szExpandedFindPath));
+ hFind=FindFirstFileA(szExpandedFindPath,&fd);
+ if(hFind==INVALID_HANDLE_VALUE) return;
+ str2=strrchr(szExpandedFindPath,'\\');
+ if(str2==NULL) str2=szExpandedFindPath;
+ else str2++;
+ *str2='\0';
+ szSection[0]='\0';
+ do {
+ lstrcpyA(szIniPath,szExpandedFindPath);
+ lstrcatA(szIniPath,fd.cFileName);
+ if(!lstrcmpiA(szUse,"prompt") && lstrcmpiA(fd.cFileName,szOverrideSecurityFilename)) {
+ int result=DialogBoxParam(GetModuleHandle(NULL),MAKEINTRESOURCE(IDD_INSTALLINI),NULL,InstallIniDlgProc,(LPARAM)szIniPath);
+ if(result==IDC_NOTOALL) break;
+ if(result==IDCANCEL) continue;
+ }
+ fp=fopen(szIniPath,"rt");
+ while(!feof(fp)) {
+ if(fgets(szLine,sizeof(szLine),fp)==NULL) break;
+ lineLength=lstrlenA(szLine);
+ while(lineLength && (BYTE)(szLine[lineLength-1])<=' ') szLine[--lineLength]='\0';
+ if(szLine[0]==';' || szLine[0]<=' ') continue;
+ if(szLine[0]=='[') {
+ char *szEnd=strchr(szLine+1,']');
+ if(szEnd==NULL) continue;
+ if(szLine[1]=='!')
+ szSection[0]='\0';
+ else {
+ lstrcpynA(szSection,szLine+1,min(sizeof(szSection),szEnd-szLine));
+ if(!lstrcmpiA(szSecurity,"none")) warnThisSection=0;
+ else if(!lstrcmpiA(szSecurity,"notsafe"))
+ warnThisSection=!IsInSpaceSeparatedList(szSection,szSafeSections);
+ else if(!lstrcmpiA(szSecurity,"onlyunsafe"))
+ warnThisSection=IsInSpaceSeparatedList(szSection,szUnsafeSections);
+ else warnThisSection=1;
+ if(!lstrcmpiA(fd.cFileName,szOverrideSecurityFilename)) warnThisSection=0;
+ }
+ }
+ else {
+ char *szValue;
+ char szName[128];
+ struct warnSettingChangeInfo_t warnInfo;
+
+ if(szSection[0]=='\0') continue;
+ szValue=strchr(szLine,'=');
+ if(szValue==NULL) continue;
+ lstrcpynA(szName,szLine,min(sizeof(szName),szValue-szLine+1));
+ szValue++;
+ warnInfo.szIniPath=szIniPath;
+ warnInfo.szName=szName;
+ warnInfo.szSafeSections=szSafeSections;
+ warnInfo.szSection=szSection;
+ warnInfo.szUnsafeSections=szUnsafeSections;
+ warnInfo.szValue=szValue;
+ warnInfo.warnNoMore=0;
+ warnInfo.cancel=0;
+ if(!warnThisSection || IDNO!=DialogBoxParam(GetModuleHandle(NULL),MAKEINTRESOURCE(IDD_WARNINICHANGE),NULL,WarnIniChangeDlgProc,(LPARAM)&warnInfo)) {
+ if(warnInfo.cancel) break;
+ if(warnInfo.warnNoMore) warnThisSection=0;
+ switch(szValue[0]) {
+ case 'b':
+ case 'B':
+ DBWriteContactSettingByte(NULL,szSection,szName,(BYTE)strtol(szValue+1,NULL,0));
+ break;
+ case 'w':
+ case 'W':
+ DBWriteContactSettingWord(NULL,szSection,szName,(WORD)strtol(szValue+1,NULL,0));
+ break;
+ case 'd':
+ case 'D':
+ DBWriteContactSettingDword(NULL,szSection,szName,(DWORD)strtoul(szValue+1,NULL,0));
+ break;
+ case 'l':
+ case 'L':
+ DBDeleteContactSetting(NULL,szSection,szName);
+ break;
+ case 's':
+ case 'S':
+ DBWriteContactSettingString(NULL,szSection,szName,szValue+1);
+ break;
+ case 'u':
+ case 'U':
+ DBWriteContactSettingStringUtf(NULL,szSection,szName,szValue+1);
+ break;
+ case 'n':
+ case 'N':
+ { PBYTE buf;
+ int len;
+ char *pszValue,*pszEnd;
+ DBCONTACTWRITESETTING cws;
+
+ buf=(PBYTE)mir_alloc(lstrlenA(szValue+1));
+ for(len=0,pszValue=szValue+1;;len++) {
+ buf[len]=(BYTE)strtol(pszValue,&pszEnd,0x10);
+ if(pszValue==pszEnd) break;
+ pszValue=pszEnd;
+ }
+ cws.szModule=szSection;
+ cws.szSetting=szName;
+ cws.value.type=DBVT_BLOB;
+ cws.value.pbVal=buf;
+ cws.value.cpbVal=len;
+ CallService(MS_DB_CONTACT_WRITESETTING,(WPARAM)(HANDLE)NULL,(LPARAM)&cws);
+ mir_free(buf);
+ }
+ break;
+ default:
+ if(ServiceExists(MS_LANGPACK_TRANSLATESTRING))
+ MessageBox(NULL,TranslateT("Invalid setting type. The first character of every value must be b, w, d, l, s or n."),TranslateT("Install Database Settings"),MB_OK);
+ else
+ MessageBoxA(NULL,"Invalid setting type. The first character of every value must be b, w, d, l, s or n.","Install Database Settings",MB_OK);
+ break;
+ }
+ }
+ }
+ }
+ fclose(fp);
+ if(!lstrcmpiA(fd.cFileName,szOverrideSecurityFilename))
+ DeleteFileA(szIniPath);
+ else {
+ char szOnCompletion[8];
+ GetPrivateProfileStringA("AutoExec","OnCompletion","recycle",szOnCompletion,sizeof(szOnCompletion),mirandabootini);
+ if(!lstrcmpiA(szOnCompletion,"delete"))
+ DeleteFileA(szIniPath);
+ else if(!lstrcmpiA(szOnCompletion,"recycle")) {
+ SHFILEOPSTRUCTA shfo={0};
+ shfo.wFunc=FO_DELETE;
+ shfo.pFrom=szIniPath;
+ szIniPath[lstrlenA(szIniPath)+1]='\0';
+ shfo.fFlags=FOF_NOCONFIRMATION|FOF_NOERRORUI|FOF_SILENT;
+ SHFileOperationA(&shfo);
+ }
+ else if(!lstrcmpiA(szOnCompletion,"rename")) {
+ char szRenamePrefix[MAX_PATH];
+ char szNewPath[MAX_PATH];
+ GetPrivateProfileStringA("AutoExec","RenamePrefix","done_",szRenamePrefix,sizeof(szRenamePrefix),mirandabootini);
+ lstrcpyA(szNewPath,szExpandedFindPath);
+ lstrcatA(szNewPath,szRenamePrefix);
+ lstrcatA(szNewPath,fd.cFileName);
+ MoveFileA(szIniPath,szNewPath);
+ }
+ else if(!lstrcmpiA(szOnCompletion,"ask"))
+ DialogBoxParam(GetModuleHandle(NULL),MAKEINTRESOURCE(IDD_INIIMPORTDONE),NULL,IniImportDoneDlgProc,(LPARAM)szIniPath);
+ }
+ } while(FindNextFileA(hFind,&fd));
+ FindClose(hFind);
+}
+
+static int CheckIniImportNow(WPARAM wParam,LPARAM lParam)
+{
+ DoAutoExec();
+ FindNextChangeNotification(hIniChangeNotification);
+ return 0;
+}
+
+int InitIni(void)
+{
+ char szMirandaDir[MAX_PATH];
+ char *str2;
+
+ DoAutoExec();
+ GetModuleFileNameA(GetModuleHandle(NULL),szMirandaDir,sizeof(szMirandaDir));
+ str2=strrchr(szMirandaDir,'\\');
+ if(str2!=NULL) *str2=0;
+ hIniChangeNotification=FindFirstChangeNotificationA(szMirandaDir,0,FILE_NOTIFY_CHANGE_FILE_NAME);
+ if(hIniChangeNotification!=INVALID_HANDLE_VALUE) {
+ CreateServiceFunction("DB/Ini/CheckImportNow",CheckIniImportNow);
+ CallService(MS_SYSTEM_WAITONHANDLE,(WPARAM)hIniChangeNotification,(LPARAM)"DB/Ini/CheckImportNow");
+ }
+ return 0;
+}
+
+void UninitIni(void)
+{
+ CallService(MS_SYSTEM_REMOVEWAIT,(WPARAM)hIniChangeNotification,0);
+ FindCloseChangeNotification(hIniChangeNotification);
+} \ No newline at end of file