Пример #1
0
/*
 * Calculates all of the scores for the searchset and stores them in the
 * mail elts. Careful, this function uses patterns so if the caller is using
 * patterns then the caller will probably have to reset the pattern functions.
 * That is, will have to call first_pattern again with the correct type.
 *
 * Args:     stream
 *        searchset -- calculate scores for this set of messages
 *         no_fetch -- we're in a callback from c-client, don't call c-client
 *
 * Returns   1 -- ok
 *           0 -- error, because of no_fetch
 */
int
calculate_some_scores(MAILSTREAM *stream, SEARCHSET *searchset, int no_fetch)
{
    PAT_S         *pat = NULL;
    PAT_STATE      pstate;
    char          *savebits;
    long           newscore, addtoscore, score;
    int            error = 0;
    long           rflags = ROLE_SCORE;
    long           n, i;
    SEARCHSET     *s;
    MESSAGECACHE  *mc;
    HEADER_TOK_S  *hdrtok;

    dprint((7, "calculate_some_scores\n"));

    if(nonempty_patterns(rflags, &pstate)){

	/* calculate scores */
	if(searchset){
	    
	    /* this calls match_pattern which messes up searched bits */
	    savebits = (char *)fs_get((stream->nmsgs+1) * sizeof(char));
	    for(i = 1L; i <= stream->nmsgs; i++)
	      savebits[i] = (mc = mail_elt(stream, i)) ? mc->searched : 0;

	    /*
	     * First set all the scores in the searchset to zero so that they
	     * will no longer be undefined.
	     */
	    score = 0L;
	    for(s = searchset; s; s = s->next)
	      for(n = s->first; n <= s->last; n++)
		set_msg_score(stream, n, score);

	    for(pat = first_pattern(&pstate);
		!error && pat;
		pat = next_pattern(&pstate)){

		newscore = pat->action->scoreval;
		hdrtok = pat->action->scorevalhdrtok;

		/*
		 * This no_fetch probably isn't necessary since
		 * we will actually have fetched this with
		 * the envelope. Just making sure.
		 */
		if(hdrtok && no_fetch){
		    error++;
		    break;
		}

		switch(match_pattern(pat->patgrp, stream, searchset, NULL, NULL,
				     (no_fetch ? MP_IN_CCLIENT_CB : 0)
				      | (SE_NOSERVER|SE_NOPREFETCH))){
		  case 1:
		    if(!pat->action || pat->action->bogus)
		      break;

		    for(s = searchset; s; s = s->next)
		      for(n = s->first; n <= s->last; n++)
			if(n > 0L && stream && n <= stream->nmsgs
			   && (mc = mail_elt(stream, n)) && mc->searched){
			    if((score = get_msg_score(stream,n)) == SCORE_UNDEF)
			      score = 0L;
			    
			    if(hdrtok)
			      addtoscore = scorevalfrommsg(stream, n, hdrtok, no_fetch);
			    else
			      addtoscore = newscore;

			    score += addtoscore;
			    set_msg_score(stream, n, score);
			}

		    break;
		
		  case 0:
		    break;

		  case -1:
		    error++;
		    break;
		}
	    }

	    for(i = 1L; i <= stream->nmsgs; i++)
	      if((mc = mail_elt(stream, i)) != NULL)
		mc->searched = savebits[i];

	    fs_give((void **)&savebits);

	    if(error){
		/*
		 * Revert to undefined scores.
		 */
		score = SCORE_UNDEF;
		for(s = searchset; s; s = s->next)
		  for(n = s->first; n <= s->last; n++)
		    set_msg_score(stream, n, score);
	    }
	}
    }

    return(error ? 0 : 1);
}
Пример #2
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 = (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);
					db_event_add(hContact,_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;
				db_event_add(hContact, dbei);
			}
		}
	}
	return 1;
}