int SendQueue::sendQueued(TWindowData *dat, const int iEntry) { CContactCache *ccActive = CContactCache::getContactCache(dat->hContact); if (ccActive == NULL) return 0; HWND hwndDlg = dat->hwnd; if (dat->sendMode & SMODE_MULTIPLE) { int iJobs = 0; int iMinLength = 0; m_jobs[iEntry].iStatus = SQ_INPROGRESS; m_jobs[iEntry].hContact = ccActive->getActiveContact(); m_jobs[iEntry].hOwnerWnd = hwndDlg; size_t iSendLength = getSendLength(iEntry); for (MCONTACT hContact = db_find_first(); hContact; hContact = db_find_next(hContact)) { HANDLE hItem = (HANDLE)SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_FINDCONTACT, hContact, 0); if (hItem && SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_GETCHECKMARK, (WPARAM)hItem, 0)) { CContactCache *c = CContactCache::getContactCache(hContact); if (c) iMinLength = (iMinLength == 0 ? c->getMaxMessageLength() : min(c->getMaxMessageLength(), iMinLength)); } } if (iSendLength >= iMinLength) { TCHAR tszError[256]; mir_sntprintf(tszError, TranslateT("The message cannot be sent delayed or to multiple contacts, because it exceeds the maximum allowed message length of %d bytes"), iMinLength); ::SendMessage(dat->hwnd, DM_ACTIVATETOOLTIP, IDC_MESSAGE, LPARAM(tszError)); sendQueue->clearJob(iEntry); return 0; } for (MCONTACT hContact = db_find_first(); hContact; hContact = db_find_next(hContact)) { HANDLE hItem = (HANDLE)SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_FINDCONTACT, hContact, 0); if (hItem && SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_GETCHECKMARK, (WPARAM)hItem, 0)) { doSendLater(iEntry, 0, hContact, false); iJobs++; } } sendQueue->clearJob(iEntry); if (iJobs) sendLater->flushQueue(); // force queue processing return 0; } if (dat->hContact == NULL) return 0; //never happens dat->nMax = dat->cache->getMaxMessageLength(); // refresh length info if (M.GetByte("autosplit", 0) && !(dat->sendMode & SMODE_SENDLATER)) { // determine send buffer length BOOL fSplit = FALSE; if (getSendLength(iEntry) >= dat->nMax) fSplit = true; if (!fSplit) goto send_unsplitted; m_jobs[iEntry].hContact = ccActive->getActiveContact(); m_jobs[iEntry].hOwnerWnd = hwndDlg; m_jobs[iEntry].iStatus = SQ_INPROGRESS; m_jobs[iEntry].iAcksNeeded = 1; m_jobs[iEntry].chunkSize = dat->nMax; DWORD dwOldFlags = m_jobs[iEntry].dwFlags; mir_forkthread(DoSplitSendA, (LPVOID)iEntry); m_jobs[iEntry].dwFlags = dwOldFlags; } else { send_unsplitted: m_jobs[iEntry].hContact = ccActive->getActiveContact(); m_jobs[iEntry].hOwnerWnd = hwndDlg; m_jobs[iEntry].iStatus = SQ_INPROGRESS; m_jobs[iEntry].iAcksNeeded = 1; if (dat->sendMode & SMODE_SENDLATER) { TCHAR tszError[256]; size_t iSendLength = getSendLength(iEntry); if (iSendLength >= dat->nMax) { mir_sntprintf(tszError, TranslateT("The message cannot be sent delayed or to multiple contacts, because it exceeds the maximum allowed message length of %d bytes"), dat->nMax); SendMessage(dat->hwnd, DM_ACTIVATETOOLTIP, IDC_MESSAGE, LPARAM(tszError)); clearJob(iEntry); return 0; } doSendLater(iEntry, dat); clearJob(iEntry); return 0; } m_jobs[iEntry].hSendId = (HANDLE)CallContactService(dat->hContact, PSS_MESSAGE, m_jobs[iEntry].dwFlags, (LPARAM)m_jobs[iEntry].szSendBuffer); if (dat->sendMode & SMODE_NOACK) { // fake the ack if we are not interested in receiving real acks ACKDATA ack = { 0 }; ack.hContact = dat->hContact; ack.hProcess = m_jobs[iEntry].hSendId; ack.type = ACKTYPE_MESSAGE; ack.result = ACKRESULT_SUCCESS; SendMessage(hwndDlg, HM_EVENTSENT, (WPARAM)MAKELONG(iEntry, 0), (LPARAM)&ack); } else SetTimer(hwndDlg, TIMERID_MSGSEND + iEntry, PluginConfig.m_MsgTimeout, NULL); } dat->iOpenJobs++; m_currentIndex++; // give icon feedback... if (dat->pContainer->hwndActive == hwndDlg) ::UpdateReadChars(dat); if (!(dat->sendMode & SMODE_NOACK)) ::HandleIconFeedback(dat, PluginConfig.g_IconSend); if (M.GetByte(SRMSGSET_AUTOMIN, SRMSGDEFSET_AUTOMIN)) ::SendMessage(dat->pContainer->hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0); return 0; }
// 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 == NULL) return 0; // should not happen 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 == 0) return 0; WORD wMyStatus = (WORD)CallProtoService(szProto, PS_GETSTATUS, 0, 0); WORD wContactStatus = c->getActiveStatus(); // 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; DWORD dwFlags = IsUtfSendAvailable(hContact) ? PREF_UTF : PREF_UNICODE; if (dwFlags & PREF_UTF) job->hProcess = (HANDLE)CallContactService(hContact, PSS_MESSAGE, dwFlags, (LPARAM)job->sendBuffer); else job->hProcess = (HANDLE)CallContactService(hContact, PSS_MESSAGE, dwFlags, (LPARAM)job->pBuf); return 0; }