From acbd8ab4c557b17a98b4259e2bd2225aafaf02cd Mon Sep 17 00:00:00 2001 From: vu1tur Date: Sat, 18 May 2013 18:51:53 +0000 Subject: spamotron sourcetree import git-svn-id: http://svn.miranda-ng.org/main/trunk@4721 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- plugins/Spamotron/src/bayes.cpp | 387 +++++++++++++++++++++++ plugins/Spamotron/src/common.h | 159 ++++++++++ plugins/Spamotron/src/options.cpp | 594 ++++++++++++++++++++++++++++++++++++ plugins/Spamotron/src/popups.cpp | 241 +++++++++++++++ plugins/Spamotron/src/resource.h | 90 ++++++ plugins/Spamotron/src/spamotron.cpp | 588 +++++++++++++++++++++++++++++++++++ plugins/Spamotron/src/utils.cpp | 521 +++++++++++++++++++++++++++++++ 7 files changed, 2580 insertions(+) create mode 100644 plugins/Spamotron/src/bayes.cpp create mode 100644 plugins/Spamotron/src/common.h create mode 100644 plugins/Spamotron/src/options.cpp create mode 100644 plugins/Spamotron/src/popups.cpp create mode 100644 plugins/Spamotron/src/resource.h create mode 100644 plugins/Spamotron/src/spamotron.cpp create mode 100644 plugins/Spamotron/src/utils.cpp (limited to 'plugins/Spamotron/src') diff --git a/plugins/Spamotron/src/bayes.cpp b/plugins/Spamotron/src/bayes.cpp new file mode 100644 index 0000000000..a0a2eab194 --- /dev/null +++ b/plugins/Spamotron/src/bayes.cpp @@ -0,0 +1,387 @@ +#include "common.h" +#include "io.h" + +#define DELIMS " ,.;!?@-\\/+&\x0D\x0A" + +sqlite3 *bayesdb; +#ifdef _DEBUG +sqlite3 *bayesdbg; +#endif +HANDLE hBayesFolder; + +int CheckBayes() +{ + FOLDERSGETDATA fgd = {0}; + char bayesdb_fullpath[MAX_PATH]; + char bayesdb_tmp[MAX_PATH]; + + char* tmp = Utils_ReplaceVars("%miranda_userdata%"); + if (tmp[strlen(tmp)-1] == '\\') + tmp[strlen(tmp)-1] = 0; + sprintf(bayesdb_tmp, "%s\\%s", tmp, BAYESDB_PATH); + mir_free(tmp); + + if (ServiceExists(MS_FOLDERS_REGISTER_PATH)) { + hBayesFolder = FoldersRegisterCustomPath(PLUGIN_NAME, Translate("Bayes database path"), bayesdb_tmp); + } else hBayesFolder = 0; + + if (hBayesFolder) + FoldersGetCustomPath(hBayesFolder, bayesdb_fullpath, MAX_PATH, bayesdb_tmp); + else + sprintf(bayesdb_fullpath, "%s", bayesdb_tmp); + + strcat(bayesdb_fullpath, "\\"BAYESDB_FILENAME); + if (_access(bayesdb_fullpath,0) == 0) + return 1; + + return 0; +} + +int OpenBayes() +{ + char bayesdb_fullpath[MAX_PATH]; + char *bayesdb_fullpath_utf8; + char *errmsg, *tmp; + sqlite3_stmt *stmt; + + if (hBayesFolder) { + FoldersGetCustomPath(hBayesFolder, bayesdb_fullpath, MAX_PATH, ""); + } + else { + tmp = Utils_ReplaceVars("%miranda_userdata%"); + if (tmp[strlen(tmp)-1] == '\\') + tmp[strlen(tmp)-1] = 0; + strcpy(bayesdb_fullpath, tmp); + strcat(bayesdb_fullpath, "\\"BAYESDB_PATH); + mir_free(tmp); + } + + CallService(MS_UTILS_CREATEDIRTREE, 0, (LPARAM)bayesdb_fullpath); + + strcat(bayesdb_fullpath, "\\"BAYESDB_FILENAME); + bayesdb_fullpath_utf8 = mir_utf8encode(bayesdb_fullpath); + + if (sqlite3_open(bayesdb_fullpath_utf8, &bayesdb) == SQLITE_OK) + { + sqlite3_exec(bayesdb, "CREATE TABLE IF NOT EXISTS spam (token blob(16), num int)", NULL, NULL, &errmsg); + sqlite3_exec(bayesdb, "CREATE TABLE IF NOT EXISTS ham (token blob(16), num int)", NULL, NULL, &errmsg); + sqlite3_exec(bayesdb, "CREATE TABLE IF NOT EXISTS stats (key varchar(32), value int)", NULL, NULL, &errmsg); + sqlite3_exec(bayesdb, "CREATE TABLE IF NOT EXISTS queue (contact int, msgtime int, message text)", NULL, NULL, &errmsg); + sqlite3_prepare_v2(bayesdb, "SELECT count(1) FROM stats WHERE key='spam_msgcount' OR key='ham_msgcount'", -1, &stmt, NULL); + if (sqlite3_step(stmt) == SQLITE_ROW) + if (sqlite3_column_int(stmt, 0) != 2) { + sqlite3_exec(bayesdb, "INSERT INTO stats VALUES ('spam_msgcount', 0)", NULL, NULL, NULL); + sqlite3_exec(bayesdb, "INSERT INTO stats VALUES ('ham_msgcount', 0)", NULL, NULL, NULL); + } + } else { + MessageBoxA(NULL, bayesdb_fullpath_utf8, "Can't open database", MB_OK); + } + + mir_free(bayesdb_fullpath_utf8); + +#ifdef _DEBUG + tmp = Utils_ReplaceVars("%miranda_userdata%"); + if (tmp[strlen(tmp)-1] == '\\') + tmp[strlen(tmp)-1] = 0; + sprintf(bayesdb_fullpath, "%s\\%s\\%s", tmp, BAYESDB_PATH, BAYESDBG_FILENAME); + mir_free(tmp); + bayesdb_fullpath_utf8 = mir_utf8encode(bayesdb_fullpath); + if (sqlite3_open(bayesdb_fullpath_utf8, &bayesdbg) == SQLITE_OK) + { + sqlite3_exec(bayesdbg, "CREATE TABLE spam (token varchar(50), num int)", NULL, NULL, &errmsg); + sqlite3_exec(bayesdbg, "CREATE TABLE ham (token varchar(50), num int)", NULL, NULL, &errmsg); + } + mir_free(bayesdb_fullpath_utf8); +#endif + + return 0; +} + +char *tokenhash(const char *token, mir_md5_byte_t *digest) +{ + mir_md5_hash((mir_md5_byte_t *)token, (int)strlen(token), digest); + return (char*)digest; +} + +int get_token_count(int type) +{ + char q[200]; + int count = 0; + sqlite3_stmt *stmt; + + if (bayesdb == NULL) + return 0; + sprintf(q, "SELECT COUNT(1) FROM %s", type == SPAM ? "spam" : "ham"); + sqlite3_prepare_v2(bayesdb, q, -1, &stmt, NULL); + if (sqlite3_step(stmt) == SQLITE_ROW) { + count = sqlite3_column_int(stmt, 0); + } + sqlite3_finalize(stmt); + return count; +} + +int get_msg_count(int type) +{ + int count = 0; + sqlite3_stmt *stmt; + + if (bayesdb == NULL) + return 0; + sqlite3_prepare_v2(bayesdb, "SELECT value FROM stats WHERE key=?", -1, &stmt, NULL); + sqlite3_bind_text(stmt, 1, type == SPAM ? "spam_msgcount" : "ham_msgcount", type == SPAM ? 13 : 12, NULL); + if (sqlite3_step(stmt) == SQLITE_ROW) { + count = sqlite3_column_int(stmt, 0); + } + sqlite3_finalize(stmt); + return count; +} + +BOOL is_token_valid(char *token) +{ + unsigned int i; + // skip digits only tokens + for (i = 0; i < strlen(token); i++) { + if ((unsigned char)token[i] >= 48 && (unsigned char)token[i] <= 57) + return FALSE; + } + + // skip 1- and 2-character tokens + if (strlen(token) < 3) + return FALSE; + + // skip "www", "com", "org", etc. + if (!strcmp(token, "www") || !strcmp(token, "com") || !strcmp(token, "org") || !strcmp(token, "edu") || + !strcmp(token, "net") || !strcmp(token, "biz") || !strcmp(token, "http") || !strcmp(token, "ftp")) + return FALSE; + + return TRUE; +} + +int get_token_score(int type, char *token) +{ + char sql[200]; + int score = 0; + mir_md5_byte_t digest[16]; + sqlite3_stmt *stmt; + + if (bayesdb == NULL) + return 0; + sprintf(sql, "SELECT num FROM %s WHERE token=?", type == SPAM ? "spam" : "ham"); + tokenhash(token, digest); + sqlite3_prepare_v2(bayesdb, sql, -1, &stmt, NULL); + sqlite3_bind_blob(stmt, 1, digest, 16, NULL); + + if (sqlite3_step(stmt) == SQLITE_ROW) { + score = sqlite3_column_int(stmt, 0); + } + sqlite3_finalize(stmt); + return score; +} + +double get_msg_score(TCHAR *msg) +{ + char *message, *token; + double spam_prob, ham_prob, tmp1 = 1, tmp2 = 1; + double *scores = NULL; + int spam_msgcount, ham_msgcount, n = 0, i; + + if (bayesdb == NULL) + return 0; + + message = mir_u2a(msg); + spam_msgcount = get_msg_count(SPAM); + ham_msgcount = get_msg_count(HAM); + token = strtok(message, DELIMS); + while (token) + { + if (!is_token_valid(token)) { + token = strtok(NULL, DELIMS); + continue; + } + scores = (double*)realloc(scores, sizeof(double)*(n + 1)); + spam_prob = spam_msgcount == 0 ? 0 : (double)get_token_score(SPAM, token) / (double)spam_msgcount; + ham_prob = ham_msgcount == 0 ? 0 : (double)get_token_score(HAM, token) / (double)ham_msgcount; + if (ham_prob == 0 && spam_prob == 0) { + spam_prob = 0.4; ham_prob = 0.6; + } + spam_prob = spam_prob > 1.0 ? 1.0 : (spam_prob < 0.01 ? 0.01 : spam_prob); + ham_prob = ham_prob > 1.0 ? 1.0 : (ham_prob < 0.01 ? 0.01 : ham_prob); + scores[n++] = spam_prob / (spam_prob + ham_prob); + + token = strtok(NULL, DELIMS); + } + + for (i = 0; i < n; i++) { + tmp1 *= scores[i]; + tmp2 *= 1-scores[i]; + } + + mir_free(message); + free(scores); + return tmp1 / (tmp1 + tmp2); +} + +void queue_message(HANDLE hContact, DWORD msgtime, TCHAR *message) +{ + char *tmp; + sqlite3_stmt *stmt; + + if (!_getOptB("BayesAutolearnApproved", defaultBayesAutolearnApproved) && + !_getOptB("BayesAutolearnNotApproved", defaultBayesAutolearnNotApproved)) + return; + + if (_getOptB("BayesEnabled", defaultBayesEnabled) == 0) + return; + if (bayesdb == NULL) + OpenBayes(); + + sqlite3_prepare_v2(bayesdb, "INSERT INTO queue VALUES(?,?,?)", -1, &stmt, NULL); + sqlite3_bind_int(stmt, 1, (DWORD)hContact); + sqlite3_bind_int(stmt, 2, msgtime); + tmp = mir_u2a(message); + sqlite3_bind_text(stmt, 3, tmp, (int)strlen(tmp), NULL); + sqlite3_step(stmt); + mir_free(tmp); + sqlite3_finalize(stmt); +} + +void bayes_approve_contact(HANDLE hContact) +{ + const char *message; + TCHAR *messageW; + int d = 0; + sqlite3_stmt *stmt; + + if (bayesdb == NULL) + return; + + sqlite3_prepare_v2(bayesdb, "SELECT message FROM queue WHERE contact=?", -1, &stmt, NULL); + sqlite3_bind_int(stmt, 1, (DWORD)hContact); + while (sqlite3_step(stmt) == SQLITE_ROW) + { + d = 1; + message = (char*)sqlite3_column_text(stmt, 0); + messageW = mir_a2u(message); + learn_ham(messageW); + mir_free(messageW); + } + sqlite3_finalize(stmt); + if (d) { + sqlite3_prepare_v2(bayesdb, "DELETE FROM queue WHERE contact=?", -1, &stmt, NULL); + sqlite3_bind_int(stmt, 1, (DWORD)hContact); + sqlite3_step(stmt); + sqlite3_finalize(stmt); + } + +} + +void dequeue_messages() +{ + time_t t = time(NULL); + sqlite3_stmt *stmt; + const char *message; + TCHAR *messageW; + int d = 0; + + if (bayesdb == NULL) + return; + + sqlite3_prepare_v2(bayesdb, "SELECT message FROM queue WHERE msgtime + ? < ?", -1, &stmt, NULL); + sqlite3_bind_int(stmt, 1, _getOptD("BayesWaitApprove", defaultBayesWaitApprove)*86400); + sqlite3_bind_int(stmt, 2, (DWORD)t); + while (sqlite3_step(stmt) == SQLITE_ROW) { + d = 1; + message = (char*)sqlite3_column_text(stmt, 0); + messageW = mir_a2u(message); + learn_spam(messageW); + mir_free(messageW); + } + sqlite3_finalize(stmt); + if (d) { + sqlite3_prepare_v2(bayesdb, "DELETE FROM queue WHERE msgtime + ? < ?", -1, &stmt, NULL); + sqlite3_bind_int(stmt, 1, _getOptD("BayesWaitApprove", defaultBayesWaitApprove)*86400); + sqlite3_bind_int(stmt, 2, (DWORD)t); + sqlite3_step(stmt); + sqlite3_finalize(stmt); + } +} + +/* Learn one message as either SPAM or HAM as specified in type parameter */ +void learn(int type, TCHAR *msg) +{ + char *tok, *message; + mir_md5_byte_t digest[16]; + char sql_select[200], sql_update[200], sql_insert[200], sql_counter[200]; + sqlite3_stmt *stmt; +#ifdef _DEBUG + sqlite3_stmt *stmtdbg; +#endif + + if (_getOptB("BayesEnabled", defaultBayesEnabled) == 0) + return; + if (bayesdb == NULL) + OpenBayes(); + + message = mir_u2a(msg); + tok = strtok(message, DELIMS); + sprintf(sql_counter, "UPDATE stats SET value=value+1 WHERE key='%s'", type == SPAM ? "spam_msgcount" : "ham_msgcount"); + sprintf(sql_select, "SELECT 1 FROM %s WHERE token=?", type == SPAM ? "spam" : "ham"); + sprintf(sql_update, "UPDATE %s SET num=num+1 WHERE token=?", type ? "spam" : "ham"); + sprintf(sql_insert, "INSERT INTO %s VALUES(?, 1)", type ? "spam" : "ham"); +#ifdef _DEBUG + sqlite3_exec(bayesdbg, "BEGIN", NULL, NULL, NULL); +#endif + sqlite3_exec(bayesdb, "BEGIN", NULL, NULL, NULL); + while (tok) { + if (!is_token_valid(tok)) { + tok = strtok(NULL, DELIMS); + continue; + } + tokenhash(tok, digest); + sqlite3_prepare_v2(bayesdb, sql_select, -1, &stmt, NULL); + sqlite3_bind_blob(stmt, 1, digest, 16, SQLITE_STATIC); + if (SQLITE_ROW == sqlite3_step(stmt)) { + sqlite3_finalize(stmt); + sqlite3_prepare_v2(bayesdb, sql_update, -1, &stmt, NULL); + } else { + sqlite3_finalize(stmt); + sqlite3_prepare_v2(bayesdb, sql_insert, -1, &stmt, NULL); + } + sqlite3_bind_blob(stmt, 1, digest, 16, SQLITE_STATIC); + sqlite3_step(stmt); + sqlite3_finalize(stmt); + +#ifdef _DEBUG + sqlite3_prepare_v2(bayesdbg, sql_select, -1, &stmtdbg, NULL); + sqlite3_bind_text(stmtdbg, 1, tok, strlen(tok), NULL); + if (SQLITE_ROW == sqlite3_step(stmtdbg)) { + sqlite3_finalize(stmtdbg); + sqlite3_prepare_v2(bayesdbg, sql_update, -1, &stmtdbg, NULL); + } else { + sqlite3_finalize(stmtdbg); + sqlite3_prepare_v2(bayesdbg, sql_insert, -1, &stmtdbg, NULL); + } + sqlite3_bind_text(stmtdbg, 1, tok, strlen(tok), SQLITE_STATIC); + sqlite3_step(stmtdbg); + sqlite3_finalize(stmtdbg); +#endif + + tok = strtok(NULL, DELIMS); + } + sqlite3_exec(bayesdb, sql_counter, NULL, NULL, NULL); + sqlite3_exec(bayesdb, "COMMIT", NULL, NULL, NULL); +#ifdef _DEBUG + sqlite3_exec(bayesdbg, "COMMIT", NULL, NULL, NULL); +#endif + mir_free(message); +} + +void learn_ham(TCHAR *msg) +{ + learn(0, msg); +} + +void learn_spam(TCHAR *msg) +{ + learn(1, msg); +} \ No newline at end of file diff --git a/plugins/Spamotron/src/common.h b/plugins/Spamotron/src/common.h new file mode 100644 index 0000000000..8cb5e81202 --- /dev/null +++ b/plugins/Spamotron/src/common.h @@ -0,0 +1,159 @@ +#include +#include +#include +#include +#define MIRANDA_VER 0x0A00 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#include "m_updater.h" +#include "m_folders.h" +#include "../pcre/pcre.h" + +#include "resource.h" + +#define PLUGIN_NAME "Spam-o-tron" + +#define SPAMOTRON_MODE_PLAIN 0 +#define SPAMOTRON_MODE_MATH 1 +#define SPAMOTRON_MODE_ROTATE 2 +#define SPAMOTRON_MODE_RANDOM 3 + +#define MAX_BUFFER_LENGTH 1024 + +#define _getCOptB(a,b,c) db_get_b(a, PLUGIN_NAME, b, c) +#define _setCOptB(a,b,c) db_set_b(a, PLUGIN_NAME, b, c) +#define _getCOptD(a,b,c) db_get_dw(a, PLUGIN_NAME, b, c) +#define _setCOptD(a,b,c) db_set_dw(a, PLUGIN_NAME, b, c) +#define _getOptB(a,b) _getCOptB(NULL, a, b) +#define _setOptB(a,b) _setCOptB(NULL, a, b) +#define _getOptD(a,b) _getCOptD(NULL, a, b) +#define _setOptD(a,b) _setCOptD(NULL, a, b) + +TCHAR* _getCOptS(TCHAR *buf, unsigned int buflen, HANDLE hContact, const char* option, const TCHAR *def); +#define _getOptS(a,b,c,d) _getCOptS(a, b, NULL, c, d) +#define _setCOptTS(a,b,c) db_set_ts(a, PLUGIN_NAME, b, c) +#define _setCOptS(a,b,c) db_set_s(a, PLUGIN_NAME, b, c) +#define _setOptTS(a,b) _setCOptTS(NULL, a, b) + +#define defaultMode SPAMOTRON_MODE_PLAIN +#define defaultChallenge TranslateT("Spam-o-tron needs to verify you're not a bot. Reply with \"%response%\" without quotes.") +#define defaultChallengeMath TranslateT("Spam-o-tron needs to verify you're not a bot. Reply with a result of expression %mathexpr%.") +#define defaultResponse _T("no-spam") +#define defaultResponseCC TRUE +#define defaultSuccessResponse TranslateT("Verified.") +#define defaultAuthChallenge TranslateT("Spam-o-tron delayed authorization request. First reply with \"%response%\" without quotes.") +#define defaultAuthChallengeMath TranslateT("Spam-o-tron delayed authorization request. First reply with a result of expression %mathexpr%.") +#define defaultReplyOnSuccess TRUE +#define defaultReplyOnAuth TRUE +#define defaultReplyOnMsg TRUE +#define defaultApproveOnMsgOut TRUE +#define defaultApproveOnMsgIn FALSE +#define defaultAddPermanently FALSE +#define defaultHideUnverified TRUE +#define defaultKeepBlockedMsg TRUE +#define defaultMarkMsgUnreadOnApproval FALSE +#define defaultLogActions TRUE +#define defaultNotifyPopup FALSE +#define defaultDontReplySameMsg TRUE +#define defaultDontReplyMsg TRUE +#define defaultApproveOnMsgInWordlist _T("") +#define defaultDontReplyMsgWordlist _T("Spam-o-tron, StopSpam, Anti-Spam") +#define defaultMaxMsgContactCountPerDay 3 +#define defaultMaxSameMsgCountPerDay 2 + +#define defaultNotifyPopupBlocked TRUE +#define defaultNotifyPopupApproved TRUE +#define defaultNotifyPopupChallenge TRUE +#define defaultPopupDefaultColors FALSE +#define defaultPopupWindowsColors FALSE +#define defaultPopupDefaultTimeout TRUE +#define defaultPopupBlockedTimeout 2 +#define defaultPopupApprovedTimeout 2 +#define defaultPopupChallengeTimeout 2 +#define defaultPopupBlockedForeground RGB(0, 0, 0) +#define defaultPopupBlockedBackground RGB(240, 128, 128) +#define defaultPopupApprovedForeground RGB(0, 0, 0) +#define defaultPopupApprovedBackground RGB(128, 240, 128) +#define defaultPopupChallengeForeground RGB(0, 0, 0) +#define defaultPopupChallengeBackground RGB(180, 210, 240) + +#define _NOTIFYP _getOptB("NotifyPopup", defaultNotifyPopup) + +TCHAR* ReplaceVars(TCHAR *dst, unsigned int len); +TCHAR* ReplaceVarsNum(TCHAR *dst, unsigned int len, int num); +TCHAR* ReplaceVar(TCHAR *dst, unsigned int len, const TCHAR *var, const TCHAR *rvar); +int get_response_id(const TCHAR *strvar); +int get_response_num(const TCHAR *str); +TCHAR* get_response(TCHAR* dst, unsigned int dstlen, int num); + +TCHAR* _tcsstr_cc(TCHAR* str, TCHAR* strSearch, BOOL cc); +BOOL _isregex(TCHAR* strSearch); +BOOL _isvalidregex(TCHAR* strSearch); +BOOL _regmatch(TCHAR* str, TCHAR* strSearch); +BOOL Contains(TCHAR* dst, TCHAR* src); +BOOL isOneDay(DWORD timestamp1, DWORD timestamp2); +void MarkUnread(HANDLE hContact); + +int ShowPopup(HANDLE hContact, BYTE popupType, TCHAR *line1, TCHAR *line2); +int ShowPopupPreview(HWND optDlg, BYTE popupType, TCHAR *line1, TCHAR *line2); +int _notify(HANDLE hContact, BYTE type, TCHAR *message, TCHAR *origmessage); +int LogToSystemHistory(char *message, char *origmessage); +#define POPUP_DEFAULT 0 +#define POPUP_BLOCKED 1 +#define POPUP_APPROVED 2 +#define POPUP_CHALLENGE 3 + +#ifdef _UNICODE +#define CONTACT_NAME(a) CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)a, GCDNF_NOMYHANDLE | GCDNF_UNICODE | GCDNF_NOCACHE) +#else +#define CONTACT_NAME(a) CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)a, GCDNF_NOMYHANDLE | GCDNF_NOCACHE) +#endif + + +/* bayes.c */ + +#include "../sqlite3/sqlite3.h" +extern sqlite3 *bayesdb; +#define BAYESDB_PATH "spamotron" +#define BAYESDB_FILENAME "bayes.db" +#define defaultBayesEnabled TRUE +#define defaultBayesBlockMsg FALSE +#define defaultBayesAutoApprove FALSE +#define defaultBayesAutolearnApproved TRUE +#define defaultBayesAutolearnAutoApproved FALSE +#define defaultBayesAutolearnNotApproved TRUE +#define defaultBayesAutolearnOutgoing FALSE +#define defaultBayesWaitApprove 2 +#define SCORE_C 0.0001 +#define defaultBayesSpamScore 9500 +#define defaultBayesHamScore 500 +#define HAM 0 +#define SPAM 1 + +int OpenBayes(); +int CheckBayes(); +void learn(int type, TCHAR *msg); +void learn_ham(TCHAR *msg); +void learn_spam(TCHAR *msg); +int get_token_count(int type); +int get_msg_count(int type); +double get_msg_score(TCHAR *msg); +void queue_message(HANDLE hContact, DWORD msgtime, TCHAR *message); +void bayes_approve_contact(HANDLE hContact); +void dequeue_messages(); + +#ifdef _DEBUG +extern sqlite3 *bayesdbg; +#define BAYESDBG_FILENAME "bayes.dbg" +#endif \ No newline at end of file diff --git a/plugins/Spamotron/src/options.cpp b/plugins/Spamotron/src/options.cpp new file mode 100644 index 0000000000..ca7c78317a --- /dev/null +++ b/plugins/Spamotron/src/options.cpp @@ -0,0 +1,594 @@ +#include "common.h" + +TCHAR currentResponse[256] = {0}; + +TCHAR* _getCOptS(TCHAR *buf, unsigned int buflen, HANDLE hContact, const char* option, const TCHAR *def) +{ + DBVARIANT dbv = {0}; + _tcsnset(buf, 0, buflen); + if (db_get_ts(hContact, PLUGIN_NAME, option, &dbv) != 0) + _tcsncpy(buf, def, min(buflen, _tcslen(def)+1)); + else if (dbv.type == DBVT_TCHAR) { + _tcsncpy(buf, dbv.ptszVal, min(buflen, _tcslen(dbv.ptszVal)+1)); + } + db_free(&dbv); + return buf; +} +TCHAR* _getMOptS(TCHAR *buf, unsigned int buflen, const char* module, const char* option, const TCHAR *def) +{ + TCHAR* tmp; + DBVARIANT dbv = {0}; + _tcsnset(buf, 0, buflen); + if (db_get_s(NULL, module, option, &dbv) != 0) + _tcsncpy(buf, def, min(buflen, _tcslen(def)+1)); + else if (dbv.type == DBVT_TCHAR) { + _tcsncpy(buf, dbv.ptszVal, min(buflen, _tcslen(dbv.ptszVal)+1)); + } else { + tmp = mir_a2u(dbv.pszVal); + _tcsncpy(buf, tmp, min(buflen, _tcslen(tmp)+1)); + mir_free(tmp); + } + db_free(&dbv); + return buf; +} + + +BOOL _saveDlgItemText(HWND hDialog, int controlID, char* option) +{ + int len; + TCHAR *tmp; + len = GetWindowTextLength(GetDlgItem(hDialog, controlID)); + tmp = (TCHAR *)malloc((len + 1)*sizeof(TCHAR)); + GetDlgItemText(hDialog, controlID, tmp, len + 1); + _setOptTS(option, tmp); + free(tmp); + return TRUE; +} +int _saveDlgItemResponse(HWND hDialog, int controlID, char* option) +{ + int ret = 0; + int isRegex = 0; + int len; + TCHAR *tmp; + len = GetWindowTextLength(GetDlgItem(hDialog, controlID)); + tmp = (TCHAR*)malloc((len+1)*sizeof(TCHAR)); + GetDlgItemText(hDialog, controlID, tmp, len+1); + isRegex = _isregex(tmp); + if (!isRegex) + ret = _saveDlgItemText(hDialog, controlID, option) ? 1 : 0; + else { + if (_isvalidregex(tmp)) + ret = _saveDlgItemText(hDialog, controlID, option) ? 1 : 0; + else + ret = -1; + } + free(tmp); + return ret; +} +BOOL _saveDlgItemInt(HWND hDialog, int controlID, char* option) +{ + int len; + TCHAR *tmp; + len = GetWindowTextLength(GetDlgItem(hDialog, controlID)); + tmp = (TCHAR *)malloc((len + 1)*sizeof(TCHAR)); + GetDlgItemText(hDialog, controlID, tmp, len + 1); + _setOptD(option, _ttoi(tmp)); + free(tmp); + return TRUE; +} +BOOL _saveDlgItemScore(HWND hDialog, int controlID, char* option) +{ + int len; + TCHAR *tmp; + len = GetWindowTextLength(GetDlgItem(hDialog, controlID)); + tmp = (TCHAR *)malloc((len + 1)*sizeof(TCHAR)); + GetDlgItemText(hDialog, controlID, tmp, len + 1); + _setOptD(option, _tcstod(tmp, NULL)/SCORE_C); + return TRUE; +} + +extern HINSTANCE hInst; + +BOOL CALLBACK DlgProcOptionsMain(HWND optDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + static int bInitializing = 0, i, j, numProtocols; + PROTOCOLDESCRIPTOR** pd; + TCHAR pName[200] = {0}; + char protoOption[256] = {0}; + char protoName[256] = {0}; + HWND hProtocolsList = GetDlgItem(optDlg, IDC_OPT_PROTOCOLS); + LVITEM lvi = {0}; + LVCOLUMN lvc = {0}; + TCHAR *buf; + int buflen = 500; + switch (msg) { + case WM_INITDIALOG: + TranslateDialogDefault(optDlg); + bInitializing = 1; + + ///Main enable switch + SendDlgItemMessage(optDlg, IDC_OPT_OUT_MSG_APPROVE, BM_SETCHECK, _getOptB("ApproveOnMsgOut", defaultApproveOnMsgOut), 0); + SendDlgItemMessage(optDlg, IDC_OPT_IN_MSG_APPROVE, BM_SETCHECK, _getOptB("ApproveOnMsgIn", defaultApproveOnMsgIn), 0); + SendDlgItemMessage(optDlg, IDC_OPT_DONT_REPLY_SAME_MSG, BM_SETCHECK, _getOptB("DontReplySameMsg", defaultDontReplySameMsg), 0); + SendDlgItemMessage(optDlg, IDC_OPT_DONT_REPLY_MSG, BM_SETCHECK, _getOptB("DontReplyMsg", defaultDontReplyMsg), 0); + SendDlgItemMessage(optDlg, IDC_OPT_HIDE_UNTIL_VERIFIED, BM_SETCHECK, _getOptB("HideUnverified", defaultHideUnverified), 0); + SendDlgItemMessage(optDlg, IDC_OPT_ADD_PERMANENTLY, BM_SETCHECK, _getOptB("AddPermanently", defaultAddPermanently), 0); + SendDlgItemMessage(optDlg, IDC_OPT_LOG_ACTIONS, BM_SETCHECK, _getOptB("LogActions", defaultLogActions), 0); + buf = (TCHAR *)mir_alloc(buflen*sizeof(TCHAR)); + SetDlgItemText(optDlg, IDC_OPT_IN_MSG_APPROVE_WORDLIST, _getOptS(buf, buflen, "ApproveOnMsgInWordlist", defaultApproveOnMsgInWordlist)); + SetDlgItemText(optDlg, IDC_OPT_MAX_MSG_CONTACT, _itot((unsigned int)_getOptD("MaxMsgContactCountPerDay", defaultMaxMsgContactCountPerDay), buf, 10)); + SetDlgItemText(optDlg, IDC_OPT_MAX_SAME_MSG, _itot((unsigned int)_getOptD("MaxSameMsgCountPerDay", defaultMaxSameMsgCountPerDay), buf, 10)); + SetDlgItemText(optDlg, IDC_OPT_DONT_REPLY_MSG_WORDLIST, _getOptS(buf, buflen, "DontReplyMsgWordlist", defaultDontReplyMsgWordlist)); + mir_free(buf); + + ///Individual protocols list + ListView_SetExtendedListViewStyle(hProtocolsList, LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT); + lvc.mask = LVCF_WIDTH; + lvc.cx = 120; + ListView_InsertColumn(hProtocolsList, 0, &lvc); + lvi.mask = LVIF_TEXT | LVIF_STATE; + CallService(MS_PROTO_ENUMACCOUNTS, (LPARAM)&numProtocols, (WPARAM)&pd); + for (i = 0, j = 0; i < numProtocols; i++) + { + lvi.iItem = i; + _getMOptS(pName, 200*sizeof(TCHAR), pd[i]->szName, "AM_BaseProto", _T("")); + if (_tcscmp(pName, _T("ICQ")) != 0) + continue; + lvi.pszText = mir_a2u(pd[i]->szName); + ListView_InsertItem(hProtocolsList, &lvi); + ZeroMemory(protoOption, 256); + strcat(protoOption, "proto_"); + strcat(protoOption, pd[i]->szName); + ListView_SetCheckState(hProtocolsList, j++, _getOptB(protoOption, 0)); + } + + bInitializing = 0; + return TRUE; + + case WM_COMMAND: + if (bInitializing) + return FALSE; + switch (LOWORD(wParam)) { + case IDC_OPT_OUT_MSG_APPROVE: + case IDC_OPT_IN_MSG_APPROVE: + case IDC_OPT_DONT_REPLY_SAME_MSG: + case IDC_OPT_DONT_REPLY_MSG: + case IDC_OPT_ADD_PERMANENTLY: + case IDC_OPT_HIDE_UNTIL_VERIFIED: + case IDC_OPT_LOG_ACTIONS: + if (HIWORD(wParam) != BN_CLICKED) + return FALSE; + break; + case IDC_OPT_IN_MSG_APPROVE_WORDLIST: + case IDC_OPT_MAX_MSG_CONTACT: + case IDC_OPT_MAX_SAME_MSG: + case IDC_OPT_DONT_REPLY_MSG_WORDLIST: + if (HIWORD(wParam) != EN_CHANGE) + return FALSE; + break; + } + SendMessage(GetParent(optDlg), PSM_CHANGED, 0, 0); + break; + case WM_NOTIFY: + if (bInitializing) + return FALSE; + switch (LOWORD(wParam)) { + case IDC_OPT_PROTOCOLS: + if ( + ((LPNMHDR)lParam)->code == LVN_ITEMCHANGED && + ((LPNMLISTVIEW)lParam)->uChanged & LVIF_STATE && + (((LPNMLISTVIEW)lParam)->uOldState & LVIS_STATEIMAGEMASK) != \ + (((LPNMLISTVIEW)lParam)->uNewState & LVIS_STATEIMAGEMASK) + ) + SendMessage(GetParent(optDlg), PSM_CHANGED, 0, 0); + break; + } + switch (((NMHDR*)lParam)->code) { + case PSN_APPLY: + _setOptB("ApproveOnMsgOut", SendDlgItemMessage(optDlg, IDC_OPT_OUT_MSG_APPROVE, BM_GETCHECK, 0, 0)); + _setOptB("ApproveOnMsgIn", SendDlgItemMessage(optDlg, IDC_OPT_IN_MSG_APPROVE, BM_GETCHECK, 0, 0)); + _setOptB("DontReplySameMsg", SendDlgItemMessage(optDlg, IDC_OPT_DONT_REPLY_SAME_MSG, BM_GETCHECK, 0, 0)); + _setOptB("DontReplyMsg", SendDlgItemMessage(optDlg, IDC_OPT_DONT_REPLY_MSG, BM_GETCHECK, 0, 0)); + _setOptB("AddPermanently", SendDlgItemMessage(optDlg, IDC_OPT_ADD_PERMANENTLY, BM_GETCHECK, 0, 0)); + _setOptB("HideUnverified", SendDlgItemMessage(optDlg, IDC_OPT_HIDE_UNTIL_VERIFIED, BM_GETCHECK, 0, 0)); + _setOptB("LogActions", SendDlgItemMessage(optDlg, IDC_OPT_LOG_ACTIONS, BM_GETCHECK, 0, 0)); + _saveDlgItemText(optDlg, IDC_OPT_IN_MSG_APPROVE_WORDLIST, "ApproveOnMsgInWordlist"); + _saveDlgItemText(optDlg, IDC_OPT_DONT_REPLY_MSG_WORDLIST, "DontReplyMsgWordlist"); + _saveDlgItemInt(optDlg, IDC_OPT_MAX_MSG_CONTACT, "MaxMsgContactCountPerDay"); + _saveDlgItemInt(optDlg, IDC_OPT_MAX_SAME_MSG, "MaxSameMsgCountPerDay"); + numProtocols = ListView_GetItemCount(hProtocolsList); + buf = (TCHAR *)malloc(256*sizeof(TCHAR *)); + for (i = 0; i < numProtocols; i++) { + ListView_GetItemText(hProtocolsList, i, 0, buf, 256); + //wcstombs(protoName, buf, 256); + ZeroMemory(protoOption, 256); + strcat(protoOption, "proto_"); + strcat(protoOption, mir_u2a(buf)); + _setOptB(protoOption, ListView_GetCheckState(hProtocolsList, i)); + } + free(buf); + return TRUE; + } + break; + case WM_DESTROY: + break; + } + return FALSE; +} + +BOOL CALLBACK DlgProcOptionsQuestion(HWND optDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + static int bInitializing = 0; + int i, selectedMode; + HWND ht; + TCHAR *buf; + unsigned int buflen = 500; + + switch (msg) { + case WM_INITDIALOG: + TranslateDialogDefault(optDlg); + bInitializing = 1; + + ht = GetDlgItem(optDlg, IDC_OPT_MODE); + SendMessage(ht, CB_ADDSTRING, 0, (LPARAM)TranslateT("Simple")); + SendMessage(ht, CB_SETITEMDATA, 0, SPAMOTRON_MODE_PLAIN); + SendMessage(ht, CB_ADDSTRING, 0, (LPARAM)TranslateT("Math expression")); + SendMessage(ht, CB_SETITEMDATA, 1, SPAMOTRON_MODE_MATH); + SendMessage(ht, CB_ADDSTRING, 0, (LPARAM)TranslateT("Round-robin")); + SendMessage(ht, CB_SETITEMDATA, 2, SPAMOTRON_MODE_ROTATE); + SendMessage(ht, CB_ADDSTRING, 0, (LPARAM)TranslateT("Random")); + SendMessage(ht, CB_SETITEMDATA, 3, SPAMOTRON_MODE_RANDOM); + + selectedMode = _getOptB("Mode", defaultMode); + for (i = 0; i < SendMessage(ht, CB_GETCOUNT, 0, 0); i++) { + if (SendMessage(ht, CB_GETITEMDATA, i, 0) == selectedMode) { + SendMessage(ht, CB_SETCURSEL, i, 0); + break; + } + } + SetDlgItemText(optDlg, IDC_OPT_MATH_RESPONSE, TranslateT("Will be automatically evaluated from %mathexpr%")); + buf = (TCHAR *)malloc(buflen*sizeof(TCHAR)); + switch (selectedMode) { + case SPAMOTRON_MODE_PLAIN: + case SPAMOTRON_MODE_ROTATE: + case SPAMOTRON_MODE_RANDOM: + ShowWindow(GetDlgItem(optDlg, IDC_STATIC_MODEMSG), 0); + ShowWindow(GetDlgItem(optDlg, IDC_OPT_MATH_RESPONSE), 0); + ShowWindow(GetDlgItem(optDlg, IDC_OPT_RESPONSE), 1); + ShowWindow(GetDlgItem(optDlg, IDC_OPT_CCRESPONSE), 1); + EnableWindow(GetDlgItem(optDlg, IDC_OPT_RESPONSE), TRUE); + SetDlgItemText(optDlg, IDC_OPT_CHALLENGE, _getOptS(buf, buflen, "Challenge", defaultChallenge)); + SetDlgItemText(optDlg, IDC_OPT_AUTH_CHALLENGE, _getOptS(buf, buflen, "AuthChallenge", defaultAuthChallenge)); + break; + case SPAMOTRON_MODE_MATH: + ShowWindow(GetDlgItem(optDlg, IDC_STATIC_MODEMSG), 1); + ShowWindow(GetDlgItem(optDlg, IDC_OPT_MATH_RESPONSE), 1); + ShowWindow(GetDlgItem(optDlg, IDC_OPT_RESPONSE), 0); + ShowWindow(GetDlgItem(optDlg, IDC_OPT_CCRESPONSE), 0); + EnableWindow(GetDlgItem(optDlg, IDC_OPT_RESPONSE), FALSE); + SetDlgItemText(optDlg, IDC_OPT_CHALLENGE, _getOptS(buf, buflen, "ChallengeMath", defaultChallengeMath)); + SetDlgItemText(optDlg, IDC_OPT_AUTH_CHALLENGE, _getOptS(buf, buflen, "AuthChallengeMath", defaultAuthChallengeMath)); + break; + } + SendDlgItemMessage(optDlg, IDC_OPT_REPLY_ON_SUCCESS, BM_SETCHECK, _getOptB("ReplyOnSuccess", defaultReplyOnSuccess), 0); + SendDlgItemMessage(optDlg, IDC_OPT_REPLY_ON_AUTH, BM_SETCHECK, _getOptB("ReplyOnAuth", defaultReplyOnAuth), 0); + SendDlgItemMessage(optDlg, IDC_OPT_REPLY_ON_MSG, BM_SETCHECK, _getOptB("ReplyOnMsg", defaultReplyOnMsg), 0); + SendDlgItemMessage(optDlg, IDC_OPT_KEEP_BLOCKED_MSG, BM_SETCHECK, _getOptB("KeepBlockedMsg", defaultKeepBlockedMsg), 0); + SendDlgItemMessage(optDlg, IDC_OPT_MARK_MSG_UNREAD_ON_APPROVAL, BM_SETCHECK, _getOptB("MarkMsgUnreadOnApproval", defaultMarkMsgUnreadOnApproval), 0); + EnableWindow(GetDlgItem(optDlg, IDC_OPT_MARK_MSG_UNREAD_ON_APPROVAL), _getOptB("KeepBlockedMsg", defaultKeepBlockedMsg)); + SendDlgItemMessage(optDlg, IDC_OPT_CCRESPONSE, BM_SETCHECK, _getOptB("ResponseCC", defaultResponseCC), 0); + SetDlgItemText(optDlg, IDC_OPT_RESPONSE, _getOptS(buf, buflen, "Response", defaultResponse)); + SetDlgItemText(optDlg, IDC_OPT_SUCCESS_RESPONSE, _getOptS(buf, buflen, "SuccessResponse", defaultSuccessResponse)); + free(buf); + + bInitializing = 0; + return TRUE; + + case WM_COMMAND: + if (bInitializing) + return FALSE; + switch (LOWORD(wParam)) { + case IDC_OPT_MODE: + if (HIWORD(wParam) != CBN_SELCHANGE) + return FALSE; + i = SendMessage(GetDlgItem(optDlg, IDC_OPT_MODE), CB_GETCURSEL, 0, 0); + selectedMode = SendMessage(GetDlgItem(optDlg, IDC_OPT_MODE), CB_GETITEMDATA, i, 0); + buf = (TCHAR*)malloc(buflen*sizeof(TCHAR)); + switch (selectedMode) { + case SPAMOTRON_MODE_PLAIN: + case SPAMOTRON_MODE_ROTATE: + case SPAMOTRON_MODE_RANDOM: + ShowWindow(GetDlgItem(optDlg, IDC_STATIC_MODEMSG), 0); + ShowWindow(GetDlgItem(optDlg, IDC_OPT_MATH_RESPONSE), 0); + ShowWindow(GetDlgItem(optDlg, IDC_OPT_RESPONSE), 1); + ShowWindow(GetDlgItem(optDlg, IDC_OPT_CCRESPONSE), 1); + EnableWindow(GetDlgItem(optDlg, IDC_OPT_RESPONSE), TRUE); + SetDlgItemText(optDlg, IDC_OPT_CHALLENGE, _getOptS(buf, buflen, "Challenge", defaultChallenge)); + SetDlgItemText(optDlg, IDC_OPT_AUTH_CHALLENGE, _getOptS(buf, buflen, "AuthChallenge", defaultAuthChallenge)); + break; + case SPAMOTRON_MODE_MATH: + ShowWindow(GetDlgItem(optDlg, IDC_STATIC_MODEMSG), 1); + ShowWindow(GetDlgItem(optDlg, IDC_OPT_MATH_RESPONSE), 1); + ShowWindow(GetDlgItem(optDlg, IDC_OPT_RESPONSE), 0); + ShowWindow(GetDlgItem(optDlg, IDC_OPT_CCRESPONSE), 0); + EnableWindow(GetDlgItem(optDlg, IDC_OPT_RESPONSE), FALSE); + SetDlgItemText(optDlg, IDC_OPT_CHALLENGE, _getOptS(buf, buflen, "ChallengeMath", defaultChallengeMath)); + SetDlgItemText(optDlg, IDC_OPT_AUTH_CHALLENGE, _getOptS(buf, buflen, "AuthChallengeMath", defaultAuthChallengeMath)); + break; + } + free(buf); + break; + case IDC_OPT_REPLY_ON_SUCCESS: + case IDC_OPT_REPLY_ON_AUTH: + case IDC_OPT_REPLY_ON_MSG: + case IDC_OPT_KEEP_BLOCKED_MSG: + case IDC_OPT_MARK_MSG_UNREAD_ON_APPROVAL: + case IDC_OPT_CCRESPONSE: + EnableWindow(GetDlgItem(optDlg, IDC_OPT_MARK_MSG_UNREAD_ON_APPROVAL), SendDlgItemMessage(optDlg, IDC_OPT_KEEP_BLOCKED_MSG, BM_GETCHECK, 0, 0)); + if (HIWORD(wParam) != BN_CLICKED) + return FALSE; + break; + case IDC_OPT_CHALLENGE: + case IDC_OPT_RESPONSE: + case IDC_OPT_SUCCESS_RESPONSE: + case IDC_OPT_AUTH_CHALLENGE: + if (HIWORD(wParam) != EN_CHANGE) + return FALSE; + break; + case IDC_DEFAULTS: + SetDlgItemText(optDlg, IDC_STATIC_MODEMSG, _T("")); + ShowWindow(GetDlgItem(optDlg, IDC_OPT_MATH_RESPONSE), 0); + ShowWindow(GetDlgItem(optDlg, IDC_OPT_RESPONSE), 1); + EnableWindow(GetDlgItem(optDlg, IDC_OPT_RESPONSE), TRUE); + SendDlgItemMessage(optDlg, IDC_OPT_MODE, CB_SETCURSEL, 0, 0); + SendDlgItemMessage(optDlg, IDC_OPT_CCRESPONSE, BM_SETCHECK, defaultResponseCC, 0); + SetDlgItemText(optDlg, IDC_OPT_CHALLENGE, defaultChallenge); + SetDlgItemText(optDlg, IDC_OPT_RESPONSE, defaultResponse); + SetDlgItemText(optDlg, IDC_OPT_SUCCESS_RESPONSE, defaultSuccessResponse); + SetDlgItemText(optDlg, IDC_OPT_AUTH_CHALLENGE, defaultAuthChallenge); + SendDlgItemMessage(optDlg, IDC_OPT_REPLY_ON_SUCCESS, BM_SETCHECK, defaultReplyOnSuccess, 0); + SendDlgItemMessage(optDlg, IDC_OPT_REPLY_ON_AUTH, BM_SETCHECK, defaultReplyOnAuth, 0); + SendDlgItemMessage(optDlg, IDC_OPT_REPLY_ON_MSG, BM_SETCHECK, defaultReplyOnMsg, 0); + SendDlgItemMessage(optDlg, IDC_OPT_KEEP_BLOCKED_MSG, BM_SETCHECK, defaultKeepBlockedMsg, 0); + SendDlgItemMessage(optDlg, IDC_OPT_MARK_MSG_UNREAD_ON_APPROVAL, BM_SETCHECK, defaultMarkMsgUnreadOnApproval, 0); + break; + } + SendMessage(GetParent(optDlg), PSM_CHANGED, 0, 0); + break; + case WM_NOTIFY: + switch (((NMHDR*)lParam)->code) { + case PSN_APPLY: + i = SendMessage(GetDlgItem(optDlg, IDC_OPT_MODE), CB_GETCURSEL, 0, 0); + selectedMode = SendMessage(GetDlgItem(optDlg, IDC_OPT_MODE), CB_GETITEMDATA, i, 0); + _setOptB("Mode", selectedMode); + _setOptB("ReplyOnSuccess", SendDlgItemMessage(optDlg, IDC_OPT_REPLY_ON_SUCCESS, BM_GETCHECK, 0, 0)); + _setOptB("ReplyOnAuth", SendDlgItemMessage(optDlg, IDC_OPT_REPLY_ON_AUTH, BM_GETCHECK, 0, 0)); + _setOptB("ReplyOnMsg", SendDlgItemMessage(optDlg, IDC_OPT_REPLY_ON_MSG, BM_GETCHECK, 0, 0)); + _setOptB("KeepBlockedMsg", SendDlgItemMessage(optDlg, IDC_OPT_KEEP_BLOCKED_MSG, BM_GETCHECK, 0, 0)); + _setOptB("MarkMsgUnreadOnApproval", SendDlgItemMessage(optDlg, IDC_OPT_MARK_MSG_UNREAD_ON_APPROVAL, BM_GETCHECK, 0, 0)); + _setOptB("ResponseCC", SendDlgItemMessage(optDlg, IDC_OPT_CCRESPONSE, BM_GETCHECK, 0, 0)); + switch (selectedMode) { + case SPAMOTRON_MODE_PLAIN: + _saveDlgItemText(optDlg, IDC_OPT_CHALLENGE, "Challenge"); + _saveDlgItemText(optDlg, IDC_OPT_AUTH_CHALLENGE, "AuthChallenge"); + break; + case SPAMOTRON_MODE_MATH: + _saveDlgItemText(optDlg, IDC_OPT_CHALLENGE, "ChallengeMath"); + _saveDlgItemText(optDlg, IDC_OPT_AUTH_CHALLENGE, "AuthChallengeMath"); + break; + } + if (_saveDlgItemResponse(optDlg, IDC_OPT_RESPONSE, "Response") == -1) { + MessageBox(NULL, TranslateT("Invalid regular expression.\nKeeping previous value."), L"Error", MB_OK); + return FALSE; + } + _saveDlgItemText(optDlg, IDC_OPT_SUCCESS_RESPONSE, "SuccessResponse"); + + return TRUE; + } + break; + case WM_DESTROY: + break; + } + return FALSE; +} + + +void EnableControlsBayes(HWND hwnd, BOOL enable) +{ + EnableWindow(GetDlgItem(hwnd, IDC_OPT_FILTERING_GROUP), enable); + EnableWindow(GetDlgItem(hwnd, IDC_OPT_LEARNING_GROUP), enable); + EnableWindow(GetDlgItem(hwnd, IDC_OPT_BAYES_BLOCK_MSG), enable); + EnableWindow(GetDlgItem(hwnd, IDC_OPT_BAYES_SPAM_SCORE), enable); + EnableWindow(GetDlgItem(hwnd, IDC_OPT_BAYES_AUTO_APPROVE), enable); + EnableWindow(GetDlgItem(hwnd, IDC_OPT_BAYES_HAM_SCORE), enable); + EnableWindow(GetDlgItem(hwnd, IDC_OPT_BAYES_AUTOLEARN_APPROVED), enable); + EnableWindow(GetDlgItem(hwnd, IDC_OPT_BAYES_AUTOLEARN_AUTOAPPROVED), enable && + SendDlgItemMessage(hwnd, IDC_OPT_BAYES_AUTO_APPROVE, BM_GETCHECK, 0, 0)); + EnableWindow(GetDlgItem(hwnd, IDC_OPT_BAYES_AUTOLEARN_NOT_APPROVED), enable); + EnableWindow(GetDlgItem(hwnd, IDC_OPT_BAYES_WAIT_APPROVE), enable); + EnableWindow(GetDlgItem(hwnd, IDC_OPT_BAYES_AUTOLEARN_NOT_APPROVED2), enable); + EnableWindow(GetDlgItem(hwnd, IDC_STATIC_DAYSASSPAM), enable); + EnableWindow(GetDlgItem(hwnd, IDC_OPT_BAYES_AUTOLEARN_OUTGOING), enable); + EnableWindow(GetDlgItem(hwnd, IDC_OPT_BAYES_LEARNBOX), enable); + EnableWindow(GetDlgItem(hwnd, IDC_OPT_BAYES_SPAM), enable); + EnableWindow(GetDlgItem(hwnd, IDC_OPT_BAYES_HAM), enable); + EnableWindow(GetDlgItem(hwnd, IDC_CHECK_MSG), enable); + EnableWindow(GetDlgItem(hwnd, IDC_STATIC_SPAM_COUNT), enable); + EnableWindow(GetDlgItem(hwnd, IDC_STATIC_HAM_COUNT), enable); + EnableWindow(GetDlgItem(hwnd, IDC_STATIC_SPAMCOUNT_LABEL), enable); + EnableWindow(GetDlgItem(hwnd, IDC_STATIC_HAMCOUNT_LABEL), enable); +} + +BOOL CALLBACK DlgProcOptionsBayes(HWND optDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + static int bInitializing = 0, len; + BOOL bEnabled; + TCHAR *dbuf; + TCHAR buf[MAX_BUFFER_LENGTH]; + char cbuf[MAX_BUFFER_LENGTH]; + switch (msg) { + case WM_INITDIALOG: + bInitializing = 1; + TranslateDialogDefault(optDlg); + bEnabled = _getOptB("BayesEnabled", defaultBayesEnabled); + SendDlgItemMessage(optDlg, IDC_OPT_BAYES_ENABLED, BM_SETCHECK, bEnabled, 0); + SendDlgItemMessage(optDlg, IDC_OPT_BAYES_BLOCK_MSG, BM_SETCHECK, _getOptB("BayesBlockMsg", defaultBayesBlockMsg), 0); + SendDlgItemMessage(optDlg, IDC_OPT_BAYES_AUTO_APPROVE, BM_SETCHECK, _getOptB("BayesAutoApprove", defaultBayesAutoApprove), 0); + SendDlgItemMessage(optDlg, IDC_OPT_BAYES_AUTOLEARN_APPROVED, BM_SETCHECK, _getOptB("BayesAutolearnApproved", defaultBayesAutolearnApproved), 0); + SendDlgItemMessage(optDlg, IDC_OPT_BAYES_AUTOLEARN_AUTOAPPROVED, BM_SETCHECK, _getOptB("BayesAutolearnAutoApproved", defaultBayesAutolearnAutoApproved), 0); + SendDlgItemMessage(optDlg, IDC_OPT_BAYES_AUTOLEARN_NOT_APPROVED, BM_SETCHECK, _getOptB("BayesAutolearnNotApproved", defaultBayesAutolearnNotApproved), 0); + SendDlgItemMessage(optDlg, IDC_OPT_BAYES_AUTOLEARN_OUTGOING, BM_SETCHECK, _getOptB("BayesAutolearnOutgoing", defaultBayesAutolearnOutgoing), 0); + + EnableControlsBayes(optDlg, bEnabled); + + mir_sntprintf(buf, MAX_BUFFER_LENGTH, _T("%0.02f"), (double)_getOptD("BayesSpamScore", defaultBayesSpamScore)*SCORE_C); + SetDlgItemText(optDlg, IDC_OPT_BAYES_SPAM_SCORE, buf); + mir_sntprintf(buf, MAX_BUFFER_LENGTH, _T("%.02f"), (double)_getOptD("BayesHamScore", defaultBayesHamScore)*SCORE_C); + SetDlgItemText(optDlg, IDC_OPT_BAYES_HAM_SCORE, buf); + mir_sntprintf(buf, MAX_BUFFER_LENGTH, _T("%d"), _getOptD("BayesWaitApprove", defaultBayesWaitApprove)); + SetDlgItemText(optDlg, IDC_OPT_BAYES_WAIT_APPROVE, buf); + + if (bEnabled) { + mir_sntprintf(buf, MAX_BUFFER_LENGTH, _T("%d"), get_msg_count(SPAM)); + SetDlgItemText(optDlg, IDC_STATIC_SPAM_COUNT, buf); + mir_sntprintf(buf, MAX_BUFFER_LENGTH, _T("%d"), get_msg_count(HAM)); + SetDlgItemText(optDlg, IDC_STATIC_HAM_COUNT, buf); + } + + bInitializing = 0; + break; + case WM_COMMAND: + if (bInitializing) + return FALSE; + switch (LOWORD(wParam)) { + case IDC_OPT_BAYES_ENABLED: + bEnabled = SendDlgItemMessage(optDlg, IDC_OPT_BAYES_ENABLED, BM_GETCHECK, 0, 0); + EnableControlsBayes(optDlg, bEnabled); + case IDC_OPT_BAYES_AUTO_APPROVE: + bEnabled = SendDlgItemMessage(optDlg, IDC_OPT_BAYES_ENABLED, BM_GETCHECK, 0, 0); + EnableWindow(GetDlgItem(optDlg, IDC_OPT_BAYES_AUTOLEARN_AUTOAPPROVED), + bEnabled && SendDlgItemMessage(optDlg, IDC_OPT_BAYES_AUTO_APPROVE, BM_GETCHECK, 0, 0)); + case IDC_OPT_BAYES_BLOCK_MSG: + case IDC_OPT_BAYES_AUTOLEARN_APPROVED: + case IDC_OPT_BAYES_AUTOLEARN_AUTOAPPROVED: + case IDC_OPT_BAYES_AUTOLEARN_NOT_APPROVED: + case IDC_OPT_BAYES_AUTOLEARN_OUTGOING: + if (HIWORD(wParam) != BN_CLICKED) + return FALSE; + break; + case IDC_OPT_BAYES_SPAM_SCORE: + case IDC_OPT_BAYES_HAM_SCORE: + case IDC_OPT_BAYES_WAIT_APPROVE: + if (HIWORD(wParam) != EN_CHANGE) + return FALSE; + break; + case IDC_OPT_BAYES_LEARNBOX: + return FALSE; + case IDC_OPT_BAYES_HAM: + // Learn ham from learnbox + len = GetWindowTextLength(GetDlgItem(optDlg, IDC_OPT_BAYES_LEARNBOX))+1; + dbuf = (TCHAR *)malloc(len*sizeof(TCHAR)); + if (!dbuf) + return FALSE; + GetDlgItemText(optDlg, IDC_OPT_BAYES_LEARNBOX, dbuf, len); + learn_ham(dbuf); + SetDlgItemText(optDlg, IDC_OPT_BAYES_LEARNBOX, _T("")); + free(dbuf); + + mir_sntprintf(buf, MAX_BUFFER_LENGTH, _T("%d"), get_msg_count(SPAM)); + SetDlgItemText(optDlg, IDC_STATIC_SPAM_COUNT, buf); + mir_sntprintf(buf, MAX_BUFFER_LENGTH, _T("%d"), get_msg_count(HAM)); + SetDlgItemText(optDlg, IDC_STATIC_HAM_COUNT, buf); + return FALSE; + + case IDC_OPT_BAYES_SPAM: + // Learn spam from learnbox + len = GetWindowTextLength(GetDlgItem(optDlg, IDC_OPT_BAYES_LEARNBOX))+1; + dbuf = (TCHAR *)malloc(len*sizeof(TCHAR)); + if (!dbuf) + return FALSE; + GetDlgItemText(optDlg, IDC_OPT_BAYES_LEARNBOX, dbuf, len); + learn_spam(dbuf); + SetDlgItemText(optDlg, IDC_OPT_BAYES_LEARNBOX, _T("")); + free(dbuf); + + mir_sntprintf(buf, MAX_BUFFER_LENGTH, _T("%d"), get_msg_count(SPAM)); + SetDlgItemText(optDlg, IDC_STATIC_SPAM_COUNT, buf); + mir_sntprintf(buf, MAX_BUFFER_LENGTH, _T("%d"), get_msg_count(HAM)); + SetDlgItemText(optDlg, IDC_STATIC_HAM_COUNT, buf); + return FALSE; + + case IDC_CHECK_MSG: + len = GetWindowTextLength(GetDlgItem(optDlg, IDC_OPT_BAYES_LEARNBOX))+1; + dbuf = (TCHAR *)malloc((len)*sizeof(TCHAR)); + if (!dbuf) + return FALSE; + GetDlgItemText(optDlg, IDC_OPT_BAYES_LEARNBOX, dbuf, len); + sprintf(cbuf, "%0.04f", get_msg_score(dbuf)); + SetDlgItemText(optDlg, IDC_OPT_BAYES_LEARNBOX, _T("")); + MessageBoxA(NULL, cbuf, Translate("Message score"), MB_OK); + free(dbuf); + return FALSE; + + } + SendMessage(GetParent(optDlg), PSM_CHANGED, 0, 0); + break; + case WM_NOTIFY: + switch (((NMHDR*)lParam)->code) { + case PSN_APPLY: + _setOptB("BayesEnabled", SendDlgItemMessage(optDlg, IDC_OPT_BAYES_ENABLED, BM_GETCHECK, 0, 0)); + _setOptB("BayesBlockMsg", SendDlgItemMessage(optDlg, IDC_OPT_BAYES_BLOCK_MSG, BM_GETCHECK, 0, 0)); + _setOptB("BayesAutoApprove", SendDlgItemMessage(optDlg, IDC_OPT_BAYES_AUTO_APPROVE, BM_GETCHECK, 0, 0)); + _setOptB("BayesAutolearnApproved", SendDlgItemMessage(optDlg, IDC_OPT_BAYES_AUTOLEARN_APPROVED, BM_GETCHECK, 0, 0)); + _setOptB("BayesAutolearnAutoApproved", SendDlgItemMessage(optDlg, IDC_OPT_BAYES_AUTOLEARN_AUTOAPPROVED, BM_GETCHECK, 0, 0)); + _setOptB("BayesAutolearnNotApproved", SendDlgItemMessage(optDlg, IDC_OPT_BAYES_AUTOLEARN_NOT_APPROVED, BM_GETCHECK, 0, 0)); + _setOptB("BayesAutolearnOutgoing", SendDlgItemMessage(optDlg, IDC_OPT_BAYES_AUTOLEARN_OUTGOING, BM_GETCHECK, 0, 0)); + _saveDlgItemScore(optDlg, IDC_OPT_BAYES_SPAM_SCORE, "BayesSpamScore"); + _saveDlgItemScore(optDlg, IDC_OPT_BAYES_HAM_SCORE, "BayesHamScore"); + _saveDlgItemInt(optDlg, IDC_OPT_BAYES_WAIT_APPROVE, "BayesWaitApprove"); + break; + } + break; + case WM_DESTROY: + break; + } + return FALSE; +} + +extern BOOL CALLBACK DlgProcOptionsPopups(HWND optDlg, UINT msg, WPARAM wParam, LPARAM lParam); + +int OnOptInitialize(WPARAM wParam, LPARAM lParam) +{ + OPTIONSDIALOGPAGE odp = {0}; + odp.cbSize = sizeof(odp); + odp.position = 0; + odp.hInstance = hInst; + odp.ptszGroup = _T("Message Sessions"); + odp.ptszTitle = _T(PLUGIN_NAME); + odp.flags = ODPF_TCHAR | ODPF_BOLDGROUPS; + + odp.ptszTab = _T("Settings"); + odp.pszTemplate = MAKEINTRESOURCEA(IDD_SPAMOTRON_MAIN); + odp.pfnDlgProc = (DLGPROC)DlgProcOptionsMain; + Options_AddPage(wParam, &odp); + + odp.ptszTab = _T("Messages"); + odp.pszTemplate = MAKEINTRESOURCEA(IDD_SPAMOTRON_Q); + odp.pfnDlgProc = (DLGPROC)DlgProcOptionsQuestion; + Options_AddPage(wParam, &odp); + + odp.ptszTab = _T("Bayes"); + odp.pszTemplate = MAKEINTRESOURCEA(IDD_SPAMOTRON_BAYES); + odp.pfnDlgProc = (DLGPROC)DlgProcOptionsBayes; + Options_AddPage(wParam, &odp); + + if (ServiceExists(MS_POPUP_ADDPOPUP)) { + odp.pszTemplate = MAKEINTRESOURCEA(IDD_SPAMOTRON_POPUPS); + odp.pfnDlgProc = (DLGPROC)DlgProcOptionsPopups; + odp.ptszGroup = _T("Popups"); + odp.ptszTab = NULL; + Options_AddPage(wParam, &odp); + } + + return 0; +} diff --git a/plugins/Spamotron/src/popups.cpp b/plugins/Spamotron/src/popups.cpp new file mode 100644 index 0000000000..60784891bb --- /dev/null +++ b/plugins/Spamotron/src/popups.cpp @@ -0,0 +1,241 @@ +#include "common.h" + +extern BOOL _saveDlgItemInt(HWND hDialog, int controlID, char* option); + +void EnablePopupControls(HWND hwnd, BOOL enable) +{ + EnableWindow(GetDlgItem(hwnd, IDC_OPT_POPUPS_NOTIFY_BLOCKED), enable); + EnableWindow(GetDlgItem(hwnd, IDC_OPT_POPUPS_NOTIFY_APPROVED), enable); + EnableWindow(GetDlgItem(hwnd, IDC_OPT_POPUPS_NOTIFY_CHALLENGE), enable); + EnableWindow(GetDlgItem(hwnd, IDC_OPT_POPUPS_BLOCKED_FOREGROUND), enable); + EnableWindow(GetDlgItem(hwnd, IDC_OPT_POPUPS_BLOCKED_BACKGROUND), enable); + EnableWindow(GetDlgItem(hwnd, IDC_OPT_POPUPS_APPROVED_FOREGROUND), enable); + EnableWindow(GetDlgItem(hwnd, IDC_OPT_POPUPS_APPROVED_BACKGROUND), enable); + EnableWindow(GetDlgItem(hwnd, IDC_OPT_POPUPS_CHALLENGE_FOREGROUND), enable); + EnableWindow(GetDlgItem(hwnd, IDC_OPT_POPUPS_CHALLENGE_BACKGROUND), enable); + EnableWindow(GetDlgItem(hwnd, IDC_OPT_POPUPS_BLOCKED_TIMEOUT), enable); + EnableWindow(GetDlgItem(hwnd, IDC_OPT_POPUPS_APPROVED_TIMEOUT), enable); + EnableWindow(GetDlgItem(hwnd, IDC_OPT_POPUPS_CHALLENGE_TIMEOUT), enable); + EnableWindow(GetDlgItem(hwnd, IDC_OPT_POPUPS_DEFAULT_COLORS), enable); + EnableWindow(GetDlgItem(hwnd, IDC_OPT_POPUPS_WINDOWS_COLORS), enable); + EnableWindow(GetDlgItem(hwnd, IDC_OPT_POPUPS_DEFAULT_TIMEOUT), enable); + EnableWindow(GetDlgItem(hwnd, IDC_OPT_POPUPS_PREVIEW), enable); +} + +void EnablePopupColors(HWND hwnd, BOOL enableDefault, BOOL enableWindows) +{ + BOOL enable, bEnabled; + bEnabled = SendDlgItemMessage(hwnd, IDC_OPT_POPUPS_ENABLED, BM_GETCHECK, 0, 0); + enable = enableDefault || enableWindows; + EnableWindow(GetDlgItem(hwnd, IDC_OPT_POPUPS_BLOCKED_FOREGROUND), !enable && bEnabled); + EnableWindow(GetDlgItem(hwnd, IDC_OPT_POPUPS_BLOCKED_BACKGROUND), !enable && bEnabled); + EnableWindow(GetDlgItem(hwnd, IDC_OPT_POPUPS_APPROVED_FOREGROUND), !enable && bEnabled); + EnableWindow(GetDlgItem(hwnd, IDC_OPT_POPUPS_APPROVED_BACKGROUND), !enable && bEnabled); + EnableWindow(GetDlgItem(hwnd, IDC_OPT_POPUPS_CHALLENGE_FOREGROUND), !enable && bEnabled); + EnableWindow(GetDlgItem(hwnd, IDC_OPT_POPUPS_CHALLENGE_BACKGROUND), !enable && bEnabled); + + EnableWindow(GetDlgItem(hwnd, IDC_OPT_POPUPS_WINDOWS_COLORS), !enableDefault && bEnabled); + EnableWindow(GetDlgItem(hwnd, IDC_OPT_POPUPS_DEFAULT_COLORS), !enableWindows && bEnabled); +} + +void EnablePopupTimeouts(HWND hwnd, BOOL enable) +{ + BOOL bEnabled = SendDlgItemMessage(hwnd, IDC_OPT_POPUPS_ENABLED, BM_GETCHECK, 0, 0); + EnableWindow(GetDlgItem(hwnd, IDC_OPT_POPUPS_BLOCKED_TIMEOUT), !enable && bEnabled); + EnableWindow(GetDlgItem(hwnd, IDC_OPT_POPUPS_APPROVED_TIMEOUT), !enable && bEnabled); + EnableWindow(GetDlgItem(hwnd, IDC_OPT_POPUPS_CHALLENGE_TIMEOUT), !enable && bEnabled); +} + +BOOL CALLBACK DlgProcOptionsPopups(HWND optDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + BOOL bEnabled; + static int bInitializing = 0; + switch (msg) { + case WM_INITDIALOG: + bInitializing = 1; + TranslateDialogDefault(optDlg); + bEnabled = _getOptB("NotifyPopup", defaultNotifyPopup); + SendDlgItemMessage(optDlg, IDC_OPT_POPUPS_ENABLED, BM_SETCHECK, bEnabled, 0); + EnablePopupControls(optDlg, bEnabled); + + SendDlgItemMessage(optDlg, IDC_OPT_POPUPS_NOTIFY_BLOCKED, BM_SETCHECK, _getOptB("NotifyPopupBlocked", defaultNotifyPopupBlocked), 0); + SendDlgItemMessage(optDlg, IDC_OPT_POPUPS_NOTIFY_APPROVED, BM_SETCHECK, _getOptB("NotifyPopupApproved", defaultNotifyPopupApproved), 0); + SendDlgItemMessage(optDlg, IDC_OPT_POPUPS_NOTIFY_CHALLENGE, BM_SETCHECK, _getOptB("NotifyPopupChallenge", defaultNotifyPopupChallenge), 0); + SendDlgItemMessage(optDlg, IDC_OPT_POPUPS_DEFAULT_COLORS, BM_SETCHECK, _getOptB("PopupDefaultColors", defaultPopupDefaultColors), 0); + SendDlgItemMessage(optDlg, IDC_OPT_POPUPS_WINDOWS_COLORS, BM_SETCHECK, _getOptB("PopupWindowsColors", defaultPopupWindowsColors), 0); + SendDlgItemMessage(optDlg, IDC_OPT_POPUPS_DEFAULT_TIMEOUT, BM_SETCHECK, _getOptB("PopupDefaultTimeout", defaultPopupDefaultTimeout), 0); + SetDlgItemInt(optDlg, IDC_OPT_POPUPS_BLOCKED_TIMEOUT, _getOptD("PopupBlockedTimeout", defaultPopupBlockedTimeout), FALSE); + SetDlgItemInt(optDlg, IDC_OPT_POPUPS_APPROVED_TIMEOUT, _getOptD("PopupApprovedTimeout", defaultPopupApprovedTimeout), FALSE); + SetDlgItemInt(optDlg, IDC_OPT_POPUPS_CHALLENGE_TIMEOUT, _getOptD("PopupChallengeTimeout", defaultPopupChallengeTimeout), FALSE); + SendDlgItemMessage(optDlg, IDC_OPT_POPUPS_BLOCKED_FOREGROUND, CPM_SETCOLOUR, 0, _getOptD("PopupBlockedForeground", defaultPopupBlockedForeground)); + SendDlgItemMessage(optDlg, IDC_OPT_POPUPS_BLOCKED_BACKGROUND, CPM_SETCOLOUR, 0, _getOptD("PopupBlockedBackground", defaultPopupBlockedBackground)); + SendDlgItemMessage(optDlg, IDC_OPT_POPUPS_APPROVED_FOREGROUND, CPM_SETCOLOUR, 0, _getOptD("PopupApprovedForeground", defaultPopupApprovedForeground)); + SendDlgItemMessage(optDlg, IDC_OPT_POPUPS_APPROVED_BACKGROUND, CPM_SETCOLOUR, 0, _getOptD("PopupApprovedBackground", defaultPopupApprovedBackground)); + SendDlgItemMessage(optDlg, IDC_OPT_POPUPS_CHALLENGE_FOREGROUND, CPM_SETCOLOUR, 0, _getOptD("PopupChallengeForeground", defaultPopupChallengeForeground)); + SendDlgItemMessage(optDlg, IDC_OPT_POPUPS_CHALLENGE_BACKGROUND, CPM_SETCOLOUR, 0, _getOptD("PopupChallengeBackground", defaultPopupChallengeBackground)); + EnablePopupTimeouts(optDlg, SendDlgItemMessage(optDlg, IDC_OPT_POPUPS_DEFAULT_TIMEOUT, BM_GETCHECK, 0, 0)); + EnablePopupColors(optDlg, + _getOptB("PopupDefaultColors", defaultPopupDefaultColors), + _getOptB("PopupWindowsColors", defaultPopupWindowsColors)); + + bInitializing = 0; + break; + case WM_COMMAND: + if (bInitializing) + return FALSE; + switch (LOWORD(wParam)) { + case IDC_OPT_POPUPS_ENABLED: + bEnabled = SendDlgItemMessage(optDlg, IDC_OPT_POPUPS_ENABLED, BM_GETCHECK, 0, 0); + EnablePopupControls(optDlg, bEnabled); + case IDC_OPT_POPUPS_NOTIFY_BLOCKED: + case IDC_OPT_POPUPS_NOTIFY_APPROVED: + case IDC_OPT_POPUPS_NOTIFY_CHALLENGE: + case IDC_OPT_POPUPS_DEFAULT_COLORS: + case IDC_OPT_POPUPS_WINDOWS_COLORS: + case IDC_OPT_POPUPS_DEFAULT_TIMEOUT: + EnablePopupColors(optDlg, + SendDlgItemMessage(optDlg, IDC_OPT_POPUPS_DEFAULT_COLORS, BM_GETCHECK, 0, 0), + SendDlgItemMessage(optDlg, IDC_OPT_POPUPS_WINDOWS_COLORS, BM_GETCHECK, 0, 0)); + EnablePopupTimeouts(optDlg, SendDlgItemMessage(optDlg, IDC_OPT_POPUPS_DEFAULT_TIMEOUT, BM_GETCHECK, 0, 0)); + + if (HIWORD(wParam) != BN_CLICKED) + return FALSE; + break; + case IDC_OPT_POPUPS_BLOCKED_TIMEOUT: + case IDC_OPT_POPUPS_APPROVED_TIMEOUT: + case IDC_OPT_POPUPS_CHALLENGE_TIMEOUT: + if (HIWORD(wParam) != EN_CHANGE) + return FALSE; + break; + case IDC_OPT_POPUPS_PREVIEW: + ShowPopupPreview(optDlg, POPUP_BLOCKED, NULL, _T("Message blocked due to preview action")); + ShowPopupPreview(optDlg, POPUP_APPROVED, NULL, _T("Message approved due to preview action")); + ShowPopupPreview(optDlg, POPUP_CHALLENGE, NULL, _T("Challenge sent to preview contact")); + return FALSE; + } + SendMessage(GetParent(optDlg), PSM_CHANGED, 0, 0); + break; + case WM_NOTIFY: + switch (((NMHDR*)lParam)->code) { + case PSN_APPLY: + _setOptB("NotifyPopup", SendDlgItemMessage(optDlg, IDC_OPT_POPUPS_ENABLED, BM_GETCHECK, 0, 0)); + _setOptB("NotifyPopupBlocked", SendDlgItemMessage(optDlg, IDC_OPT_POPUPS_NOTIFY_BLOCKED, BM_GETCHECK, 0, 0)); + _setOptB("NotifyPopupApproved", SendDlgItemMessage(optDlg, IDC_OPT_POPUPS_NOTIFY_APPROVED, BM_GETCHECK, 0, 0)); + _setOptB("NotifyPopupChallenge", SendDlgItemMessage(optDlg, IDC_OPT_POPUPS_NOTIFY_CHALLENGE, BM_GETCHECK, 0, 0)); + _setOptB("PopupDefaultColors", SendDlgItemMessage(optDlg, IDC_OPT_POPUPS_DEFAULT_COLORS, BM_GETCHECK, 0, 0)); + _setOptB("PopupWindowsColors", SendDlgItemMessage(optDlg, IDC_OPT_POPUPS_WINDOWS_COLORS, BM_GETCHECK, 0, 0)); + _setOptB("PopupDefaultTimeout", SendDlgItemMessage(optDlg, IDC_OPT_POPUPS_DEFAULT_TIMEOUT, BM_GETCHECK, 0, 0)); + _saveDlgItemInt(optDlg, IDC_OPT_POPUPS_BLOCKED_TIMEOUT, "PopupBlockedTimeout"); + _saveDlgItemInt(optDlg, IDC_OPT_POPUPS_APPROVED_TIMEOUT, "PopupApprovedTimeout"); + _saveDlgItemInt(optDlg, IDC_OPT_POPUPS_CHALLENGE_TIMEOUT, "PopupChallengeTimeout"); + _setOptD("PopupBlockedForeground", SendDlgItemMessage(optDlg,IDC_OPT_POPUPS_BLOCKED_FOREGROUND,CPM_GETCOLOUR,0,0)); + _setOptD("PopupBlockedBackground", SendDlgItemMessage(optDlg,IDC_OPT_POPUPS_BLOCKED_BACKGROUND,CPM_GETCOLOUR,0,0)); + _setOptD("PopupApprovedForeground", SendDlgItemMessage(optDlg,IDC_OPT_POPUPS_APPROVED_FOREGROUND,CPM_GETCOLOUR,0,0)); + _setOptD("PopupApprovedBackground", SendDlgItemMessage(optDlg,IDC_OPT_POPUPS_APPROVED_BACKGROUND,CPM_GETCOLOUR,0,0)); + _setOptD("PopupChallengeForeground", SendDlgItemMessage(optDlg,IDC_OPT_POPUPS_CHALLENGE_FOREGROUND,CPM_GETCOLOUR,0,0)); + _setOptD("PopupChallengeBackground", SendDlgItemMessage(optDlg,IDC_OPT_POPUPS_CHALLENGE_BACKGROUND,CPM_GETCOLOUR,0,0)); + break; + } + break; + case WM_DESTROY: + break; + } + return FALSE; +} + +int ShowPopupPreview(HWND optDlg, BYTE popupType, TCHAR *line1, TCHAR *line2) +{ + POPUPDATAW ppdp = {0}; + switch (popupType) + { + case POPUP_DEFAULT: + ppdp.colorText = SendDlgItemMessage(optDlg,IDC_OPT_POPUPS_APPROVED_FOREGROUND,CPM_GETCOLOUR,0,0); + ppdp.colorBack = SendDlgItemMessage(optDlg,IDC_OPT_POPUPS_APPROVED_BACKGROUND,CPM_GETCOLOUR,0,0); + break; + case POPUP_BLOCKED: + ppdp.colorText = SendDlgItemMessage(optDlg,IDC_OPT_POPUPS_BLOCKED_FOREGROUND,CPM_GETCOLOUR,0,0); + ppdp.colorBack = SendDlgItemMessage(optDlg,IDC_OPT_POPUPS_BLOCKED_BACKGROUND,CPM_GETCOLOUR,0,0); + ppdp.iSeconds = GetDlgItemInt(optDlg, IDC_OPT_POPUPS_BLOCKED_TIMEOUT, NULL, TRUE); + ppdp.lchIcon = LoadSkinnedIcon(SKINICON_OTHER_DELETE); + break; + case POPUP_APPROVED: + ppdp.colorText = SendDlgItemMessage(optDlg,IDC_OPT_POPUPS_APPROVED_FOREGROUND,CPM_GETCOLOUR,0,0); + ppdp.colorBack = SendDlgItemMessage(optDlg,IDC_OPT_POPUPS_APPROVED_BACKGROUND,CPM_GETCOLOUR,0,0); + ppdp.iSeconds = GetDlgItemInt(optDlg, IDC_OPT_POPUPS_APPROVED_TIMEOUT, NULL, TRUE); + ppdp.lchIcon = LoadSkinnedIcon(SKINICON_OTHER_ADDCONTACT); + break; + case POPUP_CHALLENGE: + ppdp.colorText = SendDlgItemMessage(optDlg,IDC_OPT_POPUPS_CHALLENGE_FOREGROUND,CPM_GETCOLOUR,0,0); + ppdp.colorBack = SendDlgItemMessage(optDlg,IDC_OPT_POPUPS_CHALLENGE_BACKGROUND,CPM_GETCOLOUR,0,0); + ppdp.iSeconds = GetDlgItemInt(optDlg, IDC_OPT_POPUPS_CHALLENGE_TIMEOUT, NULL, TRUE); + ppdp.lchIcon = LoadSkinnedIcon(SKINICON_EVENT_MESSAGE); + break; + } + if (SendDlgItemMessage(optDlg, IDC_OPT_POPUPS_WINDOWS_COLORS, BM_GETCHECK, 0, 0)) { + ppdp.colorText = GetSysColor(COLOR_WINDOWTEXT); + ppdp.colorBack = GetSysColor(COLOR_WINDOW); + } + if (SendDlgItemMessage(optDlg, IDC_OPT_POPUPS_DEFAULT_COLORS, BM_GETCHECK, 0, 0)) { + ppdp.colorText = (COLORREF)NULL; + ppdp.colorBack = (COLORREF)NULL; + } + if (ppdp.iSeconds < 1) + ppdp.iSeconds = -1; + if (SendDlgItemMessage(optDlg, IDC_OPT_POPUPS_DEFAULT_TIMEOUT, BM_GETCHECK, 0, 0) || popupType == POPUP_DEFAULT) + ppdp.iSeconds = 0; + + ppdp.lchContact = NULL; + mir_sntprintf((WCHAR *)ppdp.lptzContactName, MAX_CONTACTNAME, _T("%s"), (line1)?line1:_T(PLUGIN_NAME)); + if (line2) + mir_sntprintf((WCHAR *)ppdp.lptzText, MAX_SECONDLINE, _T("%s"), line2); + return PUAddPopUpW(&ppdp); + +} + +int ShowPopup(HANDLE hContact, BYTE popupType, TCHAR *line1, TCHAR *line2) +{ + POPUPDATAW ppdp = {0}; + switch (popupType) + { + case POPUP_DEFAULT: + ppdp.colorText = _getOptD("PopupApprovedForeground", defaultPopupApprovedForeground); + ppdp.colorBack = _getOptD("PopupApprovedBackground", defaultPopupApprovedBackground); + break; + case POPUP_BLOCKED: + ppdp.colorText = _getOptD("PopupBlockedForeground", defaultPopupBlockedForeground); + ppdp.colorBack = _getOptD("PopupBlockedBackground", defaultPopupBlockedBackground); + ppdp.iSeconds = _getOptD("PopupBlockedTimeout", defaultPopupBlockedTimeout); + ppdp.lchIcon = LoadSkinnedIcon(SKINICON_OTHER_DELETE); + break; + case POPUP_APPROVED: + ppdp.colorText = _getOptD("PopupApprovedForeground", defaultPopupApprovedForeground); + ppdp.colorBack = _getOptD("PopupApprovedBackground", defaultPopupApprovedBackground); + ppdp.iSeconds = _getOptD("PopupApprovedTimeout", defaultPopupApprovedTimeout); + ppdp.lchIcon = LoadSkinnedIcon(SKINICON_OTHER_ADDCONTACT); + break; + case POPUP_CHALLENGE: + ppdp.colorText = _getOptD("PopupChallengeForeground", defaultPopupChallengeForeground); + ppdp.colorBack = _getOptD("PopupChallengeBackground", defaultPopupChallengeBackground); + ppdp.iSeconds = _getOptD("PopupChallengeTimeout", defaultPopupChallengeTimeout); + ppdp.lchIcon = LoadSkinnedIcon(SKINICON_EVENT_MESSAGE); + break; + } + if (_getOptB("PopupWindowsColors", defaultPopupWindowsColors)) { + ppdp.colorText = GetSysColor(COLOR_WINDOWTEXT); + ppdp.colorBack = GetSysColor(COLOR_WINDOW); + } + if (_getOptB("PopupDefaultColors", defaultPopupDefaultColors)) { + ppdp.colorText = (COLORREF)NULL; + ppdp.colorBack = (COLORREF)NULL; + } + if (ppdp.iSeconds < 1) + ppdp.iSeconds = -1; + if (_getOptB("PopupDefaultTimeout", defaultPopupDefaultTimeout) || popupType == POPUP_DEFAULT) + ppdp.iSeconds = 0; + + ppdp.lchContact = hContact; + mir_sntprintf((WCHAR *)ppdp.lptzContactName, MAX_CONTACTNAME, _T("%s"), (line1)?line1:_T(PLUGIN_NAME)); + if (line2) + mir_sntprintf((WCHAR *)ppdp.lptzText, MAX_SECONDLINE, _T("%s"), line2); + return PUAddPopUpW(&ppdp); +} \ No newline at end of file diff --git a/plugins/Spamotron/src/resource.h b/plugins/Spamotron/src/resource.h new file mode 100644 index 0000000000..31f5166fe9 --- /dev/null +++ b/plugins/Spamotron/src/resource.h @@ -0,0 +1,90 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by spamotron.rc +// +#define IDD_SPAMOTRON_MAIN 101 +#define IDD_SPAMOTRON_Q 102 +#define IDD_SPAMOTRON_BAYES 103 +#define IDD_SPAMOTRON_POPUPS 104 +#define IDD_SPAMOTRON_MAIN1 105 +#define IDC_OPT_OUT_MSG_APPROVE 1002 +#define IDC_OPT_MODE 1003 +#define IDC_OPT_CHALLENGE 1004 +#define IDC_OPT_RESPONSE 1005 +#define IDC_OPT_SUCCESS_RESPONSE 1006 +#define IDC_OPT_AUTH_CHALLENGE 1007 +#define IDC_OPT_MATH_RESPONSE 1008 +#define IDC_STATIC_RESPONSE 1009 +#define IDC_STATIC_MODEMSG 1010 +#define IDC_DEFAULTS 1011 +#define IDC_OPT_PROTOCOLS 1012 +#define IDC_OPT_REPLY_ON_SUCCESS 1013 +#define IDC_OPT_DONT_REPLY_SAME_MSG 1014 +#define IDC_OPT_REPLY_ON_AUTH 1015 +#define IDC_OPT_BAYES_AUTOLEARN_APPROVED 1016 +#define IDC_OPT_POPUPS_NOTIFY_APPROVED 1017 +#define IDC_OPT_LOG_ACTIONS 1019 +#define IDC_OPT_MAX_MSG_CONTACT 1023 +#define IDC_OPT_DONT_REPLY_MSG_WORDLIST 1024 +#define IDC_OPT_MAX_SAME_MSG 1025 +#define IDC_OPT_IN_MSG_APPROVE 1027 +#define IDC_OPT_IN_MSG_APPROVE_WORDLIST 1028 +#define IDC_OPT_DONT_REPLY_MSG 1029 +#define IDC_OPT_HIDE_UNTIL_VERIFIED 1033 +#define IDC_OPT_ADD_PERMANENTLY 1034 +#define IDC_OPT_BAYES_SPAM 1036 +#define IDC_OPT_POPUPS_PREVIEW 1036 +#define IDC_OPT_KEEP_BLOCKED_MSG 1037 +#define IDC_OPT_BAYES_BLOCK_MSG 1039 +#define IDC_OPT_BAYES_AUTOLEARN_OUTGOING 1041 +#define IDC_OPT_BAYES_LEARNBOX 1042 +#define IDC_OPT_BAYES_HAM 1043 +#define IDC_OPT_BAYES_SPAM_SCORE 1044 +#define IDC_OPT_BAYES_AUTO_APPROVE 1045 +#define IDC_EDIT3 1046 +#define IDC_OPT_BAYES_HAM_SCORE 1046 +#define IDC_STATIC_HAM_COUNT 1047 +#define IDC_STATIC_SPAM_COUNT 1048 +#define IDC_CHECK_MSG 1049 +#define IDC_OPT_BAYES_AUTOLEARN_NOT_APPROVED 1050 +#define IDC_OPT_BAYES_WAIT_APPROVE 1051 +#define IDC_OPT_BAYES_ENABLED 1052 +#define IDC_OPT_FILTERING_GROUP 1053 +#define IDC_OPT_LEARNING_GROUP 1054 +#define IDC_OPT_BAYES_AUTOLEARN_NOT_APPROVED2 1055 +#define IDC_OPT_CCRESPONSE 1056 +#define IDC_OPT_BAYES_AUTOLEARN_AUTOAPPROVED 1058 +#define IDC_STATIC_DAYSASSPAM 1059 +#define IDC_STATIC_SPAMCOUNT_LABEL 1060 +#define IDC_STATIC_HAMCOUNT_LABEL 1061 +#define IDC_OPT_REPLY_ON_MSG 1062 +#define IDC_OPT_POPUPS_ENABLE 1063 +#define IDC_OPT_POPUPS_ENABLED 1063 +#define IDC_OPT_POPUPS_NOTIFY_BLOCKED 1064 +#define IDC_OPT_POPUPS_BLOCKED_FOREGROUND 1065 +#define IDC_OPT_POPUPS_BLOCKED_BACKGROUND 1066 +#define IDC_OPT_POPUPS_APPROVED_FOREGROUND 1067 +#define IDC_OPT_POPUPS_APPROVED_BACKGROUND 1068 +#define IDC_OPT_POPUPS_CHALLENGE_FOREGROUND 1069 +#define IDC_OPT_POPUPS_CHALLENGE_BACKGROUND 1070 +#define IDC_OPT_POPUPS_BLOCKED_TIMEOUT 1073 +#define IDC_OPT_POPUPS_APPROVED_TIMEOUT 1074 +#define IDC_OPT_POPUPS_CHALLENGE_TIMEOUT 1075 +#define IDC_OPT_POPUPS_DEFAULT_COLORS 1076 +#define IDC_OPT_POPUPS_DEFAULT_TIMEOUT 1077 +#define IDC_OPT_POPUPS_WINDOWS_COLORS 1078 +#define IDC_OPT_POPUPS_NOTIFY_CHALLENGE 1079 +#define IDC_OPT_MARK_MSG_AS_NEW_ON_APPROVAL 1080 +#define IDC_OPT_MARK_MSG_UNREAD_ON_APPROVAL 1080 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NO_MFC 1 +#define _APS_NEXT_RESOURCE_VALUE 105 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1081 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/plugins/Spamotron/src/spamotron.cpp b/plugins/Spamotron/src/spamotron.cpp new file mode 100644 index 0000000000..1d9e721712 --- /dev/null +++ b/plugins/Spamotron/src/spamotron.cpp @@ -0,0 +1,588 @@ +#include "common.h" + +HINSTANCE hInst; +HANDLE hOptInitialize, hModulesLoaded, hDBContactAdded, hDBEventAdded, hDBEventFilterAdd; +time_t last_queue_check = 0; +int hLangpack; + +#define MIID_SPAMOTRON {0x14331048,0x5a73,0x4fdb,{0xb9,0x09,0x2d,0x7e,0x18,0x25,0xa0,0x12}} /* 14331048-5a73-4fdb-b909-2d7e1825a012 */ + +PLUGININFOEX pluginInfo = { + sizeof(PLUGININFOEX), + PLUGIN_NAME, + PLUGIN_MAKE_VERSION(0,0,4,6), + "Anti-spam plugin with captcha and Bayes filtering", + "vu1tur", + "to@vu1tur.eu.org", + "© 2010 vu1tur", + "http://vu1tur.eu.org/spamotron", + UNICODE_AWARE, + MIID_SPAMOTRON +}; + + + +extern int OnOptInitialize(WPARAM wParam, LPARAM lParam); + +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + hInst = hinstDLL; + return TRUE; +} + +int OnModulesLoaded(WPARAM wParam, LPARAM lParam) +{ + hOptInitialize = HookEvent(ME_OPT_INITIALISE, OnOptInitialize); + + return 0; +} + +int OnDBContactAdded(WPARAM wParam, LPARAM lParam) +{ + //MessageBox(NULL, _T("OnDBContactAdded"), _T("Event"), MB_OK); + return 0; +} + +int OnDBEventAdded(WPARAM wParam, LPARAM lParam) +{ + + return 0; +} + +int OnDBEventFilterAdd(WPARAM wParam, LPARAM lParam) +{ + HANDLE hContact = (HANDLE)wParam; + DBEVENTINFO *dbei = (DBEVENTINFO *)lParam; + char *msgblob; + POPUPDATA ppdp = {0}; + DBTIMETOSTRING tts = {0}; + char protoOption[256] = {0}; + char *response, *tmp, *challenge; + int buflen = MAX_BUFFER_LENGTH; + TCHAR buf[MAX_BUFFER_LENGTH]; + TCHAR *message = NULL, *challengeW = NULL, *tmpW = NULL; + TCHAR *whitelist = NULL, *ptok; + TCHAR mexpr[64]; + int maxmsglen = 0, a, b, i; + BOOL bayesEnabled = _getOptB("BayesEnabled", defaultBayesEnabled); + BOOL bCorrectResponse = FALSE; + + // get hContact from DBEVENTINFO as icq_proto.c doesn't pass hContact the usual way for some reason. + if (dbei->eventType == EVENTTYPE_AUTHREQUEST) + hContact = *((PHANDLE)(dbei->pBlob+sizeof(DWORD))); + + // get maximum length of the message a protocol supports + maxmsglen = CallProtoService(dbei->szModule, PS_GETCAPS, PFLAG_MAXLENOFMESSAGE, (LPARAM)hContact); + + + /*** Dequeue and learn messages ***/ + + if (bayesEnabled && _getOptB("BayesAutolearnNotApproved", defaultBayesAutolearnNotApproved)) + if (time(NULL) - last_queue_check > 4*3600) { // dequeue every 4 hours + dequeue_messages(); + last_queue_check = time(NULL); + } + + + /*** Check for conditional and unconditional approval ***/ + + // Pass-through if protocol is not enabled + strcat(protoOption, "proto_"); + strcat(protoOption, dbei->szModule); + if (_getOptB(protoOption, 0) == 0) // Protocol is not handled by Spam-o-tron + return 0; + + // Pass-through if the event is not of type EVENTTYPE_MESSAGE or EVENTTYPE_AUTHREQUEST + if (dbei->eventType != EVENTTYPE_MESSAGE && dbei->eventType != EVENTTYPE_AUTHREQUEST) + return 0; + + // Pass-through if contact is already verified. + if (_getCOptB(hContact, "Verified", 0) == 1) + return 0; + + // Pass-through if the event is already read. + if (dbei->flags & DBEF_READ) + return 0; + + // Pass-through if event is from a contact that is already in the list. + if (db_get_b(hContact, "CList", "NotOnList", 1) == 0) // Already in the list + return 0; + + // Pass-through if event is from a contact that is already in the server-side contact list + if (db_get_w(hContact, dbei->szModule, "ServerId", 0)) + return 0; + + // Pass-through if contact is a MetaContact + if (db_get_dw(hContact, "MetaContacts", "NumContacts", 0)) + return 0; + + // Pass-through and approve if outgoing event. + if (dbei->flags & DBEF_SENT) { + if (_getOptB("ApproveOnMsgOut", 0)) { + _setCOptB(hContact, "Verified", 1); + if (_getOptB("AddPermanently", defaultAddPermanently)) + db_unset(hContact, "CList", "NotOnList"); + db_unset(hContact, "CList", "Delete"); + } + return 0; + } + + // Hide the contact until verified if option set. + if (_getOptB("HideUnverified", defaultHideUnverified)) + db_set_b(hContact, "CList", "Hidden", 1); + + // Fetch the incoming message body + if (dbei->eventType == EVENTTYPE_MESSAGE) { + msgblob = (char *)dbei->pBlob; + } else if (dbei->eventType == EVENTTYPE_AUTHREQUEST) { + msgblob = (char *)(dbei->pBlob + sizeof(DWORD) + sizeof(HANDLE)); + for(a=4;a>0;a--) + msgblob += strlen(msgblob)+1; + } + + if (dbei->flags & DBEF_UTF) + message = mir_utf8decodeW(msgblob); + else + message = mir_a2u(msgblob); + + + /*** Check for words in white-list ***/ + + if (_getOptB("ApproveOnMsgIn", defaultApproveOnMsgIn)) { + whitelist = (TCHAR*)malloc(2048 * sizeof(TCHAR)); + if (whitelist != NULL) { + _getOptS(whitelist, 2048, "ApproveOnMsgInWordlist", defaultApproveOnMsgInWordlist); + if (_isregex(whitelist)) { + if (_regmatch(message, whitelist)) + bCorrectResponse = TRUE; + } else { + ptok = _tcstok(whitelist, L" "); + while (ptok != NULL) { + if (_tcsstr(message, ptok)) { + bCorrectResponse = TRUE; + break; + } + ptok = _tcstok(NULL, L" "); + } + } + free(whitelist); + + if (bCorrectResponse) { + _setCOptB(hContact, "Verified", 1); + if (_getOptB("HideUnverified", defaultHideUnverified)) + db_unset(hContact, "CList", "Hidden"); + if (_getOptB("AddPermanently", defaultAddPermanently)) + db_unset(hContact, "CList", "NotOnList"); + db_unset(hContact, "CList", "Delete"); + if (_getOptB("ReplyOnSuccess", defaultReplyOnSuccess) && (_getCOptB(hContact, "MsgSent", 0))) { + tmp = mir_u2a(_getOptS(buf, buflen, "SuccessResponse", defaultSuccessResponse)); + response = mir_utf8encode(tmp); + mir_free(tmp); + CallContactService(hContact, PSS_MESSAGE, PREF_UTF, (LPARAM)response); + mir_free(response); + } + return 0; + } + } + } + + + /*** Check for correct answer ***/ + + switch (_getOptB("Mode", defaultMode)) + { + case SPAMOTRON_MODE_ROTATE: + case SPAMOTRON_MODE_RANDOM: + get_response(buf, buflen, _getCOptD(hContact, "ResponseNum", 0)); + if (_isregex(buf)) { + if (_regmatch(message, buf)) + bCorrectResponse = TRUE; + } else { + if (_tcsstr_cc(message, buf, _getOptB("ResponseCC", defaultResponseCC)) && + (_tcslen(message) == _tcslen(buf))) + bCorrectResponse = TRUE; + } + break; + + case SPAMOTRON_MODE_PLAIN: + _getOptS(buf, buflen, "Response", defaultResponse); + i = get_response_num(buf); + while (i-- > 0) { + get_response(buf, buflen, i-1); + if (_isregex(buf)) { + if (_regmatch(message, buf)) { + bCorrectResponse = TRUE; + break; + } + } else { + if (_tcsstr_cc(message, buf, _getOptB("ResponseCC", defaultResponseCC)) && + (_tcslen(message) == _tcslen(buf))) { + bCorrectResponse = TRUE; + break; + } + } + } + break; + + case SPAMOTRON_MODE_MATH: + if (message == NULL) + break; + _itot(_getCOptD(hContact, "ResponseMath", -1), buf, 10); + if (_tcsstr(message, buf) && (_tcslen(buf) == _tcslen(message))) { + bCorrectResponse = TRUE; + } + break; + } + + if (bCorrectResponse) + { + _setCOptB(hContact, "Verified", 1); + if (_getOptB("HideUnverified", defaultHideUnverified)) + db_unset(hContact, "CList", "Hidden"); + if (_getOptB("AddPermanently", defaultAddPermanently)) + db_unset(hContact, "CList", "NotOnList"); + db_unset(hContact, "CList", "Delete"); + db_unset(hContact, "CList", "ResponseNum"); + if (_getOptB("ReplyOnSuccess", defaultReplyOnSuccess)) { + tmp = mir_u2a(_getOptS(buf, buflen, "SuccessResponse", defaultSuccessResponse)); + response = mir_utf8encode(tmp); + mir_free(tmp); + CallContactService(hContact, PSS_MESSAGE, PREF_UTF, (LPARAM)response); + mir_free(response); + } + _notify(hContact, POPUP_APPROVED, TranslateT("Contact %s approved."), NULL); + + // Resubmit pending authorization request + if (_getCOptB(hContact, "AuthEventPending", FALSE)) { + DBVARIANT _dbv; + TCHAR AuthEventModule[100]; + char* szAuthEventModule; + if (db_get(hContact, PLUGIN_NAME, "AuthEvent", &_dbv) == 0) { + DBEVENTINFO *_dbei = NULL; + _dbei = (DBEVENTINFO *)malloc(sizeof(DBEVENTINFO)); + if (_dbei != NULL) { + memcpy(&_dbei->cbBlob, _dbv.pbVal, sizeof(DWORD)); + _dbei->eventType = EVENTTYPE_AUTHREQUEST; + _getCOptS(AuthEventModule, 100, hContact, "AuthEventModule", _T("ICQ")); + szAuthEventModule = mir_u2a(AuthEventModule); + _dbei->szModule = szAuthEventModule; + _dbei->timestamp = dbei->timestamp; + _dbei->flags = 0; + _dbei->cbSize = sizeof(DBEVENTINFO); + _dbei->pBlob = _dbv.pbVal + sizeof(DWORD); + CallService(MS_DB_EVENT_ADD, (WPARAM)hContact, (LPARAM)_dbei); + db_unset(hContact, PLUGIN_NAME, "AuthEvent"); + db_unset(hContact, PLUGIN_NAME, "AuthEventPending"); + db_unset(hContact, PLUGIN_NAME, "AuthEventModule"); + mir_free(szAuthEventModule); + free(_dbei); + } + db_free(&_dbv); + } + } + + // User approved, learn from previous messages + if (bayesEnabled && _getOptB("BayesAutolearnApproved", defaultBayesAutolearnApproved)) + bayes_approve_contact(hContact); + + // Mark previous messages unread if option set + if (_getOptB("KeepBlockedMsg", defaultKeepBlockedMsg) && + _getOptB("MarkMsgUnreadOnApproval", defaultMarkMsgUnreadOnApproval) && + hContact != NULL) { + // We will mark unread all blocked messages for the most recent day + MarkUnread(hContact); + } + + return 1; + } + + + + /*** Check for rejection ***/ + + // Completely reject if challenge was already sent today for MaxMsgContactCountPerDay times + // and the option is turned on. + if (isOneDay(dbei->timestamp, _getCOptD(hContact, "MsgSentTime", 0)) && + _getOptD("MaxMsgContactCountPerDay", defaultMaxMsgContactCountPerDay) > 0 && + _getCOptD(hContact, "MsgSent", 0) >= _getOptD("MaxMsgContactCountPerDay", defaultMaxMsgContactCountPerDay)) { + _notify(hContact, POPUP_BLOCKED, TranslateT("Message from %s rejected because it reached a maximum for challenge requests per day."), message); + if (bayesEnabled) + queue_message(hContact, dbei->timestamp, message); + return 1; + } + + // Completely reject if duplicate incoming message found + if (_getOptD("MaxSameMsgCountPerDay", defaultMaxSameMsgCountPerDay) > 0 && + _getCOptD(hContact, "SameMsgCount", 0) >= _getOptD("MaxSameMsgCountPerDay", defaultMaxSameMsgCountPerDay) && + _tcscmp(message, _getCOptS(buf, buflen, hContact, "LastInMsg", _T(""))) == 0) { + _notify(hContact, POPUP_BLOCKED, TranslateT("Message from %s rejected because it reached a maximum for same responses per day."), message); + if (bayesEnabled) + queue_message(hContact, dbei->timestamp, message); + return 1; + } + + // Completely reject if incoming message contains any word from DontReplyMsgWordlist option + if (_getOptB("DontReplyMsg", defaultDontReplyMsg) && + Contains(message, _getOptS(buf, buflen, "DontReplyMsgWordlist", defaultDontReplyMsgWordlist))) { + _notify(hContact, POPUP_BLOCKED, TranslateT("Message from %s dropped because it has a word from black list."), message); + return 1; + } + + + /*** Bayes checks ***/ + + // Drop if score > spam score + if (bayesEnabled && _getOptB("BayesBlockMsg", defaultBayesBlockMsg)) + if (get_msg_score(message) >= (double)_getOptD("BayesSpamScore", defaultBayesSpamScore) * SCORE_C) { + _notify(hContact, POPUP_BLOCKED, TranslateT("Message from %s dropped because of high spam score."), message); + if (bayesEnabled && _getOptB("BayesAutolearnNotApproved", defaultBayesAutolearnNotApproved)) + queue_message(hContact, dbei->timestamp, message); + return 1; + } + + // Accept if score < ham score + if (bayesEnabled && _getOptB("BayesAutoApprove", defaultBayesAutoApprove)) + if (get_msg_score(message) <= (double)_getOptD("BayesHamScore", defaultBayesHamScore) * SCORE_C) { + _notify(hContact, POPUP_APPROVED, TranslateT("Contact %s approved."), message); + _setCOptB(hContact, "Verified", 1); + if (_getOptB("HideUnverified", defaultHideUnverified)) + db_unset(hContact, "CList", "Hidden"); + if (_getOptB("AddPermanently", defaultAddPermanently)) + db_unset(hContact, "CList", "NotOnList"); + db_unset(hContact, "CList", "Delete"); + if (bayesEnabled && + _getOptB("BayesAutolearnApproved", defaultBayesAutolearnApproved) && + _getOptB("BayesAutolearnAutoApproved", defaultBayesAutolearnAutoApproved)) { + queue_message(hContact, dbei->timestamp, message); + bayes_approve_contact(hContact); + } + return 0; + } + + // Accept if event is EVENTTYPE_AUTHREQUEST and ReplyOnAuth is NOT set + if (dbei->eventType == EVENTTYPE_AUTHREQUEST && !_getOptB("ReplyOnAuth", defaultReplyOnAuth)) + return 0; + // Accept if event is EVENTTYPE_MESSAGE and ReplyOnMsg is NOT set + if (dbei->eventType == EVENTTYPE_MESSAGE && !_getOptB("ReplyOnMsg", defaultReplyOnMsg)) + return 0; + + /*** Send Challenge ***/ + + challengeW = (TCHAR *)malloc(maxmsglen*sizeof(TCHAR)); + tmpW = (TCHAR *)malloc(maxmsglen*sizeof(TCHAR)); + switch (_getOptB("Mode", defaultMode)) + { + case SPAMOTRON_MODE_PLAIN: + if (dbei->eventType == EVENTTYPE_AUTHREQUEST) + _getOptS(challengeW, maxmsglen, "AuthChallenge", defaultAuthChallenge); + else + _getOptS(challengeW, maxmsglen, "Challenge", defaultChallenge); + ReplaceVars(challengeW, maxmsglen); + tmp = mir_u2a(challengeW); + challenge = mir_utf8encode(tmp); + mir_free(tmp); + CallContactService(hContact, PSS_MESSAGE, PREF_UTF, (LPARAM)challenge); + mir_free(challenge); + _notify(hContact, POPUP_CHALLENGE, TranslateT("Sending plain challenge to %s."), message); + break; + + case SPAMOTRON_MODE_ROTATE: + if (dbei->eventType == EVENTTYPE_AUTHREQUEST) + _getOptS(challengeW, maxmsglen, "AuthChallenge", defaultAuthChallenge); + else + _getOptS(challengeW, maxmsglen, "Challenge", defaultChallenge); + _getOptS(buf, buflen, "Response", defaultResponse); + if (_getCOptD(hContact, "ResponseNum", 0) >= (unsigned int)(get_response_num(buf)-1)) { + _setCOptD(hContact, "ResponseNum", -1); + } + _setCOptD(hContact, "ResponseNum", _getCOptD(hContact, "ResponseNum", -1) + 1); + ReplaceVarsNum(challengeW, maxmsglen, _getCOptD(hContact, "ResponseNum", 0)); + + tmp = mir_u2a(challengeW); + challenge = mir_utf8encode(tmp); + mir_free(tmp); + CallContactService(hContact, PSS_MESSAGE, PREF_UTF, (LPARAM)challenge); + mir_free(challenge); + _notify(hContact, POPUP_CHALLENGE, TranslateT("Sending round-robin challenge to %s."), message); + break; + + case SPAMOTRON_MODE_RANDOM: + if (dbei->eventType == EVENTTYPE_AUTHREQUEST) + _getOptS(challengeW, maxmsglen, "AuthChallenge", defaultAuthChallenge); + else + _getOptS(challengeW, maxmsglen, "Challenge", defaultChallenge); + _getOptS(buf, buflen, "Response", defaultResponse); + srand(time(NULL)); + _setCOptD(hContact, "ResponseNum", rand() % get_response_num(buf)); + ReplaceVarsNum(challengeW, maxmsglen, _getCOptD(hContact, "ResponseNum", 0)); + tmp = mir_u2a(challengeW); + challenge = mir_utf8encode(tmp); + mir_free(tmp); + CallContactService(hContact, PSS_MESSAGE, PREF_UTF, (LPARAM)challenge); + mir_free(challenge); + _notify(hContact, POPUP_CHALLENGE, TranslateT("Sending random challenge to %s."), message); + break; + + case SPAMOTRON_MODE_MATH: + a = (rand() % 10) + 1; + b = (rand() % 10) + 1; + mir_sntprintf(mexpr, sizeof(mexpr), _T("%d + %d"), a, b); + if (dbei->eventType == EVENTTYPE_AUTHREQUEST) + _getOptS(challengeW, maxmsglen, "AuthChallengeMath", defaultAuthChallengeMath); + else + _getOptS(challengeW, maxmsglen, "ChallengeMath", defaultChallengeMath); + ReplaceVar(challengeW, maxmsglen, _T("%mathexpr%"), mexpr); + _setCOptD(hContact, "ResponseMath", a + b); + tmp = mir_u2a(challengeW); + challenge = mir_utf8encode(tmp); + mir_free(tmp); + CallContactService(hContact, PSS_MESSAGE, PREF_UTF, (LPARAM)challenge); + mir_free(challenge); + _notify(hContact, POPUP_CHALLENGE, TranslateT("Sending math expression challenge to %s."), message); + break; + } + free(challengeW); + free(tmpW); + + // As a workaround for temporary NotOnList contact not being deleted from server-side list + // (which was added by the ICQ server itself upon first outgoing challenge request message) + // we need to set Delete setting, so that contacts gets deleted on next restart/connect. + db_set_b(hContact, "CList", "Delete", 1); + + // Queue user message in Bayes db + if (bayesEnabled && message != NULL) + queue_message(hContact, dbei->timestamp, message); + + + /*** Do any post-send procedures we need to do ***/ + + // Increment MsgSent if it was sent the same day. Otherwise set it to 1. + if (isOneDay(dbei->timestamp, _getCOptD(hContact, "MsgSentTime",0))) + _setCOptD(hContact, "MsgSent", _getCOptD(hContact, "MsgSent", 0)+1); + else + _setCOptD(hContact, "MsgSent", 1); + _setCOptD(hContact, "MsgSentTime", dbei->timestamp); + + // Save Last Msg and update SameMsgCount + if (message != NULL) { + if (_tcscmp(_getCOptS(buf, buflen, hContact, "LastInMsg", _T("")), message) == 0) + _setCOptD(hContact, "SameMsgCount", 1+_getCOptD(hContact, "SameMsgCount", 0)); + else + _setCOptD(hContact, "SameMsgCount", 1); + _setCOptTS(hContact, "LastInMsg", message); + } + + if (message != NULL) + mir_free(message); + + // Finally silently save the message to contact history if corresponding option is set + if (_getOptB("KeepBlockedMsg", defaultKeepBlockedMsg)) { + if (dbei->eventType == EVENTTYPE_AUTHREQUEST) { + // Save the request to database so that it can be automatically submitted on user approval + PBYTE eventdata = (PBYTE)malloc(sizeof(DWORD) + dbei->cbBlob); + if (eventdata != NULL && dbei->cbBlob > 0) { + memcpy(eventdata, &dbei->cbBlob, sizeof(DWORD)); + memcpy(eventdata + sizeof(DWORD), dbei->pBlob, dbei->cbBlob); + db_set_blob(hContact, PLUGIN_NAME, "AuthEvent", eventdata, sizeof(DWORD) + dbei->cbBlob); + _setCOptS(hContact, "AuthEventModule", dbei->szModule); + _setCOptB(hContact, "AuthEventPending", TRUE); + free(eventdata); + } + } else { + if (_getOptB("MarkMsgUnreadOnApproval", defaultMarkMsgUnreadOnApproval)) { + DBVARIANT _dbv; + DWORD dbei_size = 3*sizeof(DWORD) + sizeof(WORD) + dbei->cbBlob + (DWORD)strlen(dbei->szModule)+1; + PBYTE eventdata = (PBYTE)malloc(dbei_size); + PBYTE pos = eventdata; + if (eventdata != NULL && dbei->cbBlob > 0) { + if (db_get(hContact, PLUGIN_NAME, "LastMsgEvents", &_dbv) == 0) { + eventdata = (PBYTE)realloc(eventdata, dbei_size + _dbv.cpbVal); + pos = eventdata; + memcpy(eventdata, _dbv.pbVal, _dbv.cpbVal); + pos += _dbv.cpbVal; + db_free(&_dbv); + } + memcpy(pos, &dbei->eventType, sizeof(WORD)); + memcpy(pos+sizeof(WORD), &dbei->flags, sizeof(DWORD)); + memcpy(pos+sizeof(WORD)+sizeof(DWORD), &dbei->timestamp, sizeof(DWORD)); + memcpy(pos+sizeof(WORD)+sizeof(DWORD)*2, dbei->szModule, strlen(dbei->szModule)+1); + memcpy(pos+sizeof(WORD)+sizeof(DWORD)*2+strlen(dbei->szModule)+1, &dbei->cbBlob, sizeof(DWORD)); + memcpy(pos+sizeof(WORD)+sizeof(DWORD)*3+strlen(dbei->szModule)+1, dbei->pBlob, dbei->cbBlob); + db_set_blob(hContact, PLUGIN_NAME, "LastMsgEvents", eventdata, (pos - eventdata) + dbei_size); + free(eventdata); + } + + } else { + dbei->flags |= DBEF_READ; + CallService(MS_DB_EVENT_ADD, (WPARAM)hContact, (LPARAM)dbei); + } + } + } + return 1; +} + +/* Removes NotOnList settings from contacts with Delete setting present */ +void RemoveNotOnListSettings() +{ + DBVARIANT dbv; + char protoName[256] = {0}; + HANDLE hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0); + strcpy(protoName, "proto_"); + while (hContact != NULL) { + if (db_get_s(hContact, "Protocol", "p", &dbv) == 0) { + strcat(protoName, dbv.pszVal); + if (_getOptB(protoName, 0) != 0) { + if (db_get_b(hContact, "CList", "Delete", 0) == 1) { + db_unset(hContact, "CList", "NotOnList"); + } + } + db_free(&dbv); + } + protoName[6] = 0; + hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0); + } +} + +extern "C" __declspec(dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD mirandaVersion) +{ + return &pluginInfo; +} + +static const MUUID interfaces[] = {MIID_SPAMOTRON, MIID_LAST}; +extern "C" __declspec(dllexport) const MUUID* MirandaPluginInterfaces(void) +{ + return interfaces; +} + +extern "C" __declspec(dllexport) int Load() +{ + mir_getLP(&pluginInfo); + srand((unsigned)time(0)); + bayesdb = NULL; + if (_getOptB("BayesEnabled", defaultBayesEnabled)) { + if (CheckBayes()) { + OpenBayes(); + if (_getOptB("BayesAutolearnNotApproved", defaultBayesAutolearnNotApproved)) { + dequeue_messages(); + last_queue_check = time(NULL); + } + } + } + + hModulesLoaded = HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded); + hDBContactAdded = HookEvent(ME_DB_CONTACT_ADDED, OnDBContactAdded); + hDBEventAdded = HookEvent(ME_DB_EVENT_ADDED, OnDBEventAdded); + hDBEventFilterAdd = HookEvent(ME_DB_EVENT_FILTER_ADD, OnDBEventFilterAdd); + return 0; +} + +extern "C" _declspec(dllexport) int Unload(void) +{ + RemoveNotOnListSettings(); + UnhookEvent(hOptInitialize); + UnhookEvent(hModulesLoaded); + UnhookEvent(hDBContactAdded); + UnhookEvent(hDBEventAdded); + UnhookEvent(hDBEventFilterAdd); + return 0; +} \ No newline at end of file diff --git a/plugins/Spamotron/src/utils.cpp b/plugins/Spamotron/src/utils.cpp new file mode 100644 index 0000000000..ca31eda236 --- /dev/null +++ b/plugins/Spamotron/src/utils.cpp @@ -0,0 +1,521 @@ +#include "common.h" +#include + +TCHAR *_tcstolower(TCHAR *dst) +{ + unsigned int i = 0; + setlocale(LC_ALL, ""); + if (!dst) + return NULL; + for (i = 0; i < _tcslen(dst); i++) + dst[i] = _totlower(dst[i]); + return dst; +} + +TCHAR *_tcstoupper(TCHAR *dst) +{ + unsigned int i = 0; + setlocale(LC_ALL, ""); + if (!dst) + return NULL; + for (i = 0; i < _tcslen(dst); i++) + dst[i] = _totupper(dst[i]); + return dst; +} + +BOOL _isregex(TCHAR* strSearch) +{ + BOOL ret = FALSE; + pcre *re; + const char *error; + int erroroffs, rc; + char *regex; + char regex_parse[] = "/(.*)/([igsm]*)"; + int ovector[9]; + + re = pcre_compile(regex_parse, 0, &error, &erroroffs, NULL); + if (!re) + return FALSE; + + regex = mir_u2a(strSearch); + rc = pcre_exec(re, NULL, regex, (int)strlen(regex), 0, 0, ovector, 9); + if (rc == 3) + ret = TRUE; + + if (re) + pcre_free(re); + if (regex) + mir_free(regex); + + return ret; +} + +BOOL _isvalidregex(TCHAR* strSearch) +{ + BOOL ret; + pcre *re; + const char *error; + int erroroffs, rc; + char *regex, *regexp, *mod; + int opts = 0; + char regex_parse[] = "/(.*)/([igsm]*)"; + int ovector[9]; + + re = pcre_compile(regex_parse, 0, &error, &erroroffs, NULL); + if (!re) + return FALSE; + + regex = mir_u2a(strSearch); + rc = pcre_exec(re, NULL, regex, (int)strlen(regex), 0, 0, ovector, 9); + pcre_free(re); + + if (rc == 3) { + regexp = regex + ovector[2]; + regexp[ovector[3]-ovector[2]] = 0; + mod = regex + ovector[4]; + mod[ovector[5]-ovector[4]] = 0; + + if (strstr(mod, "i")) + opts |= PCRE_CASELESS; + if (strstr(mod, "m")) + opts |= PCRE_MULTILINE; + if (strstr(mod, "s")) + opts |= PCRE_DOTALL; + + re = pcre_compile(regexp, opts, &error, &erroroffs, NULL); + ret = (re) ? TRUE : FALSE; + } + + if (re) + pcre_free(re); + if (regex) + mir_free(regex); + + return ret; +} + +BOOL _regmatch(TCHAR* str, TCHAR* strSearch) +{ + BOOL ret; + pcre *re; + const char *error; + int erroroffs, rc; + char *regex, *regexp, *data, *mod; + int opts = 0; + char regex_parse[] = "^/(.*)/([igsm]*)"; + int ovector[9]; + + re = pcre_compile(regex_parse, 0, &error, &erroroffs, NULL); + if (!re) + return FALSE; // [TODO] and log some error + + regex = mir_u2a(strSearch); + rc = pcre_exec(re, NULL, regex, (int)strlen(regex), 0, 0, ovector, 9); + if (rc != 3) { + mir_free(regex); + return FALSE; // [TODO] and log some error (better check for valid regex on options save) + } + + regexp = regex + ovector[2]; + regexp[ovector[3]-ovector[2]] = 0; + mod = regex + ovector[4]; + mod[ovector[5]-ovector[4]] = 0; + pcre_free(re); + + data = mir_u2a(str); + + if (strstr(mod, "i")) + opts |= PCRE_CASELESS; + if (strstr(mod, "m")) + opts |= PCRE_MULTILINE; + if (strstr(mod, "s")) + opts |= PCRE_DOTALL; + + re = pcre_compile(regexp, opts, &error, &erroroffs, NULL); + if (!re) { + mir_free(regex); + mir_free(data); + return FALSE; + } + + rc = pcre_exec(re, NULL, data, (int)strlen(data), 0, 0, NULL, 0); + if (rc < 0) { + ret = FALSE; + } else { + ret = TRUE; + } + + if (re) + pcre_free(re); + if (regex) + mir_free(regex); + if (data) + mir_free(data); + + return ret; +} + +int get_response_id(const TCHAR* strvar) +{ + int ret; + pcre *re; + const char *error; + int erroroffs, rc; + char *_str, *_strvar; + int opts = 0; + char regex[] = "^%response([#-_]([0-9]+))?%$"; + int ovector[9]; + + re = pcre_compile(regex, 0, &error, &erroroffs, NULL); + if (!re) + return FALSE; // [TODO] and log some error + + _strvar = mir_u2a(strvar); + rc = pcre_exec(re, NULL, _strvar, (int)strlen(_strvar), 0, 0, ovector, 9); + if (rc < 0) { + ret = -1; + } else if (rc == 3) { + _str = _strvar + ovector[4]; + _str[ovector[5]-ovector[4]] = 0; + ret = atoi(_str); + } else + ret = 0; + + if (re) + pcre_free(re); + if (_strvar) + mir_free(_strvar); + + return ret; +} + +int get_response_num(const TCHAR *str) +{ + int i = 0; + TCHAR *tmp, *strc = NULL; + strc = (TCHAR*)malloc((_tcslen(str)+1)*sizeof(TCHAR)); + if (strc != NULL) { + _tcscpy(strc, str); + tmp = _tcstok(strc, L"\r\n"); + while(tmp) { + i++; + tmp = _tcstok(NULL, L"\r\n"); + } + free(strc); + } + return i; +} + +TCHAR* get_response(TCHAR* dst, unsigned int dstlen, int num) +{ + int i = 0; + TCHAR *tmp, *src = NULL; + if (num < 0) + return dst; + src = (TCHAR*)malloc(MAX_BUFFER_LENGTH * sizeof(TCHAR)); + if (src != NULL) { + _getOptS(src, MAX_BUFFER_LENGTH, "Response", defaultResponse); + _tcscpy(src, src); + tmp = _tcstok(src, L"\r\n"); + while (tmp) { + if (i == num) { + _tcscpy(dst, tmp); + free(src); + return dst; + } + i++; + tmp = _tcstok(NULL, L"\r\n"); + } + free(src); + } + return dst; +} + +TCHAR* _tcsstr_cc(TCHAR* str, TCHAR* strSearch, BOOL cc) +{ + if (cc) + return _tcsstr(str, strSearch); + else { + TCHAR *ret; + TCHAR *_str = (TCHAR*)malloc((_tcslen(str)+1)*sizeof(TCHAR)); + TCHAR *_strSearch = (TCHAR*)malloc((_tcslen(strSearch)+1)*sizeof(TCHAR)); + _tcscpy(_str, str); + _tcscpy(_strSearch, strSearch); + ret = _tcsstr(_tcstolower(_str), _tcstolower(_strSearch)); + if (ret != NULL) + ret = (ret-_str) + str; + free(_str); + free(_strSearch); + return ret; + } +} + +BOOL Contains(TCHAR* dst, TCHAR* src) // Checks for occurence of substring from src in dst +{ + int i = 0, n = 0, value = 0; + TCHAR *tsrc = (TCHAR *)malloc((_tcslen(src)+1)*sizeof(TCHAR)); + TCHAR *tdst = (TCHAR *)malloc((_tcslen(dst)+1)*sizeof(TCHAR)); + TCHAR **tokens = NULL; + TCHAR *token = NULL; + _tcscpy(tdst, dst); + _tcscpy(tsrc, src); + token = _tcstok(tsrc, _T(",")); + while (token) { + tokens = (TCHAR **) realloc(tokens, (n+1)*sizeof(TCHAR *)); + tokens[n] = (TCHAR *)malloc((_tcslen(token)+1)*sizeof(TCHAR)); + while(!_tcsncmp(token, _T(" "), 1)) {++token;} + while(_tcschr(token+_tcslen(token)-1, _T(' '))) { + token[_tcslen(token)-1] = _T('\0'); + } + _tcscpy(tokens[n], token); + token = _tcstok(NULL, _T(",")); + ++n; + } + for (i = 0; i < n; i++) { + + if (_tcsstr(_tcstoupper(tdst), _tcstoupper(tokens[i]))) { + value = 1; + break; + } + } + free(tsrc); + if (tokens) { + for(i = 0; i < n; i++) + free(tokens[i]); + free(tokens); + } + return value; +} + +BOOL isOneDay(DWORD timestamp1, DWORD timestamp2) +{ + time_t t1, t2; + int at1[3], at2[3]; + struct tm *tm; + if (!timestamp1 || !timestamp2) + return FALSE; + t1 = (time_t)timestamp1; + t2 = (time_t)timestamp2; + tm = gmtime(&t1); + at1[0] = tm->tm_mday; + at1[1] = tm->tm_mon; + at1[2] = tm->tm_year; + tm = gmtime(&t2); + at2[0] = tm->tm_mday; + at2[1] = tm->tm_mon; + at2[2] = tm->tm_year; + if (memcmp(&at1, &at2, 3*sizeof(int)) == 0) + return TRUE; + return FALSE; +} + +TCHAR* ReplaceVar(TCHAR *dst, unsigned int len, const TCHAR *var, const TCHAR *rvar) +{ + TCHAR *_rvar = (TCHAR*)malloc((_tcslen(rvar)+1)*sizeof(TCHAR)); + TCHAR *__rvar = NULL; + TCHAR *var_start, *tmp = NULL; + TCHAR *src = NULL; + int response_id = get_response_id(var); + + src = (TCHAR*)malloc((_tcslen(dst)+1)*sizeof(TCHAR)); + _tcscpy(_rvar, rvar); + __rvar = _rvar; +/* + if (response_id >= 0) { + __rvar = _tcstok(_rvar, L"\r\n"); + while(response_id--) { + __rvar = _tcstok(NULL, L"\r\n"); + } + } else { + __rvar = _rvar; + } +*/ + if (!dst) + return NULL; + _tcscpy(src, dst); + var_start = _tcsstr(dst, var); + while (var_start) { + tmp = (TCHAR*)realloc(tmp, sizeof(TCHAR)*(_tcslen(dst) + _tcslen(__rvar) - _tcslen(var) + 1)); + _tcsncpy(tmp, dst, var_start - dst); + tmp[var_start - dst] = 0; + _tcscat(tmp, __rvar); + _tcscat(tmp, var_start + _tcslen(var)); + if (len < (_tcslen(src)+1)*sizeof(TCHAR)) { + free(tmp); + return NULL; + } + _tcscpy(dst, tmp); + var_start = _tcsstr(dst, var); + } + if (tmp) + free(tmp); + if (_rvar) + free(_rvar); + if (src) + free(src); + return dst; +} + +TCHAR* ReplaceVars(TCHAR *dst, unsigned int len) +{ + return ReplaceVarsNum(dst, len, -1); +} + +TCHAR* ReplaceVarsNum(TCHAR *dst, unsigned int len, int num) +{ + TCHAR response[2048]; + int ret, i = 1; + pcre *re; + const char *error; + int erroroffs, rc; + char *_str, *tmp; + TCHAR **r; + TCHAR *ttmp, *dstcopy; + int opts = 0; + char regex[] = "%response([#-_]([0-9]+))?%"; + int ovector[9]; + + re = pcre_compile(regex, 0, &error, &erroroffs, NULL); + if (!re) + return FALSE; // [TODO] and log some error + + _getOptS(response, 2048, "Response", defaultResponse); + + r = (TCHAR**)malloc(sizeof(char*)); + ttmp = _tcstok(response, L"\r\n"); + r[0] = ttmp; + while(ttmp) { + ttmp = _tcstok(NULL, L"\r\n"); + if (ttmp != NULL) { + r = (TCHAR**)realloc(r, (i+1)*sizeof(TCHAR*)); + r[i++] = ttmp; + } + } + + do { + _str = mir_u2a(dst); + dstcopy = (TCHAR*)malloc((_tcslen(dst)+1)*sizeof(TCHAR)); + _tcscpy(dstcopy, dst); + + rc = pcre_exec(re, NULL, _str, (int)strlen(_str), 0, 0, ovector, 9); + if (rc < 0) { + ret = -1; + } else if (rc == 3) { + ttmp = dstcopy + ovector[0]; + ttmp[ovector[1]-ovector[0]] = 0; + tmp = _str + ovector[4]; + tmp[ovector[5]-ovector[4]] = 0; + ret = atoi(tmp); + } else { + ttmp = dstcopy + ovector[0]; + ttmp[ovector[1]-ovector[0]] = 0; + ret = 0; + } + + if (ret >= 0) { + if (ret > i) + ReplaceVar(dst, len, ttmp, r[0]); + else if (ret == 0 && num > 0 && num < i) + ReplaceVar(dst, len, ttmp, r[num]); + else + ReplaceVar(dst, len, ttmp, r[ret == 0 ? 0 : ret-1]); + } + + if (_str) + mir_free(_str); + if (dstcopy) + free(dstcopy); + } while (rc >= 0); + + if (re) + pcre_free(re); + + return dst; +} + +int _notify(HANDLE hContact, BYTE type, TCHAR *message, TCHAR *origmessage) +{ + char *tmp, *tmporig; + TCHAR msg[MAX_BUFFER_LENGTH]; + mir_sntprintf(msg, MAX_BUFFER_LENGTH, message, CONTACT_NAME(hContact)); + + if (_getOptB("LogActions", defaultLogActions)) { + tmp = mir_u2a(msg); + tmporig = mir_u2a(origmessage); + LogToSystemHistory(tmp, origmessage ? tmporig : NULL); + mir_free(tmp); + mir_free(tmporig); + } + + if (_NOTIFYP) { + if (type == POPUP_BLOCKED) { + if (_getOptB("NotifyPopupBlocked", defaultNotifyPopupBlocked)) + ShowPopup(hContact, type, NULL, msg); + } else if (type == POPUP_APPROVED) { + if (_getOptB("NotifyPopupApproved", defaultNotifyPopupApproved)) + ShowPopup(hContact, type, NULL, msg); + } else if (type == POPUP_CHALLENGE) { + if (_getOptB("NotifyPopupChallenge", defaultNotifyPopupChallenge)) + ShowPopup(hContact, type, NULL, msg); + } else { + ShowPopup(hContact, type, NULL, msg); + } + } + return 0; +} +#define DOT(a) (a[strlen(a)-1] == 46) ? "" : "." +int LogToSystemHistory(char *message, char *origmessage) +{ + char *msg = (char*)malloc(MAX_BUFFER_LENGTH); + time_t tm; + DBEVENTINFO dbei; + dbei.cbSize = sizeof(DBEVENTINFO); + dbei.timestamp = time(&tm); + dbei.szModule = PLUGIN_NAME; + if (origmessage) + mir_snprintf(msg, MAX_BUFFER_LENGTH, "%s: %s%s %s: %s", PLUGIN_NAME, message, DOT(message), Translate("Their message was"), origmessage); + else + mir_snprintf(msg, MAX_BUFFER_LENGTH, "%s: %s%s", PLUGIN_NAME, message, DOT(message)); + dbei.pBlob = (PBYTE)msg; + dbei.cbBlob = (DWORD)strlen(msg)+1; + dbei.eventType = EVENTTYPE_MESSAGE; + dbei.flags = DBEF_READ; + CallService(MS_DB_EVENT_ADD, (WPARAM)NULL, (LPARAM)&dbei); + return 0; +} + +void MarkUnread(HANDLE hContact) +{ + // We're not actually marking anything. We just pushing saved events to the database from a temporary location + DBVARIANT _dbv = {0}; + DBEVENTINFO _dbei; + PBYTE pos; + + if (hContact == NULL) + return; + + if (db_get(hContact, PLUGIN_NAME, "LastMsgEvents", &_dbv) == 0) { + pos = _dbv.pbVal; + while (pos - _dbv.pbVal < _dbv.cpbVal) { + ZeroMemory(&_dbei, sizeof(_dbei)); + _dbei.cbSize = sizeof(_dbei); + + memcpy(&_dbei.eventType, pos, sizeof(WORD)); pos += sizeof(WORD); + memcpy(&_dbei.flags, pos, sizeof(DWORD)); pos += sizeof(DWORD); + memcpy(&_dbei.timestamp, pos, sizeof(DWORD)); pos += sizeof(DWORD); + + _dbei.szModule = (char*)malloc(strlen((const char*)pos)+1); + strcpy(_dbei.szModule, (const char*)pos); + pos += strlen((const char*)pos)+1; + + memcpy(&_dbei.cbBlob, pos, sizeof(DWORD)); pos += sizeof(DWORD); + _dbei.pBlob = (PBYTE)malloc(_dbei.cbBlob); + memcpy(_dbei.pBlob, pos, _dbei.cbBlob); + pos += _dbei.cbBlob; + + CallService(MS_DB_EVENT_ADD, (WPARAM)hContact, (LPARAM)&_dbei); + } + db_free(&_dbv); + db_unset(hContact, PLUGIN_NAME, "LastMsgEvents"); + } +} -- cgit v1.2.3