diff options
author | George Hazan <ghazan@miranda.im> | 2019-03-02 12:32:44 +0300 |
---|---|---|
committer | George Hazan <ghazan@miranda.im> | 2019-03-02 12:32:55 +0300 |
commit | 931a7dc1ac0dbc7e6c1083583ced915e572f5b47 (patch) | |
tree | 9fe9a6448d44030e26aa7107ce16044ed413e0d0 /protocols/YAMN/src | |
parent | dd7d9954042254e66e3bbbec7195c6be8b1a0663 (diff) |
all protocols (even virtual ones) moved to the Protocols folder
Diffstat (limited to 'protocols/YAMN/src')
30 files changed, 12147 insertions, 0 deletions
diff --git a/protocols/YAMN/src/account.cpp b/protocols/YAMN/src/account.cpp new file mode 100644 index 0000000000..0755276f7b --- /dev/null +++ b/protocols/YAMN/src/account.cpp @@ -0,0 +1,1246 @@ +/* + * This code implements manipulation with accounts + * such as reading accounts from file, writing them to file, + * finding account by name etc. + * + * (c) majvan 2002-2004 + */ + +#include "stdafx.h" + +// Account status CS +// When we check some account, thread should change status of account to idle, connecting etc. +// So if we want to read status, we have to successfully write and then read. +static mir_cs csAccountStatusCS; + +// File Writing CS +// When 2 threads want to write to file... +static mir_cs csFileWritingCS; + +struct CExportedFunctions AccountExportedFcn[] = +{ + { YAMN_GETSTATUSID, (void *)GetStatusFcn }, + { YAMN_SETSTATUSID, (void *)SetStatusFcn }, +}; + +struct CExportedServices AccountExportedSvc[] = +{ + { MS_YAMN_CREATEPLUGINACCOUNT, CreatePluginAccountSvc }, + { MS_YAMN_DELETEPLUGINACCOUNT, DeletePluginAccountSvc }, + { MS_YAMN_FINDACCOUNTBYNAME, FindAccountByNameSvc }, + { MS_YAMN_GETNEXTFREEACCOUNT, GetNextFreeAccountSvc }, + { MS_YAMN_DELETEACCOUNT, DeletePluginAccountSvc }, + { MS_YAMN_READACCOUNTS, AddAccountsFromFileSvc }, + { MS_YAMN_WRITEACCOUNTS, WriteAccountsToFileSvc }, +}; + +//-------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------- + +INT_PTR CreatePluginAccountSvc(WPARAM wParam, LPARAM lParam) +{ + HYAMNPROTOPLUGIN Plugin = (HYAMNPROTOPLUGIN)wParam; + DWORD AccountVersion = (DWORD)lParam; + + //test if we are going to initialize members of suitable structure (structures of plugin and YAMN must match) + if (AccountVersion != YAMN_ACCOUNTVERSION) + return NULL; + + if (Plugin != nullptr) + { + HACCOUNT NewAccount; + if (Plugin->Fcn->NewAccountFcnPtr != nullptr) + //Let plugin create its own structure, which can be derived from CAccount structure + NewAccount = Plugin->Fcn->NewAccountFcnPtr(Plugin, YAMN_ACCOUNTVERSION); + else + //We suggest plugin uses standard CAccount structure, so we create it + NewAccount = new struct CAccount; + + //If not created successfully + if (NewAccount == nullptr) + return NULL; + + NewAccount->Plugin = Plugin; + //Init every members of structure, used by YAMN + InitAccount(NewAccount); + + return (INT_PTR)NewAccount; + } + return NULL; +} + +INT_PTR DeletePluginAccountSvc(WPARAM wParam, LPARAM) +{ + HACCOUNT OldAccount = (HACCOUNT)wParam; + + if (OldAccount->Plugin->Fcn != nullptr) + { + //Deinit every members and allocated fields of structure used by YAMN + DeInitAccount(OldAccount); + if (OldAccount->Plugin->Fcn->DeleteAccountFcnPtr != nullptr) + { + //Let plugin delete its own CAccount derived structure + OldAccount->Plugin->Fcn->DeleteAccountFcnPtr(OldAccount); + } + else + { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"DeletePluginAccountSvc:delete OldAccount\n"); +#endif + delete OldAccount; //consider account as standard YAMN HACCOUNT and use its own destructor + } + return 1; + } + delete OldAccount; //consider account as standard YAMN HACCOUNT, not initialized before and use its own destructor + return 1; +} + +int InitAccount(HACCOUNT Which) +{ + //initialize synchronizing objects + Which->AccountAccessSO = new SWMRG; + SWMRGInitialize(Which->AccountAccessSO, nullptr); + Which->MessagesAccessSO = new SWMRG; + SWMRGInitialize(Which->MessagesAccessSO, nullptr); + Which->UsingThreads = new SCOUNTER; + SWMRGInitialize(Which->MessagesAccessSO, nullptr); + + //zero memory, where timestamps are stored + memset(&Which->LastChecked, 0, sizeof(Which->LastChecked)); + memset(&Which->LastSChecked, 0, sizeof(Which->LastSChecked)); + memset(&Which->LastSynchronised, 0, sizeof(Which->LastSynchronised)); + memset(&Which->LastMail, 0, sizeof(Which->LastMail)); + + Which->Name = nullptr; + Which->Mails = nullptr; + Which->Interval = 0; + Which->Flags = 0; + Which->StatusFlags = 0; + Which->Next = nullptr; + + Which->Server = new struct CServer; + Which->AbleToWork = TRUE; + + return 1; +} + +void DeInitAccount(HACCOUNT Which) +{ + //delete YAMN allocated fields + if (Which->Name != nullptr) + delete[] Which->Name; + if (Which->Server != nullptr) { + if (Which->Server->Name != nullptr) + delete[] Which->Server->Name; + if (Which->Server->Login != nullptr) + delete[] Which->Server->Login; + if (Which->Server->Passwd != nullptr) + delete[] Which->Server->Passwd; + delete[] Which->Server; + } + + SWMRGDelete(Which->AccountAccessSO); + delete Which->AccountAccessSO; + SWMRGDelete(Which->MessagesAccessSO); + delete Which->MessagesAccessSO; + delete Which->UsingThreads; + DeleteMessagesToEndFcn(Which, (HYAMNMAIL)Which->Mails); +} + +void StopSignalFcn(HACCOUNT Which) +//set event that we are going to delete account +{ +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"\tStopSignalFcn:stop account: %x\n",Which); +#endif + Which->AbleToWork = FALSE; + //do not use synchronizing objects anymore + //any access to these objects then ends with WAIT_FAILED + SetEvent(Which->AccountAccessSO->hFinishEV); + SetEvent(Which->MessagesAccessSO->hFinishEV); +} + +void CodeDecodeString(char *Dest, BOOL Encrypt) +{ + wchar_t Code = STARTCODEPSW; + + if (Dest == nullptr) + return; + + for (; *Dest != (wchar_t)0; Dest++) + { + if (Encrypt) + *Dest = *Dest + Code; + else + *Dest = *Dest - Code; + Code += (wchar_t)ADDCODEPSW; + } +} + +static DWORD PostFileToMemory(HANDLE File, char **MemFile, char **End) +{ + DWORD FileSize, ReadBytes; + if (!(FileSize = GetFileSize(File, nullptr))) { + CloseHandle(File); + return EACC_FILESIZE; + } + + //allocate space in memory, where we copy the whole file + if (nullptr == (*MemFile = new char[FileSize])) + { + CloseHandle(File); + return EACC_ALLOC; + } + + //copy file to memory + if (!ReadFile(File, (LPVOID)*MemFile, FileSize, &ReadBytes, nullptr)) + { + CloseHandle(File); + delete[] * MemFile; + return EACC_SYSTEM; + } + CloseHandle(File); + *End = *MemFile + FileSize; + return 0; +} + +DWORD FileToMemory(wchar_t *FileName, char **MemFile, char **End) +{ + HANDLE hFile = CreateFile(FileName, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); + if (hFile == INVALID_HANDLE_VALUE) + return EACC_SYSTEM; + + return PostFileToMemory(hFile, MemFile, End); +} + +#if defined(DEBUG_FILEREAD) || defined(DEBUG_FILEREADMESSAGES) +DWORD ReadStringFromMemory(char **Parser,wchar_t *End,char **StoreTo,wchar_t *DebugString) +{ + //This is the debug version of ReadStringFromMemory function. This version shows MessageBox where + //read string is displayed + wchar_t *Dest,*Finder; + DWORD Size; + wchar_t Debug[65536]; + + Finder=*Parser; + while((*Finder != (wchar_t)0) && (Finder<=End)) Finder++; + mir_snwprintf(Debug, L"%s: %s,length is %d, remaining %d chars", DebugString, *Parser, Finder-*Parser, End-Finder); + MessageBox(NULL,Debug,L"debug",MB_OK); + if (Finder>=End) + return EACC_FILECOMPATIBILITY; + if (Size=Finder-*Parser) + { + if (NULL==(Dest=*StoreTo=new wchar_t[Size+1])) + return EACC_ALLOC; + for (;*Parser<=Finder;(*Parser)++,Dest++) + *Dest=**Parser; + } + else + { + *StoreTo=NULL; + (*Parser)++; + } + return 0; +} +#endif + +DWORD ReadStringFromMemory(char **Parser, char *End, char **StoreTo) +{ + char *Dest, *Finder; + DWORD Size; + + Finder = *Parser; + while ((*Finder != (wchar_t)0) && (Finder <= End)) Finder++; + if (Finder >= End) + return EACC_FILECOMPATIBILITY; + if (Size = Finder - *Parser) + { + if (nullptr == (Dest = *StoreTo = new char[Size + 1])) + return EACC_ALLOC; + for (; *Parser <= Finder; (*Parser)++, Dest++) + *Dest = **Parser; + } + else + { + *StoreTo = nullptr; + (*Parser)++; + } + return 0; +} + +#if defined(DEBUG_FILEREAD) || defined(DEBUG_FILEREADMESSAGES) +DWORD ReadStringFromMemoryW(WCHAR **Parser,wchar_t *End,WCHAR **StoreTo,WCHAR *DebugString) +{ + //This is the debug version of ReadStringFromMemoryW function. This version shows MessageBox where + //read string is displayed + WCHAR *Dest,*Finder; + DWORD Size; + WCHAR Debug[65536]; + + Finder=*Parser; + while((*Finder != (WCHAR)0) && (Finder<=(WCHAR *)End)) Finder++; + mir_snwprintf(Debug, L"%s: %s,length is %d, remaining %d chars", DebugString, *Parser, Finder-*Parser, (WCHAR *)End-Finder); + MessageBoxW(NULL,Debug,L"debug",MB_OK); + if (Finder>=(WCHAR *)End) + return EACC_FILECOMPATIBILITY; + if (Size=Finder-*Parser) + { + if (NULL==(Dest=*StoreTo=new WCHAR[Size+1])) + return EACC_ALLOC; + for (;*Parser<=Finder;(*Parser)++,Dest++) + *Dest=**Parser; + } + else + { + *StoreTo=NULL; + (*Parser)++; + } + return 0; +} +#endif //if defined(DEBUG...) + +DWORD ReadStringFromMemoryW(WCHAR **Parser, WCHAR *End, WCHAR **StoreTo) +{ + WCHAR *Dest, *Finder; + DWORD Size; + + Finder = *Parser; + while ((*Finder != (WCHAR)0) && (Finder <= (WCHAR *)End)) Finder++; + if (Finder >= (WCHAR *)End) + return EACC_FILECOMPATIBILITY; + if (Size = Finder - *Parser) + { + if (nullptr == (Dest = *StoreTo = new WCHAR[Size + 1])) + return EACC_ALLOC; + for (; *Parser <= Finder; (*Parser)++, Dest++) + *Dest = **Parser; + } + else + { + *StoreTo = nullptr; + (*Parser)++; + } + return 0; +} + +static DWORD ReadNotificationFromMemory(char **Parser, char *End, YAMN_NOTIFICATION *Which) +{ + DWORD Stat; +#ifdef DEBUG_FILEREAD + wchar_t Debug[65536]; +#endif + + Which->Flags = *(DWORD *)(*Parser); + (*Parser) += sizeof(DWORD); + if (*Parser >= End) + return EACC_FILECOMPATIBILITY; +#ifdef DEBUG_FILEREAD + mir_snwprintf(Debug, L"NFlags: %04x, remaining %d chars", Which->Flags, End-*Parser); + MessageBox(NULL,Debug,L"debug",MB_OK); +#endif + + Which->PopupB = *(COLORREF *)(*Parser); + (*Parser) += sizeof(COLORREF); + if (*Parser >= End) + return EACC_FILECOMPATIBILITY; +#ifdef DEBUG_FILEREAD + mir_snwprintf(Debug, L"PopupB: %04x, remaining %d chars", Which->PopupB, End-*Parser); + MessageBox(NULL,Debug,L"debug",MB_OK); +#endif + Which->PopupT = *(COLORREF *)(*Parser); + (*Parser) += sizeof(COLORREF); + if (*Parser >= End) + return EACC_FILECOMPATIBILITY; +#ifdef DEBUG_FILEREAD + mir_snwprintf(Debug, L"PopupT: %04x, remaining %d chars", Which->PopupT, End-*Parser); + MessageBox(NULL,Debug,L"debug",MB_OK); +#endif + Which->PopupTime = *(DWORD *)(*Parser); + (*Parser) += sizeof(DWORD); + if (*Parser >= End) + return EACC_FILECOMPATIBILITY; +#ifdef DEBUG_FILEREAD + mir_snwprintf(Debug, L"PopupTime: %04x, remaining %d chars", Which->PopupTime, End-*Parser); + MessageBox(NULL,Debug,L"debug",MB_OK); +#endif + +#ifdef DEBUG_FILEREAD + if (Stat=ReadStringFromMemoryW((WCHAR **)Parser,(WCHAR*)End,&Which->App,L"App")) +#else + if (Stat = ReadStringFromMemoryW((WCHAR **)Parser, (WCHAR*)End, &Which->App)) +#endif + return Stat; +#ifdef DEBUG_FILEREAD + if (Stat=ReadStringFromMemoryW((WCHAR **)Parser,(WCHAR*)End,&Which->AppParam,L"AppParam")) +#else + if (Stat = ReadStringFromMemoryW((WCHAR **)Parser, (WCHAR*)End, &Which->AppParam)) +#endif + return Stat; + return 0; +} + +DWORD ReadMessagesFromMemory(HACCOUNT Which, char **Parser, char *End) +{ + char *Finder; + DWORD Size, Stat; + HYAMNMAIL ActualMail = nullptr; + struct CMimeItem *items; + char *ReadString; + +#ifdef DEBUG_FILEREAD + MessageBox(NULL,L"going to read messages, if any...",L"debug",MB_OK); +#endif + do + { + Finder = *Parser; + while ((*Finder != (wchar_t)0) && (Finder <= End)) Finder++; + if (Finder >= End) + return EACC_FILECOMPATIBILITY; + if (Size = Finder - *Parser) + { + if (Which->Mails == nullptr) //First message in queue + { + if (nullptr == (Which->Mails = ActualMail = CreateAccountMail(Which))) + return EACC_ALLOC; + } + else + { + if (nullptr == (ActualMail->Next = CreateAccountMail(Which))) { + return EACC_ALLOC; + } + ActualMail = ActualMail->Next; + } + items = nullptr; +#ifdef DEBUG_FILEREADMESSAGES + if (Stat=ReadStringFromMemory(Parser,End,&ActualMail->ID,L"ID")) +#else + if (Stat = ReadStringFromMemory(Parser, End, &ActualMail->ID)) +#endif + return Stat; + // ActualMail->MailData=new MAILDATA; !!! mem leake !!! this is alloc by CreateAccountMail, no need for doubble alloc !!!! + + ActualMail->MailData->Size = *(DWORD *)(*Parser); + (*Parser) += sizeof(DWORD); + if (*Parser >= End) + return EACC_FILECOMPATIBILITY; + ActualMail->Flags = *(DWORD *)(*Parser); + (*Parser) += sizeof(DWORD); + if (*Parser >= End) + return EACC_FILECOMPATIBILITY; + ActualMail->Number = *(DWORD *)(*Parser); + (*Parser) += sizeof(DWORD); + if (*Parser >= End) + return EACC_FILECOMPATIBILITY; + + if ((nullptr != Which->Plugin->MailFcn) && (nullptr != Which->Plugin->MailFcn->ReadMailOptsFcnPtr)) + Which->Plugin->MailFcn->ReadMailOptsFcnPtr(ActualMail, Parser, End); //read plugin mail settings from file + + do + { +#if defined(DEBUG_FILEREADMESSAGES) || defined(DEBUG_FILEREAD) + if (Stat=ReadStringFromMemory(Parser,End,&ReadString,L"Name")) +#else + if (Stat = ReadStringFromMemory(Parser, End, &ReadString)) +#endif + return Stat; + if (ReadString == nullptr) + break; + +#ifdef DEBUG_DECODE + DebugLog(DecodeFile,"<read name>%s</read name>",ReadString); +#endif + + if (items == nullptr) + items = ActualMail->MailData->TranslatedHeader = new struct CMimeItem; + else + { + items->Next = new struct CMimeItem; + items = items->Next; + } + if (items == nullptr) + return EACC_ALLOC; + items->name = ReadString; + +#ifdef DEBUG_FILEREADMESSAGES + if (Stat=ReadStringFromMemory(Parser,End,&ReadString,L"Value")) +#else + if (Stat = ReadStringFromMemory(Parser, End, &ReadString)) +#endif + return Stat; + items->value = ReadString; +#ifdef DEBUG_DECODE + DebugLog(DecodeFile,"<read value>%s</read value>\n",ReadString); +#endif + } while (1); + } + else + break; //no next messages, new account! + + } while (1); + (*Parser)++; + return 0; +} + +DWORD ReadAccountFromMemory(HACCOUNT Which, char **Parser, char *End) +{ + DWORD Stat; +#ifdef DEBUG_FILEREAD + wchar_t Debug[65536]; +#endif + //Read name of account + +#ifdef DEBUG_FILEREAD + if (Stat=ReadStringFromMemory(Parser,End,&Which->Name,L"Name")) +#else + if (Stat = ReadStringFromMemory(Parser, End, &Which->Name)) +#endif + return Stat; + if (Which->Name == nullptr) + return EACC_FILECOMPATIBILITY; + + //Read server parameters +#ifdef DEBUG_FILEREAD + if (Stat=ReadStringFromMemory(Parser,End,&Which->Server->Name,L"Server")) +#else + if (Stat = ReadStringFromMemory(Parser, End, &Which->Server->Name)) +#endif + return Stat; + Which->Server->Port = *(WORD *)(*Parser); + (*Parser) += sizeof(WORD); + if (*Parser >= End) + return EACC_FILECOMPATIBILITY; +#ifdef DEBUG_FILEREAD + mir_snwprintf(Debug, L"Port: %d, remaining %d chars", Which->Server->Port, End-*Parser); + MessageBox(NULL,Debug,L"debug",MB_OK); +#endif +#ifdef DEBUG_FILEREAD + if (Stat=ReadStringFromMemory(Parser,End,&Which->Server->Login,L"Login")) +#else + if (Stat = ReadStringFromMemory(Parser, End, &Which->Server->Login)) +#endif + return Stat; +#ifdef DEBUG_FILEREAD + if (Stat=ReadStringFromMemory(Parser,End,&Which->Server->Passwd,L"Password")) +#else + if (Stat = ReadStringFromMemory(Parser, End, &Which->Server->Passwd)) +#endif + return Stat; + CodeDecodeString(Which->Server->Passwd, FALSE); + + //Read account flags + Which->Flags = *(DWORD *)(*Parser); + (*Parser) += sizeof(DWORD); + if (*Parser >= End) + return EACC_FILECOMPATIBILITY; +#ifdef DEBUG_FILEREAD + mir_snwprintf(Debug, L"Flags: %04x, remaining %d chars", Which->Flags, End-*Parser); + MessageBox(NULL,Debug,L"debug",MB_OK); +#endif + Which->StatusFlags = *(DWORD *)(*Parser); + (*Parser) += sizeof(DWORD); +#ifdef DEBUG_FILEREAD + mir_snwprintf(Debug, L"STFlags: %04x, remaining %d chars", Which->StatusFlags, End-*Parser); + MessageBox(NULL,Debug,L"debug",MB_OK); +#endif + Which->PluginFlags = *(DWORD *)(*Parser); + (*Parser) += sizeof(DWORD); +#ifdef DEBUG_FILEREAD + mir_snwprintf(Debug, L"PFlags: %04x, remaining %d chars", Which->PluginFlags, End-*Parser); + MessageBox(NULL,Debug,L"debug",MB_OK); +#endif + + //Read account miscellaneous parameters + Which->Interval = *(WORD *)(*Parser); + Which->TimeLeft = Which->Interval; //check on loading + (*Parser) += sizeof(WORD); + if (*Parser >= End) + return EACC_FILECOMPATIBILITY; +#ifdef DEBUG_FILEREAD + mir_snwprintf(Debug, L"Interval: %d, remaining %d chars", Which->Interval, End-*Parser); + MessageBox(NULL,Debug,L"debug",MB_OK); +#endif + + //Read notification parameters + if (Stat = ReadNotificationFromMemory(Parser, End, &Which->NewMailN)) + return Stat; + if (Stat = ReadNotificationFromMemory(Parser, End, &Which->NoNewMailN)) + return Stat; + if (Stat = ReadNotificationFromMemory(Parser, End, &Which->BadConnectN)) + return Stat; + + //Let plugin read its own data stored in file + if (Which->Plugin->Fcn != nullptr && Which->Plugin->Fcn->ReadPluginOptsFcnPtr != nullptr) + if (Stat = Which->Plugin->Fcn->ReadPluginOptsFcnPtr(Which, Parser, End)) + return Stat; + //Read mails +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"ReadAccountFromMemory:ActualAccountMsgsSO-write wait\n"); +#endif + WaitToWriteFcn(Which->MessagesAccessSO); +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"ReadAccountFromMemory:ActualAccountMsgsSO-write enter\n"); +#endif + if (Stat = ReadMessagesFromMemory(Which, Parser, End)) + { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"ReadAccountFromMemory:ActualAccountMsgsSO-write done\n"); +#endif + WriteDoneFcn(Which->MessagesAccessSO); + return Stat; + } +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"ReadAccountFromMemory:ActualAccountMsgsSO-write done\n"); +#endif + WriteDoneFcn(Which->MessagesAccessSO); + + //Read timestamps + Which->LastChecked = *(SYSTEMTIME *)(*Parser); + (*Parser) += sizeof(SYSTEMTIME); + if (*Parser >= End) + return EACC_FILECOMPATIBILITY; +#ifdef DEBUG_FILEREAD + mir_snwprintf(Debug, L"LastChecked: %04x, remaining %d chars", Which->LastChecked, End-*Parser); + MessageBox(NULL,Debug,L"debug",MB_OK); +#endif + Which->LastSChecked = *(SYSTEMTIME *)(*Parser); + (*Parser) += sizeof(SYSTEMTIME); + if (*Parser >= End) + return EACC_FILECOMPATIBILITY; +#ifdef DEBUG_FILEREAD + mir_snwprintf(Debug, L"LastSChecked: %04x, remaining %d chars", Which->LastSChecked, End-*Parser); + MessageBox(NULL,Debug,L"debug",MB_OK); +#endif + Which->LastSynchronised = *(SYSTEMTIME *)(*Parser); + (*Parser) += sizeof(SYSTEMTIME); + if (*Parser >= End) + return EACC_FILECOMPATIBILITY; +#ifdef DEBUG_FILEREAD + mir_snwprintf(Debug, L"LastSynchronised: %04x, remaining %d chars", Which->LastSynchronised, End-*Parser); + MessageBox(NULL,Debug,L"debug",MB_OK); +#endif + Which->LastMail = *(SYSTEMTIME *)(*Parser); + (*Parser) += sizeof(SYSTEMTIME); + if (*Parser > End) //WARNING! There's only > at the end of testing + return EACC_FILECOMPATIBILITY; +#ifdef DEBUG_FILEREAD + mir_snwprintf(Debug, L"LastMail: %04x, remaining %d chars", Which->LastMail, End-*Parser); + MessageBox(NULL,Debug,L"debug",MB_OK); +#endif + if (*Parser == End) + return EACC_ENDOFFILE; + return 0; + +} + +static INT_PTR PerformAccountReading(HYAMNPROTOPLUGIN Plugin, char *MemFile, char *End) +{ + //Retrieve info for account from memory + char *Parser; + DWORD Ver, Stat; + + HACCOUNT ActualAccount, FirstAllocatedAccount; + + Ver = *(DWORD *)MemFile; + if (Ver > YAMN_ACCOUNTFILEVERSION) + { + delete[] MemFile; + return EACC_FILEVERSION; + } + Parser = MemFile + sizeof(Ver); + +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"AddAccountsFromFile:AccountBrowserSO-write wait\n"); +#endif + SWMRGWaitToWrite(Plugin->AccountBrowserSO, INFINITE); +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"AddAccountsFromFile:AccountBrowserSO-write enter\n"); +#endif + if (nullptr == (ActualAccount = (HACCOUNT)CallService(MS_YAMN_GETNEXTFREEACCOUNT, (WPARAM)Plugin, (LPARAM)YAMN_ACCOUNTVERSION))) + { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"AddAccountsFromFile:AccountBrowserSO-write done\n"); +#endif + SWMRGDoneWriting(Plugin->AccountBrowserSO); + delete[] MemFile; + return EACC_ALLOC; + } + FirstAllocatedAccount = ActualAccount; + + do + { + HACCOUNT Temp; + +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"AddAccountsFromFile:ActualAccountSO-write wait\n"); +#endif + WaitToWriteFcn(ActualAccount->AccountAccessSO); +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"AddAccountsFromFile:ActualAccountSO-write enter\n"); +#endif + Stat = ReadAccountFromMemory(ActualAccount, &Parser, End); + + if (ActualAccount->StatusFlags & (YAMN_ACC_STARTA | YAMN_ACC_STARTS)) + ActualAccount->TimeLeft = 1; //check on loading + + if (Stat && (Stat != EACC_ENDOFFILE)) + { + for (ActualAccount = FirstAllocatedAccount; ActualAccount != nullptr; ActualAccount = Temp) + { + Temp = ActualAccount->Next; + delete ActualAccount; + } + delete[] MemFile; + if (Plugin->FirstAccount == FirstAllocatedAccount) + Plugin->FirstAccount = nullptr; +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"AddAccountsFromFile:ActualAccountSO-write done\n"); +#endif + SWMRGDoneWriting(Plugin->AccountBrowserSO); + return (INT_PTR)Stat; + } + +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"AddAccountsFromFile:ActualAccountSO-write done\n"); +#endif + WriteDoneFcn(ActualAccount->AccountAccessSO); + + if ((Stat != EACC_ENDOFFILE) && (nullptr == (ActualAccount = (HACCOUNT)CallService(MS_YAMN_GETNEXTFREEACCOUNT, (WPARAM)Plugin, (LPARAM)YAMN_ACCOUNTVERSION)))) + { + for (ActualAccount = FirstAllocatedAccount; ActualAccount != nullptr; ActualAccount = Temp) + { + Temp = ActualAccount->Next; + delete ActualAccount; + } + delete[] MemFile; + if (Plugin->FirstAccount == FirstAllocatedAccount) + Plugin->FirstAccount = nullptr; +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"AddAccountsFromFile:AccountBrowserSO-write done\n"); +#endif + SWMRGDoneWriting(Plugin->AccountBrowserSO); + return EACC_ALLOC; + } + } while (Stat != EACC_ENDOFFILE); + +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"AddAccountsFromFile:AccountBrowserSO-write done\n"); +#endif + SWMRGDoneWriting(Plugin->AccountBrowserSO); + delete[] MemFile; + + return 0; +} + +// Add accounts from file to memory +INT_PTR AddAccountsFromFileSvc(WPARAM wParam, LPARAM lParam) +{ + char *MemFile, *End; + DWORD Stat = FileToMemory((wchar_t*)lParam, &MemFile, &End); + if (Stat != NO_ERROR) + return (INT_PTR)Stat; + + return PerformAccountReading((HYAMNPROTOPLUGIN)wParam, MemFile, End); +} + +DWORD WriteStringToFile(HANDLE File, char *Source) +{ + DWORD Length, WrittenBytes; + char null = 0; + + if ((Source == nullptr) || !(Length = (DWORD)mir_strlen(Source))) { + if (!WriteFile(File, &null, 1, &WrittenBytes, nullptr)) { + CloseHandle(File); + return EACC_SYSTEM; + } + } + else if (!WriteFile(File, Source, (Length + 1), &WrittenBytes, nullptr)) { + CloseHandle(File); + return EACC_SYSTEM; + } + return 0; +} + +DWORD WriteStringToFileW(HANDLE File, WCHAR *Source) +{ + DWORD Length, WrittenBytes; + WCHAR null = (WCHAR)0; + + if ((Source == nullptr) || !(Length = (DWORD)mir_wstrlen(Source))) + { + if (!WriteFile(File, &null, sizeof(WCHAR), &WrittenBytes, nullptr)) + { + CloseHandle(File); + return EACC_SYSTEM; + } + } + else if (!WriteFile(File, Source, (Length + 1)*sizeof(WCHAR), &WrittenBytes, nullptr)) + return EACC_SYSTEM; + return 0; +} + +DWORD WriteMessagesToFile(HANDLE File, HACCOUNT Which) +{ + DWORD WrittenBytes, Stat; + HYAMNMAIL ActualMail = (HYAMNMAIL)Which->Mails; + struct CMimeItem *items; + + while (ActualMail != nullptr) + { + if (Stat = WriteStringToFile(File, ActualMail->ID)) + return Stat; + if (!WriteFile(File, (char *)&ActualMail->MailData->Size, sizeof(ActualMail->MailData->Size), &WrittenBytes, nullptr) || + !WriteFile(File, (char *)&ActualMail->Flags, sizeof(ActualMail->Flags), &WrittenBytes, nullptr) || + !WriteFile(File, (char *)&ActualMail->Number, sizeof(ActualMail->Number), &WrittenBytes, nullptr)) + return EACC_SYSTEM; + if ((nullptr != Which->Plugin->MailFcn) && (nullptr != Which->Plugin->MailFcn->WriteMailOptsFcnPtr)) + Which->Plugin->MailFcn->WriteMailOptsFcnPtr(File, ActualMail); //write plugin mail options to file + for (items = ActualMail->MailData->TranslatedHeader; items != nullptr; items = items->Next) + { + if (Stat = WriteStringToFile(File, items->name)) + return Stat; + if (Stat = WriteStringToFile(File, items->value)) + return Stat; + } + if (Stat = WriteStringToFile(File, "")) + return Stat; + ActualMail = ActualMail->Next; + } + if (Stat = WriteStringToFile(File, "")) + return Stat; + return 0; +} + +static INT_PTR PerformAccountWriting(HYAMNPROTOPLUGIN Plugin, HANDLE File) +{ + DWORD WrittenBytes, Stat; + HACCOUNT ActualAccount; + DWORD Ver = YAMN_ACCOUNTFILEVERSION; + BOOL Writed = FALSE; + DWORD ReturnValue = 0, EnterCode; + +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"WriteAccountsToFile:AccountBrowserSO-read wait\n"); +#endif + SWMRGWaitToRead(Plugin->AccountBrowserSO, INFINITE); +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"WriteAccountsToFile:AccountBrowserSO-read enter\n"); +#endif + try + { + for (ActualAccount = Plugin->FirstAccount; ActualAccount != nullptr; ActualAccount = ActualAccount->Next) + { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"WriteAccountsToFile:ActualAccountSO-read wait\n"); +#endif + EnterCode = WaitToReadFcn(ActualAccount->AccountAccessSO); + if (EnterCode == WAIT_FINISH) //account is about to delete + { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"WriteAccountsToFile:ActualAccountSO-read wait failed\n"); +#endif + ActualAccount = ActualAccount->Next; + continue; + } + if (EnterCode == WAIT_FAILED) //account is deleted + break; +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"WriteAccountsToFile:ActualAccountSO-read enter\n"); +#endif + if ((ActualAccount->Name == nullptr) || (*ActualAccount->Name == (wchar_t)0)) + { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"WriteAccountsToFile:ActualAccountSO-read done\n"); +#endif + ReadDoneFcn(ActualAccount->AccountAccessSO); + continue; + } + + if (!Writed && !WriteFile(File, &Ver, sizeof(Ver), &WrittenBytes, nullptr)) + throw (DWORD)EACC_SYSTEM; + Writed = TRUE; + + if (Stat = WriteStringToFile(File, ActualAccount->Name)) + throw (DWORD)Stat; + + if (Stat = WriteStringToFile(File, ActualAccount->Server->Name)) + throw (DWORD)Stat; + + if (!WriteFile(File, (char *)&ActualAccount->Server->Port, 2, &WrittenBytes, nullptr)) + throw (DWORD)EACC_SYSTEM; + + if ((Stat = WriteStringToFile(File, ActualAccount->Server->Login))) + throw (DWORD)Stat; + + CodeDecodeString(ActualAccount->Server->Passwd, TRUE); + + if (Stat = WriteStringToFile(File, ActualAccount->Server->Passwd)) + { + CodeDecodeString(ActualAccount->Server->Passwd, FALSE); + throw (DWORD)Stat; + } + CodeDecodeString(ActualAccount->Server->Passwd, FALSE); + + if ((!WriteFile(File, (char *)&ActualAccount->Flags, sizeof(DWORD), &WrittenBytes, nullptr) || + (!WriteFile(File, (char *)&ActualAccount->StatusFlags, sizeof(DWORD), &WrittenBytes, nullptr)) || + (!WriteFile(File, (char *)&ActualAccount->PluginFlags, sizeof(DWORD), &WrittenBytes, nullptr)))) + throw (DWORD)EACC_SYSTEM; + + if (!WriteFile(File, (char *)&ActualAccount->Interval, sizeof(WORD), &WrittenBytes, nullptr)) + throw (DWORD)EACC_SYSTEM; + + if ((!WriteFile(File, (char *)&ActualAccount->NewMailN.Flags, sizeof(DWORD), &WrittenBytes, nullptr)) || + (!WriteFile(File, (char *)&ActualAccount->NewMailN.PopupB, sizeof(COLORREF), &WrittenBytes, nullptr)) || + (!WriteFile(File, (char *)&ActualAccount->NewMailN.PopupT, sizeof(COLORREF), &WrittenBytes, nullptr)) || + (!WriteFile(File, (char *)&ActualAccount->NewMailN.PopupTime, sizeof(DWORD), &WrittenBytes, nullptr))) + throw (DWORD)EACC_SYSTEM; + + if ((Stat = WriteStringToFileW(File, ActualAccount->NewMailN.App)) || + (Stat = WriteStringToFileW(File, ActualAccount->NewMailN.AppParam))) + throw (DWORD)Stat; + + if ((!WriteFile(File, (char *)&ActualAccount->NoNewMailN.Flags, sizeof(DWORD), &WrittenBytes, nullptr)) || + (!WriteFile(File, (char *)&ActualAccount->NoNewMailN.PopupB, sizeof(COLORREF), &WrittenBytes, nullptr)) || + (!WriteFile(File, (char *)&ActualAccount->NoNewMailN.PopupT, sizeof(COLORREF), &WrittenBytes, nullptr)) || + (!WriteFile(File, (char *)&ActualAccount->NoNewMailN.PopupTime, sizeof(DWORD), &WrittenBytes, nullptr))) + throw (DWORD)EACC_SYSTEM; + + if ((Stat = WriteStringToFileW(File, ActualAccount->NoNewMailN.App)) || + (Stat = WriteStringToFileW(File, ActualAccount->NoNewMailN.AppParam))) + throw (DWORD)Stat; + + if ((!WriteFile(File, (char *)&ActualAccount->BadConnectN.Flags, sizeof(DWORD), &WrittenBytes, nullptr)) || + (!WriteFile(File, (char *)&ActualAccount->BadConnectN.PopupB, sizeof(COLORREF), &WrittenBytes, nullptr)) || + (!WriteFile(File, (char *)&ActualAccount->BadConnectN.PopupT, sizeof(COLORREF), &WrittenBytes, nullptr)) || + (!WriteFile(File, (char *)&ActualAccount->BadConnectN.PopupTime, sizeof(DWORD), &WrittenBytes, nullptr))) + throw (DWORD)EACC_SYSTEM; + + if ((Stat = WriteStringToFileW(File, ActualAccount->BadConnectN.App)) || + (Stat = WriteStringToFileW(File, ActualAccount->BadConnectN.AppParam))) + throw (DWORD)Stat; + + //Let plugin write its own values into file + if (ActualAccount->Plugin->Fcn != nullptr && ActualAccount->Plugin->Fcn->WritePluginOptsFcnPtr != nullptr) + if (Stat = ActualAccount->Plugin->Fcn->WritePluginOptsFcnPtr(File, ActualAccount)) + throw (DWORD)Stat; +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"WriteAccountsToFile:ActualAccountMsgsSO-read wait\n"); +#endif + WaitToReadFcn(ActualAccount->MessagesAccessSO); +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"WriteAccountsToFile:ActualAccountMsgsSO-read enter\n"); +#endif + if (Stat = WriteMessagesToFile(File, ActualAccount)) + { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"WriteAccountsToFile:ActualAccountMsgsSO-read done\n"); +#endif + ReadDoneFcn(ActualAccount->MessagesAccessSO); + throw (DWORD)Stat; + } +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"WriteAccountsToFile:ActualAccountMsgsSO-read done\n"); +#endif + ReadDoneFcn(ActualAccount->MessagesAccessSO); + + if ((!WriteFile(File, (char *)&ActualAccount->LastChecked, sizeof(SYSTEMTIME), &WrittenBytes, nullptr)) || + (!WriteFile(File, (char *)&ActualAccount->LastSChecked, sizeof(SYSTEMTIME), &WrittenBytes, nullptr)) || + (!WriteFile(File, (char *)&ActualAccount->LastSynchronised, sizeof(SYSTEMTIME), &WrittenBytes, nullptr)) || + (!WriteFile(File, (char *)&ActualAccount->LastMail, sizeof(SYSTEMTIME), &WrittenBytes, nullptr))) + throw (DWORD)Stat; + +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"WriteAccountsToFile:ActualAccountSO-read done\n"); +#endif + ReadDoneFcn(ActualAccount->AccountAccessSO); + } + } + catch (DWORD ErrorCode) + { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"WriteAccountsToFile:ActualAccountSO-read done\n"); +#endif + ReadDoneFcn(ActualAccount->AccountAccessSO); + ReturnValue = ErrorCode; + } +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"WriteAccountsToFile:AccountBrowserSO-read done\n"); +#endif + SWMRGDoneReading(Plugin->AccountBrowserSO); + CloseHandle(File); + return 0; +} + +// Writes accounts to file +INT_PTR WriteAccountsToFileSvc(WPARAM wParam, LPARAM lParam) +{ + HYAMNPROTOPLUGIN Plugin = (HYAMNPROTOPLUGIN)wParam; + + mir_cslock lck(csFileWritingCS); + HANDLE hFile = CreateFile((wchar_t*)lParam, GENERIC_WRITE, FILE_SHARE_WRITE, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); + if (hFile == INVALID_HANDLE_VALUE) + return EACC_SYSTEM; + + return PerformAccountWriting(Plugin, hFile); +} + +INT_PTR FindAccountByNameSvc(WPARAM wParam, LPARAM lParam) +{ + HYAMNPROTOPLUGIN Plugin = (HYAMNPROTOPLUGIN)wParam; + char *SearchedAccount = (char *)lParam; + HACCOUNT Finder; + +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"FindAccountByName:AccountBrowserSO-read wait\n"); +#endif + SWMRGWaitToRead(Plugin->AccountBrowserSO, INFINITE); +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"FindAccountByName:AccountBrowserSO-read enter\n"); +#endif + for (Finder = Plugin->FirstAccount; Finder != nullptr; Finder = Finder->Next) + if ((Finder->Name != nullptr) && (0 == mir_strcmp(SearchedAccount, Finder->Name))) + break; +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"FindAccountByName:AccountBrowserSO-read done\n"); +#endif + SWMRGDoneReading(Plugin->AccountBrowserSO); + return (INT_PTR)Finder; +} + +INT_PTR GetNextFreeAccountSvc(WPARAM wParam, LPARAM lParam) +{ + HYAMNPROTOPLUGIN Plugin = (HYAMNPROTOPLUGIN)wParam; + HACCOUNT Finder; + + if (Plugin->FirstAccount == nullptr) + { + Plugin->FirstAccount = (HACCOUNT)CallService(MS_YAMN_CREATEPLUGINACCOUNT, wParam, lParam); + return (INT_PTR)Plugin->FirstAccount; + } + for (Finder = Plugin->FirstAccount; Finder->Next != nullptr; Finder = Finder->Next); + Finder->Next = (HACCOUNT)CallService(MS_YAMN_CREATEPLUGINACCOUNT, wParam, lParam); + return (INT_PTR)Finder->Next; +} + +/* +int FindPluginAccount(WPARAM wParam,LPARAM lParam) +{ +HYAMNPROTOPLUGIN Plugin=(HYAMNPROTOPLUGIN)wParam; +HACCOUNT Finder=(HACCOUNT)lParam; + +if (Finder=NULL) Finder=Plugin->FirstAccount; + +// for (;Finder != NULL && Finder->PluginID != Plugin->PluginInfo->PluginID;Finder=(HACCOUNT)Finder->Next); +return (int)Finder; +} +*/ +INT_PTR DeleteAccountSvc(WPARAM wParam, LPARAM lParam) +{ + //Deleting account works on these steps: + //1. set signal that account should stop activity (set event) + // setting this event we achieve, that any access to account is failed, + // so threads do not start any work with accounts (better saying threads of plugins should not start) + //2. wait to get write access to chained list of accounts + //3. we can write to chained list, so we change chain not to show to actual account + // now, any thread browsing list of accounts does not browse through actual account + // actual account seems to be hidden (it exists, but it is not in accounts chained list (chained list=queue)) + //Now, we should delete account from memory, BUT!!! + // Any thread can still be waked up and start asking account synchronizing object + // If account is deleted, asking about access to read account can throw memory exception (reading for + // a synchronizing object from memory, that was deleted) + //So, we cannot now delete account. We have to wait until we are sure no thread will be using account anymore + // (or to the end of Miranda, but problem is in allocated memory- it is allocated and Miranda is SMALLER, faster, easier, isn't it?) + // This deleting is achieved in 2 ways: + // We have event in UsingThreads synchronization objects. This event signals that no thread will use actual account + // 1. Any thread using account first increment UsingThread, so we know that account is used + // 2. If thread is about to close, it should decrement UsingThread + // 3. If thread creates another thread, that will use account, caller has to wait until the new thread does not + // increment UsingThreads (imagine that caller ends before the new thread set it: if no other thread is using + // account, account is automaticaly (decreasing UsingThreads) signaled as "not used" and we delete it. But then + // new thread is going to read account...). + //4. wait until UsingThread Event is signaled + //5. delete account from memory + + HYAMNPROTOPLUGIN Plugin = (HYAMNPROTOPLUGIN)wParam; + HACCOUNT Which = (HACCOUNT)lParam; + HACCOUNT Finder; + + //1. set stop signal + StopSignalFcn(Which); + WindowList_BroadcastAsync(YAMNVar.MessageWnds, WM_YAMN_STOPACCOUNT, (WPARAM)Which, 0); + if (Plugin->Fcn->StopAccountFcnPtr != nullptr) + Plugin->Fcn->StopAccountFcnPtr(Which); + + //2. wait to get write access +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"DeleteAccount:AccountBrowserSO-write wait\n"); +#endif + SWMRGWaitToWrite(Plugin->AccountBrowserSO, INFINITE); +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"DeleteAccount:AccountBrowserSO-write enter\n"); +#endif + + //3. remove from queue (chained list) + if (Plugin->FirstAccount == nullptr) + { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"DeleteAccount:AccountBrowserSO-write done\n"); +#endif + SWMRGDoneWriting(Plugin->AccountBrowserSO); + return 0; + } + if (Plugin->FirstAccount == Which) + { + Finder = Plugin->FirstAccount->Next; + Plugin->FirstAccount = Finder; + } + else + { + for (Finder = Plugin->FirstAccount; Which != Finder->Next; Finder = Finder->Next); + Finder->Next = Finder->Next->Next; + } + //leave write access +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"DeleteAccount:AccountBrowserSO-write done\n"); +#endif + SWMRGDoneWriting(Plugin->AccountBrowserSO); + + //4. wait while event "UsingThread" is not signaled + // And what to do, if this event will be signaled in 1 hour? (Although it's paranoia, because we have sent "delete signal", so + // other threads do not start any new work with actual account) We will wait in blocked state? + // No, of course not. We will create new thread, that will wait and additionally remove our thread in background. + //5. So, the last point (deleting from memory) is performed in new DeleteAccountInBackground thread + + if ((Plugin->Fcn != nullptr) && (Plugin->Fcn->WriteAccountsFcnPtr != nullptr)) + Plugin->Fcn->WriteAccountsFcnPtr(); + CloseHandle(mir_forkthread(DeleteAccountInBackground, (void*)Which)); + + //Now, plugin can consider account as deleted, but plugin really can achieve deleting this account from memory when using + //event UsingThreads. + return 1; +} + +void __cdecl DeleteAccountInBackground(void *Value) +{ + HACCOUNT Which = (HACCOUNT)Value; + WaitForSingleObject(Which->UsingThreads->Event, INFINITE); + CallService(MS_YAMN_DELETEPLUGINACCOUNT, (WPARAM)Which, 0); +} + +int StopAccounts(HYAMNPROTOPLUGIN Plugin) +{ + HACCOUNT Finder; + + //1. wait to get write access +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"StopAccounts:AccountBrowserSO-write wait\n"); +#endif + SWMRGWaitToWrite(Plugin->AccountBrowserSO, INFINITE); +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"StopAccounts:AccountBrowserSO-write enter\n"); +#endif + for (Finder = Plugin->FirstAccount; Finder != nullptr; Finder = Finder->Next) + { + //2. set stop signal + StopSignalFcn(Finder); + WindowList_BroadcastAsync(YAMNVar.MessageWnds, WM_YAMN_STOPACCOUNT, (WPARAM)Finder, 0); + if (Plugin->Fcn->StopAccountFcnPtr != nullptr) + Plugin->Fcn->StopAccountFcnPtr(Finder); + } + + //leave write access +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"StopAccounts:AccountBrowserSO-write done\n"); +#endif + SWMRGDoneWriting(Plugin->AccountBrowserSO); + + //Now, account is stopped. It can be removed from memory... + return 1; +} + +int WaitForAllAccounts(HYAMNPROTOPLUGIN Plugin, BOOL GetAccountBrowserAccess) +{ + HACCOUNT Finder; + + if (GetAccountBrowserAccess) + { + //1. wait to get write access +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"WaitForAllAccounts:AccountBrowserSO-write wait\n"); +#endif + SWMRGWaitToWrite(Plugin->AccountBrowserSO, INFINITE); +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"WaitForAllAccounts:AccountBrowserSO-write enter\n"); +#endif + } + for (Finder = Plugin->FirstAccount; Finder != nullptr; Finder = Finder->Next) + { + //2. wait for signal that account is not in use +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"WaitForAllAccounts:waiting for UsingThreadEV %x (account %x)\n",Finder->UsingThreads,Finder); +#endif + WaitForSingleObject(Finder->UsingThreads->Event, INFINITE); + SetEvent(Finder->UsingThreads->Event); +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"WaitForAllAccounts:UsingThreadEV signaled\n"); +#endif + } + if (GetAccountBrowserAccess) + { + //leave write access +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"WaitForAllAccounts:AccountBrowserSO-write done\n"); +#endif + SWMRGDoneWriting(Plugin->AccountBrowserSO); + } + + return 1; +} + +int DeleteAccounts(HYAMNPROTOPLUGIN Plugin) +{ + HACCOUNT Finder; + + //1. wait to get write access +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"DeleteAccounts:AccountBrowserSO-write wait\n"); +#endif + SWMRGWaitToWrite(Plugin->AccountBrowserSO, INFINITE); +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"DeleteAccounts:AccountBrowserSO-write enter\n"); +#endif + + WaitForAllAccounts(Plugin, FALSE); + + for (Finder = Plugin->FirstAccount; Finder != nullptr;) + { + HACCOUNT Next = Finder->Next; + DeletePluginAccountSvc((WPARAM)Finder, 0); + Finder = Next; + } + + //leave write access +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"DeleteAccounts:AccountBrowserSO-write done\n"); +#endif + SWMRGDoneWriting(Plugin->AccountBrowserSO); + return 1; +} + +void WINAPI GetStatusFcn(HACCOUNT Which, wchar_t *Value) +{ + if (Which == nullptr) + return; + + mir_cslock lck(csAccountStatusCS); + mir_wstrcpy(Value, Which->Status); +} + +void WINAPI SetStatusFcn(HACCOUNT Which, wchar_t *Value) +{ + if (Which != nullptr) { + mir_cslock lck(csAccountStatusCS); + mir_wstrcpy(Which->Status, Value); + } + + WindowList_BroadcastAsync(YAMNVar.MessageWnds, WM_YAMN_CHANGESTATUS, (WPARAM)Which, 0); +} diff --git a/protocols/YAMN/src/browser/badconnect.cpp b/protocols/YAMN/src/browser/badconnect.cpp new file mode 100644 index 0000000000..aacae630d7 --- /dev/null +++ b/protocols/YAMN/src/browser/badconnect.cpp @@ -0,0 +1,296 @@ +/* + * This code implements window handling (connection error) + * + * (c) majvan 2002,2004 + */ + +#include "../stdafx.h" + +#define BADCONNECTTITLE LPGEN("%s - connection error") +#define BADCONNECTMSG LPGEN("An error occurred. Error code: %d")//is in use? + + //-------------------------------------------------------------------------------------------------- + +LRESULT CALLBACK BadConnectPopupProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + DWORD PluginParam; + switch (msg) { + case WM_COMMAND: + // if clicked and it's new mail popup window + if ((HIWORD(wParam) == STN_CLICKED) && (CallService(MS_POPUP_GETPLUGINDATA, (WPARAM)hWnd, (LPARAM)&PluginParam))) { + PROCESS_INFORMATION pi; + STARTUPINFOW si; + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + HACCOUNT ActualAccount = (HACCOUNT)CallService(MS_POPUP_GETPLUGINDATA, (WPARAM)hWnd, 0); +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "PopupProc:LEFTCLICK:ActualAccountSO-read wait\n"); +#endif + if (WAIT_OBJECT_0 == WaitToReadFcn(ActualAccount->AccountAccessSO)) { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "PopupProc:LEFTCLICK:ActualAccountSO-read enter\n"); +#endif + if (ActualAccount->BadConnectN.App != nullptr) { + WCHAR *Command; + if (ActualAccount->BadConnectN.AppParam != nullptr) + Command = new WCHAR[mir_wstrlen(ActualAccount->BadConnectN.App) + mir_wstrlen(ActualAccount->BadConnectN.AppParam) + 6]; + else + Command = new WCHAR[mir_wstrlen(ActualAccount->BadConnectN.App) + 6]; + + if (Command != nullptr) { + mir_wstrcpy(Command, L"\""); + mir_wstrcat(Command, ActualAccount->BadConnectN.App); + mir_wstrcat(Command, L"\" "); + if (ActualAccount->BadConnectN.AppParam != nullptr) + mir_wstrcat(Command, ActualAccount->BadConnectN.AppParam); + CreateProcessW(nullptr, Command, nullptr, nullptr, FALSE, NORMAL_PRIORITY_CLASS, nullptr, nullptr, &si, &pi); + delete[] Command; + } + } +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "PopupProc:LEFTCLICK:ActualAccountSO-read done\n"); +#endif + ReadDoneFcn(ActualAccount->AccountAccessSO); + } +#ifdef DEBUG_SYNCHRO + else + DebugLog(SynchroFile, "PopupProc:LEFTCLICK:ActualAccountSO-read enter failed\n"); +#endif + PUDeletePopup(hWnd); + } + break; + + case UM_FREEPLUGINDATA: + //Here we'd free our own data, if we had it. + return FALSE; + + case UM_INITPOPUP: + //This is the equivalent to WM_INITDIALOG you'd get if you were the maker of dialog popups. + break; + case WM_CONTEXTMENU: + PUDeletePopup(hWnd); + break; + } + return DefWindowProc(hWnd, msg, wParam, lParam); +} + +INT_PTR CALLBACK DlgProcYAMNBadConnection(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + case WM_INITDIALOG: + { + BOOL ShowPopup, ShowMsg, ShowIco; + HACCOUNT ActualAccount; + DWORD ErrorCode; + char* TitleStrA; + char *Message1A = nullptr; + wchar_t *Message1W = nullptr; + POPUPDATAW BadConnectPopup; + + ActualAccount = ((struct BadConnectionParam *)lParam)->account; + ErrorCode = ((struct BadConnectionParam *)lParam)->errcode; +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "BadConnect:ActualAccountSO-read wait\n"); +#endif + if (WAIT_OBJECT_0 != WaitToReadFcn(ActualAccount->AccountAccessSO)) { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "BadConnect:ActualAccountSO-read wait failed\n"); +#endif + return FALSE; + } +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "BadConnect:ActualAccountSO-read enter\n"); +#endif + int size = (int)(mir_strlen(ActualAccount->Name) + mir_strlen(Translate(BADCONNECTTITLE))); + TitleStrA = new char[size]; + mir_snprintf(TitleStrA, size, Translate(BADCONNECTTITLE), ActualAccount->Name); + + ShowPopup = ActualAccount->BadConnectN.Flags & YAMN_ACC_POP; + ShowMsg = ActualAccount->BadConnectN.Flags & YAMN_ACC_MSG; + ShowIco = ActualAccount->BadConnectN.Flags & YAMN_ACC_ICO; + + if (ShowPopup) { + BadConnectPopup.lchIcon = g_LoadIconEx(3); + BadConnectPopup.colorBack = ActualAccount->BadConnectN.Flags & YAMN_ACC_POPC ? ActualAccount->BadConnectN.PopupB : GetSysColor(COLOR_BTNFACE); + BadConnectPopup.colorText = ActualAccount->BadConnectN.Flags & YAMN_ACC_POPC ? ActualAccount->BadConnectN.PopupT : GetSysColor(COLOR_WINDOWTEXT); + BadConnectPopup.iSeconds = ActualAccount->BadConnectN.PopupTime; + + BadConnectPopup.PluginWindowProc = BadConnectPopupProc; + BadConnectPopup.PluginData = ActualAccount; + mir_wstrncpy(BadConnectPopup.lpwzContactName, _A2T(ActualAccount->Name), _countof(BadConnectPopup.lpwzContactName)); + } + + if (ActualAccount->Plugin->Fcn != nullptr && ActualAccount->Plugin->Fcn->GetErrorStringWFcnPtr != nullptr) { + Message1W = ActualAccount->Plugin->Fcn->GetErrorStringWFcnPtr(ErrorCode); + SetDlgItemText(hDlg, IDC_STATICMSG, Message1W); + wcsncpy_s(BadConnectPopup.lpwzText, Message1W, _TRUNCATE); + if (ShowPopup) + PUAddPopupW(&BadConnectPopup); + } + else if (ActualAccount->Plugin->Fcn != nullptr && ActualAccount->Plugin->Fcn->GetErrorStringAFcnPtr != nullptr) { + Message1W = ActualAccount->Plugin->Fcn->GetErrorStringWFcnPtr(ErrorCode); + SetDlgItemText(hDlg, IDC_STATICMSG, Message1W); + wcsncpy_s(BadConnectPopup.lpwzText, Message1W, _TRUNCATE); + if (ShowPopup) + PUAddPopupW(&BadConnectPopup); + } + else { + Message1W = TranslateT("Unknown error"); + SetDlgItemText(hDlg, IDC_STATICMSG, Message1W); + wcsncpy_s(BadConnectPopup.lpwzText, Message1W, _TRUNCATE); + if (ShowPopup) + PUAddPopupW(&BadConnectPopup); + } + + if (!ShowMsg && !ShowIco) + DestroyWindow(hDlg); +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "BadConnect:ActualAccountSO-read done\n"); +#endif + ReadDoneFcn(ActualAccount->AccountAccessSO); + + SetWindowTextA(hDlg, TitleStrA); + delete[] TitleStrA; + if (Message1A != nullptr) + delete[] Message1A; + if (ActualAccount->Plugin->Fcn != nullptr && ActualAccount->Plugin->Fcn->DeleteErrorStringFcnPtr != nullptr && Message1A != nullptr) + ActualAccount->Plugin->Fcn->DeleteErrorStringFcnPtr(Message1A); + if (ActualAccount->Plugin->Fcn != nullptr && ActualAccount->Plugin->Fcn->DeleteErrorStringFcnPtr != nullptr && Message1W != nullptr) + ActualAccount->Plugin->Fcn->DeleteErrorStringFcnPtr(Message1W); + return 0; + } + case WM_DESTROY: + { + NOTIFYICONDATA nid; + + memset(&nid, 0, sizeof(NOTIFYICONDATA)); + nid.cbSize = sizeof(NOTIFYICONDATA); + nid.hWnd = hDlg; + nid.uID = 0; + Shell_NotifyIcon(NIM_DELETE, &nid); + PostQuitMessage(0); + break; + } + case WM_YAMN_NOTIFYICON: + switch (lParam) { + case WM_LBUTTONDBLCLK: + ShowWindow(hDlg, SW_SHOWNORMAL); + SetForegroundWindow(hDlg); + break; + } + return 0; + case WM_CHAR: + switch ((wchar_t)wParam) { + case 27: + case 13: + DestroyWindow(hDlg); + break; + } + break; + case WM_SYSCOMMAND: + switch (wParam) { + case SC_CLOSE: + DestroyWindow(hDlg); + } + break; + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDC_BTNOK: + DestroyWindow(hDlg); + } + break; + } + return 0; +} + +void __cdecl BadConnection(void *Param) +{ + MSG msg; + HWND hBadConnect; + HACCOUNT ActualAccount; + + struct BadConnectionParam MyParam = *(struct BadConnectionParam *)Param; + ActualAccount = MyParam.account; +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "BadConnect:Incrementing \"using threads\" %x (account %x)\n", ActualAccount->UsingThreads, ActualAccount); +#endif + SCIncFcn(ActualAccount->UsingThreads); + + // we will not use params in stack anymore + SetEvent(MyParam.ThreadRunningEV); + + __try { + hBadConnect = CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_DLGBADCONNECT), nullptr, DlgProcYAMNBadConnection, (LPARAM)&MyParam); + Window_SetIcon_IcoLib(hBadConnect, g_GetIconHandle(3)); + +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "BadConnect:ActualAccountSO-read wait\n"); +#endif + if (WAIT_OBJECT_0 != WaitToReadFcn(ActualAccount->AccountAccessSO)) { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "BadConnect:ActualAccountSO-read wait failed\n"); +#endif + return; + } +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "BadConnect:ActualAccountSO-read enter\n"); +#endif + if (ActualAccount->BadConnectN.Flags & YAMN_ACC_SND) + Skin_PlaySound(YAMN_CONNECTFAILSOUND); + + if (ActualAccount->BadConnectN.Flags & YAMN_ACC_MSG) + ShowWindow(hBadConnect, SW_SHOWNORMAL); + + if (ActualAccount->BadConnectN.Flags & YAMN_ACC_ICO) { + NOTIFYICONDATA nid = {}; + nid.cbSize = sizeof(nid); + nid.hWnd = hBadConnect; + nid.hIcon = g_LoadIconEx(3); + nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; + nid.uCallbackMessage = WM_YAMN_NOTIFYICON; + mir_snwprintf(nid.szTip, L"%S%s", ActualAccount->Name, TranslateT(" - connection error")); + Shell_NotifyIcon(NIM_ADD, &nid); + } + +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "BadConnect:ActualAccountSO-read done\n"); +#endif + ReadDoneFcn(ActualAccount->AccountAccessSO); + + UpdateWindow(hBadConnect); + while (GetMessage(&msg, nullptr, 0, 0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + // now, write to file. Why? Because we want to write when was new mail last checked + if ((ActualAccount->Plugin->Fcn != nullptr) && (ActualAccount->Plugin->Fcn->WriteAccountsFcnPtr != nullptr) && ActualAccount->AbleToWork) + ActualAccount->Plugin->Fcn->WriteAccountsFcnPtr(); + } + __finally { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "BadConnect:Decrementing \"using threads\" %x (account %x)\n", ActualAccount->UsingThreads, ActualAccount); +#endif + SCDecFcn(ActualAccount->UsingThreads); + } +} + + +INT_PTR RunBadConnectionSvc(WPARAM wParam, LPARAM lParam) +{ + // an event for successfull copy parameters to which point a pointer in stack for new thread + PYAMN_BADCONNECTIONPARAM Param = (PYAMN_BADCONNECTIONPARAM)wParam; + if ((DWORD)lParam != YAMN_BADCONNECTIONVERSION) + return 0; + + HANDLE ThreadRunningEV = CreateEvent(nullptr, FALSE, FALSE, nullptr); + Param->ThreadRunningEV = ThreadRunningEV; + + HANDLE NewThread = mir_forkthread(BadConnection, Param); + if (nullptr == NewThread) + return 0; + + WaitForSingleObject(ThreadRunningEV, INFINITE); + CloseHandle(ThreadRunningEV); + return 1; +} diff --git a/protocols/YAMN/src/browser/browser.h b/protocols/YAMN/src/browser/browser.h new file mode 100644 index 0000000000..abab95f761 --- /dev/null +++ b/protocols/YAMN/src/browser/browser.h @@ -0,0 +1,39 @@ +#ifndef __MAILBROWSER_H +#define __MAILBROWSER_H + +typedef struct MailBrowserWinParam +{ +#define YAMN_MAILBROWSERVERSION 1 + HANDLE ThreadRunningEV; + HACCOUNT account; + DWORD nflags; //flags YAMN_ACC_??? when new mails + DWORD nnflags; //flags YAMN_ACC_??? when no new mails + void *Param; +} YAMN_MAILBROWSERPARAM,*PYAMN_MAILBROWSERPARAM; + +typedef struct MailShowMsgWinParam +{ + HANDLE ThreadRunningEV; + HACCOUNT account; + HYAMNMAIL mail; +} YAMN_MAILSHOWPARAM, *PYAMN_MAILSHOWPARAM; + +typedef struct NoNewMailParam +{ +#define YAMN_NONEWMAILVERSION 1 + HANDLE ThreadRunningEV; + HACCOUNT account; + DWORD flags; + void *Param; +} YAMN_NONEWMAILPARAM,*PYAMN_NONEWMAILPARAM; + +typedef struct BadConnectionParam +{ +#define YAMN_BADCONNECTIONVERSION 1 + HANDLE ThreadRunningEV; + HACCOUNT account; + UINT_PTR errcode; + void *Param; +} YAMN_BADCONNECTIONPARAM,*PYAMN_BADCONNECTIONPARAM; + +#endif diff --git a/protocols/YAMN/src/browser/mailbrowser.cpp b/protocols/YAMN/src/browser/mailbrowser.cpp new file mode 100644 index 0000000000..bbac178d15 --- /dev/null +++ b/protocols/YAMN/src/browser/mailbrowser.cpp @@ -0,0 +1,2369 @@ +/* + * This code implements window handling (new mail) + * + * (c) majvan 2002-2004 + */ + /* There can be problems when compiling this file, because in this file + * we are using both unicode and no-unicode functions and compiler does not + * like it in one file + * When you got errors, try to comment the #define <stdio.h> and compile, then + * put it back to uncommented and compile again :) + */ + +#include "../stdafx.h" + +#define TIMER_FLASHING 0x09061979 +#define MAILBROWSER_MINXSIZE 200 //min size of mail browser window +#define MAILBROWSER_MINYSIZE 130 + +#define MAILBROWSERTITLE LPGEN("%s - %d new mail messages, %d total") + +void __cdecl ShowEmailThread(void *Param); + +//-------------------------------------------------------------------------------------------------- +char* s_MonthNames[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; +bool bDate = false, bSub = false, bSize = false, bFrom = false; +int PosX = 0, PosY = 0, SizeX = 460, SizeY = 100; +int HeadSizeX = 0x2b2, HeadSizeY = 0x0b5, HeadPosX = 100, HeadPosY = 100; +int HeadSplitPos = 250; // per-mils of the size +static int FromWidth = 250, SubjectWidth = 280, SizeWidth = 50, SizeDate = 205; +unsigned char optDateTime = (SHOWDATELONG | SHOWDATENOTODAY); + +struct CMailNumbersSub +{ + int Total; //any mail + int New; //uses YAMN_MSG_NEW flag + int UnSeen; //uses YAMN_MSG_UNSEEN flag + // int Browser; //uses YAMN_MSG_BROWSER flag + int BrowserUC; //uses YAMN_MSG_BROWSER flag and YAMN_MSG_UNSEEN flag + int Display; //uses YAMN_MSG_DISPLAY flag + int DisplayTC; //uses YAMN_MSG_DISPLAY flag and YAMN_MSG_DISPLAYC flag + int DisplayUC; //uses YAMN_MSG_DISPLAY flag and YAMN_MSG_DISPLAYC flag and YAMN_MSG_UNSEEN flag + int Popup; //uses YAMN_MSG_POPUP flag + int PopupTC; //uses YAMN_MSG_POPUPC flag + int PopupNC; //uses YAMN_MSG_POPUPC flag and YAMN_MSG_NEW flag + int PopupRun; //uses YAMN_MSG_POPUP flag and YAMN_MSG_NEW flag + int PopupSL2NC; //uses YAMN_MSG_SPAML2 flag and YAMN_MSG_NEW flag + int PopupSL3NC; //uses YAMN_MSG_SPAML3 flag and YAMN_MSG_NEW flag + // int SysTray; //uses YAMN_MSG_SYSTRAY flag + int SysTrayUC; //uses YAMN_MSG_SYSTRAY flag and YAMN_MSG_UNSEEN flag + // int Sound; //uses YAMN_MSG_SOUND flag + int SoundNC; //uses YAMN_MSG_SOUND flag and YAMN_MSG_NEW flag + // int App; //uses YAMN_MSG_APP flag + int AppNC; //uses YAMN_MSG_APP flag and YAMN_MSG_NEW flag + int EventNC; //uses YAMN_MSG_NEVENT flag and YAMN_MSG_NEW flag +}; + +struct CMailNumbers +{ + struct CMailNumbersSub Real; + struct CMailNumbersSub Virtual; +}; + +struct CMailWinUserInfo +{ + HACCOUNT Account; + int TrayIconState; + BOOL UpdateMailsMessagesAccess; + BOOL Seen; + BOOL RunFirstTime; +}; + +struct CChangeContent +{ + DWORD nflags; + DWORD nnflags; +}; + +struct CUpdateMails +{ + struct CChangeContent *Flags; + BOOL Waiting; + HANDLE Copied; +}; + +struct CSortList +{ + HWND hDlg; + int iSubItem; +}; + +// Retrieves HACCOUNT, whose mails are displayed in ListMails +// hLM- handle of dialog window +// returns handle of account +inline HACCOUNT GetWindowAccount(HWND hDialog); + +// Looks to mail flags and increment mail counter (e.g. if mail is new, increments the new mail counter +// msgq- mail, which increments the counters +// MN- counnters structure +void IncrementMailCounters(HYAMNMAIL msgq, struct CMailNumbers *MN); + +enum +{ + UPDATE_FAIL = 0, //function failed + UPDATE_NONE, //none update has been performed + UPDATE_OK, //some changes occured, update performed +}; + +// Just looks for mail changes in account and update the mail browser window +// hDlg- dialog handle +// ActualAccount- account handle +// nflags- flags what to do when new mail arrives +// nnflags- flags what to do when no new mail arrives +// returns one of UPDATE_XXX value(not implemented yet) +int UpdateMails(HWND hDlg, HACCOUNT ActualAccount, DWORD nflags, DWORD nnflags); + +// When new mail occurs, shows window, plays sound, runs application... +// hDlg- dialog handle. Dialog of mailbrowser is already created and actions are performed over this window +// ActualAccount- handle of account, whose mails are to be notified +// MN- statistics of mails in account +// nflags- what to do or not to do (e.g. to show mailbrowser window or prohibit to show) +// nflags- flags what to do when new mail arrives +// nnflags- flags what to do when no new mail arrives +void DoMailActions(HWND hDlg, HACCOUNT ActualAccount, struct CMailNumbers *MN, DWORD nflags, DWORD nnflags); + +// Looks for items in mailbrowser and if they were deleted, delete them from browser window +// hListView- handle of listview window +// ActualAccount- handle of account, whose mails are show +// MailNumbers- pointer to structure, in which function stores numbers of mails with some property +// returns one of UPDATE_XXX value (not implemented yet) +int ChangeExistingMailStatus(HWND hListView, HACCOUNT ActualAccount); + +// Adds new mails to ListView and if any new, shows multi popup (every new message is new popup window created by popup plugin) +// hListView- handle of listview window +// ActualAccount- handle of account, whose mails are show +// NewMailPopup- pointer to prepared structure for popup plugin, can be NULL if no popup show +// MailNumbers- pointer to structure, in which function stores numbers of mails with some property +// nflags- flags what to do when new mail arrives +// returns one of UPDATE_XXX value (not implemented yet) +int AddNewMailsToListView(HWND hListView, HACCOUNT ActualAccount, DWORD nflags); + +// Window callback procedure for popup window (created by popup plugin) +LRESULT CALLBACK NewMailPopupProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + +// Window callback procedure for popup window (created by popup plugin) +LRESULT CALLBACK NoNewMailPopupProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + +// Dialog callback procedure for mail browser +INT_PTR CALLBACK DlgProcYAMNMailBrowser(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); + +// MailBrowser thread function creates window if needed, tray icon and plays sound +void __cdecl MailBrowser(void *Param); + +LRESULT CALLBACK ListViewSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); + +// Runs mail browser in new thread +INT_PTR RunMailBrowserSvc(WPARAM, LPARAM); + +#define YAMN_BROWSER_SHOWPOPUP 0x01 + +// list view items' order criteria +#define LVORDER_NOORDER -1 +#define LVORDER_STRING 0 +#define LVORDER_NUMERIC 1 +#define LVORDER_DATETIME 2 + +// list view order direction +#define LVORDER_ASCENDING 1 +#define LVORDER_NONE 0 +#define LVORDER_DESCENDING -1 + +// list view sort type +#define LVSORTPRIORITY_NONE -1 + +// List view column info. +typedef struct _SAMPLELISTVIEWCOLUMN +{ + UINT uCXCol; // index + int nSortType; // sorting type (STRING = 0, NUMERIC, DATE, DATETIME) + int nSortOrder; // sorting order (ASCENDING = -1, NONE, DESCENDING) + int nPriority; // sort priority (-1 for none, 0, 1, ..., nColumns - 1 maximum) + wchar_t lpszName[128]; // column name +} SAMPLELISTVIEWCOLUMN; + +// Compare priority +typedef struct _LVCOMPAREINFO +{ + int iIdx; // Index + int iPriority; // Priority +} LVCOMPAREINFO, *LPLVCOMPAREINFO; + +//-------------------------------------------------------------------------------------------------- + +LPARAM readItemLParam(HWND hwnd, DWORD iItem) +{ + LVITEM item; + item.mask = LVIF_PARAM; + item.iItem = iItem; + item.iSubItem = 0; + SendMessage(hwnd, LVM_GETITEM, 0, (LPARAM)&item); + return item.lParam; +} + +inline HACCOUNT GetWindowAccount(HWND hDlg) +{ + struct CMailWinUserInfo *mwui = (struct CMailWinUserInfo *)GetWindowLongPtr(hDlg, DWLP_USER); + + return (mwui == nullptr) ? nullptr : mwui->Account; +} + +void IncrementMailCounters(HYAMNMAIL msgq, struct CMailNumbers *MN) +{ + if (msgq->Flags & YAMN_MSG_VIRTUAL) + MN->Virtual.Total++; + else + MN->Real.Total++; + + if (msgq->Flags & YAMN_MSG_NEW) + if (msgq->Flags & YAMN_MSG_VIRTUAL) + MN->Virtual.New++; + else + MN->Real.New++; + if (msgq->Flags & YAMN_MSG_UNSEEN) + if (msgq->Flags & YAMN_MSG_VIRTUAL) + MN->Virtual.UnSeen++; + else + MN->Real.UnSeen++; + if ((msgq->Flags & (YAMN_MSG_UNSEEN | YAMN_MSG_BROWSER)) == (YAMN_MSG_UNSEEN | YAMN_MSG_BROWSER)) + if (msgq->Flags & YAMN_MSG_VIRTUAL) + MN->Virtual.BrowserUC++; + else + MN->Real.BrowserUC++; + if (msgq->Flags & YAMN_MSG_DISPLAY) + if (msgq->Flags & YAMN_MSG_VIRTUAL) + MN->Virtual.Display++; + else + MN->Real.Display++; + if ((msgq->Flags & (YAMN_MSG_DISPLAYC | YAMN_MSG_DISPLAY)) == (YAMN_MSG_DISPLAYC | YAMN_MSG_DISPLAY)) + if (msgq->Flags & YAMN_MSG_VIRTUAL) + MN->Virtual.DisplayTC++; + else + MN->Real.DisplayTC++; + if ((msgq->Flags & (YAMN_MSG_UNSEEN | YAMN_MSG_DISPLAYC | YAMN_MSG_DISPLAY)) == (YAMN_MSG_UNSEEN | YAMN_MSG_DISPLAYC | YAMN_MSG_DISPLAY)) + if (msgq->Flags & YAMN_MSG_VIRTUAL) + MN->Virtual.DisplayUC++; + else + MN->Real.DisplayUC++; + if (msgq->Flags & YAMN_MSG_POPUP) + if (msgq->Flags & YAMN_MSG_VIRTUAL) + MN->Virtual.Popup++; + else + MN->Real.Popup++; + if ((msgq->Flags & YAMN_MSG_POPUPC) == YAMN_MSG_POPUPC) + if (msgq->Flags & YAMN_MSG_VIRTUAL) + MN->Virtual.PopupTC++; + else + MN->Real.PopupTC++; + if ((msgq->Flags & (YAMN_MSG_NEW | YAMN_MSG_POPUPC)) == (YAMN_MSG_NEW | YAMN_MSG_POPUPC)) + if (msgq->Flags & YAMN_MSG_VIRTUAL) + MN->Virtual.PopupNC++; + else + MN->Real.PopupNC++; + if ((msgq->Flags & (YAMN_MSG_NEW | YAMN_MSG_POPUP)) == (YAMN_MSG_NEW | YAMN_MSG_POPUP)) + if (msgq->Flags & YAMN_MSG_VIRTUAL) + MN->Virtual.PopupRun++; + else + MN->Real.PopupRun++; + if ((msgq->Flags & YAMN_MSG_NEW) && YAMN_MSG_SPAML(msgq->Flags, YAMN_MSG_SPAML2)) + if (msgq->Flags & YAMN_MSG_VIRTUAL) + MN->Virtual.PopupSL2NC++; + else + MN->Real.PopupSL2NC++; + if ((msgq->Flags & YAMN_MSG_NEW) && YAMN_MSG_SPAML(msgq->Flags, YAMN_MSG_SPAML3)) + if (msgq->Flags & YAMN_MSG_VIRTUAL) + MN->Virtual.PopupSL3NC++; + else + MN->Real.PopupSL3NC++; + /* if (msgq->MailData->Flags & YAMN_MSG_SYSTRAY) + if (msgq->Flags & YAMN_MSG_VIRTUAL) + MN->Virtual.SysTray++; + else + MN->Real.SysTray++; + */ if ((msgq->Flags & (YAMN_MSG_UNSEEN | YAMN_MSG_SYSTRAY)) == (YAMN_MSG_UNSEEN | YAMN_MSG_SYSTRAY)) + if (msgq->Flags & YAMN_MSG_VIRTUAL) + MN->Virtual.SysTrayUC++; + else + MN->Real.SysTrayUC++; + /* if (msgq->MailData->Flags & YAMN_MSG_SOUND) + if (msgq->Flags & YAMN_MSG_VIRTUAL) + MN->Virtual.Sound++; + else + MN->Real.Sound++; + */ if ((msgq->Flags & (YAMN_MSG_NEW | YAMN_MSG_SOUND)) == (YAMN_MSG_NEW | YAMN_MSG_SOUND)) + if (msgq->Flags & YAMN_MSG_VIRTUAL) + MN->Virtual.SoundNC++; + else + MN->Real.SoundNC++; + /* if (msgq->MailData->Flags & YAMN_MSG_APP) + if (msgq->Flags & YAMN_MSG_VIRTUAL) + MN->Virtual.App++; + else + MN->Real.App++; + */ if ((msgq->Flags & (YAMN_MSG_NEW | YAMN_MSG_APP)) == (YAMN_MSG_NEW | YAMN_MSG_APP)) + if (msgq->Flags & YAMN_MSG_VIRTUAL) + MN->Virtual.AppNC++; + else + MN->Real.AppNC++; + if ((msgq->Flags & (YAMN_MSG_NEW | YAMN_MSG_NEVENT)) == (YAMN_MSG_NEW | YAMN_MSG_NEVENT)) + if (msgq->Flags & YAMN_MSG_VIRTUAL) + MN->Virtual.EventNC++; + else + MN->Real.EventNC++; +} + +int UpdateMails(HWND hDlg, HACCOUNT ActualAccount, DWORD nflags, DWORD nnflags) +{ + struct CMailNumbers MN; + + BOOL Loaded; + BOOL RunMailBrowser, RunPopups; + + struct CMailWinUserInfo *mwui = (struct CMailWinUserInfo *)GetWindowLongPtr(hDlg, DWLP_USER); + //now we ensure read access for account and write access for its mails +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "UpdateMails:ActualAccountSO-read wait\n"); +#endif + if (WAIT_OBJECT_0 != WaitToReadFcn(ActualAccount->AccountAccessSO)) { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "UpdateMails:ActualAccountSO-read wait failed\n"); +#endif + PostMessage(hDlg, WM_DESTROY, 0, 0); + + return UPDATE_FAIL; + } +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "UpdateMails:ActualAccountSO-read enter\n"); +#endif + +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "UpdateMails:ActualAccountMsgsSO-write wait\n"); +#endif + if (WAIT_OBJECT_0 != WaitToWriteFcn(ActualAccount->MessagesAccessSO)) { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "UpdateMails:ActualAccountMsgsSO-write wait failed\n"); + DebugLog(SynchroFile, "UpdateMails:ActualAccountSO-read done\n"); +#endif + ReadDoneFcn(ActualAccount->AccountAccessSO); + + PostMessage(hDlg, WM_DESTROY, 0, 0); + return UPDATE_FAIL; + } +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "UpdateMails:ActualAccountMsgsSO-write enter\n"); +#endif + + memset(&MN, 0, sizeof(MN)); + + for (HYAMNMAIL msgq = (HYAMNMAIL)ActualAccount->Mails; msgq != nullptr; msgq = msgq->Next) { + if (!LoadedMailData(msgq)) //check if mail is already in memory + { + Loaded = false; + if (nullptr == LoadMailData(msgq)) //if we could not load mail to memory, consider this mail deleted and do not display it + continue; + } + else + Loaded = true; + + IncrementMailCounters(msgq, &MN); + + if (!Loaded) + UnloadMailData(msgq); //do not keep data for mail in memory + } + + if (mwui != nullptr) + mwui->UpdateMailsMessagesAccess = TRUE; + + //Now we are going to check if extracting data from mail headers are needed. + //If popups will be displayed or mailbrowser window + if ((((mwui != nullptr) && !(mwui->RunFirstTime)) && + ( + ((nnflags & YAMN_ACC_MSGP) && !(MN.Real.BrowserUC + MN.Virtual.BrowserUC)) || + ((nflags & YAMN_ACC_MSGP) && (MN.Real.BrowserUC + MN.Virtual.BrowserUC)) + ) + ) || //if mail window was displayed before and flag YAMN_ACC_MSGP is set + ((nnflags & YAMN_ACC_MSG) && !(MN.Real.BrowserUC + MN.Virtual.BrowserUC)) || //if needed to run mailbrowser when no unseen and no unseen mail found + ((nflags & YAMN_ACC_MSG) && (MN.Real.BrowserUC + MN.Virtual.BrowserUC)) || //if unseen mails found, we sure run mailbrowser + ((nflags & YAMN_ACC_ICO) && (MN.Real.SysTrayUC + MN.Virtual.SysTrayUC)) + ) //if needed to run systray + RunMailBrowser = TRUE; + else + RunMailBrowser = FALSE; + + // if some popups with mails are needed to show + if ((nflags & YAMN_ACC_POP) && (ActualAccount->Flags & YAMN_ACC_POPN) && (MN.Real.PopupNC + MN.Virtual.PopupNC)) + RunPopups = TRUE; + else RunPopups = FALSE; + + if (RunMailBrowser) + ChangeExistingMailStatus(GetDlgItem(hDlg, IDC_LISTMAILS), ActualAccount); + if (RunMailBrowser || RunPopups) + AddNewMailsToListView(hDlg == nullptr ? nullptr : GetDlgItem(hDlg, IDC_LISTMAILS), ActualAccount, nflags); + + if (RunMailBrowser) { + size_t len = mir_strlen(ActualAccount->Name) + mir_strlen(Translate(MAILBROWSERTITLE)) + 10; //+10 chars for numbers + char *TitleStrA = new char[len]; + WCHAR *TitleStrW = new WCHAR[len]; + + mir_snprintf(TitleStrA, len, Translate(MAILBROWSERTITLE), ActualAccount->Name, MN.Real.DisplayUC + MN.Virtual.DisplayUC, MN.Real.Display + MN.Virtual.Display); + MultiByteToWideChar(CP_ACP, MB_USEGLYPHCHARS, TitleStrA, -1, TitleStrW, (int)mir_strlen(TitleStrA) + 1); + SetWindowTextW(hDlg, TitleStrW); + delete[] TitleStrA; + delete[] TitleStrW; + } + +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "UpdateMails:Do mail actions\n"); +#endif + + DoMailActions(hDlg, ActualAccount, &MN, nflags, nnflags); + +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "UpdateMails:Do mail actions done\n"); +#endif + + SetRemoveFlagsInQueueFcn((HYAMNMAIL)ActualAccount->Mails, YAMN_MSG_NEW, 0, YAMN_MSG_NEW, YAMN_FLAG_REMOVE); //rempve the new flag + if (!RunMailBrowser) + SetRemoveFlagsInQueueFcn((HYAMNMAIL)ActualAccount->Mails, YAMN_MSG_UNSEEN, YAMN_MSG_STAYUNSEEN, YAMN_MSG_UNSEEN, YAMN_FLAG_REMOVE); //remove the unseen flag when it was not displayed and it has not "stay unseen" flag set + + if (mwui != nullptr) { + mwui->UpdateMailsMessagesAccess = FALSE; + mwui->RunFirstTime = FALSE; + } +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "UpdateMails:ActualAccountMsgsSO-write done\n"); + DebugLog(SynchroFile, "UpdateMails:ActualAccountSO-read done\n"); +#endif + WriteDoneFcn(ActualAccount->MessagesAccessSO); + ReadDoneFcn(ActualAccount->AccountAccessSO); + + if (RunMailBrowser) + UpdateWindow(GetDlgItem(hDlg, IDC_LISTMAILS)); + else if (hDlg != nullptr) + DestroyWindow(hDlg); + + return 1; +} + +int ChangeExistingMailStatus(HWND hListView, HACCOUNT ActualAccount) +{ + LVITEM item; + HYAMNMAIL mail, msgq; + + int in = ListView_GetItemCount(hListView); + item.mask = LVIF_PARAM; + + for (int i = 0; i < in; i++) { + item.iItem = i; + item.iSubItem = 0; + if (TRUE == ListView_GetItem(hListView, &item)) + mail = (HYAMNMAIL)item.lParam; + else + continue; + for (msgq = (HYAMNMAIL)ActualAccount->Mails; (msgq != nullptr) && (msgq != mail); msgq = msgq->Next); //found the same mail in account queue + if (msgq == nullptr) //if mail was not found + if (TRUE == ListView_DeleteItem(hListView, i)) { + in--; i--; + continue; + } + } + + return TRUE; +} + +void MimeDateToLocalizedDateTime(char *datein, WCHAR *dateout, int lendateout); +int AddNewMailsToListView(HWND hListView, HACCOUNT ActualAccount, DWORD nflags) +{ + WCHAR *FromStr; + WCHAR SizeStr[20]; + WCHAR LocalDateStr[128]; + + LVITEMW item; + LVFINDINFO fi; + + int foundi = 0, lfoundi = 0; + struct CHeader UnicodeHeader; + BOOL Loaded, Extracted, FromStrNew = FALSE; + + memset(&item, 0, sizeof(item)); + memset(&UnicodeHeader, 0, sizeof(UnicodeHeader)); + + if (hListView != nullptr) { + item.mask = LVIF_TEXT | LVIF_PARAM; + item.iItem = 0; + memset(&fi, 0, sizeof(fi)); + fi.flags = LVFI_PARAM; //let's go search item by lParam number + lfoundi = 0; + } + + POPUPDATAW NewMailPopup = { 0 }; + NewMailPopup.lchContact = (ActualAccount->hContact != NULL) ? ActualAccount->hContact : (UINT_PTR)ActualAccount; + NewMailPopup.lchIcon = g_LoadIconEx(2); + if (nflags & YAMN_ACC_POPC) { + NewMailPopup.colorBack = ActualAccount->NewMailN.PopupB; + NewMailPopup.colorText = ActualAccount->NewMailN.PopupT; + } + else { + NewMailPopup.colorBack = GetSysColor(COLOR_BTNFACE); + NewMailPopup.colorText = GetSysColor(COLOR_WINDOWTEXT); + } + NewMailPopup.iSeconds = ActualAccount->NewMailN.PopupTime; + + NewMailPopup.PluginWindowProc = NewMailPopupProc; + NewMailPopup.PluginData = nullptr; //it's new mail popup + + for (HYAMNMAIL msgq = (HYAMNMAIL)ActualAccount->Mails; msgq != nullptr; msgq = msgq->Next, lfoundi++) { + // now we hide mail pointer to item's lParam member. We can later use it to retrieve mail datas + + Extracted = FALSE; FromStr = nullptr; FromStrNew = FALSE; + + if (hListView != nullptr) { + fi.lParam = (LPARAM)msgq; + if (-1 != (foundi = ListView_FindItem(hListView, -1, &fi))) { // if mail is already in window + lfoundi = foundi; + continue; // do not insert any item + } + + item.iItem = lfoundi; // insert after last found item + item.lParam = (LPARAM)msgq; + } + + if (!LoadedMailData(msgq)) { // check if mail is already in memory + Loaded = false; + if (nullptr == LoadMailData(msgq)) //if we could not load mail to memory, consider this mail deleted and do not display it + continue; + } + else Loaded = true; + + if (((hListView != nullptr) && (msgq->Flags & YAMN_MSG_DISPLAY)) || + ((nflags & YAMN_ACC_POP) && (ActualAccount->Flags & YAMN_ACC_POPN) && (msgq->Flags & YAMN_MSG_POPUP) && (msgq->Flags & YAMN_MSG_NEW))) { + + if (!Extracted) ExtractHeader(msgq->MailData->TranslatedHeader, msgq->MailData->CP, &UnicodeHeader); + Extracted = TRUE; + + if ((UnicodeHeader.From != nullptr) && (UnicodeHeader.FromNick != nullptr)) { + size_t size = mir_wstrlen(UnicodeHeader.From) + mir_wstrlen(UnicodeHeader.FromNick) + 4; + FromStr = new WCHAR[size]; + mir_snwprintf(FromStr, size, L"%s <%s>", UnicodeHeader.FromNick, UnicodeHeader.From); + FromStrNew = TRUE; + } + else if (UnicodeHeader.From != nullptr) + FromStr = UnicodeHeader.From; + else if (UnicodeHeader.FromNick != nullptr) + FromStr = UnicodeHeader.FromNick; + else if (UnicodeHeader.ReturnPath != nullptr) + FromStr = UnicodeHeader.ReturnPath; + + if (nullptr == FromStr) { + FromStr = L""; + FromStrNew = FALSE; + } + } + + if ((hListView != nullptr) && (msgq->Flags & YAMN_MSG_DISPLAY)) { + item.iSubItem = 0; + item.pszText = FromStr; + item.iItem = SendMessage(hListView, LVM_INSERTITEM, 0, (LPARAM)&item); + + item.iSubItem = 1; + item.pszText = (nullptr != UnicodeHeader.Subject ? UnicodeHeader.Subject : (WCHAR*)L""); + SendMessage(hListView, LVM_SETITEMTEXT, (WPARAM)item.iItem, (LPARAM)&item); + + item.iSubItem = 2; + mir_snwprintf(SizeStr, L"%d kB", msgq->MailData->Size / 1024); + item.pszText = SizeStr; + SendMessage(hListView, LVM_SETITEMTEXT, (WPARAM)item.iItem, (LPARAM)&item); + + item.iSubItem = 3; + item.pszText = L""; + + for (CMimeItem *heads = msgq->MailData->TranslatedHeader; heads != nullptr; heads = heads->Next) { + if (!_stricmp(heads->name, "Date")) { + MimeDateToLocalizedDateTime(heads->value, LocalDateStr, 128); + item.pszText = LocalDateStr; + break; + } + } + SendMessage(hListView, LVM_SETITEMTEXT, (WPARAM)item.iItem, (LPARAM)&item); + } + + if ((nflags & YAMN_ACC_POP) && (ActualAccount->Flags & YAMN_ACC_POPN) && (msgq->Flags & YAMN_MSG_POPUP) && (msgq->Flags & YAMN_MSG_NEW)) { + mir_wstrncpy(NewMailPopup.lpwzContactName, FromStr, _countof(NewMailPopup.lpwzContactName)); + mir_wstrncpy(NewMailPopup.lpwzText, UnicodeHeader.Subject, _countof(NewMailPopup.lpwzText)); + + PYAMN_MAILSHOWPARAM MailParam = (PYAMN_MAILSHOWPARAM)malloc(sizeof(YAMN_MAILSHOWPARAM)); + if (MailParam) { + MailParam->account = ActualAccount; + MailParam->mail = msgq; + MailParam->ThreadRunningEV = nullptr; + NewMailPopup.PluginData = MailParam; + PUAddPopupW(&NewMailPopup); + } + } + + if ((msgq->Flags & YAMN_MSG_UNSEEN) && (ActualAccount->NewMailN.Flags & YAMN_ACC_KBN)) + CallService(MS_KBDNOTIFY_EVENTSOPENED, 1, NULL); + + if (FromStrNew) + delete[] FromStr; + + if (Extracted) { + DeleteHeaderContent(&UnicodeHeader); + memset(&UnicodeHeader, 0, sizeof(UnicodeHeader)); + } + + if (!Loaded) { + SaveMailData(msgq); + UnloadMailData(msgq); //do not keep data for mail in memory + } + } + + return TRUE; +} + +void DoMailActions(HWND hDlg, HACCOUNT ActualAccount, struct CMailNumbers *MN, DWORD nflags, DWORD nnflags) +{ + NOTIFYICONDATA nid = {}; + nid.cbSize = sizeof(nid); + nid.hWnd = hDlg; + + if (MN->Real.EventNC + MN->Virtual.EventNC) + NotifyEventHooks(hNewMailHook, 0, 0); + + if ((nflags & YAMN_ACC_KBN) && (MN->Real.PopupRun + MN->Virtual.PopupRun)) + CallService(MS_KBDNOTIFY_STARTBLINK, (WPARAM)MN->Real.PopupNC + MN->Virtual.PopupNC, NULL); + + if ((nflags & YAMN_ACC_CONT) && (MN->Real.PopupRun + MN->Virtual.PopupRun)) { + wchar_t tszMsg[250]; + mir_snwprintf(tszMsg, TranslateT("%s : %d new mail message(s), %d total"), _A2T(ActualAccount->Name), MN->Real.PopupNC + MN->Virtual.PopupNC, MN->Real.PopupTC + MN->Virtual.PopupTC); + + if (!(nflags & YAMN_ACC_CONTNOEVENT)) { + CLISTEVENT evt = {}; + evt.flags = CLEF_UNICODE; + evt.hContact = ActualAccount->hContact; + evt.hIcon = g_LoadIconEx(2); + evt.hDbEvent = ActualAccount->hContact; + evt.lParam = ActualAccount->hContact; + evt.pszService = MS_YAMN_CLISTDBLCLICK; + evt.szTooltip.w = tszMsg; + g_clistApi.pfnAddEvent(&evt); + } + db_set_ws(ActualAccount->hContact, "CList", "StatusMsg", tszMsg); + + if (nflags & YAMN_ACC_CONTNICK) + g_plugin.setWString(ActualAccount->hContact, "Nick", tszMsg); + } + + if ((nflags & YAMN_ACC_POP) && + !(ActualAccount->Flags & YAMN_ACC_POPN) && + (MN->Real.PopupRun + MN->Virtual.PopupRun)) { + POPUPDATAW NewMailPopup = { 0 }; + + NewMailPopup.lchContact = (ActualAccount->hContact != NULL) ? ActualAccount->hContact : (UINT_PTR)ActualAccount; + NewMailPopup.lchIcon = g_LoadIconEx(2); + if (nflags & YAMN_ACC_POPC) { + NewMailPopup.colorBack = ActualAccount->NewMailN.PopupB; + NewMailPopup.colorText = ActualAccount->NewMailN.PopupT; + } + else { + NewMailPopup.colorBack = GetSysColor(COLOR_BTNFACE); + NewMailPopup.colorText = GetSysColor(COLOR_WINDOWTEXT); + } + NewMailPopup.iSeconds = ActualAccount->NewMailN.PopupTime; + + NewMailPopup.PluginWindowProc = NewMailPopupProc; + NewMailPopup.PluginData = (void *)nullptr; //multiple popups + + mir_wstrncpy(NewMailPopup.lpwzContactName, _A2T(ActualAccount->Name), _countof(NewMailPopup.lpwzContactName)); + mir_snwprintf(NewMailPopup.lpwzText, TranslateT("%d new mail message(s), %d total"), MN->Real.PopupNC + MN->Virtual.PopupNC, MN->Real.PopupTC + MN->Virtual.PopupTC); + PUAddPopupW(&NewMailPopup); + } + + // destroy tray icon if no new mail + if ((MN->Real.SysTrayUC + MN->Virtual.SysTrayUC == 0) && (hDlg != nullptr)) + Shell_NotifyIcon(NIM_DELETE, &nid); + + // and remove the event + if ((nflags & YAMN_ACC_CONT) && (!(nflags & YAMN_ACC_CONTNOEVENT)) && (MN->Real.UnSeen + MN->Virtual.UnSeen == 0)) + g_clistApi.pfnRemoveEvent(ActualAccount->hContact, ActualAccount->hContact); + + if ((MN->Real.BrowserUC + MN->Virtual.BrowserUC == 0) && (hDlg != nullptr)) { + if (!IsWindowVisible(hDlg) && !(nflags & YAMN_ACC_MSG)) + PostMessage(hDlg, WM_DESTROY, 0, 0); //destroy window if no new mail and window is not visible + if (nnflags & YAMN_ACC_MSG) //if no new mail and msg should be executed + { + SetForegroundWindow(hDlg); + ShowWindow(hDlg, SW_SHOWNORMAL); + } + } + else + if (hDlg != nullptr) //else insert icon and set window if new mails + { + SendDlgItemMessageW(hDlg, IDC_LISTMAILS, LVM_SCROLL, 0, (LPARAM)0x7ffffff); + + if ((nflags & YAMN_ACC_ICO) && (MN->Real.SysTrayUC + MN->Virtual.SysTrayUC)) { + nid.hIcon = g_LoadIconEx(2); + nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; + nid.uCallbackMessage = WM_YAMN_NOTIFYICON; + mir_snwprintf(nid.szTip, L"%S %s", ActualAccount->Name, TranslateT("- new mail message(s)")); + Shell_NotifyIcon(NIM_ADD, &nid); + SetTimer(hDlg, TIMER_FLASHING, 500, nullptr); + } + if (nflags & YAMN_ACC_MSG) //if no new mail and msg should be executed + ShowWindow(hDlg, SW_SHOWNORMAL); + } + + if (MN->Real.AppNC + MN->Virtual.AppNC != 0) { + if (nflags & YAMN_ACC_APP) { + PROCESS_INFORMATION pi; + STARTUPINFOW si; + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + + if (ActualAccount->NewMailN.App != nullptr) { + WCHAR *Command; + if (ActualAccount->NewMailN.AppParam != nullptr) + Command = new WCHAR[mir_wstrlen(ActualAccount->NewMailN.App) + mir_wstrlen(ActualAccount->NewMailN.AppParam) + 6]; + else + Command = new WCHAR[mir_wstrlen(ActualAccount->NewMailN.App) + 6]; + + if (Command != nullptr) { + mir_wstrcpy(Command, L"\""); + mir_wstrcat(Command, ActualAccount->NewMailN.App); + mir_wstrcat(Command, L"\" "); + if (ActualAccount->NewMailN.AppParam != nullptr) + mir_wstrcat(Command, ActualAccount->NewMailN.AppParam); + CreateProcessW(nullptr, Command, nullptr, nullptr, FALSE, NORMAL_PRIORITY_CLASS, nullptr, nullptr, &si, &pi); + delete[] Command; + } + } + } + } + + if (MN->Real.SoundNC + MN->Virtual.SoundNC != 0) + if (nflags & YAMN_ACC_SND) + Skin_PlaySound(YAMN_NEWMAILSOUND); + + if ((nnflags & YAMN_ACC_POP) && (MN->Real.PopupRun + MN->Virtual.PopupRun == 0)) { + POPUPDATAW NoNewMailPopup; + + NoNewMailPopup.lchContact = (ActualAccount->hContact != NULL) ? ActualAccount->hContact : (UINT_PTR)ActualAccount; + NoNewMailPopup.lchIcon = g_LoadIconEx(1); + if (nflags & YAMN_ACC_POPC) { + NoNewMailPopup.colorBack = ActualAccount->NoNewMailN.PopupB; + NoNewMailPopup.colorText = ActualAccount->NoNewMailN.PopupT; + } + else { + NoNewMailPopup.colorBack = GetSysColor(COLOR_BTNFACE); + NoNewMailPopup.colorText = GetSysColor(COLOR_WINDOWTEXT); + } + NoNewMailPopup.iSeconds = ActualAccount->NoNewMailN.PopupTime; + + NoNewMailPopup.PluginWindowProc = NoNewMailPopupProc; + NoNewMailPopup.PluginData = nullptr; //it's not new mail popup + + mir_wstrncpy(NoNewMailPopup.lpwzContactName, _A2T(ActualAccount->Name), _countof(NoNewMailPopup.lpwzContactName)); + if (MN->Real.PopupSL2NC + MN->Virtual.PopupSL2NC) + mir_snwprintf(NoNewMailPopup.lpwzText, TranslateT("No new mail message, %d spam(s)"), MN->Real.PopupSL2NC + MN->Virtual.PopupSL2NC); + else + mir_wstrncpy(NoNewMailPopup.lpwzText, TranslateT("No new mail message"), _countof(NoNewMailPopup.lpwzText)); + PUAddPopupW(&NoNewMailPopup); + } + + if ((nflags & YAMN_ACC_CONT) && (MN->Real.PopupRun + MN->Virtual.PopupRun == 0)) { + if (ActualAccount->hContact != NULL) { + if (MN->Real.PopupTC + MN->Virtual.PopupTC) { + char tmp[255]; + mir_snprintf(tmp, Translate("%d new mail message(s), %d total"), MN->Real.PopupNC + MN->Virtual.PopupNC, MN->Real.PopupTC + MN->Virtual.PopupTC); + db_set_s(ActualAccount->hContact, "CList", "StatusMsg", tmp); + } + else db_set_s(ActualAccount->hContact, "CList", "StatusMsg", Translate("No new mail message")); + + if (nflags & YAMN_ACC_CONTNICK) + g_plugin.setString(ActualAccount->hContact, "Nick", ActualAccount->Name); + } + } + return; +} + +LRESULT CALLBACK NewMailPopupProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + INT_PTR PluginParam = 0; + switch (msg) { + case WM_COMMAND: + //if clicked and it's new mail popup window + if ((HIWORD(wParam) == STN_CLICKED) && (-1 != (PluginParam = CallService(MS_POPUP_GETPLUGINDATA, (WPARAM)hWnd, (LPARAM)&PluginParam)))) { + MCONTACT hContact = 0; + HACCOUNT Account; + if (PluginParam) { + PYAMN_MAILSHOWPARAM MailParam = new YAMN_MAILSHOWPARAM; + memcpy(MailParam, (PINT_PTR)PluginParam, sizeof(YAMN_MAILSHOWPARAM)); + hContact = MailParam->account->hContact; + Account = MailParam->account; + mir_forkthread(ShowEmailThread, MailParam); + } + else { + DBVARIANT dbv; + + hContact = PUGetContact(hWnd); + + if (!g_plugin.getString(hContact, "Id", &dbv)) { + Account = (HACCOUNT)CallService(MS_YAMN_FINDACCOUNTBYNAME, (WPARAM)POP3Plugin, (LPARAM)dbv.pszVal); + db_free(&dbv); + } + else + Account = (HACCOUNT)hContact; //???? + + +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "PopupProc:LEFTCLICK:ActualAccountSO-read wait\n"); +#endif + if (WAIT_OBJECT_0 == WaitToReadFcn(Account->AccountAccessSO)) { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "PopupProc:LEFTCLICK:ActualAccountSO-read enter\n"); +#endif + switch (msg) { + case WM_COMMAND: + { + YAMN_MAILBROWSERPARAM Param = { (HANDLE)nullptr, Account, + (Account->NewMailN.Flags & ~YAMN_ACC_POP) | YAMN_ACC_MSGP | YAMN_ACC_MSG, + (Account->NoNewMailN.Flags & ~YAMN_ACC_POP) | YAMN_ACC_MSGP | YAMN_ACC_MSG }; + + RunMailBrowserSvc((WPARAM)&Param, (LPARAM)YAMN_MAILBROWSERVERSION); + } + break; + } +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "PopupProc:LEFTCLICK:ActualAccountSO-read done\n"); +#endif + ReadDoneFcn(Account->AccountAccessSO); + } +#ifdef DEBUG_SYNCHRO + else + DebugLog(SynchroFile, "PopupProc:LEFTCLICK:ActualAccountSO-read enter failed\n"); +#endif + } + if ((Account->NewMailN.Flags & YAMN_ACC_CONT) && !(Account->NewMailN.Flags & YAMN_ACC_CONTNOEVENT)) + g_clistApi.pfnRemoveEvent(hContact, hContact); + } + __fallthrough; + + case WM_CONTEXTMENU: + PUDeletePopup(hWnd); + break; + case UM_FREEPLUGINDATA: + { + PYAMN_MAILSHOWPARAM mpd = (PYAMN_MAILSHOWPARAM)PUGetPluginData(hWnd); + if ((mpd) && (INT_PTR)mpd != -1)free(mpd); + return FALSE; + } + case UM_INITPOPUP: + //This is the equivalent to WM_INITDIALOG you'd get if you were the maker of dialog popups. + WindowList_Add(YAMNVar.MessageWnds, hWnd); + break; + case UM_DESTROYPOPUP: + WindowList_Remove(YAMNVar.MessageWnds, hWnd); + break; + case WM_YAMN_STOPACCOUNT: + { + HACCOUNT ActualAccount; + DBVARIANT dbv; + + MCONTACT hContact = PUGetContact(hWnd); + + if (!g_plugin.getString(hContact, "Id", &dbv)) { + ActualAccount = (HACCOUNT)CallService(MS_YAMN_FINDACCOUNTBYNAME, (WPARAM)POP3Plugin, (LPARAM)dbv.pszVal); + db_free(&dbv); + } + else + ActualAccount = (HACCOUNT)hContact; + + if ((HACCOUNT)wParam != ActualAccount) + break; + DestroyWindow(hWnd); + return 0; + } + case WM_NOTIFY: + default: + break; + } + return DefWindowProc(hWnd, msg, wParam, lParam); +} + +LRESULT CALLBACK NoNewMailPopupProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + case WM_COMMAND: + if ((HIWORD(wParam) == STN_CLICKED) && (msg == WM_COMMAND)) { + HACCOUNT ActualAccount; + DBVARIANT dbv; + + MCONTACT hContact = PUGetContact(hWnd); + + if (!g_plugin.getString(hContact, "Id", &dbv)) { + ActualAccount = (HACCOUNT)CallService(MS_YAMN_FINDACCOUNTBYNAME, (WPARAM)POP3Plugin, (LPARAM)dbv.pszVal); + db_free(&dbv); + } + else + ActualAccount = (HACCOUNT)hContact; + +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "PopupProc:LEFTCLICK:ActualAccountSO-read wait\n"); +#endif + if (WAIT_OBJECT_0 == WaitToReadFcn(ActualAccount->AccountAccessSO)) { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "PopupProc:LEFTCLICK:ActualAccountSO-read enter\n"); +#endif + switch (msg) { + case WM_COMMAND: + { + YAMN_MAILBROWSERPARAM Param = { (HANDLE)nullptr, ActualAccount, ActualAccount->NewMailN.Flags, ActualAccount->NoNewMailN.Flags, nullptr }; + + Param.nnflags = Param.nnflags | YAMN_ACC_MSG; //show mails in account even no new mail in account + Param.nnflags = Param.nnflags & ~YAMN_ACC_POP; + + Param.nflags = Param.nflags | YAMN_ACC_MSG; //show mails in account even no new mail in account + Param.nflags = Param.nflags & ~YAMN_ACC_POP; + + RunMailBrowserSvc((WPARAM)&Param, (LPARAM)YAMN_MAILBROWSERVERSION); + } + break; + } +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "PopupProc:LEFTCLICK:ActualAccountSO-read done\n"); +#endif + ReadDoneFcn(ActualAccount->AccountAccessSO); + } +#ifdef DEBUG_SYNCHRO + else + DebugLog(SynchroFile, "PopupProc:LEFTCLICK:ActualAccountSO-read enter failed\n"); +#endif + PUDeletePopup(hWnd); + } + break; + + case WM_CONTEXTMENU: + PUDeletePopup(hWnd); + break; + + case UM_FREEPLUGINDATA: + //Here we'd free our own data, if we had it. + return FALSE; + case UM_INITPOPUP: + //This is the equivalent to WM_INITDIALOG you'd get if you were the maker of dialog popups. + WindowList_Add(YAMNVar.MessageWnds, hWnd); + break; + case UM_DESTROYPOPUP: + WindowList_Remove(YAMNVar.MessageWnds, hWnd); + break; + case WM_YAMN_STOPACCOUNT: + { + HACCOUNT ActualAccount; + DBVARIANT dbv; + + MCONTACT hContact = PUGetContact(hWnd); + + if (!g_plugin.getString(hContact, "Id", &dbv)) { + ActualAccount = (HACCOUNT)CallService(MS_YAMN_FINDACCOUNTBYNAME, (WPARAM)POP3Plugin, (LPARAM)dbv.pszVal); + db_free(&dbv); + } + else + ActualAccount = (HACCOUNT)hContact; + + if ((HACCOUNT)wParam != ActualAccount) + break; + + DestroyWindow(hWnd); + return 0; + } + case WM_NOTIFY: + /* switch(((LPNMHDR)lParam)->code) + { + case NM_CLICK: + { + } + } + break; + */ default: + break; + } + return DefWindowProc(hWnd, msg, wParam, lParam); +} + +#ifdef __GNUC__ +//number of 100 ns periods between FILETIME 0 (1601/01/01 00:00:00.0000000) and TIMESTAMP 0 (1970/01/01 00:00:00) +#define NUM100NANOSEC 116444736000000000ULL +//The biggest time Get[Date|Time]Format can handle (Fri, 31 Dec 30827 23:59:59.9999999) +#define MAXFILETIME 0x7FFF35F4F06C7FFFULL +#else +#define NUM100NANOSEC 116444736000000000 +#define MAXFILETIME 0x7FFF35F4F06C7FFF +#endif + +ULONGLONG MimeDateToFileTime(char *datein) +{ + char *day = nullptr, *month = nullptr, *year = nullptr, *time = nullptr, *shift = nullptr; + SYSTEMTIME st; + ULONGLONG res = 0; + int wShiftSeconds = TimeZone_ToLocal(0); + GetLocalTime(&st); + //datein = "Xxx, 1 Jan 2060 5:29:1 +0530 XXX"; + //datein = "Xxx, 1 Jan 2060 05:29:10 "; + //datein = " ManySpaces 1.5 Jan 2060 05::"; + //datein = "Xxx, 35 February 20 :29:10 "; + //datein = "01.12.2007 (22:38:17)"; // + if (datein) { + char tmp[64]; + while (datein[0] == ' ') datein++; // eat leading spaces + strncpy(tmp, datein, 63); tmp[63] = 0; + if (atoi(tmp)) { // Parseable integer on DayOfWeek field? Buggy mime date. + day = tmp; + } + else { + int i = 0; + while (tmp[i] == ' ')i++; if (day = strchr(&tmp[i], ' ')) { day[0] = 0; day++; } + } + if (day) { while (day[0] == ' ') day++; if (month = strchr(day, ' ')) { month[0] = 0; month++; } } + if (month) { while (month[0] == ' ')month++; if (year = strchr(month, ' ')) { year[0] = 0; year++; } } + if (year) { while (year[0] == ' ') year++; if (time = strchr(year, ' ')) { time[0] = 0; time++; } } + if (time) { while (time[0] == ' ') time++; if (shift = strchr(time, ' ')) { shift[0] = 0; shift++; shift[5] = 0; } } + + if (year) { + st.wYear = atoi(year); + if (mir_strlen(year) < 4) if (st.wYear < 70)st.wYear += 2000; else st.wYear += 1900; + }; + if (month) for (int i = 0; i < 12; i++) if (strncmp(month, s_MonthNames[i], 3) == 0) { st.wMonth = i + 1; break; } + if (day) st.wDay = atoi(day); + if (time) { + char *h, *m, *s; + h = time; + if (m = strchr(h, ':')) { + m[0] = 0; m++; + if (s = strchr(m, ':')) { s[0] = 0; s++; } + } + else s = nullptr; + st.wHour = atoi(h); + st.wMinute = m ? atoi(m) : 0; + st.wSecond = s ? atoi(s) : 0; + } + else { st.wHour = st.wMinute = st.wSecond = 0; } + + if (shift) { + if (mir_strlen(shift) < 4) { + //has only hour + wShiftSeconds = (atoi(shift)) * 3600; + } + else { + char *smin = shift + mir_strlen(shift) - 2; + int ismin = atoi(smin); + smin[0] = 0; + int ishour = atoi(shift); + wShiftSeconds = (ishour * 60 + (ishour < 0 ? -1 : 1)*ismin) * 60; + } + } + } // if (datein) + FILETIME ft; + if (SystemTimeToFileTime(&st, &ft)) { + res = ((ULONGLONG)ft.dwHighDateTime << 32) | ((ULONGLONG)ft.dwLowDateTime); + LONGLONG w100nano = Int32x32To64((DWORD)wShiftSeconds, 10000000); + res -= w100nano; + } + else { + res = 0; + } + return res; +} + +void FileTimeToLocalizedDateTime(LONGLONG filetime, WCHAR *dateout, int lendateout) +{ + int localeID = Langpack_GetDefaultLocale(); + //int localeID = MAKELCID(LANG_URDU, SORT_DEFAULT); + if (localeID == CALLSERVICE_NOTFOUND) localeID = LOCALE_USER_DEFAULT; + if (filetime > MAXFILETIME) filetime = MAXFILETIME; + else if (filetime <= 0) { + wcsncpy(dateout, TranslateT("Invalid"), lendateout); + return; + } + SYSTEMTIME st; + WORD wTodayYear = 0, wTodayMonth = 0, wTodayDay = 0; + FILETIME ft; + BOOL willShowDate = !(optDateTime&SHOWDATENOTODAY); + if (!willShowDate) { + GetLocalTime(&st); + wTodayYear = st.wYear; + wTodayMonth = st.wMonth; + wTodayDay = st.wDay; + } + ft.dwLowDateTime = (DWORD)filetime; + ft.dwHighDateTime = (DWORD)(filetime >> 32); + FILETIME localft; + if (!FileTimeToLocalFileTime(&ft, &localft)) { + // this should never happen + wcsncpy(dateout, L"Incorrect FileTime", lendateout); + } + else { + if (!FileTimeToSystemTime(&localft, &st)) { + // this should never happen + wcsncpy(dateout, L"Incorrect LocalFileTime", lendateout); + } + else { + dateout[lendateout - 1] = 0; + int templen = 0; + if (!willShowDate) willShowDate = (wTodayYear != st.wYear) || (wTodayMonth != st.wMonth) || (wTodayDay != st.wDay); + if (willShowDate) { + templen = GetDateFormatW(localeID, (optDateTime&SHOWDATELONG) ? DATE_LONGDATE : DATE_SHORTDATE, &st, nullptr, dateout, lendateout - 2); + dateout[templen - 1] = ' '; + } + if (templen < (lendateout - 1)) { + GetTimeFormatW(localeID, (optDateTime&SHOWDATENOSECONDS) ? TIME_NOSECONDS : 0, &st, nullptr, &dateout[templen], lendateout - templen - 1); + } + } + } +} + +void MimeDateToLocalizedDateTime(char *datein, WCHAR *dateout, int lendateout) +{ + ULONGLONG ft = MimeDateToFileTime(datein); + FileTimeToLocalizedDateTime(ft, dateout, lendateout); +} + +int CALLBACK ListViewCompareProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) +{ + if (lParam1 == NULL || lParam2 == NULL) + return 0; + + int nResult = 0; + char *str1; + char *str2; + HYAMNMAIL email1 = (HYAMNMAIL)lParam1; + HYAMNMAIL email2 = (HYAMNMAIL)lParam2; + struct CShortHeader Header1; + struct CShortHeader Header2; + memset(&Header1, 0, sizeof(Header1)); + memset(&Header2, 0, sizeof(Header2)); + + try { + ExtractShortHeader(email1->MailData->TranslatedHeader, &Header1); + ExtractShortHeader(email2->MailData->TranslatedHeader, &Header2); + + switch ((int)lParamSort) { + case 0: //From + if (Header1.FromNick == nullptr) + str1 = Header1.From; + else str1 = Header1.FromNick; + + if (Header2.FromNick == nullptr) + str2 = Header2.From; + else str2 = Header2.FromNick; + + nResult = mir_strcmp(str1, str2); + + if (bFrom) nResult = -nResult; + break; + case 1: //Subject + if (Header1.Subject == nullptr) + str1 = " "; + else str1 = Header1.Subject; + + if (Header2.Subject == nullptr) + str2 = " "; + else str2 = Header2.Subject; + + nResult = mir_strcmp(str1, str2); + + if (bSub) nResult = -nResult; + break; + case 2: //Size + if (email1->MailData->Size == email2->MailData->Size) nResult = 0; + if (email1->MailData->Size > email2->MailData->Size) nResult = 1; + if (email1->MailData->Size < email2->MailData->Size) nResult = -1; + + if (bSize) nResult = -nResult; + break; + + case 3: //Date + { + ULONGLONG ts1 = 0, ts2 = 0; + ts1 = MimeDateToFileTime(Header1.Date); + ts2 = MimeDateToFileTime(Header2.Date); + if (ts1 > ts2) nResult = 1; + else if (ts1 < ts2) nResult = -1; + else nResult = 0; + } + if (bDate) nResult = -nResult; + break; + + default: + if (Header1.Subject == nullptr) str1 = " "; + else str1 = Header1.Subject; + + if (Header2.Subject == nullptr) str2 = " "; + else str2 = Header2.Subject; + + nResult = mir_strcmp(str1, str2); + break; + } + //MessageBox(NULL,str1,str2,0); + } + catch (...) { + } + + //free mem + DeleteShortHeaderContent(&Header1); + DeleteShortHeaderContent(&Header2); + return nResult; + +} + +HCURSOR hCurSplitNS, hCurSplitWE; +#define DM_SPLITTERMOVED (WM_USER+15) + +static LRESULT CALLBACK SplitterSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + case WM_NCHITTEST: + return HTCLIENT; + + case WM_SETCURSOR: + SetCursor(hCurSplitNS); + return TRUE; + + case WM_LBUTTONDOWN: + SetCapture(hwnd); + return 0; + + case WM_MOUSEMOVE: + if (GetCapture() == hwnd) { + RECT rc; + GetClientRect(hwnd, &rc); + SendMessage(GetParent(hwnd), DM_SPLITTERMOVED, (short)HIWORD(GetMessagePos()) + rc.bottom / 2, (LPARAM)hwnd); + } + return 0; + + case WM_LBUTTONUP: + ReleaseCapture(); + return 0; + } + return mir_callNextSubclass(hwnd, SplitterSubclassProc, msg, wParam, lParam); +} + +void ConvertCodedStringToUnicode(char *stream, WCHAR **storeto, DWORD cp, int mode); +int ConvertStringToUnicode(char *stream, unsigned int cp, WCHAR **out); + +INT_PTR CALLBACK DlgProcYAMNShowMessage(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + case WM_INITDIALOG: + { + PYAMN_MAILSHOWPARAM MailParam = (PYAMN_MAILSHOWPARAM)lParam; + WCHAR *iHeaderW = nullptr; + WCHAR *iValueW = nullptr; + int StrLen; + HWND hListView = GetDlgItem(hDlg, IDC_LISTHEADERS); + mir_subclassWindow(GetDlgItem(hDlg, IDC_SPLITTER), SplitterSubclassProc); + SetWindowLongPtr(hDlg, DWLP_USER, (LONG_PTR)MailParam); + Window_SetIcon_IcoLib(hDlg, g_GetIconHandle(2)); + + ListView_SetUnicodeFormat(hListView, TRUE); + ListView_SetExtendedListViewStyle(hListView, LVS_EX_FULLROWSELECT); + + StrLen = MultiByteToWideChar(CP_ACP, MB_USEGLYPHCHARS, Translate("Header"), -1, nullptr, 0); + iHeaderW = new WCHAR[StrLen + 1]; + MultiByteToWideChar(CP_ACP, MB_USEGLYPHCHARS, Translate("Header"), -1, iHeaderW, StrLen); + + StrLen = MultiByteToWideChar(CP_ACP, MB_USEGLYPHCHARS, Translate("Value"), -1, nullptr, 0); + iValueW = new WCHAR[StrLen + 1]; + MultiByteToWideChar(CP_ACP, MB_USEGLYPHCHARS, Translate("Value"), -1, iValueW, StrLen); + + LVCOLUMN lvc0 = { LVCF_FMT | LVCF_TEXT | LVCF_WIDTH, LVCFMT_LEFT, 130, iHeaderW, 0, 0 }; + LVCOLUMN lvc1 = { LVCF_FMT | LVCF_TEXT | LVCF_WIDTH, LVCFMT_LEFT, 400, iValueW, 0, 0 }; + SendMessage(hListView, LVM_INSERTCOLUMN, 0, (LPARAM)&lvc0); + SendMessage(hListView, LVM_INSERTCOLUMN, 1, (LPARAM)&lvc1); + if (nullptr != iHeaderW) + delete[] iHeaderW; + if (nullptr != iValueW) + delete[] iValueW; + + SendMessage(hDlg, WM_YAMN_CHANGECONTENT, 0, (LPARAM)MailParam); + MoveWindow(hDlg, HeadPosX, HeadPosY, HeadSizeX, HeadSizeY, 0); + ShowWindow(hDlg, SW_SHOWNORMAL); + } + break; + + case WM_YAMN_CHANGECONTENT: + { + PYAMN_MAILSHOWPARAM MailParam = (PYAMN_MAILSHOWPARAM) + (lParam ? lParam : GetWindowLongPtr(hDlg, DWLP_USER)); + HWND hListView = GetDlgItem(hDlg, IDC_LISTHEADERS); + HWND hEdit = GetDlgItem(hDlg, IDC_EDITBODY); + //do not redraw + SendMessage(hListView, WM_SETREDRAW, 0, 0); + ListView_DeleteAllItems(hListView); + struct CMimeItem *Header; + LVITEMW item; + item.mask = LVIF_TEXT | LVIF_PARAM; + WCHAR *From = nullptr, *Subj = nullptr; + char *contentType = nullptr, *transEncoding = nullptr, *body = nullptr; //should not be delete[]-ed + for (Header = MailParam->mail->MailData->TranslatedHeader; Header != nullptr; Header = Header->Next) { + WCHAR *str1 = nullptr; + WCHAR *str2 = nullptr; + WCHAR str_nul[2] = { 0 }; + if (!body) if (!_stricmp(Header->name, "Body")) { body = Header->value; continue; } + if (!contentType) if (!_stricmp(Header->name, "Content-Type")) contentType = Header->value; + if (!transEncoding) if (!_stricmp(Header->name, "Content-Transfer-Encoding")) transEncoding = Header->value; + //ConvertCodedStringToUnicode(Header->name,&str1,MailParam->mail->MailData->CP,1); + { + int streamsize = MultiByteToWideChar(20127, 0, Header->name, -1, nullptr, 0); + str1 = (WCHAR *)malloc(sizeof(WCHAR) * (streamsize + 1)); + MultiByteToWideChar(20127, 0, Header->name, -1, str1, streamsize);//US-ASCII + } + ConvertCodedStringToUnicode(Header->value, &str2, MailParam->mail->MailData->CP, 1); + if (!str2) { str2 = (WCHAR *)str_nul; }// the header value may be NULL + if (!From) if (!_stricmp(Header->name, "From")) { + From = new WCHAR[mir_wstrlen(str2) + 1]; + mir_wstrcpy(From, str2); + } + if (!Subj) if (!_stricmp(Header->name, "Subject")) { + Subj = new WCHAR[mir_wstrlen(str2) + 1]; + mir_wstrcpy(Subj, str2); + } + //if (!hasBody) if (!mir_strcmp(Header->name,"Body")) hasBody = true; + int count = 0; WCHAR **split = nullptr; + int ofs = 0; + while (str2[ofs]) { + if ((str2[ofs] == 0x266A) || (str2[ofs] == 0x25D9) || (str2[ofs] == 0x25CB) || + (str2[ofs] == 0x09) || (str2[ofs] == 0x0A) || (str2[ofs] == 0x0D))count++; + ofs++; + } + split = new WCHAR*[count + 1]; + count = 0; ofs = 0; + split[0] = str2; + while (str2[ofs]) { + if ((str2[ofs] == 0x266A) || (str2[ofs] == 0x25D9) || (str2[ofs] == 0x25CB) || + (str2[ofs] == 0x09) || (str2[ofs] == 0x0A) || (str2[ofs] == 0x0D)) { + if (str2[ofs - 1]) { + count++; + } + split[count] = (WCHAR *)(str2 + ofs + 1); + str2[ofs] = 0; + } + ofs++; + }; + + if (!_stricmp(Header->name, "From") || !_stricmp(Header->name, "To") || !_stricmp(Header->name, "Date") || !_stricmp(Header->name, "Subject")) + item.iItem = 0; + else + item.iItem = 999; + for (int i = 0; i <= count; i++) { + item.iSubItem = 0; + if (i == 0) + item.pszText = str1; + else { + item.iItem++; + item.pszText = nullptr; + } + item.iItem = SendMessage(hListView, LVM_INSERTITEM, 0, (LPARAM)&item); + item.iSubItem = 1; + item.pszText = str2 ? split[i] : nullptr; + SendMessage(hListView, LVM_SETITEMTEXT, (WPARAM)item.iItem, (LPARAM)&item); + } + delete[] split; + + if (str1) + free(str1); + if (str2 != (WCHAR *)str_nul) + free(str2); + } + if (body) { + WCHAR *bodyDecoded = nullptr; + char *localBody = nullptr; + if (contentType) { + if (!_strnicmp(contentType, "text", 4)) { + if (transEncoding) { + if (!_stricmp(transEncoding, "base64")) { + int size = (int)mir_strlen(body) * 3 / 4 + 5; + localBody = new char[size + 1]; + DecodeBase64(body, localBody, size); + } + else if (!_stricmp(transEncoding, "quoted-printable")) { + int size = (int)mir_strlen(body) + 2; + localBody = new char[size + 1]; + DecodeQuotedPrintable(body, localBody, size, FALSE); + } + } + } + else if (!_strnicmp(contentType, "multipart/", 10)) { + char *bondary = nullptr; + if (nullptr != (bondary = ExtractFromContentType(contentType, "boundary="))) { + bodyDecoded = ParseMultipartBody(body, bondary); + delete[] bondary; + } + } + } + if (!bodyDecoded)ConvertStringToUnicode(localBody ? localBody : body, MailParam->mail->MailData->CP, &bodyDecoded); + SetWindowTextW(hEdit, bodyDecoded); + delete[] bodyDecoded; + if (localBody) delete[] localBody; + SetFocus(hEdit); + } + if (!(MailParam->mail->Flags & YAMN_MSG_BODYRECEIVED)) { + MailParam->mail->Flags |= YAMN_MSG_BODYREQUESTED; + CallService(MS_YAMN_ACCOUNTCHECK, (WPARAM)MailParam->account, 0); + } + else { + if (MailParam->mail->Flags & YAMN_MSG_UNSEEN) { + MailParam->mail->Flags &= ~YAMN_MSG_UNSEEN; //mark the message as seen + HWND hMailBrowser = WindowList_Find(YAMNVar.NewMailAccountWnd, (UINT_PTR)MailParam->account); + if (hMailBrowser) { + struct CChangeContent Params = { MailParam->account->NewMailN.Flags | YAMN_ACC_MSGP, MailParam->account->NoNewMailN.Flags | YAMN_ACC_MSGP }; + SendMessage(hMailBrowser, WM_YAMN_CHANGECONTENT, (WPARAM)MailParam->account, (LPARAM)&Params); + } + else UpdateMails(nullptr, MailParam->account, MailParam->account->NewMailN.Flags, MailParam->account->NoNewMailN.Flags); + } + } + ShowWindow(GetDlgItem(hDlg, IDC_SPLITTER), (MailParam->mail->Flags & YAMN_MSG_BODYRECEIVED) ? SW_SHOW : SW_HIDE); + ShowWindow(hEdit, (MailParam->mail->Flags & YAMN_MSG_BODYRECEIVED) ? SW_SHOW : SW_HIDE); + WCHAR *title = nullptr; + size_t size = (From ? mir_wstrlen(From) : 0) + (Subj ? mir_wstrlen(Subj) : 0) + 4; + title = new WCHAR[size]; + if (From && Subj) + mir_snwprintf(title, size, L"%s (%s)", Subj, From); + else if (From) + wcsncpy_s(title, size, From, _TRUNCATE); + else if (Subj) + wcsncpy_s(title, size, Subj, _TRUNCATE); + else + wcsncpy_s(title, size, L"none", _TRUNCATE); + if (Subj) delete[] Subj; + if (From) delete[] From; + SetWindowTextW(hDlg, title); + delete[] title; + // turn on redrawing + SendMessage(hListView, WM_SETREDRAW, 1, 0); + SendMessage(hDlg, WM_SIZE, 0, HeadSizeY << 16 | HeadSizeX); + } break; + + case WM_YAMN_STOPACCOUNT: + { + PYAMN_MAILSHOWPARAM MailParam = (PYAMN_MAILSHOWPARAM) + (lParam ? lParam : GetWindowLongPtr(hDlg, DWLP_USER)); + + if (nullptr == MailParam) + break; + if ((HACCOUNT)wParam != MailParam->account) + break; +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "ShowMessage:STOPACCOUNT:sending destroy msg\n"); +#endif + DestroyWindow(hDlg); + } + return 1; + + case WM_CTLCOLORSTATIC: + // here should be check if this is our edittext control. + // but we have only one static control (for now); + SetBkColor((HDC)wParam, GetSysColor(COLOR_WINDOW)); + SetTextColor((HDC)wParam, GetSysColor(COLOR_WINDOWTEXT)); + return (INT_PTR)GetSysColorBrush(COLOR_WINDOW); + + case WM_DESTROY: + Window_FreeIcon_IcoLib(hDlg); + { + RECT coord; + if (GetWindowRect(hDlg, &coord)) { + HeadPosX = coord.left; + HeadSizeX = coord.right - coord.left; + HeadPosY = coord.top; + HeadSizeY = coord.bottom - coord.top; + } + + PostQuitMessage(1); + } + break; + + case WM_SYSCOMMAND: + switch (wParam) { + case SC_CLOSE: + DestroyWindow(hDlg); + break; + } + break; + + case WM_MOVE: + HeadPosX = LOWORD(lParam); //((LPRECT)lParam)->right-((LPRECT)lParam)->left; + HeadPosY = HIWORD(lParam); //((LPRECT)lParam)->bottom-((LPRECT)lParam)->top; + return 0; + + case DM_SPLITTERMOVED: + if ((HWND)lParam == GetDlgItem(hDlg, IDC_SPLITTER)) { + POINT pt; + pt.x = 0; + pt.y = wParam; + ScreenToClient(hDlg, &pt); + HeadSplitPos = (pt.y * 1000) / HeadSizeY;//+rc.bottom-rc.top; + if (HeadSplitPos >= 1000) HeadSplitPos = 999; + else if (HeadSplitPos <= 0) HeadSplitPos = 1; + else SendMessage(hDlg, WM_SIZE, 0, HeadSizeY << 16 | HeadSizeX); + } + return 0; + + case WM_SIZE: + if (wParam == SIZE_RESTORED) { + HWND hList = GetDlgItem(hDlg, IDC_LISTHEADERS); + HWND hEdit = GetDlgItem(hDlg, IDC_EDITBODY); + BOOL isBodyShown = ((PYAMN_MAILSHOWPARAM)(GetWindowLongPtr(hDlg, DWLP_USER)))->mail->Flags & YAMN_MSG_BODYRECEIVED; + HeadSizeX = LOWORD(lParam); //((LPRECT)lParam)->right-((LPRECT)lParam)->left; + HeadSizeY = HIWORD(lParam); //((LPRECT)lParam)->bottom-((LPRECT)lParam)->top; + int localSplitPos = (HeadSplitPos*HeadSizeY) / 1000; + int localSizeX; + RECT coord; + MoveWindow(GetDlgItem(hDlg, IDC_SPLITTER), 5, localSplitPos, HeadSizeX - 10, 2, TRUE); + MoveWindow(hEdit, 5, localSplitPos + 6, HeadSizeX - 10, HeadSizeY - localSplitPos - 11, TRUE); //where to put text window while resizing + MoveWindow(hList, 5, 5, HeadSizeX - 10, (isBodyShown ? localSplitPos : HeadSizeY) - 10, TRUE); //where to put headers list window while resizing + //if (changeX) { + if (GetClientRect(hList, &coord)) { + localSizeX = coord.right - coord.left; + } + else localSizeX = HeadSizeX; + LONG iNameWidth = ListView_GetColumnWidth(hList, 0); + ListView_SetColumnWidth(hList, 1, (localSizeX <= iNameWidth) ? 0 : (localSizeX - iNameWidth)); + //} + } + return 0; + + case WM_CONTEXTMENU: + if (GetWindowLongPtr((HWND)wParam, GWLP_ID) == IDC_LISTHEADERS) { + //MessageBox(0,"LISTHEADERS","Debug",0); + HWND hList = GetDlgItem(hDlg, IDC_LISTHEADERS); + POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; + if (pt.x == -1) pt.x = 0; + if (pt.y == -1) pt.y = 0; + if (int numRows = ListView_GetItemCount(hList)) { + HMENU hMenu = CreatePopupMenu(); + AppendMenu(hMenu, MF_STRING, (UINT_PTR)1, TranslateT("Copy Selected")); + AppendMenu(hMenu, MF_STRING, (UINT_PTR)2, TranslateT("Copy All")); + AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr); + AppendMenu(hMenu, MF_STRING, (UINT_PTR)0, TranslateT("Cancel")); + int nReturnCmd = TrackPopupMenu(hMenu, TPM_RETURNCMD, pt.x, pt.y, 0, hDlg, nullptr); + DestroyMenu(hMenu); + if (nReturnCmd > 0) { + int courRow = 0; + size_t sizeNeeded = 0; + wchar_t headname[64] = { 0 }, headvalue[256] = { 0 }; + for (courRow = 0; courRow < numRows; courRow++) { + if ((nReturnCmd == 1) && (ListView_GetItemState(hList, courRow, LVIS_SELECTED) == 0)) continue; + ListView_GetItemText(hList, courRow, 0, headname, _countof(headname)); + ListView_GetItemText(hList, courRow, 1, headvalue, _countof(headvalue)); + size_t headnamelen = mir_wstrlen(headname); + if (headnamelen) sizeNeeded += 1 + headnamelen; + sizeNeeded += 3 + mir_wstrlen(headvalue); + } + if (sizeNeeded && OpenClipboard(hDlg)) { + EmptyClipboard(); + HGLOBAL hData = GlobalAlloc(GMEM_MOVEABLE, (sizeNeeded + 1) * sizeof(wchar_t)); + wchar_t *buff = (wchar_t*)GlobalLock(hData); + int courPos = 0; + for (courRow = 0; courRow < numRows; courRow++) { + if ((nReturnCmd == 1) && (ListView_GetItemState(hList, courRow, LVIS_SELECTED) == 0)) continue; + ListView_GetItemText(hList, courRow, 0, headname, _countof(headname)); + ListView_GetItemText(hList, courRow, 1, headvalue, _countof(headvalue)); + if (mir_wstrlen(headname)) courPos += mir_snwprintf(&buff[courPos], sizeNeeded + 1, L"%s:\t%s\r\n", headname, headvalue); + else courPos += mir_snwprintf(&buff[courPos], sizeNeeded + 1, L"\t%s\r\n", headvalue); + } + GlobalUnlock(hData); + + SetClipboardData(CF_UNICODETEXT, hData); + + CloseClipboard(); + } + } + } + } + break; // just in case + } + return 0; +} + +void __cdecl ShowEmailThread(void *Param) +{ + struct MailShowMsgWinParam MyParam = *(struct MailShowMsgWinParam *)Param; + +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "ShowMessage:Incrementing \"using threads\" %x (account %x)\n", MyParam.account->UsingThreads, MyParam.account); +#endif + SCIncFcn(MyParam.account->UsingThreads); + + if (MyParam.mail->MsgWindow) { + //if (!BringWindowToTop(MyParam.mail->MsgWindow)) { + if (!SetForegroundWindow(MyParam.mail->MsgWindow)) { + SendMessage(MyParam.mail->MsgWindow, WM_DESTROY, 0, 0); + MyParam.mail->MsgWindow = nullptr; + goto CREADTEVIEWMESSAGEWINDOW; + } + + if (IsIconic(MyParam.mail->MsgWindow)) + OpenIcon(MyParam.mail->MsgWindow); + } + else { +CREADTEVIEWMESSAGEWINDOW: + MyParam.mail->MsgWindow = CreateDialogParamW(g_plugin.getInst(), MAKEINTRESOURCEW(IDD_DLGSHOWMESSAGE), nullptr, DlgProcYAMNShowMessage, (LPARAM)&MyParam); + WindowList_Add(YAMNVar.MessageWnds, MyParam.mail->MsgWindow); + MSG msg; + while (GetMessage(&msg, nullptr, 0, 0)) { + if (MyParam.mail->MsgWindow == nullptr || !IsDialogMessage(MyParam.mail->MsgWindow, &msg)) { /* Wine fix. */ + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + WindowList_Remove(YAMNVar.MessageWnds, MyParam.mail->MsgWindow); + MyParam.mail->MsgWindow = nullptr; + } +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "ShowMessage:Decrementing \"using threads\" %x (account %x)\n", MyParam.account->UsingThreads, MyParam.account); +#endif + SCDecFcn(MyParam.account->UsingThreads); + delete (struct MailShowMsgWinParam*)Param; +} + +INT_PTR CALLBACK DlgProcYAMNMailBrowser(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + HACCOUNT ActualAccount; + int Items; + + switch (msg) { + case WM_INITDIALOG: + { + struct MailBrowserWinParam *MyParam = (struct MailBrowserWinParam *)lParam; + + ListView_SetUnicodeFormat(GetDlgItem(hDlg, IDC_LISTMAILS), TRUE); + ListView_SetExtendedListViewStyle(GetDlgItem(hDlg, IDC_LISTMAILS), LVS_EX_FULLROWSELECT); + + ActualAccount = MyParam->account; + struct CMailWinUserInfo *mwui = new struct CMailWinUserInfo; + mwui->Account = ActualAccount; + mwui->TrayIconState = 0; + mwui->UpdateMailsMessagesAccess = FALSE; + mwui->Seen = FALSE; + mwui->RunFirstTime = TRUE; + + SetWindowLongPtr(hDlg, DWLP_USER, (LONG_PTR)mwui); + if (WAIT_OBJECT_0 != WaitToReadFcn(ActualAccount->AccountAccessSO)) { + DestroyWindow(hDlg); + return FALSE; + } + + SetDlgItemText(hDlg, IDC_BTNAPP, TranslateT("Run application")); + SetDlgItemText(hDlg, IDC_BTNDEL, TranslateT("Delete selected")); + SetDlgItemText(hDlg, IDC_BTNCHECKALL, TranslateT("Select All")); + SetDlgItemText(hDlg, IDC_BTNOK, TranslateT("OK")); + + LVCOLUMN lvc0 = { LVCF_FMT | LVCF_TEXT | LVCF_WIDTH, LVCFMT_LEFT, FromWidth, TranslateT("From"), 0, 0 }; + LVCOLUMN lvc1 = { LVCF_FMT | LVCF_TEXT | LVCF_WIDTH, LVCFMT_LEFT, SubjectWidth, TranslateT("Subject"), 0, 0 }; + LVCOLUMN lvc2 = { LVCF_FMT | LVCF_TEXT | LVCF_WIDTH, LVCFMT_LEFT, SizeWidth, TranslateT("Size"), 0, 0 }; + LVCOLUMN lvc3 = { LVCF_FMT | LVCF_TEXT | LVCF_WIDTH, LVCFMT_LEFT, SizeDate, TranslateT("Date"), 0, 0 }; + SendDlgItemMessage(hDlg, IDC_LISTMAILS, LVM_INSERTCOLUMN, 0, (LPARAM)&lvc0); + SendDlgItemMessage(hDlg, IDC_LISTMAILS, LVM_INSERTCOLUMN, 1, (LPARAM)&lvc1); + SendDlgItemMessage(hDlg, IDC_LISTMAILS, LVM_INSERTCOLUMN, (WPARAM)2, (LPARAM)&lvc2); + SendDlgItemMessage(hDlg, IDC_LISTMAILS, LVM_INSERTCOLUMN, (WPARAM)3, (LPARAM)&lvc3); + + if ((ActualAccount->NewMailN.App != nullptr) && (mir_wstrlen(ActualAccount->NewMailN.App))) + EnableWindow(GetDlgItem(hDlg, IDC_BTNAPP), TRUE); + else + EnableWindow(GetDlgItem(hDlg, IDC_BTNAPP), FALSE); + + ReadDoneFcn(ActualAccount->AccountAccessSO); + + WindowList_Add(YAMNVar.MessageWnds, hDlg); + WindowList_Add(YAMNVar.NewMailAccountWnd, hDlg, (UINT_PTR)ActualAccount); + + { + wchar_t accstatus[512]; + GetStatusFcn(ActualAccount, accstatus); + SetDlgItemText(hDlg, IDC_STSTATUS, accstatus); + } + SetTimer(hDlg, TIMER_FLASHING, 500, nullptr); + + if (ActualAccount->hContact != NULL) + g_clistApi.pfnRemoveEvent(ActualAccount->hContact, (LPARAM)"yamn new mail message"); + + mir_subclassWindow(GetDlgItem(hDlg, IDC_LISTMAILS), ListViewSubclassProc); + } + break; + + case WM_DESTROY: + { + RECT coord; + LVCOLUMN ColInfo; + HYAMNMAIL Parser; + + Window_FreeIcon_IcoLib(hDlg); + + struct CMailWinUserInfo *mwui = (struct CMailWinUserInfo *)GetWindowLongPtr(hDlg, DWLP_USER); + if (nullptr == (ActualAccount = GetWindowAccount(hDlg))) + break; + ColInfo.mask = LVCF_WIDTH; + if (ListView_GetColumn(GetDlgItem(hDlg, IDC_LISTMAILS), 0, &ColInfo)) + FromWidth = ColInfo.cx; + if (ListView_GetColumn(GetDlgItem(hDlg, IDC_LISTMAILS), 1, &ColInfo)) + SubjectWidth = ColInfo.cx; + if (ListView_GetColumn(GetDlgItem(hDlg, IDC_LISTMAILS), 2, &ColInfo)) + SizeWidth = ColInfo.cx; + if (ListView_GetColumn(GetDlgItem(hDlg, IDC_LISTMAILS), 3, &ColInfo)) + SizeDate = ColInfo.cx; + +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "MailBrowser:DESTROY:save window position\n"); +#endif + if (!YAMNVar.Shutdown && GetWindowRect(hDlg, &coord)) //the YAMNVar.Shutdown testing is because M<iranda strange functionality at shutdown phase, when call to DBWriteContactSetting freezes calling thread + { + PosX = coord.left; + SizeX = coord.right - coord.left; + PosY = coord.top; + SizeY = coord.bottom - coord.top; + g_plugin.setDword(YAMN_DBPOSX, PosX); + g_plugin.setDword(YAMN_DBPOSY, PosY); + g_plugin.setDword(YAMN_DBSIZEX, SizeX); + g_plugin.setDword(YAMN_DBSIZEY, SizeY); + } + KillTimer(hDlg, TIMER_FLASHING); + +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "MailBrowser:DESTROY:remove window from list\n"); +#endif + WindowList_Remove(YAMNVar.NewMailAccountWnd, hDlg); + WindowList_Remove(YAMNVar.MessageWnds, hDlg); + +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "MailBrowser:DESTROY:ActualAccountMsgsSO-write wait\n"); +#endif + if (WAIT_OBJECT_0 != WaitToWriteFcn(ActualAccount->MessagesAccessSO)) { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "MailBrowser:DESTROY:ActualAccountMsgsSO-write wait failed\n"); +#endif + break; + } +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "MailBrowser:DESTROY:ActualAccountMsgsSO-write enter\n"); +#endif + //delete mails from queue, which are deleted from server (spam level 3 mails e.g.) + for (Parser = (HYAMNMAIL)ActualAccount->Mails; Parser != nullptr; Parser = Parser->Next) { + if ((Parser->Flags & YAMN_MSG_DELETED) && YAMN_MSG_SPAML(Parser->Flags, YAMN_MSG_SPAML3) && mwui->Seen) //if spaml3 was already deleted and user knows about it + { + DeleteMessageFromQueueFcn((HYAMNMAIL *)&ActualAccount->Mails, Parser, 1); + CallService(MS_YAMN_DELETEACCOUNTMAIL, (WPARAM)ActualAccount->Plugin, (LPARAM)Parser); + } + } + + //mark mails as read (remove "new" and "unseen" flags) + if (mwui->Seen) + SetRemoveFlagsInQueueFcn((HYAMNMAIL)ActualAccount->Mails, YAMN_MSG_DISPLAY, 0, YAMN_MSG_NEW | YAMN_MSG_UNSEEN, 0); +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "MailBrowser:DESTROY:ActualAccountMsgsSO-write done\n"); +#endif + WriteDoneFcn(ActualAccount->MessagesAccessSO); + + NOTIFYICONDATA nid; + memset(&nid, 0, sizeof(NOTIFYICONDATA)); + + delete mwui; + SetWindowLongPtr(hDlg, DWLP_USER, NULL); + + nid.cbSize = sizeof(NOTIFYICONDATA); + nid.hWnd = hDlg; + nid.uID = 0; + Shell_NotifyIcon(NIM_DELETE, &nid); + PostQuitMessage(0); + } + break; + + case WM_SHOWWINDOW: + { + struct CMailWinUserInfo *mwui = (struct CMailWinUserInfo *)GetWindowLongPtr(hDlg, DWLP_USER); + + if (mwui == nullptr) + return 0; + mwui->Seen = TRUE; + } + + case WM_YAMN_CHANGESTATUS: + if (nullptr == (ActualAccount = GetWindowAccount(hDlg))) + break; + + if ((HACCOUNT)wParam != ActualAccount) + break; + + wchar_t accstatus[512]; + GetStatusFcn(ActualAccount, accstatus); + SetDlgItemText(hDlg, IDC_STSTATUS, accstatus); + return 1; + + case WM_YAMN_CHANGECONTENT: + { + struct CUpdateMails UpdateParams; + BOOL ThisThreadWindow = (GetCurrentThreadId() == GetWindowThreadProcessId(hDlg, nullptr)); + + if (nullptr == (UpdateParams.Copied = CreateEvent(nullptr, FALSE, FALSE, nullptr))) { + DestroyWindow(hDlg); + return 0; + } + UpdateParams.Flags = (struct CChangeContent *)lParam; + UpdateParams.Waiting = !ThisThreadWindow; + +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "MailBrowser:CHANGECONTENT:posting UPDATEMAILS\n"); +#endif + if (ThisThreadWindow) { + if (!UpdateMails(hDlg, (HACCOUNT)wParam, UpdateParams.Flags->nflags, UpdateParams.Flags->nnflags)) + DestroyWindow(hDlg); + } + else if (PostMessage(hDlg, WM_YAMN_UPDATEMAILS, wParam, (LPARAM)&UpdateParams)) //this ensures UpdateMails will execute the thread who created the browser window + { + if (!ThisThreadWindow) { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "MailBrowser:CHANGECONTENT:waiting for event\n"); +#endif + WaitForSingleObject(UpdateParams.Copied, INFINITE); +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "MailBrowser:CHANGECONTENT:event signaled\n"); +#endif + } + } + + CloseHandle(UpdateParams.Copied); + } + return 1; + case WM_YAMN_UPDATEMAILS: + { + struct CUpdateMails *um = (struct CUpdateMails *)lParam; + DWORD nflags, nnflags; + +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "MailBrowser:UPDATEMAILS\n"); +#endif + + if (nullptr == (ActualAccount = GetWindowAccount(hDlg))) + return 0; + if ((HACCOUNT)wParam != ActualAccount) + return 0; + + nflags = um->Flags->nflags; + nnflags = um->Flags->nnflags; + + if (um->Waiting) + SetEvent(um->Copied); + + if (!UpdateMails(hDlg, ActualAccount, nflags, nnflags)) + DestroyWindow(hDlg); + } + return 1; + case WM_YAMN_STOPACCOUNT: + if (nullptr == (ActualAccount = GetWindowAccount(hDlg))) + break; + if ((HACCOUNT)wParam != ActualAccount) + break; + PostQuitMessage(0); + return 1; + + case WM_YAMN_NOTIFYICON: + if (nullptr == (ActualAccount = GetWindowAccount(hDlg))) + break; + + switch (lParam) { + case WM_LBUTTONDBLCLK: +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "MailBrowser:DBLCLICKICON:ActualAccountSO-read wait\n"); +#endif + if (WAIT_OBJECT_0 != WaitToReadFcn(ActualAccount->AccountAccessSO)) { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "MailBrowser:DBLCLICKICON:ActualAccountSO-read wait failed\n"); +#endif + return 0; + } +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "MailBrowser:DBLCLICKICON:ActualAccountSO-read enter\n"); +#endif + if (ActualAccount->AbilityFlags & YAMN_ACC_BROWSE) { + ShowWindow(hDlg, SW_SHOWNORMAL); + SetForegroundWindow(hDlg); + } + else + DestroyWindow(hDlg); +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "MailBrowser:DBLCLICKICON:ActualAccountSO-read done\n"); +#endif + ReadDoneFcn(ActualAccount->AccountAccessSO); + break; + } + break; + + case WM_YAMN_SHOWSELECTED: + { + int iSelect = SendDlgItemMessage(hDlg, IDC_LISTMAILS, LVM_GETNEXTITEM, -1, MAKELPARAM((UINT)LVNI_FOCUSED, 0)); // return item selected + if (iSelect != -1) { + LV_ITEMW item; + + item.iItem = iSelect; + item.iSubItem = 0; + item.mask = LVIF_PARAM | LVIF_STATE; + item.stateMask = 0xFFFFFFFF; + ListView_GetItem(GetDlgItem(hDlg, IDC_LISTMAILS), &item); + HYAMNMAIL ActualMail = (HYAMNMAIL)item.lParam; + if (nullptr != ActualMail) { + PYAMN_MAILSHOWPARAM MailParam = new YAMN_MAILSHOWPARAM; + MailParam->account = GetWindowAccount(hDlg); + MailParam->mail = ActualMail; + mir_forkthread(ShowEmailThread, MailParam); + } + } + } + break; + + case WM_SYSCOMMAND: + if (nullptr == (ActualAccount = GetWindowAccount(hDlg))) + break; + switch (wParam) { + case SC_CLOSE: + DestroyWindow(hDlg); + break; + } + break; + + case WM_COMMAND: + if (nullptr == (ActualAccount = GetWindowAccount(hDlg))) + break; + + switch (LOWORD(wParam)) { + case IDC_BTNCHECKALL: + ListView_SetItemState(GetDlgItem(hDlg, IDC_LISTMAILS), -1, 0, LVIS_SELECTED); // deselect all items + ListView_SetItemState(GetDlgItem(hDlg, IDC_LISTMAILS), -1, LVIS_SELECTED, LVIS_SELECTED); + Items = ListView_GetItemCount(GetDlgItem(hDlg, IDC_LISTMAILS)); + ListView_RedrawItems(GetDlgItem(hDlg, IDC_LISTMAILS), 0, Items); + UpdateWindow(GetDlgItem(hDlg, IDC_LISTMAILS)); + SetFocus(GetDlgItem(hDlg, IDC_LISTMAILS)); + break; + + case IDC_BTNOK: + DestroyWindow(hDlg); + break; + + case IDC_BTNAPP: + { + PROCESS_INFORMATION pi; + STARTUPINFOW si; + + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + + if (WAIT_OBJECT_0 == WaitToReadFcn(ActualAccount->AccountAccessSO)) { + if (ActualAccount->NewMailN.App != nullptr) { + WCHAR *Command; + if (ActualAccount->NewMailN.AppParam != nullptr) + Command = new WCHAR[mir_wstrlen(ActualAccount->NewMailN.App) + mir_wstrlen(ActualAccount->NewMailN.AppParam) + 6]; + else + Command = new WCHAR[mir_wstrlen(ActualAccount->NewMailN.App) + 6]; + + if (Command != nullptr) { + mir_wstrcpy(Command, L"\""); + mir_wstrcat(Command, ActualAccount->NewMailN.App); + mir_wstrcat(Command, L"\" "); + if (ActualAccount->NewMailN.AppParam != nullptr) + mir_wstrcat(Command, ActualAccount->NewMailN.AppParam); + CreateProcessW(nullptr, Command, nullptr, nullptr, FALSE, NORMAL_PRIORITY_CLASS, nullptr, nullptr, &si, &pi); + delete[] Command; + } + } + + ReadDoneFcn(ActualAccount->AccountAccessSO); + } + + if (!(GetKeyState(VK_SHIFT) & 0x8000) && !(GetKeyState(VK_CONTROL) & 0x8000)) + DestroyWindow(hDlg); + } + break; + + case IDC_BTNDEL: + { + HYAMNMAIL ActualMail; + DWORD Total = 0; + + // we use event to signal, that running thread has all needed stack parameters copied + HANDLE ThreadRunningEV = CreateEvent(nullptr, FALSE, FALSE, nullptr); + if (ThreadRunningEV == nullptr) + break; + + Items = ListView_GetItemCount(GetDlgItem(hDlg, IDC_LISTMAILS)); + + LVITEM item; + item.stateMask = 0xFFFFFFFF; + + if (WAIT_OBJECT_0 == WaitToWriteFcn(ActualAccount->MessagesAccessSO)) { + for (int i = 0; i < Items; i++) { + item.iItem = i; + item.iSubItem = 0; + item.mask = LVIF_PARAM | LVIF_STATE; + item.stateMask = 0xFFFFFFFF; + ListView_GetItem(GetDlgItem(hDlg, IDC_LISTMAILS), &item); + ActualMail = (HYAMNMAIL)item.lParam; + if (nullptr == ActualMail) + break; + if (item.state & LVIS_SELECTED) { + ActualMail->Flags |= YAMN_MSG_USERDELETE; //set to mail we are going to delete it + Total++; + } + } + + // Enable write-access to mails + WriteDoneFcn(ActualAccount->MessagesAccessSO); + + if (Total) { + wchar_t DeleteMsg[1024]; + + mir_snwprintf(DeleteMsg, TranslateT("Do you really want to delete %d selected mails?"), Total); + if (IDOK == MessageBox(hDlg, DeleteMsg, TranslateT("Delete confirmation"), MB_OKCANCEL | MB_ICONWARNING)) { + struct DeleteParam ParamToDeleteMails = { YAMN_DELETEVERSION, ThreadRunningEV, ActualAccount, nullptr }; + + // Find if there's mail marked to delete, which was deleted before + if (WAIT_OBJECT_0 == WaitToWriteFcn(ActualAccount->MessagesAccessSO)) { + for (ActualMail = (HYAMNMAIL)ActualAccount->Mails; ActualMail != nullptr; ActualMail = ActualMail->Next) { + if ((ActualMail->Flags & YAMN_MSG_DELETED) && ((ActualMail->Flags & YAMN_MSG_USERDELETE))) //if selected mail was already deleted + { + DeleteMessageFromQueueFcn((HYAMNMAIL *)&ActualAccount->Mails, ActualMail, 1); + CallService(MS_YAMN_DELETEACCOUNTMAIL, (WPARAM)ActualAccount->Plugin, (LPARAM)ActualMail); //delete it from memory + continue; + } + } + // Set flag to marked mails that they can be deleted + SetRemoveFlagsInQueueFcn((HYAMNMAIL)ActualAccount->Mails, YAMN_MSG_DISPLAY | YAMN_MSG_USERDELETE, 0, YAMN_MSG_DELETEOK, 1); + // Create new thread which deletes marked mails. + HANDLE NewThread = mir_forkthread(ActualAccount->Plugin->Fcn->DeleteMailsFcnPtr, &ParamToDeleteMails); + if (NewThread != nullptr) + WaitForSingleObject(ThreadRunningEV, INFINITE); + + // Enable write-access to mails + WriteDoneFcn(ActualAccount->MessagesAccessSO); + } + } + else //else mark messages that they are not to be deleted + SetRemoveFlagsInQueueFcn((HYAMNMAIL)ActualAccount->Mails, YAMN_MSG_DISPLAY | YAMN_MSG_USERDELETE, 0, YAMN_MSG_USERDELETE, 0); + } + } + CloseHandle(ThreadRunningEV); + if (g_plugin.getByte(YAMN_CLOSEDELETE, 0)) + DestroyWindow(hDlg); + } + break; + } + break; + + case WM_SIZE: + if (wParam == SIZE_RESTORED) { + LONG x = LOWORD(lParam); //((LPRECT)lParam)->right-((LPRECT)lParam)->left; + LONG y = HIWORD(lParam); //((LPRECT)lParam)->bottom-((LPRECT)lParam)->top; + MoveWindow(GetDlgItem(hDlg, IDC_BTNDEL), 5, y - 5 - 25, (x - 20) / 3, 25, TRUE); //where to put DELETE button while resizing + MoveWindow(GetDlgItem(hDlg, IDC_BTNCHECKALL), 10 + (x - 20) / 3, y - 5 - 25, (x - 20) / 6, 25, TRUE); //where to put CHECK ALL button while resizing + MoveWindow(GetDlgItem(hDlg, IDC_BTNAPP), 15 + (x - 20) / 3 + (x - 20) / 6, y - 5 - 25, (x - 20) / 3, 25, TRUE); //where to put RUN APP button while resizing + MoveWindow(GetDlgItem(hDlg, IDC_BTNOK), 20 + 2 * (x - 20) / 3 + (x - 20) / 6, y - 5 - 25, (x - 20) / 6, 25, TRUE); //where to put OK button while resizing + MoveWindow(GetDlgItem(hDlg, IDC_LISTMAILS), 5, 5, x - 10, y - 55, TRUE); //where to put list mail window while resizing + MoveWindow(GetDlgItem(hDlg, IDC_STSTATUS), 5, y - 5 - 45, x - 10, 15, TRUE); //where to put account status text while resizing + } + return 0; + + case WM_GETMINMAXINFO: + ((LPMINMAXINFO)lParam)->ptMinTrackSize.x = MAILBROWSER_MINXSIZE; + ((LPMINMAXINFO)lParam)->ptMinTrackSize.y = MAILBROWSER_MINYSIZE; + return 0; + + case WM_TIMER: + { + NOTIFYICONDATA nid; + struct CMailWinUserInfo *mwui = (struct CMailWinUserInfo *)GetWindowLongPtr(hDlg, DWLP_USER); + + memset(&nid, 0, sizeof(nid)); + nid.cbSize = sizeof(NOTIFYICONDATA); + nid.hWnd = hDlg; + nid.uID = 0; + nid.uFlags = NIF_ICON; + if (mwui->TrayIconState == 0) + nid.hIcon = g_LoadIconEx(0); + else + nid.hIcon = g_LoadIconEx(2); + Shell_NotifyIcon(NIM_MODIFY, &nid); + mwui->TrayIconState = !mwui->TrayIconState; + // UpdateWindow(hDlg); + } + break; + + case WM_NOTIFY: + switch (((LPNMHDR)lParam)->idFrom) { + case IDC_LISTMAILS: + switch (((LPNMHDR)lParam)->code) { + case NM_DBLCLK: + SendMessage(hDlg, WM_YAMN_SHOWSELECTED, 0, 0); + break; + + case LVN_COLUMNCLICK: + if (nullptr != (ActualAccount = GetWindowAccount(hDlg))) { + NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)lParam; + if (WAIT_OBJECT_0 == WaitToReadFcn(ActualAccount->AccountAccessSO)) { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "MailBrowser:COLUMNCLICK:ActualAccountSO-read enter\n"); +#endif + switch ((int)pNMListView->iSubItem) { + case 0: + bFrom = !bFrom; + break; + case 1: + bSub = !bSub; + break; + case 2: + bSize = !bSize; + break; + case 3: + bDate = !bDate; + break; + default: + break; + } + ListView_SortItems(pNMListView->hdr.hwndFrom, ListViewCompareProc, pNMListView->iSubItem); + ReadDoneFcn(ActualAccount->AccountAccessSO); + } + } + break; + + case NM_CUSTOMDRAW: + { + LPNMLVCUSTOMDRAW cd = (LPNMLVCUSTOMDRAW)lParam; + LONG_PTR PaintCode; + + if (nullptr == (ActualAccount = GetWindowAccount(hDlg))) + break; + + switch (cd->nmcd.dwDrawStage) { + case CDDS_PREPAINT: + PaintCode = CDRF_NOTIFYITEMDRAW; + break; + case CDDS_ITEMPREPAINT: + PaintCode = CDRF_NOTIFYSUBITEMDRAW; + break; + case CDDS_ITEMPREPAINT | CDDS_SUBITEM: + { + BOOL umma; + { + struct CMailWinUserInfo *mwui = (struct CMailWinUserInfo *)GetWindowLongPtr(hDlg, DWLP_USER); + umma = mwui->UpdateMailsMessagesAccess; + } + HYAMNMAIL ActualMail = (HYAMNMAIL)cd->nmcd.lItemlParam; + if (!ActualMail) + ActualMail = (HYAMNMAIL)readItemLParam(cd->nmcd.hdr.hwndFrom, cd->nmcd.dwItemSpec); + + if (!umma) + if (WAIT_OBJECT_0 != WaitToReadFcn(ActualAccount->MessagesAccessSO)) + return 0; + + switch (ActualMail->Flags & YAMN_MSG_SPAMMASK) { + case YAMN_MSG_SPAML1: + case YAMN_MSG_SPAML2: + cd->clrText = RGB(150, 150, 150); + break; + case YAMN_MSG_SPAML3: + cd->clrText = RGB(200, 200, 200); + cd->clrTextBk = RGB(160, 160, 160); + break; + case 0: + if (cd->nmcd.dwItemSpec & 1) + cd->clrTextBk = RGB(230, 230, 230); + break; + default: + break; + } + if (ActualMail->Flags & YAMN_MSG_UNSEEN) + cd->clrTextBk = RGB(220, 235, 250); + PaintCode = CDRF_DODEFAULT; + + if (!umma) + ReadDoneFcn(ActualAccount->MessagesAccessSO); + break; + } + default: + PaintCode = 0; + } + SetWindowLongPtr(hDlg, DWLP_MSGRESULT, PaintCode); + return 1; + } + } + } + break; + + case WM_CONTEXTMENU: + if (GetWindowLongPtr((HWND)wParam, GWLP_ID) == IDC_LISTMAILS) { + //MessageBox(0,"LISTHEADERS","Debug",0); + HWND hList = GetDlgItem(hDlg, IDC_LISTMAILS); + POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; + if (pt.x == -1) pt.x = 0; + if (pt.y == -1) pt.y = 0; + if (int numRows = ListView_GetItemCount(hList)) { + HMENU hMenu = CreatePopupMenu(); + AppendMenu(hMenu, MF_STRING, (UINT_PTR)1, TranslateT("Copy Selected")); + AppendMenu(hMenu, MF_STRING, (UINT_PTR)2, TranslateT("Copy All")); + AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr); + AppendMenu(hMenu, MF_STRING, (UINT_PTR)0, TranslateT("Cancel")); + int nReturnCmd = TrackPopupMenu(hMenu, TPM_RETURNCMD, pt.x, pt.y, 0, hDlg, nullptr); + DestroyMenu(hMenu); + if (nReturnCmd > 0) { + int courRow = 0; + size_t sizeNeeded = 0; + wchar_t from[128] = { 0 }, subject[256] = { 0 }, size[16] = { 0 }, date[64] = { 0 }; + for (courRow = 0; courRow < numRows; courRow++) { + if ((nReturnCmd == 1) && (ListView_GetItemState(hList, courRow, LVIS_SELECTED) == 0)) continue; + ListView_GetItemText(hList, courRow, 0, from, _countof(from)); + ListView_GetItemText(hList, courRow, 1, subject, _countof(subject)); + ListView_GetItemText(hList, courRow, 2, size, _countof(size)); + ListView_GetItemText(hList, courRow, 3, date, _countof(date)); + sizeNeeded += 5 + mir_wstrlen(from) + mir_wstrlen(subject) + mir_wstrlen(size) + mir_wstrlen(date); + } + if (sizeNeeded && OpenClipboard(hDlg)) { + EmptyClipboard(); + HGLOBAL hData = GlobalAlloc(GMEM_MOVEABLE, (sizeNeeded + 1) * sizeof(wchar_t)); + wchar_t *buff = (wchar_t *)GlobalLock(hData); + int courPos = 0; + for (courRow = 0; courRow < numRows; courRow++) { + if ((nReturnCmd == 1) && (ListView_GetItemState(hList, courRow, LVIS_SELECTED) == 0)) continue; + ListView_GetItemText(hList, courRow, 0, from, _countof(from)); + ListView_GetItemText(hList, courRow, 1, subject, _countof(subject)); + ListView_GetItemText(hList, courRow, 2, size, _countof(size)); + ListView_GetItemText(hList, courRow, 3, date, _countof(date)); + courPos += mir_snwprintf(&buff[courPos], sizeNeeded + 1, L"%s\t%s\t%s\t%s\r\n", from, subject, size, date); + } + GlobalUnlock(hData); + + SetClipboardData(CF_UNICODETEXT, hData); + + CloseClipboard(); + } + } + } + } + break; // just in case + } + return 0; +} + +LRESULT CALLBACK ListViewSubclassProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + HWND hwndParent = GetParent(hDlg); + + switch (msg) { + case WM_GETDLGCODE: + { + LPMSG lpmsg = (LPMSG)lParam; + if (lpmsg != nullptr) { + if (lpmsg->message == WM_KEYDOWN + && lpmsg->wParam == VK_RETURN) + return DLGC_WANTALLKEYS; + } + } + break; + + case WM_KEYDOWN: + { + BOOL isCtrl = GetKeyState(VK_CONTROL) & 0x8000; + BOOL isShift = GetKeyState(VK_SHIFT) & 0x8000; + BOOL isAlt = GetKeyState(VK_MENU) & 0x8000; + + switch (wParam) { + case 'A': // ctrl-a + if (!isAlt && !isShift && isCtrl) SendMessage(hwndParent, WM_COMMAND, IDC_BTNCHECKALL, 0); + break; + case VK_RETURN: + case VK_SPACE: + if (!isAlt && !isShift && !isCtrl) SendMessage(hwndParent, WM_YAMN_SHOWSELECTED, 0, 0); + break; + case VK_DELETE: + SendMessage(hwndParent, WM_COMMAND, IDC_BTNDEL, 0); + break; + } + } + break; + } + return mir_callNextSubclass(hDlg, ListViewSubclassProc, msg, wParam, lParam); +} + +void __cdecl MailBrowser(void *Param) +{ + MSG msg; + + HWND hMailBrowser; + BOOL WndFound = FALSE; + + struct MailBrowserWinParam MyParam = *(struct MailBrowserWinParam *)Param; + HACCOUNT ActualAccount = MyParam.account; + SCIncFcn(ActualAccount->UsingThreads); + + // we will not use params in stack anymore + SetEvent(MyParam.ThreadRunningEV); + + __try { + if (WAIT_OBJECT_0 != WaitToReadFcn(ActualAccount->AccountAccessSO)) + return; + + if (!(ActualAccount->AbilityFlags & YAMN_ACC_BROWSE)) { + MyParam.nflags = MyParam.nflags & ~YAMN_ACC_MSG; + MyParam.nnflags = MyParam.nnflags & ~YAMN_ACC_MSG; + } + + if (!(ActualAccount->AbilityFlags & YAMN_ACC_POPUP)) + MyParam.nflags = MyParam.nflags & ~YAMN_ACC_POP; + + ReadDoneFcn(ActualAccount->AccountAccessSO); + + if (nullptr != (hMailBrowser = WindowList_Find(YAMNVar.NewMailAccountWnd, (UINT_PTR)ActualAccount))) + WndFound = TRUE; + + if ((hMailBrowser == nullptr) && ((MyParam.nflags & YAMN_ACC_MSG) || (MyParam.nflags & YAMN_ACC_ICO) || (MyParam.nnflags & YAMN_ACC_MSG))) { + hMailBrowser = CreateDialogParamW(g_plugin.getInst(), MAKEINTRESOURCEW(IDD_DLGVIEWMESSAGES), nullptr, DlgProcYAMNMailBrowser, (LPARAM)&MyParam); + Window_SetIcon_IcoLib(hMailBrowser, g_GetIconHandle(2)); + MoveWindow(hMailBrowser, PosX, PosY, SizeX, SizeY, TRUE); + } + + if (hMailBrowser != nullptr) { + struct CChangeContent Params = { MyParam.nflags, MyParam.nnflags }; //if this thread created window, just post message to update mails + + SendMessage(hMailBrowser, WM_YAMN_CHANGECONTENT, (WPARAM)ActualAccount, (LPARAM)&Params); //we ensure this will do the thread who created the browser window + } + else + UpdateMails(nullptr, ActualAccount, MyParam.nflags, MyParam.nnflags); //update mails without displaying or refreshing any window + + if ((hMailBrowser != nullptr) && !WndFound) { //we process message loop only for thread that created window + while (GetMessage(&msg, nullptr, 0, 0)) { + if (hMailBrowser == nullptr || !IsDialogMessage(hMailBrowser, &msg)) { /* Wine fix. */ + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + } + + if ((!WndFound) && (ActualAccount->Plugin->Fcn != nullptr) && (ActualAccount->Plugin->Fcn->WriteAccountsFcnPtr != nullptr) && ActualAccount->AbleToWork) + ActualAccount->Plugin->Fcn->WriteAccountsFcnPtr(); + } + __finally { + SCDecFcn(ActualAccount->UsingThreads); + } +} + +INT_PTR RunMailBrowserSvc(WPARAM wParam, LPARAM lParam) +{ + PYAMN_MAILBROWSERPARAM Param = (PYAMN_MAILBROWSERPARAM)wParam; + + if ((DWORD)lParam != YAMN_MAILBROWSERVERSION) + return 0; + + //an event for successfull copy parameters to which point a pointer in stack for new thread + HANDLE ThreadRunningEV = CreateEvent(nullptr, FALSE, FALSE, nullptr); + Param->ThreadRunningEV = ThreadRunningEV; + + HANDLE NewThread = mir_forkthread(MailBrowser, Param); + if (NewThread != nullptr) + WaitForSingleObject(ThreadRunningEV, INFINITE); + + CloseHandle(ThreadRunningEV); + return 1; +} diff --git a/protocols/YAMN/src/debug.cpp b/protocols/YAMN/src/debug.cpp new file mode 100644 index 0000000000..971b3dcbce --- /dev/null +++ b/protocols/YAMN/src/debug.cpp @@ -0,0 +1,127 @@ +/* + * YAMN plugin main file + * Miranda homepage: http://miranda-icq.sourceforge.net/ + * + * Debug functions used in DEBUG release (you need to global #define DEBUG to get debug version) + * + * (c) majvan 2002-2004 + */ + +#include "stdafx.h" + +#ifdef _DEBUG + +//-------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------- + +wchar_t DebugUserDirectory[MAX_PATH] = L"."; +CRITICAL_SECTION FileAccessCS; + +#ifdef DEBUG_SYNCHRO +wchar_t DebugSynchroFileName2[]=L"%s\\yamn-debug.synchro.log"; +HANDLE SynchroFile; +#endif + +#ifdef DEBUG_COMM +wchar_t DebugCommFileName2[]=L"%s\\yamn-debug.comm.log"; +HANDLE CommFile; +#endif + +#ifdef DEBUG_DECODE +wchar_t DebugDecodeFileName2[]=L"%s\\yamn-debug.decode.log"; +HANDLE DecodeFile; +#endif + +//-------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------- + +void InitDebug() +{ +#if defined (DEBUG_SYNCHRO) || defined (DEBUG_COMM) || defined (DEBUG_DECODE) + wchar_t DebugFileName[MAX_PATH]; +#endif + InitializeCriticalSection(&FileAccessCS); + +#ifdef DEBUG_SYNCHRO + mir_snwprintf(DebugFileName, DebugSynchroFileName2, DebugUserDirectory); + + SynchroFile=CreateFile(DebugFileName,GENERIC_WRITE,FILE_SHARE_WRITE|FILE_SHARE_READ,NULL,CREATE_ALWAYS,0,NULL); + DebugLog(SynchroFile,"Synchro debug file created by %s\n",YAMN_VER); +#endif + +#ifdef DEBUG_COMM + mir_snwprintf(DebugFileName, DebugCommFileName2, DebugUserDirectory); + + CommFile=CreateFile(DebugFileName,GENERIC_WRITE,FILE_SHARE_WRITE|FILE_SHARE_READ,NULL,CREATE_ALWAYS,0,NULL); + DebugLog(CommFile,"Communication debug file created by %s\n",YAMN_VER); +#endif + +#ifdef DEBUG_DECODE + mir_snwprintf(DebugFileName, DebugDecodeFileName2, DebugUserDirectory); + + DecodeFile=CreateFile(DebugFileName,GENERIC_WRITE,FILE_SHARE_WRITE|FILE_SHARE_READ,NULL,CREATE_ALWAYS,0,NULL); + DebugLog(DecodeFile,"Decoding kernel debug file created by %s\n",YAMN_VER); +#endif +} + +void UnInitDebug() +{ + DeleteCriticalSection(&FileAccessCS); +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"File is being closed normally."); + CloseHandle(SynchroFile); +#endif +#ifdef DEBUG_COMM + DebugLog(CommFile,"File is being closed normally."); + CloseHandle(CommFile); +#endif +#ifdef DEBUG_DECODE + DebugLog(DecodeFile,"File is being closed normally."); + CloseHandle(DecodeFile); +#endif +} + + +void DebugLog(HANDLE File,const char *fmt,...) +{ + char *str; + char tids[32]; + va_list vararg; + int strsize; + DWORD Written; + + va_start(vararg,fmt); + str=(char *)malloc(strsize=65536); + mir_snprintf(tids, "[%x]",GetCurrentThreadId()); + while(mir_vsnprintf(str, strsize, fmt, vararg)==-1) + str=(char *)realloc(str,strsize+=65536); + va_end(vararg); + EnterCriticalSection(&FileAccessCS); + WriteFile(File,tids,(DWORD)mir_strlen(tids),&Written,nullptr); + WriteFile(File,str,(DWORD)mir_strlen(str),&Written,nullptr); + LeaveCriticalSection(&FileAccessCS); + free(str); +} + +void DebugLogW(HANDLE File,const WCHAR *fmt,...) +{ + WCHAR *str; + char tids[32]; + va_list vararg; + int strsize; + DWORD Written; + + va_start(vararg,fmt); + str=(WCHAR *)malloc((strsize=65536)*sizeof(WCHAR)); + mir_snprintf(tids, "[%x]",GetCurrentThreadId()); + while(mir_vsnwprintf(str, strsize, fmt, vararg)==-1) + str=(WCHAR *)realloc(str,(strsize+=65536)*sizeof(WCHAR)); + va_end(vararg); + EnterCriticalSection(&FileAccessCS); + WriteFile(File,tids,(DWORD)mir_strlen(tids),&Written,nullptr); + WriteFile(File,str,(DWORD)mir_wstrlen(str)*sizeof(WCHAR),&Written,nullptr); + LeaveCriticalSection(&FileAccessCS); + free(str); +} + +#endif //ifdef DEBUG
\ No newline at end of file diff --git a/protocols/YAMN/src/debug.h b/protocols/YAMN/src/debug.h new file mode 100644 index 0000000000..a28add5662 --- /dev/null +++ b/protocols/YAMN/src/debug.h @@ -0,0 +1,51 @@ +#ifndef __DEBUG_H +#define __DEBUG_H + +#ifdef _DEBUG + +//#define DEBUG_SYNCHRO //debug synchro to a file +//#define DEBUG_COMM //debug communiation to a file +//#define DEBUG_DECODE //debug header decoding to a file +//#define DEBUG_DECODECODEPAGE //add info about codepage used in conversion +//#define DEBUG_DECODEBASE64 //add info about base64 result +//#define DEBUG_DECODEQUOTED //add info about quoted printable result +//#define DEBUG_FILEREAD //debug file reading to message boxes +//#define DEBUG_FILEREADMESSAGES //debug file reading messages to message boxes + +void DebugLog(HANDLE,const char *fmt,...); +void DebugLogW(HANDLE File,const WCHAR *fmt,...); + +#ifdef DEBUG_SYNCHRO +// Used for synchronization debug +extern HANDLE SynchroFile; +#endif + +#ifdef DEBUG_COMM +// Used for communication debug +extern HANDLE CommFile; +#endif + +#ifdef DEBUG_DECODE +// Used for decoding debug +extern HANDLE DecodeFile; +#endif + +#if defined(DEBUG_FILEREAD) || defined(DEBUG_FILEREADMESSAGES) +DWORD ReadStringFromMemory(char **Parser,char *End,char **StoreTo,char *DebugString); + +DWORD ReadStringFromMemoryW(char **Parser,wchar_t *End,char **StoreTo,wchar_t *DebugString); + +#else +DWORD ReadStringFromMemory(char **Parser,char *End,char **StoreTo); + +DWORD ReadStringFromMemoryW(WCHAR **Parser,WCHAR *End,WCHAR **StoreTo); + +#endif + +//#ifdef DEBUG_ACCOUNTS +//int GetAccounts(); +//void WriteAccounts(); +//#endif + +#endif //YAMN_DEBUG +#endif //_DEBUG_H diff --git a/protocols/YAMN/src/filterplugin.cpp b/protocols/YAMN/src/filterplugin.cpp new file mode 100644 index 0000000000..dca5780152 --- /dev/null +++ b/protocols/YAMN/src/filterplugin.cpp @@ -0,0 +1,200 @@ +/* + * YAMN plugin export functions for filtering + * + * (c) majvan 2002-2004 + */ + +#include "stdafx.h" + +//-------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------- + +PYAMN_FILTERPLUGINQUEUE FirstFilterPlugin=nullptr; + +INT_PTR RegisterFilterPluginSvc(WPARAM,LPARAM); + +//Removes plugin from queue and deletes its structures +INT_PTR UnregisterFilterPlugin(HYAMNFILTERPLUGIN Plugin); + +INT_PTR UnregisterFilterPluginSvc(WPARAM wParam,LPARAM lParam); + +//Removes all filter plugins +INT_PTR UnregisterFilterPlugins(); + +INT_PTR FilterMailSvc(WPARAM,LPARAM); + +//Sets imported functions for an plugin and therefore it starts plugin to be registered and running +// Plugin- plugin, which wants to set its functions +// Importance- importance of plugin (see m_filterplugin.h) +// YAMNFilterFcn- pointer to imported functions +// YAMNfilterFcnVer- version of YAMN_FILTERIMPORTFCN, use YAMN_FILTERIMPORTFCNVERSION +// returns nonzero if success +int WINAPI SetFilterPluginFcnImportFcn(HYAMNFILTERPLUGIN Plugin,DWORD Importance,PYAMN_FILTERIMPORTFCN YAMNFilterFcn,DWORD YAMNFilterFcnVer); + +struct CExportedFunctions FilterPluginExportedFcn[]= +{ + {YAMN_SETFILTERPLUGINFCNIMPORTID,(void *)SetFilterPluginFcnImportFcn}, +}; + +struct CExportedServices FilterPluginExportedSvc[]= +{ + {MS_YAMN_REGISTERFILTERPLUGIN,RegisterFilterPluginSvc}, + {MS_YAMN_UNREGISTERFILTERPLUGIN,UnregisterFilterPluginSvc}, +}; + +//-------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------- + +INT_PTR RegisterFilterPluginSvc(WPARAM wParam,LPARAM lParam) +{ + PYAMN_FILTERREGISTRATION Registration=(PYAMN_FILTERREGISTRATION)wParam; + HYAMNFILTERPLUGIN Plugin; + + if (lParam != YAMN_FILTERREGISTRATIONVERSION) + return 0; + if ((Registration->Name==nullptr) || (Registration->Ver==nullptr)) + return NULL; + if (nullptr==(Plugin=new YAMN_FILTERPLUGIN)) + return NULL; + + Plugin->PluginInfo=Registration; + + Plugin->FilterFcn=nullptr; + +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"::: YAMN- new filter registered: %0x (%s) :::\n",Plugin,Registration->Name); +#endif + return (INT_PTR)Plugin; +} + +INT_PTR UnregisterFilterPlugin(HYAMNFILTERPLUGIN Plugin) +{ + PYAMN_FILTERPLUGINQUEUE Parser,Found; + + if (FirstFilterPlugin->Plugin==Plugin) + { + Found=FirstFilterPlugin; + FirstFilterPlugin=FirstFilterPlugin->Next; + } + else + { + for (Parser=FirstFilterPlugin;(Parser->Next != nullptr) && (Plugin != Parser->Next->Plugin);Parser=Parser->Next); + if (Parser->Next != nullptr) + { + Found=Parser->Next; + Parser->Next=Parser->Next->Next; + } + else + Found=nullptr; + } + if (Found != nullptr) + { + if (Plugin->FilterFcn->UnLoadFcn != nullptr) + Plugin->FilterFcn->UnLoadFcn((void *)nullptr); + + delete Found->Plugin; + delete Found; +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"::: YAMN- filter %0x unregistered :::\n",Plugin); +#endif + } + else + return 0; + return 1; +} + +INT_PTR UnregisterFilterPluginSvc(WPARAM wParam,LPARAM) +{ + HYAMNFILTERPLUGIN Plugin=(HYAMNFILTERPLUGIN)wParam; + + mir_cslock lck(PluginRegCS); + UnregisterFilterPlugin(Plugin); + return 1; +} + +INT_PTR UnregisterFilterPlugins() +{ + mir_cslock lck(PluginRegCS); + + // We remove protocols from the protocol list + while(FirstFilterPlugin != nullptr) + UnregisterFilterPlugin(FirstFilterPlugin->Plugin); + return 1; +} + +int WINAPI SetFilterPluginFcnImportFcn(HYAMNFILTERPLUGIN Plugin,DWORD Importance,PYAMN_FILTERIMPORTFCN YAMNFilterFcn,DWORD YAMNFilterFcnVer) +{ + PYAMN_FILTERPLUGINQUEUE Parser,Previous; + + if (YAMNFilterFcnVer != YAMN_FILTERIMPORTFCNVERSION) + return 0; + if (YAMNFilterFcn==nullptr) + return 0; + +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"::: YAMN- filter %0x import succeed :::\n",Plugin); +#endif + Plugin->Importance=Importance; + Plugin->FilterFcn=YAMNFilterFcn; + + mir_cslock lck(PluginRegCS); + // We add protocol to the protocol list + for (Previous = nullptr, Parser = FirstFilterPlugin; Parser != nullptr && Parser->Next != nullptr && Parser->Plugin->Importance <= Importance; Previous = Parser, Parser = Parser->Next); + if (Previous==nullptr) //insert to the beginnig of queue + { + FirstFilterPlugin=new YAMN_FILTERPLUGINQUEUE; + FirstFilterPlugin->Plugin=Plugin; + FirstFilterPlugin->Next=Parser; + } + else + { + Previous->Next=new YAMN_FILTERPLUGINQUEUE; + Previous=Previous->Next; //leave previous, go to actual plugin + Previous->Plugin=Plugin; + Previous->Next=Parser; //and in actual plugin set, that next plugin is the one we insert in front of + } + return 1; +} + +INT_PTR FilterMailSvc(WPARAM wParam,LPARAM lParam) +{ + HACCOUNT Account=(HACCOUNT)wParam; + HYAMNMAIL Mail=(HYAMNMAIL)lParam; + PYAMN_FILTERPLUGINQUEUE ActualPlugin; + + mir_cslock lck(PluginRegCS); +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"FilterMail:ActualAccountMsgsSO-write wait\n"); +#endif + WaitToWriteFcn(Account->MessagesAccessSO); +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"FilterMail:ActualAccountMsgsSO-write enter\n"); +#endif + for (ActualPlugin=FirstFilterPlugin;ActualPlugin != nullptr;ActualPlugin=ActualPlugin->Next) + { + if (ActualPlugin->Plugin->FilterFcn->FilterMailFcnPtr != nullptr) + { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"\tFiltering Mail, running plugin %0x to filter mail\n",ActualPlugin->Plugin); +#endif + ActualPlugin->Plugin->FilterFcn->FilterMailFcnPtr(Account,YAMN_ACCOUNTVERSION,Mail,YAMN_MAILVERSION); +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"\tFiltering Mail done\n"); +#endif + } + } + Mail->Flags|=YAMN_MSG_FILTERED; + +//Set mail flags according to spamlevel settings + if ((Mail->Flags & YAMN_MSG_SPAMMASK) > YAMN_MSG_SPAML1) + Mail->Flags=Mail->Flags & ~(YAMN_MSG_BROWSER | YAMN_MSG_POPUP | YAMN_MSG_SYSTRAY | YAMN_MSG_SOUND | YAMN_MSG_APP | YAMN_MSG_NEVENT); + if (YAMN_MSG_SPAML(Mail->Flags,YAMN_MSG_SPAML3) || YAMN_MSG_SPAML(Mail->Flags,YAMN_MSG_SPAML4)) + Mail->Flags=Mail->Flags | (YAMN_MSG_AUTODELETE | YAMN_MSG_DELETEOK); //set message to delete + if (YAMN_MSG_SPAML(Mail->Flags,YAMN_MSG_SPAML3)) + Mail->Flags=Mail->Flags & ~(YAMN_MSG_MEMDELETE); //set message not to delete it immidiatelly from memory +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"FilterMail:ActualAccountMsgsSO-write done\n"); +#endif + WriteDoneFcn(Account->MessagesAccessSO); + return 1; +} diff --git a/protocols/YAMN/src/mails/decode.cpp b/protocols/YAMN/src/mails/decode.cpp new file mode 100644 index 0000000000..5ac1346c7c --- /dev/null +++ b/protocols/YAMN/src/mails/decode.cpp @@ -0,0 +1,555 @@ +/* + * This code implements decoding encoded MIME header in style + * =?iso-8859-2?Q? "User using email in central Europe characters such as =E9" ?= + * + * (c) majvan 2002-2004 + */ +#include "../stdafx.h" +//-------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------- + +struct _tcptable CodePageNamesAll[]= +{ + { "ANSI", "",TRUE,CP_ACP}, + { "WINDOWS-1", "250",0,1250}, + { "WINDOWS-1", "251",0,1251}, + { "WINDOWS-1", "252",0,1252}, + { "WINDOWS-1", "253",0,1253}, + { "WINDOWS-1", "254",0,1254}, + { "WINDOWS-1", "255",0,1255}, + { "WINDOWS-1", "256",0,1256}, + { "WINDOWS-1", "257",0,1257}, + { "WINDOWS-1", "258",0,1258}, + { "CP1", "250",0,1250}, + { "CP1", "251",0,1251}, + { "CP1", "252",0,1252}, + { "CP1", "253",0,1253}, + { "CP1", "254",0,1254}, + { "CP1", "255",0,1255}, + { "CP1", "256",0,1256}, + { "CP1", "257",0,1257}, + { "CP1", "258",0,1258}, + { "ANSI-1", "250",0,1250}, + { "ANSI-1", "251",0,1251}, + { "ANSI-1", "252",0,1252}, + { "ANSI-1", "253",0,1253}, + { "ANSI-1", "254",0,1254}, + { "ANSI-1", "255",0,1255}, + { "ANSI-1", "256",0,1256}, + { "ANSI-1", "257",0,1257}, + { "ANSI-1", "258",0,1258}, + { "KOI8", "-R",0,20866}, + { "KOI8", "",0,20866}, + { "KOI8", "-U",0,21866}, + { "KOI8", "-RU",0,21866}, + { "US-", "ASCII",0,20127}, + { "CP", "367",0,20127}, + { "ASCII", "",0,20127}, + { "ASCII", "7",0,20127}, + { "ISO-8859", "-1",0,28591}, + { "ISO-8859", "-2",0,28592}, + { "ISO-8859", "-3",0,28593}, + { "ISO-8859", "-4",0,28594}, + { "ISO-8859", "-5",0,28595}, + { "ISO-8859", "-6",0,28596}, + { "ISO-8859", "-7",0,28597}, + { "ISO-8859", "-8",0,28598}, + { "ISO-8859", "-9",0,28599}, + { "ISO-8859", "-15",0,28605}, + { "ISO_8859", "-1",0,28591}, + { "ISO_8859", "-2",0,28592}, + { "ISO_8859", "-3",0,28593}, + { "ISO_8859", "-4",0,28594}, + { "ISO_8859", "-5",0,28595}, + { "ISO_8859", "-6",0,28596}, + { "ISO_8859", "-7",0,28597}, + { "ISO_8859", "-8",0,28598}, + { "ISO_8859", "-9",0,28599}, + { "ISO_8859", "-15",0,28605}, + { "ISO-", "10646-USC2",0,1200}, + { "ISO-2022", "/2-JP",0,50220}, + { "ISO-2022", "-JP",0,50221}, + { "ISO-2022", "/JIS-JP",0,50222}, + { "ISO-2022", "-KR",0,50225}, + { "ISO-2022", "-CH(SP)",0,50227}, + { "ISO-2022", "-CH(TR)",0,50229}, + { "UTF-", "7",0,65000}, + { "UTF-", "8",0,65001}, + { "ARAB-", "TRANSPARENT",0,710}, + { "ASMO-", "TRANSPARENT",0,720}, + { "ASMO-", "449",0,709}, + { "ASMO-", "708",0,708}, + { "BIG5", "",0,950}, + { "EUC-", "CH(SP)",0,51936}, + { "EUC-", "CH(TR)",0,51950}, + { "EUC-", "JP",0,51932}, + { "EUC-", "KR",0,51949}, + { "GB-", "2312",0,20936}, + { "GB", "2312",0,20936}, + { "HZGB-", "2312",0,52936}, + { "IBM-", "037",0,37}, + { "IBM-", "290",0,290}, + { "IBM-", "437",0,437}, + { "IBM-", "500",0,500}, + { "IBM-", "775",0,775}, + { "IBM-", "850",0,850}, + { "IBM-", "852",0,852}, + { "IBM-", "855",0,855}, + { "IBM-", "857",0,857}, + { "IBM-", "860",0,860}, + { "IBM-", "861",0,861}, + { "IBM-", "862",0,862}, + { "IBM-", "863",0,863}, + { "IBM-", "864",0,864}, + { "IBM-", "865",0,865}, + { "IBM-", "866",0,866}, + { "IBM-", "869",0,869}, + { "IBM-", "870",0,870}, + { "IBM-", "875",0,875}, + { "IBM-", "1026",0,1026}, + { "IBM-", "273",0,20273}, + { "IBM-", "277",0,20277}, + { "IBM-", "278",0,20278}, + { "IBM-", "280",0,20280}, + { "IBM-", "284",0,20284}, + { "IBM-", "285",0,20285}, + { "IBM-", "290",0,20290}, + { "IBM-", "297",0,20297}, + { "IBM-", "420",0,20420}, + { "IBM-", "423",0,20423}, + { "IBM-", "871",0,20871}, + { "IBM-", "880",0,20880}, + { "IBM-", "905",0,20905}, + { "IBM-", "THAI",0,20838}, + { "ISCII-", "DEVANAGARI",0,57002}, + { "ISCII-", "BENGALI",0,57003}, + { "ISCII-", "TAMIL",0,57004}, + { "ISCII-", "TELUGU",0,57005}, + { "ISCII-", "ASSAMESE",0,57006}, + { "ISCII-", "ORIYA",0,57007}, + { "ISCII-", "KANNADA",0,57008}, + { "ISCII-", "MALAYALAM",0,57009}, + { "ISCII-", "GUJARATI",0,57010}, + { "ISCII-", "PUNJABI",0,57011}, + { "KOR-", "JOHAB",0,1361}, + { "KSC-", "5601",0,1361}, + { "MAC-", "ROMAN",0,10000}, + { "MAC-", "JP",0,10001}, + { "MAC-", "CH(SP)(BIG5)",0,10002}, + { "MAC-", "KR",0,10003}, + { "MAC-", "AR",0,10004}, + { "MAC-", "HW",0,10005}, + { "MAC-", "GR",0,10006}, + { "MAC-", "CY",0,10007}, + { "MAC-", "CH(SP)(GB2312)",0,10008}, + { "MAC-", "ROMANIA",0,10010}, + { "MAC-", "UA",0,10017}, + { "MAC-", "TH",0,10021}, + { "MAC-", "LAT2",0,10029}, + { "MAC-", "ICE",0,10079}, + { "MAC-", "TR",0,10081}, + { "MAC-", "CR",0,10082} +}; + +int CPLENALL = _countof(CodePageNamesAll); +struct _tcptable *CodePageNamesSupp; +int CPLENSUPP = 1; + +//Gets codepage ID from string representing charset such as "iso-8859-1" +// input- the string +// size- max length of input string +int GetCharsetFromString(char *input,size_t size); + +//HexValue to DecValue ('a' to 10) +// HexValue- hexa value ('a') +// DecValue- poiner where to store dec value +// returns 0 if not success +int FromHexa(char HexValue,char *DecValue); + +//Decodes a char from Base64 +// Base64Value- input char in Base64 +// DecValue- pointer where to store the result +// returns 0 if not success +int FromBase64(char Base64Value,char *DecValue); + +//Decodes string in quoted printable +// Src- input string +// Dst- where to store output string +// DstLen- how max long should be output string +// isQ- if is "Q-encoding" modification. should be TRUE in headers +// always returns 1 +int DecodeQuotedPrintable(char *Src,char *Dst,int DstLen, BOOL isQ); + +//Decodes string in base64 +// Src- input string +// Dst- where to store output string +// DstLen- how max long should be output string +// returns 0 if string was not properly decoded +int DecodeBase64(char *Src,char *Dst,int DstLen); + +//Converts string to unicode from string with specified codepage +// stream- input string +// cp- codepage of input string +// out- pointer to new allocated memory that contains unicode string +int ConvertStringToUnicode(char *stream,unsigned int cp,WCHAR **out); + +//Converts string from MIME header to unicode +// stream- input string +// cp- codepage of input string +// storeto- pointer to memory that contains unicode string +// mode- MIME_PLAIN or MIME_MAIL (MIME_MAIL deletes '"' from start and end of string) +void ConvertCodedStringToUnicode(char *stream,WCHAR **storeto,DWORD cp,int mode); + +//-------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------- + +int GetCharsetFromString(char *input,size_t size) +//"ISO-8859-1" to ID from table +{ + char *pin=input; + char *pout,*parser; + + if ((size<1) || (parser=pout=new char[size+1])==nullptr) + return -1; + while((*pin != 0) && (pin-input< (INT_PTR)size)) + { + if ((*pin>='a') && (*pin<='z')) + *parser++=*(pin++)-('a'-'A'); // make it capital + //else if (*pin=='\"') // this is already done in ExtractFromContentType + // *pin++; //skip the quotes if any + else + *parser++=*pin++; + } + + *parser = 0; + +#ifdef DEBUG_DECODECODEPAGE + DebugLog(DecodeFile,"<CodePage>%s</CodePage>",pout); +#endif + for (int i=0;i<CPLENALL;i++) { + size_t len = mir_strlen(CodePageNamesAll[i].NameBase); + if (0==strncmp(pout,CodePageNamesAll[i].NameBase,len)) { + if (0==mir_strcmp(pout+len,CodePageNamesAll[i].NameSub)) { + delete[] pout; + return CodePageNamesAll[i].CP; + } + } + } + delete[] pout; + return -1; //not found +} + +int FromHexa(char HexValue,char *DecValue) +{ + if (HexValue>='0' && HexValue<='9') + { + *DecValue=HexValue-'0'; + return 1; + } + if (HexValue>='A' && HexValue<='F') + { + *DecValue=HexValue-'A'+10; + return 1; + } + if (HexValue>='a' && HexValue<='f') + { + *DecValue=HexValue-'a'+10; + return 1; + } + return 0; +} + +int FromBase64(char Base64Value,char *DecValue) +{ + if (Base64Value>='A' && Base64Value<='Z') + { + *DecValue=Base64Value-'A'; + return 1; + } + if (Base64Value>='a' && Base64Value<='z') + { + *DecValue=Base64Value-'a'+26; + return 1; + } + if (Base64Value>='0' && Base64Value<='9') + { + *DecValue=Base64Value-'0'+52; + return 1; + } + if (Base64Value=='+') + { + *DecValue=Base64Value-'+'+62; + return 1; + } + if (Base64Value=='/') + { + *DecValue=Base64Value-'/'+63; + return 1; + } + if (Base64Value=='=') + { + *DecValue=0; + return 1; + } + return 0; +} + +int DecodeQuotedPrintable(char *Src,char *Dst,int DstLen, BOOL isQ) +{ +#ifdef DEBUG_DECODEQUOTED + char *DstTemp=Dst; + DebugLog(DecodeFile,"<Decode Quoted><Input>%s</Input>",Src); +#endif + for (int Counter=0;(*Src != 0) && DstLen && (Counter++<DstLen);Src++,Dst++) + if (*Src=='=') + { + if (!isQ) { + if (Src[1]==0x0D) { + Src++; Src++; + if (Src[0]==0x0A) Src++; + goto CopyCharQuotedPrintable; + } + if (Src[1]==0x0A) { + Src++; Src++; + goto CopyCharQuotedPrintable; + } + } + char First,Second; + if (!FromHexa(*(++Src),&First)) + { + *Dst++='=';Src--; + continue; + } + if (!FromHexa(*(++Src),&Second)) + { + *Dst++='=';Src--;Src--; + continue; + } + *Dst=(char)(First)<<4; + *Dst+=Second; + } + else if (isQ && *Src=='_') + *Dst=' '; + else +CopyCharQuotedPrintable: // Yeah. Bad programming stile. + *Dst=*Src; + *Dst=0; +#ifdef DEBUG_DECODEQUOTED + DebugLog(DecodeFile,"<Output>%s</Output></Decode Quoted>",DstTemp); +#endif + return 1; +} + +int DecodeBase64(char *Src,char *Dst,int DstLen) +{ + int Result=0; + char Locator=0,MiniResult[4]; + char *End=Dst+DstLen; + + MiniResult[0]=MiniResult[1]=MiniResult[2]=MiniResult[3]=0; + +#ifdef DEBUG_DECODEBASE64 + char *DstTemp=Dst; + DebugLog(DecodeFile,"<Decode Base64><Input>\n%s\n</Input>\n",Src); +#endif + while(*Src != 0 && DstLen && Dst != End) + { + if ((*Src==0x0D)||(*Src==0x0A)) { + Src++; + continue; + } + if ((!(Result=FromBase64(*Src,MiniResult+Locator)) && (*Src==0)) || Locator++==3) //end_of_str || end_of_4_bytes + { + Locator=0; //next write to the first byte + *Dst++=(char)((MiniResult[0]<<2) | (MiniResult[1]>>4)); + if (Dst==End) goto end; //DstLen exceeded? + *Dst++=(char)((MiniResult[1]<<4) | (MiniResult[2]>>2)); + if (Dst==End) goto end; //someones don't like goto, but not me + *Dst++=(char)((MiniResult[2]<<6) | MiniResult[3]); + if (!Result && (*Src==0)) goto end; //end of string? + MiniResult[0]=MiniResult[1]=MiniResult[2]=MiniResult[3]=0; //zero 4byte buffer for next loop + } + if (!Result) return 0; //unrecognised character occured + Src++; + } +end: + *Dst=0; +#ifdef DEBUG_DECODEBASE64 + DebugLog(DecodeFile,"<Output>\n%s\n</Output></Decode Base64>",DstTemp); +#endif + return 1; +} + + + +int ConvertStringToUnicode(char *stream,unsigned int cp,WCHAR **out) +{ + CPINFO CPInfo; + WCHAR *temp,*src=*out,*dest; + size_t outlen; + int streamlen,Index; + + //codepages, which require to have set 0 in dwFlags parameter when calling MultiByteToWideChar + DWORD CodePagesZeroFlags[]={50220,50221,50222,50225,50227,50229,52936,54936,57002,57003,57004,57005,57006,57007,57008,57009,57010,57011,65000,65001}; + + if ((cp != CP_ACP) && (cp != CP_OEMCP) && (cp != CP_MACCP) && (cp != CP_THREAD_ACP) && (cp != CP_SYMBOL) && (cp != CP_UTF7) && (cp != CP_UTF8) && !GetCPInfo(cp,&CPInfo)) + cp=CP_ACP; +#ifdef DEBUG_DECODECODEPAGE + DebugLog(DecodeFile,"<CodePage #>%d</CodePage #>",cp); +#endif + + for (Index=0;Index<sizeof(CodePagesZeroFlags)/sizeof(CodePagesZeroFlags[0]);Index++) + if (CodePagesZeroFlags[Index]==cp) + { + Index=-1; + break; + } + if (Index==-1) + streamlen=MultiByteToWideChar(cp,0,stream,-1,nullptr,0); + else + streamlen=MultiByteToWideChar(cp,MB_USEGLYPHCHARS,stream,-1,nullptr,0); + + if (*out != nullptr) + outlen=mir_wstrlen(*out); + else + outlen=0; + temp=new WCHAR[streamlen+outlen+1]; + + if (*out != nullptr) + { + for (dest=temp;*src != (WCHAR)0;src++,dest++) //copy old string from *out to temp + *dest=*src; +// *dest++=L' '; //add space? + delete[] *out; + } + else + dest=temp; + *out=temp; + + if (Index==-1) + { + if (!MultiByteToWideChar(cp,0,stream,-1,dest,streamlen)) + return 0; + } + else + { + if (!MultiByteToWideChar(cp,MB_USEGLYPHCHARS,stream,-1,dest,streamlen)) + return 0; + } + return 1; +} + +void ConvertCodedStringToUnicode(char *stream,WCHAR **storeto,DWORD cp,int mode) +{ + char *start=stream,*finder,*finderend; + char Encoding=0; + + if (stream==nullptr) + return; + + while(WS(start)) start++; + WCHAR *tempstore=nullptr; + if (!ConvertStringToUnicode(stream,cp,&tempstore))return; + + size_t tempstoreLength = mir_wstrlen(tempstore); + + size_t outind = 0; + while(*start != 0) { + if (CODES(start)) { + finder=start+2;finderend=finder; + while(!CODED(finderend) && !EOS(finderend)) finderend++; + start = finderend; + if (CODED(finderend)) + { + Encoding=*(finderend+1); + switch(Encoding) + { + case 'b': + case 'B': + case 'q': + case 'Q': + break; + default: + goto NotEncoded; + } + if (-1==(cp=(DWORD)GetCharsetFromString(finder,finderend-finder))) + cp=CP_ACP; + if (Encoding != 0) + { + int size = 0,codeend; + char *pcodeend = nullptr; + + finder=finderend+2; + if (CODED(finder)) + finder++; + while(WS(finder)) finder++; + finderend=finder; + while(!CODEE(finderend) && !EOS(finderend)) finderend++; + if (codeend=CODEE(finderend)) + pcodeend=finderend; + while(WS(finderend-1)) finderend--; + if ((mode==MIME_MAIL) && (((*finder=='"') && (*(finderend-1)=='"')))) + { + finder++; + finderend--; + } + char *oneWordEncoded = new char[finderend-finder+1]; + strncpy(oneWordEncoded,finder,finderend-finder); + oneWordEncoded[finderend-finder]=0; + switch(Encoding) + { + case 'b': + case 'B': + size=(finderend-finder)*3/4+3+1+1; + break; + case 'q': + case 'Q': + size=finderend-finder+1+1; + break; + } + + char *DecodedResult = new char[size+1]; + switch(Encoding) + { + case 'q': + case 'Q': + DecodeQuotedPrintable(oneWordEncoded,DecodedResult,size, TRUE); + break; + case 'b': + case 'B': + DecodeBase64(oneWordEncoded,DecodedResult,size); + break; + } + delete[] oneWordEncoded; + if (codeend) + finderend=pcodeend+2; + if (WS(finderend)) //if string continues and there's some whitespace, add space to string that is to be converted + { + size_t len=mir_strlen(DecodedResult); + DecodedResult[len]=' '; + DecodedResult[len+1]=0; + finderend++; + } + WCHAR *oneWord=nullptr; + if (ConvertStringToUnicode(DecodedResult,cp,&oneWord)) { + size_t len = mir_wstrlen(oneWord); + memcpy(&tempstore[outind],oneWord,len*sizeof(WCHAR)); + outind += len; + } + delete oneWord; + oneWord = nullptr; + delete[] DecodedResult; + start = finderend; + } else if (!EOS(start)) start++; + } else if (!EOS(start)) start++; + }else{ +NotEncoded: + tempstore[outind] = tempstore[start-stream]; + outind++; + if (outind > tempstoreLength) break; + start++; + } + } + tempstore[outind] = 0; + *storeto = tempstore; +} diff --git a/protocols/YAMN/src/mails/decode.h b/protocols/YAMN/src/mails/decode.h new file mode 100644 index 0000000000..cc643594b2 --- /dev/null +++ b/protocols/YAMN/src/mails/decode.h @@ -0,0 +1,23 @@ +#ifndef __DECODE_H +#define __DECODE_H + +#define DOTLINE(s) ((((s)[-2]=='\r') || ((s)[-2]=='\n')) && ((s)[-1]=='.') && (((s)[0]=='\r') || ((s)[0]=='\n') || ((s)[0]=='\0'))) // be careful, it's different to ESR's pop3.c ;-) +#define ENDLINE(s) (((s)[0]=='\r') || ((s)[0]=='\n')) //endline +#define WS(s) (((s)[0]==' ') || ((s)[0]=='\t')) //whitespace +#define ENDLINEWS(s) ((((s)[0]=='\r') || ((s)[0]=='\n')) && (((((s)[1]=='\r') || ((s)[1]=='\n')) && (((s)[2]==' ') || ((s)[2]=='\t'))) || (((s)[1]==' ') || ((s)[1]=='\t')))) //endline+whitespace: enters(CR or LF and their combinations) followed by space or tab +#define EOS(s) ((s)[0]==0) //end of string (stream) + +#define CODES(s) ((s[0]=='=') && (s[1]=='?')) //start of coded string +#define CODEE(s) ((s[0]=='?') && (s[1]=='=')) //end of coded string +#define CODED(s) (s[0]=='?') //code delimiter + +#define MIME_PLAIN 1 +#define MIME_MAIL 2 + +struct cptable +{ + char *name; + unsigned int ID; +}; + +#endif diff --git a/protocols/YAMN/src/mails/mails.cpp b/protocols/YAMN/src/mails/mails.cpp new file mode 100644 index 0000000000..ee570fa8e2 --- /dev/null +++ b/protocols/YAMN/src/mails/mails.cpp @@ -0,0 +1,494 @@ +/* + * This code implements retrieving info from MIME header + * + * (c) majvan 2002-2004 + */ + +#include "../stdafx.h" + +//-------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------- + +// SMALL INTRO +// Mails are queued in a queue (chained list). Pointer to first mail is pointed from Account structure +// member called Mails. +// Mail queue is ended with NULL- pointered mail (NULL handle) + +//Creates new mail for plugin (calling plugin's constructor, when plugin imported to YAMN) +INT_PTR CreateAccountMailSvc(WPARAM wParam,LPARAM lParam); + +//Deletes mail for plugin (calling plugin's destructor, when plugin imported to YAMN) +INT_PTR DeleteAccountMailSvc(WPARAM wParam,LPARAM lParam); + +//Loads mail data from standard storage to memory +INT_PTR LoadMailDataSvc(WPARAM wParam,LPARAM lParam); + +//Deletes mail data from memory +INT_PTR UnloadMailDataSvc(WPARAM wParam,LPARAM); + +//Saves mail data from memory to standard storage +INT_PTR SaveMailDataSvc(WPARAM wParam,LPARAM lParam); + +//Appends second MIME mail queue to the first one +//Only finds the end of first queue and its Next memember repoints to second one +void WINAPI AppendQueueFcn(HYAMNMAIL first,HYAMNMAIL second); + +//Synchronizes two accounts +//Function finds, if there were some mails deleted from mailbox and deletes (depends on RemovedOld param) them from OldQueue +//Next finds, if there are new mails. Mails that are still on mailbox are deleted (depends on RemovedNew param) from NewQueue +//After this, OldQueue is pointer to mails that are on mailbox, but not new mails +//and NewQueue contains new mails in account +//New accounts can be then appended to account mails queue, but they have set the New flag +// +//Two mails equals if they have the same ID +// +// hPlugin- handle of plugin going to delete mails +// OldQueue- queue of mails that we found on mailbox last time, after function finishes queue contains all mails except new ones +// RemovedOld- queue of mails where to store removed mails from OldQueue, if NULL deletes mails from OldQueue +// NewQueue- queue of mails that we found on mailbox (all mails), after function finishes queue contains only new mails +// RemovedNew- queue of mails where to store removed mails from NewQueue, if NULL deletes mails from NewQueue +//So function works like: +//1. delete (or move to RemovedOld queue if RemovedOld is not NULL) all mails from OldQueue not found in NewQueue +//2. delete (or move to RemovedNew queue if RemovedNew is not NULL) all mails from NewQueue found in OldQueue +void WINAPI SynchroMessagesFcn(HACCOUNT Account,HYAMNMAIL *OldQueue,HYAMNMAIL *RemovedOld,HYAMNMAIL *NewQueue,HYAMNMAIL *RemovedNew); + +//Deletes messages from mail From to the end +// Account- account who owns mails +// From- first mail in queue, which is going to delete +void WINAPI DeleteMessagesToEndFcn(HACCOUNT Account,HYAMNMAIL From); + +//Removes message from queue, does not delete from memory +// From- queue pointer +// Which- mail to delete +// mode- nonzero if you want to decrement numbers in messages that are bigger than the one in Which mail, 0 if not +void WINAPI DeleteMessageFromQueueFcn(HYAMNMAIL *From,HYAMNMAIL Which,int mode); + +//Finds message in queue that has the same ID number +// From- message queue +// ID- pointer to ID +// returns pointer to found message, NULL if not found +HYAMNMAIL WINAPI FindMessageByIDFcn(HYAMNMAIL From,char *ID); + +//Translate header from text to queue of CMimeItem structures +//This means that new queue will contain all info about headers +// stream- pointer to text containing header (can be ended with zero) +// len- length of stream +// head- function fills this pointer to first header item in queue +void WINAPI TranslateHeaderFcn(char *stream,int len,struct CMimeItem **head); + +//Creates new mail queue, copying only these mails, that have set flag for deleting +// From- message queue, whose mail with given flag are duplicated +// returns new mail queue (or NULL when no mail with flag is in From queue) +//Function does not copy the whole mails, it copies only ID string. And ID is copied as string, so +//you can use this fcn only if you have your ID as pointer to char string ended with zero character +HYAMNMAIL WINAPI CreateNewDeleteQueueFcn(HYAMNMAIL From); + +//Sets/removes flags from specific mails +// From- pointer to first message +// FlagsSet- mail must have set these flags... +// FlagsNotSet- ...and must not have set these flags... +// FlagsToSetRemove- ...to set/remove these flags (see mode) +// mode- nonzero to set, else remove +void WINAPI SetRemoveFlagsInQueueFcn(HYAMNMAIL From,DWORD FlagsSet,DWORD FlagsNotSet,DWORD FlagsToSetRemove,int mode); + +struct CExportedFunctions MailExportedFcn[]= +{ + {YAMN_SYNCHROMIMEMSGSID,(void *)SynchroMessagesFcn}, + {YAMN_TRANSLATEHEADERID,(void *)TranslateHeaderFcn}, + {YAMN_APPENDQUEUEID,(void *)AppendQueueFcn}, + {YAMN_DELETEMIMEQUEUEID,(void *)DeleteMessagesToEndFcn}, + {YAMN_DELETEMIMEMESSAGEID,(void *)DeleteMessageFromQueueFcn}, + {YAMN_FINDMIMEMESSAGEID,(void *)FindMessageByIDFcn}, + {YAMN_CREATENEWDELETEQUEUEID,(void *)CreateNewDeleteQueueFcn}, + {YAMN_SETREMOVEQUEUEFLAGSID,(void *)SetRemoveFlagsInQueueFcn}, +}; + +struct CExportedServices MailExportedSvc[]= +{ + {MS_YAMN_CREATEACCOUNTMAIL,CreateAccountMailSvc}, + {MS_YAMN_DELETEACCOUNTMAIL,DeleteAccountMailSvc}, + {MS_YAMN_LOADMAILDATA,LoadMailDataSvc}, + {MS_YAMN_UNLOADMAILDATA,UnloadMailDataSvc}, + {MS_YAMN_SAVEMAILDATA,SaveMailDataSvc}, +}; + + +//-------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------- + +INT_PTR CreateAccountMailSvc(WPARAM wParam,LPARAM lParam) +{ + HACCOUNT Account=(HACCOUNT)wParam; + DWORD MailVersion=(DWORD)lParam; + HYAMNMAIL NewMail; + +//test if we are going to initialize members of suitable structure (structures of plugin and YAMN must match) + if (MailVersion != YAMN_MAILVERSION) + return NULL; + + if (Account->Plugin != nullptr) + { + if (Account->Plugin->MailFcn->NewMailFcnPtr != nullptr) + { +//Let plugin create its own structure, which can be derived from CAccount structure + if (nullptr==(NewMail=Account->Plugin->MailFcn->NewMailFcnPtr(Account,YAMN_MAILVERSION))) + return NULL; + } + else + { +//We suggest plugin uses standard CAccount structure, so we create it + if (nullptr==(NewMail=new YAMNMAIL)) +//If not created successfully + return NULL; + NewMail->MailData=nullptr; + } +//Init every members of structure, used by YAMN + return (INT_PTR)NewMail; + } + return NULL; +} + +INT_PTR DeleteAccountMailSvc(WPARAM wParam,LPARAM lParam) +{ + HYAMNPROTOPLUGIN Plugin=(HYAMNPROTOPLUGIN)wParam; + HYAMNMAIL OldMail=(HYAMNMAIL)lParam; + struct CMimeItem *TH; + + if (Plugin->MailFcn != nullptr) { + if (Plugin->MailFcn->DeleteMailFcnPtr != nullptr) { + //Let plugin delete its own CMimeMsgQueue derived structure + Plugin->MailFcn->DeleteMailFcnPtr(OldMail); + return 1; + } + } + if (OldMail->MailData != nullptr) { + if (OldMail->MailData->Body != nullptr) + delete[] OldMail->MailData->Body; + if ((TH=OldMail->MailData->TranslatedHeader) != nullptr) + for (;OldMail->MailData->TranslatedHeader != nullptr;) { + TH=TH->Next; + if (OldMail->MailData->TranslatedHeader->name != nullptr) + delete[] OldMail->MailData->TranslatedHeader->name; + if (OldMail->MailData->TranslatedHeader->value != nullptr) + delete[] OldMail->MailData->TranslatedHeader->value; + delete OldMail->MailData->TranslatedHeader; + OldMail->MailData->TranslatedHeader=TH; + } + delete OldMail->MailData; + } + if (OldMail->ID != nullptr) + delete[] OldMail->ID; + + delete OldMail; //consider mail as standard HYAMNMAIL, not initialized before and use its own destructor + return 1; +} + + +void WINAPI AppendQueueFcn(HYAMNMAIL first,HYAMNMAIL second) +{ + HYAMNMAIL Finder=first; + while(Finder->Next != nullptr) Finder=Finder->Next; + Finder->Next=second; +} + +INT_PTR LoadMailDataSvc(WPARAM wParam,LPARAM lParam) +{ + HYAMNMAIL Mail=(HYAMNMAIL)wParam; + DWORD MailVersion=(DWORD)lParam; + + if (MailVersion != YAMN_MAILDATAVERSION) + return NULL; + + // now we have all data to memory persisting, so no loading is needed + return (INT_PTR)Mail->MailData; +} + +INT_PTR UnloadMailDataSvc(WPARAM, LPARAM) +{ + return 1; +} + +INT_PTR SaveMailDataSvc(WPARAM, LPARAM lParam) +{ + DWORD MailVersion=(DWORD)lParam; + + if (MailVersion != YAMN_MAILDATAVERSION) + return (INT_PTR)-1; + + // now we have all data to memory persisting, so no saving is needed + return (INT_PTR)0; +} + +void WINAPI SynchroMessagesFcn(HACCOUNT Account,HYAMNMAIL *OldQueue,HYAMNMAIL *RemovedOld,HYAMNMAIL *NewQueue,HYAMNMAIL *RemovedNew) +//deletes messages from new queue, if they are old +//it also deletes messages from old queue, if they are not in mailbox anymore +//"YAMN_MSG_DELETED" messages in old queue remain in old queue (are never removed, although they are not in new queue) +//"YAMN_MSG_DELETED" messages in new queue remain in new queue (are never removed, although they can be in old queue) +{ + HYAMNMAIL Finder,FinderPrev; + HYAMNMAIL Parser,ParserPrev; + HYAMNMAIL RemovedOldParser =nullptr; + HYAMNMAIL RemovedNewParser =nullptr; + if (RemovedOld != nullptr) *RemovedOld=nullptr; + if (RemovedNew != nullptr) *RemovedNew=nullptr; + + for (FinderPrev=nullptr,Finder=*OldQueue;Finder != nullptr;) + { + if (Finder->Flags & YAMN_MSG_DELETED) //if old queue contains deleted mail + { + FinderPrev=Finder; + Finder=Finder->Next; //get next message in old queue for testing + continue; + } + for (ParserPrev=nullptr,Parser=*NewQueue;Parser != nullptr;ParserPrev=Parser,Parser=Parser->Next) + { + if (Parser->Flags & YAMN_MSG_DELETED) + continue; + + if (Parser->ID==nullptr) //simply ignore the message, that has not filled its ID + continue; + + if (0==mir_strcmp(Parser->ID,Finder->ID)) //search for equal message in new queue + break; + } + if (Parser != nullptr) //found equal message in new queue + { + if (Parser==*NewQueue) + *NewQueue=(*NewQueue)->Next; + else + ParserPrev->Next=Parser->Next; + Finder->Number=Parser->Number; //rewrite the number of current message in old queue + + if (RemovedNew==nullptr) //delete from new queue + DeleteAccountMailSvc((WPARAM)Account->Plugin,(LPARAM)Parser); + else //or move to RemovedNew + { + if (RemovedNewParser==nullptr) //if it is first mail removed from NewQueue + *RemovedNew=Parser; //set RemovedNew queue to point to first message in removed queue + else + RemovedNewParser->Next=Parser; //else don't forget to show to next message in RemovedNew queue + RemovedNewParser=Parser; //follow RemovedNew queue + RemovedNewParser->Next=nullptr; + } + FinderPrev=Finder; + Finder=Finder->Next; //get next message in old queue for testing + } + else //a message was already deleted from mailbox + { + if (Finder==*OldQueue) //if we are at the first item in OldQueue + { + *OldQueue=(*OldQueue)->Next; //set OldQueue to next item + if (RemovedOld==nullptr) //delete from old queue + DeleteAccountMailSvc((WPARAM)Account->Plugin,(LPARAM)Finder); + else //or move to RemovedOld + { + if (RemovedOldParser==nullptr) //if it is first mail removed from OldQueue + *RemovedOld=Finder; //set RemovedOld queue to point to first message in removed queue + else + RemovedOldParser->Next=Finder; //else don't forget to show to next message in RemovedNew queue + RemovedOldParser=Finder; //follow RemovedOld queue + RemovedOldParser->Next=nullptr; + } + Finder=*OldQueue; + } + else + { + FinderPrev->Next=Finder->Next; + if (RemovedOld==nullptr) //delete from old queue + DeleteAccountMailSvc((WPARAM)Account->Plugin,(LPARAM)Finder); + else //or move to RemovedOld + { + if (RemovedOldParser==nullptr) //if it is first mail removed from OldQueue + *RemovedOld=Finder; //set RemovedOld queue to point to first message in removed queue + else + RemovedOldParser->Next=Finder; //else don't forget to show to next message in RemovedNew queue + RemovedOldParser=Finder; //follow RemovedOld queue + RemovedOldParser->Next=nullptr; + } + Finder=FinderPrev->Next; + } + } + } +} + +void WINAPI DeleteMessagesToEndFcn(HACCOUNT Account,HYAMNMAIL From) +{ + HYAMNMAIL Temp; + while(From != nullptr) + { + Temp=From; + From=From->Next; + DeleteAccountMailSvc((WPARAM)Account->Plugin,(LPARAM)Temp); + } +} + +void WINAPI DeleteMessageFromQueueFcn(HYAMNMAIL *From,HYAMNMAIL Which,int mode=0) +{ + DWORD Number=Which->Number; + HYAMNMAIL Parser; + + if (*From==Which) + { + Parser=Which->Next; + *From=Parser; + } + else + { + for (Parser=*From;Which != Parser->Next;Parser=Parser->Next) + if (mode && (Parser->Number>Number)) Parser->Number--; + if (mode && (Parser->Number>Number)) Parser->Number--; + Parser->Next=Parser->Next->Next; + Parser=Which->Next; + } + if (mode) + for (;Parser != nullptr;Parser=Parser->Next) + if (Parser->Number>Number) Parser->Number--; +} + +void DeleteMessagesFromQueue(HYAMNMAIL *From,HYAMNMAIL Which,int mode=0) +{ + HYAMNMAIL Parser; + + for (Parser=Which;Parser != nullptr;Parser=Parser->Next) + DeleteMessageFromQueueFcn(From,Parser,mode); +} + +HYAMNMAIL WINAPI FindMessageByIDFcn(HYAMNMAIL From,char *ID) +{ + HYAMNMAIL Browser; + + for (Browser=From;Browser != nullptr;Browser=Browser->Next) + if (0==mir_strcmp(Browser->ID,ID)) + break; + return Browser; +} + +void WINAPI TranslateHeaderFcn(char *stream,int len,struct CMimeItem **head) +{ + try + { + char *finder=stream; + char *prev1,*prev2,*prev3; + struct CMimeItem *Item=nullptr; + + while(finder<=(stream+len)) + { + while(ENDLINEWS(finder)) finder++; + + //at the start of line + if (DOTLINE(finder+1)) //at the end of stream + break; + + prev1=finder; + + while(*finder != ':' && !EOS(finder)) finder++; + if (!EOS(finder)) + prev2=finder++; + else + break; + + while(WS(finder) && !EOS(finder)) finder++; + if (!EOS(finder)) + prev3=finder; + else + break; + + do + { + if (ENDLINEWS(finder)) finder+=2; //after endline information continues + while(!ENDLINE(finder) && !EOS(finder)) finder++; + }while(ENDLINEWS(finder)); + + if (Item != nullptr) + { + if (nullptr==(Item->Next=new struct CMimeItem)) + break; + Item=Item->Next; + } + else + { + Item = new CMimeItem; + *head = Item; + } + + Item->Next=nullptr; + Item->name=new char [prev2-prev1+1]; + mir_strncpy(Item->name,prev1,prev2-prev1+1); + Item->value=new char [finder-prev3+1]; + mir_strncpy(Item->value,prev3,finder-prev3+1); + + if (EOS(finder)) + break; + finder++; + if (ENDLINE(finder)) { + finder++; + if (ENDLINE(finder)) { + // end of headers. message body begins + finder++; + if (ENDLINE(finder))finder++; + prev1 = finder; + while (!DOTLINE(finder+1))finder++; + if (ENDLINE(finder))finder--; + prev2 = finder; + if (prev2>prev1) { // yes, we have body + if (nullptr==(Item->Next=new struct CMimeItem)) break; // Cant create new item?! + Item=Item->Next; + Item->Next=nullptr;//just in case; + Item->name=new char[5]; strncpy(Item->name,"Body",5); + Item->value=new char [prev2-prev1]; + mir_strncpy(Item->value,prev1,prev2-prev1-1); + } + break; // there is nothing else + } + } + } + } + catch(...) + { + MessageBoxA(nullptr,"Translate header error","",0); + } +} + +HYAMNMAIL WINAPI CreateNewDeleteQueueFcn(HYAMNMAIL From) +{ + HYAMNMAIL FirstMail,Browser = nullptr; + + for (FirstMail=nullptr;From != nullptr;From=From->Next) + { + if ((From->Flags & (YAMN_MSG_USERDELETE | YAMN_MSG_AUTODELETE)) && !(From->Flags & YAMN_MSG_DELETED)) + { + if (FirstMail==nullptr) + { + FirstMail=Browser=new YAMNMAIL; + if (FirstMail==nullptr) + break; + } + else + { + Browser->Next=new YAMNMAIL; + Browser=Browser->Next; + } + Browser->ID=new char[mir_strlen(From->ID)+1]; + mir_strcpy(Browser->ID,From->ID); + Browser->Number=From->Number; + Browser->Flags=From->Flags; + } + } + return FirstMail; +} + +void WINAPI SetRemoveFlagsInQueueFcn(HYAMNMAIL From,DWORD FlagsSet,DWORD FlagsNotSet,DWORD FlagsToSetRemove,int mode) +{ + HYAMNMAIL msgq; + + for (msgq=(HYAMNMAIL)From;msgq != nullptr;msgq=msgq->Next) + { + if ((FlagsSet==(msgq->Flags & FlagsSet)) && (0==(msgq->Flags & FlagsNotSet))) + { + if (mode) + msgq->Flags=msgq->Flags | FlagsToSetRemove; + else + msgq->Flags=msgq->Flags & ~FlagsToSetRemove; + } + } +} diff --git a/protocols/YAMN/src/mails/mime.cpp b/protocols/YAMN/src/mails/mime.cpp new file mode 100644 index 0000000000..52ac1a5ff7 --- /dev/null +++ b/protocols/YAMN/src/mails/mime.cpp @@ -0,0 +1,718 @@ +/* + * This code implements retrieving info from MIME header + * + * (c) majvan 2002-2004 + */ + +#include "../stdafx.h" + +//-------------------------------------------------------------------------------------------------- + +//Copies one string to another +// srcstart- source string +// srcend- address to the end of source string +// dest- pointer that stores new allocated string that contains copy of source string +// mode- MIME_PLAIN or MIME_MAIL (MIME_MAIL deletes '"' characters (or '<' and '>') if they are at start and end of source string +void CopyToHeader(char *srcstart,char *srcend,char **dest,int mode); + +//Extracts email address (finds nick name and mail and then stores them to strings) +// finder- source string +// storeto- pointer that receives address of mail string +// storetonick- pointer that receives address of nickname +void ExtractAddressFromLine(char *finder,char **storeto,char **storetonick); + +//Extracts simple text from string +// finder- source string +// storeto- pointer that receives address of string +void ExtractStringFromLine(char *finder,char **storeto); + +//Extracts some item from content-type string +//Example: ContentType string: "TEXT/PLAIN; charset=US-ASCII", item:"charset=", returns: "US-ASCII" +// ContetType- content-type string +// value- string item +// returns extracted string (or NULL when not found) +char *ExtractFromContentType(char *ContentType,char *value); + +//Extracts info from header text into header members +//Note that this function as well as struct CShortHeadwer can be always changed, because there are many items to extract +//(e.g. the X-Priority and Importance and so on) +// items- translated header (see TranslateHeaderFcn) +// head- header to be filled with values extracted from items +void ExtractShortHeader(struct CMimeItem *items,struct CShortHeader *head); + +//Extracts header to mail using ExtractShortHeader fcn. +// items- translated header (see TranslateHeaderFcn) +// CP- codepage used when no default found +// head- header to be filled with values extracted from items, in unicode (wide char) +void ExtractHeader(struct CMimeItem *items,int &CP,struct CHeader *head); + +//Deletes items in CShortHeader structure +// head- structure whose items are deleted +void DeleteShortHeaderContent(struct CShortHeader *head); + +//Deletes list of YAMN_MIMENAMES structures +// Names- pointer to first item of list +void DeleteNames(PYAMN_MIMENAMES Names); + +//Deletes list of YAMN_MIMESHORTNAMES structures +// Names- pointer to first item of list +void DeleteShortNames(PYAMN_MIMESHORTNAMES Names); + +//Makes a string lowercase +// string- string to be lowercased +void inline ToLower(char *string); + +//-------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------- + +void CopyToHeader(char *srcstart,char *srcend,char **dest,int mode) +{ + char *dst; + + if (dest==nullptr) + return; + if (srcstart>=srcend) + return; + + if ((mode==MIME_MAIL) && (((*srcstart=='"') && (*(srcend-1)=='"')) || ((*srcstart=='<') && (*(srcend-1)=='>')))) + { + srcstart++; + srcend--; + } + + if (srcstart>=srcend) + return; + + if (nullptr != *dest) + delete[] *dest; + if (nullptr==(*dest=new char[srcend-srcstart+1])) + return; + + dst=*dest; + + for (;srcstart<srcend;dst++,srcstart++) + { + if (ENDLINE(srcstart)) + { + while(ENDLINE(srcstart) || WS(srcstart)) srcstart++; + *dst=' '; + srcstart--; //because at the end of "for loop" we increment srcstart + } + else + *dst=*srcstart; + } + *dst=0; +} + +void ExtractAddressFromLine(char *finder,char **storeto,char **storetonick) +{ + if (finder==nullptr) + { + *storeto=*storetonick=nullptr; + return; + } + while(WS(finder)) finder++; + if ((*finder) != '<') + { + char *finderend=finder+1; + do + { + if (ENDLINEWS(finderend)) //after endline information continues + finderend+=2; + while(!ENDLINE(finderend) && !EOS(finderend)) finderend++; //seek to the end of line or to the end of string + }while(ENDLINEWS(finderend)); + finderend--; + while(WS(finderend) || ENDLINE(finderend)) finderend--; //find the end of text, no whitespace + if (*finderend != '>') //not '>' at the end of line + CopyToHeader(finder,finderend+1,storeto,MIME_MAIL); + else //at the end of line, there's '>' + { + char *finder2=finderend; + while((*finder2 != '<') && (finder2>finder)) finder2--; //go to matching '<' or to the start + CopyToHeader(finder2,finderend+1,storeto,MIME_MAIL); + if (*finder2=='<') //if we found '<', the rest copy as from nick + { + finder2--; + while(WS(finder2) || ENDLINE(finder2)) finder2--; //parse whitespace + CopyToHeader(finder,finder2+1,storetonick,MIME_MAIL); //and store nickname + } + } + } + else + { + char *finderend=finder+1; + do + { + if (ENDLINEWS(finderend)) //after endline information continues + finderend+=2; + while(!ENDLINE(finderend) && (*finderend != '>') && !EOS(finderend)) finderend++; //seek to the matching < or to the end of line or to the end of string + }while(ENDLINEWS(finderend)); + CopyToHeader(finder,finderend+1,storeto,MIME_MAIL); //go to first '>' or to the end and copy + finder=finderend+1; + while(WS(finder)) finder++; //parse whitespace + if (!ENDLINE(finder) && !EOS(finder)) //if there are chars yet, it's nick + { + finderend=finder+1; + while(!ENDLINE(finderend) && !EOS(finderend)) finderend++; //seek to the end of line or to the end of string + finderend--; + while(WS(finderend)) finderend--; //find the end of line, no whitespace + CopyToHeader(finder,finderend+1,storetonick,MIME_MAIL); + } + } +} + +void ExtractStringFromLine(char *finder,char **storeto) +{ + if (finder==nullptr) + { + *storeto=nullptr; + return; + } + while(WS(finder)) finder++; + char *finderend=finder; + + do + { + if (ENDLINEWS(finderend)) finderend++; //after endline information continues + while(!ENDLINE(finderend) && !EOS(finderend)) finderend++; + }while(ENDLINEWS(finderend)); + finderend--; + while(WS(finderend)) finderend--; //find the end of line, no whitespace + CopyToHeader(finder,finderend+1,storeto,MIME_PLAIN); +} + +char *ExtractFromContentType(char *ContentType,char *value) +{ + char *lowered = _strdup(ContentType); + ToLower(lowered); + char *finder=strstr(lowered,value); + if (finder==nullptr) { + free (lowered); + return nullptr; + } + finder = finder-lowered+ContentType; + free (lowered); + + char *temp,*copier; + char *CopiedString; + + temp=finder-1; + while((temp>ContentType) && WS(temp)) temp--; //now we have to find, if the word "Charset=" is located after ';' like "; Charset=" + if (*temp != ';' && !ENDLINE(temp) && temp != ContentType) + return nullptr; + finder=finder+mir_strlen(value); //jump over value string + + while(WS(finder)) finder++; //jump over whitespaces + temp=finder; + while(*temp != 0 && *temp != ';') temp++; //jump to the end of setting (to the next ;) + temp--; + while(WS(temp)) temp--; //remove whitespaces from the end + if (*finder=='\"') { //remove heading and tailing quotes + finder++; + if (*temp=='\"') temp--; + } + if (nullptr==(CopiedString=new char[++temp-finder+1])) + return nullptr; + for (copier=CopiedString;finder != temp;*copier++=*finder++); //copy string + *copier=0; //and end it with zero character + + return CopiedString; +} + +void ExtractShortHeader(struct CMimeItem *items,struct CShortHeader *head) +{ + for (;items != nullptr;items=items->Next) + { + //at the start of line + //MessageBox(NULL,items->value,items->name,0); + if (0==_strnicmp(items->name,"From",4)) + { + if (items->value==nullptr) + continue; + #ifdef DEBUG_DECODE + DebugLog(DecodeFile,"<Extracting from>"); + #endif + ExtractAddressFromLine(items->value,&head->From,&head->FromNick); + #ifdef DEBUG_DECODE + DebugLog(DecodeFile,"</Extracting>\n"); + #endif + } + else if (0==_strnicmp(items->name,"Return-Path",11)) + { + if (items->value==nullptr) + continue; + #ifdef DEBUG_DECODE + DebugLog(DecodeFile,"<Extracting return-path>"); + #endif + ExtractAddressFromLine(items->value,&head->ReturnPath,&head->ReturnPathNick); + #ifdef DEBUG_DECODE + DebugLog(DecodeFile,"</Extracting>\n"); + #endif + } + else if (0==_strnicmp(items->name,"Subject",7)) + { + if (items->value==nullptr) + continue; + #ifdef DEBUG_DECODE + DebugLog(DecodeFile,"<Extracting subject>"); + #endif + ExtractStringFromLine(items->value,&head->Subject); + #ifdef DEBUG_DECODE + DebugLog(DecodeFile,"</Extracting>\n"); + #endif + } + else if (0==_strnicmp(items->name,"Body",4)) + { + if (items->value==nullptr) + continue; + #ifdef DEBUG_DECODE + DebugLog(DecodeFile,"<Extracting body>"); + #endif + ExtractStringFromLine(items->value,&head->Body); + #ifdef DEBUG_DECODE + DebugLog(DecodeFile,"</Extracting>\n"); + #endif + } + else if (0==_strnicmp(items->name,"Date",4)) + { + if (items->value==nullptr) + continue; + #ifdef DEBUG_DECODE + DebugLog(DecodeFile,"<Extracting date>"); + #endif + ExtractStringFromLine(items->value,&head->Date); + #ifdef DEBUG_DECODE + DebugLog(DecodeFile,"</Extracting>\n"); + #endif + } + else if (0==_strnicmp(items->name,"Content-Type",12)) + { + if (items->value==nullptr) + continue; + + char *ContentType=nullptr,*CharSetStr; + #ifdef DEBUG_DECODE + DebugLog(DecodeFile,"<Extracting Content-Type>"); + #endif + ExtractStringFromLine(items->value,&ContentType); + #ifdef DEBUG_DECODE + DebugLog(DecodeFile,"</Extracting>\n"); + #endif + ToLower(ContentType); + if (nullptr != (CharSetStr=ExtractFromContentType(ContentType,"charset="))) + { + head->CP=GetCharsetFromString(CharSetStr,mir_strlen(CharSetStr)); + delete[] CharSetStr; + } + delete[] ContentType; + } + else if (0==_strnicmp(items->name,"Importance",10)) + { + if (items->value==nullptr) + continue; + #ifdef DEBUG_DECODE + DebugLog(DecodeFile,"<Extracting importance>"); + #endif + if (head->Priority != -1) + { + if (0==strncmp(items->value,"low",3)) + head->Priority=5; + else if (0==strncmp(items->value,"normal",6)) + head->Priority=3; + else if (0==strncmp(items->value,"high",4)) + head->Priority=1; + } + #ifdef DEBUG_DECODE + DebugLog(DecodeFile,"</Extracting>\n"); + #endif + } + else if (0==_strnicmp(items->name,"X-Priority",10)) + { + if (items->value==nullptr) + continue; + #ifdef DEBUG_DECODE + DebugLog(DecodeFile,"<X-Priority>"); + #endif + if ((*items->value>='1') && (*items->value<='5')) + head->Priority=*items->value-'0'; + #ifdef DEBUG_DECODE + DebugLog(DecodeFile,"</Extracting>\n"); + #endif + } + + } +} + +void ExtractHeader(struct CMimeItem *items,int &CP,struct CHeader *head) +{ + struct CShortHeader ShortHeader; + + memset(&ShortHeader, 0, sizeof(struct CShortHeader)); + ShortHeader.Priority=ShortHeader.CP=-1; + #ifdef DEBUG_DECODE + DebugLog(DecodeFile,"<Extracting header>\n"); + #endif + ExtractShortHeader(items,&ShortHeader); + + head->Priority=ShortHeader.Priority==-1 ? 3 : ShortHeader.Priority; + CP=ShortHeader.CP==-1 ? CP : ShortHeader.CP; + #ifdef DEBUG_DECODE + if (NULL != ShortHeader.From) + DebugLog(DecodeFile,"<Decoded from>%s</Decoded)\n",ShortHeader.From); + if (NULL != ShortHeader.FromNick) + DebugLog(DecodeFile,"<Decoded from-nick>%s</Decoded)\n",ShortHeader.FromNick); + if (NULL != ShortHeader.ReturnPath) + DebugLog(DecodeFile,"<Decoded return-path>%s</Decoded)\n",ShortHeader.ReturnPath); + if (NULL != ShortHeader.ReturnPathNick) + DebugLog(DecodeFile,"<Decoded return-path nick>%s</Decoded)\n",ShortHeader.ReturnPathNick); + if (NULL != ShortHeader.Subject) + DebugLog(DecodeFile,"<Decoded subject>%s</Decoded)\n",ShortHeader.Subject); + if (NULL != ShortHeader.Date) + DebugLog(DecodeFile,"<Decoded date>%s</Decoded)\n",ShortHeader.Date); + DebugLog(DecodeFile,"</Extracting header>\n"); + DebugLog(DecodeFile,"<Convert>\n"); + #endif + + ConvertCodedStringToUnicode(ShortHeader.From,&head->From,CP,MIME_PLAIN); + + #ifdef DEBUG_DECODE + if (NULL != head->From) + DebugLogW(DecodeFile,L"<Converted from>%s</Converted>\n",head->From); + #endif + ConvertCodedStringToUnicode(ShortHeader.FromNick,&head->FromNick,CP,MIME_MAIL); + #ifdef DEBUG_DECODE + if (NULL != head->FromNick) + DebugLogW(DecodeFile,L"<Converted from-nick>%s</Converted>\n",head->FromNick); + #endif + ConvertCodedStringToUnicode(ShortHeader.ReturnPath,&head->ReturnPath,CP,MIME_PLAIN); + #ifdef DEBUG_DECODE + if (NULL != head->ReturnPath) + DebugLogW(DecodeFile,L"<Converted return-path>%s</Converted>\n",head->ReturnPath); + #endif + ConvertCodedStringToUnicode(ShortHeader.ReturnPathNick,&head->ReturnPathNick,CP,MIME_MAIL); + #ifdef DEBUG_DECODE + if (NULL != head->ReturnPathNick) + DebugLogW(DecodeFile,L"<Converted return-path nick>%s</Converted>\n",head->ReturnPathNick); + #endif + ConvertCodedStringToUnicode(ShortHeader.Subject,&head->Subject,CP,MIME_PLAIN); + #ifdef DEBUG_DECODE + if (NULL != head->Subject) + DebugLogW(DecodeFile,L"<Converted subject>%s</Converted>\n",head->Subject); + #endif + ConvertCodedStringToUnicode(ShortHeader.Date,&head->Date,CP,MIME_PLAIN); + #ifdef DEBUG_DECODE + if (NULL != head->Date) + DebugLogW(DecodeFile,L"<Converted date>%s</Converted>\n",head->Date); + #endif + + ConvertCodedStringToUnicode(ShortHeader.Body,&head->Body,CP,MIME_PLAIN); + #ifdef DEBUG_DECODE + if (NULL != head->Body) + DebugLogW(DecodeFile,L"<Converted Body>%s</Converted>\n",head->Body); + #endif + + #ifdef DEBUG_DECODE + DebugLog(DecodeFile,"</Convert>\n"); + #endif + + DeleteShortHeaderContent(&ShortHeader); + +// head->From=L"Frommmm"; +// head->Subject=L"Subject"; + return; +} + +void DeleteShortHeaderContent(struct CShortHeader *head) +{ + if (head->From != nullptr) delete[] head->From; + if (head->FromNick != nullptr) delete[] head->FromNick; + if (head->ReturnPath != nullptr) delete[] head->ReturnPath; + if (head->ReturnPathNick != nullptr) delete[] head->ReturnPathNick; + if (head->Subject != nullptr) delete[] head->Subject; + if (head->Date != nullptr) delete[] head->Date; + if (head->To != nullptr) DeleteShortNames(head->To); + if (head->Cc != nullptr) DeleteShortNames(head->Cc); + if (head->Bcc != nullptr) DeleteShortNames(head->Bcc); + if (head->Body != nullptr) delete[] head->Body; +} + +void DeleteHeaderContent(struct CHeader *head) +{ + if (head->From != nullptr) delete[] head->From; + if (head->FromNick != nullptr) delete[] head->FromNick; + if (head->ReturnPath != nullptr) delete[] head->ReturnPath; + if (head->ReturnPathNick != nullptr) delete[] head->ReturnPathNick; + if (head->Subject != nullptr) delete[] head->Subject; + if (head->Date != nullptr) delete[] head->Date; + if (head->Body != nullptr) delete[] head->Body; + if (head->To != nullptr) DeleteNames(head->To); + if (head->Cc != nullptr) DeleteNames(head->Cc); + if (head->Bcc != nullptr) DeleteNames(head->Bcc); +} + +void DeleteNames(PYAMN_MIMENAMES Names) +{ + PYAMN_MIMENAMES Parser=Names,Old; + for (;Parser != nullptr;Parser=Parser->Next) + { + if (Parser->Value != nullptr) + delete[] Parser->Value; + if (Parser->ValueNick != nullptr) + delete[] Parser->ValueNick; + Old=Parser; + Parser=Parser->Next; + delete Old; + } +} + +void DeleteShortNames(PYAMN_MIMESHORTNAMES Names) +{ + PYAMN_MIMESHORTNAMES Parser=Names,Old; + for (;Parser != nullptr;Parser=Parser->Next) + { + if (Parser->Value != nullptr) + delete[] Parser->Value; + if (Parser->ValueNick != nullptr) + delete[] Parser->ValueNick; + Old=Parser; + Parser=Parser->Next; + delete Old; + } +} + + +void inline ToLower(char *string) +{ + for (;*string != 0;string++) + if (*string>='A' && *string<='Z') *string=*string-'A'+'a'; +} + +#define TE_UNKNOWN +#define TE_QUOTEDPRINTABLE 1 +#define TE_BASE64 2 +struct APartDataType +{ + char *Src;//Input + char *ContType; + int CodePage; + char *TransEnc; + BYTE TransEncType; //TE_something + char *body; + int bodyLen; + WCHAR *wBody; +}; + + +void ParseAPart(APartDataType *data) +{ + size_t len = mir_strlen(data->Src); + try + { + char *finder=data->Src; + char *prev1,*prev2,*prev3; + + while(finder<=(data->Src+len)) + { + while(ENDLINEWS(finder)) finder++; + + //at the start of line + if (finder>data->Src) { + if (*(finder-2)=='\r' || *(finder-2)=='\n') + *(finder-2)=0; + if (*(finder-1)=='\r' || *(finder-1)=='\n') + *(finder-1)=0; + } + prev1=finder; + + while(*finder != ':' && !EOS(finder) && !ENDLINE(finder)) finder++; + if (ENDLINE(finder)||EOS(finder)) { + // no ":" in the line? here the body begins; + data->body = prev1; + break; + } + prev2=finder++; + + while(WS(finder) && !EOS(finder)) finder++; + if (!EOS(finder)) + prev3=finder; + else + break; + + do + { + if (ENDLINEWS(finder)) finder+=2; //after endline information continues + while(!ENDLINE(finder) && !EOS(finder)) finder++; + }while(ENDLINEWS(finder)); + + if (!_strnicmp(prev1,"Content-type",prev2-prev1)) { + data->ContType = prev3; + } else if (!_strnicmp(prev1,"Content-Transfer-Encoding",prev2-prev1)) { + data->TransEnc = prev3; + } + + if (EOS(finder)) + break; + finder++; + if (ENDLINE(finder)) { + finder++; + if (ENDLINE(finder)) { + // end of headers. message body begins + if (finder>data->Src) { + if (*(finder-2)=='\r' || *(finder-2)=='\n') + *(finder-2)=0; + if (*(finder-1)=='\r' || *(finder-1)=='\n') + *(finder-1)=0; + } + finder++; + if (ENDLINE(finder))finder++; + prev1 = finder; + while (!EOS(finder+1))finder++; + if (ENDLINE(finder))finder--; + prev2 = finder; + if (prev2>prev1) { // yes, we have body + data->body = prev1; + } + break; // there is nothing else + } + } + } + } + catch(...) + { + MessageBox(nullptr, TranslateT("Translate header error"), L"", 0); + } + if (data->body) data->bodyLen = (int)mir_strlen(data->body); +} + +//from decode.cpp +int DecodeQuotedPrintable(char *Src,char *Dst,int DstLen, BOOL isQ); +int DecodeBase64(char *Src,char *Dst,int DstLen); +int ConvertStringToUnicode(char *stream,unsigned int cp,WCHAR **out); + +WCHAR *ParseMultipartBody(char *src, char *bond) +{ + char *srcback = _strdup(src); + size_t sizebond = mir_strlen(bond); + int numparts = 1; + int i; + char *courbond = srcback; + WCHAR *dest; + for (;(courbond=strstr(courbond,bond));numparts++,courbond+=sizebond); + APartDataType *partData = new APartDataType[numparts]; + memset(partData, 0, sizeof(APartDataType)*numparts); + partData[0].Src = courbond = srcback; + for (i=1;(courbond=strstr(courbond,bond));i++,courbond+=sizebond) { + *(courbond-2) = 0; + partData[i].Src = courbond+sizebond; + while (ENDLINE(partData[i].Src)) partData[i].Src++; + } + size_t resultSize=0; + for (i=0;i<numparts;i++) { + ParseAPart(&partData[i]); + if (partData[i].body) { + if (partData[i].TransEnc) { + if (!_stricmp(partData[i].TransEnc,"base64")) partData[i].TransEncType=TE_BASE64; + else if (!_stricmp(partData[i].TransEnc,"quoted-printable"))partData[i].TransEncType=TE_QUOTEDPRINTABLE; + } + if (partData[i].ContType) { + char *CharSetStr; + if (nullptr != (CharSetStr=ExtractFromContentType(partData[i].ContType,"charset="))) + { + partData[i].CodePage=GetCharsetFromString(CharSetStr,mir_strlen(CharSetStr)); + delete[] CharSetStr; + } + } + if (partData[i].ContType && !_strnicmp(partData[i].ContType,"text",4)) { + char *localBody=nullptr; + switch (partData[i].TransEncType) { + case TE_BASE64: + { + int size =partData[i].bodyLen*3/4+5; + localBody = new char[size+1]; + DecodeBase64(partData[i].body,localBody,size); + }break; + case TE_QUOTEDPRINTABLE: + { + int size = partData[i].bodyLen+2; + localBody = new char[size+1]; + DecodeQuotedPrintable(partData[i].body,localBody,size,FALSE); + }break; + } + ConvertStringToUnicode(localBody?localBody:partData[i].body,partData[i].CodePage,&partData[i].wBody); + if (localBody) delete[] localBody; + } else if (partData[i].ContType && !_strnicmp(partData[i].ContType,"multipart/",10)) { + //Multipart in mulitipart recursive? should be SPAM. Ah well + char *bondary=nullptr; + if (nullptr != (bondary=ExtractFromContentType(partData[i].ContType,"boundary="))) + { + partData[i].wBody = ParseMultipartBody(partData[i].body,bondary); + delete[] bondary; + } else goto FailBackRaw; //multipart with no boundary? badly formatted messages. + } else { +FailBackRaw: + ConvertStringToUnicode(partData[i].body,partData[i].CodePage,&partData[i].wBody); + } + resultSize += mir_wstrlen(partData[i].wBody); + }// if (partData[i].body) + resultSize += 100+4+3; //cr+nl+100+ 3*bullet + } + dest = new WCHAR[resultSize+1]; + size_t destpos = 0; + for (i=0;i<numparts;i++) { + if (i) { // part before first boudary should not have headers + char infoline[1024]; size_t linesize = 0; + mir_snprintf(infoline, "%s %d", Translate("Part"), i); + linesize = mir_strlen(infoline); + if (partData[i].TransEnc) { + mir_snprintf(infoline + linesize, _countof(infoline) - linesize, "; %s", partData[i].TransEnc); + linesize = mir_strlen(infoline); + } + if (partData[i].ContType) { + char *CharSetStr=strchr(partData[i].ContType,';'); + if (CharSetStr) { + CharSetStr[0]=0; + mir_snprintf(infoline + linesize, _countof(infoline) - linesize, "; %s", partData[i].ContType); + linesize = mir_strlen(infoline); + partData[i].ContType=CharSetStr+1; + if (nullptr != (CharSetStr=ExtractFromContentType(partData[i].ContType,"charset="))) { + mir_snprintf(infoline + linesize, _countof(infoline) - linesize, "; %s", CharSetStr); + linesize = mir_strlen(infoline); + delete[] CharSetStr; + } + if (nullptr != (CharSetStr=ExtractFromContentType(partData[i].ContType,"name="))) { + mir_snprintf(infoline + linesize, _countof(infoline) - linesize, "; \"%s\"", CharSetStr); + linesize = mir_strlen(infoline); + delete[] CharSetStr; + } + } + else { + mir_snprintf(infoline + linesize, _countof(infoline) - linesize, "; %s", partData[i].ContType); + linesize = mir_strlen(infoline); + } + } + mir_snprintf(infoline + linesize, _countof(infoline) - linesize, ".\r\n"); + { + WCHAR *temp=nullptr; + dest[destpos] = dest[destpos+1] = dest[destpos+2] = 0x2022; // bullet; + destpos += 3; + ConvertStringToUnicode(infoline,CP_ACP,&temp); + size_t wsize = mir_wstrlen(temp); + mir_wstrcpy(&dest[destpos],temp); + destpos += wsize; + delete[] temp; + } + } // if (i) + + if (partData[i].wBody) { + size_t wsize = mir_wstrlen(partData[i].wBody); + mir_wstrcpy(&dest[destpos],partData[i].wBody); + destpos += wsize; + delete[] partData[i].wBody; + } + } + + free (srcback); + delete[] partData; + dest[resultSize] = 0;//just in case + return dest; +} diff --git a/protocols/YAMN/src/main.cpp b/protocols/YAMN/src/main.cpp new file mode 100644 index 0000000000..75a7ea1896 --- /dev/null +++ b/protocols/YAMN/src/main.cpp @@ -0,0 +1,353 @@ +/* + * YAMN plugin main file + * Miranda homepage: http://miranda-icq.sourceforge.net/ + * YAMN homepage: http://www.majvan.host.sk/Projekty/YAMN + * + * initializes all variables for further work + * + * (c) majvan 2002-2004 + */ + +#include "stdafx.h" + + //-------------------------------------------------------------------------------------------------- + +wchar_t ProfileName[MAX_PATH]; +wchar_t UserDirectory[MAX_PATH]; + +wchar_t szMirandaDir[MAX_PATH]; +wchar_t szProfileDir[MAX_PATH]; + +int YAMN_STATUS; + +BOOL UninstallPlugins; + +HANDLE hAccountFolder; + +HINSTANCE *hDllPlugins; +static int iDllPlugins = 0; + +YAMN_VARIABLES YAMNVar; + +CMPlugin g_plugin; + +HANDLE hNewMailHook; +HANDLE NoWriterEV; +HANDLE hTTButton; + +UINT SecTimer; + +HGENMENU hMenuItemMain = nullptr; +HGENMENU hMenuItemCont = nullptr; +HGENMENU hMenuItemContApp = nullptr; + +#define FIXED_TAB_SIZE 100 // default value for fixed width tabs + +static void GetProfileDirectory(wchar_t *szPath, int cbPath) +//This is copied from Miranda's sources. In 0.2.1.0 it is needed, in newer vesions of Miranda use MS_DB_GETPROFILEPATH service +{ + wchar_t tszOldPath[MAX_PATH]; + Profile_GetPathW(_countof(tszOldPath), tszOldPath); + mir_wstrcat(tszOldPath, L"\\*.book"); + + VARSW ptszNewPath(L"%miranda_userdata%"); + + SHFILEOPSTRUCT file_op = { + nullptr, + FO_MOVE, + tszOldPath, + ptszNewPath, + FOF_NOERRORUI | FOF_NOCONFIRMATION | FOF_SILENT, + false, + nullptr, + L"" }; + SHFileOperation(&file_op); + + wcsncpy(szPath, ptszNewPath, cbPath); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = { MIID_PROTOCOL, MIID_LAST }; + +///////////////////////////////////////////////////////////////////////////////////////// + +PLUGININFOEX pluginInfoEx = { + sizeof(PLUGININFOEX), + __PLUGIN_NAME, + PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM), + __DESCRIPTION, + __AUTHOR, + __COPYRIGHT, + __AUTHORWEB, + UNICODE_AWARE, + // {B047A7E5-027A-4CFC-8B18-EDA8345D2790} + {0xb047a7e5, 0x27a, 0x4cfc, {0x8b, 0x18, 0xed, 0xa8, 0x34, 0x5d, 0x27, 0x90}} +}; + +CMPlugin::CMPlugin() : + PLUGIN<CMPlugin>(YAMN_DBMODULE, pluginInfoEx) +{ + RegisterProtocol(PROTOTYPE_VIRTUAL); + SetUniqueId("Id"); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// The callback function + +BOOL CALLBACK EnumSystemCodePagesProc(LPTSTR cpStr) +{ + // Convert code page string to number + UINT cp = _wtoi(cpStr); + if (!IsValidCodePage(cp)) + return TRUE; + + // Get Code Page name + CPINFOEX info; + if (GetCPInfoEx(cp, 0, &info)) { + for (int i = 1; i < CPLENALL; i++) if (CodePageNamesAll[i].CP == cp) { + CodePageNamesAll[i].isValid = TRUE; + CPLENSUPP++; + break; + } + } + return TRUE; +} + +void CheckMenuItems() +{ + Menu_ShowItem(hMenuItemMain, g_plugin.getByte(YAMN_SHOWMAINMENU, 1) != 0); +} + +int SystemModulesLoaded(WPARAM, LPARAM) +{ + //Insert "Check mail (YAMN)" item to Miranda's menu + CMenuItem mi(&g_plugin); + + SET_UID(mi, 0xa01ff3d9, 0x53cb, 0x4406, 0x85, 0xd9, 0xf1, 0x90, 0x3a, 0x94, 0xed, 0xf4); + mi.position = 0xb0000000; + mi.hIcolibItem = g_GetIconHandle(0); + mi.name.a = LPGEN("Check &mail (All Account)"); + mi.pszService = MS_YAMN_FORCECHECK; + hMenuItemMain = Menu_AddMainMenuItem(&mi); + + SET_UID(mi, 0xfe22191f, 0x40c8, 0x479f, 0x93, 0x5d, 0xa5, 0x17, 0x1f, 0x57, 0x2f, 0xcb); + mi.name.a = LPGEN("Check &mail (This Account)"); + mi.pszService = MS_YAMN_CLISTCONTEXT; + hMenuItemCont = Menu_AddContactMenuItem(&mi, YAMN_DBMODULE); + + SET_UID(mi, 0x147c7800, 0x12d0, 0x4209, 0xab, 0xcc, 0xfa, 0x64, 0xc6, 0xb0, 0xa6, 0xeb); + mi.hIcolibItem = g_GetIconHandle(1); + mi.name.a = LPGEN("Launch application"); + mi.pszService = MS_YAMN_CLISTCONTEXTAPP; + hMenuItemContApp = Menu_AddContactMenuItem(&mi, YAMN_DBMODULE); + + CheckMenuItems(); + + if (hAccountFolder = FoldersRegisterCustomPathT(LPGEN("YAMN"), LPGEN("YAMN Account Folder"), UserDirectory)) + FoldersGetCustomPathT(hAccountFolder, UserDirectory, MAX_PATH, UserDirectory); + + RegisterPOP3Plugin(0, 0); + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static IconItem iconList[] = +{ + { LPGEN("Check mail"), "YAMN_Check", IDI_CHECKMAIL }, + { LPGEN("Launch application"), "YAMN_Launch", IDI_LAUNCHAPP }, + { LPGEN("New Mail"), "YAMN_NewMail", IDI_NEWMAIL }, + { LPGEN("Connect Fail"), "YAMN_ConnectFail", IDI_BADCONNECT }, +}; + +void LoadIcons() +{ + g_plugin.registerIcon("YAMN", iconList); +} + +HANDLE WINAPI g_GetIconHandle(int idx) +{ + if (idx >= _countof(iconList)) + return nullptr; + return iconList[idx].hIcolib; +} + +HICON WINAPI g_LoadIconEx(int idx, bool big) +{ + if (idx >= _countof(iconList)) + return nullptr; + return IcoLib_GetIcon(iconList[idx].szName, big); +} + +static void LoadPlugins() +{ + wchar_t szSearchPath[MAX_PATH]; + mir_snwprintf(szSearchPath, L"%s\\Plugins\\YAMN\\*.dll", szMirandaDir); + + hDllPlugins = nullptr; + + WIN32_FIND_DATA fd; + HANDLE hFind = FindFirstFile(szSearchPath, &fd); + if (hFind != INVALID_HANDLE_VALUE) { + do { + //rewritten from Miranda sources... Needed because Win32 API has a bug in FindFirstFile, search is done for *.dlllllll... too + wchar_t *dot = wcsrchr(fd.cFileName, '.'); + if (dot == nullptr) + continue; + + // we have a dot + int len = (int)mir_wstrlen(fd.cFileName); // find the length of the string + wchar_t* end = fd.cFileName + len; // get a pointer to the NULL + int safe = (end - dot) - 1; // figure out how many chars after the dot are "safe", not including NULL + + if ((safe != 3) || (mir_wstrcmpi(dot + 1, L"dll") != 0)) //not bound, however the "dll" string should mean only 3 chars are compared + continue; + + wchar_t szPluginPath[MAX_PATH]; + mir_snwprintf(szPluginPath, L"%s\\Plugins\\YAMN\\%s", szMirandaDir, fd.cFileName); + HINSTANCE hDll = LoadLibrary(szPluginPath); + if (hDll == nullptr) + continue; + + LOADFILTERFCN LoadFilter = (LOADFILTERFCN)GetProcAddress(hDll, "LoadFilter"); + if (nullptr == LoadFilter) { + FreeLibrary(hDll); + hDll = nullptr; + continue; + } + + if (!LoadFilter(GetFcnPtrSvc)) { + FreeLibrary(hDll); + hDll = nullptr; + } + + if (hDll != nullptr) { + hDllPlugins = (HINSTANCE *)realloc(hDllPlugins, (iDllPlugins + 1) * sizeof(HINSTANCE)); + hDllPlugins[iDllPlugins++] = hDll; + } + } while (FindNextFile(hFind, &fd)); + + FindClose(hFind); + } +} + +int CMPlugin::Load() +{ + YAMN_STATUS = ID_STATUS_OFFLINE; + + // we get the Miranda Root Path + PathToAbsoluteW(L".", szMirandaDir); + + // retrieve the current profile name + Profile_GetNameW(_countof(ProfileName), ProfileName); + wchar_t *fc = wcsrchr(ProfileName, '.'); + if (fc != nullptr) *fc = 0; + + // we get the user path where our yamn-account.book.ini is stored from mirandaboot.ini file + GetProfileDirectory(UserDirectory, _countof(UserDirectory)); + + // Enumerate all the code pages available for the System Locale + EnumSystemCodePages(EnumSystemCodePagesProc, CP_INSTALLED); + CodePageNamesSupp = new _tcptable[CPLENSUPP]; + for (int i = 0, k = 0; i < CPLENALL; i++) { + if (CodePageNamesAll[i].isValid) { + CodePageNamesSupp[k] = CodePageNamesAll[i]; + k++; + } + } + + if (nullptr == (NoWriterEV = CreateEvent(nullptr, TRUE, TRUE, nullptr))) + return 1; + if (nullptr == (WriteToFileEV = CreateEvent(nullptr, FALSE, FALSE, nullptr))) + return 1; + if (nullptr == (ExitEV = CreateEvent(nullptr, TRUE, FALSE, nullptr))) + return 1; + + PosX = g_plugin.getDword(YAMN_DBPOSX, 0); + PosY = g_plugin.getDword(YAMN_DBPOSY, 0); + SizeX = g_plugin.getDword(YAMN_DBSIZEX, 800); + SizeY = g_plugin.getDword(YAMN_DBSIZEY, 200); + + HeadPosX = g_plugin.getDword(YAMN_DBMSGPOSX, 0); + HeadPosY = g_plugin.getDword(YAMN_DBMSGPOSY, 0); + HeadSizeX = g_plugin.getDword(YAMN_DBMSGSIZEX, 690); + HeadSizeY = g_plugin.getDword(YAMN_DBMSGSIZEY, 300); + HeadSplitPos = g_plugin.getWord(YAMN_DBMSGPOSSPLIT, 250); + + optDateTime = g_plugin.getByte(YAMN_DBTIMEOPTIONS, optDateTime); + + // Create new window queues for broadcast messages + YAMNVar.MessageWnds = WindowList_Create(); + YAMNVar.NewMailAccountWnd = WindowList_Create(); + YAMNVar.Shutdown = FALSE; + + hCurSplitNS = LoadCursor(nullptr, IDC_SIZENS); + hCurSplitWE = LoadCursor(nullptr, IDC_SIZEWE); + +#ifdef _DEBUG + InitDebug(); +#endif + + CreateServiceFunctions(); + + g_plugin.addSound(YAMN_NEWMAILSOUND, L"YAMN", YAMN_NEWMAILSNDDESC); + g_plugin.addSound(YAMN_CONNECTFAILSOUND, L"YAMN", YAMN_CONNECTFAILSNDDESC); + + HookEvents(); + + LoadIcons(); + LoadPlugins(); + + HOTKEYDESC hkd = {}; + hkd.pszName = "YAMN_hotkey"; + hkd.pszService = MS_YAMN_FORCECHECK; + hkd.szSection.a = YAMN_DBMODULE; + hkd.szDescription.a = LPGEN("Check mail"); + hkd.DefHotKey = HOTKEYCODE(HOTKEYF_CONTROL, VK_F11); + g_plugin.addHotkey(&hkd); + + //Create thread that will be executed every second + if (!(SecTimer = SetTimer(nullptr, 0, 1000, TimerProc))) + return 1; + + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static void UnloadPlugins() +{ + if (hDllPlugins == nullptr) + return; + for (int i = iDllPlugins - 1; i >= 0; i--) { + if (FreeLibrary(hDllPlugins[i])) { + hDllPlugins[i] = nullptr; //for safety + iDllPlugins--; + } + } + free((void *)hDllPlugins); + hDllPlugins = nullptr; +} + +int CMPlugin::Unload() +{ +#ifdef _DEBUG + UnInitDebug(); +#endif + + WindowList_Destroy(YAMNVar.MessageWnds); + WindowList_Destroy(YAMNVar.NewMailAccountWnd); + + DestroyCursor(hCurSplitNS); + DestroyCursor(hCurSplitWE); + + CloseHandle(NoWriterEV); + CloseHandle(WriteToFileEV); + CloseHandle(ExitEV); + + UnloadPlugins(); + + delete[] CodePageNamesSupp; + return 0; +} diff --git a/protocols/YAMN/src/main.h b/protocols/YAMN/src/main.h new file mode 100644 index 0000000000..bbfee0e6cf --- /dev/null +++ b/protocols/YAMN/src/main.h @@ -0,0 +1,39 @@ +#ifndef __MAIN_H +#define __MAIN_H + +#define YAMN_NEWMAILSNDDESC LPGENW("New mail message") +#define YAMN_CONNECTFAILSNDDESC LPGENW("Connect failed") +#define YAMN_CONNECTFAILSOUND "YAMN/Sound/ConnectFail" +#define YAMN_NEWMAILSOUND "YAMN/Sound/NewMail" + +#define YAMN_DBMODULE "YAMN" +#define YAMN_DBPOSX "MailBrowserWinX" +#define YAMN_DBPOSY "MailBrowserWinY" +#define YAMN_DBSIZEX "MailBrowserWinW" +#define YAMN_DBSIZEY "MailBrowserWinH" +#define YAMN_DBMSGPOSX "MailMessageWinX" +#define YAMN_DBMSGPOSY "MailMessageWinY" +#define YAMN_DBMSGSIZEX "MailMessageWinW" +#define YAMN_DBMSGSIZEY "MailMessageWinH" +#define YAMN_DBMSGPOSSPLIT "MailMessageSplitY" +#define YAMN_TTBFCHECK "ForceCheckTTB" +#define YAMN_SHOWMAINMENU "ShowMainMenu" +#define YAMN_CLOSEDELETE "CloseOnDelete" +#define YAMN_SHOWASPROTO "ShowAsProtcol" +#define YAMN_DBTIMEOPTIONS "MailBrowserTimeOpts" + +#define YAMN_DEFAULTHK MAKEWORD(VK_F11,MOD_CONTROL) + +#define SHOWDATELONG 0x01 +#define SHOWDATENOTODAY 0x02 +#define SHOWDATENOSECONDS 0x04 + +extern unsigned char optDateTime; + +// Loading Icon and checking for icolib +void LoadIcons(); + +typedef INT_PTR (*LOADFILTERFCN)(MIRANDASERVICE GetYAMNFcn); + +#endif + diff --git a/protocols/YAMN/src/proto/netclient.h b/protocols/YAMN/src/proto/netclient.h new file mode 100644 index 0000000000..99ec0888f1 --- /dev/null +++ b/protocols/YAMN/src/proto/netclient.h @@ -0,0 +1,22 @@ +#ifndef __CLIENT_H +#define __CLIENT_H + +class CNetClient +{ +public: + CNetClient(): Stopped(FALSE) {} + virtual void Connect(const char* servername, const int port)=0; + virtual void Send(const char *query)=0; + virtual char* Recv(char *buf= nullptr, int buflen=65536)=0; + virtual void Disconnect()=0; + virtual BOOL Connected()=0; + virtual void SSLify()=0; + + BOOL Stopped; + int Rcv; + DWORD NetworkError; + DWORD SystemError; + BOOL ifTLSed; +}; + +#endif diff --git a/protocols/YAMN/src/proto/netlib.cpp b/protocols/YAMN/src/proto/netlib.cpp new file mode 100644 index 0000000000..02fb6d3cdc --- /dev/null +++ b/protocols/YAMN/src/proto/netlib.cpp @@ -0,0 +1,242 @@ +/* + * This code implements communication based on Miranda netlib library + * + * (c) majvan 2002-2004 + */ + +#include "../stdafx.h" + + //-------------------------------------------------------------------------------------------------- + //-------------------------------------------------------------------------------------------------- + +BOOL SSLLoaded = FALSE; +HNETLIBUSER hNetlibUser = nullptr; + +void __stdcall SSL_DebugLog(const char *fmt, ...) +{ + char str[4096]; + va_list vararg; + + va_start(vararg, fmt); + int tBytes = mir_vsnprintf(str, _countof(str), fmt, vararg); + if (tBytes == 0) + return; + + if (tBytes > 0) + str[tBytes] = 0; + else + str[sizeof(str) - 1] = 0; + + Netlib_Log(hNetlibUser, str); + va_end(vararg); +} + +HANDLE RegisterNLClient(const char *name) +{ + char desc[128]; + + mir_snprintf(desc, Translate("%s connection"), name); + + #ifdef DEBUG_COMM + DebugLog(CommFile, "<Register PROXY support>"); + #endif + + NETLIBUSER nlu = {}; + nlu.flags = NUF_OUTGOING | NUF_HTTPCONNS; + nlu.szDescriptiveName.a = desc; + nlu.szSettingsModule = (char *)name; + hNetlibUser = Netlib_RegisterUser(&nlu); + + #ifdef DEBUG_COMM + if (NULL == hNetlibUser) + DebugLog(CommFile, "<error></Register PROXY support>\n"); + else + DebugLog(CommFile, "</Register PROXY support>\n"); + #endif + return hNetlibUser; +} + +//Move connection to SSL +void CNLClient::SSLify() throw(DWORD) +{ + #ifdef DEBUG_COMM + SSL_DebugLog("Staring SSL..."); + #endif + int socket = Netlib_GetSocket(hConnection); + if (socket != INVALID_SOCKET) { + #ifdef DEBUG_COMM + SSL_DebugLog("Staring netlib core SSL"); + #endif + if (Netlib_StartSsl(hConnection, nullptr)) { + #ifdef DEBUG_COMM + SSL_DebugLog("Netlib core SSL started"); + #endif + isTLSed = true; + SSLLoaded = TRUE; + return; + } + } + + //ssl could not be created + throw NetworkError = (DWORD)ESSL_CREATESSL; +} + +//Connects to the server through the sock +//if not success, exception is throwed +void CNLClient::Connect(const char* servername, const int port) throw(DWORD) +{ + NetworkError = SystemError = 0; + isTLSed = false; + + #ifdef DEBUG_COMM + DebugLog(CommFile, "<connect>\n"); + #endif + try { + NETLIBOPENCONNECTION nloc; + nloc.cbSize = sizeof(NETLIBOPENCONNECTION); + nloc.szHost = servername; + nloc.wPort = port; + nloc.flags = 0; + if (nullptr == (hConnection = Netlib_OpenConnection(hNetlibUser, &nloc))) { + SystemError = WSAGetLastError(); + throw NetworkError = (DWORD)ENL_CONNECT; + } + #ifdef DEBUG_COMM + DebugLog(CommFile, "</connect>\n"); + #endif + return; + } + catch (...) { + #ifdef DEBUG_COMM + DebugLog(CommFile, "<error></connect>\n"); + #endif + throw; + } +} + +//Performs a simple query +// query- command to send +int CNLClient::LocalNetlib_Send(HNETLIBCONN hConn, const char *buf, int len, int flags) +{ + return Netlib_Send(hConn, buf, len, flags); +} + +void CNLClient::Send(const char *query) throw(DWORD) +{ + unsigned int Sent; + + if (nullptr == query) + return; + if (hConnection == nullptr) + return; + #ifdef DEBUG_COMM + DebugLog(CommFile, "<send>%s", query); + #endif + try { + if ((SOCKET_ERROR == (Sent = LocalNetlib_Send(hConnection, query, (int)mir_strlen(query), MSG_DUMPASTEXT))) || Sent != (unsigned int)mir_strlen(query)) { + SystemError = WSAGetLastError(); + throw NetworkError = (DWORD)ENL_SEND; + } + #ifdef DEBUG_COMM + DebugLog(CommFile, "</send>\n"); + #endif + } + catch (...) { + #ifdef DEBUG_COMM + DebugLog(CommFile, "<error></send>\n"); + #endif + throw; + } +} + +//Reads data from socket +// buf- buffer where to store max. buflen of received characters +// if buf is NULL, creates buffer of buflen size +// buf is NULL by default +//You need free() returned buffer, which can be allocated in this function +//if not success, exception is throwed + +int CNLClient::LocalNetlib_Recv(HNETLIBCONN hConn, char *buf, int len, int flags) +{ + int iReturn = Netlib_Recv(hConn, buf, len, flags); + if (isTLSed) { + #ifdef DEBUG_COMM + SSL_DebugLog("SSL recv: %s", buf); + #endif + } + + return iReturn; +} + +char* CNLClient::Recv(char *buf, int buflen) throw(DWORD) +{ + #ifdef DEBUG_COMM + DebugLog(CommFile, "<reading>"); + #endif + try { + if (buf == nullptr) + buf = (char *)malloc(sizeof(char)*(buflen + 1)); + if (buf == nullptr) + throw NetworkError = (DWORD)ENL_RECVALLOC; + + if (!isTLSed) { + NETLIBSELECT nls = {}; + nls.dwTimeout = 60000; + nls.hReadConns[0] = hConnection; + switch (Netlib_Select(&nls)) { + case SOCKET_ERROR: + free(buf); + SystemError = WSAGetLastError(); + throw NetworkError = (DWORD)ENL_RECV; + case 0: // time out! + free(buf); + throw NetworkError = (DWORD)ENL_TIMEOUT; + } + } + + memset(buf, 0, buflen); + if (SOCKET_ERROR == (Rcv = LocalNetlib_Recv(hConnection, buf, buflen, MSG_DUMPASTEXT))) { + free(buf); + SystemError = WSAGetLastError(); + throw NetworkError = (DWORD)ENL_RECV; + } + if (!Rcv) { + free(buf); + SystemError = WSAGetLastError(); + throw NetworkError = (DWORD)ENL_RECV; + } + #ifdef DEBUG_COMM + *(buf + Rcv) = 0; //end the buffer to write it to file + DebugLog(CommFile, "%s", buf); + DebugLog(CommFile, "</reading>\n"); + #endif + return(buf); + } + catch (...) { + #ifdef DEBUG_COMM + DebugLog(CommFile, "<error></reading>\n"); + #endif + throw; + } +} + +//Closes netlib connection +void CNLClient::Disconnect() +{ + Netlib_CloseHandle(hConnection); + hConnection = nullptr; +} + +//Uninitializes netlib library +void UnregisterNLClient() +{ + #ifdef DEBUG_COMM + DebugLog(CommFile, "<Unregister PROXY support>"); + #endif + + Netlib_CloseHandle(hNetlibUser); + hNetlibUser = nullptr; + #ifdef DEBUG_COMM + DebugLog(CommFile, "</Unregister PROXY support>\n"); + #endif +} diff --git a/protocols/YAMN/src/proto/netlib.h b/protocols/YAMN/src/proto/netlib.h new file mode 100644 index 0000000000..778d6497ed --- /dev/null +++ b/protocols/YAMN/src/proto/netlib.h @@ -0,0 +1,51 @@ +#ifndef __NETLIB_H +#define __NETLIB_H + +class CNLClient: public CNetClient +{ +public: + CNLClient(): hConnection(nullptr) {} + void Connect(const char* servername, const int port) throw(DWORD); + void Send(const char *query) throw(DWORD); + char* Recv(char *buf= nullptr, int buflen = 65536) throw(DWORD); + void Disconnect(); + void SSLify()throw(DWORD); + + inline BOOL Connected() {return hConnection != nullptr;} + +protected: + HNETLIBCONN hConnection; + BOOL isTLSed; + int LocalNetlib_Send(HNETLIBCONN hConn, const char *buf, int len, int flags); + int LocalNetlib_Recv(HNETLIBCONN hConn, char *buf, int len, int flags); +}; + +void SSL_DebugLog(const char *fmt, ...); + +enum +{ + ENL_WINSOCKINIT=1, //error initializing socket //only wsock + ENL_GETHOSTBYNAME, //DNS error //only wsock + ENL_CREATESOCKET, //error creating socket //only wsock + ENL_CONNECT, //cannot connect to server + ENL_SEND, //cannot send data + ENL_RECV, //cannot receive data + ENL_RECVALLOC, //cannot allocate memory for received data + ENL_TIMEOUT, //timed out during recv +}; + +enum +{ + ESSL_NOTLOADED=1, //OpenSSL is not loaded + ESSL_WINSOCKINIT, //WinSock 2.0 init failed + ESSL_GETHOSTBYNAME, //DNS error + ESSL_CREATESOCKET, //error creating socket + ESSL_SOCKETCONNECT, //error connecting with socket + ESSL_CREATESSL, //error creating SSL session structure + ESSL_SETSOCKET, //error connect socket with SSL session for bidirect I/O space + ESSL_CONNECT, //cannot connect to server + ESSL_SEND, //cannot send data + ESSL_RECV, //cannot receive data + ESSL_RECVALLOC, //cannot allocate memory for received data +}; +#endif diff --git a/protocols/YAMN/src/proto/pop3/pop3.cpp b/protocols/YAMN/src/proto/pop3/pop3.cpp new file mode 100644 index 0000000000..d47fac4836 --- /dev/null +++ b/protocols/YAMN/src/proto/pop3/pop3.cpp @@ -0,0 +1,356 @@ +/* + * This code implements basics of POP3 protocol + * + * (c) majvan 2002-2004 + */ +/* This was made from the libspopc project + * copyright c 2002 Benoit Rouits <brouits@free.fr> + * released under the terms of GNU LGPL + * (GNU Lesser General Public Licence). + * libspopc offers simple API for a pop3 client (MTA). + * See RFC 1725 for pop3 specifications. + * more information on http://brouits.free.fr/libspopc/ + */ +/* + * This file is not original and is changed by majvan <om3tn@psg.sk> + * for mail checker purpose. Please see original web page to + * obtain the original. I rewrote it in C++, but good ideas were, + * I think, unchanged. + * + * Note that this file was not designed to work under Unix. It's + * needed to add Unix-specific features. I was interested only in + * Windows for my project. majvan + * + */ + +#include "../../stdafx.h" + +//-------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------- + +//Connects to the server through the netlib +//if not success, exception is throwed +//returns welcome string returned by server +//sets AckFlag +char *CPop3Client::Connect(const char* servername,const int port,BOOL UseSSL, BOOL NoTLS) +{ + char *temp = nullptr; + if (Stopped) //check if we can work with this POP3 client session + throw POP3Error=(DWORD)EPOP3_STOPPED; + + if (NetClient != nullptr) + delete NetClient; + SSL=UseSSL; + NetClient=new CNLClient; + +#ifdef DEBUG_DECODE + DebugLog(DecodeFile,"Connect:servername: %s port:%d\n",servername,port); +#endif + POP3Error=EPOP3_CONNECT; + NetClient->Connect(servername,port); + POP3Error=0; + + if (SSL) + { + try { NetClient->SSLify(); } + catch (...) + { + NetClient->Disconnect(); + return nullptr; + } + } + + temp = RecvRest(NetClient->Recv(),POP3_SEARCHACK); + extern BOOL SSLLoaded; + if (!NoTLS & !(SSL)) { + if (NetClient->Stopped) //check if we can work with this POP3 client session + throw POP3Error=(DWORD)EPOP3_STOPPED; + NetClient->Send("STLS\r\n"); + free(temp); + temp=RecvRest(NetClient->Recv(),POP3_SEARCHACK); + if (AckFlag==POP3_FOK) { // Ok, we are going to tls + try { + NetClient->SSLify(); + } catch (...) { + NetClient->Disconnect(); + return nullptr; + } +// temp = RecvRest(NetClient->Recv(),POP3_SEARCHACK); + } + } +// SSL_DebugLog("Received: %s",temp); + return temp; +} + +//Receives data to the end of packet +// prev- previous data read (appends to this string next received data) +// mode- mode of packet. +// Packet can end with ack state (+OK or -ERR): set mode to POP3_SEARCHACK +// If packet ends with '.' (end of string), set mode to POP3_SEARCHDOT +// size- received data are stored to memory, but if length of data is more than allocated memory, function allocates +// new memory. New allocated memory has allocated size more bytes +// This value can be selectable: if you think it is better to reallocate by 1kB size, select size to 1024, +// default is 128. You do not need to use this parameter +char* CPop3Client::RecvRest(char* prev,int mode,int size) +{ + int SizeRead=0; + int SizeLeft=size-NetClient->Rcv; + int RcvAll=NetClient->Rcv; + char *LastString,*PrevString=prev; + + AckFlag=0; + + while(((mode==POP3_SEARCHDOT) && !SearchFromEnd(PrevString+RcvAll-1,RcvAll-3,POP3_SEARCHDOT) && !SearchFromStart(PrevString,2,POP3_SEARCHERR)) || //we are looking for dot or -err phrase + ((mode==POP3_SEARCHACK) && (!SearchFromStart(PrevString,RcvAll-3,mode) || !((RcvAll>3) && SearchFromEnd(PrevString+RcvAll-1,1,POP3_SEARCHNL))))) //we are looking for +ok or -err phrase ended with newline + { //if not found + if (NetClient->Stopped) //check if we can work with this POP3 client session + { + if (PrevString != nullptr) + free(PrevString); + throw POP3Error=(DWORD)EPOP3_STOPPED; + } + if (SizeLeft==0) //if block is full + { + SizeRead+=size; + SizeLeft=size; + LastString=NetClient->Recv(nullptr,SizeLeft); + PrevString=(char *)realloc(PrevString,sizeof(char)*(SizeRead+size)); + if (PrevString==nullptr) + throw POP3Error=(DWORD)EPOP3_RESTALLOC; + memcpy(PrevString+SizeRead,LastString,size); + free(LastString); + } + else + NetClient->Recv(PrevString+RcvAll,SizeLeft); //to Rcv stores received bytes + SizeLeft=SizeLeft-NetClient->Rcv; + RcvAll+=NetClient->Rcv; + } + NetClient->Rcv=RcvAll; //at the end, store the number of all bytes, no the number of last received bytes + return PrevString; +} + +// CPop3Client::SearchFromEnd +// returns 1 if substring DOTLINE or ENDLINE found from end in bs bytes +// if you need to add condition for mode, insert it into switch statement +BOOL CPop3Client::SearchFromEnd(char *end,int bs,int mode) +{ + while(bs>=0) + { + switch(mode) + { + case POP3_SEARCHDOT: + if (DOTLINE(end)) + return 1; + break; + case POP3_SEARCHNL: + if (ENDLINE(end)) + return 1; + break; + } + end--; + bs--; + } + return 0; +} + +//Finds for a occurence of some pattern in string +// returns 1 if substring OKLINE, ERRLINE or any of them found from start in bs bytes +//call only this function to retrieve ack status (+OK or -ERR), because it sets flag AckFlag +//if you need to add condition for mode, insert it into switch statement +BOOL CPop3Client::SearchFromStart(char *start,int bs,int mode) +{ + while(bs>=0) + { + switch(mode) + { + case POP3_SEARCHOK: + if (OKLINE(start)) + { + AckFlag=POP3_FOK; + return 1; + } + break; + case POP3_SEARCHERR: + if (ERRLINE(start)) + { + AckFlag=POP3_FERR; + return 1; + } + break; + case POP3_SEARCHACK: + if (ACKLINE(start)) + { + OKLINE(start) ? AckFlag=POP3_FOK : AckFlag=POP3_FERR; + return 1; + } + break; + } + start++; + bs--; + } + return 0; +} + +//Performs "USER" pop query and returns server response +//sets AckFlag +char* CPop3Client::User(char* name) +{ + if (NetClient->Stopped) //check if we can work with this POP3 client session + throw POP3Error=(DWORD)EPOP3_STOPPED; + + char query[128]; + char *Result; + + mir_snprintf(query, "USER %s\r\n", name); + NetClient->Send(query); + Result=RecvRest(NetClient->Recv(),POP3_SEARCHACK); + if (AckFlag==POP3_FERR) + throw POP3Error=(DWORD)EPOP3_BADUSER; + POP3Error=0; + return Result; +} + +//Performs "PASS" pop query and returns server response +//sets AckFlag +char* CPop3Client::Pass(char* pw) +{ + if (NetClient->Stopped) //check if we can work with this POP3 client session + throw POP3Error=(DWORD)EPOP3_STOPPED; + + char query[128]; + char *Result; + + mir_snprintf(query, "PASS %s\r\n", pw); + NetClient->Send(query); + Result=RecvRest(NetClient->Recv(),POP3_SEARCHACK); + if (AckFlag==POP3_FERR) + throw POP3Error=(DWORD)EPOP3_BADPASS; + return Result; +} + +//Performs "APOP" pop query and returns server response +//sets AckFlag +char* CPop3Client::APOP(char* name, char* pw, char* timestamp) +{ + if (NetClient->Stopped) //check if we can work with this POP3 client session + throw POP3Error=(DWORD)EPOP3_STOPPED; + + char query[512]; + char *Result; + unsigned char digest[16]; + + if (timestamp==nullptr) + throw POP3Error=(DWORD)EPOP3_APOP; + mir_md5_state_s ctx; + mir_md5_init(&ctx); + mir_md5_append(&ctx,(const unsigned char *)timestamp,(unsigned int)mir_strlen(timestamp)); + mir_md5_append(&ctx,(const unsigned char *)pw,(unsigned int)mir_strlen(pw)); + mir_md5_finish(&ctx, digest); + + char hexdigest[40]; + mir_snprintf(query, "APOP %s %s\r\n", name, bin2hex(digest, sizeof(digest), hexdigest)); + + NetClient->Send(query); + Result=RecvRest(NetClient->Recv(),POP3_SEARCHACK); + if (AckFlag==POP3_FERR) + throw POP3Error=(DWORD)EPOP3_BADUSER; + return Result; +} + +//Performs "QUIT" pop query and returns server response +//sets AckFlag +char* CPop3Client::Quit() +{ + char query[]="QUIT\r\n"; + + NetClient->Send(query); + return RecvRest(NetClient->Recv(),POP3_SEARCHACK); +} + +//Performs "STAT" pop query and returns server response +//sets AckFlag +char* CPop3Client::Stat() +{ + if (NetClient->Stopped) //check if we can work with this POP3 client session + throw POP3Error=(DWORD)EPOP3_STOPPED; + + char query[]="STAT\r\n"; + + NetClient->Send(query); + return RecvRest(NetClient->Recv(),POP3_SEARCHACK); +} + +//Performs "LIST" pop query and returns server response +//sets AckFlag +char* CPop3Client::List() +{ + if (NetClient->Stopped) //check if we can work with this POP3 client session + throw POP3Error=(DWORD)EPOP3_STOPPED; + + char query[]="LIST\r\n"; + + NetClient->Send(query); + return RecvRest(NetClient->Recv(),POP3_SEARCHDOT); +} + +//Performs "TOP" pop query and returns server response +//sets AckFlag +char* CPop3Client::Top(int nr, int lines) +{ + if (NetClient->Stopped) //check if we can work with this POP3 client session + throw POP3Error=(DWORD)EPOP3_STOPPED; + + char query[128]; + + mir_snprintf(query, "TOP %d %d\r\n", nr, lines); + NetClient->Send(query); + return RecvRest(NetClient->Recv(),POP3_SEARCHDOT); +} + +//Performs "UIDL" pop query and returns server response +//sets AckFlag +char* CPop3Client::Uidl(int nr) +{ + if (NetClient->Stopped) //check if we can work with this POP3 client session + throw POP3Error=(DWORD)EPOP3_STOPPED; + + char query[128]; + + if (nr) + { + mir_snprintf(query, "UIDL %d\r\n", nr); + NetClient->Send(query); + return RecvRest(NetClient->Recv(),POP3_SEARCHACK); + } + mir_snprintf(query, "UIDL\r\n"); + NetClient->Send(query); + return RecvRest(NetClient->Recv(),POP3_SEARCHDOT); +} + +//Performs "DELE" pop query and returns server response +//sets AckFlag +char* CPop3Client::Dele(int nr) +{ + if (NetClient->Stopped) //check if we can work with this POP3 client session + throw POP3Error=(DWORD)EPOP3_STOPPED; + + char query[128]; + + mir_snprintf(query, "DELE %d\r\n", nr); + NetClient->Send(query); + return RecvRest(NetClient->Recv(),POP3_SEARCHACK); +} +//Performs "RETR" pop query and returns server response +//sets AckFlag +char* CPop3Client::Retr(int nr) +{ + if (NetClient->Stopped) //check if we can work with this POP3 client session + throw POP3Error=(DWORD)EPOP3_STOPPED; + + char query[128]; + + mir_snprintf(query, "RETR %d\r\n", nr); + NetClient->Send(query); + RecvRest(NetClient->Recv(),POP3_SEARCHACK); + return NetClient->Recv(); +}
\ No newline at end of file diff --git a/protocols/YAMN/src/proto/pop3/pop3.h b/protocols/YAMN/src/proto/pop3/pop3.h new file mode 100644 index 0000000000..1fd7994bed --- /dev/null +++ b/protocols/YAMN/src/proto/pop3/pop3.h @@ -0,0 +1,63 @@ +#ifndef __POP3_H +#define __POP3_H + +#define DOTLINE(s) ((((s)[-2]=='\r') || ((s)[-2]=='\n')) && ((s)[-1]=='.') && (((s)[0]=='\r') || ((s)[0]=='\n') || ((s)[0]=='\0'))) // be careful, it's different to ESR's pop3.c ;-) +#define ENDLINE(s) (((s)[0]=='\r') || ((s)[0]=='\n')) //endline +#define OKLINE(s) (((s)[0]=='+') && (((s)[1]=='o') || ((s)[1]=='O')) && (((s)[2]=='k') || ((s)[2]=='K'))) // +OK +#define ERRLINE(s) (((s)[0]=='-') && (((s)[1]=='e') || ((s)[1]=='E')) && (((s)[2]=='r') || ((s)[2]=='R')) && (((s)[3]=='r') || ((s)[3]=='R'))) // -ERR +#define ACKLINE(s) (OKLINE(s) || ERRLINE(s)) + +#define POP3_SEARCHDOT 1 +#define POP3_SEARCHACK 2 +#define POP3_SEARCHOK 3 +#define POP3_SEARCHERR 4 +#define POP3_SEARCHNL 5 + +#define POP3_FOK 1 +#define POP3_FERR 2 + +class CPop3Client +{ +public: + CPop3Client(): NetClient(nullptr), Stopped(FALSE) {} + ~CPop3Client() {if (NetClient != nullptr) delete NetClient;} + + char* Connect(const char* servername,const int port=110,BOOL UseSSL=FALSE, BOOL NoTLS=FALSE); + char* RecvRest(char* prev,int mode,int size=65536); + char* User(char* name); + char* Pass(char* pw); + char* APOP(char* name, char* pw, char* timestamp); + char* Quit(); + char* Stat(); + char* List(); + char* Top(int nr, int lines=0); + char* Uidl(int nr=0); + char* Dele(int nr); + char* Retr(int nr); + + unsigned char AckFlag; + BOOL SSL; + BOOL Stopped; + + DWORD POP3Error; + class CNetClient *NetClient; //here the network layout is defined (TCP or SSL+TCP etc.) +private: + BOOL SearchFromEnd(char *end,int bs,int mode); + BOOL SearchFromStart(char *end,int bs,int mode); +}; + +enum +{ + EPOP3_QUEUEALLOC=1, //memory allocation + EPOP3_STOPPED, //stop account + EPOP3_CONNECT, //cannot connect to server + EPOP3_RESTALLOC, //cannot allocate memory for received data + EPOP3_BADUSER, //cannot login because USER command failed + EPOP3_BADPASS, //cannot login because PASS command failed + EPOP3_APOP, //server does not send timestamp for APOP auth + EPOP3_STAT, + EPOP3_LIST, + EPOP3_UIDL, +}; + +#endif diff --git a/protocols/YAMN/src/proto/pop3/pop3comm.cpp b/protocols/YAMN/src/proto/pop3/pop3comm.cpp new file mode 100644 index 0000000000..dad9ee1e28 --- /dev/null +++ b/protocols/YAMN/src/proto/pop3/pop3comm.cpp @@ -0,0 +1,1558 @@ +/* + * This code implements POP3 server checking for new mail and so on. + * There's function SynchroPOP3 in this file- for checking and synchronising POP3 account + * and DeleteMailsPOP3- for deleting mails from POP3 server + * + * Note this file acts as main file for internal plugin. + * + * (c) majvan 2002-2004 + * 18/08 + */ + + +#include "../../stdafx.h" + +#define ERRORSTR_MAXLEN 1024 //in wide-chars + +//-------------------------------------------------------------------------------------------------- + +HANDLE hNetLib = nullptr; +PSCOUNTER CPOP3Account::AccountWriterSO = nullptr; + +//Creates new CPOP3Account structure +HACCOUNT WINAPI CreatePOP3Account(HYAMNPROTOPLUGIN Plugin, DWORD CAccountVersion); + +//Deletes CPOP3Account structure +void WINAPI DeletePOP3Account(HACCOUNT Which); + +//Sets stop flag to account +void WINAPI StopPOP3Account(HACCOUNT Which); + +//Function registers standard functions for YAMN +int RegisterPOP3Plugin(WPARAM, LPARAM); + +//Unloads all variables created on heap (delete[]) +DWORD WINAPI UnLoadPOP3(void *); + +//Function writes POP3 accounts using YAMN exported functions +DWORD WINAPI WritePOP3Accounts(); + +//Function stores plugin's data for account to file +DWORD WINAPI WritePOP3Options(HANDLE, HACCOUNT); + +//Function reads plugin's data for account from file +DWORD WINAPI ReadPOP3Options(HACCOUNT, char **, char *); + +//Creates new mail for an account +HYAMNMAIL WINAPI CreatePOP3Mail(HACCOUNT Account, DWORD CMimeMailVersion); + +//Function does all needed work when connection failed or any error occured +//Creates structure containing error code, closes internet session, runs "bad connect" function +static void PostErrorProc(HPOP3ACCOUNT ActualAccount, void *ParamToBadConnect, DWORD POP3PluginParam, BOOL UseSSL); + +//Checks POP3 account and stores all info to account. It deletes old mails=> synchro +// WhichTemp- pointer to strucure containing needed information +DWORD WINAPI SynchroPOP3(struct CheckParam *WhichTemp); + +//Deletes mails from POP3 server +// WhichTemp- structure containing needed information (queued messages to delete) +//Function deletes from memory queue in WhichTemp structure +void __cdecl DeleteMailsPOP3(void *param); + +//Function makes readable message about error. It sends it back to YAMN, so YAMN then +//can show it to the message window +wchar_t* WINAPI GetErrorString(DWORD Code); + +//Function deletes string allocated in GetErrorString +void WINAPI DeleteErrorString(LPVOID String); + +//Extracts info from result of POP3's STAT command +// stream- source string +// len- length of source string +// mboxsize- adreess to integer, that receives size of mailbox +// mails- adreess to integer, that receives number of mails +void ExtractStat(char *stream, int *mboxsize, int *mails); + +//Extracts mail ID on mailbox +// stream- source string +// len- length of source string +// queue- address of first message, where first ID will be stored +void ExtractUIDL(char *stream, int len, HYAMNMAIL queue); + +//Extracts mail size on mailbox +// stream- source string +// len- length of source string +// queue- address of first message, where size of message #1 will be stored +void ExtractList(char *stream, int len, HYAMNMAIL queue); + +void ExtractMail(char *stream, int len, HYAMNMAIL queue); + +YAMNExportedFcns *pYAMNFcn = nullptr; +MailExportedFcns *pYAMNMailFcn = nullptr; + +YAMN_PROTOIMPORTFCN POP3ProtocolFunctions = +{ + CreatePOP3Account, + DeletePOP3Account, + StopPOP3Account, + WritePOP3Options, + ReadPOP3Options, + SynchroPOP3, + SynchroPOP3, + SynchroPOP3, + DeleteMailsPOP3, + GetErrorString, + nullptr, + DeleteErrorString, + WritePOP3Accounts, + nullptr, + UnLoadPOP3, +}; + +YAMN_MAILIMPORTFCN POP3MailFunctions = +{ + CreatePOP3Mail, + nullptr, + nullptr, + nullptr, +}; + +PYAMN_VARIABLES pYAMNVar = nullptr; +HYAMNPROTOPLUGIN POP3Plugin = nullptr; + +YAMN_PROTOREGISTRATION POP3ProtocolRegistration = +{ + "POP3 protocol (internal)", + __VERSION_STRING_DOTS, + __COPYRIGHT, + __DESCRIPTION, + __AUTHORWEB, +}; + +static wchar_t *FileName = nullptr; + +HANDLE RegisterNLClient(const char *name); + +//-------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------- + +CPOP3Account::CPOP3Account() +{ + //NOTE! This constructor constructs CAccount structure. If your plugin is not internal, + //you will need these constructors. All you need is in Account.cpp. Just copy to your source code + //constructor and destructor of CAccount. + UseInternetFree = CreateEvent(nullptr, FALSE, TRUE, nullptr); + InternetQueries = new SCOUNTER; + AbilityFlags = YAMN_ACC_BROWSE | YAMN_ACC_POPUP; + + SetAccountStatus((HACCOUNT)this, TranslateT("Disconnected")); +} + +CPOP3Account::~CPOP3Account() +{ + CloseHandle(UseInternetFree); + if (InternetQueries != nullptr) + delete InternetQueries; +} + +HACCOUNT WINAPI CreatePOP3Account(HYAMNPROTOPLUGIN, DWORD) +{ + //First, we should check whether CAccountVersion matches. + //But this is internal plugin, so YAMN's CAccount structure and our CAccount structure are + //the same, so we do not need to test version. Otherwise, if CAccount version does not match + //in your plugin, you should return NULL, like this: + // if (CAccountVersion != YAMN_ACCOUNTVERSION) return NULL; + + //Now it is needed to construct our POP3 account and return its handle + return (HACCOUNT)new struct CPOP3Account(); +} + +void WINAPI DeletePOP3Account(HACCOUNT Which) +{ + delete (HPOP3ACCOUNT)Which; +} + +void WINAPI StopPOP3Account(HACCOUNT Which) +{ + ((HPOP3ACCOUNT)Which)->Client.Stopped = TRUE; + if (((HPOP3ACCOUNT)Which)->Client.NetClient != nullptr) //we should inform also network client. Usefull only when network client implements this feature + ((HPOP3ACCOUNT)Which)->Client.NetClient->Stopped = TRUE; +} + +//This function is like main function for POP3 internal protocol +int RegisterPOP3Plugin(WPARAM, LPARAM) +{ + + //Get YAMN variables we can use + if (nullptr == (pYAMNVar = (PYAMN_VARIABLES)CallService(MS_YAMN_GETVARIABLES, (WPARAM)YAMN_VARIABLESVERSION, 0))) + return 0; + + //We have to get pointers to YAMN exported functions: allocate structure and fill it + if (nullptr == (pYAMNFcn = new struct YAMNExportedFcns)) + { + UnLoadPOP3(nullptr); return 0; + } + + //Register new pop3 user in netlib + if (nullptr == (hNetLib = RegisterNLClient("YAMN-POP3"))) + { + UnLoadPOP3(nullptr); return 0; + } + + pYAMNFcn->SetProtocolPluginFcnImportFcn = (YAMN_SETPROTOCOLPLUGINFCNIMPORTFCN)CallService(MS_YAMN_GETFCNPTR, (WPARAM)YAMN_SETPROTOCOLPLUGINFCNIMPORTID, 0); + pYAMNFcn->WaitToWriteFcn = (YAMN_WAITTOWRITEFCN)CallService(MS_YAMN_GETFCNPTR, (WPARAM)YAMN_WAITTOWRITEID, 0); + pYAMNFcn->WriteDoneFcn = (YAMN_WRITEDONEFCN)CallService(MS_YAMN_GETFCNPTR, (WPARAM)YAMN_WRITEDONEID, 0); + pYAMNFcn->WaitToReadFcn = (YAMN_WAITTOREADFCN)CallService(MS_YAMN_GETFCNPTR, (WPARAM)YAMN_WAITTOREADID, 0); + pYAMNFcn->ReadDoneFcn = (YAMN_READDONEFCN)CallService(MS_YAMN_GETFCNPTR, (WPARAM)YAMN_READDONEID, 0); + pYAMNFcn->SCGetNumberFcn = (YAMN_SCMANAGEFCN)CallService(MS_YAMN_GETFCNPTR, (WPARAM)YAMN_SCGETNUMBERID, 0); + pYAMNFcn->SCIncFcn = (YAMN_SCMANAGEFCN)CallService(MS_YAMN_GETFCNPTR, (WPARAM)YAMN_SCINCID, 0); + pYAMNFcn->SCDecFcn = (YAMN_SCMANAGEFCN)CallService(MS_YAMN_GETFCNPTR, (WPARAM)YAMN_SCDECID, 0); + pYAMNFcn->SetStatusFcn = (YAMN_SETSTATUSFCN)CallService(MS_YAMN_GETFCNPTR, (WPARAM)YAMN_SETSTATUSID, 0); + pYAMNFcn->GetStatusFcn = (YAMN_GETSTATUSFCN)CallService(MS_YAMN_GETFCNPTR, (WPARAM)YAMN_GETSTATUSID, 0); + + if (nullptr == (pYAMNMailFcn = new struct MailExportedFcns)) + { + UnLoadPOP3(nullptr); return 0; + } + + pYAMNMailFcn->SynchroMessagesFcn = (YAMN_SYNCHROMIMEMSGSFCN)CallService(MS_YAMN_GETFCNPTR, (WPARAM)YAMN_SYNCHROMIMEMSGSID, 0); + pYAMNMailFcn->TranslateHeaderFcn = (YAMN_TRANSLATEHEADERFCN)CallService(MS_YAMN_GETFCNPTR, (WPARAM)YAMN_TRANSLATEHEADERID, 0); + pYAMNMailFcn->AppendQueueFcn = (YAMN_APPENDQUEUEFCN)CallService(MS_YAMN_GETFCNPTR, (WPARAM)YAMN_APPENDQUEUEID, 0); + pYAMNMailFcn->DeleteMessagesToEndFcn = (YAMN_DELETEMIMEQUEUEFCN)CallService(MS_YAMN_GETFCNPTR, (WPARAM)YAMN_DELETEMIMEQUEUEID, 0); + pYAMNMailFcn->DeleteMessageFromQueueFcn = (YAMN_DELETEMIMEMESSAGEFCN)CallService(MS_YAMN_GETFCNPTR, (WPARAM)YAMN_DELETEMIMEMESSAGEID, 0); + pYAMNMailFcn->FindMessageByIDFcn = (YAMN_FINDMIMEMESSAGEFCN)CallService(MS_YAMN_GETFCNPTR, (WPARAM)YAMN_FINDMIMEMESSAGEID, 0); + pYAMNMailFcn->CreateNewDeleteQueueFcn = (YAMN_CREATENEWDELETEQUEUEFCN)CallService(MS_YAMN_GETFCNPTR, (WPARAM)YAMN_CREATENEWDELETEQUEUEID, 0); + + //set static variable + if (CPOP3Account::AccountWriterSO == nullptr) { + if (nullptr == (CPOP3Account::AccountWriterSO = new SCOUNTER)) + { + UnLoadPOP3(nullptr); return 0; + } + } + + //First, we register this plugin + //it is quite impossible this function returns zero (failure) as YAMN and internal plugin structre versions are the same + POP3ProtocolRegistration.Name = Translate("POP3 protocol (internal)"); + POP3ProtocolRegistration.Description = Translate(__DESCRIPTION); + if (nullptr == (POP3Plugin = (HYAMNPROTOPLUGIN)CallService(MS_YAMN_REGISTERPROTOPLUGIN, (WPARAM)&POP3ProtocolRegistration, (LPARAM)YAMN_PROTOREGISTRATIONVERSION))) + return 0; + + //Next we set our imported functions for YAMN + if (!SetProtocolPluginFcnImport(POP3Plugin, &POP3ProtocolFunctions, YAMN_PROTOIMPORTFCNVERSION, &POP3MailFunctions, YAMN_MAILIMPORTFCNVERSION)) + return 0; + + //Then, we read all mails for accounts. + //You must first register account, before using this function as YAMN must use CreatePOP3Account function to add new accounts + //But if CreatePOP3Account is not implemented (equals to NULL), YAMN creates account as YAMN's standard HACCOUNT + if (FileName) CallService(MS_YAMN_DELETEFILENAME, (WPARAM)FileName, 0); //shoud not happen (only for secure) + FileName = (wchar_t *)CallService(MS_YAMN_GETFILENAME, (WPARAM)L"pop3", 0); + + switch (CallService(MS_YAMN_READACCOUNTS, (WPARAM)POP3Plugin, (LPARAM)FileName)) { + case EACC_FILEVERSION: + MessageBox(nullptr, TranslateT("Found new version of account book, not compatible with this version of YAMN."), TranslateT("YAMN (internal POP3) read error"), MB_OK); + CallService(MS_YAMN_DELETEFILENAME, (WPARAM)FileName, 0); + FileName = nullptr; + return 0; + case EACC_FILECOMPATIBILITY: + MessageBox(nullptr, TranslateT("Error reading account file. Account file corrupted."), TranslateT("YAMN (internal POP3) read error"), MB_OK); + CallService(MS_YAMN_DELETEFILENAME, (WPARAM)FileName, 0); + FileName = nullptr; + return 0; + case EACC_ALLOC: + MessageBox(nullptr, TranslateT("Memory allocation error while data reading"), TranslateT("YAMN (internal POP3) read error"), MB_OK); + CallService(MS_YAMN_DELETEFILENAME, (WPARAM)FileName, 0); + FileName = nullptr; + return 0; + case EACC_SYSTEM: + if (ERROR_FILE_NOT_FOUND != GetLastError()) + { + wchar_t temp[1024] = { 0 }; + mir_snwprintf(temp, L"%s\n%s", TranslateT("Reading file error. File already in use?"), FileName); + MessageBox(nullptr, temp, TranslateT("YAMN (internal POP3) read error"), MB_OK); + CallService(MS_YAMN_DELETEFILENAME, (WPARAM)FileName, 0); + FileName = nullptr; + return 0; + } + break; + } + + HACCOUNT Finder; + DBVARIANT dbv; + + for (Finder = POP3Plugin->FirstAccount; Finder != nullptr; Finder = Finder->Next) { + Finder->hContact = NULL; + for (auto &hContact : Contacts(YAMN_DBMODULE)) { + if (!g_plugin.getString(hContact, "Id", &dbv)) { + if (mir_strcmp(dbv.pszVal, Finder->Name) == 0) { + Finder->hContact = hContact; + g_plugin.setWord(Finder->hContact, "Status", ID_STATUS_ONLINE); + db_set_s(Finder->hContact, "CList", "StatusMsg", Translate("No new mail message")); + if ((Finder->Flags & YAMN_ACC_ENA) && (Finder->NewMailN.Flags & YAMN_ACC_CONT)) + db_unset(Finder->hContact, "CList", "Hidden"); + + if (!(Finder->Flags & YAMN_ACC_ENA) || !(Finder->NewMailN.Flags & YAMN_ACC_CONT)) + db_set_b(Finder->hContact, "CList", "Hidden", 1); + } + db_free(&dbv); + } + } + + if (!Finder->hContact && (Finder->Flags & YAMN_ACC_ENA) && (Finder->NewMailN.Flags & YAMN_ACC_CONT)) { + // No account contact found, have to create one + Finder->hContact = db_add_contact(); + Proto_AddToContact(Finder->hContact, YAMN_DBMODULE); + g_plugin.setString(Finder->hContact, "Id", Finder->Name); + g_plugin.setString(Finder->hContact, "Nick", Finder->Name); + g_plugin.setWord(Finder->hContact, "Status", YAMN_STATUS); + } + } + + return 0; +} + +DWORD WINAPI UnLoadPOP3(void *) +{ + //pYAMNVar is only a pointr, no need delete or free + if (hNetLib) { + Netlib_CloseHandle(hNetLib); hNetLib = nullptr; + } + if (CPOP3Account::AccountWriterSO) { + delete CPOP3Account::AccountWriterSO; CPOP3Account::AccountWriterSO = nullptr; + } + if (pYAMNMailFcn) { + delete pYAMNMailFcn; pYAMNMailFcn = nullptr; + } + if (pYAMNFcn) { + delete pYAMNFcn; pYAMNFcn = nullptr; + } + if (FileName) { + CallService(MS_YAMN_DELETEFILENAME, (WPARAM)FileName, 0); FileName = nullptr; + } + +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"UnLoadPOP3:done\n"); +#endif + return 1; +} + +DWORD WINAPI WritePOP3Accounts() +{ + DWORD ReturnValue = CallService(MS_YAMN_WRITEACCOUNTS, (WPARAM)POP3Plugin, (LPARAM)FileName); + if (ReturnValue == EACC_SYSTEM) { + wchar_t temp[1024] = { 0 }; + mir_snwprintf(temp, L"%s\n%s", TranslateT("Error while copying data to disk occurred. Is file in use?"), FileName); + MessageBox(nullptr, temp, TranslateT("POP3 plugin - write file error"), MB_OK); + } + + return ReturnValue; +} + +DWORD WINAPI WritePOP3Options(HANDLE File, HACCOUNT Which) +{ + DWORD WrittenBytes; + DWORD Ver = POP3_FILEVERSION; + + if ((!WriteFile(File, (char *)&Ver, sizeof(DWORD), &WrittenBytes, nullptr)) || + (!WriteFile(File, (char *)&((HPOP3ACCOUNT)Which)->CP, sizeof(WORD), &WrittenBytes, nullptr))) + return EACC_SYSTEM; + return 0; +} + +DWORD WINAPI ReadPOP3Options(HACCOUNT Which, char **Parser, char *End) +{ + DWORD Ver; +#ifdef DEBUG_FILEREAD + wchar_t Debug[256]; +#endif + Ver = *(DWORD *)(*Parser); + (*Parser) += sizeof(DWORD); + if (*Parser >= End) + return EACC_FILECOMPATIBILITY; + if (Ver != POP3_FILEVERSION) + return EACC_FILECOMPATIBILITY; + + ((HPOP3ACCOUNT)Which)->CP = *(WORD *)(*Parser); + (*Parser) += sizeof(WORD); + if (*Parser >= End) + return EACC_FILECOMPATIBILITY; +#ifdef DEBUG_FILEREAD + mir_snwprintf(Debug, L"CodePage: %d, remaining %d chars", ((HPOP3ACCOUNT)Which)->CP, End-*Parser); + MessageBox(NULL,Debug,L"debug",MB_OK); +#endif + return 0; +} + +HYAMNMAIL WINAPI CreatePOP3Mail(HACCOUNT Account, DWORD) +{ + HYAMNMAIL NewMail; + //First, we should check whether MAILDATA matches. + //But this is internal plugin, so YAMN's MAILDATA structure and our MAILDATA structure are + //the same, so we do not need to test version. Otherwise, if MAILDATA version does not match + //in your plugin, you should return NULL, like this: + // if (MailDataVersion != YAMN_MAILDATAVERSION) return NULL; + + //Now it is needed to construct our POP3 account and return its handle + if (nullptr == (NewMail = new YAMNMAIL)) + return nullptr; + + if (nullptr == (NewMail->MailData = new MAILDATA)) + { + delete NewMail; + return nullptr; + } + NewMail->MailData->CP = ((HPOP3ACCOUNT)Account)->CP; + return (HYAMNMAIL)NewMail; +} + +static void SetContactStatus(HACCOUNT account, int status) +{ + if ((account->hContact) && (account->NewMailN.Flags & YAMN_ACC_CONT)) + g_plugin.setWord(account->hContact, "Status", status); +} + +static void PostErrorProc(HPOP3ACCOUNT ActualAccount, void *ParamToBadConnection, DWORD POP3PluginParam, BOOL UseSSL) +{ + char *DataRX; + + //We create new structure, that we pass to bad connection dialog procedure. This procedure next calls YAMN imported fuction + //from POP3 protocol to determine the description of error. We can describe error from our error code structure, because later, + //when YAMN calls our function, it passes us our error code. This is pointer to structure for POP3 protocol in fact. + PPOP3_ERRORCODE ErrorCode; + + //We store status before we do Quit(), because quit can destroy our errorcode status + if (nullptr != (ErrorCode = new POP3_ERRORCODE)) + { + ErrorCode->SSL = UseSSL; + ErrorCode->AppError = ActualAccount->SystemError; + ErrorCode->POP3Error = ActualAccount->Client.POP3Error; + ErrorCode->NetError = ActualAccount->Client.NetClient->NetworkError; + ErrorCode->SystemError = ActualAccount->Client.NetClient->SystemError; + } + + if (POP3PluginParam == (DWORD)NULL) //if it was normal YAMN call (force check or so on) + { + try + { + DataRX = ActualAccount->Client.Quit(); + if (DataRX != nullptr) + free(DataRX); + } + catch (...) + { + } + //We always close connection if error occured + try + { + ActualAccount->Client.NetClient->Disconnect(); + } + catch (...) + { + } + + SetAccountStatus(ActualAccount, TranslateT("Disconnected")); + + //If we cannot allocate memory, do nothing + if (ErrorCode == nullptr) + { + SetEvent(ActualAccount->UseInternetFree); + return; + } + } + else //else it was called from POP3 plugin, probably error when deleting old mail (POP3 synchro calls POP3 delete) + if (ErrorCode == nullptr) + return; + + if ((ActualAccount->BadConnectN.Flags & YAMN_ACC_MSG) || (ActualAccount->BadConnectN.Flags & YAMN_ACC_ICO) || (ActualAccount->BadConnectN.Flags & YAMN_ACC_POP)) + { + YAMN_BADCONNECTIONPARAM cp = { (HANDLE)nullptr, ActualAccount, (UINT_PTR)ErrorCode, ParamToBadConnection }; + + CallService(MS_YAMN_BADCONNECTION, (WPARAM)&cp, (LPARAM)YAMN_BADCONNECTIONVERSION); + } + if (POP3PluginParam == (DWORD)NULL) //if it was normal YAMN call + SetEvent(ActualAccount->UseInternetFree); +} + +//Checks POP3 account and synchronizes it +DWORD WINAPI SynchroPOP3(struct CheckParam * WhichTemp) +{ + HPOP3ACCOUNT ActualAccount; + CPop3Client *MyClient; + HYAMNMAIL NewMails = nullptr, MsgQueuePtr = nullptr; + char* DataRX = nullptr, *Temp; + int mboxsize, msgs, i; + SYSTEMTIME now; + LPVOID YAMNParam; + DWORD CheckFlags; + BOOL UsingInternet = FALSE; + struct { + char *ServerName; + DWORD ServerPort; + char *ServerLogin; + char *ServerPasswd; + DWORD Flags; + DWORD NFlags; + DWORD NNFlags; + } ActualCopied; + + //First, we should compare our version of CheckParam structure, but here it is not needed, because YAMN and internal plugin + //have the same version. But your plugin should do that in this way: + // if (((struct CheckParam *)WhichTemp)->Ver != YAMN_CHECKVERSION) + // { + // SetEvent(((struct CheckParam *)WhichTemp)->ThreadRunningEV); //don't forget to unblock YAMN + // return (DWORD)-1; //ok, but we should return value. + // //When our plugin returns e.g. 0xFFFFFFFF (=-1, this is only our plugin value, YAMN does nothing with return value, + // //but only tests if it is nonzero. If yes, it calls GetErrorStringFcn. We know problem occured in YAMN incompatibility + // //and then we can in our GetErrorStringFcn e.g. return string "Uncompatible version of YAMN". + // } + + ActualAccount = (HPOP3ACCOUNT)WhichTemp->AccountParam; //copy address of structure from calling thread to stack of this thread + YAMNParam = WhichTemp->BrowserParam; + CheckFlags = WhichTemp->Flags; +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"CheckPOP3:Incrementing \"using threads\" %x (account %x)\n",ActualAccount->UsingThreads,ActualAccount); +#endif + SCInc(ActualAccount->UsingThreads); + //Unblock YAMN, signal that we have copied all parameters from YAMN thread stack + if (INVALID_HANDLE_VALUE != WhichTemp->ThreadRunningEV) + SetEvent(WhichTemp->ThreadRunningEV); + +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"CheckPOP3:ActualAccountSO-read wait\n"); +#endif + if (WAIT_OBJECT_0 != WaitToRead(ActualAccount)) + { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"CheckPOP3:ActualAccountSO-read wait failed\n"); +#endif +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"CheckPOP3:Decrementing \"using threads\" %x (account %x)\n",ActualAccount->UsingThreads,ActualAccount); +#endif + SCDec(ActualAccount->UsingThreads); + return 0; + } +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"CheckPOP3:ActualAccountSO-read enter\n"); +#endif + MyClient = &ActualAccount->Client; + //Now, copy all needed information about account to local variables, so ActualAccount is not blocked in read mode during all connection process, which can last for several minutes. + ActualCopied.ServerName = _strdup(ActualAccount->Server->Name); + ActualCopied.ServerPort = ActualAccount->Server->Port; + ActualCopied.Flags = ActualAccount->Flags; + ActualCopied.ServerLogin = _strdup(ActualAccount->Server->Login); + ActualCopied.ServerPasswd = _strdup(ActualAccount->Server->Passwd); + ActualCopied.NFlags = ActualAccount->NewMailN.Flags; + ActualCopied.NNFlags = ActualAccount->NoNewMailN.Flags; + +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"CheckPOP3:ActualAccountSO-read done\n"); +#endif + ReadDone(ActualAccount); + + SCInc(ActualAccount->InternetQueries); //increment counter, that there is one more thread waiting for connection +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"CheckPOP3:InternetFreeEV-wait\n"); +#endif + WaitForSingleObject(ActualAccount->UseInternetFree, INFINITE); //wait until we can use connection +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"CheckPOP3:InternetFreeEV-enter\n"); +#endif + SCDec(ActualAccount->InternetQueries); + + //OK, we enter the "use internet" section. But after we start communication, we can test if we did not enter the "use internet" section only for the reason, + //that previous thread release the internet section because this account has stop signal (we stop account and there are 2 threads: one communicating, + //the second one waiting for network access- the first one ends because we want to stop account, this one is released, but should be stopped as well). + if (!ActualAccount->AbleToWork) + { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"CheckPOP3:stop signal-InternetFreeEV-done\n"); +#endif + SetEvent(ActualAccount->UseInternetFree); +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"CheckPOP3:stop signal-Decrementing \"using threads\" %x (account %x)\n",ActualAccount->UsingThreads,ActualAccount); +#endif + SCDec(ActualAccount->UsingThreads); + return 0; + } + UsingInternet = TRUE; + + GetLocalTime(&now); + ActualAccount->SystemError = 0; //now we can use internet for this socket. First, clear errorcode. + try + { + SetContactStatus(ActualAccount, ID_STATUS_OCCUPIED); +#ifdef DEBUG_COMM + DebugLog(CommFile,"<--------Communication-------->\n"); +#endif + // if we are already connected, we have open session (another thread left us open session), so we don't need to login + // note that connected state without logging cannot occur, because if we close session, we always close socket too (we must close socket is the right word :)) + if ((MyClient->NetClient == nullptr) || !MyClient->NetClient->Connected()) + { + SetAccountStatus(ActualAccount, TranslateT("Connecting to server")); + + DataRX = MyClient->Connect(ActualCopied.ServerName, ActualCopied.ServerPort, ActualCopied.Flags & YAMN_ACC_SSL23, ActualCopied.Flags & YAMN_ACC_NOTLS); + char *timestamp = nullptr; + + if (DataRX != nullptr) + { + if (ActualCopied.Flags & YAMN_ACC_APOP) + { + char *lpos = strchr(DataRX, '<'); + char *rpos = strchr(DataRX, '>'); + if (lpos && rpos && rpos > lpos) { + int sz = (int)(rpos - lpos + 2); + timestamp = new char[sz]; + memcpy(timestamp, lpos, sz - 1); + timestamp[sz - 1] = '\0'; + } + } + free(DataRX); + DataRX = nullptr; + } + + SetAccountStatus(ActualAccount, TranslateT("Entering POP3 account")); + + if (ActualCopied.Flags & YAMN_ACC_APOP) + { + DataRX = MyClient->APOP(ActualCopied.ServerLogin, ActualCopied.ServerPasswd, timestamp); + if (DataRX != nullptr) + free(DataRX); + DataRX = nullptr; + delete[] timestamp; + } + else { + DataRX = MyClient->User(ActualCopied.ServerLogin); + if (DataRX != nullptr) + free(DataRX); + DataRX = nullptr; + DataRX = MyClient->Pass(ActualCopied.ServerPasswd); + if (DataRX != nullptr) + free(DataRX); + DataRX = nullptr; + } + } + SetAccountStatus(ActualAccount, TranslateT("Searching for new mail message")); + + DataRX = MyClient->Stat(); + +#ifdef DEBUG_DECODE + DebugLog(DecodeFile,"<--------Account checking-------->\n"); + DebugLog(DecodeFile,"<Extracting stat>\n"); +#endif + ExtractStat(DataRX, &mboxsize, &msgs); +#ifdef DEBUG_DECODE + DebugLog(DecodeFile,"<MailBoxSize>%d</MailBoxSize>\n",mboxsize); + DebugLog(DecodeFile,"<Msgs>%d</Msgs>\n",msgs); + DebugLog(DecodeFile,"</Extracting stat>\n"); +#endif + if (DataRX != nullptr) + free(DataRX); + DataRX = nullptr; + for (i = 0; i < msgs; i++) + { + if (!i) + MsgQueuePtr = NewMails = (HYAMNMAIL)CallService(MS_YAMN_CREATEACCOUNTMAIL, (WPARAM)ActualAccount, (LPARAM)YAMN_MAILVERSION); + else + { + MsgQueuePtr->Next = (HYAMNMAIL)CallService(MS_YAMN_CREATEACCOUNTMAIL, (WPARAM)ActualAccount, (LPARAM)YAMN_MAILVERSION); + MsgQueuePtr = MsgQueuePtr->Next; + } + if (MsgQueuePtr == nullptr) + { + ActualAccount->SystemError = EPOP3_QUEUEALLOC; + throw (DWORD)ActualAccount->SystemError; + } + } + + if (msgs) + { + DataRX = MyClient->List(); +#ifdef DEBUG_DECODE + DebugLog(DecodeFile,"<Extracting list>\n"); +#endif + ExtractList(DataRX, MyClient->NetClient->Rcv, NewMails); +#ifdef DEBUG_DECODE + DebugLog(DecodeFile,"</Extracting list>\n"); +#endif + if (DataRX != nullptr) + free(DataRX); + DataRX = nullptr; + +#ifdef DEBUG_DECODE + DebugLog(DecodeFile,"<Extracting UIDL>\n"); +#endif + DataRX = MyClient->Uidl(); + ExtractUIDL(DataRX, MyClient->NetClient->Rcv, NewMails); +#ifdef DEBUG_DECODE + DebugLog(DecodeFile,"</Extracting UIDL>\n"); +#endif + if (DataRX != nullptr) + free(DataRX); + DataRX = nullptr; + } +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"CheckPOP3:ActualAccountMsgsSO-write wait\n"); +#endif + if (WAIT_OBJECT_0 != MsgsWaitToWrite(ActualAccount)) + { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"CheckPOP3:ActualAccountMsgsSO-write wait failed\n"); +#endif + throw (DWORD)(ActualAccount->SystemError = EACC_STOPPED); + } +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"CheckPOP3:ActualAccountMsgsSO-write enter\n"); +#endif + ActualAccount->LastChecked = now; + for (MsgQueuePtr = (HYAMNMAIL)ActualAccount->Mails; MsgQueuePtr != nullptr; MsgQueuePtr = MsgQueuePtr->Next) { + if (MsgQueuePtr->Flags&YAMN_MSG_BODYREQUESTED) { + HYAMNMAIL NewMsgsPtr = nullptr; + for (NewMsgsPtr = (HYAMNMAIL)NewMails; NewMsgsPtr != nullptr; NewMsgsPtr = NewMsgsPtr->Next) { + if (!mir_strcmp(MsgQueuePtr->ID, NewMsgsPtr->ID)) { + wchar_t accstatus[512]; + mir_snwprintf(accstatus, TranslateT("Reading body %s"), NewMsgsPtr->ID); + SetAccountStatus(ActualAccount, accstatus); + DataRX = MyClient->Top(MsgQueuePtr->Number, 100); +#ifdef DEBUG_DECODE + DebugLog(DecodeFile,"<Reading body>\n"); + DebugLog(DecodeFile,"<Header>%s</Header>\n",DataRX); +#endif + if (DataRX != nullptr) + { + Temp = DataRX; + while ((Temp < DataRX + MyClient->NetClient->Rcv) && (WS(Temp) || ENDLINE(Temp))) Temp++; + + if (OKLINE(DataRX)) + for (Temp = DataRX; (Temp < DataRX + MyClient->NetClient->Rcv) && (!ENDLINE(Temp)); Temp++); + while ((Temp < DataRX + MyClient->NetClient->Rcv) && ENDLINE(Temp)) Temp++; + } + else + continue; + //delete all the headers of the old mail MsgQueuePtr->MailData->TranslatedHeader + struct CMimeItem *TH = MsgQueuePtr->MailData->TranslatedHeader; + if (TH) for (; MsgQueuePtr->MailData->TranslatedHeader != nullptr;) + { + TH = TH->Next; + if (MsgQueuePtr->MailData->TranslatedHeader->name != nullptr) + delete[] MsgQueuePtr->MailData->TranslatedHeader->name; + if (MsgQueuePtr->MailData->TranslatedHeader->value != nullptr) + delete[] MsgQueuePtr->MailData->TranslatedHeader->value; + delete MsgQueuePtr->MailData->TranslatedHeader; + MsgQueuePtr->MailData->TranslatedHeader = TH; + } + + TranslateHeader(Temp, MyClient->NetClient->Rcv - (Temp - DataRX), &MsgQueuePtr->MailData->TranslatedHeader); + + +#ifdef DEBUG_DECODE + DebugLog(DecodeFile,"</Reading body>\n"); +#endif + MsgQueuePtr->Flags |= YAMN_MSG_BODYRECEIVED; + + if (DataRX != nullptr) + free(DataRX); + DataRX = nullptr; + break; + } + } + } + } + + SynchroMessages(ActualAccount, (HYAMNMAIL *)&ActualAccount->Mails, nullptr, (HYAMNMAIL *)&NewMails, nullptr); //we get only new mails on server! + // NewMails=NULL; + +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"CheckPOP3:ActualAccountMsgsSO-write done\n"); +#endif + MsgsWriteDone(ActualAccount); + for (MsgQueuePtr = (HYAMNMAIL)ActualAccount->Mails; MsgQueuePtr != nullptr; MsgQueuePtr = MsgQueuePtr->Next) { + if ((MsgQueuePtr->Flags&YAMN_MSG_BODYREQUESTED) && (MsgQueuePtr->Flags&YAMN_MSG_BODYRECEIVED)) { + MsgQueuePtr->Flags &= ~YAMN_MSG_BODYREQUESTED; + if (MsgQueuePtr->MsgWindow) + SendMessage(MsgQueuePtr->MsgWindow, WM_YAMN_CHANGECONTENT, 0, 0); + } + } + + for (msgs = 0, MsgQueuePtr = NewMails; MsgQueuePtr != nullptr; MsgQueuePtr = MsgQueuePtr->Next, msgs++); //get number of new mails + + try + { + wchar_t accstatus[512]; + + for (i = 0, MsgQueuePtr = NewMails; MsgQueuePtr != nullptr; i++) + { + BOOL autoretr = (ActualAccount->Flags & YAMN_ACC_BODY) != 0; + DataRX = MyClient->Top(MsgQueuePtr->Number, autoretr ? 100 : 0); + mir_snwprintf(accstatus, TranslateT("Reading new mail messages (%d%% done)"), 100 * i / msgs); + SetAccountStatus(ActualAccount, accstatus); + +#ifdef DEBUG_DECODE + DebugLog(DecodeFile,"<New mail>\n"); + DebugLog(DecodeFile,"<Header>%s</Header>\n",DataRX); +#endif + if (DataRX != nullptr) + { + Temp = DataRX; + while ((Temp < DataRX + MyClient->NetClient->Rcv) && (WS(Temp) || ENDLINE(Temp))) Temp++; + + if (OKLINE(DataRX)) + for (Temp = DataRX; (Temp < DataRX + MyClient->NetClient->Rcv) && (!ENDLINE(Temp)); Temp++); + while ((Temp < DataRX + MyClient->NetClient->Rcv) && ENDLINE(Temp)) Temp++; + } + else + continue; + + TranslateHeader(Temp, MyClient->NetClient->Rcv - (Temp - DataRX), &MsgQueuePtr->MailData->TranslatedHeader); + + +#ifdef DEBUG_DECODE + DebugLog(DecodeFile,"</New mail>\n"); +#endif + MsgQueuePtr->Flags |= YAMN_MSG_NORMALNEW; + if (autoretr) MsgQueuePtr->Flags |= YAMN_MSG_BODYRECEIVED; + + //We are going to filter mail. Warning!- we must not be in read access neither write access to mails when calling this service + //This is done, because the "NewMails" queue is not synchronised. It is because it is new queue. Only this thread uses new queue yet, it is not + //connected to account mail queue. + // CallService(MS_YAMN_FILTERMAIL,(WPARAM)ActualAccount,(LPARAM)MsgQueuePtr); + FilterMailSvc((WPARAM)ActualAccount, (LPARAM)MsgQueuePtr); + + if (DataRX != nullptr) + free(DataRX); + DataRX = nullptr; + + //MsgQueuePtr->MailData->Body=MyClient->Retr(MsgQueuePtr->Number); + + MsgQueuePtr = MsgQueuePtr->Next; + + } +#ifdef DEBUG_DECODE + DebugLog(DecodeFile,"</--------Account checking-------->\n"); +#endif + +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"CheckPOP3:ActualAccountMsgsSO-write wait\n"); +#endif + if (WAIT_OBJECT_0 != MsgsWaitToWrite(ActualAccount)) + { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"CheckPOP3:ActualAccountMsgsSO-write wait failed\n"); +#endif + throw (DWORD)ActualAccount->SystemError == EACC_STOPPED; + } +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"CheckPOP3:ActualAccountMsgsSO-write enter\n"); +#endif + if (ActualAccount->Mails == nullptr) + ActualAccount->Mails = NewMails; + else + { + ActualAccount->LastMail = ActualAccount->LastChecked; + AppendQueue((HYAMNMAIL)ActualAccount->Mails, NewMails); + } +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"CheckPOP3:ActualAccountMsgsSO-write done\n"); +#endif + MsgsWriteDone(ActualAccount); + + // we are going to delete mails having SPAM flag level3 and 4 (see m_mails.h) set + { + struct DeleteParam ParamToDeleteMails = { YAMN_DELETEVERSION, INVALID_HANDLE_VALUE, ActualAccount, YAMNParam, (void *)POP3_DELETEFROMCHECK }; + + // Delete mails from server. Here we should not be in write access for account's mails + DeleteMailsPOP3(&ParamToDeleteMails); + } + + // if there is no waiting thread for internet connection close it + // else leave connection open + if (0 == SCGetNumber(ActualAccount->InternetQueries)) + { + DataRX = MyClient->Quit(); + if (DataRX != nullptr) + free(DataRX); + DataRX = nullptr; + MyClient->NetClient->Disconnect(); + + SetAccountStatus(ActualAccount, TranslateT("Disconnected")); + } + + UsingInternet = FALSE; +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"CheckPOP3:InternetFreeEV-done\n"); +#endif + SetEvent(ActualAccount->UseInternetFree); + + ActualAccount->LastSChecked = ActualAccount->LastChecked; + ActualAccount->LastSynchronised = ActualAccount->LastChecked; + } + catch (...) + { + throw; //go to the main exception handling + } + + { + YAMN_MAILBROWSERPARAM Param = { (HANDLE)nullptr, ActualAccount, ActualCopied.NFlags, ActualCopied.NNFlags, YAMNParam }; + + if (CheckFlags & YAMN_FORCECHECK) + Param.nnflags |= YAMN_ACC_POP; //if force check, show popup anyway and if mailbrowser was opened, do not close + Param.nnflags |= YAMN_ACC_MSGP; //do not close browser if already open + CallService(MS_YAMN_MAILBROWSER, (WPARAM)&Param, (LPARAM)YAMN_MAILBROWSERVERSION); + } + SetContactStatus(ActualAccount, ActualAccount->isCounting ? ID_STATUS_ONLINE : ID_STATUS_OFFLINE); + } +#ifdef DEBUG_COMM + catch(DWORD ErrorCode) +#else + catch (DWORD) +#endif + { + if (ActualAccount->Client.POP3Error == EPOP3_STOPPED) + ActualAccount->SystemError = EACC_STOPPED; +#ifdef DEBUG_COMM + DebugLog(CommFile,"ERROR: %x\n",ErrorCode); +#endif +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"CheckPOP3:ActualAccountMsgsSO-write wait\n"); +#endif + if (WAIT_OBJECT_0 == MsgsWaitToWrite(ActualAccount)) + { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"CheckPOP3:ActualAccountMsgsSO-write enter\n"); +#endif + ActualAccount->LastChecked = now; +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"CheckPOP3:ActualAccountMsgsSO-write done\n"); +#endif + MsgsWriteDone(ActualAccount); + } +#ifdef DEBUG_SYNCHRO + else + DebugLog(SynchroFile,"CheckPOP3:ActualAccountMsgsSO-write wait failed\n"); +#endif + + DeleteMIMEQueue(ActualAccount, NewMails); + + if (DataRX != nullptr) + free(DataRX); + DataRX = nullptr; + switch (ActualAccount->SystemError) + { + case EACC_QUEUEALLOC: + case EACC_STOPPED: + ActualAccount->Client.NetClient->Disconnect(); + break; + default: + PostErrorProc(ActualAccount, YAMNParam, (DWORD)NULL, MyClient->SSL); //it closes internet connection too + } + + if (UsingInternet) //if our thread still uses internet + { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"CheckPOP3:InternetFreeEV-done\n"); +#endif + SetEvent(ActualAccount->UseInternetFree); + } + SetContactStatus(ActualAccount, ID_STATUS_NA); + } + free(ActualCopied.ServerName); + free(ActualCopied.ServerLogin); + free(ActualCopied.ServerPasswd); +#ifdef DEBUG_COMM + DebugLog(CommFile,"</--------Communication-------->\n"); +#endif + // WriteAccounts(); +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"CheckPOP3:Decrementing \"using threads\" %x (account %x)\n",ActualAccount->UsingThreads,ActualAccount); +#endif + SCDec(ActualAccount->UsingThreads); + return 0; +} + +void __cdecl DeleteMailsPOP3(void *param) +{ + DeleteParam *WhichTemp = (DeleteParam *)param; + + CPop3Client *MyClient; + HYAMNMAIL DeleteMails, NewMails = nullptr, MsgQueuePtr = nullptr; + char* DataRX = nullptr; + int mboxsize = 0, msgs = 0, i; + BOOL UsingInternet = FALSE; + struct { + char *ServerName; + DWORD ServerPort; + char *ServerLogin; + char *ServerPasswd; + DWORD Flags; + DWORD NFlags; + DWORD NNFlags; + } ActualCopied; + + //First, we should compare our version of DeleteParam structure, but here it is not needed, because YAMN and internal plugin + //have the same version. But your plugin should do that in this way: + // if (((struct DeleteParam *)WhichTemp)->Ver != YAMN_DELETEVERSION) + // { + // SetEvent(((struct DeleteParam *)WhichTemp)->ThreadRunningEV); //don't forget to unblock YAMN + // return (DWORD)-1; //ok, but we should return value. + // //When our plugin returns e.g. 0xFFFFFFFF (this is only our plugin value, YAMN does nothing with return value, + // //but only tests if it is nonzero. If yes, it calls GetErrorStringFcn), we know problem occured in YAMN incompatibility + // //and then we can in our GetErrorStringFcn e.g. return string "Uncompatible version of YAMN". + // } + + HPOP3ACCOUNT ActualAccount = (HPOP3ACCOUNT)WhichTemp->AccountParam; //copy address of structure from calling thread to stack of this thread + LPVOID YAMNParam = WhichTemp->BrowserParam; + UINT_PTR POP3PluginParam = (UINT_PTR)((struct DeleteParam *)WhichTemp)->CustomParam; +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"DeleteMailsPOP3:Incrementing \"using threads\" %x (account %x)\n",ActualAccount->UsingThreads,ActualAccount); +#endif + SCInc(ActualAccount->UsingThreads); + if (INVALID_HANDLE_VALUE != WhichTemp->ThreadRunningEV) + SetEvent(WhichTemp->ThreadRunningEV); + +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"DeleteMailsPOP3:ActualAccountSO-read wait\n"); +#endif + if (WAIT_OBJECT_0 != WaitToRead(ActualAccount)) + { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"DeleteMailsPOP3:ActualAccountSO-read wait failed\n"); +#endif +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"DeleteMailsPOP3:Decrementing \"using threads\" %x (account %x)\n",ActualAccount->UsingThreads,ActualAccount); +#endif + SCDec(ActualAccount->UsingThreads); + return; + } +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"DeleteMailsPOP3:ActualAccountSO-read enter\n"); +#endif + if (nullptr == (DeleteMails = (HYAMNMAIL)CreateNewDeleteQueue((HYAMNMAIL)ActualAccount->Mails))) //if there's no mail for deleting, return + { + if (POP3_DELETEFROMCHECK != POP3PluginParam) //We do not wait for free internet when calling from SynchroPOP3. It is because UseInternetFree is blocked + { + YAMN_MAILBROWSERPARAM Param = { (HANDLE)nullptr, ActualAccount, YAMN_ACC_MSGP, YAMN_ACC_MSGP, YAMNParam }; //Just update the window + + CallService(MS_YAMN_MAILBROWSER, (WPARAM)&Param, (LPARAM)YAMN_MAILBROWSERVERSION); + } +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"DeleteMailsPOP3:ActualAccountSO-read done\n"); +#endif + ReadDone(ActualAccount); +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"DeleteMailsPOP3:Decrementing \"using threads\" %x (account %x)\n",ActualAccount->UsingThreads,ActualAccount); +#endif + SCDec(ActualAccount->UsingThreads); + + return; + } + MyClient = &(ActualAccount->Client); + + //Now, copy all needed information about account to local variables, so ActualAccount is not blocked in read mode during all connection process, which can last for several minutes. + ActualCopied.ServerName = _strdup(ActualAccount->Server->Name); + ActualCopied.ServerPort = ActualAccount->Server->Port; + ActualCopied.Flags = ActualAccount->Flags; + ActualCopied.ServerLogin = _strdup(ActualAccount->Server->Login); + ActualCopied.ServerPasswd = _strdup(ActualAccount->Server->Passwd); + ActualCopied.NFlags = ActualAccount->NewMailN.Flags; + ActualCopied.NNFlags = ActualAccount->NoNewMailN.Flags; + +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"DeleteMailsPOP3:ActualAccountSO-read done\n"); +#endif + ReadDone(ActualAccount); + + SCInc(ActualAccount->InternetQueries); //This is POP3-internal SCOUNTER, we set another thread wait for this account to be connected to inet + if (POP3_DELETEFROMCHECK != POP3PluginParam) //We do not wait for free internet when calling from SynchroPOP3. It is because UseInternetFree is blocked + { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"DeleteMailsPOP3:InternetFreeEV-wait\n"); +#endif + WaitForSingleObject(ActualAccount->UseInternetFree, INFINITE); +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"DeleteMailsPOP3:InternetFreeEV-enter\n"); +#endif + } + SCDec(ActualAccount->InternetQueries); + UsingInternet = TRUE; + + try + { + SetContactStatus(ActualAccount, ID_STATUS_OCCUPIED); +#ifdef DEBUG_COMM + DebugLog(CommFile,"<--------Communication-------->\n"); +#endif + if ((MyClient->NetClient == nullptr) || !MyClient->NetClient->Connected()) + { + SetAccountStatus(ActualAccount, TranslateT("Connecting to server")); + + DataRX = MyClient->Connect(ActualCopied.ServerName, ActualCopied.ServerPort, ActualCopied.Flags & YAMN_ACC_SSL23, ActualCopied.Flags & YAMN_ACC_NOTLS); + + char *timestamp = nullptr; + if (DataRX != nullptr) { + if (ActualAccount->Flags & YAMN_ACC_APOP) { + char *lpos = strchr(DataRX, '<'); + char *rpos = strchr(DataRX, '>'); + if (lpos && rpos && rpos > lpos) { + int sz = (int)(rpos - lpos + 2); + timestamp = new char[sz]; + memcpy(timestamp, lpos, sz - 1); + timestamp[sz - 1] = '\0'; + } + } + free(DataRX); + DataRX = nullptr; + } + SetAccountStatus(ActualAccount, TranslateT("Entering POP3 account")); + + if (ActualAccount->Flags & YAMN_ACC_APOP) + { + DataRX = MyClient->APOP(ActualCopied.ServerLogin, ActualCopied.ServerPasswd, timestamp); + if (DataRX != nullptr) + free(DataRX); + DataRX = nullptr; + delete[] timestamp; + } + else { + DataRX = MyClient->User(ActualCopied.ServerLogin); + if (DataRX != nullptr) + free(DataRX); + DataRX = nullptr; + DataRX = MyClient->Pass(ActualCopied.ServerPasswd); + if (DataRX != nullptr) + free(DataRX); + DataRX = nullptr; + } + } + +#ifdef DEBUG_DECODE + DebugLog(DecodeFile,"<--------Deleting requested mails-------->\n"); +#endif + if (POP3_DELETEFROMCHECK != POP3PluginParam) //We do not need to get mails on server as we have already it from check function + { + SetAccountStatus(ActualAccount, TranslateT("Deleting requested mails")); + + DataRX = MyClient->Stat(); + +#ifdef DEBUG_DECODE + DebugLog(DecodeFile,"<Extracting stat>\n"); +#endif + ExtractStat(DataRX, &mboxsize, &msgs); +#ifdef DEBUG_DECODE + DebugLog(DecodeFile,"<MailBoxSize>%d</MailBoxSize>\n",mboxsize); + DebugLog(DecodeFile,"<Msgs>%d</Msgs>\n",msgs); + DebugLog(DecodeFile,"</Extracting stat>\n"); +#endif + if (DataRX != nullptr) + free(DataRX); + DataRX = nullptr; + for (i = 0; i < msgs; i++) + { + if (!i) + MsgQueuePtr = NewMails = (HYAMNMAIL)CallService(MS_YAMN_CREATEACCOUNTMAIL, (WPARAM)ActualAccount, (LPARAM)YAMN_MAILVERSION); + else + { + MsgQueuePtr->Next = (HYAMNMAIL)CallService(MS_YAMN_CREATEACCOUNTMAIL, (WPARAM)ActualAccount, (LPARAM)YAMN_MAILVERSION); + MsgQueuePtr = MsgQueuePtr->Next; + } + if (MsgQueuePtr == nullptr) + { + ActualAccount->SystemError = EPOP3_QUEUEALLOC; + throw (DWORD)ActualAccount->SystemError; + } + } + + if (msgs) + { +#ifdef DEBUG_DECODE + DebugLog(DecodeFile,"<Extracting UIDL>\n"); +#endif + DataRX = MyClient->Uidl(); + ExtractUIDL(DataRX, MyClient->NetClient->Rcv, NewMails); +#ifdef DEBUG_DECODE + DebugLog(DecodeFile,"</Extracting UIDL>\n"); +#endif + if (DataRX != nullptr) + free(DataRX); + DataRX = nullptr; + // we get "new mails" on server (NewMails will contain all mails on server not found in DeleteMails) + // but also in DeleteMails we get only those, which are still on server with their responsable numbers + SynchroMessages(ActualAccount, (HYAMNMAIL *)&DeleteMails, nullptr, (HYAMNMAIL *)&NewMails, nullptr); + } + } + else + SetAccountStatus(ActualAccount, TranslateT("Deleting spam")); +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"DeleteMailsPOP3:ActualAccountMsgsSO-write wait\n"); +#endif + if (WAIT_OBJECT_0 != MsgsWaitToWrite(ActualAccount)) + { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"DeleteMailsPOP3:ActualAccountMsgsSO-write wait failed\n"); +#endif + throw (DWORD)EACC_STOPPED; + } +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"DeleteMailsPOP3:ActualAccountMsgsSO-write enter\n"); +#endif + if (msgs || POP3_DELETEFROMCHECK == POP3PluginParam) + { + try + { + HYAMNMAIL Temp; + + for (i = 0, MsgQueuePtr = DeleteMails; MsgQueuePtr != nullptr; i++) + { + if (!(MsgQueuePtr->Flags & YAMN_MSG_VIRTUAL)) //of course we can only delete real mails, not virtual + { + DataRX = MyClient->Dele(MsgQueuePtr->Number); + Temp = MsgQueuePtr->Next; + if (POP3_FOK == MyClient->AckFlag) //if server answers that mail was deleted + { + DeleteMIMEMessage((HYAMNMAIL *)&DeleteMails, MsgQueuePtr); + HYAMNMAIL DeletedMail = FindMIMEMessageByID((HYAMNMAIL)ActualAccount->Mails, MsgQueuePtr->ID); + if ((MsgQueuePtr->Flags & YAMN_MSG_MEMDELETE)) //if mail should be deleted from memory (or disk) + { + DeleteMIMEMessage((HYAMNMAIL *)&ActualAccount->Mails, DeletedMail); //remove from queue + CallService(MS_YAMN_DELETEACCOUNTMAIL, (WPARAM)POP3Plugin, (LPARAM)DeletedMail); + } + else //else mark it only as "deleted mail" + { + DeletedMail->Flags |= (YAMN_MSG_VIRTUAL | YAMN_MSG_DELETED); + DeletedMail->Flags &= ~(YAMN_MSG_NEW | YAMN_MSG_USERDELETE | YAMN_MSG_AUTODELETE); //clear "new mail" + } + delete MsgQueuePtr->MailData; + delete[] MsgQueuePtr->ID; + delete MsgQueuePtr; + } + MsgQueuePtr = Temp; + + if (DataRX != nullptr) + free(DataRX); + DataRX = nullptr; + } + else + MsgQueuePtr = MsgQueuePtr->Next; + } + } + catch (...) //if any exception in the code where we have write-access to account occured, don't forget to leave write-access + { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"DeleteMailsPOP3:ActualAccountMsgsSO-write done\n"); +#endif + MsgsWriteDone(ActualAccount); + throw; //and go to the main exception handling + } + + if (NewMails != nullptr) + // in ActualAccount->Mails we have all mails stored before calling this function + // in NewMails we have all mails not found in DeleteMails (in other words: we performed new ID checking and we + // stored all mails found on server, then we deleted the ones we wanted to delete in this function + // and NewMails queue now contains actual state of mails on server). But we will not use NewMails as actual state, because NewMails does not contain header data (subject, from...) + // We perform deleting from ActualAccount->Mails: we remove from original queue (ActualAccount->Mails) all deleted mails + SynchroMessages(ActualAccount, (HYAMNMAIL *)&ActualAccount->Mails, nullptr, (HYAMNMAIL *)&NewMails, nullptr); + // Now ActualAccount->Mails contains all mails when calling this function except the ones, we wanted to delete (these are in DeleteMails) + // And in NewMails we have new mails (if any) + else if (POP3_DELETEFROMCHECK != POP3PluginParam) + { + DeleteMIMEQueue(ActualAccount, (HYAMNMAIL)ActualAccount->Mails); + ActualAccount->Mails = nullptr; + } + } + else + { + DeleteMIMEQueue(ActualAccount, (HYAMNMAIL)ActualAccount->Mails); + ActualAccount->Mails = nullptr; + } +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"DeleteMailsPOP3:ActualAccountMsgsSO-write done\n"); +#endif + MsgsWriteDone(ActualAccount); +#ifdef DEBUG_DECODE + DebugLog(DecodeFile,"</--------Deleting requested mails-------->\n"); +#endif + + // TODO: now, we have in NewMails new mails. If NewMails is not NULL, we found some new mails, so Checking for new mail should be performed + // now, we do not call CheckPOP3 + + // if there is no waiting thread for internet connection close it + // else leave connection open + // if this functin was called from SynchroPOP3, then do not try to disconnect + if (POP3_DELETEFROMCHECK != POP3PluginParam) + { + YAMN_MAILBROWSERPARAM Param = { (HANDLE)nullptr, ActualAccount, ActualCopied.NFlags, YAMN_ACC_MSGP, YAMNParam }; + + CallService(MS_YAMN_MAILBROWSER, (WPARAM)&Param, (LPARAM)YAMN_MAILBROWSERVERSION); + + if (0 == SCGetNumber(ActualAccount->InternetQueries)) + { + DataRX = MyClient->Quit(); + if (DataRX != nullptr) + free(DataRX); + DataRX = nullptr; + MyClient->NetClient->Disconnect(); + + SetAccountStatus(ActualAccount, TranslateT("Disconnected")); + } + + UsingInternet = FALSE; +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"DeleteMailsPOP3:InternetFreeEV-done\n"); +#endif + SetEvent(ActualAccount->UseInternetFree); + } + SetContactStatus(ActualAccount, ActualAccount->isCounting ? ID_STATUS_ONLINE : ID_STATUS_OFFLINE); + } +#ifdef DEBUG_COMM + catch(DWORD ErrorCode) +#else + catch (DWORD) +#endif + { + if (ActualAccount->Client.POP3Error == EPOP3_STOPPED) + ActualAccount->SystemError = EACC_STOPPED; +#ifdef DEBUG_COMM + DebugLog(CommFile,"ERROR %x\n",ErrorCode); +#endif + if (DataRX != nullptr) + free(DataRX); + switch (ActualAccount->SystemError) + { + case EACC_QUEUEALLOC: + case EACC_STOPPED: + ActualAccount->Client.NetClient->Disconnect(); + break; + default: + PostErrorProc(ActualAccount, YAMNParam, POP3PluginParam, MyClient->SSL); //it closes internet connection too + } + + if (UsingInternet && (POP3_DELETEFROMCHECK != POP3PluginParam)) //if our thread still uses internet and it is needed to release internet + { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"CheckPOP3:InternetFreeEV-done\n"); +#endif + SetEvent(ActualAccount->UseInternetFree); + } + } + + free(ActualCopied.ServerName); + free(ActualCopied.ServerLogin); + free(ActualCopied.ServerPasswd); + + DeleteMIMEQueue(ActualAccount, NewMails); + DeleteMIMEQueue(ActualAccount, DeleteMails); + +#ifdef DEBUG_COMM + DebugLog(CommFile,"</--------Communication-------->\n"); +#endif + // WriteAccounts(); +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"DeleteMailsPOP3:Decrementing \"using threads\" %x (account %x)\n",ActualAccount->UsingThreads,ActualAccount); +#endif + SCDec(ActualAccount->UsingThreads); + return; +} + +void ExtractStat(char *stream, int *mboxsize, int *mails) +{ + char *finder = stream; + while (WS(finder) || ENDLINE(finder)) finder++; + if (ACKLINE(finder)) + { + while (!WS(finder)) finder++; + while (WS(finder)) finder++; + } + if (1 != sscanf(finder, "%d", mails)) + throw (DWORD)EPOP3_STAT; + while (!WS(finder)) finder++; + while (WS(finder)) finder++; + if (1 != sscanf(finder, "%d", mboxsize)) + throw (DWORD)EPOP3_STAT; +} +void ExtractMail(char *stream, int len, HYAMNMAIL queue) +{ + char *finder = stream; + char *finderend; + int msgnr, i; + HYAMNMAIL queueptr = queue; + + while (WS(finder) || ENDLINE(finder)) finder++; + while (!ACKLINE(finder)) finder++; + while (!ENDLINE(finder)) finder++; //now we at the end of first ack line + while (finder <= (stream + len)) + { + while (ENDLINE(finder)) finder++; //go to the new line + if (DOTLINE(finder + 1)) //at the end of stream + break; +#ifdef DEBUG_DECODE + DebugLog(DecodeFile,"<Message>\n"); +#endif + while (WS(finder)) finder++; //jump whitespace + if (1 != sscanf(finder, "%d", &msgnr)) + throw (DWORD)EPOP3_UIDL; +#ifdef DEBUG_DECODE + DebugLog(DecodeFile,"<Nr>%d</Nr>\n",msgnr); +#endif + // for (i=1,queueptr=queue;(queueptr->Next != NULL) && (i<msgnr);queueptr=queueptr->Next,i++); + // if (i != msgnr) + // throw (DWORD)EPOP3_UIDL; + while (!WS(finder)) finder++; //jump characters + while (WS(finder)) finder++; //jump whitespace + finderend = finder + 1; + while (!WS(finderend) && !ENDLINE(finderend)) finderend++; + queueptr->ID = new char[finderend - finder + 1]; + for (i = 0; finder != finderend; finder++, i++) + queueptr->MailData->Body[i] = *finder; + queueptr->MailData->Body[i] = 0; //ends string + queueptr->Number = msgnr; +#ifdef DEBUG_DECODE + DebugLog(DecodeFile,"<ID>%s</ID>\n",queueptr->MailData->Body); + DebugLog(DecodeFile,"</Message>\n"); +#endif + queueptr = queueptr->Next; + while (!ENDLINE(finder)) finder++; + } +} + +void ExtractUIDL(char *stream, int len, HYAMNMAIL queue) +{ + char *finder = stream; + char *finderend; + int msgnr, i; + HYAMNMAIL queueptr = queue; + + while (WS(finder) || ENDLINE(finder)) finder++; + while (!ACKLINE(finder)) finder++; + while (!ENDLINE(finder)) finder++; //now we at the end of first ack line + while (finder <= (stream + len)) + { + while (ENDLINE(finder)) finder++; //go to the new line + if (DOTLINE(finder + 1)) //at the end of stream + break; +#ifdef DEBUG_DECODE + DebugLog(DecodeFile,"<Message>\n"); +#endif + while (WS(finder)) finder++; //jump whitespace + if (1 != sscanf(finder, "%d", &msgnr)) + throw (DWORD)EPOP3_UIDL; +#ifdef DEBUG_DECODE + DebugLog(DecodeFile,"<Nr>%d</Nr>\n",msgnr); +#endif + // for (i=1,queueptr=queue;(queueptr->Next != NULL) && (i<msgnr);queueptr=queueptr->Next,i++); + // if (i != msgnr) + // throw (DWORD)EPOP3_UIDL; + while (!WS(finder)) finder++; //jump characters + while (WS(finder)) finder++; //jump whitespace + finderend = finder + 1; + while (!WS(finderend) && !ENDLINE(finderend)) finderend++; + queueptr->ID = new char[finderend - finder + 1]; + for (i = 0; finder != finderend; finder++, i++) + queueptr->ID[i] = *finder; + queueptr->ID[i] = 0; //ends string + queueptr->Number = msgnr; +#ifdef DEBUG_DECODE + DebugLog(DecodeFile,"<ID>%s</ID>\n",queueptr->ID); + DebugLog(DecodeFile,"</Message>\n"); +#endif + queueptr = queueptr->Next; + while (!ENDLINE(finder)) finder++; + } +} + +void ExtractList(char *stream, int len, HYAMNMAIL queue) +{ + char *finder = stream; + char *finderend; + int msgnr, i; + HYAMNMAIL queueptr; + + while (WS(finder) || ENDLINE(finder)) finder++; + while (!ACKLINE(finder)) finder++; + while (!ENDLINE(finder)) finder++; //now we at the end of first ack line + while (finder <= (stream + len)) + { + while (ENDLINE(finder)) finder++; //go to the new line + if (DOTLINE(finder + 1)) //at the end of stream + break; +#ifdef DEBUG_DECODE + DebugLog(DecodeFile,"<Message>\n",NULL,0); +#endif + while (WS(finder)) finder++; //jump whitespace + if (1 != sscanf(finder, "%d", &msgnr)) //message nr. + throw (DWORD)EPOP3_LIST; +#ifdef DEBUG_DECODE + DebugLog(DecodeFile,"<Nr>%d</Nr>\n",msgnr); +#endif + + for (i = 1, queueptr = queue; (queueptr->Next != nullptr) && (i < msgnr); queueptr = queueptr->Next, i++); + if (i != msgnr) + throw (DWORD)EPOP3_LIST; + while (!WS(finder)) finder++; //jump characters + while (WS(finder)) finder++; //jump whitespace + finderend = finder + 1; + if (1 != sscanf(finder, "%d", &queueptr->MailData->Size)) + throw (DWORD)EPOP3_LIST; +#ifdef DEBUG_DECODE + DebugLog(DecodeFile,"<Nr>%d</Nr>\n",queueptr->MailData->Size); +#endif + while (!ENDLINE(finder)) finder++; + } +} + +wchar_t* WINAPI GetErrorString(DWORD Code) +{ + static wchar_t *POP3Errors[] = + { + LPGENW("Memory allocation error."), //memory allocation + LPGENW("Account is about to be stopped."), //stop account + LPGENW("Cannot connect to POP3 server."), + LPGENW("Cannot allocate memory for received data."), + LPGENW("Cannot login to POP3 server."), + LPGENW("Bad user or password."), + LPGENW("Server does not support APOP authorization."), + LPGENW("Error while executing POP3 command."), + LPGENW("Error while executing POP3 command."), + LPGENW("Error while executing POP3 command."), + }; + + static wchar_t *NetlibErrors[] = + { + LPGENW("Cannot connect to server with NetLib."), + LPGENW("Cannot send data."), + LPGENW("Cannot receive data."), + LPGENW("Cannot allocate memory for received data."), + }; + + static wchar_t *SSLErrors[] = + { + LPGENW("OpenSSL not loaded."), + LPGENW("Windows socket 2.0 init failed."), + LPGENW("DNS lookup error."), + LPGENW("Error while creating base socket."), + LPGENW("Error connecting to server with socket."), + LPGENW("Error while creating SSL structure."), + LPGENW("Error connecting socket with SSL."), + LPGENW("Server rejected connection with SSL."), + LPGENW("Cannot write SSL data."), + LPGENW("Cannot read SSL data."), + LPGENW("Cannot allocate memory for received data."), + }; + + wchar_t *ErrorString = new wchar_t[ERRORSTR_MAXLEN]; + POP3_ERRORCODE *ErrorCode = (POP3_ERRORCODE *)(UINT_PTR)Code; + + mir_snwprintf(ErrorString, ERRORSTR_MAXLEN, TranslateT("Error %d-%d-%d-%d:"), ErrorCode->AppError, ErrorCode->POP3Error, ErrorCode->NetError, ErrorCode->SystemError); + if (ErrorCode->POP3Error) + mir_snwprintf(ErrorString, ERRORSTR_MAXLEN, L"%s\n%s", ErrorString, TranslateW(POP3Errors[ErrorCode->POP3Error - 1])); + if (ErrorCode->NetError) { + if (ErrorCode->SSL) + mir_snwprintf(ErrorString, ERRORSTR_MAXLEN, L"%s\n%s", ErrorString, TranslateW(SSLErrors[ErrorCode->NetError - 1])); + else + mir_snwprintf(ErrorString, ERRORSTR_MAXLEN, L"%s\n%s", ErrorString, TranslateW(NetlibErrors[ErrorCode->NetError - 4])); + } + + return ErrorString; +} + +void WINAPI DeleteErrorString(LPVOID String) +{ + delete (char *)String; +} diff --git a/protocols/YAMN/src/proto/pop3/pop3comm.h b/protocols/YAMN/src/proto/pop3/pop3comm.h new file mode 100644 index 0000000000..3d98675d3c --- /dev/null +++ b/protocols/YAMN/src/proto/pop3/pop3comm.h @@ -0,0 +1,83 @@ +#ifndef __POP3COMM_H +#define __POP3COMM_H + +#define POP3_FILEVERSION 1 //Version of aditional information stored in book file + +typedef struct CPOP3Account: public CAccount +{ +// We can use SCOUNTER structure, because this is internal plugin. +// This SO is used to determine if any POP3 account is in "write access" mode + static PSCOUNTER AccountWriterSO; + +// It is usefull to have client structure in account. With this structure we have access to account's socket. +// This is related to InternetQueries and UseInternetFree +// This member should be synchronized with UseInternetFree + class CPop3Client Client; + +// This member is usefull for MIME headers. It is default codepage, if no other codepage found + WORD CP; //access only through AccountAccessSO + +// In this memeber last error code is stored + DWORD SystemError; //access through UseInternetFree + +// We use only counter from this object and it is # of threads waiting to work on internet. +// We use event UseInternet to access critical sections. +// It is usefull in 2 ways: we have mutual exclusion that only one thread works with account on internet. +// Thread, which has done its work with account on internet can close socket, but it is not needed, when any other +// thread wants to work (e.g. we have deleted mails, but when deleting, another thread wants to check new mail, so +// we delete all needed mails and check if there's thread that wants to work. If yes, we do not need to quit session, +// we leave socket open, and leave internet. Another thread then start checking and does not connect, does not send +// user and password... because socket is open- it continues) + PSCOUNTER InternetQueries; + HANDLE UseInternetFree; + + CPOP3Account(); + ~CPOP3Account(); + +} POP3ACCOUNT,*HPOP3ACCOUNT; + +typedef struct POP3LayeredError +{ + BOOL SSL; + DWORD AppError; + DWORD POP3Error; + DWORD NetError; + DWORD SystemError; +} POP3_ERRORCODE,*PPOP3_ERRORCODE; + +struct YAMNExportedFcns +{ + YAMN_SETPROTOCOLPLUGINFCNIMPORTFCN SetProtocolPluginFcnImportFcn; + YAMN_WAITTOWRITEFCN WaitToWriteFcn; + YAMN_WRITEDONEFCN WriteDoneFcn; + YAMN_WAITTOREADFCN WaitToReadFcn; + YAMN_READDONEFCN ReadDoneFcn; + YAMN_SCMANAGEFCN SCGetNumberFcn; + YAMN_SCMANAGEFCN SCIncFcn; + YAMN_SCMANAGEFCN SCDecFcn; + YAMN_SETSTATUSFCN SetStatusFcn; + YAMN_GETSTATUSFCN GetStatusFcn; +}; + +struct MailExportedFcns +{ + YAMN_SYNCHROMIMEMSGSFCN SynchroMessagesFcn; + YAMN_TRANSLATEHEADERFCN TranslateHeaderFcn; + YAMN_APPENDQUEUEFCN AppendQueueFcn; + YAMN_DELETEMIMEQUEUEFCN DeleteMessagesToEndFcn; + YAMN_DELETEMIMEMESSAGEFCN DeleteMessageFromQueueFcn; + YAMN_FINDMIMEMESSAGEFCN FindMessageByIDFcn; + YAMN_CREATENEWDELETEQUEUEFCN CreateNewDeleteQueueFcn; +}; + +enum +{ + EACC_QUEUEALLOC=1, //memory allocation + EACC_STOPPED, //stop account +}; + +#define NO_MAIL_FOR_DELETE 1 + +#define POP3_DELETEFROMCHECK 1 + +#endif diff --git a/protocols/YAMN/src/proto/pop3/pop3opt.cpp b/protocols/YAMN/src/proto/pop3/pop3opt.cpp new file mode 100644 index 0000000000..b22b237e47 --- /dev/null +++ b/protocols/YAMN/src/proto/pop3/pop3opt.cpp @@ -0,0 +1,1481 @@ +/* + * This code implements POP3 options window handling + * + * (c) majvan 2002-2003 +*/ + +#include "../../stdafx.h" + +//-------------------------------------------------------------------------------------------------- + +static BOOL Check0, Check1, Check2, Check3, Check4, Check5, Check6, Check7, Check8, Check9; +static char DlgInput[MAX_PATH]; + +void CheckMenuItems(); + +//-------------------------------------------------------------------------------------------------- + +INT_PTR CALLBACK DlgProcYAMNOpt(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + case WM_INITDIALOG: + TranslateDialogDefault(hDlg); + CheckDlgButton(hDlg, IDC_CHECKTTB, g_plugin.getByte(YAMN_TTBFCHECK, 1) ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_LONGDATE, (optDateTime&SHOWDATELONG) ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_SMARTDATE, (optDateTime&SHOWDATENOTODAY) ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_NOSECONDS, (optDateTime&SHOWDATENOSECONDS) ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_MAINMENU, g_plugin.getByte(YAMN_SHOWMAINMENU, 1) ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_YAMNASPROTO, g_plugin.getByte(YAMN_SHOWASPROTO, 1) ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CLOSEONDELETE, g_plugin.getByte(YAMN_CLOSEDELETE, 0) ? BST_CHECKED : BST_UNCHECKED); + break; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDC_YAMNASPROTO: + case IDC_MAINMENU: + case IDC_CHECKTTB: + case IDC_CLOSEONDELETE: + case IDC_LONGDATE: + case IDC_SMARTDATE: + case IDC_NOSECONDS: + SendMessage(GetParent(hDlg), PSM_CHANGED, 0, 0); + break; + } + break; + + case WM_NOTIFY: + switch (((LPNMHDR)lParam)->idFrom) { + case 0: + switch (((LPNMHDR)lParam)->code) { + case PSN_APPLY: + g_plugin.setByte(YAMN_SHOWASPROTO, IsDlgButtonChecked(hDlg, IDC_YAMNASPROTO)); + g_plugin.setByte(YAMN_SHOWMAINMENU, IsDlgButtonChecked(hDlg, IDC_MAINMENU)); + g_plugin.setByte(YAMN_CLOSEDELETE, IsDlgButtonChecked(hDlg, IDC_CLOSEONDELETE)); + g_plugin.setByte(YAMN_TTBFCHECK, IsDlgButtonChecked(hDlg, IDC_CHECKTTB)); + + AddTopToolbarIcon(0, 0); + CheckMenuItems(); + + optDateTime = 0; + if (IsDlgButtonChecked(hDlg, IDC_LONGDATE))optDateTime |= SHOWDATELONG; + if (IsDlgButtonChecked(hDlg, IDC_SMARTDATE))optDateTime |= SHOWDATENOTODAY; + if (IsDlgButtonChecked(hDlg, IDC_NOSECONDS))optDateTime |= SHOWDATENOSECONDS; + g_plugin.setByte(YAMN_DBTIMEOPTIONS, optDateTime); + } + } + break; + } + + return FALSE; +} + +INT_PTR CALLBACK DlgProcPluginOpt(HWND hDlg, UINT msg, WPARAM wParam, LPARAM) +{ + switch (msg) { + case WM_INITDIALOG: + TranslateDialogDefault(hDlg); + break; + + case WM_COMMAND: + { + WORD wNotifyCode = HIWORD(wParam); + switch (LOWORD(wParam)) { + case IDC_COMBOPLUGINS: + if (wNotifyCode == CBN_SELCHANGE) { + HWND hCombo = GetDlgItem(hDlg, IDC_COMBOPLUGINS); + PYAMN_PROTOPLUGINQUEUE PParser; + PYAMN_FILTERPLUGINQUEUE FParser; + int index, id; + + if (CB_ERR == (index = SendMessage(hCombo, CB_GETCURSEL, 0, 0))) + break; + id = SendMessage(hCombo, CB_GETITEMDATA, (WPARAM)index, 0); + + mir_cslock lck(PluginRegCS); + for (PParser = FirstProtoPlugin; PParser != nullptr; PParser = PParser->Next) + if (id == (INT_PTR)PParser->Plugin) { + SetDlgItemTextA(hDlg, IDC_STVER, PParser->Plugin->PluginInfo->Ver); + SetDlgItemTextA(hDlg, IDC_STDESC, PParser->Plugin->PluginInfo->Description == nullptr ? "" : PParser->Plugin->PluginInfo->Description); + SetDlgItemTextA(hDlg, IDC_STCOPY, PParser->Plugin->PluginInfo->Copyright == nullptr ? "" : PParser->Plugin->PluginInfo->Copyright); + SetDlgItemTextA(hDlg, IDC_STWWW, PParser->Plugin->PluginInfo->WWW == nullptr ? "" : PParser->Plugin->PluginInfo->WWW); + break; + } + for (FParser = FirstFilterPlugin; FParser != nullptr; FParser = FParser->Next) + if (id == (INT_PTR)FParser->Plugin) { + SetDlgItemTextA(hDlg, IDC_STVER, FParser->Plugin->PluginInfo->Ver); + SetDlgItemTextA(hDlg, IDC_STDESC, FParser->Plugin->PluginInfo->Description == nullptr ? "" : FParser->Plugin->PluginInfo->Description); + SetDlgItemTextA(hDlg, IDC_STCOPY, FParser->Plugin->PluginInfo->Copyright == nullptr ? "" : FParser->Plugin->PluginInfo->Copyright); + SetDlgItemTextA(hDlg, IDC_STWWW, FParser->Plugin->PluginInfo->WWW == nullptr ? "" : FParser->Plugin->PluginInfo->WWW); + break; + } + } + break; + case IDC_STWWW: + { + char str[1024]; + GetDlgItemTextA(hDlg, IDC_STWWW, str, _countof(str)); + Utils_OpenUrl(str); + break; + } + + } + break; + } + case WM_SHOWWINDOW: + if (TRUE == (BOOL)wParam) { + { + mir_cslock lck(PluginRegCS); + for (PYAMN_PROTOPLUGINQUEUE PParser = FirstProtoPlugin; PParser != nullptr; PParser = PParser->Next) { + int index = SendDlgItemMessageA(hDlg, IDC_COMBOPLUGINS, CB_ADDSTRING, 0, (LPARAM)PParser->Plugin->PluginInfo->Name); + SendDlgItemMessage(hDlg, IDC_COMBOPLUGINS, CB_SETITEMDATA, (WPARAM)index, (LPARAM)PParser->Plugin); + } + for (PYAMN_FILTERPLUGINQUEUE FParser = FirstFilterPlugin; FParser != nullptr; FParser = FParser->Next) { + int index = SendDlgItemMessageA(hDlg, IDC_COMBOPLUGINS, CB_ADDSTRING, 0, (LPARAM)FParser->Plugin->PluginInfo->Name); + SendDlgItemMessage(hDlg, IDC_COMBOPLUGINS, CB_SETITEMDATA, (WPARAM)index, (LPARAM)FParser->Plugin); + } + } + + SendDlgItemMessage(hDlg, IDC_COMBOPLUGINS, CB_SETCURSEL, 0, 0); + SendMessage(hDlg, WM_COMMAND, MAKELONG(IDC_COMBOPLUGINS, CBN_SELCHANGE), 0); + break; + } + else { //delete all items in combobox + int cbn = SendDlgItemMessage(hDlg, IDC_COMBOPLUGINS, CB_GETCOUNT, 0, 0); + for (int i = 0; i < cbn; i++) + SendDlgItemMessage(hDlg, IDC_COMBOPLUGINS, CB_DELETESTRING, 0, 0); + break; + } + } + + return FALSE; +} + + +int YAMNOptInitSvc(WPARAM wParam, LPARAM) +{ + OPTIONSDIALOGPAGE odp = {}; + odp.szGroup.a = LPGEN("Network"); + odp.szTitle.a = LPGEN("YAMN"); + odp.flags = ODPF_BOLDGROUPS; + + odp.szTab.a = LPGEN("Accounts"); + odp.pszTemplate = MAKEINTRESOURCEA(IDD_POP3ACCOUNTOPT); + odp.pfnDlgProc = DlgProcPOP3AccOpt; + g_plugin.addOptions(wParam, &odp); + + odp.szTab.a = LPGEN("General"); + odp.pszTemplate = MAKEINTRESOURCEA(IDD_YAMNOPT); + odp.pfnDlgProc = DlgProcYAMNOpt; + g_plugin.addOptions(wParam, &odp); + + odp.szTab.a = LPGEN("Plugins"); + odp.pszTemplate = MAKEINTRESOURCEA(IDD_PLUGINOPT); + odp.pfnDlgProc = DlgProcPluginOpt; + g_plugin.addOptions(wParam, &odp); + + if (ServiceExists(MS_POPUP_ADDPOPUPW)) { + odp.szGroup.a = LPGEN("Popups"); + odp.szTab.a = LPGEN("YAMN"); + odp.pszTemplate = MAKEINTRESOURCEA(IDD_POP3ACCOUNTPOPUP); + odp.pfnDlgProc = DlgProcPOP3AccPopup; + g_plugin.addOptions(wParam, &odp); + } + return 0; +} + +//-------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------- +BOOL DlgEnableAccountStatus(HWND hDlg, WPARAM wParam, LPARAM) +{ + EnableWindow(GetDlgItem(hDlg, IDC_CHECKST0), (BOOL)wParam); + EnableWindow(GetDlgItem(hDlg, IDC_CHECKST1), (BOOL)wParam); + EnableWindow(GetDlgItem(hDlg, IDC_CHECKST2), (BOOL)wParam); + EnableWindow(GetDlgItem(hDlg, IDC_CHECKST3), (BOOL)wParam); + EnableWindow(GetDlgItem(hDlg, IDC_CHECKST4), (BOOL)wParam); + EnableWindow(GetDlgItem(hDlg, IDC_CHECKST5), (BOOL)wParam); + EnableWindow(GetDlgItem(hDlg, IDC_CHECKST6), (BOOL)wParam); + EnableWindow(GetDlgItem(hDlg, IDC_CHECKST7), (BOOL)wParam); + EnableWindow(GetDlgItem(hDlg, IDC_CHECKST8), (BOOL)wParam); + EnableWindow(GetDlgItem(hDlg, IDC_CHECKST9), (BOOL)wParam); + EnableWindow(GetDlgItem(hDlg, IDC_CHECKST9), (BOOL)wParam); + return TRUE; +} + +BOOL DlgEnableAccountPopup(HWND hDlg, WPARAM wParam, LPARAM) +{ + EnableWindow(GetDlgItem(hDlg, IDC_CHECKPOP), (BOOL)wParam); + EnableWindow(GetDlgItem(hDlg, IDC_EDITPOPS), (IsDlgButtonChecked(hDlg, IDC_CHECKPOP) == BST_CHECKED) && wParam); + EnableWindow(GetDlgItem(hDlg, IDC_CHECKCOL), (IsDlgButtonChecked(hDlg, IDC_CHECKPOP) == BST_CHECKED) && wParam); + EnableWindow(GetDlgItem(hDlg, IDC_CPB), (IsDlgButtonChecked(hDlg, IDC_CHECKCOL) == BST_CHECKED) && (IsDlgButtonChecked(hDlg, IDC_CHECKPOP) == BST_CHECKED) && wParam); + EnableWindow(GetDlgItem(hDlg, IDC_CPT), (IsDlgButtonChecked(hDlg, IDC_CHECKCOL) == BST_CHECKED) && (IsDlgButtonChecked(hDlg, IDC_CHECKPOP) == BST_CHECKED) && wParam); + EnableWindow(GetDlgItem(hDlg, IDC_RADIOPOPN), (IsDlgButtonChecked(hDlg, IDC_CHECKPOP) == BST_CHECKED) && wParam); + EnableWindow(GetDlgItem(hDlg, IDC_RADIOPOP1), (IsDlgButtonChecked(hDlg, IDC_CHECKPOP) == BST_CHECKED) && wParam); + EnableWindow(GetDlgItem(hDlg, IDC_CHECKNPOP), (BOOL)wParam); + EnableWindow(GetDlgItem(hDlg, IDC_EDITNPOPS), (IsDlgButtonChecked(hDlg, IDC_CHECKNPOP) == BST_CHECKED) && wParam); + EnableWindow(GetDlgItem(hDlg, IDC_CHECKNCOL), (IsDlgButtonChecked(hDlg, IDC_CHECKNPOP) == BST_CHECKED) && wParam); + EnableWindow(GetDlgItem(hDlg, IDC_CPNB), (IsDlgButtonChecked(hDlg, IDC_CHECKNCOL) == BST_CHECKED) && (IsDlgButtonChecked(hDlg, IDC_CHECKNPOP) == BST_CHECKED) && wParam); + EnableWindow(GetDlgItem(hDlg, IDC_CPNT), (IsDlgButtonChecked(hDlg, IDC_CHECKNCOL) == BST_CHECKED) && (IsDlgButtonChecked(hDlg, IDC_CHECKNPOP) == BST_CHECKED) && wParam); + EnableWindow(GetDlgItem(hDlg, IDC_CHECKFPOP), (BOOL)wParam); + EnableWindow(GetDlgItem(hDlg, IDC_EDITFPOPS), (IsDlgButtonChecked(hDlg, IDC_CHECKFPOP) == BST_CHECKED) && wParam); + EnableWindow(GetDlgItem(hDlg, IDC_CHECKFCOL), (IsDlgButtonChecked(hDlg, IDC_CHECKFPOP) == BST_CHECKED) && wParam); + EnableWindow(GetDlgItem(hDlg, IDC_CPFB), (IsDlgButtonChecked(hDlg, IDC_CHECKFCOL) == BST_CHECKED) && (IsDlgButtonChecked(hDlg, IDC_CHECKFPOP) == BST_CHECKED) && wParam); + EnableWindow(GetDlgItem(hDlg, IDC_CPFT), (IsDlgButtonChecked(hDlg, IDC_CHECKFCOL) == BST_CHECKED) && (IsDlgButtonChecked(hDlg, IDC_CHECKFPOP) == BST_CHECKED) && wParam); + EnableWindow(GetDlgItem(hDlg, IDC_CHECKAPOP), (BOOL)wParam); + return TRUE; +} + +BOOL DlgEnableAccount(HWND hDlg, WPARAM wParam, LPARAM) +{ + EnableWindow(GetDlgItem(hDlg, IDC_CHECK), (BOOL)wParam); + EnableWindow(GetDlgItem(hDlg, IDC_EDITSERVER), wParam); + EnableWindow(GetDlgItem(hDlg, IDC_EDITNAME), wParam); + EnableWindow(GetDlgItem(hDlg, IDC_EDITPORT), (BOOL)wParam); + EnableWindow(GetDlgItem(hDlg, IDC_EDITLOGIN), (BOOL)wParam); + EnableWindow(GetDlgItem(hDlg, IDC_EDITPASS), (BOOL)wParam); + EnableWindow(GetDlgItem(hDlg, IDC_EDITINTERVAL), (BOOL)wParam); + EnableWindow(GetDlgItem(hDlg, IDC_CHECKSND), (BOOL)wParam); + EnableWindow(GetDlgItem(hDlg, IDC_CHECKMSG), (BOOL)wParam); + EnableWindow(GetDlgItem(hDlg, IDC_CHECKICO), (BOOL)wParam); + EnableWindow(GetDlgItem(hDlg, IDC_CHECKAPP), (BOOL)wParam); + EnableWindow(GetDlgItem(hDlg, IDC_CHECKKBN), (BOOL)wParam); + EnableWindow(GetDlgItem(hDlg, IDC_BTNAPP), (IsDlgButtonChecked(hDlg, IDC_CHECKAPP) == BST_CHECKED) && wParam); + EnableWindow(GetDlgItem(hDlg, IDC_EDITAPP), (IsDlgButtonChecked(hDlg, IDC_CHECKAPP) == BST_CHECKED) && wParam); + EnableWindow(GetDlgItem(hDlg, IDC_EDITAPPPARAM), (IsDlgButtonChecked(hDlg, IDC_CHECKAPP) == BST_CHECKED) && wParam); + EnableWindow(GetDlgItem(hDlg, IDC_CHECKNMSGP), (BOOL)wParam); + EnableWindow(GetDlgItem(hDlg, IDC_CHECKFSND), (BOOL)wParam); + EnableWindow(GetDlgItem(hDlg, IDC_CHECKFMSG), (BOOL)wParam); + EnableWindow(GetDlgItem(hDlg, IDC_CHECKFICO), (BOOL)wParam); + /*EnableWindow(GetDlgItem(hDlg,IDC_CHECKST0),(BOOL)wParam); + EnableWindow(GetDlgItem(hDlg,IDC_CHECKST1),(BOOL)wParam); + EnableWindow(GetDlgItem(hDlg,IDC_CHECKST2),(BOOL)wParam); + EnableWindow(GetDlgItem(hDlg,IDC_CHECKST3),(BOOL)wParam); + EnableWindow(GetDlgItem(hDlg,IDC_CHECKST4),(BOOL)wParam); + EnableWindow(GetDlgItem(hDlg,IDC_CHECKST5),(BOOL)wParam); + EnableWindow(GetDlgItem(hDlg,IDC_CHECKST6),(BOOL)wParam); + EnableWindow(GetDlgItem(hDlg,IDC_CHECKST7),(BOOL)wParam); + EnableWindow(GetDlgItem(hDlg,IDC_CHECKST8),(BOOL)wParam); + EnableWindow(GetDlgItem(hDlg,IDC_CHECKST9),(BOOL)wParam); + EnableWindow(GetDlgItem(hDlg,IDC_CHECKST9),(BOOL)wParam);*/ + EnableWindow(GetDlgItem(hDlg, IDC_CHECKSTART), (BOOL)wParam); + EnableWindow(GetDlgItem(hDlg, IDC_CHECKFORCE), (BOOL)wParam); + EnableWindow(GetDlgItem(hDlg, IDC_COMBOCP), (BOOL)wParam); + EnableWindow(GetDlgItem(hDlg, IDC_STTIMELEFT), (BOOL)wParam); + EnableWindow(GetDlgItem(hDlg, IDC_BTNRESET), (BOOL)wParam); + EnableWindow(GetDlgItem(hDlg, IDC_BTNDEFAULT), (BOOL)wParam); + EnableWindow(GetDlgItem(hDlg, IDC_BTNSTATUS), (BOOL)wParam); + EnableWindow(GetDlgItem(hDlg, IDC_CHECKSSL), (BOOL)wParam); + EnableWindow(GetDlgItem(hDlg, IDC_CHECKNOTLS), (IsDlgButtonChecked(hDlg, IDC_CHECKSSL) == BST_UNCHECKED) && wParam); + EnableWindow(GetDlgItem(hDlg, IDC_AUTOBODY), (BOOL)wParam); + EnableWindow(GetDlgItem(hDlg, IDC_CHECKCONTACT), (BOOL)wParam); + EnableWindow(GetDlgItem(hDlg, IDC_CHECKCONTACTNICK), (IsDlgButtonChecked(hDlg, IDC_CHECKCONTACT) == BST_CHECKED) && wParam); + EnableWindow(GetDlgItem(hDlg, IDC_CHECKCONTACTNOEVENT), (IsDlgButtonChecked(hDlg, IDC_CHECKCONTACT) == BST_CHECKED) && wParam); + return TRUE; +} +BOOL DlgShowAccountStatus(HWND hDlg, WPARAM wParam, LPARAM lParam) +{ + HPOP3ACCOUNT ActualAccount = (HPOP3ACCOUNT)lParam; + + if ((DWORD)wParam == M_SHOWACTUAL) { + #ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "Options:SHOWACCOUNT:ActualAccountSO-read wait\n"); + #endif + WaitToRead(ActualAccount); //we do not need to check if account is deleted. It is not deleted, because only thread that can delete account is this thread + #ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "Options:SHOWACCOUNT:ActualAccountSO-read enter\n"); + #endif + CheckDlgButton(hDlg, IDC_CHECKST0, ActualAccount->StatusFlags & YAMN_ACC_ST0 ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKST1, ActualAccount->StatusFlags & YAMN_ACC_ST1 ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKST2, ActualAccount->StatusFlags & YAMN_ACC_ST2 ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKST3, ActualAccount->StatusFlags & YAMN_ACC_ST3 ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKST4, ActualAccount->StatusFlags & YAMN_ACC_ST4 ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKST5, ActualAccount->StatusFlags & YAMN_ACC_ST5 ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKST6, ActualAccount->StatusFlags & YAMN_ACC_ST6 ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKST7, ActualAccount->StatusFlags & YAMN_ACC_ST7 ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKST8, ActualAccount->StatusFlags & YAMN_ACC_ST8 ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKST9, ActualAccount->StatusFlags & YAMN_ACC_ST9 ? BST_CHECKED : BST_UNCHECKED); + ReadDone(ActualAccount); + } + else { + CheckDlgButton(hDlg, IDC_CHECKST0, BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKST1, BST_CHECKED); + CheckDlgButton(hDlg, IDC_CHECKST2, BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKST3, BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKST4, BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKST5, BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKST6, BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKST7, BST_CHECKED); + CheckDlgButton(hDlg, IDC_CHECKST8, BST_CHECKED); + CheckDlgButton(hDlg, IDC_CHECKST9, BST_CHECKED); + } + return TRUE; +} +BOOL DlgShowAccountPopup(HWND hDlg, WPARAM wParam, LPARAM lParam) +{ + HPOP3ACCOUNT ActualAccount = (HPOP3ACCOUNT)lParam; + + if ((DWORD)wParam == M_SHOWACTUAL) { + #ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "Options:SHOWACCOUNT:ActualAccountSO-read wait\n"); + #endif + WaitToRead(ActualAccount); //we do not need to check if account is deleted. It is not deleted, because only thread that can delete account is this thread + #ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "Options:SHOWACCOUNT:ActualAccountSO-read enter\n"); + #endif + SetDlgItemInt(hDlg, IDC_EDITPOPS, ActualAccount->NewMailN.PopupTime, FALSE); + SetDlgItemInt(hDlg, IDC_EDITNPOPS, ActualAccount->NoNewMailN.PopupTime, FALSE); + SetDlgItemInt(hDlg, IDC_EDITFPOPS, ActualAccount->BadConnectN.PopupTime, FALSE); + + + CheckDlgButton(hDlg, IDC_CHECKPOP, ActualAccount->NewMailN.Flags & YAMN_ACC_POP ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKCOL, ActualAccount->NewMailN.Flags & YAMN_ACC_POPC ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKNPOP, ActualAccount->NoNewMailN.Flags & YAMN_ACC_POP ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKNCOL, ActualAccount->NoNewMailN.Flags & YAMN_ACC_POPC ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKFPOP, ActualAccount->BadConnectN.Flags & YAMN_ACC_POP ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKFCOL, ActualAccount->BadConnectN.Flags & YAMN_ACC_POPC ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_RADIOPOPN, ActualAccount->Flags & YAMN_ACC_POPN ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_RADIOPOP1, ActualAccount->Flags & YAMN_ACC_POPN ? BST_UNCHECKED : BST_CHECKED); + #ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "Options:SHOWACCOUNT:ActualAccountSO-read done\n"); + #endif + ReadDone(ActualAccount); + } + else //default + { + + SetDlgItemInt(hDlg, IDC_EDITPOPS, 0, FALSE); + SetDlgItemInt(hDlg, IDC_EDITNPOPS, 0, FALSE); + SetDlgItemInt(hDlg, IDC_EDITFPOPS, 0, FALSE); + CheckDlgButton(hDlg, IDC_CHECKPOP, BST_CHECKED); + CheckDlgButton(hDlg, IDC_CHECKCOL, BST_CHECKED); + CheckDlgButton(hDlg, IDC_CHECKNPOP, BST_CHECKED); + CheckDlgButton(hDlg, IDC_CHECKNCOL, BST_CHECKED); + CheckDlgButton(hDlg, IDC_CHECKFPOP, BST_CHECKED); + CheckDlgButton(hDlg, IDC_CHECKFCOL, BST_CHECKED); + CheckDlgButton(hDlg, IDC_RADIOPOPN, BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_RADIOPOP1, BST_CHECKED); + } + return TRUE; +} +BOOL DlgShowAccount(HWND hDlg, WPARAM wParam, LPARAM lParam) +{ + HPOP3ACCOUNT ActualAccount = (HPOP3ACCOUNT)lParam; + int i; + + if ((DWORD)wParam == M_SHOWACTUAL) { + wchar_t accstatus[256]; + #ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "Options:SHOWACCOUNT:ActualAccountSO-read wait\n"); + #endif + WaitToRead(ActualAccount); //we do not need to check if account is deleted. It is not deleted, because only thread that can delete account is this thread + #ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "Options:SHOWACCOUNT:ActualAccountSO-read enter\n"); + #endif + DlgSetItemText(hDlg, IDC_EDITSERVER, ActualAccount->Server->Name); + DlgSetItemText(hDlg, IDC_EDITNAME, ActualAccount->Name); + DlgSetItemText(hDlg, IDC_EDITLOGIN, ActualAccount->Server->Login); + DlgSetItemText(hDlg, IDC_EDITPASS, ActualAccount->Server->Passwd); + DlgSetItemTextW(hDlg, IDC_EDITAPP, ActualAccount->NewMailN.App); + DlgSetItemTextW(hDlg, IDC_EDITAPPPARAM, ActualAccount->NewMailN.AppParam); + SetDlgItemInt(hDlg, IDC_EDITPORT, ActualAccount->Server->Port, FALSE); + SetDlgItemInt(hDlg, IDC_EDITINTERVAL, ActualAccount->Interval / 60, FALSE); + SetDlgItemInt(hDlg, IDC_EDITPOPS, ActualAccount->NewMailN.PopupTime, FALSE); + SetDlgItemInt(hDlg, IDC_EDITNPOPS, ActualAccount->NoNewMailN.PopupTime, FALSE); + SetDlgItemInt(hDlg, IDC_EDITFPOPS, ActualAccount->BadConnectN.PopupTime, FALSE); + for (i = 0; i <= CPLENSUPP; i++) + if ((i < CPLENSUPP) && (CodePageNamesSupp[i].CP == ActualAccount->CP)) { + SendDlgItemMessage(hDlg, IDC_COMBOCP, CB_SETCURSEL, (WPARAM)i, 0); + break; + } + if (i == CPLENSUPP) + SendDlgItemMessage(hDlg, IDC_COMBOCP, CB_SETCURSEL, (WPARAM)CPDEFINDEX, 0); + + CheckDlgButton(hDlg, IDC_CHECK, ActualAccount->Flags & YAMN_ACC_ENA ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKSND, ActualAccount->NewMailN.Flags & YAMN_ACC_SND ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKMSG, ActualAccount->NewMailN.Flags & YAMN_ACC_MSG ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKICO, ActualAccount->NewMailN.Flags & YAMN_ACC_ICO ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKPOP, ActualAccount->NewMailN.Flags & YAMN_ACC_POP ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKCOL, ActualAccount->NewMailN.Flags & YAMN_ACC_POPC ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKAPP, ActualAccount->NewMailN.Flags & YAMN_ACC_APP ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKKBN, ActualAccount->NewMailN.Flags & YAMN_ACC_KBN ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKNPOP, ActualAccount->NoNewMailN.Flags & YAMN_ACC_POP ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKNCOL, ActualAccount->NoNewMailN.Flags & YAMN_ACC_POPC ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKNMSGP, ActualAccount->NoNewMailN.Flags & YAMN_ACC_MSGP ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKFSND, ActualAccount->BadConnectN.Flags & YAMN_ACC_SND ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKFMSG, ActualAccount->BadConnectN.Flags & YAMN_ACC_MSG ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKFICO, ActualAccount->BadConnectN.Flags & YAMN_ACC_ICO ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKFPOP, ActualAccount->BadConnectN.Flags & YAMN_ACC_POP ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKFCOL, ActualAccount->BadConnectN.Flags & YAMN_ACC_POPC ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_RADIOPOPN, ActualAccount->Flags & YAMN_ACC_POPN ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_RADIOPOP1, ActualAccount->Flags & YAMN_ACC_POPN ? BST_UNCHECKED : BST_CHECKED); + CheckDlgButton(hDlg, IDC_CHECKSSL, ActualAccount->Flags & YAMN_ACC_SSL23 ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKNOTLS, ActualAccount->Flags & YAMN_ACC_NOTLS ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKAPOP, ActualAccount->Flags & YAMN_ACC_APOP ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_AUTOBODY, ActualAccount->Flags & YAMN_ACC_BODY ? BST_CHECKED : BST_UNCHECKED); + /*CheckDlgButton(hDlg,IDC_CHECKST0,ActualAccount->StatusFlags & YAMN_ACC_ST0 ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg,IDC_CHECKST1,ActualAccount->StatusFlags & YAMN_ACC_ST1 ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg,IDC_CHECKST2,ActualAccount->StatusFlags & YAMN_ACC_ST2 ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg,IDC_CHECKST3,ActualAccount->StatusFlags & YAMN_ACC_ST3 ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg,IDC_CHECKST4,ActualAccount->StatusFlags & YAMN_ACC_ST4 ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg,IDC_CHECKST5,ActualAccount->StatusFlags & YAMN_ACC_ST5 ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg,IDC_CHECKST6,ActualAccount->StatusFlags & YAMN_ACC_ST6 ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg,IDC_CHECKST7,ActualAccount->StatusFlags & YAMN_ACC_ST7 ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg,IDC_CHECKST8,ActualAccount->StatusFlags & YAMN_ACC_ST8 ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg,IDC_CHECKST9,ActualAccount->StatusFlags & YAMN_ACC_ST9 ? BST_CHECKED : BST_UNCHECKED);*/ + Check0 = ActualAccount->StatusFlags & YAMN_ACC_ST0; + Check1 = ActualAccount->StatusFlags & YAMN_ACC_ST1; + Check2 = ActualAccount->StatusFlags & YAMN_ACC_ST2; + Check3 = ActualAccount->StatusFlags & YAMN_ACC_ST3; + Check4 = ActualAccount->StatusFlags & YAMN_ACC_ST4; + Check5 = ActualAccount->StatusFlags & YAMN_ACC_ST5; + Check6 = ActualAccount->StatusFlags & YAMN_ACC_ST6; + Check7 = ActualAccount->StatusFlags & YAMN_ACC_ST7; + Check8 = ActualAccount->StatusFlags & YAMN_ACC_ST8; + Check9 = ActualAccount->StatusFlags & YAMN_ACC_ST9; + CheckDlgButton(hDlg, IDC_CHECKSTART, ActualAccount->StatusFlags & YAMN_ACC_STARTS ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKFORCE, ActualAccount->StatusFlags & YAMN_ACC_FORCE ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKCONTACT, ActualAccount->NewMailN.Flags & YAMN_ACC_CONT ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKCONTACTNICK, ActualAccount->NewMailN.Flags & YAMN_ACC_CONTNICK ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKCONTACTNOEVENT, ActualAccount->NewMailN.Flags & YAMN_ACC_CONTNOEVENT ? BST_CHECKED : BST_UNCHECKED); + #ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "Options:SHOWACCOUNT:ActualAccountSO-read done\n"); + #endif + GetAccountStatus(ActualAccount, accstatus); + SetDlgItemText(hDlg, IDC_STSTATUS, accstatus); + ReadDone(ActualAccount); + } + else //default + { + DlgSetItemText(hDlg, (WPARAM)IDC_EDITSERVER, nullptr); + DlgSetItemText(hDlg, (WPARAM)IDC_EDITNAME, nullptr); + DlgSetItemText(hDlg, (WPARAM)IDC_EDITLOGIN, nullptr); + DlgSetItemText(hDlg, (WPARAM)IDC_EDITPASS, nullptr); + DlgSetItemText(hDlg, (WPARAM)IDC_EDITAPP, nullptr); + DlgSetItemText(hDlg, (WPARAM)IDC_EDITAPPPARAM, nullptr); + DlgSetItemText(hDlg, (WPARAM)IDC_STTIMELEFT, nullptr); + SetDlgItemInt(hDlg, IDC_EDITPORT, 110, FALSE); + SetDlgItemInt(hDlg, IDC_EDITINTERVAL, 30, FALSE); + SetDlgItemInt(hDlg, IDC_EDITPOPS, 0, FALSE); + SetDlgItemInt(hDlg, IDC_EDITNPOPS, 0, FALSE); + SetDlgItemInt(hDlg, IDC_EDITFPOPS, 0, FALSE); + SendDlgItemMessage(hDlg, IDC_COMBOCP, CB_SETCURSEL, (WPARAM)CPDEFINDEX, 0); + CheckDlgButton(hDlg, IDC_CHECK, BST_CHECKED); + CheckDlgButton(hDlg, IDC_CHECKSND, BST_CHECKED); + CheckDlgButton(hDlg, IDC_CHECKMSG, BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKICO, BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKPOP, BST_CHECKED); + CheckDlgButton(hDlg, IDC_CHECKCOL, BST_CHECKED); + CheckDlgButton(hDlg, IDC_CHECKAPP, BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKPOP, BST_CHECKED); + CheckDlgButton(hDlg, IDC_CHECKCOL, BST_CHECKED); + CheckDlgButton(hDlg, IDC_CHECKFSND, BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKFMSG, BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKFICO, BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKFPOP, BST_CHECKED); + CheckDlgButton(hDlg, IDC_CHECKFCOL, BST_CHECKED); + /*CheckDlgButton(hDlg,IDC_CHECKST0,BST_UNCHECKED); + CheckDlgButton(hDlg,IDC_CHECKST1,BST_CHECKED); + CheckDlgButton(hDlg,IDC_CHECKST2,BST_UNCHECKED); + CheckDlgButton(hDlg,IDC_CHECKST3,BST_UNCHECKED); + CheckDlgButton(hDlg,IDC_CHECKST4,BST_UNCHECKED); + CheckDlgButton(hDlg,IDC_CHECKST5,BST_UNCHECKED); + CheckDlgButton(hDlg,IDC_CHECKST6,BST_UNCHECKED); + CheckDlgButton(hDlg,IDC_CHECKST7,BST_CHECKED); + CheckDlgButton(hDlg,IDC_CHECKST8,BST_CHECKED); + CheckDlgButton(hDlg,IDC_CHECKST9,BST_CHECKED);*/ + CheckDlgButton(hDlg, IDC_CHECKSTART, BST_CHECKED); + CheckDlgButton(hDlg, IDC_CHECKFORCE, BST_CHECKED); + CheckDlgButton(hDlg, IDC_RADIOPOPN, BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_RADIOPOP1, BST_CHECKED); + CheckDlgButton(hDlg, IDC_CHECKSSL, BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKNOTLS, BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKAPOP, BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_AUTOBODY, BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_CHECKCONTACT, BST_CHECKED); + + SetDlgItemText(hDlg, IDC_STSTATUS, TranslateT("No account selected")); + } + return TRUE; +} + +BOOL DlgShowAccountColors(HWND hDlg, WPARAM, LPARAM lParam) +{ + HPOP3ACCOUNT ActualAccount = (HPOP3ACCOUNT)lParam; + #ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "Options:SHOWACCOUNTCOLORS:ActualAccountSO-read wait\n"); + #endif + WaitToRead(ActualAccount); //we do not need to check if account is deleted. It is not deleted, because only thread that can delete account is this thread + #ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "Options:SHOWACCOUNTCOLORS:ActualAccountSO-read enter\n"); + #endif + if (ActualAccount->NewMailN.Flags & YAMN_ACC_POPC) { + SendDlgItemMessage(hDlg, IDC_CPB, CPM_SETCOLOUR, 0, (LPARAM)ActualAccount->NewMailN.PopupB); + SendDlgItemMessage(hDlg, IDC_CPT, CPM_SETCOLOUR, 0, (LPARAM)ActualAccount->NewMailN.PopupT); + } + else { + SendDlgItemMessage(hDlg, IDC_CPB, CPM_SETCOLOUR, 0, (LPARAM)GetSysColor(COLOR_BTNFACE)); + SendDlgItemMessage(hDlg, IDC_CPT, CPM_SETCOLOUR, 0, (LPARAM)GetSysColor(COLOR_WINDOWTEXT)); + } + if (ActualAccount->BadConnectN.Flags & YAMN_ACC_POPC) { + SendDlgItemMessage(hDlg, IDC_CPFB, CPM_SETCOLOUR, 0, (LPARAM)ActualAccount->BadConnectN.PopupB); + SendDlgItemMessage(hDlg, IDC_CPFT, CPM_SETCOLOUR, 0, (LPARAM)ActualAccount->BadConnectN.PopupT); + } + else { + SendDlgItemMessage(hDlg, IDC_CPFB, CPM_SETCOLOUR, 0, (LPARAM)GetSysColor(COLOR_BTNFACE)); + SendDlgItemMessage(hDlg, IDC_CPFT, CPM_SETCOLOUR, 0, (LPARAM)GetSysColor(COLOR_WINDOWTEXT)); + } + if (ActualAccount->NoNewMailN.Flags & YAMN_ACC_POPC) { + SendDlgItemMessage(hDlg, IDC_CPNB, CPM_SETCOLOUR, 0, (LPARAM)ActualAccount->NoNewMailN.PopupB); + SendDlgItemMessage(hDlg, IDC_CPNT, CPM_SETCOLOUR, 0, (LPARAM)ActualAccount->NoNewMailN.PopupT); + } + else { + SendDlgItemMessage(hDlg, IDC_CPNB, CPM_SETCOLOUR, 0, (LPARAM)GetSysColor(COLOR_BTNFACE)); + SendDlgItemMessage(hDlg, IDC_CPNT, CPM_SETCOLOUR, 0, (LPARAM)GetSysColor(COLOR_WINDOWTEXT)); + } + #ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "Options:SHOWACCOUNTCOLORS:ActualAccountSO-read done\n"); + #endif + ReadDone(ActualAccount); //we do not need to check if account is deleted. It is not deleted, because only thread that can delete account is this thread + return TRUE; +} + +BOOL DlgSetItemText(HWND hDlg, WPARAM wParam, const char* str) +{ + if (str == nullptr) + SetDlgItemTextA(hDlg, wParam, ""); + else + SetDlgItemTextA(hDlg, wParam, str); + return TRUE; +} + +BOOL DlgSetItemTextW(HWND hDlg, WPARAM wParam, const WCHAR* str) +{ + if (str == nullptr) + SetDlgItemTextW(hDlg, wParam, L""); + else + SetDlgItemTextW(hDlg, wParam, str); + return TRUE; +} + +INT_PTR CALLBACK DlgProcPOP3AccStatusOpt(HWND hDlg, UINT msg, WPARAM wParam, LPARAM) +{ + static HPOP3ACCOUNT ActualAccount; + switch (msg) { + case WM_INITDIALOG: + ActualAccount = (HPOP3ACCOUNT)CallService(MS_YAMN_FINDACCOUNTBYNAME, (WPARAM)POP3Plugin, (LPARAM)DlgInput); + if (ActualAccount != nullptr) { + DlgShowAccountStatus(hDlg, (WPARAM)M_SHOWACTUAL, (LPARAM)ActualAccount); + DlgEnableAccountStatus(hDlg, TRUE, TRUE); + } + else { + CheckDlgButton(hDlg, IDC_CHECKST0, BST_CHECKED); + CheckDlgButton(hDlg, IDC_CHECKST1, BST_CHECKED); + CheckDlgButton(hDlg, IDC_CHECKST2, BST_CHECKED); + CheckDlgButton(hDlg, IDC_CHECKST3, BST_CHECKED); + CheckDlgButton(hDlg, IDC_CHECKST4, BST_CHECKED); + CheckDlgButton(hDlg, IDC_CHECKST5, BST_CHECKED); + CheckDlgButton(hDlg, IDC_CHECKST6, BST_CHECKED); + CheckDlgButton(hDlg, IDC_CHECKST7, BST_CHECKED); + CheckDlgButton(hDlg, IDC_CHECKST8, BST_CHECKED); + CheckDlgButton(hDlg, IDC_CHECKST9, BST_CHECKED); + } + TranslateDialogDefault(hDlg); + SendMessage(GetParent(hDlg), PSM_UNCHANGED, (WPARAM)hDlg, 0); + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: + Check0 = (IsDlgButtonChecked(hDlg, IDC_CHECKST0) == BST_CHECKED); + Check1 = (IsDlgButtonChecked(hDlg, IDC_CHECKST1) == BST_CHECKED); + Check2 = (IsDlgButtonChecked(hDlg, IDC_CHECKST2) == BST_CHECKED); + Check3 = (IsDlgButtonChecked(hDlg, IDC_CHECKST3) == BST_CHECKED); + Check4 = (IsDlgButtonChecked(hDlg, IDC_CHECKST4) == BST_CHECKED); + Check5 = (IsDlgButtonChecked(hDlg, IDC_CHECKST5) == BST_CHECKED); + Check6 = (IsDlgButtonChecked(hDlg, IDC_CHECKST6) == BST_CHECKED); + Check7 = (IsDlgButtonChecked(hDlg, IDC_CHECKST7) == BST_CHECKED); + Check8 = (IsDlgButtonChecked(hDlg, IDC_CHECKST8) == BST_CHECKED); + Check9 = (IsDlgButtonChecked(hDlg, IDC_CHECKST9) == BST_CHECKED); + WindowList_BroadcastAsync(YAMNVar.MessageWnds, WM_YAMN_CHANGESTATUSOPTION, 0, 0); + EndDialog(hDlg, 0); + DestroyWindow(hDlg); + break; + + case IDCANCEL: + EndDialog(hDlg, 0); + DestroyWindow(hDlg); + break; + } + } + return FALSE; +} + +INT_PTR CALLBACK DlgProcPOP3AccOpt(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + BOOL Changed = FALSE; + INT_PTR Result; + static BOOL InList = FALSE; + static HPOP3ACCOUNT ActualAccount; + static UCHAR ActualStatus; + // static struct CPOP3Options POP3Options; + + switch (msg) { + case WM_INITDIALOG: + { + int i; + + EnableWindow(GetDlgItem(hDlg, IDC_BTNDEL), FALSE); + + DlgEnableAccount(hDlg, FALSE, FALSE); + DlgShowAccount(hDlg, (WPARAM)M_SHOWDEFAULT, 0); + + #ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "Options:INITDIALOG:AccountBrowserSO-read wait\n"); + #endif + WaitToReadSO(POP3Plugin->AccountBrowserSO); + #ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "Options:INITDIALOG:AccountBrowserSO-read enter\n"); + #endif + + for (ActualAccount = (HPOP3ACCOUNT)POP3Plugin->FirstAccount; ActualAccount != nullptr; ActualAccount = (HPOP3ACCOUNT)ActualAccount->Next) + if (ActualAccount->Name != nullptr) + SendDlgItemMessageA(hDlg, IDC_COMBOACCOUNT, CB_ADDSTRING, 0, (LPARAM)ActualAccount->Name); + + #ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "Options:INITDIALOG:AccountBrowserSO-read done\n"); + #endif + ReadDoneSO(POP3Plugin->AccountBrowserSO); + SendDlgItemMessage(hDlg, IDC_COMBOCP, CB_ADDSTRING, 0, (LPARAM)TranslateT("Default")); + for (i = 1; i < CPLENSUPP; i++) { + CPINFOEX info; GetCPInfoEx(CodePageNamesSupp[i].CP, 0, &info); + size_t len = mir_wstrlen(info.CodePageName + 7); + info.CodePageName[len + 6] = 0; + SendDlgItemMessage(hDlg, IDC_COMBOCP, CB_ADDSTRING, 0, (LPARAM)(info.CodePageName + 7)); + } + + SendDlgItemMessage(hDlg, IDC_COMBOCP, CB_SETCURSEL, (WPARAM)CPDEFINDEX, 0); + ActualAccount = nullptr; + TranslateDialogDefault(hDlg); + SendMessage(GetParent(hDlg), PSM_UNCHANGED, (WPARAM)hDlg, 0); + return TRUE; + } + + case WM_SHOWWINDOW: + if (wParam == FALSE) { + WindowList_Remove(pYAMNVar->MessageWnds, hDlg); + SendMessage(GetParent(hDlg), PSM_UNCHANGED, (WPARAM)hDlg, 0); + } + else WindowList_Add(pYAMNVar->MessageWnds, hDlg); + return TRUE; + + case WM_YAMN_CHANGESTATUS: + if ((HPOP3ACCOUNT)wParam == ActualAccount) { + wchar_t accstatus[256]; + GetAccountStatus(ActualAccount, accstatus); + SetDlgItemText(hDlg, IDC_STSTATUS, accstatus); + return TRUE; + } + break; + + case WM_YAMN_CHANGESTATUSOPTION: + Changed = TRUE; + SendMessage(GetParent(hDlg), PSM_CHANGED, 0, 0); + return TRUE; + + case WM_YAMN_CHANGETIME: + if ((HPOP3ACCOUNT)wParam == ActualAccount) { + wchar_t Text[256]; + mir_snwprintf(Text, TranslateT("Time left to next check [s]: %d"), (DWORD)lParam); + SetDlgItemText(hDlg, IDC_STTIMELEFT, Text); + } + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDC_COMBOACCOUNT: + switch (HIWORD(wParam)) { + case CBN_EDITCHANGE: + ActualAccount = nullptr; + DlgSetItemText(hDlg, (WPARAM)IDC_STTIMELEFT, nullptr); + + if (GetDlgItemTextA(hDlg, IDC_COMBOACCOUNT, DlgInput, _countof(DlgInput))) + DlgEnableAccount(hDlg, TRUE, FALSE); + else + DlgEnableAccount(hDlg, FALSE, FALSE); + break; + + case CBN_KILLFOCUS: + GetDlgItemTextA(hDlg, IDC_COMBOACCOUNT, DlgInput, _countof(DlgInput)); + if (nullptr == (ActualAccount = (HPOP3ACCOUNT)CallService(MS_YAMN_FINDACCOUNTBYNAME, (WPARAM)POP3Plugin, (LPARAM)DlgInput))) { + DlgSetItemText(hDlg, (WPARAM)IDC_STTIMELEFT, nullptr); + EnableWindow(GetDlgItem(hDlg, IDC_BTNDEL), FALSE); + if (mir_strlen(DlgInput)) + DlgEnableAccount(hDlg, TRUE, TRUE); + else + DlgEnableAccount(hDlg, FALSE, FALSE); + } + else { + DlgShowAccount(hDlg, (WPARAM)M_SHOWACTUAL, (LPARAM)ActualAccount); + DlgEnableAccount(hDlg, TRUE, TRUE); + EnableWindow(GetDlgItem(hDlg, IDC_BTNDEL), TRUE); + } + break; + + case CBN_SELCHANGE: + if (CB_ERR != (Result = SendDlgItemMessage(hDlg, IDC_COMBOACCOUNT, CB_GETCURSEL, 0, 0))) + SendDlgItemMessageA(hDlg, IDC_COMBOACCOUNT, CB_GETLBTEXT, (WPARAM)Result, (LPARAM)DlgInput); + + if ((Result == CB_ERR) || (nullptr == (ActualAccount = (HPOP3ACCOUNT)CallService(MS_YAMN_FINDACCOUNTBYNAME, (WPARAM)POP3Plugin, (LPARAM)DlgInput)))) { + DlgSetItemText(hDlg, (WPARAM)IDC_STTIMELEFT, nullptr); + EnableWindow(GetDlgItem(hDlg, IDC_BTNDEL), FALSE); + } + else { + DlgShowAccount(hDlg, (WPARAM)M_SHOWACTUAL, (LPARAM)ActualAccount); + DlgEnableAccount(hDlg, TRUE, FALSE); + EnableWindow(GetDlgItem(hDlg, IDC_BTNDEL), TRUE); + } + break; + } + break; + + case IDC_COMBOCP: + { + int sel = SendDlgItemMessage(hDlg, IDC_COMBOCP, CB_GETCURSEL, 0, 0); + CPINFOEX info; GetCPInfoEx(CodePageNamesSupp[sel].CP, 0, &info); + DlgSetItemTextT(hDlg, IDC_STSTATUS, info.CodePageName); + } + case IDC_CHECK: + case IDC_CHECKSND: + case IDC_CHECKMSG: + case IDC_CHECKICO: + case IDC_CHECKFSND: + case IDC_CHECKFMSG: + case IDC_CHECKFICO: + case IDC_CHECKST0: + case IDC_CHECKST1: + case IDC_CHECKST2: + case IDC_CHECKST3: + case IDC_CHECKST4: + case IDC_CHECKST5: + case IDC_CHECKST6: + case IDC_CHECKST7: + case IDC_CHECKST8: + case IDC_CHECKST9: + case IDC_CHECKSTART: + case IDC_CHECKFORCE: + case IDC_EDITAPPPARAM: + case IDC_CHECKAPOP: + case IDC_AUTOBODY: + case IDC_CHECKCONTACTNICK: + case IDC_CHECKCONTACTNOEVENT: + case IDC_CHECKNOTLS: + Changed = TRUE; + break; + + case IDC_CHECKCONTACT: + Changed = IsDlgButtonChecked(hDlg, IDC_CHECKCONTACT) == BST_CHECKED; + EnableWindow(GetDlgItem(hDlg, IDC_CHECKCONTACTNICK), Changed); + EnableWindow(GetDlgItem(hDlg, IDC_CHECKCONTACTNOEVENT), Changed); + Changed = TRUE; + break; + + case IDC_CHECKSSL: + { + BOOL SSLC = (IsDlgButtonChecked(hDlg, IDC_CHECKSSL) == BST_CHECKED); + SetDlgItemInt(hDlg, IDC_EDITPORT, SSLC ? 995 : 110, FALSE); + EnableWindow(GetDlgItem(hDlg, IDC_CHECKNOTLS), SSLC ? 0 : 1); + } + Changed = TRUE; + break; + + case IDC_CPB: + case IDC_CPT: + case IDC_CPFB: + case IDC_CPFT: + case IDC_CPNB: + case IDC_CPNT: + if (HIWORD(wParam) != CPN_COLOURCHANGED) + break; + + case IDC_CHECKKBN: + Changed = TRUE; + break; + + case IDC_CHECKAPP: + Changed = TRUE; + EnableWindow(GetDlgItem(hDlg, IDC_BTNAPP), IsDlgButtonChecked(hDlg, IDC_CHECKAPP) == BST_CHECKED); + EnableWindow(GetDlgItem(hDlg, IDC_EDITAPP), IsDlgButtonChecked(hDlg, IDC_CHECKAPP) == BST_CHECKED); + EnableWindow(GetDlgItem(hDlg, IDC_EDITAPPPARAM), IsDlgButtonChecked(hDlg, IDC_CHECKAPP) == BST_CHECKED); + break; + + case IDC_BTNSTATUS: + DialogBoxParamW(g_plugin.getInst(), MAKEINTRESOURCEW(IDD_CHOOSESTATUSMODES), hDlg, DlgProcPOP3AccStatusOpt, NULL); + break; + + case IDC_BTNADD: + DlgSetItemText(hDlg, (WPARAM)IDC_STTIMELEFT, nullptr); + DlgShowAccount(hDlg, (WPARAM)M_SHOWDEFAULT, 0); + DlgEnableAccount(hDlg, TRUE, TRUE); + EnableWindow(GetDlgItem(hDlg, IDC_BTNDEL), FALSE); + DlgSetItemTextT(hDlg, IDC_EDITNAME, TranslateT("New Account")); + { + int index = SendDlgItemMessage(hDlg, IDC_COMBOACCOUNT, CB_ADDSTRING, 0, (LPARAM)TranslateT("New Account")); + if (index != CB_ERR && index != CB_ERRSPACE) + SendDlgItemMessage(hDlg, IDC_COMBOACCOUNT, CB_SETCURSEL, index, (LPARAM)TranslateT("New Account")); + } + break; + + case IDC_BTNAPP: + { + wchar_t filter[MAX_PATH]; + mir_snwprintf(filter, L"%s (*.exe;*.bat;*.cmd;*.com)%c*.exe;*.bat;*.cmd;*.com%c%s (*.*)%c*.*%c", + TranslateT("Executables"), 0, 0, TranslateT("All Files"), 0, 0); + + OPENFILENAME OFNStruct = { 0 }; + OFNStruct.lStructSize = sizeof(OPENFILENAME); + OFNStruct.hwndOwner = hDlg; + OFNStruct.lpstrFilter = filter; + OFNStruct.nFilterIndex = 1; + OFNStruct.nMaxFile = MAX_PATH; + OFNStruct.lpstrFile = new wchar_t[MAX_PATH]; + OFNStruct.lpstrFile[0] = (wchar_t)0; + OFNStruct.lpstrTitle = TranslateT("Select executable used for notification"); + OFNStruct.Flags = OFN_FILEMUSTEXIST | OFN_NONETWORKBUTTON | OFN_PATHMUSTEXIST | OFN_NOCHANGEDIR; + if (!GetOpenFileName(&OFNStruct)) { + if (CommDlgExtendedError()) + MessageBox(hDlg, TranslateT("Dialog box error"), TranslateT("Failed"), MB_OK); + } + else DlgSetItemTextT(hDlg, IDC_EDITAPP, OFNStruct.lpstrFile); + delete[] OFNStruct.lpstrFile; + } + break; + + case IDC_BTNDEFAULT: + DlgShowAccount(hDlg, (WPARAM)M_SHOWDEFAULT, 0); + break; + + case IDC_BTNDEL: + GetDlgItemTextA(hDlg, IDC_COMBOACCOUNT, DlgInput, _countof(DlgInput)); + EnableWindow(GetDlgItem(hDlg, IDC_BTNDEL), FALSE); + if ((CB_ERR == (Result = SendDlgItemMessage(hDlg, IDC_COMBOACCOUNT, CB_GETCURSEL, 0, 0))) + || (nullptr == (ActualAccount = (HPOP3ACCOUNT)CallService(MS_YAMN_FINDACCOUNTBYNAME, (WPARAM)POP3Plugin, (LPARAM)DlgInput)))) + return TRUE; + + if (IDOK != MessageBox(hDlg, TranslateT("Do you really want to delete this account?"), TranslateT("Delete account confirmation"), MB_OKCANCEL | MB_ICONWARNING)) + return TRUE; + + DlgSetItemTextT(hDlg, IDC_STTIMELEFT, TranslateT("Please wait while no account is in use.")); + + if (ActualAccount->hContact != NULL) + db_delete_contact(ActualAccount->hContact); + + CallService(MS_YAMN_DELETEACCOUNT, (WPARAM)POP3Plugin, (LPARAM)ActualAccount); + + //We can consider our account as deleted. + + SendDlgItemMessage(hDlg, IDC_COMBOACCOUNT, CB_DELETESTRING, (WPARAM)Result, 0); + DlgSetItemText(hDlg, (WPARAM)IDC_COMBOACCOUNT, nullptr); + DlgEnableAccount(hDlg, FALSE, 0); + DlgShowAccount(hDlg, (WPARAM)M_SHOWDEFAULT, 0); + break; + + case IDC_BTNRESET: + if (ActualAccount != nullptr) + ActualAccount->TimeLeft = ActualAccount->Interval; + return 1; + } + + if (HIWORD(wParam) == EN_CHANGE) + Changed = TRUE; + break; + + case WM_NOTIFY: + if (((LPNMHDR)lParam)->idFrom == 0 && ((LPNMHDR)lParam)->code == PSN_APPLY) { + char Text[MAX_PATH]; + WCHAR TextW[MAX_PATH]; + BOOL Translated, NewAcc = FALSE, Check, CheckMsg, CheckSnd, CheckIco, CheckApp, CheckAPOP; + BOOL CheckNMsgP, CheckFMsg, CheckFSnd, CheckFIco; + BOOL CheckKBN, CheckContact, CheckContactNick, CheckContactNoEvent; + BOOL CheckSSL, CheckABody, CheckNoTLS; + //BOOL Check0,Check1,Check2,Check3,Check4,Check5,Check6,Check7,Check8,Check9, + BOOL CheckStart, CheckForce; + size_t Length, index; + UINT Port, Interval; + + if (GetDlgItemTextA(hDlg, IDC_COMBOACCOUNT, Text, _countof(Text))) { + Check = (IsDlgButtonChecked(hDlg, IDC_CHECK) == BST_CHECKED); + CheckSSL = (IsDlgButtonChecked(hDlg, IDC_CHECKSSL) == BST_CHECKED); + CheckNoTLS = (IsDlgButtonChecked(hDlg, IDC_CHECKNOTLS) == BST_CHECKED); + CheckAPOP = (IsDlgButtonChecked(hDlg, IDC_CHECKAPOP) == BST_CHECKED); + + CheckABody = (IsDlgButtonChecked(hDlg, IDC_AUTOBODY) == BST_CHECKED); + CheckMsg = (IsDlgButtonChecked(hDlg, IDC_CHECKMSG) == BST_CHECKED); + CheckSnd = (IsDlgButtonChecked(hDlg, IDC_CHECKSND) == BST_CHECKED); + CheckIco = (IsDlgButtonChecked(hDlg, IDC_CHECKICO) == BST_CHECKED); + + CheckApp = (IsDlgButtonChecked(hDlg, IDC_CHECKAPP) == BST_CHECKED); + CheckKBN = (IsDlgButtonChecked(hDlg, IDC_CHECKKBN) == BST_CHECKED); + CheckContact = (IsDlgButtonChecked(hDlg, IDC_CHECKCONTACT) == BST_CHECKED); + CheckContactNick = (IsDlgButtonChecked(hDlg, IDC_CHECKCONTACTNICK) == BST_CHECKED); + CheckContactNoEvent = (IsDlgButtonChecked(hDlg, IDC_CHECKCONTACTNOEVENT) == BST_CHECKED); + + CheckFSnd = (IsDlgButtonChecked(hDlg, IDC_CHECKFSND) == BST_CHECKED); + CheckFMsg = (IsDlgButtonChecked(hDlg, IDC_CHECKFMSG) == BST_CHECKED); + CheckFIco = (IsDlgButtonChecked(hDlg, IDC_CHECKFICO) == BST_CHECKED); + + CheckNMsgP = (IsDlgButtonChecked(hDlg, IDC_CHECKNMSGP) == BST_CHECKED); + + Port = GetDlgItemInt(hDlg, IDC_EDITPORT, &Translated, FALSE); + if (!Translated) { + MessageBox(hDlg, TranslateT("This is not a valid number value"), TranslateT("Input error"), MB_OK); + SetFocus(GetDlgItem(hDlg, IDC_EDITPORT)); + break; + } + Interval = GetDlgItemInt(hDlg, IDC_EDITINTERVAL, &Translated, FALSE); + if (!Translated) { + MessageBox(hDlg, TranslateT("This is not a valid number value"), TranslateT("Input error"), MB_OK); + SetFocus(GetDlgItem(hDlg, IDC_EDITINTERVAL)); + break; + } + + GetDlgItemTextA(hDlg, IDC_EDITAPP, Text, _countof(Text)); + if (CheckApp && !(Length = mir_strlen(Text))) { + MessageBox(hDlg, TranslateT("Please select application to run"), TranslateT("Input error"), MB_OK); + break; + } + + GetDlgItemTextA(hDlg, IDC_COMBOACCOUNT, Text, _countof(Text)); + if (!(Length = mir_strlen(Text))) { + GetDlgItemTextA(hDlg, IDC_EDITNAME, Text, _countof(Text)); + if (!(Length = mir_strlen(Text))) + break; + } + + DlgSetItemTextT(hDlg, IDC_STTIMELEFT, TranslateT("Please wait while no account is in use.")); + + if (nullptr == (ActualAccount = (HPOP3ACCOUNT)CallService(MS_YAMN_FINDACCOUNTBYNAME, (WPARAM)POP3Plugin, (LPARAM)Text))) { + NewAcc = TRUE; + #ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "Options:APPLY:AccountBrowserSO-write wait\n"); + #endif + WaitToWriteSO(POP3Plugin->AccountBrowserSO); + #ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "Options:APPLY:AccountBrowserSO-write enter\n"); + #endif + if (nullptr == (ActualAccount = (HPOP3ACCOUNT)CallService(MS_YAMN_GETNEXTFREEACCOUNT, (WPARAM)POP3Plugin, (LPARAM)YAMN_ACCOUNTVERSION))) { + #ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "Options:APPLY:AccountBrowserSO-write done\n"); + #endif + WriteDoneSO(POP3Plugin->AccountBrowserSO); + MessageBox(hDlg, TranslateT("Cannot allocate memory space for new account"), TranslateT("Memory error"), MB_OK); + break; + } + } + else { + #ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "Options:APPLY:AccountBrowserSO-write wait\n"); + #endif + //We have to get full access to AccountBrowser, so other iterating thrads cannot get new account until new account is right set + WaitToWriteSO(POP3Plugin->AccountBrowserSO); + #ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "Options:APPLY:AccountBrowserSO-write enter\n"); + #endif + } + #ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "Options:APPLY:ActualAccountSO-write wait\n"); + #endif + if (WAIT_OBJECT_0 != WaitToWrite(ActualAccount)) { + #ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "Options:APPLY:ActualAccountSO-write wait failed\n"); + #endif + #ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "Options:APPLY:ActualBrowserSO-write done\n"); + #endif + WriteDoneSO(POP3Plugin->AccountBrowserSO); + + } + #ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "Options:APPLY:ActualAccountSO-write enter\n"); + #endif + + GetDlgItemTextA(hDlg, IDC_EDITNAME, Text, _countof(Text)); + if (!(Length = mir_strlen(Text))) + break; + if (nullptr != ActualAccount->Name) + delete[] ActualAccount->Name; + ActualAccount->Name = new char[mir_strlen(Text) + 1]; + mir_strcpy(ActualAccount->Name, Text); + + GetDlgItemTextA(hDlg, IDC_EDITSERVER, Text, _countof(Text)); + if (nullptr != ActualAccount->Server->Name) + delete[] ActualAccount->Server->Name; + ActualAccount->Server->Name = new char[mir_strlen(Text) + 1]; + mir_strcpy(ActualAccount->Server->Name, Text); + + GetDlgItemTextA(hDlg, IDC_EDITLOGIN, Text, _countof(Text)); + if (nullptr != ActualAccount->Server->Login) + delete[] ActualAccount->Server->Login; + ActualAccount->Server->Login = new char[mir_strlen(Text) + 1]; + mir_strcpy(ActualAccount->Server->Login, Text); + + GetDlgItemTextA(hDlg, IDC_EDITPASS, Text, _countof(Text)); + if (nullptr != ActualAccount->Server->Passwd) + delete[] ActualAccount->Server->Passwd; + ActualAccount->Server->Passwd = new char[mir_strlen(Text) + 1]; + mir_strcpy(ActualAccount->Server->Passwd, Text); + + GetDlgItemTextW(hDlg, IDC_EDITAPP, TextW, _countof(TextW)); + if (nullptr != ActualAccount->NewMailN.App) + delete[] ActualAccount->NewMailN.App; + ActualAccount->NewMailN.App = new WCHAR[mir_wstrlen(TextW) + 1]; + mir_wstrcpy(ActualAccount->NewMailN.App, TextW); + + GetDlgItemTextW(hDlg, IDC_EDITAPPPARAM, TextW, _countof(TextW)); + if (nullptr != ActualAccount->NewMailN.AppParam) + delete[] ActualAccount->NewMailN.AppParam; + ActualAccount->NewMailN.AppParam = new WCHAR[mir_wstrlen(TextW) + 1]; + mir_wstrcpy(ActualAccount->NewMailN.AppParam, TextW); + + ActualAccount->Server->Port = Port; + ActualAccount->Interval = Interval * 60; + + if (CB_ERR == (index = SendDlgItemMessage(hDlg, IDC_COMBOCP, CB_GETCURSEL, 0, 0))) + index = CPDEFINDEX; + ActualAccount->CP = CodePageNamesSupp[index].CP; + + if (NewAcc) + ActualAccount->TimeLeft = Interval * 60; + + CheckStart = (IsDlgButtonChecked(hDlg, IDC_CHECKSTART) == BST_CHECKED); + CheckForce = (IsDlgButtonChecked(hDlg, IDC_CHECKFORCE) == BST_CHECKED); + + ActualAccount->Flags = + (Check ? YAMN_ACC_ENA : 0) | + (CheckSSL ? YAMN_ACC_SSL23 : 0) | + (CheckNoTLS ? YAMN_ACC_NOTLS : 0) | + (CheckAPOP ? YAMN_ACC_APOP : 0) | + (CheckABody ? YAMN_ACC_BODY : 0) | + (ActualAccount->Flags & YAMN_ACC_POPN); + + ActualAccount->StatusFlags = + (Check0 ? YAMN_ACC_ST0 : 0) | + (Check1 ? YAMN_ACC_ST1 : 0) | + (Check2 ? YAMN_ACC_ST2 : 0) | + (Check3 ? YAMN_ACC_ST3 : 0) | + (Check4 ? YAMN_ACC_ST4 : 0) | + (Check5 ? YAMN_ACC_ST5 : 0) | + (Check6 ? YAMN_ACC_ST6 : 0) | + (Check7 ? YAMN_ACC_ST7 : 0) | + (Check8 ? YAMN_ACC_ST8 : 0) | + (Check9 ? YAMN_ACC_ST9 : 0) | + (CheckStart ? YAMN_ACC_STARTS : 0) | + (CheckForce ? YAMN_ACC_FORCE : 0); + + ActualAccount->NewMailN.Flags = + (CheckSnd ? YAMN_ACC_SND : 0) | + (CheckMsg ? YAMN_ACC_MSG : 0) | + (CheckIco ? YAMN_ACC_ICO : 0) | + (ActualAccount->NewMailN.Flags & YAMN_ACC_POP) | + (ActualAccount->NewMailN.Flags & YAMN_ACC_POPC) | + (CheckApp ? YAMN_ACC_APP : 0) | + (CheckKBN ? YAMN_ACC_KBN : 0) | + (CheckContact ? YAMN_ACC_CONT : 0) | + (CheckContactNick ? YAMN_ACC_CONTNICK : 0) | + (CheckContactNoEvent ? YAMN_ACC_CONTNOEVENT : 0) | + YAMN_ACC_MSGP; //this is default: when new mail arrives and window was displayed, leave it displayed. + + ActualAccount->NoNewMailN.Flags = + (ActualAccount->NoNewMailN.Flags & YAMN_ACC_POP) | + (ActualAccount->NoNewMailN.Flags & YAMN_ACC_POPC) | + (CheckNMsgP ? YAMN_ACC_MSGP : 0); + + ActualAccount->BadConnectN.Flags = + (CheckFSnd ? YAMN_ACC_SND : 0) | + (CheckFMsg ? YAMN_ACC_MSG : 0) | + (CheckFIco ? YAMN_ACC_ICO : 0) | + (ActualAccount->BadConnectN.Flags & YAMN_ACC_POP) | + (ActualAccount->BadConnectN.Flags & YAMN_ACC_POPC); + + #ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "Options:APPLY:ActualAccountSO-write done\n"); + #endif + WriteDone(ActualAccount); + #ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "Options:APPLY:AccountBrowserSO-write done\n"); + #endif + WriteDoneSO(POP3Plugin->AccountBrowserSO); + + EnableWindow(GetDlgItem(hDlg, IDC_BTNDEL), TRUE); + + DlgSetItemText(hDlg, (WPARAM)IDC_STTIMELEFT, nullptr); + + index = SendDlgItemMessage(hDlg, IDC_COMBOACCOUNT, CB_GETCURSEL, 0, 0); + + HPOP3ACCOUNT temp = ActualAccount; + + SendDlgItemMessage(hDlg, IDC_COMBOACCOUNT, CB_RESETCONTENT, 0, 0); + if (POP3Plugin->FirstAccount != nullptr) + for (ActualAccount = (HPOP3ACCOUNT)POP3Plugin->FirstAccount; ActualAccount != nullptr; ActualAccount = (HPOP3ACCOUNT)ActualAccount->Next) + if (ActualAccount->Name != nullptr) + SendDlgItemMessageA(hDlg, IDC_COMBOACCOUNT, CB_ADDSTRING, 0, (LPARAM)ActualAccount->Name); + + ActualAccount = temp; + SendDlgItemMessage(hDlg, IDC_COMBOACCOUNT, CB_SETCURSEL, (WPARAM)index, (LPARAM)ActualAccount->Name); + + WritePOP3Accounts(); + RefreshContact(); + return TRUE; + } + } + break; + } + if (Changed) + SendMessage(GetParent(hDlg), PSM_CHANGED, 0, 0); + return FALSE; +} + +INT_PTR CALLBACK DlgProcPOP3AccPopup(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + BOOL Changed = FALSE; + static BOOL InList = FALSE; + static HPOP3ACCOUNT ActualAccount; + static UCHAR ActualStatus; + // static struct CPOP3Options POP3Options; + + switch (msg) { + case WM_INITDIALOG: + { + DlgEnableAccountPopup(hDlg, FALSE, FALSE); + DlgShowAccountPopup(hDlg, (WPARAM)M_SHOWDEFAULT, 0); + //DlgShowAccountColors(hDlg,0,(LPARAM)ActualAccount); + #ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "Options:INITDIALOG:AccountBrowserSO-read wait\n"); + #endif + WaitToReadSO(POP3Plugin->AccountBrowserSO); + #ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "Options:INITDIALOG:AccountBrowserSO-read enter\n"); + #endif + if (POP3Plugin->FirstAccount != nullptr) + for (ActualAccount = (HPOP3ACCOUNT)POP3Plugin->FirstAccount; ActualAccount != nullptr; ActualAccount = (HPOP3ACCOUNT)ActualAccount->Next) + if (ActualAccount->Name != nullptr) + SendDlgItemMessageA(hDlg, IDC_COMBOACCOUNT, CB_ADDSTRING, 0, (LPARAM)ActualAccount->Name); + #ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "Options:INITDIALOG:AccountBrowserSO-read done\n"); + #endif + ReadDoneSO(POP3Plugin->AccountBrowserSO); + ActualAccount = nullptr; + + + TranslateDialogDefault(hDlg); + SendMessage(GetParent(hDlg), PSM_UNCHANGED, (WPARAM)hDlg, 0); + return TRUE; + } + + case WM_SHOWWINDOW: + if ((BOOL)wParam == FALSE) { + WindowList_Remove(pYAMNVar->MessageWnds, hDlg); + SendMessage(GetParent(hDlg), PSM_UNCHANGED, (WPARAM)hDlg, 0); + } + else { + WindowList_Add(pYAMNVar->MessageWnds, hDlg); + + int index = SendDlgItemMessage(hDlg, IDC_COMBOACCOUNT, CB_GETCURSEL, 0, 0); + HPOP3ACCOUNT temp = ActualAccount; + SendDlgItemMessage(hDlg, IDC_COMBOACCOUNT, CB_RESETCONTENT, 0, 0); + + if (POP3Plugin->FirstAccount != nullptr) + for (ActualAccount = (HPOP3ACCOUNT)POP3Plugin->FirstAccount; ActualAccount != nullptr; ActualAccount = (HPOP3ACCOUNT)ActualAccount->Next) + if (ActualAccount->Name != nullptr) + SendDlgItemMessageA(hDlg, IDC_COMBOACCOUNT, CB_ADDSTRING, 0, (LPARAM)ActualAccount->Name); + + ActualAccount = temp; + + if (ActualAccount != nullptr) { + SendDlgItemMessage(hDlg, IDC_COMBOACCOUNT, CB_SETCURSEL, (WPARAM)index, (LPARAM)ActualAccount->Name); + DlgShowAccount(hDlg, (WPARAM)M_SHOWACTUAL, (LPARAM)ActualAccount); + DlgShowAccountColors(hDlg, 0, (LPARAM)ActualAccount); + DlgEnableAccountPopup(hDlg, TRUE, FALSE); + } + else { + DlgShowAccountPopup(hDlg, (WPARAM)M_SHOWDEFAULT, 0); + DlgEnableAccountPopup(hDlg, FALSE, FALSE); + } + + } + return TRUE; + + case WM_COMMAND: + { + WORD wNotifyCode = HIWORD(wParam); + switch (LOWORD(wParam)) { + LONG Result; + case IDC_COMBOACCOUNT: + switch (wNotifyCode) { + + case CBN_KILLFOCUS: + GetDlgItemTextA(hDlg, IDC_COMBOACCOUNT, DlgInput, _countof(DlgInput)); + if (nullptr == (ActualAccount = (HPOP3ACCOUNT)CallService(MS_YAMN_FINDACCOUNTBYNAME, (WPARAM)POP3Plugin, (LPARAM)DlgInput))) { + DlgSetItemText(hDlg, (WPARAM)IDC_STTIMELEFT, nullptr); + if (mir_strlen(DlgInput)) + DlgEnableAccountPopup(hDlg, TRUE, TRUE); + else + DlgEnableAccountPopup(hDlg, FALSE, FALSE); + } + else { + DlgShowAccount(hDlg, (WPARAM)M_SHOWACTUAL, (LPARAM)ActualAccount); + DlgShowAccountColors(hDlg, 0, (LPARAM)ActualAccount); + DlgEnableAccountPopup(hDlg, TRUE, TRUE); + } + break; + case CBN_SELCHANGE: + if (CB_ERR != (Result = SendDlgItemMessage(hDlg, IDC_COMBOACCOUNT, CB_GETCURSEL, 0, 0))) + SendDlgItemMessageA(hDlg, IDC_COMBOACCOUNT, CB_GETLBTEXT, (WPARAM)Result, (LPARAM)DlgInput); + if ((Result == CB_ERR) || (nullptr == (ActualAccount = (HPOP3ACCOUNT)CallService(MS_YAMN_FINDACCOUNTBYNAME, (WPARAM)POP3Plugin, (LPARAM)DlgInput)))) { + DlgSetItemText(hDlg, (WPARAM)IDC_STTIMELEFT, nullptr); + } + else { + DlgShowAccount(hDlg, (WPARAM)M_SHOWACTUAL, (LPARAM)ActualAccount); + DlgShowAccountColors(hDlg, 0, (LPARAM)ActualAccount); + DlgEnableAccountPopup(hDlg, TRUE, FALSE); + } + break; + } + break; + case IDC_COMBOCP: + { + int sel = SendDlgItemMessage(hDlg, IDC_COMBOCP, CB_GETCURSEL, 0, 0); + CPINFOEX info; GetCPInfoEx(CodePageNamesSupp[sel].CP, 0, &info); + DlgSetItemTextT(hDlg, IDC_STSTATUS, info.CodePageName); + } + case IDC_RADIOPOPN: + case IDC_RADIOPOP1: + Changed = TRUE; + break; + case IDC_CPB: + case IDC_CPT: + case IDC_CPFB: + case IDC_CPFT: + case IDC_CPNB: + case IDC_CPNT: + if (HIWORD(wParam) != CPN_COLOURCHANGED) + break; + case IDC_CHECKCOL: + case IDC_CHECKFCOL: + case IDC_CHECKNCOL: + EnableWindow(GetDlgItem(hDlg, IDC_CPB), (IsDlgButtonChecked(hDlg, IDC_CHECKCOL) == BST_CHECKED) && (IsDlgButtonChecked(hDlg, IDC_CHECKPOP) == BST_CHECKED) && wParam); + EnableWindow(GetDlgItem(hDlg, IDC_CPT), (IsDlgButtonChecked(hDlg, IDC_CHECKCOL) == BST_CHECKED) && (IsDlgButtonChecked(hDlg, IDC_CHECKPOP) == BST_CHECKED) && wParam); + EnableWindow(GetDlgItem(hDlg, IDC_CPNB), (IsDlgButtonChecked(hDlg, IDC_CHECKNCOL) == BST_CHECKED) && (IsDlgButtonChecked(hDlg, IDC_CHECKNPOP) == BST_CHECKED) && wParam); + EnableWindow(GetDlgItem(hDlg, IDC_CPNT), (IsDlgButtonChecked(hDlg, IDC_CHECKNCOL) == BST_CHECKED) && (IsDlgButtonChecked(hDlg, IDC_CHECKNPOP) == BST_CHECKED) && wParam); + EnableWindow(GetDlgItem(hDlg, IDC_CPFB), (IsDlgButtonChecked(hDlg, IDC_CHECKFCOL) == BST_CHECKED) && (IsDlgButtonChecked(hDlg, IDC_CHECKFPOP) == BST_CHECKED) && wParam); + EnableWindow(GetDlgItem(hDlg, IDC_CPFT), (IsDlgButtonChecked(hDlg, IDC_CHECKFCOL) == BST_CHECKED) && (IsDlgButtonChecked(hDlg, IDC_CHECKFPOP) == BST_CHECKED) && wParam); + Changed = TRUE; + break; + + case IDC_PREVIEW: + { + POPUPDATAW Tester; + POPUPDATAW TesterF; + POPUPDATAW TesterN; + BOOL TesterC = (IsDlgButtonChecked(hDlg, IDC_CHECKCOL) == BST_CHECKED); + BOOL TesterFC = (IsDlgButtonChecked(hDlg, IDC_CHECKFCOL) == BST_CHECKED); + BOOL TesterNC = (IsDlgButtonChecked(hDlg, IDC_CHECKNCOL) == BST_CHECKED); + + memset(&Tester, 0, sizeof(Tester)); + memset(&TesterF, 0, sizeof(TesterF)); + memset(&TesterN, 0, sizeof(TesterN)); + Tester.lchIcon = g_LoadIconEx(2); + TesterF.lchIcon = g_LoadIconEx(3); + TesterN.lchIcon = g_LoadIconEx(1); + + mir_wstrncpy(Tester.lpwzContactName, TranslateT("Account Test"), MAX_CONTACTNAME); + mir_wstrncpy(TesterF.lpwzContactName, TranslateT("Account Test (failed)"), MAX_CONTACTNAME); + mir_wstrncpy(TesterN.lpwzContactName, TranslateT("Account Test"), MAX_CONTACTNAME); + mir_wstrncpy(Tester.lpwzText, TranslateT("You have N new mail messages"), MAX_SECONDLINE); + mir_wstrncpy(TesterF.lpwzText, TranslateT("Connection failed message"), MAX_SECONDLINE); + mir_wstrncpy(TesterN.lpwzText, TranslateT("No new mail message"), MAX_SECONDLINE); + if (TesterC) { + Tester.colorBack = SendDlgItemMessage(hDlg, IDC_CPB, CPM_GETCOLOUR, 0, 0); + Tester.colorText = SendDlgItemMessage(hDlg, IDC_CPT, CPM_GETCOLOUR, 0, 0); + } + else { + Tester.colorBack = GetSysColor(COLOR_BTNFACE); + Tester.colorText = GetSysColor(COLOR_WINDOWTEXT); + } + if (TesterFC) { + TesterF.colorBack = SendDlgItemMessage(hDlg, IDC_CPFB, CPM_GETCOLOUR, 0, 0); + TesterF.colorText = SendDlgItemMessage(hDlg, IDC_CPFT, CPM_GETCOLOUR, 0, 0); + } + else { + TesterF.colorBack = GetSysColor(COLOR_BTNFACE); + TesterF.colorText = GetSysColor(COLOR_WINDOWTEXT); + } + if (TesterNC) { + TesterN.colorBack = SendDlgItemMessage(hDlg, IDC_CPNB, CPM_GETCOLOUR, 0, 0); + TesterN.colorText = SendDlgItemMessage(hDlg, IDC_CPNT, CPM_GETCOLOUR, 0, 0); + } + else { + TesterN.colorBack = GetSysColor(COLOR_BTNFACE); + TesterN.colorText = GetSysColor(COLOR_WINDOWTEXT); + } + Tester.PluginWindowProc = nullptr; + TesterF.PluginWindowProc = nullptr; + TesterN.PluginWindowProc = nullptr; + Tester.PluginData = nullptr; + TesterF.PluginData = nullptr; + TesterN.PluginData = nullptr; + + if (IsDlgButtonChecked(hDlg, IDC_CHECKPOP) == BST_CHECKED) + PUAddPopupW(&Tester); + if (IsDlgButtonChecked(hDlg, IDC_CHECKFPOP) == BST_CHECKED) + PUAddPopupW(&TesterF); + if (IsDlgButtonChecked(hDlg, IDC_CHECKNPOP) == BST_CHECKED) + PUAddPopupW(&TesterN); + Changed = TRUE; + } + break; + case IDC_CHECKKBN: + Changed = TRUE; + break; + case IDC_CHECKPOP: + Changed = TRUE; + EnableWindow(GetDlgItem(hDlg, IDC_CHECKCOL), IsDlgButtonChecked(hDlg, IDC_CHECKPOP) == BST_CHECKED); + EnableWindow(GetDlgItem(hDlg, IDC_CPB), (IsDlgButtonChecked(hDlg, IDC_CHECKCOL) == BST_CHECKED) && IsDlgButtonChecked(hDlg, IDC_CHECKPOP) == BST_CHECKED); + EnableWindow(GetDlgItem(hDlg, IDC_CPT), (IsDlgButtonChecked(hDlg, IDC_CHECKCOL) == BST_CHECKED) && IsDlgButtonChecked(hDlg, IDC_CHECKPOP) == BST_CHECKED); + EnableWindow(GetDlgItem(hDlg, IDC_RADIOPOPN), (IsDlgButtonChecked(hDlg, IDC_CHECKPOP) == BST_CHECKED)); + EnableWindow(GetDlgItem(hDlg, IDC_RADIOPOP1), (IsDlgButtonChecked(hDlg, IDC_CHECKPOP) == BST_CHECKED)); + EnableWindow(GetDlgItem(hDlg, IDC_EDITPOPS), (IsDlgButtonChecked(hDlg, IDC_CHECKPOP) == BST_CHECKED)); + break; + case IDC_CHECKFPOP: + Changed = TRUE; + EnableWindow(GetDlgItem(hDlg, IDC_CHECKFCOL), IsDlgButtonChecked(hDlg, IDC_CHECKFPOP) == BST_CHECKED); + EnableWindow(GetDlgItem(hDlg, IDC_CPFB), (IsDlgButtonChecked(hDlg, IDC_CHECKFCOL) == BST_CHECKED) && IsDlgButtonChecked(hDlg, IDC_CHECKFPOP) == BST_CHECKED); + EnableWindow(GetDlgItem(hDlg, IDC_CPFT), (IsDlgButtonChecked(hDlg, IDC_CHECKFCOL) == BST_CHECKED) && IsDlgButtonChecked(hDlg, IDC_CHECKFPOP) == BST_CHECKED); + EnableWindow(GetDlgItem(hDlg, IDC_EDITFPOPS), (IsDlgButtonChecked(hDlg, IDC_CHECKFPOP) == BST_CHECKED)); + break; + case IDC_CHECKNPOP: + Changed = TRUE; + EnableWindow(GetDlgItem(hDlg, IDC_CHECKNCOL), IsDlgButtonChecked(hDlg, IDC_CHECKNPOP) == BST_CHECKED); + EnableWindow(GetDlgItem(hDlg, IDC_CPNB), (IsDlgButtonChecked(hDlg, IDC_CHECKNCOL) == BST_CHECKED) && IsDlgButtonChecked(hDlg, IDC_CHECKNPOP) == BST_CHECKED); + EnableWindow(GetDlgItem(hDlg, IDC_CPNT), (IsDlgButtonChecked(hDlg, IDC_CHECKNCOL) == BST_CHECKED) && IsDlgButtonChecked(hDlg, IDC_CHECKNPOP) == BST_CHECKED); + EnableWindow(GetDlgItem(hDlg, IDC_EDITNPOPS), (IsDlgButtonChecked(hDlg, IDC_CHECKNPOP) == BST_CHECKED)); + break; + + } + if (HIWORD(wParam) == EN_CHANGE) + Changed = TRUE; + break; + } + case WM_NOTIFY: + switch (((LPNMHDR)lParam)->idFrom) { + case 0: + switch (((LPNMHDR)lParam)->code) { + case PSN_APPLY: + { + wchar_t Text[MAX_PATH]; + BOOL Translated, CheckPopup, CheckPopupW; + BOOL CheckNPopup, CheckNPopupW, CheckFPopup, CheckFPopupW; + BOOL CheckPopN; + UINT Time, TimeN, TimeF; + + if (GetDlgItemText(hDlg, IDC_COMBOACCOUNT, Text, _countof(Text))) { + CheckPopup = (IsDlgButtonChecked(hDlg, IDC_CHECKPOP) == BST_CHECKED); + CheckPopupW = (IsDlgButtonChecked(hDlg, IDC_CHECKCOL) == BST_CHECKED); + + CheckFPopup = (IsDlgButtonChecked(hDlg, IDC_CHECKFPOP) == BST_CHECKED); + CheckFPopupW = (IsDlgButtonChecked(hDlg, IDC_CHECKFCOL) == BST_CHECKED); + + CheckNPopup = (IsDlgButtonChecked(hDlg, IDC_CHECKNPOP) == BST_CHECKED); + CheckNPopupW = (IsDlgButtonChecked(hDlg, IDC_CHECKNCOL) == BST_CHECKED); + + CheckPopN = (IsDlgButtonChecked(hDlg, IDC_RADIOPOPN) == BST_CHECKED); + + + Time = GetDlgItemInt(hDlg, IDC_EDITPOPS, &Translated, FALSE); + if (!Translated) { + MessageBox(hDlg, TranslateT("This is not a valid number value"), TranslateT("Input error"), MB_OK); + SetFocus(GetDlgItem(hDlg, IDC_EDITPOPS)); + break; + } + TimeN = GetDlgItemInt(hDlg, IDC_EDITNPOPS, &Translated, FALSE); + if (!Translated) { + MessageBox(hDlg, TranslateT("This is not a valid number value"), TranslateT("Input error"), MB_OK); + SetFocus(GetDlgItem(hDlg, IDC_EDITNPOPS)); + break; + } + TimeF = GetDlgItemInt(hDlg, IDC_EDITFPOPS, &Translated, FALSE); + if (!Translated) { + MessageBox(hDlg, TranslateT("This is not a valid number value"), TranslateT("Input error"), MB_OK); + SetFocus(GetDlgItem(hDlg, IDC_EDITFPOPS)); + break; + } + + DlgSetItemTextT(hDlg, IDC_STTIMELEFT, TranslateT("Please wait while no account is in use.")); + + ActualAccount->Flags = + (ActualAccount->Flags & YAMN_ACC_ENA) | + (ActualAccount->Flags & YAMN_ACC_SSL23) | + (ActualAccount->Flags & YAMN_ACC_NOTLS) | + (ActualAccount->Flags & YAMN_ACC_APOP) | + (ActualAccount->Flags & YAMN_ACC_BODY) | + (CheckPopN ? YAMN_ACC_POPN : 0); + + ActualAccount->NewMailN.Flags = + (ActualAccount->NewMailN.Flags & YAMN_ACC_SND) | + (ActualAccount->NewMailN.Flags & YAMN_ACC_MSG) | + (ActualAccount->NewMailN.Flags & YAMN_ACC_ICO) | + (CheckPopup ? YAMN_ACC_POP : 0) | + (CheckPopupW ? YAMN_ACC_POPC : 0) | + (ActualAccount->NewMailN.Flags & YAMN_ACC_APP) | + (ActualAccount->NewMailN.Flags & YAMN_ACC_KBN) | + (ActualAccount->NewMailN.Flags & YAMN_ACC_CONT) | + (ActualAccount->NewMailN.Flags & YAMN_ACC_CONTNICK) | + (ActualAccount->NewMailN.Flags & YAMN_ACC_CONTNOEVENT) | + YAMN_ACC_MSGP; + + ActualAccount->NoNewMailN.Flags = + (CheckNPopup ? YAMN_ACC_POP : 0) | + (CheckNPopupW ? YAMN_ACC_POPC : 0) | + (ActualAccount->NoNewMailN.Flags & YAMN_ACC_MSGP); + + ActualAccount->BadConnectN.Flags = + (ActualAccount->BadConnectN.Flags & YAMN_ACC_SND) | + (ActualAccount->BadConnectN.Flags & YAMN_ACC_MSG) | + (ActualAccount->BadConnectN.Flags & YAMN_ACC_ICO) | + (CheckFPopup ? YAMN_ACC_POP : 0) | + (CheckFPopupW ? YAMN_ACC_POPC : 0); + + ActualAccount->NewMailN.PopupB = SendDlgItemMessage(hDlg, IDC_CPB, CPM_GETCOLOUR, 0, 0); + ActualAccount->NewMailN.PopupT = SendDlgItemMessage(hDlg, IDC_CPT, CPM_GETCOLOUR, 0, 0); + ActualAccount->NewMailN.PopupTime = Time; + + ActualAccount->NoNewMailN.PopupB = SendDlgItemMessage(hDlg, IDC_CPNB, CPM_GETCOLOUR, 0, 0); + ActualAccount->NoNewMailN.PopupT = SendDlgItemMessage(hDlg, IDC_CPNT, CPM_GETCOLOUR, 0, 0); + ActualAccount->NoNewMailN.PopupTime = TimeN; + + ActualAccount->BadConnectN.PopupB = SendDlgItemMessage(hDlg, IDC_CPFB, CPM_GETCOLOUR, 0, 0); + ActualAccount->BadConnectN.PopupT = SendDlgItemMessage(hDlg, IDC_CPFT, CPM_GETCOLOUR, 0, 0); + ActualAccount->BadConnectN.PopupTime = TimeF; + + #ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "Options:APPLY:ActualAccountSO-write done\n"); + #endif + WriteDone(ActualAccount); + #ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "Options:APPLY:AccountBrowserSO-write done\n"); + #endif + WriteDoneSO(POP3Plugin->AccountBrowserSO); + + WritePOP3Accounts(); + RefreshContact(); + return TRUE; + } + } + break; + } + break; + } + break; + } + if (Changed) + SendMessage(GetParent(hDlg), PSM_CHANGED, 0, 0); + return FALSE; +} diff --git a/protocols/YAMN/src/proto/pop3/pop3opt.h b/protocols/YAMN/src/proto/pop3/pop3opt.h new file mode 100644 index 0000000000..245f7679a4 --- /dev/null +++ b/protocols/YAMN/src/proto/pop3/pop3opt.h @@ -0,0 +1,40 @@ +#ifndef __OPTIONS_H +#define __OPTIONS_H + +#define M_SHOWACTUAL 0 +#define M_SHOWDEFAULT 1 + + +//Enables account in options +BOOL DlgEnableAccount(HWND hDlg,WPARAM wParam,LPARAM lParam); + +//Sets dialog controls to match current account +BOOL DlgShowAccount(HWND hDlg,WPARAM wParam,LPARAM lParam); + +//Sets colors to match colors of actual account +BOOL DlgShowAccountColors(HWND hDlg,WPARAM wParam,LPARAM lParam); + +//Options dialog procedure +INT_PTR CALLBACK DlgProcPOP3AccOpt(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); + +//Options dialog procedure +INT_PTR CALLBACK DlgProcPOP3AccStatusOpt(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); + +//Options dialog procedure +INT_PTR CALLBACK DlgProcYAMNOpt(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); + +//Options dialog procedure +INT_PTR CALLBACK DlgProcPOP3AccPopup(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); + +//Initializes POP3 options for Miranda +int POP3OptInit(WPARAM wParam,LPARAM lParam); + +//Sets dialog item text +BOOL DlgSetItemText(HWND hDlg,WPARAM wParam,const char*); +BOOL DlgSetItemTextW(HWND hDlg,WPARAM wParam,const WCHAR*); + + +#define DlgSetItemTextT DlgSetItemTextW + + +#endif diff --git a/protocols/YAMN/src/protoplugin.cpp b/protocols/YAMN/src/protoplugin.cpp new file mode 100644 index 0000000000..4999b69401 --- /dev/null +++ b/protocols/YAMN/src/protoplugin.cpp @@ -0,0 +1,192 @@ +/* + * YAMN plugin export functions for protocols + * + * (c) majvan 2002-2004 + */ + +#include "stdafx.h" + +//-------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------- + +PYAMN_PROTOPLUGINQUEUE FirstProtoPlugin=nullptr; + +INT_PTR RegisterProtocolPluginSvc(WPARAM,LPARAM); + +//Removes plugin from queue and deletes registration structures +INT_PTR UnregisterProtocolPlugin(HYAMNPROTOPLUGIN Plugin); + +INT_PTR UnregisterProtocolPluginSvc(WPARAM,LPARAM); + +//Removes plugins from queue and deletes registration structures +INT_PTR UnregisterProtoPlugins(); + +//Sets imported functions for an plugin and therefore it starts plugin to be registered and running +// Plugin- plugin, which wants to set its functions +// YAMNFcn- pointer to imported functions with accounts +// YAMNFcnVer- version of YAMN_PROTOIMPORTFCN, use YAMN_PROTOIMPORTFCNVERSION +// YAMNMailFcn- pointer to imported functions with mails +// YAMNMailFcnVer- version of YAMN_MAILIMPORTFCN, use YAMN_MAILIMPORTFCNVERSION +// returns nonzero if success +int WINAPI SetProtocolPluginFcnImportFcn(HYAMNPROTOPLUGIN Plugin,PYAMN_PROTOIMPORTFCN YAMNFcn,DWORD YAMNFcnVer,PYAMN_MAILIMPORTFCN YAMNMailFcn,DWORD YAMNMailFcnVer); + +struct CExportedFunctions ProtoPluginExportedFcn[]= +{ + {YAMN_SETPROTOCOLPLUGINFCNIMPORTID,(void *)SetProtocolPluginFcnImportFcn}, +}; + +struct CExportedServices ProtoPluginExportedSvc[]= +{ + {MS_YAMN_REGISTERPROTOPLUGIN,RegisterProtocolPluginSvc}, + {MS_YAMN_UNREGISTERPROTOPLUGIN,UnregisterProtocolPluginSvc}, + {MS_YAMN_GETFILENAME,GetFileNameSvc}, + {MS_YAMN_DELETEFILENAME,DeleteFileNameSvc}, +}; + +//-------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------- + +INT_PTR RegisterProtocolPluginSvc(WPARAM wParam,LPARAM lParam) +{ + PYAMN_PROTOREGISTRATION Registration=(PYAMN_PROTOREGISTRATION)wParam; + HYAMNPROTOPLUGIN Plugin; + + if (lParam != YAMN_PROTOREGISTRATIONVERSION) + return 0; + if ((Registration->Name==nullptr) || (Registration->Ver==nullptr)) + return (INT_PTR)NULL; + if (nullptr==(Plugin=new YAMN_PROTOPLUGIN)) + return (INT_PTR)NULL; + + Plugin->PluginInfo=Registration; + + Plugin->FirstAccount=nullptr; + + Plugin->AccountBrowserSO=new SWMRG; + SWMRGInitialize(Plugin->AccountBrowserSO,nullptr); + + Plugin->Fcn=nullptr; + Plugin->MailFcn=nullptr; + +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"::: YAMN- new protocol registered: %0x (%s) :::\n",Plugin,Registration->Name); +#endif + return (INT_PTR)Plugin; +} + +int WINAPI SetProtocolPluginFcnImportFcn(HYAMNPROTOPLUGIN Plugin,PYAMN_PROTOIMPORTFCN YAMNFcn,DWORD YAMNFcnVer,PYAMN_MAILIMPORTFCN YAMNMailFcn,DWORD YAMNMailFcnVer) +{ + PYAMN_PROTOPLUGINQUEUE Parser; + + if (YAMNFcnVer != YAMN_PROTOIMPORTFCNVERSION) + return 0; + if (YAMNMailFcnVer != YAMN_MAILIMPORTFCNVERSION) + return 0; + if (YAMNFcn==nullptr) + return 0; + if (YAMNMailFcn==nullptr) + return 0; + +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"::: YAMN- protocol %0x import succeed :::\n",Plugin); +#endif + Plugin->Fcn=YAMNFcn; + Plugin->MailFcn=YAMNMailFcn; + + mir_cslock lck(PluginRegCS); + // We add protocol to the protocol list + for (Parser=FirstProtoPlugin;Parser != nullptr && Parser->Next != nullptr;Parser=Parser->Next); + if (Parser==nullptr) + { + FirstProtoPlugin=new YAMN_PROTOPLUGINQUEUE; + Parser=FirstProtoPlugin; + } + else + { + Parser->Next=new YAMN_PROTOPLUGINQUEUE; + Parser=Parser->Next; + } + + Parser->Plugin=Plugin; + Parser->Next=nullptr; + return 1; +} + +INT_PTR UnregisterProtocolPlugin(HYAMNPROTOPLUGIN Plugin) +{ + PYAMN_PROTOPLUGINQUEUE Parser,Found; + +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"Entering UnregisterProtocolPlugin\n"); +#endif + if (FirstProtoPlugin->Plugin==Plugin) + { + Found=FirstProtoPlugin; + FirstProtoPlugin=FirstProtoPlugin->Next; + } + else + { + for (Parser=FirstProtoPlugin;(Parser->Next != nullptr) && (Plugin != Parser->Next->Plugin);Parser=Parser->Next); + if (Parser->Next != nullptr) + { + Found=Parser->Next; + Parser->Next=Parser->Next->Next; + } + else + Found=nullptr; + } + if (Found != nullptr) + { + StopAccounts(Plugin); + DeleteAccounts(Plugin); + if (Plugin->Fcn->UnLoadFcn != nullptr) + Plugin->Fcn->UnLoadFcn((void *)nullptr); + + delete Found->Plugin->AccountBrowserSO; + delete Found->Plugin; + delete Found; + +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"::: YAMN- protocol %0x unregistered :::\n",Plugin); +#endif + } + else + return 0; + return 1; +} + +INT_PTR UnregisterProtocolPluginSvc(WPARAM wParam,LPARAM) +{ + HYAMNPROTOPLUGIN Plugin=(HYAMNPROTOPLUGIN)wParam; + + mir_cslock lck(PluginRegCS); + UnregisterProtocolPlugin(Plugin); + return 1; +} + +INT_PTR UnregisterProtoPlugins() +{ + mir_cslock lck(PluginRegCS); + // We remove protocols from the protocol list + while(FirstProtoPlugin != nullptr) + UnregisterProtocolPlugin(FirstProtoPlugin->Plugin); + return 1; +} + +INT_PTR GetFileNameSvc(WPARAM wParam,LPARAM) +{ + wchar_t *FileName = new wchar_t[MAX_PATH]; + if (FileName == nullptr) + return NULL; + + mir_snwprintf(FileName, MAX_PATH, L"%s\\yamn-accounts.%s.%s.book", UserDirectory, wParam, ProfileName); + return (INT_PTR)FileName; +} + +INT_PTR DeleteFileNameSvc(WPARAM wParam,LPARAM) +{ + if (( wchar_t* )wParam != nullptr) + delete[] ( wchar_t* ) wParam; + + return 0; +} diff --git a/protocols/YAMN/src/resource.h b/protocols/YAMN/src/resource.h new file mode 100644 index 0000000000..4c76c05bf9 --- /dev/null +++ b/protocols/YAMN/src/resource.h @@ -0,0 +1,127 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by YAMN.rc +// +#define IDI_CHECKMAIL 104 +#define IDI_LAUNCHAPP 105 +#define IDD_DLGVIEWMESSAGES 107 +#define IDD_DLGSHOWMESSAGE 108 +#define IDI_ICOYAMN2 112 +#define IDI_ICOYAMN1 113 +#define IDD_DLGBADCONNECT 115 +#define IDD_POP3ACCOUNTOPT 121 +#define IDD_YAMNOPT 126 +#define IDI_BADCONNECT 131 +#define IDI_ICOTTBUP 138 +#define IDD_PLUGINOPT 141 +#define IDI_NEWMAIL 159 +#define IDD_CHOOSESTATUSMODES 310 +#define IDD_OPTIONS 311 +#define IDD_POP3ACCOUNTPOPUP 312 +#define IDC_EDITSERVER 1000 +#define IDC_EDITPORT 1001 +#define IDC_EDITLOGIN 1002 +#define IDC_EDITPASS 1003 +#define IDC_COMBOACCOUNT 1005 +#define IDC_BTNDEFAULT 1006 +#define IDC_EDITINTERVAL 1007 +#define IDC_CHECKSND 1008 +#define IDC_CHECKMSG 1009 +#define IDC_CHECKAPP 1010 +#define IDC_BTNAPP 1011 +#define IDC_CHECKICO 1012 +#define IDC_CHECK 1013 +#define IDC_BTNDEL 1014 +#define IDC_STSERVER 1015 +#define IDC_CHECKFSND 1016 +#define IDC_CHECKFMSG 1017 +#define IDC_CHECKFICO 1018 +#define IDC_CHECKST0 1019 +#define IDC_CHECKST1 1020 +#define IDC_CHECKST2 1021 +#define IDC_CHECKST3 1022 +#define IDC_CHECKST4 1023 +#define IDC_CHECKST5 1024 +#define IDC_CHECKST6 1025 +#define IDC_CHECKST7 1026 +#define IDC_EDITAPP 1027 +#define IDC_CHECKST8 1028 +#define IDC_CHECKST9 1029 +#define IDC_CHECKCONTACT 1030 +#define IDC_CHECKCONTACTNICK 1031 +#define IDC_CHECKCONTACTNOEVENT 1032 +#define IDC_STTIMELEFT 1033 +#define IDC_LISTMAILS 1038 +#define IDC_LISTHEADERS 1039 +#define IDC_EDITAPPPARAM 1044 +#define IDC_BTNOK 1047 +#define IDC_COMBOCP 1050 +#define IDC_STCP 1055 +#define IDC_STATICMSG 1055 +#define IDC_STPORT 1056 +#define IDC_STLOGIN 1057 +#define IDC_STPASS 1058 +#define IDC_STINTERVAL 1059 +#define IDC_AUTOBODY 1062 +#define IDC_BTNRESET 1063 +#define IDC_CHECKSTART 1064 +#define IDC_STWCHECK 1065 +#define IDC_CHECKFORCE 1066 +#define IDC_RADIOPOP1 1068 +#define IDC_RADIOPOPN 1069 +#define IDC_CPB 1070 +#define IDC_CPNB 1071 +#define IDC_CHECKCOL 1073 +#define IDC_CPT 1074 +#define IDC_CPFB 1075 +#define IDC_CPFT 1076 +#define IDC_CHECKFCOL 1077 +#define IDC_CHECKNCOL 1078 +#define IDC_CPNT 1079 +#define IDC_CHECKPOP 1087 +#define IDC_CHECKNPOP 1088 +#define IDC_CHECKFPOP 1089 +#define IDC_EDITPOPS 1090 +#define IDC_EDITNPOPS 1091 +#define IDC_EDITFPOPS 1092 +#define IDC_GBNEWMAIL 1094 +#define IDC_GBNONEWMAIL 1095 +#define IDC_GBBADCONNECT 1096 +#define IDC_STSTATUS 1102 +#define IDC_COMBOPLUGINS 1104 +#define IDC_STWWW 1111 +#define IDC_STCOPY 1114 +#define IDC_STDESC 1115 +#define IDC_STVER 1116 +#define IDC_CHECKTTB 1117 +#define IDC_CHECKSSL 1117 +#define IDC_CHECKNMSGP 1118 +#define IDC_CHECKNOTLS 1120 +#define IDC_CHECKKBN 1121 +#define IDC_BTNSTATUS 1123 +#define IDC_OPTIONSTAB 1124 +#define IDC_BTNCHECKALL 1125 +#define IDC_MAINMENU 1126 +#define IDC_CLOSEONDELETE 1127 +#define IDC_LONGDATE 1128 +#define IDC_SMARTDATE 1129 +#define IDC_NOSECONDS 1130 +#define IDC_YAMNASPROTO 1131 +#define IDC_CHECKAPOP 1200 +#define IDC_STATUSGROUP 1338 +#define IDC_SPLITTER 1400 +#define IDC_EDITBODY 1401 +#define IDC_PREVIEW 1402 +#define IDC_BTNADD 1403 +#define IDC_EDITNAME 1404 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 143 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1407 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/protocols/YAMN/src/services.cpp b/protocols/YAMN/src/services.cpp new file mode 100644 index 0000000000..e9ba7b0aaf --- /dev/null +++ b/protocols/YAMN/src/services.cpp @@ -0,0 +1,450 @@ +#include "stdafx.h" + +static INT_PTR Service_GetCaps(WPARAM wParam, LPARAM) +{ + if (wParam == PFLAGNUM_4) + return PF4_NOCUSTOMAUTH; + if (wParam == PFLAG_UNIQUEIDTEXT) + return (INT_PTR)Translate("Nick"); + if (wParam == PFLAG_MAXLENOFMESSAGE) + return 400; + if (wParam == PFLAGNUM_2) + return PF2_ONLINE | PF2_SHORTAWAY | PF2_LONGAWAY | PF2_LIGHTDND; + if (wParam == PFLAGNUM_5) { + if (g_plugin.getByte(YAMN_SHOWASPROTO, 1)) + return PF2_SHORTAWAY | PF2_LONGAWAY | PF2_LIGHTDND; + return PF2_ONLINE | PF2_SHORTAWAY | PF2_LONGAWAY | PF2_LIGHTDND; + } + return 0; +} + +static INT_PTR Service_GetStatus(WPARAM, LPARAM) +{ + return YAMN_STATUS; +} + +static INT_PTR Service_SetStatus(WPARAM wParam, LPARAM) +{ + int newstatus = (wParam != ID_STATUS_OFFLINE) ? ID_STATUS_ONLINE : ID_STATUS_OFFLINE; + if (newstatus != YAMN_STATUS) { + int oldstatus = YAMN_STATUS; + YAMN_STATUS = newstatus; + ProtoBroadcastAck(YAMN_DBMODULE, NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)oldstatus, newstatus); + } + return 0; + +} + +static INT_PTR Service_GetName(WPARAM wParam, LPARAM lParam) +{ + mir_strncpy((char *)lParam, YAMN_DBMODULE, wParam); + return 0; +} + +static INT_PTR Service_LoadIcon(WPARAM wParam, LPARAM) +{ + if (LOWORD(wParam) == PLI_PROTOCOL) + return (INT_PTR)CopyIcon(g_LoadIconEx(0)); // noone cares about other than PLI_PROTOCOL + + return (INT_PTR)(HICON)NULL; +} + +INT_PTR ClistContactDoubleclicked(WPARAM, LPARAM lParam) +{ + ContactDoubleclicked(((CLISTEVENT*)lParam)->lParam, lParam); + return 0; +} + +static int Service_ContactDoubleclicked(WPARAM wParam, LPARAM lParam) +{ + ContactDoubleclicked(wParam, lParam); + return 0; +} + +static INT_PTR ContactApplication(WPARAM wParam, LPARAM) +{ + char *szProto = GetContactProto(wParam); + if (mir_strcmp(szProto, YAMN_DBMODULE)) + return 0; + + DBVARIANT dbv; + if (g_plugin.getString(wParam, "Id", &dbv)) + return 0; + + HACCOUNT ActualAccount = (HACCOUNT)CallService(MS_YAMN_FINDACCOUNTBYNAME, (WPARAM)POP3Plugin, (LPARAM)dbv.pszVal); + if (ActualAccount != nullptr) { + STARTUPINFOW si = { 0 }; + si.cb = sizeof(si); + +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "ContactApplication:ActualAccountSO-read wait\n"); +#endif + if (WAIT_OBJECT_0 == WaitToReadFcn(ActualAccount->AccountAccessSO)) { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "ContactApplication:ualAccountSO-read enter\n"); +#endif + if (ActualAccount->NewMailN.App != nullptr) { + WCHAR *Command; + if (ActualAccount->NewMailN.AppParam != nullptr) + Command = new WCHAR[mir_wstrlen(ActualAccount->NewMailN.App) + mir_wstrlen(ActualAccount->NewMailN.AppParam) + 6]; + else + Command = new WCHAR[mir_wstrlen(ActualAccount->NewMailN.App) + 6]; + + if (Command != nullptr) { + mir_wstrcpy(Command, L"\""); + mir_wstrcat(Command, ActualAccount->NewMailN.App); + mir_wstrcat(Command, L"\" "); + if (ActualAccount->NewMailN.AppParam != nullptr) + mir_wstrcat(Command, ActualAccount->NewMailN.AppParam); + + PROCESS_INFORMATION pi; + CreateProcessW(nullptr, Command, nullptr, nullptr, FALSE, NORMAL_PRIORITY_CLASS, nullptr, nullptr, &si, &pi); + delete[] Command; + } + } + +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "ContactApplication:ActualAccountSO-read done\n"); +#endif + ReadDoneFcn(ActualAccount->AccountAccessSO); + } +#ifdef DEBUG_SYNCHRO + else + DebugLog(SynchroFile, "ContactApplication:ActualAccountSO-read enter failed\n"); +#endif + } + db_free(&dbv); + return 0; +} + +DWORD WINAPI SWMRGWaitToRead(PSWMRG pSWMRG, DWORD dwTimeout); +static INT_PTR AccountMailCheck(WPARAM wParam, LPARAM lParam) +{ + //This service will check/sincronize the account pointed by wParam + HACCOUNT ActualAccount = (HACCOUNT)wParam; + // copy/paste make mistakes + if (ActualAccount != nullptr) { + //we use event to signal, that running thread has all needed stack parameters copied + HANDLE ThreadRunningEV = CreateEvent(nullptr, FALSE, FALSE, nullptr); + if (ThreadRunningEV == nullptr) + return 0; + //if we want to close miranda, we get event and do not run pop3 checking anymore + if (WAIT_OBJECT_0 == WaitForSingleObject(ExitEV, 0)) + return 0; + + mir_cslock lck(PluginRegCS); +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "AccountCheck:ActualAccountSO-read wait\n"); +#endif + if (WAIT_OBJECT_0 != SWMRGWaitToRead(ActualAccount->AccountAccessSO, 0)) { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "ForceCheck:ActualAccountSO-read wait failed\n"); +#endif + } + else { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "ForceCheck:ActualAccountSO-read enter\n"); +#endif + if ((ActualAccount->Flags & YAMN_ACC_ENA) && ActualAccount->Plugin->Fcn->SynchroFcnPtr) { + CheckParam ParamToPlugin = { YAMN_CHECKVERSION, ThreadRunningEV, ActualAccount, lParam ? YAMN_FORCECHECK : YAMN_NORMALCHECK, nullptr, nullptr }; + + ActualAccount->TimeLeft = ActualAccount->Interval; + DWORD tid; + HANDLE NewThread = CreateThread(nullptr, 0, (YAMN_STANDARDFCN)ActualAccount->Plugin->Fcn->SynchroFcnPtr, &ParamToPlugin, 0, &tid); + if (NewThread) { + WaitForSingleObject(ThreadRunningEV, INFINITE); + CloseHandle(NewThread); + } + } + ReadDoneFcn(ActualAccount->AccountAccessSO); + } + CloseHandle(ThreadRunningEV); + } + return 0; +} + +static INT_PTR ContactMailCheck(WPARAM hContact, LPARAM) +{ + char *szProto = GetContactProto(hContact); + if (mir_strcmp(szProto, YAMN_DBMODULE)) + return 0; + + DBVARIANT dbv; + if (g_plugin.getString(hContact, "Id", &dbv)) + return 0; + + HACCOUNT ActualAccount = (HACCOUNT)CallService(MS_YAMN_FINDACCOUNTBYNAME, (WPARAM)POP3Plugin, (LPARAM)dbv.pszVal); + if (ActualAccount != nullptr) { + //we use event to signal, that running thread has all needed stack parameters copied + HANDLE ThreadRunningEV; + if (nullptr == (ThreadRunningEV = CreateEvent(nullptr, FALSE, FALSE, nullptr))) + return 0; + //if we want to close miranda, we get event and do not run pop3 checking anymore + if (WAIT_OBJECT_0 == WaitForSingleObject(ExitEV, 0)) + return 0; + mir_cslock lck(PluginRegCS); +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "ForceCheck:ActualAccountSO-read wait\n"); +#endif + if (WAIT_OBJECT_0 != WaitToReadFcn(ActualAccount->AccountAccessSO)) { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "ForceCheck:ActualAccountSO-read wait failed\n"); +#endif + } + else { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "ForceCheck:ActualAccountSO-read enter\n"); +#endif + if ((ActualAccount->Flags & YAMN_ACC_ENA) && (ActualAccount->StatusFlags & YAMN_ACC_FORCE)) //account cannot be forced to check + { + if (ActualAccount->Plugin->Fcn->ForceCheckFcnPtr == nullptr) + ReadDoneFcn(ActualAccount->AccountAccessSO); + + DWORD tid; + struct CheckParam ParamToPlugin = { YAMN_CHECKVERSION, ThreadRunningEV, ActualAccount, YAMN_FORCECHECK, (void *)nullptr, nullptr }; + if (nullptr == CreateThread(nullptr, 0, (YAMN_STANDARDFCN)ActualAccount->Plugin->Fcn->ForceCheckFcnPtr, &ParamToPlugin, 0, &tid)) + ReadDoneFcn(ActualAccount->AccountAccessSO); + else + WaitForSingleObject(ThreadRunningEV, INFINITE); + } + ReadDoneFcn(ActualAccount->AccountAccessSO); + } + CloseHandle(ThreadRunningEV); + } + db_free(&dbv); + return 0; +} + +/*static*/ void ContactDoubleclicked(WPARAM wParam, LPARAM) +{ + char *szProto = GetContactProto(wParam); + if (mir_strcmp(szProto, YAMN_DBMODULE)) + return; + + DBVARIANT dbv; + if (g_plugin.getString(wParam, "Id", &dbv)) + return; + + HACCOUNT ActualAccount = (HACCOUNT)CallService(MS_YAMN_FINDACCOUNTBYNAME, (WPARAM)POP3Plugin, (LPARAM)dbv.pszVal); + if (ActualAccount != nullptr) { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "Service_ContactDoubleclicked:ActualAccountSO-read wait\n"); +#endif + if (WAIT_OBJECT_0 == WaitToReadFcn(ActualAccount->AccountAccessSO)) { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "Service_ContactDoubleclicked:ActualAccountSO-read enter\n"); +#endif + YAMN_MAILBROWSERPARAM Param = { nullptr, ActualAccount, ActualAccount->NewMailN.Flags, ActualAccount->NoNewMailN.Flags, nullptr }; + + Param.nnflags = Param.nnflags | YAMN_ACC_MSG; //show mails in account even no new mail in account + Param.nnflags = Param.nnflags & ~YAMN_ACC_POP; + + Param.nflags = Param.nflags | YAMN_ACC_MSG; //show mails in account even no new mail in account + Param.nflags = Param.nflags & ~YAMN_ACC_POP; + + RunMailBrowserSvc((WPARAM)&Param, YAMN_MAILBROWSERVERSION); + +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "Service_ContactDoubleclicked:ActualAccountSO-read done\n"); +#endif + ReadDoneFcn(ActualAccount->AccountAccessSO); + } +#ifdef DEBUG_SYNCHRO + else + DebugLog(SynchroFile, "Service_ContactDoubleclicked:ActualAccountSO-read enter failed\n"); +#endif + + } + db_free(&dbv); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +HBITMAP LoadBmpFromIcon(HICON hIcon) +{ + int IconSizeX = 16; + int IconSizeY = 16; + + HBRUSH hBkgBrush = CreateSolidBrush(GetSysColor(COLOR_3DFACE)); + + BITMAPINFOHEADER bih = { 0 }; + bih.biSize = sizeof(bih); + bih.biBitCount = 24; + bih.biPlanes = 1; + bih.biCompression = BI_RGB; + bih.biHeight = IconSizeY; + bih.biWidth = IconSizeX; + + RECT rc; + rc.top = rc.left = 0; + rc.right = bih.biWidth; + rc.bottom = bih.biHeight; + + HDC hdc = GetDC(nullptr); + HBITMAP hBmp = CreateCompatibleBitmap(hdc, bih.biWidth, bih.biHeight); + HDC hdcMem = CreateCompatibleDC(hdc); + HBITMAP hoBmp = (HBITMAP)SelectObject(hdcMem, hBmp); + FillRect(hdcMem, &rc, hBkgBrush); + DrawIconEx(hdcMem, 0, 0, hIcon, bih.biWidth, bih.biHeight, 0, nullptr, DI_NORMAL); + SelectObject(hdcMem, hoBmp); + return hBmp; +} + +int AddTopToolbarIcon(WPARAM, LPARAM) +{ + if (g_plugin.getByte(YAMN_TTBFCHECK, 1)) { + if (ServiceExists(MS_TTB_REMOVEBUTTON) && hTTButton == nullptr) { + TTBButton btn = {}; + btn.pszService = MS_YAMN_FORCECHECK; + btn.dwFlags = TTBBF_VISIBLE | TTBBF_SHOWTOOLTIP; + btn.hIconHandleUp = btn.hIconHandleDn = g_GetIconHandle(0); + btn.name = btn.pszTooltipUp = LPGEN("Check mail"); + hTTButton = g_plugin.addTTB(&btn); + } + } + else { + if (hTTButton != nullptr) { + CallService(MS_TTB_REMOVEBUTTON, (WPARAM)hTTButton, 0); + hTTButton = nullptr; + } + } + + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +int Shutdown(WPARAM, LPARAM) +{ + CallService(MS_TTB_REMOVEBUTTON, (WPARAM)hTTButton, 0); + + g_plugin.setDword(YAMN_DBMSGPOSX, HeadPosX); + g_plugin.setDword(YAMN_DBMSGPOSY, HeadPosY); + g_plugin.setDword(YAMN_DBMSGSIZEX, HeadSizeX); + g_plugin.setDword(YAMN_DBMSGSIZEY, HeadSizeY); + g_plugin.setWord(YAMN_DBMSGPOSSPLIT, HeadSplitPos); + YAMNVar.Shutdown = TRUE; + KillTimer(nullptr, SecTimer); + + UnregisterProtoPlugins(); + UnregisterFilterPlugins(); + return 0; +} + +int SystemModulesLoaded(WPARAM, LPARAM); //in main.cpp + +void HookEvents(void) +{ + HookEvent(ME_SYSTEM_MODULESLOADED, SystemModulesLoaded); + HookEvent(ME_TTB_MODULELOADED, AddTopToolbarIcon); + HookEvent(ME_OPT_INITIALISE, YAMNOptInitSvc); + HookEvent(ME_SYSTEM_PRESHUTDOWN, Shutdown); + HookEvent(ME_CLIST_DOUBLECLICKED, Service_ContactDoubleclicked); +} + +void CreateServiceFunctions(void) +{ + // Standard 'protocol' services + CreateServiceFunction(YAMN_DBMODULE PS_GETCAPS, Service_GetCaps); + CreateServiceFunction(YAMN_DBMODULE PS_GETSTATUS, Service_GetStatus); + CreateServiceFunction(YAMN_DBMODULE PS_SETSTATUS, Service_SetStatus); + CreateServiceFunction(YAMN_DBMODULE PS_GETNAME, Service_GetName); + CreateServiceFunction(YAMN_DBMODULE PS_LOADICON, Service_LoadIcon); + + // Function with which protocol plugin can register + CreateServiceFunction(MS_YAMN_GETFCNPTR, GetFcnPtrSvc); + + // Function returns pointer to YAMN variables + CreateServiceFunction(MS_YAMN_GETVARIABLES, GetVariablesSvc); + + // Function with which protocol plugin can register + CreateServiceFunction(MS_YAMN_REGISTERPROTOPLUGIN, RegisterProtocolPluginSvc); + + // Function with which protocol plugin can unregister + CreateServiceFunction(MS_YAMN_UNREGISTERPROTOPLUGIN, UnregisterProtocolPluginSvc); + + // Function creates an account for plugin + CreateServiceFunction(MS_YAMN_CREATEPLUGINACCOUNT, CreatePluginAccountSvc); + + // Function deletes plugin account + CreateServiceFunction(MS_YAMN_DELETEPLUGINACCOUNT, DeletePluginAccountSvc); + + // Finds account for plugin by name + CreateServiceFunction(MS_YAMN_FINDACCOUNTBYNAME, FindAccountByNameSvc); + + // Creates next account for plugin + CreateServiceFunction(MS_YAMN_GETNEXTFREEACCOUNT, GetNextFreeAccountSvc); + + // Function removes account from YAMN queue. Does not delete it from memory + CreateServiceFunction(MS_YAMN_DELETEACCOUNT, DeleteAccountSvc); + + // Function finds accounts for specified plugin + CreateServiceFunction(MS_YAMN_READACCOUNTS, AddAccountsFromFileSvc); + + // Function that stores all plugin mails to one file + CreateServiceFunction(MS_YAMN_WRITEACCOUNTS, WriteAccountsToFileSvc); + + // Function that returns user's filename + CreateServiceFunction(MS_YAMN_GETFILENAME, GetFileNameSvc); + + // Releases unicode string from memory + CreateServiceFunction(MS_YAMN_DELETEFILENAME, DeleteFileNameSvc); + + // Checks mail + CreateServiceFunction(MS_YAMN_FORCECHECK, ForceCheckSvc); + + // Runs YAMN's mail browser + CreateServiceFunction(MS_YAMN_MAILBROWSER, RunMailBrowserSvc); + + // Runs YAMN's bad conenction window + CreateServiceFunction(MS_YAMN_BADCONNECTION, RunBadConnectionSvc); + + // Function creates new mail for plugin + CreateServiceFunction(MS_YAMN_CREATEACCOUNTMAIL, CreateAccountMailSvc); + + // Function deletes plugin account + CreateServiceFunction(MS_YAMN_DELETEACCOUNTMAIL, DeleteAccountMailSvc); + + // Function with which filter plugin can register + CreateServiceFunction(MS_YAMN_REGISTERFILTERPLUGIN, RegisterFilterPluginSvc); + + // Function with which filter plugin can unregister + CreateServiceFunction(MS_YAMN_UNREGISTERFILTERPLUGIN, UnregisterFilterPluginSvc); + + // Function filters mail + CreateServiceFunction(MS_YAMN_FILTERMAIL, FilterMailSvc); + + // Function contact list double click + CreateServiceFunction(MS_YAMN_CLISTDBLCLICK, ClistContactDoubleclicked); + + // Function to check individual account + CreateServiceFunction(MS_YAMN_ACCOUNTCHECK, AccountMailCheck); + + // Function contact list context menu click + CreateServiceFunction(MS_YAMN_CLISTCONTEXT, ContactMailCheck); + + // Function contact list context menu click + CreateServiceFunction(MS_YAMN_CLISTCONTEXTAPP, ContactApplication); +} + +//Function to put all enabled contact to the Online status +void RefreshContact(void) +{ + HACCOUNT Finder; + for (Finder = POP3Plugin->FirstAccount; Finder != nullptr; Finder = Finder->Next) { + if (Finder->hContact != NULL) { + if ((Finder->Flags & YAMN_ACC_ENA) && (Finder->NewMailN.Flags & YAMN_ACC_CONT)) + db_unset(Finder->hContact, "CList", "Hidden"); + else + db_set_b(Finder->hContact, "CList", "Hidden", 1); + } + else if ((Finder->Flags & YAMN_ACC_ENA) && (Finder->NewMailN.Flags & YAMN_ACC_CONT)) { + Finder->hContact = db_add_contact(); + Proto_AddToContact(Finder->hContact, YAMN_DBMODULE); + g_plugin.setString(Finder->hContact, "Id", Finder->Name); + g_plugin.setString(Finder->hContact, "Nick", Finder->Name); + g_plugin.setWord(Finder->hContact, "Status", ID_STATUS_ONLINE); + db_set_s(Finder->hContact, "CList", "StatusMsg", Translate("No new mail message")); + } + } +} diff --git a/protocols/YAMN/src/stdafx.cxx b/protocols/YAMN/src/stdafx.cxx new file mode 100644 index 0000000000..1b563fc866 --- /dev/null +++ b/protocols/YAMN/src/stdafx.cxx @@ -0,0 +1,18 @@ +/* +Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org) + +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 version 2 +of the License. + +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, see <http://www.gnu.org/licenses/>. +*/ + +#include "stdafx.h"
\ No newline at end of file diff --git a/protocols/YAMN/src/stdafx.h b/protocols/YAMN/src/stdafx.h new file mode 100644 index 0000000000..246e9468fa --- /dev/null +++ b/protocols/YAMN/src/stdafx.h @@ -0,0 +1,262 @@ + +#ifndef __YAMN_H +#define __YAMN_H + +#define VC_EXTRALEAN + +#include <windows.h> +#include <windowsx.h> +#include <commctrl.h> + +#include <win2k.h> +#include <newpluginapi.h> +#include <m_skin.h> +#include <m_langpack.h> +#include <m_clistint.h> +#include <m_options.h> +#include <m_database.h> +#include <m_protosvc.h> +#include <m_icolib.h> +#include <m_popup.h> +#include <m_messages.h> +#include <m_netlib.h> +#include <m_hotkeys.h> +#include <m_timezones.h> + +#include <m_toptoolbar.h> +#include <m_kbdnotify.h> +#include <m_filterplugin.h> +#include <m_yamn.h> +#include <m_protoplugin.h> +#include <m_folders.h> + +#include "main.h" +#include "mails/decode.h" +#include "browser/browser.h" +#include "resource.h" +#include "debug.h" +#include "version.h" +#include "proto/netclient.h" +#include "proto/netlib.h" +#include "proto/pop3/pop3.h" +#include "proto/pop3/pop3comm.h" +#include "proto/pop3/pop3opt.h" + +struct CMPlugin : public PLUGIN<CMPlugin> +{ + CMPlugin(); + + int Load() override; + int Unload() override; +}; + +// From services.cpp +void CreateServiceFunctions(void); +void HookEvents(void); +void RefreshContact(void); +void ContactDoubleclicked(WPARAM wParam, LPARAM lParam); +INT_PTR ClistContactDoubleclicked(WPARAM wParam, LPARAM lParam); + +extern mir_cs PluginRegCS; +extern SCOUNTER *AccountWriterSO; +extern HANDLE ExitEV; +extern HANDLE WriteToFileEV; + +// From debug.cpp +#ifdef _DEBUG +void InitDebug(); +void UnInitDebug(); +#endif + +// From yamn.cpp +INT_PTR GetFcnPtrSvc(WPARAM wParam, LPARAM lParam); +INT_PTR GetVariablesSvc(WPARAM, LPARAM); +void CALLBACK TimerProc(HWND, UINT, UINT_PTR, DWORD); +INT_PTR ForceCheckSvc(WPARAM, LPARAM); + +extern struct YAMNExportedFcns *pYAMNFcn; + +// From account.cpp +INT_PTR CreatePluginAccountSvc(WPARAM wParam, LPARAM lParam); +INT_PTR DeletePluginAccountSvc(WPARAM wParam, LPARAM); +int InitAccount(HACCOUNT Which); +void DeInitAccount(HACCOUNT Which); +void StopSignalFcn(HACCOUNT Which); +void CodeDecodeString(char *Dest, BOOL Encrypt); +DWORD FileToMemory(wchar_t *FileName, char **MemFile, char **End); + +#if defined(DEBUG_FILEREAD) || defined(DEBUG_FILEREADMESSAGES) +DWORD ReadStringFromMemory(char **Parser,char *End,char **StoreTo,char *DebugString); +#endif +DWORD ReadStringFromMemory(char **Parser, char *End, char **StoreTo); +DWORD ReadMessagesFromMemory(HACCOUNT Which, char **Parser, char *End); +DWORD ReadAccountFromMemory(HACCOUNT Which, char **Parser, wchar_t *End); +INT_PTR AddAccountsFromFileSvc(WPARAM wParam, LPARAM lParam); + +DWORD WriteStringToFile(HANDLE File, char *Source); +DWORD WriteStringToFileW(HANDLE File, WCHAR *Source); + + +DWORD WriteMessagesToFile(HANDLE File, HACCOUNT Which); +DWORD WINAPI WritePOP3Accounts(); +INT_PTR WriteAccountsToFileSvc(WPARAM wParam, LPARAM lParam); +INT_PTR FindAccountByNameSvc(WPARAM wParam, LPARAM lParam); +INT_PTR GetNextFreeAccountSvc(WPARAM wParam, LPARAM lParam); + +INT_PTR DeleteAccountSvc(WPARAM wParam, LPARAM); +void __cdecl DeleteAccountInBackground(void *Which); +int StopAccounts(HYAMNPROTOPLUGIN Plugin); +int WaitForAllAccounts(HYAMNPROTOPLUGIN Plugin, BOOL GetAccountBrowserAccess = FALSE); +int DeleteAccounts(HYAMNPROTOPLUGIN Plugin); + +void WINAPI GetStatusFcn(HACCOUNT Which, wchar_t *Value); +void WINAPI SetStatusFcn(HACCOUNT Which, wchar_t *Value); + +INT_PTR UnregisterProtoPlugins(); +INT_PTR RegisterProtocolPluginSvc(WPARAM, LPARAM); +INT_PTR UnregisterProtocolPluginSvc(WPARAM, LPARAM); +INT_PTR GetFileNameSvc(WPARAM, LPARAM); +INT_PTR DeleteFileNameSvc(WPARAM, LPARAM); + +//From filterplugin.cpp +//struct CExportedFunctions FilterPluginExported[]; +INT_PTR UnregisterFilterPlugins(); +INT_PTR RegisterFilterPluginSvc(WPARAM, LPARAM); +INT_PTR UnregisterFilterPluginSvc(WPARAM, LPARAM); +INT_PTR FilterMailSvc(WPARAM, LPARAM); + +//From mails.cpp (MIME) +//struct CExportedFunctions MailExported[]; +INT_PTR CreateAccountMailSvc(WPARAM wParam, LPARAM lParam); +INT_PTR DeleteAccountMailSvc(WPARAM wParam, LPARAM lParam); +INT_PTR LoadMailDataSvc(WPARAM wParam, LPARAM lParam); +INT_PTR UnloadMailDataSvc(WPARAM wParam, LPARAM); +INT_PTR SaveMailDataSvc(WPARAM wParam, LPARAM lParam); + +//From mime.cpp +//void WINAPI ExtractHeaderFcn(char *,int,WORD,HYAMNMAIL); //already in MailExported +struct _tcptable +{ + char *NameBase, *NameSub; + BOOLEAN isValid; + unsigned short int CP; +}; +extern struct _tcptable CodePageNamesAll[]; // in mime/decode.cpp +extern int CPLENALL; +extern struct _tcptable *CodePageNamesSupp; // in mime/decode.cpp +extern int CPLENSUPP; + +extern int PosX, PosY, SizeX, SizeY; +extern int HeadPosX, HeadPosY, HeadSizeX, HeadSizeY, HeadSplitPos; + +//#define CPDEFINDEX 63 //ISO-8859-1 +#define CPDEFINDEX 0 //ACP + +//From pop3comm.cpp +int RegisterPOP3Plugin(WPARAM, LPARAM); + +//From mailbrowser.cpp +INT_PTR RunMailBrowserSvc(WPARAM, LPARAM); + +//From badconnect.cpp +INT_PTR RunBadConnectionSvc(WPARAM, LPARAM); + +//From YAMNopts.cpp +int YAMNOptInitSvc(WPARAM, LPARAM); + +//From main.cpp +int PostLoad(WPARAM, LPARAM); //Executed after all plugins loaded YAMN reads mails from file and notify every protocol it should set its functions +int Shutdown(WPARAM, LPARAM); //Executed before Miranda is going to shutdown +int AddTopToolbarIcon(WPARAM, LPARAM); //Executed when TopToolBar plugin loaded Adds bitmap to toolbar + +extern wchar_t UserDirectory[]; //e.g. "F:\WINNT\Profiles\UserXYZ" +extern wchar_t ProfileName[]; //e.g. "majvan" +extern SWMRG *AccountBrowserSO; +extern YAMN_VARIABLES YAMNVar; +extern HANDLE hNewMailHook; +extern HANDLE hTTButton; +extern HCURSOR hCurSplitNS, hCurSplitWE; +extern UINT SecTimer; + +HANDLE WINAPI g_GetIconHandle(int idx); +HICON WINAPI g_LoadIconEx(int idx, bool big = false); + +//From synchro.cpp +void WINAPI DeleteMessagesToEndFcn(HACCOUNT Account, HYAMNMAIL From); +DWORD WINAPI WaitToWriteFcn(PSWMRG SObject, PSCOUNTER SCounter = nullptr); +void WINAPI WriteDoneFcn(PSWMRG SObject, PSCOUNTER SCounter = nullptr); +DWORD WINAPI WaitToReadFcn(PSWMRG SObject); +void WINAPI ReadDoneFcn(PSWMRG SObject); +DWORD WINAPI SCIncFcn(PSCOUNTER SCounter); +DWORD WINAPI SCDecFcn(PSCOUNTER SCounter); +BOOL WINAPI SWMRGInitialize(PSWMRG, wchar_t *); +void WINAPI SWMRGDelete(PSWMRG); +DWORD WINAPI SWMRGWaitToWrite(PSWMRG pSWMRG, DWORD dwTimeout); +void WINAPI SWMRGDoneWriting(PSWMRG pSWMRG); +DWORD WINAPI SWMRGWaitToRead(PSWMRG pSWMRG, DWORD dwTimeout); +void WINAPI SWMRGDoneReading(PSWMRG pSWMRG); + +//From mails.cpp +void WINAPI DeleteMessageFromQueueFcn(HYAMNMAIL *From, HYAMNMAIL Which, int mode); +void WINAPI SetRemoveFlagsInQueueFcn(HYAMNMAIL From, DWORD FlagsSet, DWORD FlagsNotSet, DWORD FlagsToSet, int mode); + +//From mime.cpp +void ExtractHeader(struct CMimeItem *items, int &CP, struct CHeader *head); +void ExtractShortHeader(struct CMimeItem *items, struct CShortHeader *head); +void DeleteHeaderContent(struct CHeader *head); +void DeleteShortHeaderContent(struct CShortHeader *head); +char *ExtractFromContentType(char *ContentType, char *value); +WCHAR *ParseMultipartBody(char *src, char *bond); + +//From account.cpp +void WINAPI GetStatusFcn(HACCOUNT Which, wchar_t *Value); + +extern HYAMNPROTOPLUGIN POP3Plugin; + +//from decode.cpp +int DecodeQuotedPrintable(char *Src, char *Dst, int DstLen, BOOL isQ); +int DecodeBase64(char *Src, char *Dst, int DstLen); + +//From filterplugin.cpp +extern PYAMN_FILTERPLUGINQUEUE FirstFilterPlugin; + +//From protoplugin.cpp +extern PYAMN_PROTOPLUGINQUEUE FirstProtoPlugin; + +extern struct CExportedFunctions ProtoPluginExportedFcn[1]; +extern struct CExportedServices ProtoPluginExportedSvc[5]; +//From filterplugin.cpp +extern struct CExportedFunctions FilterPluginExportedFcn[1]; +extern struct CExportedServices FilterPluginExportedSvc[2]; +//From synchro.cpp +extern struct CExportedFunctions SynchroExportedFcn[7]; +//From account.cpp +extern struct CExportedFunctions AccountExportedFcn[2]; +extern struct CExportedServices AccountExportedSvc[9]; +//From mails.cpp (MIME) +extern struct CExportedFunctions MailExportedFcn[8]; +extern struct CExportedServices MailExportedSvc[5]; + +extern char *iconDescs[]; +extern char *iconNames[]; +extern HIMAGELIST CSImages; + +extern void __stdcall SSL_DebugLog(const char *fmt, ...); + +extern int YAMN_STATUS; + +extern struct WndHandles *MessageWnd; + +extern int GetCharsetFromString(char *input, size_t size); +extern void SendMsgToRecepients(struct WndHandles *FirstWin, UINT msg, WPARAM wParam, LPARAM lParam); +extern void ConvertCodedStringToUnicode(char *stream, WCHAR **storeto, DWORD cp, int mode); +extern void __cdecl MailBrowser(void *Param); +extern DWORD WINAPI NoNewMailProc(LPVOID Param); +extern void __cdecl BadConnection(void *Param); +extern PVOID TLSCtx; +extern PVOID SSLCtx; + +extern HGENMENU hMenuItemMain, hMenuItemCont, hMenuItemContApp; +extern PYAMN_VARIABLES pYAMNVar; + +#endif diff --git a/protocols/YAMN/src/synchro.cpp b/protocols/YAMN/src/synchro.cpp new file mode 100644 index 0000000000..433f0768b6 --- /dev/null +++ b/protocols/YAMN/src/synchro.cpp @@ -0,0 +1,359 @@ +/* + * This code implements synchronization objects code between threads. If you want, you can include it to your + * code. This file is not dependent on any other external code (functions) + * + * (c) majvan 2002-2004 + */ + +#include "stdafx.h" + +// Initializes a SWMRG structure. This structure must be +// initialized before any writer or reader threads attempt +// to wait on it. +// The structure must be allocated by the application and +// the structure's address is passed as the first parameter. +// The lpszName parameter is the name of the object. Pass +// NULL if you do not want to share the object. +BOOL WINAPI SWMRGInitialize(PSWMRG pSWMRG,wchar_t *Name); + +// Deletes the system resources associated with a SWMRG +// structure. The structure must be deleted only when +// no writer or reader threads in the calling process +// will wait on it. +void WINAPI SWMRGDelete(PSWMRG pSWMRG); + +// A writer thread calls this function to know when +// it can successfully write to the shared data. +// returns WAIT_FINISH when we are in write-access or WAIT_FAILED +// when event about quick finishing is set (or when system returns fail when waiting for synchro object) +DWORD WINAPI SWMRGWaitToWrite(PSWMRG pSWMRG,DWORD dwTimeout); + +// A writer thread calls this function to let other threads +// know that it no longer needs to write to the shared data. +void WINAPI SWMRGDoneWriting(PSWMRG pSWMRG); + +// A reader thread calls this function to know when +// it can successfully read the shared data. +// returns WAIT_FINISH when we are in read-access or WAIT_FAILED +// when event about quick finishing is set (or when system returns fail when waiting for synchro object) +DWORD WINAPI SWMRGWaitToRead(PSWMRG pSWMRG, DWORD dwTimeout); + +// A reader thread calls this function to let other threads +// know when it no longer needs to read the shared data. +void WINAPI SWMRGDoneReading(PSWMRG pSWMRG); + +// WaitToReadFcn +// is used to wait for read access with SWMRG SO, but it also increments counter if successfull +// returns WAIT_FAILED or WAIT_FINISH +// when WAIT_FAILED, we should not begin to access datas, we are not in read-access mode +DWORD WINAPI WaitToReadFcn(PSWMRG SObject); + +// WriteDoneFcn +// is used to release read access with SWMRG SO, but it also decrements counter if successfull +void WINAPI ReadDoneFcn(PSWMRG SObject); + +// This functions is for export purposes +// Plugin can call this function to manage SCOUNTER synchronization object + +// Gets number value stored in SCOUNTER SO +// Note you must not read the number from memory directly, because +// CPU can stop reading thread when it has read HI-Word, then another thread +// can change the value and then OS starts the previous thread, that reads the +// LO-WORD of DWORD. And the return value HI+LO-WORD is corrupted +DWORD WINAPI SCGetNumberFcn(PSCOUNTER SCounter); + +// Increments SCOUNTER and unsets event +// Returns Number after incrementing +DWORD WINAPI SCIncFcn(PSCOUNTER SCounter); + +// Decrements SCOUNTER and sets event if zero +// Returns Number after decrementing +DWORD WINAPI SCDecFcn(PSCOUNTER SCounter); + +struct CExportedFunctions SynchroExportedFcn[]= +{ + {YAMN_WAITTOWRITEID,(void *)WaitToWriteFcn}, + {YAMN_WRITEDONEID,(void *)WriteDoneFcn}, + {YAMN_WAITTOREADID,(void *)WaitToReadFcn}, + {YAMN_READDONEID,(void *)ReadDoneFcn}, + {YAMN_SCGETNUMBERID,(void *)SCGetNumberFcn}, + {YAMN_SCINCID,(void *)SCIncFcn}, + {YAMN_SCDECID,(void *)SCDecFcn}, +}; + +//-------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------- + +void WINAPI SWMRGDelete(PSWMRG pSWMRG) +{ +// Destroys any synchronization objects that were +// successfully created. + if (nullptr != pSWMRG->hEventNoWriter) + CloseHandle(pSWMRG->hEventNoWriter); + if (nullptr != pSWMRG->hEventNoReaders) + CloseHandle(pSWMRG->hEventNoReaders); + if (nullptr != pSWMRG->hSemNumReaders) + CloseHandle(pSWMRG->hSemNumReaders); + if (nullptr != pSWMRG->hFinishEV) + CloseHandle(pSWMRG->hFinishEV); +} + +BOOL WINAPI SWMRGInitialize(PSWMRG pSWMRG,wchar_t *Name) +{ + pSWMRG->hEventNoWriter=nullptr; + pSWMRG->hEventNoReaders=nullptr; + pSWMRG->hSemNumReaders=nullptr; + pSWMRG->hFinishEV=nullptr; + +// Creates the automatic-reset event that is signalled when +// no writer threads are writing. +// Initially no reader threads are reading. + if (Name != nullptr) + Name[0]=(wchar_t)'W'; + pSWMRG->hEventNoWriter=CreateEvent(nullptr,FALSE,TRUE,Name); + +// Creates the manual-reset event that is signalled when +// no reader threads are reading. +// Initially no reader threads are reading. + if (Name != nullptr) + Name[0]=(wchar_t)'R'; + pSWMRG->hEventNoReaders=CreateEvent(nullptr,TRUE,TRUE,Name); + +// Initializes the variable that indicates the number of +// reader threads that are reading. +// Initially no reader threads are reading. + if (Name != nullptr) + Name[0]=(wchar_t)'C'; + pSWMRG->hSemNumReaders=CreateSemaphore(nullptr,0,0x7FFFFFFF,Name); + + if (Name != nullptr) + Name[0]=(wchar_t)'F'; + pSWMRG->hFinishEV=CreateEvent(nullptr,TRUE,FALSE,Name); + +// If a synchronization object could not be created, +// destroys any created objects and return failure. + if ((nullptr==pSWMRG->hEventNoWriter) || (nullptr==pSWMRG->hEventNoReaders) || (nullptr==pSWMRG->hSemNumReaders) || (nullptr==pSWMRG->hFinishEV)) + { + SWMRGDelete(pSWMRG); + return FALSE; + } + return TRUE; +} + +DWORD WINAPI SWMRGWaitToWrite(PSWMRG pSWMRG,DWORD dwTimeout) +{ + DWORD dw; + HANDLE aHandles[2]; + +// We can write if the following are true: +// 1. No other threads are writing. +// 2. No threads are reading. +// But first we have to know if SWMRG structure is not about to delete + aHandles[0]=pSWMRG->hEventNoWriter; + aHandles[1]=pSWMRG->hEventNoReaders; + if (WAIT_OBJECT_0==(dw=WaitForSingleObject(pSWMRG->hFinishEV,0))) + return WAIT_FINISH; + if (WAIT_FAILED==dw) + return dw; + dw=WaitForMultipleObjects(2,aHandles,TRUE,dwTimeout); +// if a request to delete became later, we should not catch it. Try once more to ask if account is not about to delete + if ((dw != WAIT_FAILED) && (WAIT_OBJECT_0==(WaitForSingleObject(pSWMRG->hFinishEV,0)))) + { + SetEvent(pSWMRG->hEventNoWriter); + return WAIT_FINISH; + } + +// This thread can write to the shared data. +// Automatic event for NoWriter sets hEventNoWriter to nonsignaled after WaitForMultipleObject + +// Because a writer thread is writing, the Event +// should not be reset. This stops other +// writers and readers. + return dw; +} + +void WINAPI SWMRGDoneWriting(PSWMRG pSWMRG) +// Presumably, a writer thread calling this function has +// successfully called WaitToWrite. This means that we +// do not have to wait on any synchronization objects +// here because the writer already owns the Event. +{ +// Allow other writer/reader threads to use +// the SWMRG synchronization object. + SetEvent(pSWMRG->hEventNoWriter); +} + +DWORD WINAPI SWMRGWaitToRead(PSWMRG pSWMRG, DWORD dwTimeout) +{ + DWORD dw; + LONG lPreviousCount; + +// We can read if no threads are writing. +// And there's not request to delete structure + if (WAIT_OBJECT_0==(dw=WaitForSingleObject(pSWMRG->hFinishEV,0))) + return WAIT_FINISH; + if (WAIT_FAILED==dw) + return dw; + dw=WaitForSingleObject(pSWMRG->hEventNoWriter, dwTimeout); +// if a request to delete became later, we should not catch it. Try once more to ask if account is not about to delete + if ((dw != WAIT_FAILED) && (WAIT_OBJECT_0==(WaitForSingleObject(pSWMRG->hFinishEV,0)))) + { + SetEvent(pSWMRG->hEventNoWriter); + return WAIT_FINISH; + } + + if (dw==WAIT_OBJECT_0) + { + // This thread can read from the shared data. + // Increment the number of reader threads. + // But there can't be more than one thread incrementing readers, + // so this is why we use semaphore. + ReleaseSemaphore(pSWMRG->hSemNumReaders,1,&lPreviousCount); + if (lPreviousCount==0) + // If this is the first reader thread, + // set event to reflect this. Other reader threads can read, no writer thread can write. + ResetEvent(pSWMRG->hEventNoReaders); + + // Allow other writer/reader threads to use + // the SWMRG synchronization object. hEventNoWrite is still non-signaled + // (it looks like writer is processing thread, but it is not true) + SetEvent(pSWMRG->hEventNoWriter); + } + + return(dw); +} + +void WINAPI SWMRGDoneReading(PSWMRG pSWMRG) +{ + HANDLE aHandles[2]; + LONG lNumReaders; + +// We can stop reading if the events are available, +// but when we stop reading we must also decrement the +// number of reader threads. + aHandles[0]=pSWMRG->hEventNoWriter; + aHandles[1]=pSWMRG->hSemNumReaders; + WaitForMultipleObjects(2,aHandles,TRUE,INFINITE); + +// Get the remaining number of readers by releasing the +// semaphore and then restoring the count by immediately +// performing a wait. + ReleaseSemaphore(pSWMRG->hSemNumReaders,1,&lNumReaders); + WaitForSingleObject(pSWMRG->hSemNumReaders,INFINITE); + +// If there are no remaining readers, +// set the event to relect this. + if (lNumReaders==0) + // If there are no reader threads, + // set our event to reflect this. + SetEvent(pSWMRG->hEventNoReaders); + +// Allow other writer/reader threads to use +// the SWMRG synchronization object. +// (it looks like writer is processing thread, but it is not true) + SetEvent(pSWMRG->hEventNoWriter); +} + +DWORD WINAPI WaitToWriteFcn(PSWMRG SObject,PSCOUNTER SCounter) +{ + DWORD EnterCode; +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"\tSO WaitToWrite: %x\n",SObject); +#endif + if (WAIT_OBJECT_0==(EnterCode=SWMRGWaitToWrite(SObject,INFINITE))) + if (SCounter != nullptr) + SCIncFcn(SCounter); + return EnterCode; +} + +void WINAPI WriteDoneFcn(PSWMRG SObject,PSCOUNTER SCounter) +{ +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"\tSO WriteDone: %x\n",SObject); +#endif + SWMRGDoneWriting(SObject); + if (SCounter != nullptr) + SCDecFcn(SCounter); +} + +DWORD WINAPI WaitToReadFcn(PSWMRG SObject) +{ + DWORD EnterCode; +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"\tSO WaitToRead: %x\n",SObject); +#endif + EnterCode=SWMRGWaitToRead(SObject,INFINITE); + return EnterCode; +} + +void WINAPI ReadDoneFcn(PSWMRG SObject) +{ +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"\tSO ReadDone: %x\n",SObject); +#endif + SWMRGDoneReading(SObject); +} + +DWORD WINAPI SCGetNumberFcn(PSCOUNTER SCounter) +{ + DWORD Temp; +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"\tGetNumber-cs wait\n"); +#endif + EnterCriticalSection(&SCounter->CounterCS); +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"\tGetNumber-cs enter\n"); +#endif + Temp=SCounter->Number; +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"\tValue: %d\n",Temp); + DebugLog(SynchroFile,"\tGetNumber-cs done\n"); +#endif + LeaveCriticalSection(&SCounter->CounterCS); + return Temp; +} + +DWORD WINAPI SCIncFcn(PSCOUNTER SCounter) +{ + DWORD Temp; +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"\tIncrementValue-cs wait\n"); +#endif + EnterCriticalSection(&SCounter->CounterCS); +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"\tIncrementValue-cs enter\n"); +#endif + Temp=++SCounter->Number; + ResetEvent(SCounter->Event); +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"\tValue: %d\n",Temp); + DebugLog(SynchroFile,"\tIncrementValue-cs done\n"); +#endif + LeaveCriticalSection(&SCounter->CounterCS); + return Temp; +} + +DWORD WINAPI SCDecFcn(PSCOUNTER SCounter) +{ + DWORD Temp; +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"\tDecrementValue-cs wait\n"); +#endif + EnterCriticalSection(&SCounter->CounterCS); +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"\tDecrementValue-cs enter\n"); +#endif + if (!(Temp=--SCounter->Number)) + { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"\tDecrementValue-zero ev set\n"); +#endif + SetEvent(SCounter->Event); + } +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile,"\tValue: %d\n",Temp); + DebugLog(SynchroFile,"\tDecrementValue-cs done\n"); +#endif + LeaveCriticalSection(&SCounter->CounterCS); + return Temp; +} diff --git a/protocols/YAMN/src/version.h b/protocols/YAMN/src/version.h new file mode 100644 index 0000000000..20429be688 --- /dev/null +++ b/protocols/YAMN/src/version.h @@ -0,0 +1,13 @@ +#define __MAJOR_VERSION 0 +#define __MINOR_VERSION 1 +#define __RELEASE_NUM 2 +#define __BUILD_NUM 6 + +#include <stdver.h> + +#define __PLUGIN_NAME "Mail Notifier" +#define __FILENAME "YAMN.dll" +#define __DESCRIPTION "Mail notifier and browser for Miranda NG. Included POP3 protocol." +#define __AUTHOR "y_b, tweety, majvan" +#define __AUTHORWEB "https://miranda-ng.org/p/YAMN/" +#define __COPYRIGHT "© 2002-2004 majvan, 2005-2007 tweety, y_b, Miranda community" diff --git a/protocols/YAMN/src/yamn.cpp b/protocols/YAMN/src/yamn.cpp new file mode 100644 index 0000000000..e45e22f06e --- /dev/null +++ b/protocols/YAMN/src/yamn.cpp @@ -0,0 +1,320 @@ +/* + * This code implements miscellaneous usefull functions + * + * (c) majvan 2002-2004 + */ + +#include "stdafx.h" + +//-------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------- + +//Plugin registration CS +//Used if we add (register) plugin to YAMN plugins and when we browse through registered plugins +mir_cs PluginRegCS; + +//AccountWriterCS +//We want to store number of writers of Accounts (number of Accounts used for writing) +//If we want to read all accounts (for saving to file) immidiatelly, we have to wait until no account is changing (no thread writing to account) +SCOUNTER *AccountWriterSO; + +//NoExitEV +//Event that is signaled when there's a request to exit, so no new pop3 check should be performed +HANDLE ExitEV; + +//WriteToFileEV +//If this is signaled, write accounts to file is performed. Set this event if you want to actualize your accounts and messages +HANDLE WriteToFileEV; + +//Returns pointer to YAMN exported function +INT_PTR GetFcnPtrSvc(WPARAM wParam, LPARAM lParam); + +//Returns pointer to YAMN variables +INT_PTR GetVariablesSvc(WPARAM wParam, LPARAM); + +// Function every seconds decrements account counter of seconds and checks if they are 0 +// If yes, creates a POP3 thread to check account +void CALLBACK TimerProc(HWND, UINT, UINT, DWORD); + +// Function called to check all accounts immidialtelly +// no params +INT_PTR ForceCheckSvc(WPARAM, LPARAM); + +//thread is running all the time +//waits for WriteToFileEV and then writes all accounts to file +//DWORD WINAPI FileWritingThread(PVOID); + +// Function is called when Miranda notifies plugin that it is about to exit +// Ensures succesfull end of POP3 checking, sets event that no next checking should be performed +// If there's no writer to account (POP3 thread), saves the results to the file +//not used now, perhaps in the future + + +//int ExitProc(WPARAM wParam, LPARAM lParam); + +//-------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------- + +INT_PTR GetFcnPtrSvc(WPARAM wParam, LPARAM) +{ + register int i; + + for (i=0;i<sizeof(ProtoPluginExportedFcn)/sizeof(ProtoPluginExportedFcn[0]);i++) + if (0==mir_strcmp((char *)wParam, ProtoPluginExportedFcn[i].ID)) + return (INT_PTR)ProtoPluginExportedFcn[i].Ptr; + for (i=0;i<sizeof(ProtoPluginExportedSvc)/sizeof(ProtoPluginExportedSvc[0]);i++) + if (0==mir_strcmp((char *)wParam, ProtoPluginExportedSvc[i].ID)) + return (INT_PTR)ProtoPluginExportedSvc[i].Ptr; + for (i=0;i<sizeof(SynchroExportedFcn)/sizeof(SynchroExportedFcn[0]);i++) + if (0==mir_strcmp((char *)wParam, SynchroExportedFcn[i].ID)) + return (INT_PTR)SynchroExportedFcn[i].Ptr; + for (i=0;i<sizeof(AccountExportedFcn)/sizeof(AccountExportedFcn[0]);i++) + if (0==mir_strcmp((char *)wParam, AccountExportedFcn[i].ID)) + return (INT_PTR)AccountExportedFcn[i].Ptr; + for (i=0;i<sizeof(AccountExportedSvc)/sizeof(AccountExportedSvc[0]);i++) + if (0==mir_strcmp((char *)wParam, AccountExportedSvc[i].ID)) + return (INT_PTR)AccountExportedSvc[i].Ptr; + for (i=0;i<sizeof(MailExportedFcn)/sizeof(MailExportedFcn[0]);i++) + if (0==mir_strcmp((char *)wParam, MailExportedFcn[i].ID)) + return (INT_PTR)MailExportedFcn[i].Ptr; + for (i=0;i<sizeof(MailExportedSvc)/sizeof(MailExportedSvc[0]);i++) + if (0==mir_strcmp((char *)wParam, MailExportedSvc[i].ID)) + return (INT_PTR)MailExportedSvc[i].Ptr; + for (i=0;i<sizeof(FilterPluginExportedFcn)/sizeof(FilterPluginExportedFcn[0]);i++) + if (0==mir_strcmp((char *)wParam, FilterPluginExportedFcn[i].ID)) + return (INT_PTR)FilterPluginExportedFcn[i].Ptr; + for (i=0;i<sizeof(FilterPluginExportedSvc)/sizeof(FilterPluginExportedSvc[0]);i++) + if (0==mir_strcmp((char *)wParam, FilterPluginExportedSvc[i].ID)) + return (INT_PTR)FilterPluginExportedSvc[i].Ptr; + return (INT_PTR)NULL; +} + +INT_PTR GetVariablesSvc(WPARAM wParam, LPARAM) +{ + return wParam==YAMN_VARIABLESVERSION ? (INT_PTR)&YAMNVar : (INT_PTR)NULL; +} + +void CALLBACK TimerProc(HWND, UINT, UINT_PTR, DWORD) +{ + HACCOUNT ActualAccount; + DWORD Status, tid; + +// we use event to signal, that running thread has all needed stack parameters copied + HANDLE ThreadRunningEV = CreateEvent(nullptr, FALSE, FALSE, nullptr); + if (ThreadRunningEV == nullptr) + return; +// if we want to close miranda, we get event and do not run checking anymore + if (WAIT_OBJECT_0==WaitForSingleObject(ExitEV, 0)) + return; +// Get actual status of current user in Miranda + Status=CallService(MS_CLIST_GETSTATUSMODE, 0, 0); + + mir_cslock lck(PluginRegCS); + for (PYAMN_PROTOPLUGINQUEUE ActualPlugin = FirstProtoPlugin; ActualPlugin != nullptr; ActualPlugin = ActualPlugin->Next) { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "TimerProc:AccountBrowserSO-read wait\n"); +#endif + if (WAIT_OBJECT_0 != SWMRGWaitToRead(ActualPlugin->Plugin->AccountBrowserSO, 0)) //we want to access accounts immiadtelly + { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "TimerProc:AccountBrowserSO-read enter failed\n"); +#endif + return; + } +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "TimerProc:AccountBrowserSO-read enter\n"); +#endif + for (ActualAccount=ActualPlugin->Plugin->FirstAccount;ActualAccount != nullptr;ActualAccount=ActualAccount->Next) + { + if (ActualAccount->Plugin==nullptr || ActualAccount->Plugin->Fcn==nullptr) //account not inited + continue; +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "TimerProc:ActualAccountSO-read wait\n"); +#endif + if (WAIT_OBJECT_0 != SWMRGWaitToRead(ActualAccount->AccountAccessSO, 0)) + { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "TimerProc:ActualAccountSO-read wait failed\n"); +#endif + continue; + } +#ifdef DEBUG_SYNCHRO + + switch(Status) + { + case ID_STATUS_OFFLINE: + DebugLog(SynchroFile, "TimerProc:ActualAccountSO-read enter status offline\n"); + break; + case ID_STATUS_ONLINE: + DebugLog(SynchroFile, "TimerProc:ActualAccountSO-read enter status online\n"); + break; + case ID_STATUS_AWAY: + DebugLog(SynchroFile, "TimerProc:ActualAccountSO-read enter status away\n"); + break; + case ID_STATUS_DND: + DebugLog(SynchroFile, "TimerProc:ActualAccountSO-read enter status dnd\n"); + break; + case ID_STATUS_NA: + DebugLog(SynchroFile, "TimerProc:ActualAccountSO-read enter status na\n"); + break; + case ID_STATUS_OCCUPIED: + DebugLog(SynchroFile, "TimerProc:ActualAccountSO-read enter status occupied\n"); + break; + case ID_STATUS_FREECHAT: + DebugLog(SynchroFile, "TimerProc:ActualAccountSO-read enter status freechat\n"); + break; + case ID_STATUS_INVISIBLE: + DebugLog(SynchroFile, "TimerProc:ActualAccountSO-read enter status invisible\n"); + break; + case ID_STATUS_ONTHEPHONE: + DebugLog(SynchroFile, "TimerProc:ActualAccountSO-read enter status onthephone\n"); + break; + case ID_STATUS_OUTTOLUNCH: + DebugLog(SynchroFile, "TimerProc:ActualAccountSO-read enter status outtolunch\n"); + break; + default: + DebugLog(SynchroFile, "TimerProc:ActualAccountSO-read enter status unknown\n"); + break; + } +#endif + BOOL isAccountCounting = 0; + if ( + (ActualAccount->Flags & YAMN_ACC_ENA) && + (((ActualAccount->StatusFlags & YAMN_ACC_ST0) && (Status<=ID_STATUS_OFFLINE)) || + ((ActualAccount->StatusFlags & YAMN_ACC_ST1) && (Status==ID_STATUS_ONLINE)) || + ((ActualAccount->StatusFlags & YAMN_ACC_ST2) && (Status==ID_STATUS_AWAY)) || + ((ActualAccount->StatusFlags & YAMN_ACC_ST3) && (Status==ID_STATUS_DND)) || + ((ActualAccount->StatusFlags & YAMN_ACC_ST4) && (Status==ID_STATUS_NA)) || + ((ActualAccount->StatusFlags & YAMN_ACC_ST5) && (Status==ID_STATUS_OCCUPIED)) || + ((ActualAccount->StatusFlags & YAMN_ACC_ST6) && (Status==ID_STATUS_FREECHAT)) || + ((ActualAccount->StatusFlags & YAMN_ACC_ST7) && (Status==ID_STATUS_INVISIBLE)) || + ((ActualAccount->StatusFlags & YAMN_ACC_ST8) && (Status==ID_STATUS_ONTHEPHONE)) || + ((ActualAccount->StatusFlags & YAMN_ACC_ST9) && (Status==ID_STATUS_OUTTOLUNCH)))) + { + + if ((!ActualAccount->Interval && !ActualAccount->TimeLeft) || ActualAccount->Plugin->Fcn->TimeoutFcnPtr==nullptr) + { + goto ChangeIsCountingStatusLabel; + } + if (ActualAccount->TimeLeft) { + ActualAccount->TimeLeft--; + isAccountCounting = TRUE; + } +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "TimerProc:time left : %i\n", ActualAccount->TimeLeft); +#endif + WindowList_BroadcastAsync(YAMNVar.MessageWnds, WM_YAMN_CHANGETIME, (WPARAM)ActualAccount, (LPARAM)ActualAccount->TimeLeft); + if (!ActualAccount->TimeLeft) + { + struct CheckParam ParamToPlugin={YAMN_CHECKVERSION, ThreadRunningEV, ActualAccount, YAMN_NORMALCHECK, (void *)nullptr, nullptr}; + + ActualAccount->TimeLeft=ActualAccount->Interval; + HANDLE NewThread = CreateThread(nullptr, 0, (YAMN_STANDARDFCN)ActualAccount->Plugin->Fcn->TimeoutFcnPtr, &ParamToPlugin, 0, &tid); + if (NewThread == nullptr) + { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "TimerProc:ActualAccountSO-read done\n"); +#endif + ReadDoneFcn(ActualAccount->AccountAccessSO); + continue; + } + else + { + WaitForSingleObject(ThreadRunningEV, INFINITE); + CloseHandle(NewThread); + } + } + + } +ChangeIsCountingStatusLabel: +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "TimerProc:ActualAccountSO-read done\n"); +#endif + if (((ActualAccount->isCounting) != 0) != isAccountCounting) { + ActualAccount->isCounting=isAccountCounting; + WORD cStatus = g_plugin.getWord(ActualAccount->hContact, "Status"); + switch (cStatus) { + case ID_STATUS_ONLINE: + case ID_STATUS_OFFLINE: + g_plugin.setWord(ActualAccount->hContact, "Status", isAccountCounting ? ID_STATUS_ONLINE : ID_STATUS_OFFLINE); + default: break; + } + } + ReadDoneFcn(ActualAccount->AccountAccessSO); + } +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "TimerProc:AccountBrowserSO-read done\n"); +#endif + SWMRGDoneReading(ActualPlugin->Plugin->AccountBrowserSO); + } + CloseHandle(ThreadRunningEV); +} + +INT_PTR ForceCheckSvc(WPARAM, LPARAM) +{ + HACCOUNT ActualAccount; + DWORD tid; + + //we use event to signal, that running thread has all needed stack parameters copied + HANDLE ThreadRunningEV = CreateEvent(nullptr, FALSE, FALSE, nullptr); + if (ThreadRunningEV == nullptr) + return 0; + //if we want to close miranda, we get event and do not run pop3 checking anymore + if (WAIT_OBJECT_0 == WaitForSingleObject(ExitEV, 0)) + return 0; + + { + mir_cslock lck(PluginRegCS); + for (PYAMN_PROTOPLUGINQUEUE ActualPlugin = FirstProtoPlugin; ActualPlugin != nullptr; ActualPlugin = ActualPlugin->Next) { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "ForceCheck:AccountBrowserSO-read wait\n"); +#endif + SWMRGWaitToRead(ActualPlugin->Plugin->AccountBrowserSO, INFINITE); +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "ForceCheck:AccountBrowserSO-read enter\n"); +#endif + for (ActualAccount = ActualPlugin->Plugin->FirstAccount; ActualAccount != nullptr; ActualAccount = ActualAccount->Next) { + if (ActualAccount->Plugin->Fcn == nullptr) //account not inited + continue; +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "ForceCheck:ActualAccountSO-read wait\n"); +#endif + if (WAIT_OBJECT_0 != WaitToReadFcn(ActualAccount->AccountAccessSO)) { +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "ForceCheck:ActualAccountSO-read wait failed\n"); +#endif + continue; + } +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "ForceCheck:ActualAccountSO-read enter\n"); +#endif + if ((ActualAccount->Flags & YAMN_ACC_ENA) && (ActualAccount->StatusFlags & YAMN_ACC_FORCE)) //account cannot be forced to check + { + if (ActualAccount->Plugin->Fcn->ForceCheckFcnPtr == nullptr) { + ReadDoneFcn(ActualAccount->AccountAccessSO); + continue; + } + struct CheckParam ParamToPlugin = { YAMN_CHECKVERSION, ThreadRunningEV, ActualAccount, YAMN_FORCECHECK, (void *)nullptr, nullptr }; + + if (nullptr == CreateThread(nullptr, 0, (YAMN_STANDARDFCN)ActualAccount->Plugin->Fcn->ForceCheckFcnPtr, &ParamToPlugin, 0, &tid)) { + ReadDoneFcn(ActualAccount->AccountAccessSO); + continue; + } + else + WaitForSingleObject(ThreadRunningEV, INFINITE); + } + ReadDoneFcn(ActualAccount->AccountAccessSO); + } +#ifdef DEBUG_SYNCHRO + DebugLog(SynchroFile, "ForceCheck:AccountBrowserSO-read done\n"); +#endif + SWMRGDoneReading(ActualPlugin->Plugin->AccountBrowserSO); + } + } + + CloseHandle(ThreadRunningEV); + + if (hTTButton) + CallService(MS_TTB_SETBUTTONSTATE, (WPARAM)hTTButton, 0); + return 1; +} |