summaryrefslogtreecommitdiff
path: root/plugins/TabSRMM/src/sendlater.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/TabSRMM/src/sendlater.cpp')
-rw-r--r--plugins/TabSRMM/src/sendlater.cpp720
1 files changed, 376 insertions, 344 deletions
diff --git a/plugins/TabSRMM/src/sendlater.cpp b/plugins/TabSRMM/src/sendlater.cpp
index b91fa27ee5..3d7af976c5 100644
--- a/plugins/TabSRMM/src/sendlater.cpp
+++ b/plugins/TabSRMM/src/sendlater.cpp
@@ -28,128 +28,219 @@
#include "stdafx.h"
-CSendLater *sendLater = nullptr;
+/////////////////////////////////////////////////////////////////////////////////////////
+// CSendLaterJob class implementation
-// implementation of the CSendLaterJob class
-CSendLaterJob::CSendLaterJob()
+struct CSendLaterJob : public MZeroedObject
{
- memset(this, 0, sizeof(CSendLaterJob));
- fSuccess = false;
-}
+ // job status/error codes
+ enum {
+ INVALID_CONTACT = 'I',
+ JOB_DEFERRED = 'D',
+ JOB_AGE = 'O',
+ JOB_MYSTATUS = 'M',
+ JOB_STATUS = 'S',
+ JOB_WAITACK = 'A',
+ JOB_REMOVABLE = 'R',
+ JOB_HOLD = 'H',
+ };
+
+ // internal flags
+ enum {
+ SLF_SUSPEND = 1,
+ SLF_INVALID = 2
+ };
+
+ char szId[20]; // database key name (time stamp of original send)
+ MCONTACT hContact; // original contact where the message has been assigned
+ MCONTACT hTargetContact; // *real* contact (can be different for metacontacts, e.g).
+ HANDLE hProcess; // returned from the protocols sending service. needed to find it in the ACK handler
+ time_t created; // job was created at this time (important to kill jobs, that are too old)
+ time_t lastSent; // time at which the delivery was initiated. used to handle timeouts
+ char *sendBuffer; // utf-8 send buffer
+ PBYTE pBuf; // conventional send buffer (for non-utf8 protocols)
+ DWORD dwFlags;
+ int iSendCount; // # of times we tried to send it...
+ bool fSuccess, fFailed;
+ BYTE bCode; // error/progress code (for the UI)
+
+ // returns true if this job is persistent (saved to the database).
+ // such a job will survive a restart of Miranda
+ __inline bool isPersistentJob() const {
+ return(szId[0] == 'S' ? true : false);
+ }
-// return true if this job is persistent (saved to the database).
-// such a job will survive a restart of Miranda
-bool CSendLaterJob::isPersistentJob()
-{
- return(szId[0] == 'S' ? true : false);
-}
+ // try to send an open job from the job list
+ // this is ONLY called from the WM_TIMER handler and should never be executed directly.
-// check conditions for deletion
-bool CSendLaterJob::mustDelete()
-{
- if (fSuccess)
- return true;
+ int sendIt()
+ {
+ time_t now = time(0);
+ if (bCode == JOB_HOLD || bCode == JOB_DEFERRED || fSuccess || fFailed || lastSent > now)
+ return 0; // this one is frozen or done (will be removed soon), don't process it now.
- if (fFailed && bCode == JOB_REMOVABLE)
- return true;
+ if (now - created > SENDLATER_AGE_THRESHOLD) { // too old, this will be discarded and user informed by popup
+ fFailed = true;
+ bCode = JOB_AGE;
+ return 0;
+ }
- return false;
-}
+ // mark job as deferred (5 unsuccessful sends). Job will not be removed, but
+ // the user must manually reset it in order to trigger a new send attempt.
+ if (iSendCount == 5) {
+ bCode = JOB_DEFERRED;
+ return 0;
+ }
-// clean database entries for a persistent job (currently: manual send later jobs)
-void CSendLaterJob::cleanDB()
-{
- if (isPersistentJob()) {
- char szKey[100];
-
- db_unset(hContact, "SendLater", szId);
- int iCount = db_get_dw(hContact, "SendLater", "count", 0);
- if (iCount)
- iCount--;
- db_set_dw(hContact, "SendLater", "count", iCount);
-
- // delete flags
- mir_snprintf(szKey, "$%s", szId);
- db_unset(hContact, "SendLater", szKey);
+ if (iSendCount > 0 && (now - lastSent < SENDLATER_RESEND_THRESHOLD))
+ return 0; // this one was sent, but probably failed. Resend it after a while
+
+ CContactCache *c = CContactCache::getContactCache(hContact);
+ if (!c->isValid()) {
+ fFailed = true;
+ bCode = INVALID_CONTACT;
+ return 0; // can happen (contact has been deleted). mark the job as failed
+ }
+
+ MCONTACT cc = c->getActiveContact();
+ const char *szProto = c->getActiveProto();
+ if (!cc || szProto == nullptr)
+ return 0;
+
+ int wMyStatus = Proto_GetStatus(szProto);
+
+ // status mode checks
+ if (wMyStatus == ID_STATUS_OFFLINE) {
+ bCode = JOB_MYSTATUS;
+ return 0;
+ }
+ if (szId[0] == 'S') {
+ if (wMyStatus != ID_STATUS_ONLINE || wMyStatus != ID_STATUS_FREECHAT) {
+ bCode = JOB_MYSTATUS;
+ return 0;
+ }
+ }
+
+ lastSent = now;
+ iSendCount++;
+ hTargetContact = cc;
+ bCode = JOB_WAITACK;
+ hProcess = (HANDLE)ProtoChainSend(cc, PSS_MESSAGE, 0, (LPARAM)sendBuffer);
+ return 0;
}
-}
-// read flags for a persistent jobs from the db
-// flag key name is the job id with a "$" prefix.
-void CSendLaterJob::readFlags()
-{
- if (isPersistentJob()) {
- char szKey[100];
- DWORD localFlags;
+ // reads flags for a persistent jobs from the db
+ // flag key name is the job id with a "$" prefix.
+ void readFlags()
+ {
+ if (isPersistentJob()) {
+ char szKey[100];
+ DWORD localFlags;
- mir_snprintf(szKey, "$%s", szId);
- localFlags = db_get_dw(hContact, "SendLater", szKey, 0);
+ mir_snprintf(szKey, "$%s", szId);
+ localFlags = db_get_dw(hContact, "SendLater", szKey, 0);
- if (localFlags & SLF_SUSPEND)
- bCode = JOB_HOLD;
+ if (localFlags & SLF_SUSPEND)
+ bCode = JOB_HOLD;
+ }
}
-}
-// write flags for a persistent jobs from the db
-// flag key name is the job id with a "$" prefix.
-void CSendLaterJob::writeFlags()
-{
- if (isPersistentJob()) {
- DWORD localFlags = (bCode == JOB_HOLD ? SLF_SUSPEND : 0);
- char szKey[100];
+ // writes flags for a persistent jobs from the db
+ // flag key name is the job id with a "$" prefix.
+ void writeFlags()
+ {
+ if (isPersistentJob()) {
+ DWORD localFlags = (bCode == JOB_HOLD ? SLF_SUSPEND : 0);
+ char szKey[100];
- mir_snprintf(szKey, "$%s", szId);
- db_set_dw(hContact, "SendLater", szKey, localFlags);
+ mir_snprintf(szKey, "$%s", szId);
+ db_set_dw(hContact, "SendLater", szKey, localFlags);
+ }
}
-}
-// delete a send later job
-CSendLaterJob::~CSendLaterJob()
-{
- if (fSuccess || fFailed) {
- if ((sendLater->haveErrorPopups() && fFailed) || (sendLater->haveSuccessPopups() && fSuccess)) {
- bool fShowPopup = true;
-
- if (fFailed && bCode == JOB_REMOVABLE) // no popups for jobs removed on user's request
- fShowPopup = false;
- /*
- * show a popup notification, unless they are disabled
- */
- if (fShowPopup) {
- wchar_t *tszName = Clist_GetContactDisplayName(hContact);
-
- POPUPDATAW ppd;
- ppd.lchContact = hContact;
- wcsncpy_s(ppd.lpwzContactName, (tszName ? tszName : TranslateT("'(Unknown contact)'")), _TRUNCATE);
- wchar_t *msgPreview = Utils::GetPreviewWithEllipsis(reinterpret_cast<wchar_t *>(&pBuf[mir_strlen((char *)pBuf) + 1]), 100);
- if (fSuccess) {
- mir_snwprintf(ppd.lpwzText, TranslateT("A send later job completed successfully.\nThe original message: %s"),
- msgPreview);
- mir_free(msgPreview);
- }
- else if (fFailed) {
- mir_snwprintf(ppd.lpwzText, TranslateT("A send later job failed to complete.\nThe original message: %s"),
- msgPreview);
- mir_free(msgPreview);
- }
+ // cleans database entries for a persistent job (currently: manual send later jobs)
+ void cleanDB()
+ {
+ if (isPersistentJob()) {
+ char szKey[100];
+
+ db_unset(hContact, "SendLater", szId);
+ int iCount = db_get_dw(hContact, "SendLater", "count", 0);
+ if (iCount)
+ iCount--;
+ db_set_dw(hContact, "SendLater", "count", iCount);
+
+ // delete flags
+ mir_snprintf(szKey, "$%s", szId);
+ db_unset(hContact, "SendLater", szKey);
+ }
+ }
+
+ // checks conditions for deletion
+ bool mustDelete()
+ {
+ if (fSuccess)
+ return true;
+
+ if (fFailed && bCode == JOB_REMOVABLE)
+ return true;
+
+ return false;
+ }
+
+ ~CSendLaterJob()
+ {
+ if (fSuccess || fFailed) {
+ if ((SendLater::ErrorPopups && fFailed) || (SendLater::SuccessPopups && fSuccess)) {
+ bool fShowPopup = true;
+
+ if (fFailed && bCode == JOB_REMOVABLE) // no popups for jobs removed on user's request
+ fShowPopup = false;
/*
- * use message settings (timeout/colors) for success popups
- */
- ppd.colorText = fFailed ? RGB(255, 245, 225) : nen_options.colTextMsg;
- ppd.colorBack = fFailed ? RGB(191, 0, 0) : nen_options.colBackMsg;
- ppd.PluginWindowProc = Utils::PopupDlgProcError;
- ppd.lchIcon = fFailed ? PluginConfig.g_iconErr : PluginConfig.g_IconMsgEvent;
- ppd.PluginData = nullptr;
- ppd.iSeconds = fFailed ? -1 : nen_options.iDelayMsg;
- PUAddPopupW(&ppd);
+ * show a popup notification, unless they are disabled
+ */
+ if (fShowPopup) {
+ wchar_t *tszName = Clist_GetContactDisplayName(hContact);
+
+ POPUPDATAW ppd;
+ ppd.lchContact = hContact;
+ wcsncpy_s(ppd.lpwzContactName, (tszName ? tszName : TranslateT("'(Unknown contact)'")), _TRUNCATE);
+ ptrW msgPreview(Utils::GetPreviewWithEllipsis(reinterpret_cast<wchar_t *>(&pBuf[mir_strlen((char *)pBuf) + 1]), 100));
+ if (fSuccess)
+ mir_snwprintf(ppd.lpwzText, TranslateT("A send later job completed successfully.\nThe original message: %s"), msgPreview);
+ else if (fFailed)
+ mir_snwprintf(ppd.lpwzText, TranslateT("A send later job failed to complete.\nThe original message: %s"), msgPreview);
+
+ /*
+ * use message settings (timeout/colors) for success popups
+ */
+ ppd.colorText = fFailed ? RGB(255, 245, 225) : nen_options.colTextMsg;
+ ppd.colorBack = fFailed ? RGB(191, 0, 0) : nen_options.colBackMsg;
+ ppd.PluginWindowProc = Utils::PopupDlgProcError;
+ ppd.lchIcon = fFailed ? PluginConfig.g_iconErr : PluginConfig.g_IconMsgEvent;
+ ppd.PluginData = nullptr;
+ ppd.iSeconds = fFailed ? -1 : nen_options.iDelayMsg;
+ PUAddPopupW(&ppd);
+ }
}
+ if (fFailed && (bCode == JOB_AGE || bCode == JOB_REMOVABLE) && szId[0] == 'S')
+ cleanDB();
}
- if (fFailed && (bCode == JOB_AGE || bCode == JOB_REMOVABLE) && szId[0] == 'S')
- cleanDB();
+
mir_free(sendBuffer);
mir_free(pBuf);
}
-}
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Module data
+
+LIST<void> g_sendLaterContactList(5, PtrKeySortT);
+LIST<CSendLaterJob> g_sendLaterJobList(5);
+
+bool g_bIsInteractive = false;
+time_t g_last_sendlater_processed = time(0);
+int g_currJob = -1;
/////////////////////////////////////////////////////////////////////////////////////////
// Send Later dialog
@@ -163,9 +254,6 @@ static class CSendLaterDlg *pDialog;
class CSendLaterDlg : public CDlgBase
{
- friend class CSendLater;
-
- CSendLater *m_later;
MCONTACT m_hFilter = 0; // contact handle to filter the qmgr list (0 = no filter, show all)
int m_sel = -1; // index of the combo box entry corresponding to the contact filter;
@@ -180,105 +268,6 @@ class CSendLaterDlg : public CDlgBase
return m_sel;
}
- // fills the list of jobs with current contents of the job queue
- // filters by m_hFilter (contact handle)
- void FillList()
- {
- wchar_t *formatTime = L"%Y.%m.%d - %H:%M";
-
- m_sel = 0;
- m_filter.InsertString(TranslateT("<All contacts>"), -1, 0);
-
- LVITEM lvItem = { 0 };
-
- BYTE bCode = '-';
- unsigned uIndex = 0;
- for (auto &p : m_later->m_sendLaterJobList) {
- CContactCache *c = CContactCache::getContactCache(p->hContact);
-
- const wchar_t *tszNick = c->getNick();
- if (m_hFilter && m_hFilter != p->hContact) {
- AddFilter(c->getContact(), tszNick);
- continue;
- }
-
- lvItem.mask = LVIF_TEXT | LVIF_PARAM;
- wchar_t tszBuf[255];
- mir_snwprintf(tszBuf, L"%s [%s]", tszNick, c->getRealAccount());
- lvItem.pszText = tszBuf;
- lvItem.cchTextMax = _countof(tszBuf);
- lvItem.iItem = uIndex++;
- lvItem.iSubItem = 0;
- lvItem.lParam = LPARAM(p);
- m_list.InsertItem(&lvItem);
- AddFilter(c->getContact(), tszNick);
-
- lvItem.mask = LVIF_TEXT;
- wchar_t tszTimestamp[30];
- wcsftime(tszTimestamp, 30, formatTime, _localtime32((__time32_t *)&p->created));
- tszTimestamp[29] = 0;
- lvItem.pszText = tszTimestamp;
- lvItem.iSubItem = 1;
- m_list.SetItem(&lvItem);
-
- wchar_t *msg = mir_utf8decodeW(p->sendBuffer);
- wchar_t *preview = Utils::GetPreviewWithEllipsis(msg, 255);
- lvItem.pszText = preview;
- lvItem.iSubItem = 2;
- m_list.SetItem(&lvItem);
- mir_free(preview);
- mir_free(msg);
-
- const wchar_t *tszStatusText = nullptr;
- if (p->fFailed) {
- tszStatusText = p->bCode == CSendLaterJob::JOB_REMOVABLE ?
- TranslateT("Removed") : TranslateT("Failed");
- }
- else if (p->fSuccess)
- tszStatusText = TranslateT("Sent OK");
- else {
- switch (p->bCode) {
- case CSendLaterJob::JOB_DEFERRED:
- tszStatusText = TranslateT("Deferred");
- break;
- case CSendLaterJob::JOB_AGE:
- tszStatusText = TranslateT("Failed");
- break;
- case CSendLaterJob::JOB_HOLD:
- tszStatusText = TranslateT("Suspended");
- break;
- default:
- tszStatusText = TranslateT("Pending");
- break;
- }
- }
- if (p->bCode)
- bCode = p->bCode;
-
- wchar_t tszStatus[20];
- mir_snwprintf(tszStatus, L"X/%s[%c] (%d)", tszStatusText, bCode, p->iSendCount);
- tszStatus[0] = p->szId[0];
- lvItem.pszText = tszStatus;
- lvItem.iSubItem = 3;
- m_list.SetItem(&lvItem);
-
- if (p->lastSent == 0)
- wcsncpy_s(tszTimestamp, L"Never", _TRUNCATE);
- else {
- wcsftime(tszTimestamp, 30, formatTime, _localtime32((__time32_t *)&p->lastSent));
- tszTimestamp[29] = 0;
- }
- lvItem.pszText = tszTimestamp;
- lvItem.iSubItem = 4;
- m_list.SetItem(&lvItem);
- }
-
- if (m_hFilter == 0)
- m_filter.SetCurSel(0);
- else
- m_filter.SetCurSel(m_sel);
- }
-
// set the column headers
void SetupColumns()
{
@@ -314,14 +303,15 @@ class CSendLaterDlg : public CDlgBase
}
CCtrlCheck chkSuccess, chkError;
+ CCtrlHyperlink m_link;
CCtrlCombo m_filter;
+
+public:
CCtrlListView m_list;
- CCtrlHyperlink m_link;
public:
- CSendLaterDlg(CSendLater *pLater) :
+ CSendLaterDlg() :
CDlgBase(g_plugin, IDD_SENDLATER_QMGR),
- m_later(pLater),
m_list(this, IDC_QMGR_LIST),
m_link(this, IDC_QMGR_HELP, "https://wiki.miranda-ng.org/index.php?title=Plugin:TabSRMM/en/Send_later"),
m_filter(this, IDC_QMGR_FILTER),
@@ -346,8 +336,8 @@ public:
SetupColumns();
FillList();
- chkSuccess.SetState(m_later->m_fSuccessPopups);
- chkError.SetState(m_later->m_fErrorPopups);
+ chkSuccess.SetState(SendLater::SuccessPopups);
+ chkError.SetState(SendLater::ErrorPopups);
Show();
return true;
}
@@ -390,10 +380,10 @@ public:
if (m_list.GetSelectedCount() == 1)
::EnableMenuItem(hSubMenu, ID_QUEUEMANAGER_COPYMESSAGETOCLIPBOARD, MF_ENABLED);
- m_later->m_fIsInteractive = true;
+ g_bIsInteractive = true;
int selection = ::TrackPopupMenu(hSubMenu, TPM_RETURNCMD, pt.x, pt.y, 0, m_hwnd, nullptr);
if (selection == ID_QUEUEMANAGER_CANCELALLMULTISENDJOBS) {
- for (auto &p : m_later->m_sendLaterJobList) {
+ for (auto &p : g_sendLaterJobList) {
if (p->szId[0] == 'M') {
p->fFailed = true;
p->bCode = CSendLaterJob::JOB_REMOVABLE;
@@ -402,21 +392,19 @@ public:
}
else if (selection != 0) {
HandleMenuClick(selection);
- m_later->m_last_sendlater_processed = 0; // force a queue check
+ g_last_sendlater_processed = 0; // force a queue check
}
- m_later->m_fIsInteractive = false;
+ g_bIsInteractive = false;
}
void onChange_Success(CCtrlCheck *)
{
- m_later->m_fSuccessPopups = chkSuccess.GetState();
- db_set_b(0, SRMSGMOD_T, "qmgrSuccessPopups", m_later->m_fSuccessPopups);
+ SendLater::SuccessPopups = chkSuccess.GetState();
}
void onChange_Error(CCtrlCheck *)
{
- m_later->m_fErrorPopups = chkError.GetState();
- db_set_b(0, SRMSGMOD_T, "qmgrErrorPopups", m_later->m_fErrorPopups);
+ SendLater::ErrorPopups = chkError.GetState();
}
// this handles all commands sent by the context menu
@@ -477,30 +465,135 @@ public:
}
FillList();
}
+
+ // fills the list of jobs with current contents of the job queue
+ // filters by m_hFilter (contact handle)
+ void FillList()
+ {
+ wchar_t *formatTime = L"%Y.%m.%d - %H:%M";
+
+ m_sel = 0;
+ m_filter.InsertString(TranslateT("<All contacts>"), -1, 0);
+
+ LVITEM lvItem = { 0 };
+
+ BYTE bCode = '-';
+ unsigned uIndex = 0;
+ for (auto &p : g_sendLaterJobList) {
+ CContactCache *c = CContactCache::getContactCache(p->hContact);
+
+ const wchar_t *tszNick = c->getNick();
+ if (m_hFilter && m_hFilter != p->hContact) {
+ AddFilter(c->getContact(), tszNick);
+ continue;
+ }
+
+ lvItem.mask = LVIF_TEXT | LVIF_PARAM;
+ wchar_t tszBuf[255];
+ mir_snwprintf(tszBuf, L"%s [%s]", tszNick, c->getRealAccount());
+ lvItem.pszText = tszBuf;
+ lvItem.cchTextMax = _countof(tszBuf);
+ lvItem.iItem = uIndex++;
+ lvItem.iSubItem = 0;
+ lvItem.lParam = LPARAM(p);
+ m_list.InsertItem(&lvItem);
+ AddFilter(c->getContact(), tszNick);
+
+ lvItem.mask = LVIF_TEXT;
+ wchar_t tszTimestamp[30];
+ wcsftime(tszTimestamp, 30, formatTime, _localtime32((__time32_t *)&p->created));
+ tszTimestamp[29] = 0;
+ lvItem.pszText = tszTimestamp;
+ lvItem.iSubItem = 1;
+ m_list.SetItem(&lvItem);
+
+ wchar_t *msg = mir_utf8decodeW(p->sendBuffer);
+ wchar_t *preview = Utils::GetPreviewWithEllipsis(msg, 255);
+ lvItem.pszText = preview;
+ lvItem.iSubItem = 2;
+ m_list.SetItem(&lvItem);
+ mir_free(preview);
+ mir_free(msg);
+
+ const wchar_t *tszStatusText = nullptr;
+ if (p->fFailed) {
+ tszStatusText = p->bCode == CSendLaterJob::JOB_REMOVABLE ?
+ TranslateT("Removed") : TranslateT("Failed");
+ }
+ else if (p->fSuccess)
+ tszStatusText = TranslateT("Sent OK");
+ else {
+ switch (p->bCode) {
+ case CSendLaterJob::JOB_DEFERRED:
+ tszStatusText = TranslateT("Deferred");
+ break;
+ case CSendLaterJob::JOB_AGE:
+ tszStatusText = TranslateT("Failed");
+ break;
+ case CSendLaterJob::JOB_HOLD:
+ tszStatusText = TranslateT("Suspended");
+ break;
+ default:
+ tszStatusText = TranslateT("Pending");
+ break;
+ }
+ }
+ if (p->bCode)
+ bCode = p->bCode;
+
+ wchar_t tszStatus[20];
+ mir_snwprintf(tszStatus, L"X/%s[%c] (%d)", tszStatusText, bCode, p->iSendCount);
+ tszStatus[0] = p->szId[0];
+ lvItem.pszText = tszStatus;
+ lvItem.iSubItem = 3;
+ m_list.SetItem(&lvItem);
+
+ if (p->lastSent == 0)
+ wcsncpy_s(tszTimestamp, L"Never", _TRUNCATE);
+ else {
+ wcsftime(tszTimestamp, 30, formatTime, _localtime32((__time32_t *)&p->lastSent));
+ tszTimestamp[29] = 0;
+ }
+ lvItem.pszText = tszTimestamp;
+ lvItem.iSubItem = 4;
+ m_list.SetItem(&lvItem);
+ }
+
+ if (m_hFilter == 0)
+ m_filter.SetCurSel(0);
+ else
+ m_filter.SetCurSel(m_sel);
+ }
};
-CSendLater::CSendLater() :
- m_sendLaterContactList(5, PtrKeySortT),
- m_sendLaterJobList(5),
- m_fAvail(SRMSGMOD_T, "sendLaterAvail", false),
- m_fErrorPopups(SRMSGMOD_T, "qmgrErrorPopups", false),
- m_fSuccessPopups(SRMSGMOD_T, "qmgrSuccessPopups", false)
-{
- m_last_sendlater_processed = time(0);
-}
+/////////////////////////////////////////////////////////////////////////////////////////
+// SendLater class
+
+CMOption<bool> SendLater::Avail(SRMSGMOD_T, "sendLaterAvail", false);
+CMOption<bool> SendLater::ErrorPopups(SRMSGMOD_T, "qmgrErrorPopups", false);
+CMOption<bool> SendLater::SuccessPopups(SRMSGMOD_T, "qmgrSuccessPopups", false);
+bool SendLater::isInteractive() { return g_bIsInteractive; }
+bool SendLater::isJobListEmpty() { return g_sendLaterJobList.getCount() == 0; }
+time_t SendLater::lastProcessed() { return g_last_sendlater_processed; }
+void SendLater::setLastProcessed(const time_t _t) { g_last_sendlater_processed = _t; }
+void SendLater::flushQueue() { g_last_sendlater_processed = 0; }
+bool SendLater::haveJobs() { return (g_sendLaterJobList.getCount() != 0 && g_currJob != -1); }
+
+/////////////////////////////////////////////////////////////////////////////////////////
// clear all open send jobs. Only called on system shutdown to remove
// the jobs from memory. Must _NOT_ delete any sendlater related stuff from
// the database (only successful sends may do this).
-CSendLater::~CSendLater()
+
+void SendLater::shutDown()
{
if (pDialog)
pDialog->Close();
- if (m_sendLaterJobList.getCount() == 0)
+ if (g_sendLaterJobList.getCount() == 0)
return;
- for (auto &p : m_sendLaterJobList) {
+ for (auto &p : g_sendLaterJobList) {
mir_free(p->sendBuffer);
mir_free(p->pBuf);
p->fSuccess = false; // avoid clearing jobs from the database
@@ -508,14 +601,15 @@ CSendLater::~CSendLater()
}
}
-void CSendLater::startJobListProcess()
+void SendLater::startJobListProcess()
{
- m_currJob = 0;
+ g_currJob = 0;
if (pDialog)
pDialog->m_list.Disable();
}
+/////////////////////////////////////////////////////////////////////////////////////////
// checks if the current job in the timer-based process queue is subject
// for deletion (that is, it has failed or succeeded)
//
@@ -525,65 +619,62 @@ void CSendLater::startJobListProcess()
// hotkeyhandler.cpp.
//
// returns true if more jobs are awaiting processing, false otherwise.
-bool CSendLater::processCurrentJob()
+
+bool SendLater::processCurrentJob()
{
- if (!m_sendLaterJobList.getCount() || m_currJob == -1)
+ if (!g_sendLaterJobList.getCount() || g_currJob == -1)
return false;
- if (m_currJob >= m_sendLaterJobList.getCount()) {
- m_currJob = -1;
+ if (g_currJob >= g_sendLaterJobList.getCount()) {
+ g_currJob = -1;
return false;
}
- CSendLaterJob *p = m_sendLaterJobList[m_currJob];
+ CSendLaterJob *p = g_sendLaterJobList[g_currJob];
if (p->fSuccess || p->fFailed) {
if (p->mustDelete()) {
- m_sendLaterJobList.remove(m_currJob);
+ g_sendLaterJobList.remove(g_currJob);
delete p;
}
- else m_currJob++;
+ else g_currJob++;
}
- else sendIt(m_sendLaterJobList[m_currJob++]);
+ else g_sendLaterJobList[g_currJob++]->sendIt();
- if (m_currJob >= m_sendLaterJobList.getCount()) {
- m_currJob = -1;
+ if (g_currJob >= g_sendLaterJobList.getCount()) {
+ g_currJob = -1;
return false;
}
return true;
}
-// stub used as enum proc for the database enumeration, collecting
-// all entries in the SendLater module
-// (static function)
-int _cdecl CSendLater::addStub(const char *szSetting, void *lParam)
+/////////////////////////////////////////////////////////////////////////////////////////
+// called periodically from a timer, check if new contacts were added
+// and process them
+
+static int _cdecl addStub(const char *szSetting, void *lParam)
{
- return(sendLater->addJob(szSetting, lParam));
+ return(SendLater::addJob(szSetting, lParam));
}
-// Process a single contact from the list of contacts with open send later jobs
-// enum the "SendLater" module and add all jobs to the list of open jobs.
-// addJob() will deal with possible duplicates
-// @param hContact HANDLE: contact's handle
-void CSendLater::processSingleContact(const MCONTACT hContact)
+static void processSingleContact(const MCONTACT hContact)
{
int iCount = db_get_dw(hContact, "SendLater", "count", 0);
if (iCount)
- db_enum_settings(hContact, CSendLater::addStub, "SendLater", (void*)hContact);
+ db_enum_settings(hContact, addStub, "SendLater", (void*)hContact);
}
-// called periodically from a timer, check if new contacts were added
-// and process them
-void CSendLater::processContacts()
+void SendLater::processContacts()
{
- if (m_fAvail) {
- for (auto &it : m_sendLaterContactList)
+ if (SendLater::Avail) {
+ for (auto &it : g_sendLaterContactList)
processSingleContact((UINT_PTR)it);
- m_sendLaterContactList.destroy();
+ g_sendLaterContactList.destroy();
}
}
+/////////////////////////////////////////////////////////////////////////////////////////
// This function adds a new job to the list of messages to send unattended
// used by the send later feature and multisend
//
@@ -593,20 +684,21 @@ void CSendLater::processContacts()
//
// @param lParam: a contact handle for which the job should be scheduled
// @return 0 on failure, 1 otherwise
-int CSendLater::addJob(const char *szSetting, void *lParam)
+
+int SendLater::addJob(const char *szSetting, void *lParam)
{
MCONTACT hContact = (UINT_PTR)lParam;
DBVARIANT dbv = { 0 };
char *szOrig_Utf = nullptr;
- if (!m_fAvail || !szSetting || !mir_strcmp(szSetting, "count") || mir_strlen(szSetting) < 8)
+ if (!SendLater::Avail || !szSetting || !mir_strcmp(szSetting, "count") || mir_strlen(szSetting) < 8)
return 0;
if (szSetting[0] != 'S' && szSetting[0] != 'M')
return 0;
// check for possible dupes
- for (auto &p : m_sendLaterJobList)
+ for (auto &p : g_sendLaterJobList)
if (p->hContact == hContact && !mir_strcmp(p->szId, szSetting))
return 0;
@@ -656,105 +748,48 @@ int CSendLater::addJob(const char *szSetting, void *lParam)
mir_free(szWchar);
job->readFlags();
- m_sendLaterJobList.insert(job);
+ g_sendLaterJobList.insert(job);
qMgrUpdate();
return 1;
}
-// Try to send an open job from the job list
-// this is ONLY called from the WM_TIMER handler and should never be executed directly.
-int CSendLater::sendIt(CSendLaterJob *job)
-{
- time_t now = time(0);
- if (job->bCode == CSendLaterJob::JOB_HOLD || job->bCode == CSendLaterJob::JOB_DEFERRED || job->fSuccess || job->fFailed || job->lastSent > now)
- return 0; // this one is frozen or done (will be removed soon), don't process it now.
-
- if (now - job->created > SENDLATER_AGE_THRESHOLD) { // too old, this will be discarded and user informed by popup
- job->fFailed = true;
- job->bCode = CSendLaterJob::JOB_AGE;
- return 0;
- }
-
- // mark job as deferred (5 unsuccessful sends). Job will not be removed, but
- // the user must manually reset it in order to trigger a new send attempt.
- if (job->iSendCount == 5) {
- job->bCode = CSendLaterJob::JOB_DEFERRED;
- return 0;
- }
-
- if (job->iSendCount > 0 && (now - job->lastSent < SENDLATER_RESEND_THRESHOLD))
- return 0; // this one was sent, but probably failed. Resend it after a while
-
- CContactCache *c = CContactCache::getContactCache(job->hContact);
- if (!c->isValid()) {
- job->fFailed = true;
- job->bCode = CSendLaterJob::INVALID_CONTACT;
- return 0; // can happen (contact has been deleted). mark the job as failed
- }
-
- MCONTACT hContact = c->getActiveContact();
- const char *szProto = c->getActiveProto();
- if (!hContact || szProto == nullptr)
- return 0;
-
- int wMyStatus = Proto_GetStatus(szProto);
-
- // status mode checks
- if (wMyStatus == ID_STATUS_OFFLINE) {
- job->bCode = CSendLaterJob::JOB_MYSTATUS;
- return 0;
- }
- if (job->szId[0] == 'S') {
- if (wMyStatus != ID_STATUS_ONLINE || wMyStatus != ID_STATUS_FREECHAT) {
- job->bCode = CSendLaterJob::JOB_MYSTATUS;
- return 0;
- }
- }
-
- job->lastSent = now;
- job->iSendCount++;
- job->hTargetContact = hContact;
- job->bCode = CSendLaterJob::JOB_WAITACK;
- job->hProcess = (HANDLE)ProtoChainSend(hContact, PSS_MESSAGE, 0, (LPARAM)job->sendBuffer);
- return 0;
-}
-
+/////////////////////////////////////////////////////////////////////////////////////////
// add a contact to the list of contacts having open send later jobs.
// This is is periodically checked for new additions (processContacts())
// and new jobs are created.
-void CSendLater::addContact(const MCONTACT hContact)
+
+void SendLater::addContact(const MCONTACT hContact)
{
- if (!m_fAvail)
+ if (!SendLater::Avail)
return;
- if (m_sendLaterContactList.getCount() == 0) {
- m_sendLaterContactList.insert((HANDLE)hContact);
- m_last_sendlater_processed = 0; // force processing at next tick
+ if (g_sendLaterContactList.getCount() == 0) {
+ g_sendLaterContactList.insert((HANDLE)hContact);
+ g_last_sendlater_processed = 0; // force processing at next tick
return;
}
- /*
- * this list should not have duplicate entries
- */
-
- if (m_sendLaterContactList.find((HANDLE)hContact))
+ // this list should not have duplicate entries
+ if (g_sendLaterContactList.find((HANDLE)hContact))
return;
- m_sendLaterContactList.insert((HANDLE)hContact);
- m_last_sendlater_processed = 0; // force processing at next tick
+ g_sendLaterContactList.insert((HANDLE)hContact);
+ g_last_sendlater_processed = 0; // force processing at next tick
}
+/////////////////////////////////////////////////////////////////////////////////////////
// process ACK messages for the send later job list. Called from the proto ack
// handler when it does not find a match in the normal send queue
//
// Add the message to the database and mark it as successful. The job will be
// removed later by the job list processing code.
-HANDLE CSendLater::processAck(const ACKDATA *ack)
+
+HANDLE SendLater::processAck(const ACKDATA *ack)
{
- if (m_sendLaterJobList.getCount() == 0 || !m_fAvail)
+ if (g_sendLaterJobList.getCount() == 0 || !SendLater::Avail)
return nullptr;
- for (auto &p : m_sendLaterJobList)
+ for (auto &p : g_sendLaterJobList)
if (p->hProcess == ack->hProcess && p->hTargetContact == ack->hContact && !(p->fSuccess || p->fFailed)) {
if (!p->fSuccess) {
DBEVENTINFO dbei = {};
@@ -778,26 +813,23 @@ HANDLE CSendLater::processAck(const ACKDATA *ack)
return nullptr;
}
+/////////////////////////////////////////////////////////////////////////////////////////
// UI stuff (dialog procedures for the queue manager dialog
-void CSendLater::qMgrUpdate(bool fReEnable)
+
+void SendLater::qMgrUpdate(bool fReEnable)
{
if (pDialog) {
if (fReEnable)
- pDialog->m_list.Enable(true);
+ pDialog->m_list.Enable();
pDialog->FillList();
}
}
+/////////////////////////////////////////////////////////////////////////////////////////
// invoke queue manager dialog - do nothing if this dialog is already open
-void CSendLater::invokeQueueMgrDlg()
-{
- if (pDialog == nullptr)
- (new CSendLaterDlg(this))->Create();
-}
-// service function to invoke the queue manager
-INT_PTR CSendLater::svcQMgr(WPARAM, LPARAM)
+void SendLater::invokeQueueMgrDlg()
{
- sendLater->invokeQueueMgrDlg();
- return 0;
+ if (pDialog == nullptr)
+ (new CSendLaterDlg())->Create();
}