diff options
Diffstat (limited to 'protocols/LotusNotify/src/LotusNotify.cpp')
-rw-r--r-- | protocols/LotusNotify/src/LotusNotify.cpp | 1753 |
1 files changed, 1753 insertions, 0 deletions
diff --git a/protocols/LotusNotify/src/LotusNotify.cpp b/protocols/LotusNotify/src/LotusNotify.cpp new file mode 100644 index 0000000000..29dc2d91e6 --- /dev/null +++ b/protocols/LotusNotify/src/LotusNotify.cpp @@ -0,0 +1,1753 @@ +/* +Miranda plugin template, originally by Richard Hughes +http://miranda-icq.sourceforge.net/ + +This file is placed in the public domain. Anybody is free to use or +modify it as they wish with no restriction. +There is no warranty. +*/ + +#include "stdafx.h" + +#include "debug.h" +#include "resource.h" +#include "version.h" +#include "lotusnotes.h" +#include "LotusNotify.h" + +INT_PTR SetStatus(WPARAM wParam, LPARAM lParam); + +#define MAX_FIELD 256 +#define MAX_SETTING_STR 512 +#define STATUS_COUNT 9 + +char MODULENAME[64] = {0}; //init at init_pluginname(); +CMPlugin g_plugin; + +HINSTANCE hLotusDll; +HEMREGISTRATION hLotusRegister = 0; + +boolean volatile Plugin_Terminated = false; +mir_cs checkthreadCS; + +HGENMENU hMenuHandle = nullptr; +HANDLE hCheckEvent = nullptr; + +static HWND hTimerWnd = (HWND)nullptr; +static UINT TID = (UINT)2006; + +char settingServer[MAX_SETTING_STR] = "", settingServerSec[MAX_SETTING_STR] = "", settingDatabase[MAX_SETTING_STR] = "", + settingCommand[MAX_SETTING_STR] = "", settingParameters[MAX_SETTING_STR] = "", settingPassword[MAX_SETTING_STR] = ""; +wchar_t settingFilterSubject[MAX_SETTING_STR] = TEXT(""), settingFilterSender[MAX_SETTING_STR] = TEXT(""), settingFilterTo[MAX_SETTING_STR] = TEXT(""); + +COLORREF settingBgColor, settingFgColor; +int settingInterval = 0, settingInterval1 = 0; +DWORD settingNewestID = 0; +BYTE settingSetColours = 0, settingShowError = 1, settingIniAnswer = -1, settingIniCheck = 0, + settingOnceOnly = 0, settingNonClickedOnly = 0, settingNewest = 0, settingEvenNonClicked = 0, settingKeepConnection = 1; +BOOL settingStatus[STATUS_COUNT]; +BOOL bMirandaCall=FALSE; + +struct HISTORIA *first = nullptr; +BOOL running = FALSE; +BOOL second = FALSE; +BOOL isPopupWaiting = FALSE; +int currentStatus = ID_STATUS_OFFLINE; +int diffstat = 0; +int startuperror = 0; +wchar_t *startuperrors[] = { + LPGENW("Unable to load all required Lotus API functions"), + LPGENW("Lotus Notes Client not detected. Check plugin configuration description at https://miranda-ng.org/p/LotusNotify"), + LPGENW("Unable to initialize Notes."), + LPGENW("Lotus Notes Extension Manager was not registered. Authentication function will not work properly"), + LPGENW("In notes.ini file there is no required entry EXTMGR_ADDINS=plugindllnamewithout\".dll\"") + }; + +///////////////////////////////////////////////////////////////////////////////////////// + +PLUGININFOEX pluginInfoEx = { + sizeof(PLUGININFOEX), + __PLUGIN_NAME, + PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM), + __DESCRIPTION, + __AUTHOR, + __COPYRIGHT, + __AUTHORWEB, + UNICODE_AWARE, + { 0x23eacc0d, 0xbab0, 0x49c0, { 0x8f, 0x37, 0x5e, 0x25, 0x9e, 0xce, 0x52, 0x7f } } // {23EACC0D-BAB0-49c0-8F37-5E259ECE527F} +}; + +CMPlugin::CMPlugin() : + PLUGIN<CMPlugin>(MODULENAME, pluginInfoEx) +{ + RegisterProtocol(PROTOTYPE_PROTOCOL); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = { MIID_PROTOCOL, MIID_LAST }; + +///////////////////////////////////////////////////////////////////////////////////////// +// authentication callback futnction from extension manager called by nnotes.dll + +STATUS LNPUBLIC __stdcall EMCallBack (EMRECORD * pData) +{ + VARARG_PTR pArgs; + DWORD maxPwdLen; + DWORD * retLength; + char * retPassword; + char * fileName; + char * ownerName; + + if (pData->EId != EM_GETPASSWORD) return (ERR_EM_CONTINUE); //asking for password? + if (pData->Status != NOERROR) return (ERR_EM_CONTINUE); + + pArgs= pData->Ap; + maxPwdLen = VARARG_GET (pArgs, DWORD); + retLength = VARARG_GET (pArgs, DWORD *); + retPassword = VARARG_GET (pArgs, char *); + fileName = VARARG_GET (pArgs, char *); + ownerName = VARARG_GET (pArgs, char *); + strncpy(retPassword, settingPassword, mir_strlen(settingPassword)); //set our password + retPassword[mir_strlen(settingPassword)]='\0'; + *retLength = (DWORD)mir_strlen(retPassword);//and his length + return ERR_BSAFE_EXTERNAL_PASSWORD; +} + + +//Main entry point for Ext Manager of Lotus API. called by nnotes.dll +//It don't work (don't know why), and Mirandas Load function is called with value 1 or 0 as parameter... +__declspec(dllexport)STATUS LNPUBLIC MainEntryPoint (void) +{ + STATUS rc = EMRegister1 (EM_GETPASSWORD, EM_REG_BEFORE | EM_REG_AFTER, EMCallBack, 0, &hLotusRegister); //Extension Manager must know that we are here + if(rc) { + //Extension magager don't know who we are :( + startuperror+=8; + // Get the info from the .ini file + } + return rc; +} + + +//Clear Extension Manager when exiting +void ExtClear() +{ + STATUS status; + if (0 != hLotusRegister) { + status = EMDeregister1(&hLotusRegister); //we was registered, so let's unregister + } else { + status = NOERROR; + } + log_p(L"ExtClear() status=%d", status); + return; +} + + +//check if msg was clicked and exists on msgs list +struct HISTORIA* getEl(DWORD id) +{ + for(struct HISTORIA *cur = first; cur != nullptr; cur = cur->next) + { + if(cur->noteID == id) + return cur; + } + return nullptr; +} + + +//creates new entry on list of msgs +void addNewId(DWORD id) +{ + struct HISTORIA* nowy = (struct HISTORIA*)mir_alloc(sizeof(struct HISTORIA)) ; + assert(nowy); + nowy->noteID = id; + nowy->next = first; + nowy->pq = nullptr; + nowy->again = FALSE; + first = nowy; +} + + +//add popup handle. This queue is used to close popups with same msg +void addPopup(DWORD id,HWND hWnd) +{ + struct POPUPSQUEUE* nowy = (struct POPUPSQUEUE*)mir_alloc(sizeof(struct POPUPSQUEUE)) ; + struct HISTORIA *elem = getEl(id); + assert(nowy); + nowy->hWnd = hWnd; + nowy->next = elem->pq; + elem->pq = nowy; +} + + +//clear popups handles list +void deletePopupsHandles(struct POPUPSQUEUE **firstpq, BOOL closePopup) +{ + struct POPUPSQUEUE *curpq = *firstpq, *delpq; + while(curpq != nullptr) { + delpq = curpq; + curpq = curpq->next; + if(closePopup) + PUDeletePopup(delpq->hWnd); + mir_free(delpq); + } + *firstpq = nullptr; +} + + +//clear msgs list +void deleteElements() +{ + struct HISTORIA *cur = first, *del; + while(cur != nullptr) + { + del = cur; + cur = cur->next; + deletePopupsHandles(&(del->pq),FALSE); + mir_free(del); + first = cur; + } + first = nullptr; +} + + +//set plugin name +void init_pluginname() +{ + char text[MAX_PATH], *p, *q; + WIN32_FIND_DATAA ffd; + + // Try to find name of the file having original letter sizes + GetModuleFileNameA(g_plugin.getInst(), text, sizeof(text)); + + HANDLE hFind = FindFirstFileA(text, &ffd); + if(hFind != INVALID_HANDLE_VALUE) { + strncpy_s(text, _countof(text), ffd.cFileName, mir_strlen(ffd.cFileName)); + FindClose(hFind); + } + // Check if we have relative or full path + if(p = strrchr(text, '\\')) { + p++; + } else { + p = text; + } + if(q = strrchr(p, '.')) { + *q = '\0'; + } + if((q = strstr(p, "debug")) && mir_strlen(q) == 5) { + *q = '\0'; + } + + // copy to static variable + strncpy_s(MODULENAME, _countof(MODULENAME), p, mir_strlen(p)); + assert(mir_strlen(MODULENAME)>0); + +} + + +BOOL strrep(char *src, char *needle, char *newstring) +{ + char *found, begining[MAX_SETTING_STR], tail[MAX_SETTING_STR]; + + //strset(begining,' '); + //strset(tail,' '); + if(!(found=strstr(src,needle))) + return FALSE; + + size_t pos = (found-src); + strncpy_s(begining, _countof(begining), src, pos); + begining[pos]='\0'; + + pos += mir_strlen(needle); + strncpy_s(tail, _countof(tail), src+pos, _countof(tail)); + begining[pos]='\0'; + + pos = sprintf(src, "%s%s%s", begining, newstring, tail); //!!!!!!!!!!!!!!!! + return TRUE; +} + + +//check if given string contain filter string +//param field= 0-sender +// 1-subject +BOOL checkFilters(wchar_t* str, int field) +{ + wchar_t buff[512] = L""; + wchar_t *strptr = nullptr; + switch(field) { + case 0: + wcsncpy_s(buff, settingFilterSender, _TRUNCATE); + break; + case 1: + wcsncpy_s(buff, settingFilterSubject, _TRUNCATE); + break; + case 2: + wcsncpy_s(buff, settingFilterTo, _TRUNCATE); + break; + } + + + while(strptr = wcschr(buff, ';')) + { + wchar_t tmp[512] = TEXT(""), *ptr; + wcsncpy_s(tmp, buff, (strptr-buff)); + wcsncpy_s(buff, strptr + 1, _TRUNCATE); + + if(wcsstr(wcslwr(ptr=wcsdup(str)),wcslwr(tmp))) + { + free(ptr); + return TRUE; + } + free(ptr); + } + return FALSE; +} + + +//subfunction called from popup plugin callback function +void Click(HWND hWnd,BOOL execute) +{ + POPUPATT *pid = (POPUPATT*)PUGetPluginData(hWnd); + if(settingOnceOnly&&settingNonClickedOnly) + (getEl(pid->id))->clicked=TRUE;//add to msgs list + + deletePopupsHandles((&(getEl(pid->id))->pq),TRUE); + + if(settingNewest && (pid->id > settingNewestID) ){ + g_plugin.setDword("LNNewestID", settingNewestID=pid->id); + } + if(execute && settingCommand[0] ) { + char tmpcommand[2*MAX_SETTING_STR]; + char tmpparameters[2*MAX_SETTING_STR]; + strncpy_s(tmpcommand, _countof(tmpcommand), settingCommand, _countof(tmpcommand)); + strncpy_s(tmpparameters, _countof(tmpparameters), settingParameters, _countof(tmpparameters)); + strrep(tmpcommand, "%OID%", pid->strNote); + strrep(tmpparameters, "%OID%", pid->strNote); + log_p(L"executing: %S %S", tmpcommand, tmpparameters); + ShellExecuteA(nullptr, "Open", tmpcommand, tmpparameters, nullptr, SW_NORMAL); + } +} + + +//popup plugin callback function +static LRESULT CALLBACK PopupDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch(message) { + case WM_COMMAND: + { + + if (HIWORD(wParam) == STN_CLICKED)//client clicked on popup with left mouse button + { + Click(hWnd,TRUE); + + //system(settingCommand); + //if(!settingOnceOnly) + //addNewId(noteID); + return TRUE; + } + + break; + } + + case WM_RBUTTONUP: + { + Click(hWnd,FALSE); + break; + } + + case UM_INITPOPUP: + { + POPUPATT *pid = (POPUPATT*)PUGetPluginData(hWnd); + addPopup(pid->id,hWnd); + //PUDeletePopUp(hWnd); + break; + } + + case UM_FREEPLUGINDATA: + { + POPUPATT *mpd = (POPUPATT*)PUGetPluginData(hWnd); + if (mpd > 0) free(mpd); + return TRUE; //TRUE or FALSE is the same, it gets ignored. + } + + default: + break; + } + + return DefWindowProc(hWnd, message, wParam, lParam); +} + + +//check notes.ini if it has entry about our plugin +//return TRUE if notes.ini is set correctly +//give bInfo=TRUE to show MsgBoxes +BOOL checkNotesIniFile(BOOL bInfo) +{ + char tmp[MAXENVVALUE+1], tmp1[MAXENVVALUE+1]; + (OSGetEnvironmentString1) ("EXTMGR_ADDINS", tmp, MAXENVVALUE);//get current setting + strncpy_s(tmp1,_countof(tmp1),tmp,sizeof(tmp1));//copy temporary + assert(mir_strlen(tmp1)>0); + + char* PLUGINNAME_lower = _strlwr(mir_strdup(MODULENAME)); + + //is there our plugin as safe? + if(strstr(tmp1,PLUGINNAME_lower) == nullptr) + { + if(!settingIniCheck && !bInfo) + return FALSE; + + if(!settingIniAnswer || bInfo){ + switch(MessageBox(nullptr, TranslateT("This utility check your notes.ini file if it's set to authenticate this plugin as safe. Plugin is not added as Lotus Extension, so plugin built-in authentication will not work properly. Do you want to add plugin as Lotus Extension (modify notes.ini by adding \"EXTMGR_ADDINS=PLUGINNAME\")?"), TranslateT("LotusNotify plugin configuration"), MB_YESNO)) + { + case IDYES: + { + settingIniAnswer=1; + break; + } + case IDNO: + { + settingIniAnswer=-1; + break; + } + } + } + + if(settingIniAnswer == 1) + { + if(mir_strlen(tmp) > 0) { + strcat_s(tmp, _countof(tmp), ","); + strcat_s(tmp, _countof(tmp), PLUGINNAME_lower); //add our plugin to extensions + } else { + strncpy_s(tmp, _countof(tmp), PLUGINNAME_lower, mir_strlen(PLUGINNAME_lower)); //set our plugin as extension + } + + (OSSetEnvironmentVariable1) ("EXTMGR_ADDINS", tmp); //set notes.ini entry + if(bInfo) { + MessageBox(nullptr, TranslateT("notes.ini modified correctly. Miranda restart required."), TranslateT("LotusNotify plugin configuration"), MB_OK); + } else{ + ErMsgT(TranslateT("notes.ini modified correctly. Miranda restart required.")); + } + return TRUE; + } else { if(settingIniAnswer == 0xFF) + return FALSE; + } + } else { + //setting set already + if(bInfo) + MessageBox(nullptr, TranslateT("notes.ini seem to be set correctly."), TranslateT("LotusNotify plugin configuration"), MB_OK); + return TRUE; + } + + mir_free(PLUGINNAME_lower); + return FALSE; +} + + +//popup plugin to show popup function +void showMsg(wchar_t* sender,wchar_t* text, DWORD id, char *strUID) +{ + + POPUPDATAW ppd; + //hContact = A_VALID_HANDLE_YOU_GOT_FROM_SOMEWHERE; + //hIcon = A_VALID_HANDLE_YOU_GOT_SOMEWHERE; + //char * lpzContactName = (char*)CallService(MS_CLIST_GETCONTACTDISPLAYNAME,(WPARAM)lhContact,0); + //99% of the times you'll just copy this line. + //1% of the times you may wish to change the contact's name. I don't know why you should, but you can. + //char * lpzText; + //The text for the second line. You could even make something like: char lpzText[128]; mir_wstrcpy(lpzText, "Hello world!"); It's your choice. + + POPUPATT * mpd = (POPUPATT*)malloc(sizeof(POPUPATT)); + memset(&ppd, 0, sizeof(ppd)); //This is always a good thing to do. + ppd.lchContact = NULL; //(HANDLE)hContact; //Be sure to use a GOOD handle, since this will not be checked. + ppd.lchIcon = LoadIcon(g_plugin.getInst(), MAKEINTRESOURCE(IDI_ICON1)); + wcscpy_s(ppd.lpwzContactName, _countof(ppd.lpwzContactName), sender); + wcscpy_s(ppd.lpwzText, _countof(ppd.lpwzText), text); + if(settingSetColours) + { + ppd.colorBack = settingBgColor; + ppd.colorText = settingFgColor; + } + ppd.PluginWindowProc = PopupDlgProc; + + ppd.iSeconds=settingInterval1; + //Now the "additional" data. + mpd->id = id; + strncpy_s(mpd->strNote, _countof(mpd->strNote), strUID, mir_strlen(strUID)); + //mpd->newStatus = ID_STATUS_ONLINE; + + //Now that the plugin data has been filled, we add it to the PopUpData. + ppd.PluginData = mpd; + + //Now that every field has been filled, we want to see the popup. + PUAddPopupW(&ppd); +} + + +//what to do with error msg +void ErMsgW(WCHAR* msg) +{ + wchar_t* msgT = mir_wstrdup(msg); + ErMsgT(msgT); + mir_free(msgT); +} +///TODO wchar_t->WCHAR and test +void ErMsgT(wchar_t* msg) +{ + log_p(L"Error: %S", msg); + if(settingShowError && !isPopupWaiting) { + wchar_t buffer[256+14]; + wcsncpy_s(buffer, L"LotusNotify: ", _TRUNCATE); + wcscat_s(buffer, msg); + isPopupWaiting = TRUE; + PUShowMessageW(buffer, SM_WARNING); + isPopupWaiting = FALSE; + } +} + + +//Lotus error occured so translate it +void ErMsgByLotusCode(STATUS erno) +{ + char far error_text_LMBCS[200]; + char far error_text_UNICODEatCHAR[400]; + WCHAR far error_text_UNICODE[200]; + WORD text_len; + + text_len = OSLoadString1(NULLHANDLE, erno, error_text_LMBCS, sizeof(error_text_LMBCS)-1); + OSTranslate1(OS_TRANSLATE_LMBCS_TO_UNICODE, error_text_LMBCS, (WORD)mir_strlen(error_text_LMBCS), error_text_UNICODEatCHAR, sizeof(error_text_UNICODEatCHAR)-1); + memcpy(error_text_UNICODE, error_text_UNICODEatCHAR, sizeof(error_text_UNICODE)); + + ErMsgW(error_text_UNICODE); +} + +int check() { + + log_p(L"check: Entering check function. running=%d", running); + + if(startuperror) { + int cnt; + for(cnt = 0; cnt <= 4; cnt++) + if(startuperror >> cnt & 1) + ErMsgT(TranslateW(startuperrors[cnt])); + return 1; + } + + if (Plugin_Terminated || Miranda_IsTerminated()){ + log_p(L"check: Plugin_Terminated (=%d) OR Miranda_IsTerminated()", Plugin_Terminated); + return 0; + } + + if(running) { + ErMsgT(TranslateT("Now checking Lotus, try again later")); + return 1; + } + + running = TRUE; + Menu_EnableItem(hMenuHandle, !running); + + log(L"check: starting checkthread"); + mir_forkthread(checkthread); + + return 0; +} + + + +//before pure lotus notes api functions call +void checkthread(void*) +{ + STATUS error = NOERROR; + char fullpath[255]; + DBHANDLE db_handle = NULLHANDLE; /* database handle */ + char UserName[MAXUSERNAME + 1]; + HANDLE hTable; + + DWORD noteID = 0L; + BOOL fFirst = TRUE; + + NOTEHANDLE note_handle; + WORD field_len; + char field_date[MAXALPHATIMEDATE + 1]; + + char field_lotus_LMBCS[MAX_FIELD]; + char field_lotus_UNICODEatCHAR[MAX_FIELD * sizeof(wchar_t)]; + WCHAR field_from_UNICODE[MAX_FIELD], field_subject_UNICODE[MAX_FIELD], field_to_UNICODE[MAX_FIELD],field_copy_UNICODE[MAX_FIELD]; + + mir_cslock lck(checkthreadCS); + log(L"checkthread: inside new check thread"); + + if (error = NotesInitThread1()) { + goto errorblock; + } +#ifdef _DEBUG + log(L"checkthread: Started NotesInitThread"); +#endif + + if (error = OSPathNetConstruct1(nullptr, settingServer, settingDatabase, fullpath)) { + goto errorblock; + } +#ifdef _DEBUG + log_p(L"checkthread: OSPathNetConstruct: %S", fullpath); +#endif + + if (error = NSFDbOpen1(fullpath, &db_handle)) { + if (mir_strcmp(settingServerSec, "") != 0) { + if (error = OSPathNetConstruct1(nullptr, settingServerSec, settingDatabase, fullpath)) { + goto errorblock; + } + else { + if (error = NSFDbOpen1(fullpath, &db_handle)) { + goto errorblock; + } + } + } + else { + goto errorblock; + } + } + assert(db_handle); +#ifdef _DEBUG + log(L"checkthread: DBOpened"); +#endif + + if (error = SECKFMGetUserName1(UserName)) { + goto errorblock0; + } + assert(UserName); +#ifdef _DEBUG + log_p(L"checkthread: Username: %S", UserName); +#endif + + /* Get the unread list */ + if (error = NSFDbGetUnreadNoteTable1(db_handle, UserName, (WORD)mir_strlen(UserName), TRUE, &hTable)) { + goto errorblock0; + } +#ifdef _DEBUG + log(L"checkthread: Unread Table got"); +#endif + + //error = IDTableCopy (hTable, &hOriginalTable); + //IDDestroyTable (hTable); + if (error = NSFDbUpdateUnread1(db_handle, hTable)) { + goto errorblock; + } +#ifdef _DEBUG + log(L"checkthread: Unread Table updated"); +#endif + assert(hTable); + + + while (IDScan1(hTable, fFirst, ¬eID)) { + + WORD Att; + BLOCKID bhAttachment; + DWORD cSize = 0; + DWORD attSize = 0; + OID retNoteOID; + TIMEDATE retModified; /* modified timedate */ + WORD retNoteClass; /* note class */ + TIMEDATE sendDate; + char strLink[4 * 16]; + + if (Plugin_Terminated || Miranda_IsTerminated()) { + log_p(L"checkthread: Plugin_Terminated (=%d) OR Miranda_IsTerminated()", Plugin_Terminated); + break; + } + +#ifdef _DEBUG + log_p(L"checkthread: Getting info about: %d", noteID); +#endif + + fFirst = FALSE; + assert(noteID); + if (!getEl(noteID)) + addNewId(noteID); + else + (getEl(noteID))->again = TRUE; + + if (!settingOnceOnly && (getEl(noteID))->again == TRUE) { + //don't show again and note was not showed (ID not on list) + continue; + } + +#ifdef _DEBUG + log(L"checkthread: skiped-don't show again and note was not showed (ID not on list)"); +#endif + + if (settingOnceOnly && settingNonClickedOnly && (getEl(noteID))->clicked == TRUE) { + //show again, but only not clicked (id added to list on Left Button click) + continue; + } + +#ifdef _DEBUG + log(L"checkthread: skiped-show again, but only not clicked (id added to list on Left Button click)"); +#endif + + if (settingNewest && settingNewestID >= noteID) { + //only newest option enabled, so if old id don't show it + continue; + } + +#ifdef _DEBUG + log(L"checkthread: skiped-only newest option enabled, so if old id don't show it"); +#endif + + // remember newest id depending on options set + if (settingNewest&&settingEvenNonClicked && (noteID > settingNewestID)) + g_plugin.setDword("LNNewestID", settingNewestID = noteID); + + //if(((!settingOnceOnly||(settingOnceOnly&&settingNonClickedOnly))&&existElem(noteID))||(settingNewest&&settingNewestID>=noteID)) + //continue; + + if (error = NSFNoteOpen1(db_handle, noteID, 0, ¬e_handle)) { + continue; + } + +#ifdef _DEBUG + log_p(L"checkthread: Opened Note: %d", noteID); +#endif + + NSFDbGetNoteInfo1(db_handle, /* DBHANDLE */ + noteID, /* NOTEID */ + &retNoteOID, /* out: OID */ + &retModified, /* out: */ + &retNoteClass); + memset(strLink, 0, sizeof(strLink)); + mir_snprintf(strLink, "%.8lX%.8lX%.8lX%.8lX", + retNoteOID.File.Innards[1], + retNoteOID.File.Innards[0], + retNoteOID.Note.Innards[1], + retNoteOID.Note.Innards[0] + ); + + log_p(L"checkthread: got noteInfo, built link: %S", strLink); + + field_len = NSFItemGetText1(note_handle, MAIL_FROM_ITEM, field_lotus_LMBCS, (WORD)sizeof(field_lotus_LMBCS)); + OSTranslate1(OS_TRANSLATE_LMBCS_TO_UNICODE, field_lotus_LMBCS, field_len, field_lotus_UNICODEatCHAR, sizeof(field_lotus_UNICODEatCHAR)); + memcpy(field_from_UNICODE, field_lotus_UNICODEatCHAR, field_len * sizeof(wchar_t)); + field_from_UNICODE[field_len] = '\0'; + + NSFItemGetTime1(note_handle, MAIL_POSTEDDATE_ITEM, &sendDate); + error = ConvertTIMEDATEToText1(nullptr, nullptr, &sendDate, field_date, MAXALPHATIMEDATE, &field_len); + field_date[field_len] = '\0'; + + field_len = NSFItemGetText1(note_handle, MAIL_SUBJECT_ITEM, field_lotus_LMBCS, (WORD)sizeof(field_lotus_LMBCS)); + OSTranslate1(OS_TRANSLATE_LMBCS_TO_UNICODE, field_lotus_LMBCS, field_len, field_lotus_UNICODEatCHAR, sizeof(field_lotus_UNICODEatCHAR)); + memcpy(field_subject_UNICODE, field_lotus_UNICODEatCHAR, field_len * sizeof(wchar_t)); + field_subject_UNICODE[field_len] = '\0'; + + field_len = NSFItemGetText1(note_handle, MAIL_SENDTO_ITEM, field_lotus_LMBCS, (WORD)sizeof(field_lotus_LMBCS)); + OSTranslate1(OS_TRANSLATE_LMBCS_TO_UNICODE, field_lotus_LMBCS, field_len, field_lotus_UNICODEatCHAR, sizeof(field_lotus_UNICODEatCHAR)); + memcpy(field_to_UNICODE, field_lotus_UNICODEatCHAR, field_len * sizeof(wchar_t)); + field_to_UNICODE[field_len] = '\0'; + + field_len = NSFItemGetText1(note_handle, MAIL_COPYTO_ITEM, field_lotus_LMBCS, (WORD)sizeof(field_lotus_LMBCS)); + OSTranslate1(OS_TRANSLATE_LMBCS_TO_UNICODE, field_lotus_LMBCS, field_len, field_lotus_UNICODEatCHAR, sizeof(field_lotus_UNICODEatCHAR)); + memcpy(field_copy_UNICODE, field_lotus_UNICODEatCHAR, field_len * sizeof(wchar_t)); + field_copy_UNICODE[field_len] = '\0'; + + + WCHAR msgFrom[512], msgSubject[512]; + memset(msgFrom, 0, sizeof(msgFrom)); + memset(msgSubject, 0, sizeof(msgSubject)); + + if (mir_wstrlen(field_from_UNICODE) < 512 && mir_wstrlen(field_from_UNICODE) > 3 && wcsstr(field_from_UNICODE, L"CN=") == field_from_UNICODE) + wcsncpy_s(msgFrom, &(field_from_UNICODE[3]), wcscspn(field_from_UNICODE, L"/") - 3); + else + wcsncpy_s(msgFrom, field_from_UNICODE, _TRUNCATE); + + for (Att = 0; MailGetMessageAttachmentInfo1(note_handle, Att, &bhAttachment, nullptr, &cSize, nullptr, nullptr, nullptr, nullptr); Att++) + attSize += cSize; + +#ifdef _DEBUG + log_p(L"checkthread: MAIL INFO: date=[%S], from=[%s], to=[%s], cc=[%s], sub=[%s], attSize=[%d]" + , field_date + , field_from_UNICODE + , field_to_UNICODE + , field_copy_UNICODE + , field_subject_UNICODE + , attSize + ); +#else + //do not put private user data into log + log_p(L"checkthread: MAIL INFO (sizes): date=[%S], from=[%d], to=[%d], cc=[%d], sub=[%d], attSize=[%d]" + ,field_date + ,mir_wstrlen(field_from_UNICODE) + ,mir_wstrlen(field_to_UNICODE) + ,mir_wstrlen(field_copy_UNICODE) + ,mir_wstrlen(field_subject_UNICODE) + ,attSize + ); +#endif + + + if (attSize) { + WCHAR field_attachments_UNICODE[MAX_FIELD]; + mir_snwprintf(field_attachments_UNICODE, TranslateT("Attachments: %d bytes"), attSize); + mir_snwprintf(msgSubject, L"%S\n%s\n%s", field_date, field_subject_UNICODE, field_attachments_UNICODE); + } + else { + mir_snwprintf(msgSubject, L"%S\n%s", field_date, field_subject_UNICODE); + } + + //check if this is not filtered msg + if (!checkFilters(field_from_UNICODE, 0) + && !checkFilters(field_subject_UNICODE, 1) + && !checkFilters(field_to_UNICODE, 2) + && !checkFilters(field_copy_UNICODE, 2)) { + log(L"checkthread: filters checked - positive"); + ///TODO eliminate popups with blank fields + showMsg(msgFrom, msgSubject, noteID, strLink); + Skin_PlaySound("LotusNotify"); + } + else { + log(L"checkthread: filters checked - negative"); + } + + if (error = NSFNoteClose1(note_handle)) { + continue; + } +#ifdef _DEBUG + log_p(L"checkthread: Close note id: %d", noteID); +#endif + + } + + if (error = IDDestroyTable1(hTable)) { + goto errorblock0; + } +#ifdef _DEBUG + log(L"checkthread: Table destroyed"); +#endif + + if (error = NSFDbClose1(db_handle)) { + goto errorblock; + } +#ifdef _DEBUG + log(L"checkthread: DB closed"); +#endif + + //NotesTerm(); + NotesTermThread1(); + +#ifdef _DEBUG + log(L"checkthread: Terminating Notes thread"); +#endif + running = FALSE; + if (currentStatus != ID_STATUS_OFFLINE) + Menu_EnableItem(hMenuHandle, !running); + return; + +errorblock0: + log(L"checkthread: errorblock0"); + NSFDbClose1(db_handle); +errorblock: + log_p(L"checkthread: errorblock. error=%d", error); + ErMsgByLotusCode(error); + //NotesTerm(); + + // go offline if connection error occurs and let KeepStatus or other plugin managing reconnection + if (!settingKeepConnection && currentStatus != ID_STATUS_OFFLINE) { + Menu_EnableItem(hMenuHandle, !running); + SetStatus(ID_STATUS_OFFLINE, 0); + } + + running = FALSE; + return; +} + + +//hooked notification from service that listning to check lotus +static int eventCheck(WPARAM, LPARAM) +{ + log(L"check event..."); + check(); + return 0; +} + + +//on click to menu callback function +static INT_PTR PluginMenuCommand(WPARAM wParam, LPARAM lParam) +{ + NotifyEventHooks(hCheckEvent, wParam, lParam); //create event to check lotus + return 0; +} + + +//window timer callback function, called on timer event +static void CALLBACK atTime(HWND, UINT, UINT_PTR idEvent, DWORD) +{ + log(L"atTime: start"); + KillTimer(hTimerWnd, idEvent); + if (currentStatus != ID_STATUS_OFFLINE) { + //if status lets to check + check(); + if (settingInterval != 0) { + log_p(L"atTime: SetTimer settingInterval=%d", settingInterval * 60000); + SetTimer(hTimerWnd, TID, settingInterval * 60000, atTime); + } + } +} + + +void decodeServer(char *tmp) +{ + if (strstr(tmp, "CN=") && strstr(tmp, "OU=") && strstr(tmp, "O=")) { + //if lotus convention + while (strrep(tmp, "CN=", "")); + while (strrep(tmp, "OU=", "")); + while (strrep(tmp, "O=", "")); + } +} + + +//fill combo in options dlgbox with all known servers +void fillServersList(HWND hwndDlg) +{ + HANDLE hServerList = NULLHANDLE; + BYTE far *pServerList; /* Pointer to start of Server List */ + WORD wServerCount; /* Number of servers in list. */ + WORD far *pwServerLength; /* Index to array of servername lens */ + BYTE far *pServerName; + STATUS error = NOERROR; /* Error return from API routines. */ + char ServerString[MAXPATH]; /* String to hold server names. */ + LPSTR szServerString = ServerString; + + if (!hLotusDll) { + return; + } + + error = NSGetServerList1(nullptr, &hServerList); + if (error == NOERROR) { + + pServerList = (BYTE far *) OSLockObject1(hServerList); + wServerCount = (WORD)*pServerList; + + pwServerLength = (WORD *)(pServerList + sizeof(WORD)); + + pServerName = (BYTE far *) pServerList + sizeof(wServerCount) + ((wServerCount)* sizeof(WORD)); + + for (USHORT i = 0; i < wServerCount; pServerName += pwServerLength[i], i++) { + memmove(szServerString, pServerName, pwServerLength[i]); + szServerString[pwServerLength[i]] = '\0'; + decodeServer(ServerString); + SendDlgItemMessageA(hwndDlg, IDC_SERVER, CB_ADDSTRING, 0, (LPARAM)szServerString); + } + OSUnlockObject1(hServerList); + OSMemFree1(hServerList); + + } + else { + ErMsgByLotusCode(error); + } +} + + +//gets default settings from notes.ini file +static void lookupLotusDefaultSettings(HWND hwndDlg) +{ + char tmp[MAXENVVALUE + 1]; + // Get the info from the .ini file + if (hLotusDll) { + if (OSGetEnvironmentString1("MailFile", tmp, MAXENVVALUE)) //path to mail file + SetDlgItemTextA(hwndDlg, IDC_DATABASE, tmp); //and set fields in opt. dialog + if (OSGetEnvironmentString1("MailServer", tmp, MAXENVVALUE)) //server name + { + decodeServer(tmp); + SetDlgItemTextA(hwndDlg, IDC_SERVER, tmp); + } + } + +} + +// get variables values stored in db. +static void LoadSettings() +{ + settingInterval = (INT)g_plugin.getDword("LNInterval", 15); + settingInterval1 = (INT)g_plugin.getDword("LNInterval1", 0); + settingKeepConnection = g_plugin.getByte("LNKeepConnection", 1); + + DBVARIANT dbv; + if (!g_plugin.getString("LNDatabase", &dbv)) { + strncpy_s(settingDatabase, _countof(settingDatabase), dbv.pszVal, _countof(settingDatabase)); + db_free(&dbv); + } + if (!g_plugin.getString("LNServer", &dbv)) { + strncpy_s(settingServer, _countof(settingServer), dbv.pszVal, _countof(settingServer)); + db_free(&dbv); + } + if (!g_plugin.getString("LNServerSec", &dbv)) { + strncpy_s(settingServerSec, _countof(settingServerSec), dbv.pszVal, _countof(settingServerSec)); + db_free(&dbv); + } + if (!g_plugin.getString("LNPassword", &dbv)) { + strncpy_s(settingPassword, _countof(settingPassword), dbv.pszVal, _countof(settingPassword)); + db_free(&dbv); + } + if (!g_plugin.getString("LNCommand", &dbv)) { + strncpy_s(settingCommand, _countof(settingCommand), dbv.pszVal, _countof(settingCommand)); + db_free(&dbv); + } + if (!g_plugin.getString("LNParameters", &dbv)) { + strncpy_s(settingParameters, _countof(settingParameters), dbv.pszVal, _countof(settingParameters)); + db_free(&dbv); + } + + if (!g_plugin.getWString("LNFilterSender", &dbv)) { + wcsncpy_s(settingFilterSender, dbv.pwszVal, _TRUNCATE); + db_free(&dbv); + } + if (!g_plugin.getWString("LNFilterSubject", &dbv)) { + wcsncpy_s(settingFilterSubject, dbv.pwszVal, _TRUNCATE); + db_free(&dbv); + } + if (!g_plugin.getWString("LNFilterTo", &dbv)) { + wcsncpy_s(settingFilterTo, dbv.pwszVal, _TRUNCATE); + db_free(&dbv); + } + + settingOnceOnly = g_plugin.getByte("LNOnceOnly", 0); + + settingNonClickedOnly = g_plugin.getByte("LNNonClickedOnly", 1); + settingShowError = g_plugin.getByte("LNShowError", 1); + settingSetColours = g_plugin.getByte("LNSetColours", 0); + settingBgColor = (COLORREF)g_plugin.getDword("LNBgColor", (DWORD)0xFFFFFF); + settingFgColor = (COLORREF)g_plugin.getDword("LNFgColor", (DWORD)0x000000); + settingNewest = g_plugin.getByte("LNNewest", 0); + settingEvenNonClicked = g_plugin.getByte("LNEvenNonClicked", 0); + settingNewestID = (DWORD)g_plugin.getDword("LNNewestID", 0); + settingIniAnswer = g_plugin.getByte("LNIniAnswer", 0); + settingIniCheck = g_plugin.getByte("LNIniCheck", 0); + + for (int i = 0; i < STATUS_COUNT; i++) { + char buff[128]; + mir_snprintf(buff, "LNStatus%d", i); + settingStatus[i] = (g_plugin.getByte(buff, 0) == 1); + } + //lookupLotusDefaultSettings(); +} + +static void SaveSettings(HWND hwndDlg) +{ + char buff[128]; + GetDlgItemTextA(hwndDlg, IDC_SERVER, settingServer, _countof(settingServer)); + g_plugin.setString("LNServer", settingServer); + g_plugin.setString("LNServerSec", settingServerSec); + g_plugin.setString("LNPassword", settingPassword); + g_plugin.setString("LNDatabase", settingDatabase); + g_plugin.setDword("LNInterval", settingInterval); + g_plugin.setDword("LNInterval1", settingInterval1); + g_plugin.setByte("LNKeepConnection", settingKeepConnection); + g_plugin.setString("LNCommand", settingCommand); + g_plugin.setString("LNParameters", settingParameters); + g_plugin.setByte("LNOnceOnly", settingOnceOnly); + g_plugin.setByte("LNNonClickedOnly", settingNonClickedOnly); + g_plugin.setByte("LNShowError", settingShowError); + g_plugin.setByte("LNSetColours", settingSetColours); + g_plugin.setDword("LNBgColor", (DWORD)settingBgColor); + g_plugin.setDword("LNFgColor", (DWORD)settingFgColor); + g_plugin.setByte("LNNewest", settingNewest); + g_plugin.setByte("LNEvenNonClicked", settingEvenNonClicked); + g_plugin.setByte("LNIniCheck", settingIniCheck); + g_plugin.setByte("LNIniAnswer", settingIniAnswer); + + for (int i = 0; i < STATUS_COUNT; i++) { + mir_snprintf(buff, "LNStatus%d", i); + settingStatus[i] = (ListView_GetCheckState(GetDlgItem(hwndDlg, IDC_STATUS), i) ? TRUE : FALSE); + g_plugin.setByte(buff, settingStatus[i] ? 1 : 0); + } + + settingFilterSender[0] = 0; + for (int i = 0; i < SendDlgItemMessage(hwndDlg, IDC_FILTER_SENDER, CB_GETCOUNT, 0, 0); i++) { + wchar_t text[512] = TEXT(""); + SendDlgItemMessage(hwndDlg, IDC_FILTER_SENDER, CB_GETLBTEXT, (WPARAM)i, (LPARAM)text); + wcscat_s(settingFilterSender, _countof(settingFilterSender), text); + wcscat_s(settingFilterSender, _countof(settingFilterSender), TEXT(";")); + } + g_plugin.setWString("LNFilterSender", settingFilterSender); + + settingFilterSubject[0] = 0; + for (int i = 0; i < SendDlgItemMessage(hwndDlg, IDC_FILTER_SUBJECT, CB_GETCOUNT, 0, 0); i++) { + wchar_t text[512] = TEXT(""); + SendDlgItemMessage(hwndDlg, IDC_FILTER_SUBJECT, CB_GETLBTEXT, (WPARAM)i, (LPARAM)text); + wcscat_s(settingFilterSubject, _countof(settingFilterSubject), text); + wcscat_s(settingFilterSubject, _countof(settingFilterSubject), TEXT(";")); + } + g_plugin.setWString("LNFilterSubject", settingFilterSubject); + + settingFilterTo[0] = 0; + for (int i = 0; i < SendDlgItemMessage(hwndDlg, IDC_FILTER_TO, CB_GETCOUNT, 0, 0); i++) { + wchar_t text[512] = TEXT(""); + SendDlgItemMessage(hwndDlg, IDC_FILTER_TO, CB_GETLBTEXT, (WPARAM)i, (LPARAM)text); + wcscat_s(settingFilterTo, _countof(settingFilterTo), text); + wcscat_s(settingFilterTo, _countof(settingFilterTo), TEXT(";")); + } + g_plugin.setWString("LNFilterTo", settingFilterTo); +} + +//callback function to speak with user interactions in options page +static INT_PTR CALLBACK DlgProcLotusNotifyConnectionOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + static bool bInit = false; + + switch (msg) { + case WM_INITDIALOG://initialize dialog, so set properties from db. + bInit = true; + TranslateDialogDefault(hwndDlg);//translate miranda function + LoadSettings(); + CheckDlgButton(hwndDlg, IDC_BUTTON_CHECK, settingIniCheck ? BST_CHECKED : BST_UNCHECKED); + SetDlgItemTextA(hwndDlg, IDC_SERVER, settingServer); + SetDlgItemTextA(hwndDlg, IDC_SERVERSEC, settingServerSec); + SetDlgItemTextA(hwndDlg, IDC_DATABASE, settingDatabase); + SetDlgItemTextA(hwndDlg, IDC_PASSWORD, settingPassword); + SetDlgItemInt(hwndDlg, IDC_INTERVAL, settingInterval, FALSE); + CheckDlgButton(hwndDlg, IDC_KEEP_CONNEXION_ON_ERROR, settingKeepConnection ? BST_CHECKED : BST_UNCHECKED); + bInit = false; + break; + + case WM_COMMAND://user changed something, so get changes to variables + if (!bInit) { + switch (HIWORD(wParam)) { + case EN_CHANGE: // text is modified in an edit ctrl + case BN_CLICKED: // a checkbox is modified + PostMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + break; + } + } + switch (LOWORD(wParam)) { + case IDC_BUTTON_DETECT: + lookupLotusDefaultSettings(hwndDlg); + GetDlgItemTextA(hwndDlg, IDC_SERVER, settingServer, _countof(settingServer)); + GetDlgItemTextA(hwndDlg, IDC_DATABASE, settingDatabase, _countof(settingDatabase)); + break; + case IDC_BUTTON_CHECK: + settingIniCheck = (BYTE)IsDlgButtonChecked(hwndDlg, IDC_BUTTON_CHECK); + checkNotesIniFile(TRUE); + break; + case IDC_DATABASE: + GetDlgItemTextA(hwndDlg, IDC_DATABASE, settingDatabase, _countof(settingDatabase)); + break; + case IDC_SERVER: + switch (HIWORD(wParam)) { + case CBN_SELCHANGE: + { + int i = SendDlgItemMessage(hwndDlg, IDC_SERVER, CB_GETCURSEL, 0, 0); + char text[MAXENVVALUE]; + SendDlgItemMessageA(hwndDlg, IDC_SERVER, CB_GETLBTEXT, (WPARAM)i, (LPARAM)text); + SetDlgItemTextA(hwndDlg, IDC_SERVER, text); + if (!bInit) { + PostMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + } + break; + } + + case CBN_DROPDOWN: + SendDlgItemMessage(hwndDlg, IDC_SERVER, CB_RESETCONTENT, 0, 0); + fillServersList(hwndDlg); + SendDlgItemMessageA(hwndDlg, IDC_SERVER, CB_ADDSTRING, 0, (LPARAM)settingServer); + SendDlgItemMessageA(hwndDlg, IDC_SERVER, CB_SELECTSTRING, -1, (LPARAM)settingServer); + break; + } + break; + case IDC_SERVERSEC: + GetDlgItemTextA(hwndDlg, IDC_SERVERSEC, settingServerSec, _countof(settingServerSec)); + break; + case IDC_PASSWORD: + GetDlgItemTextA(hwndDlg, IDC_PASSWORD, settingPassword, _countof(settingPassword)); + break; + case IDC_INTERVAL: + settingInterval = GetDlgItemInt(hwndDlg, IDC_INTERVAL, nullptr, FALSE); + break; + case IDC_KEEP_CONNEXION_ON_ERROR: + settingKeepConnection = (BYTE)IsDlgButtonChecked(hwndDlg, IDC_KEEP_CONNEXION_ON_ERROR); + break; + } + break; + + case WM_NOTIFY://apply changes so write it to db + switch (((LPNMHDR)lParam)->idFrom) { + case 0: + switch (((LPNMHDR)lParam)->code) { + case PSN_RESET: + LoadSettings(); + return TRUE; + + case PSN_APPLY: + SaveSettings(hwndDlg); + return TRUE; + } + break; + } //id from + + break; //switch(msg) + + } + return FALSE; +} + +static INT_PTR CALLBACK DlgProcLotusNotifyPopupOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + static bool bInit = false; + + switch (msg) { + case WM_INITDIALOG://initialize dialog, so set properties from db. + bInit = true; + TranslateDialogDefault(hwndDlg);//translate miranda function + LoadSettings(); + + CheckDlgButton(hwndDlg, IDC_SETCOLOURS, settingSetColours ? BST_CHECKED : BST_UNCHECKED); + SendDlgItemMessage(hwndDlg, IDC_BGCOLOR, CPM_SETCOLOUR, 0, (LPARAM)settingBgColor); + EnableWindow(GetDlgItem(hwndDlg, IDC_BGCOLOR), settingSetColours != 0); + SendDlgItemMessage(hwndDlg, IDC_FGCOLOR, CPM_SETCOLOUR, 0, (LPARAM)settingFgColor); + EnableWindow(GetDlgItem(hwndDlg, IDC_FGCOLOR), settingSetColours != 0); + + SetDlgItemInt(hwndDlg, IDC_INTERVAL1, settingInterval1, TRUE); + CheckDlgButton(hwndDlg, IDC_ONCEONLY, settingOnceOnly ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hwndDlg, IDC_NONCLICKEDONLY, settingNonClickedOnly ? BST_CHECKED : BST_UNCHECKED); + EnableWindow(GetDlgItem(hwndDlg, IDC_NONCLICKEDONLY), settingOnceOnly != 0); + CheckDlgButton(hwndDlg, IDC_SHOWERROR, settingShowError ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hwndDlg, IDC_NEWEST, settingNewest ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hwndDlg, IDC_REMEMBEREVENNONCLICKED, settingEvenNonClicked ? BST_CHECKED : BST_UNCHECKED); + EnableWindow(GetDlgItem(hwndDlg, IDC_REMEMBEREVENNONCLICKED), settingNewest != 0); + SetDlgItemTextA(hwndDlg, IDC_COMMAND, settingCommand); + SetDlgItemTextA(hwndDlg, IDC_PARAMETERS, settingParameters); + + bInit = FALSE; + break; + + case WM_COMMAND://user changed something, so get changes to variables + if (!bInit) { + switch (HIWORD(wParam)) { + case EN_CHANGE: // text is modified in an edit ctrl + case BN_CLICKED: // a checkbox is modified + case CPN_COLOURCHANGED: // a color has changed + PostMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + break; + } + } + switch (LOWORD(wParam)) { + case IDC_SETCOLOURS: + settingSetColours = IsDlgButtonChecked(hwndDlg, IDC_SETCOLOURS); + EnableWindow(GetDlgItem(hwndDlg, IDC_BGCOLOR), settingSetColours); + EnableWindow(GetDlgItem(hwndDlg, IDC_FGCOLOR), settingSetColours); + break; + case IDC_BGCOLOR: + settingBgColor = (COLORREF)SendDlgItemMessage(hwndDlg, IDC_BGCOLOR, CPM_GETCOLOUR, 0, 0); + break; + case IDC_FGCOLOR: + settingFgColor = (COLORREF)SendDlgItemMessage(hwndDlg, IDC_FGCOLOR, CPM_GETCOLOUR, 0, 0); + break; + case IDC_INTERVAL1: + settingInterval1 = GetDlgItemInt(hwndDlg, IDC_INTERVAL1, nullptr, TRUE); + break; + case IDC_ONCEONLY: + settingOnceOnly = (BYTE)IsDlgButtonChecked(hwndDlg, IDC_ONCEONLY); + EnableWindow(GetDlgItem(hwndDlg, IDC_NONCLICKEDONLY), settingOnceOnly); + break; + case IDC_NONCLICKEDONLY: + settingNonClickedOnly = (BYTE)IsDlgButtonChecked(hwndDlg, IDC_NONCLICKEDONLY); + break; + case IDC_SHOWERROR: + settingShowError = (BYTE)IsDlgButtonChecked(hwndDlg, IDC_SHOWERROR); + break; + case IDC_NEWEST: + settingNewest = (BYTE)IsDlgButtonChecked(hwndDlg, IDC_NEWEST); + EnableWindow(GetDlgItem(hwndDlg, IDC_REMEMBEREVENNONCLICKED), settingNewest); + break; + case IDC_REMEMBEREVENNONCLICKED: + settingEvenNonClicked = (BYTE)IsDlgButtonChecked(hwndDlg, IDC_REMEMBEREVENNONCLICKED); + break; + case IDC_COMMAND: + GetDlgItemTextA(hwndDlg, IDC_COMMAND, settingCommand, _countof(settingCommand)); + break; + case IDC_PARAMETERS: + GetDlgItemTextA(hwndDlg, IDC_PARAMETERS, settingParameters, _countof(settingParameters)); + break; + case IDC_BUTTON_CLEAR: + deleteElements(); + break; + } + break; + + case WM_NOTIFY://apply changes so write it to db + switch (((LPNMHDR)lParam)->idFrom) { + case 0: + { + switch (((LPNMHDR)lParam)->code) { + case PSN_RESET: + LoadSettings(); + return TRUE; + case PSN_APPLY: + SaveSettings(hwndDlg); + + return TRUE; + break; + } + //KillTimer(hTimerWnd,TID); + //if(settingInterval!=0) + // SetTimer(hTimerWnd, TID, settingInterval*60000, (TIMERPROC)atTime); + + break; + } //case 0 + } //id from + + break; //switch(msg) + + } + return FALSE; +} + +static INT_PTR CALLBACK DlgProcLotusNotifyMiscOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + static bool bInit = false; + + wchar_t* strptr; + LVITEM lvI = { 0 }; + LVCOLUMN lvc = { 0 }; + switch (msg) { + case WM_INITDIALOG://initialize dialog, so set properties from db. + { + wchar_t buff[512]; + bInit = true; + TranslateDialogDefault(hwndDlg);//translate miranda function + LoadSettings(); + + //fill filter combos + + wcsncpy_s(buff, settingFilterSender, _TRUNCATE); + while (strptr = wcschr(buff, TEXT(';'))) { + wchar_t tmp[512] = TEXT(""); + wcsncpy_s(tmp, buff, (strptr - buff)); + SendDlgItemMessage(hwndDlg, IDC_FILTER_SENDER, CB_ADDSTRING, 0, (LPARAM)tmp); + wcsncpy_s(buff, strptr + 1, _TRUNCATE); + } + + wcsncpy_s(buff, settingFilterSubject, _TRUNCATE); + while (strptr = wcschr(buff, TEXT(';'))) { + wchar_t tmp[512] = TEXT(""); + wcsncpy_s(tmp, buff, (strptr - buff)); + SendDlgItemMessage(hwndDlg, IDC_FILTER_SUBJECT, CB_ADDSTRING, 0, (LPARAM)tmp); + wcsncpy_s(buff, strptr + 1, _TRUNCATE); + } + + wcsncpy_s(buff, settingFilterTo, _TRUNCATE); + while (strptr = wcschr(buff, TEXT(';'))) { + wchar_t tmp[512] = TEXT(""); + wcsncpy_s(tmp, buff, (strptr - buff)); + SendDlgItemMessage(hwndDlg, IDC_FILTER_TO, CB_ADDSTRING, 0, (LPARAM)tmp); + wcsncpy_s(buff, strptr + 1, _TRUNCATE); + } + + // initialise and fill listbox + HWND hwndList = GetDlgItem(hwndDlg, IDC_STATUS); + ListView_DeleteAllItems(hwndList); + + SendMessage(hwndList, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_FULLROWSELECT | LVS_EX_CHECKBOXES); + + // Initialize the LVCOLUMN structure. + // The mask specifies that the format, width, text, and + // subitem members of the structure are valid. + lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM; + lvc.fmt = LVCFMT_LEFT; + + lvc.iSubItem = 0; + lvc.pszText = TranslateT("Status"); + lvc.cx = 120; // width of column in pixels + ListView_InsertColumn(hwndList, 0, &lvc); + + // Some code to create the list-view control. + // Initialize LVITEM members that are common to all items. + lvI.mask = LVIF_TEXT; + for (int i = 0; i < STATUS_COUNT; i++) { + lvI.pszText = Clist_GetStatusModeDescription(ID_STATUS_ONLINE + i, 0); + lvI.iItem = i; + ListView_InsertItem(hwndList, &lvI); + ListView_SetCheckState(hwndList, i, settingStatus[i]); + } + + bInit = false; + break; + } + case WM_COMMAND://user changed something, so get changes to variables + { + if (!bInit && (HIWORD(wParam) == EN_CHANGE)) { + PostMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + } + char tmp[255]; + int index, size; + switch (LOWORD(wParam)) { + case IDC_BUTTON_ADD_SENDER_FILTER: + GetDlgItemTextA(hwndDlg, IDC_FILTER_SENDER, tmp, _countof(tmp)); + if (strlen(tmp) > 0) { + SendDlgItemMessageA(hwndDlg, IDC_FILTER_SENDER, CB_ADDSTRING, 0, (LPARAM)tmp); + PostMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + } + break; + case IDC_BUTTON_REMOVE_SENDER_FILTER: + index = SendDlgItemMessage(hwndDlg, IDC_FILTER_SENDER, CB_GETCURSEL, 0, 0); + size = SendDlgItemMessage(hwndDlg, IDC_FILTER_SENDER, CB_DELETESTRING, index, 0); + SendDlgItemMessage(hwndDlg, IDC_FILTER_SENDER, CB_SETCURSEL, min(index, size - 1), 0); + PostMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + break; + case IDC_BUTTON_ADD_SUBJECT_FILTER: + GetDlgItemTextA(hwndDlg, IDC_FILTER_SUBJECT, tmp, _countof(tmp)); + if (strlen(tmp) > 0) { + SendDlgItemMessageA(hwndDlg, IDC_FILTER_SUBJECT, CB_ADDSTRING, 0, (LPARAM)tmp); + PostMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + } + break; + case IDC_BUTTON_REMOVE_SUBJECT_FILTER: + index = SendDlgItemMessage(hwndDlg, IDC_FILTER_SUBJECT, CB_GETCURSEL, 0, 0); + size = SendDlgItemMessage(hwndDlg, IDC_FILTER_SUBJECT, CB_DELETESTRING, index, 0); + SendDlgItemMessage(hwndDlg, IDC_FILTER_SUBJECT, CB_SETCURSEL, min(index, size - 1), 0); + PostMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + break; + case IDC_BUTTON_ADD_TO_FILTER: + GetDlgItemTextA(hwndDlg, IDC_FILTER_TO, tmp, _countof(tmp)); + if (strlen(tmp) > 0) { + SendDlgItemMessageA(hwndDlg, IDC_FILTER_TO, CB_ADDSTRING, 0, (LPARAM)tmp); + PostMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + } + break; + case IDC_BUTTON_REMOVE_TO_FILTER: + index = SendDlgItemMessage(hwndDlg, IDC_FILTER_TO, CB_GETCURSEL, 0, 0); + size = SendDlgItemMessage(hwndDlg, IDC_FILTER_TO, CB_DELETESTRING, index, 0); + SendDlgItemMessage(hwndDlg, IDC_FILTER_TO, CB_SETCURSEL, min(index, size - 1), 0); + PostMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + break; + } + break; + } + case WM_NOTIFY://apply changes so write it to db + if (bInit) { + break; + } + switch (((LPNMHDR)lParam)->idFrom) { + case 0: + switch (((LPNMHDR)lParam)->code) { + case PSN_RESET: + LoadSettings(); + return TRUE; + + case PSN_APPLY: + SaveSettings(hwndDlg); + return TRUE; + } + + break; + } //id from + + if (GetDlgItem(hwndDlg, IDC_STATUS) == ((LPNMHDR)lParam)->hwndFrom) { + switch (((LPNMHDR)lParam)->code) { + case LVN_ITEMCHANGED: + { + NMLISTVIEW *nmlv = (NMLISTVIEW *)lParam; + if ((nmlv->uNewState ^ nmlv->uOldState) & LVIS_STATEIMAGEMASK) { + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + } + break; + + } + break; + } + } + break; //switch(msg) + + } + return FALSE; +} + + +//options page on miranda called +int LotusNotifyOptInit(WPARAM wParam, LPARAM) +{ + OPTIONSDIALOGPAGE odp = {}; + odp.szGroup.w = LPGENW("Plugins"); + odp.szTitle.w = _A2W(__PLUGIN_NAME); + odp.flags = ODPF_BOLDGROUPS | ODPF_UNICODE; + + odp.szTab.w = LPGENW("Connection"); + odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_LOTUS_CONECTION); + odp.pfnDlgProc = DlgProcLotusNotifyConnectionOpts; + g_plugin.addOptions(wParam, &odp); + + odp.szTab.w = LPGENW("Popup"); + odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_LOTUS_POPUP); + odp.pfnDlgProc = DlgProcLotusNotifyPopupOpts; + g_plugin.addOptions(wParam, &odp); + + odp.szTab.w = LPGENW("Miscellaneous"); + odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_LOTUS_MISC); + odp.pfnDlgProc = DlgProcLotusNotifyMiscOpts; + g_plugin.addOptions(wParam, &odp); + return 0; +} + + +//gives protocol avainable statuses +INT_PTR GetCaps(WPARAM wParam, LPARAM) +{ + if (wParam == PFLAGNUM_1) + return 0; + if (wParam == PFLAGNUM_2) + return PF2_ONLINE; // add the possible statuses here. + if (wParam == PFLAGNUM_3) + return 0; + return 0; +} + + +//gives name to protocol module +INT_PTR GetName(WPARAM wParam, LPARAM lParam) +{ + strncpy((char*)lParam, MODULENAME, wParam); + return 0; +} + + +//gives icon for proto module +INT_PTR TMLoadIcon(WPARAM wParam, LPARAM) +{ + UINT id; + + switch (wParam & 0xFFFF) { + case PLI_PROTOCOL: + id = IDI_ICON1; + break; // IDI_TM is the main icon for the protocol + default: + return 0; + } + return (INT_PTR)LoadImage(g_plugin.getInst(), MAKEINTRESOURCE(id), IMAGE_ICON, GetSystemMetrics(wParam & PLIF_SMALL ? SM_CXSMICON : SM_CXICON), GetSystemMetrics(wParam & PLIF_SMALL ? SM_CYSMICON : SM_CYICON), 0); +} + + +INT_PTR SetStatus(WPARAM wParam, LPARAM lParam) +{ + if (wParam == ID_STATUS_OFFLINE) { + // the status has been changed to online (maybe run some more code) + Menu_EnableItem(hMenuHandle, FALSE); + diffstat = 0; + + } + else if (wParam == ID_STATUS_ONLINE) { + diffstat = 0; + //Menu_EnableItem(hMenuHandle ,TRUE); + //NotifyEventHooks(hCheckEvent,wParam,lParam); + // the status has been changed to offline (maybe run some more code) + if (currentStatus != ID_STATUS_ONLINE) { + if (startuperror) { + int cnt; + for (cnt = 0; cnt <= 4; cnt++) + if (startuperror >> cnt & 1) + ErMsgT(TranslateW(startuperrors[cnt])); + return 1; + } + + if (check() == 0) { + if (settingInterval != 0) + SetTimer(hTimerWnd, TID, settingInterval * 60000, atTime); + Menu_EnableItem(hMenuHandle, TRUE); + } + else { + ProtoBroadcastAck(MODULENAME, NULL, ACKTYPE_STATUS, ACKRESULT_FAILED, (HANDLE)currentStatus, wParam); + return -1; + } + } + } + else { + int retv; + if (settingStatus[wParam - ID_STATUS_ONLINE]) + retv = SetStatus(ID_STATUS_OFFLINE, lParam); + else + retv = SetStatus(ID_STATUS_ONLINE, lParam); + //Menu_EnableItem(hMenuHandle ,TRUE); + diffstat = wParam; + return retv; + // the status has been changed to unknown (maybe run some more code) + } + //broadcast the message + if (currentStatus != (int)wParam) + ProtoBroadcastAck(MODULENAME, NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)currentStatus, wParam); + currentStatus = wParam; + + return 0; +} + + +void checkEnvPath(wchar_t *path) +{ + log_p(L"checkEnvPath: [%s]", path); + + wcslwr(path); + wchar_t *cur = _wgetenv(L"PATH"); + wcslwr(cur); + wchar_t *found = wcsstr(cur, path); + size_t len = mir_wstrlen(path); + if (found != nullptr && (found[len] == ';' || found[len] == 0 || (found[len] == '\\' && (found[len + 1] == ';' || found[len + 1] == 0)))) + return; + + _wputenv(CMStringW(FORMAT, L"PATH=%s;%s;", cur, path)); +} + +//GetStatus +static INT_PTR GetStatus(WPARAM, LPARAM) +{ + return currentStatus; +} + + +//called after all plugins loaded. +//all lotus staff will be called, that will not hang miranda on startup +static int modulesloaded(WPARAM, LPARAM) +{ + int cnt; + wchar_t path[255] = { 0 }; + + log(L"Modules loaded, lets start LN..."); + + GetLotusPath(path, sizeof(path)); + checkEnvPath(path); + wcscat_s(path, _countof(path), L"nnotes.dll"); + assert(mir_wstrlen(path) > 0); + + log_p(L"Loading dll: %s", path); + + hLotusDll = LoadLibrary(path); + assert(hLotusDll); + if (hLotusDll != nullptr) { + + log(L"Loading LN Functions"); + + if (!HookLotusFunctions()) { + FreeLibrary(hLotusDll); + startuperror += 1; + } + else { + + log(L"Initializing Lotus"); + + if (NotesInitExtended1(0, nullptr)) { + + //initialize lotus //TODO: Lotus can terminate miranda process here with msgbox "Shared Memory from a previous Notes/Domino run has been detected, this process will exit now" + startuperror += 4; + running = TRUE; + Menu_EnableItem(hMenuHandle, !running);//disable menu cause lotus is not initialized + + } + else { + log(L"Checking Notes Ini File"); + if (!checkNotesIniFile(FALSE)) { + startuperror += 16; + } + } + } + + } + else { + startuperror += 2; + } + + assert(startuperror == 0); + for (cnt = 0; cnt <= 4; cnt++) { + if (startuperror >> cnt & 1) + ErMsgT(TranslateW(startuperrors[cnt])); + } + + return 0; +} + + +//function hooks before unload +static int preshutdown(WPARAM, LPARAM) +{ + Plugin_Terminated = true; + deleteElements(); + if (hLotusDll) { + NotesTerm1(); + FreeLibrary(hLotusDll); + } + return 0; +} + + +int CMPlugin::Load() +{ + Plugin_Terminated = false; + + //if(pluginLink)//strange, but this function is called by Lotus API Extension Manager (instead of MainEntryPoint) probably always with parameter poiter =1 + if (bMirandaCall) { + STATUS rc = EMRegister1(EM_GETPASSWORD, EM_REG_BEFORE | EM_REG_AFTER, EMCallBack, 0, &hLotusRegister); //Extension Manager must know that we are here + if (rc) { + //Extension magager don't know who we are :( + startuperror += 8; + // Get the info from the .ini file + } + //log_p(L"Load: Registered Ext. Mngr. res=%d", rc); + return rc; + } + bMirandaCall = TRUE; + + init_pluginname(); + logRegister(); + log_p(L"Load: Entering LotusNotify.dll Load() bMirandaCall=%d MODULENAME=[%S]", bMirandaCall, MODULENAME); + + if (!(hCheckEvent = CreateHookableEvent("LotusNotify/Check"))) //check if there is another copy of plugin running + second = TRUE; + + HookEvent("LotusNotify/Check", eventCheck); //hook function to menu click event + + if (!second) //if its first plugin instance + { + //function that will be called on menu click + CreateServiceFunction("LotusNotify/MenuCommand", PluginMenuCommand); + + CMenuItem mi(&g_plugin); + SET_UID(mi, 0x4519458, 0xb55a, 0x4e22, 0xac, 0x95, 0x5e, 0xa4, 0x4d, 0x92, 0x65, 0x65); + mi.position = -0x7FFFFFFF; //on top menu position + mi.flags = CMIF_UNICODE; + mi.hIcolibItem = LoadIcon(g_plugin.getInst(), MAKEINTRESOURCE(IDI_ICON1)); + mi.name.w = LPGENW("&Check Lotus"); + mi.pszService = "LotusNotify/MenuCommand"; //service name thet listning for menu call + hMenuHandle = Menu_AddMainMenuItem(&mi); //create menu pos. + + Menu_EnableItem(hMenuHandle, FALSE); + } + + // set all contacts to offline + for (auto &hContact : Contacts(MODULENAME)) + g_plugin.setWord(hContact, "status", ID_STATUS_OFFLINE); + + CreateProtoServiceFunction(MODULENAME, PS_GETCAPS, GetCaps); + CreateProtoServiceFunction(MODULENAME, PS_GETNAME, GetName); + CreateProtoServiceFunction(MODULENAME, PS_LOADICON, TMLoadIcon); + CreateProtoServiceFunction(MODULENAME, PS_SETSTATUS, SetStatus); + CreateProtoServiceFunction(MODULENAME, PS_GETSTATUS, GetStatus); + + LoadSettings(); //read from db to variables + + g_plugin.addSound("LotusNotify", LPGENW("Lotus Notify"), LPGENW("New Lotus document detected")); + + HookEvent(ME_OPT_INITIALISE, LotusNotifyOptInit); //register service to hook option call + HookEvent(ME_SYSTEM_MODULESLOADED, modulesloaded); //hook event that all plugins are loaded + HookEvent(ME_SYSTEM_PRESHUTDOWN, preshutdown); + + log(L"Load: ok"); + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +int CMPlugin::Unload() +{ + log(L"Unload: start"); + Plugin_Terminated = true; + mir_cslock lck(checkthreadCS); + + DestroyHookableEvent(hCheckEvent); + + log(L"Unload: ok"); + logUnregister(); + + ExtClear(); + return 0; +} |