diff options
Diffstat (limited to 'plugins/UserInfoEx/src/svc_refreshci.cpp')
| -rw-r--r-- | plugins/UserInfoEx/src/svc_refreshci.cpp | 936 | 
1 files changed, 936 insertions, 0 deletions
diff --git a/plugins/UserInfoEx/src/svc_refreshci.cpp b/plugins/UserInfoEx/src/svc_refreshci.cpp new file mode 100644 index 0000000000..bddc9e60ca --- /dev/null +++ b/plugins/UserInfoEx/src/svc_refreshci.cpp @@ -0,0 +1,936 @@ +/*
 +UserinfoEx plugin for Miranda IM
 +
 +Copyright:
 +ฉ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
 +
 +This program is free software; you can redistribute it and/or
 +modify it under the terms of the GNU General Public License
 +as published by the Free Software Foundation; either version 2
 +of the License, or (at your option) any later version.
 +
 +This program is distributed in the hope that it will be useful,
 +but WITHOUT ANY WARRANTY; without even the implied warranty of
 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 +GNU General Public License for more details.
 +
 +You should have received a copy of the GNU General Public License
 +along with this program; if not, write to the Free Software
 +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 +
 +===============================================================================
 +
 +File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/svc_refreshci.cpp $
 +Revision       : $Revision: 187 $
 +Last change on : $Date: 2010-09-08 16:05:54 +0400 (ะกั, 08 ัะตะฝ 2010) $
 +Last change by : $Author: ing.u.horn $
 +
 +===============================================================================
 +*/
 +#include "commonheaders.h"
 +#include "mir_contactqueue.h"
 +#include "mir_menuitems.h"
 +#include "svc_refreshci.h"
 +
 +#define HM_PROTOACK	(WM_USER+100)
 +
 +typedef INT_PTR	(*PUpdCallback) (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, PVOID UserData);
 +
 +/***********************************************************************************************************
 + * class CUpdProgress
 + ***********************************************************************************************************/
 +
 +class CUpdProgress
 +{
 +protected:
 +	BOOLEAN			_bBBCode;		// TRUE if text renderer can handle BBCodes
 +	BOOLEAN			_bIsCanceled;	// is set to TRUE uppon click on the CANCEL button
 +	PUpdCallback	_pFnCallBack;	// a pointer to a callback function, which can be used 
 +									// to catch several messages by the caller.
 +	PVOID			_pData;			// application defined data
 +	HWND			_hWnd;			// window handle of the progress dialog/popup
 +
 +	/**
 +	 * This is the default window procedure, which is called for both progress dialog and popup.
 +	 * It handles some common messages and calls the user defined callback function if defined.
 +	 *
 +	 * @param	pProgress		- This is the pointer to the object of CUpdProgress.
 +	 * @param	hWnd			- HWND window handle of the progress dialog
 +	 * @param	uMsg			- message sent to the dialog
 +	 * @param	wParam			- message specific parameter
 +	 * @param	lParam			- message specific parameter
 +	 *
 +	 * @return	This method returns 0.
 +	 **/
 +	static INT_PTR CALLBACK DefWndProc(CUpdProgress *pProgress, HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 
 +	{
 +		__try
 +		{
 +			if (PtrIsValid(pProgress))
 +			{
 +				switch (uMsg)
 +				{
 +				case UM_POPUPACTION:
 +				case WM_COMMAND:
 +					{
 +						if (wParam == MAKEWORD(IDSKIP, BN_CLICKED))
 +						{
 +							pProgress->Destroy();
 +						}
 +						else						
 +						if (wParam == MAKEWORD(IDCANCEL, BN_CLICKED))
 +						{
 +							pProgress->_bIsCanceled = TRUE;
 +						}
 +					}
 +				}
 +				if (PtrIsValid(pProgress->_pFnCallBack))
 +				{
 +					pProgress->_pFnCallBack(hWnd, uMsg, wParam, lParam, pProgress->_pData);
 +				}
 +			}
 +		}
 +		__except(GetExceptionCode()==EXCEPTION_ACCESS_VIOLATION ? 
 +						EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) 
 +		{ // code to handle exception
 +			puts("Exception Occurred");
 +		}
 +		return 0;
 +	}
 +
 +public:
 +
 +	virtual HWND	Create		(LPCTSTR szTitle, PUpdCallback pFnCallBack) = 0;
 +	virtual VOID	Destroy		(VOID) {};
 +	virtual VOID	SetTitle	(LPCTSTR szText) = 0;
 +	virtual VOID	SetText		(LPCTSTR szText) = 0;
 +
 +	BOOLEAN IsVisible() const
 +	{
 +		return _hWnd != NULL;
 +	}
 +	/**
 +	 *
 +	 *
 +	 **/
 +	BOOLEAN IsCanceled() const
 +	{
 +		return _bIsCanceled;
 +	}
 +
 +	/**
 +	 *
 +	 *
 +	 **/
 +	VOID SetTitleParam(LPCTSTR szText, ...)
 +	{
 +		if (szText)
 +		{
 +			TCHAR buf[MAXDATASIZE];
 +			va_list vl;
 +			
 +			va_start(vl, szText);
 +			if (mir_vsntprintf(buf, SIZEOF(buf), szText, vl) != -1)
 +			{
 +				SetTitle(buf);	 
 +			}
 +			va_end(vl);
 +		}
 +	}
 +
 +	/**
 +	 * This method is used to set the popups or dialogs message text.
 +	 * It takes text with parameters as sprintf does. If bbcodes are
 +	 * disabled this method automatically deletes them from the text. 
 +	 *
 +	 * @param	szText			- the text to display. Can contain formats like
 +	 *							  sprintf does.
 +	 * @param	...				- a list of parameters, which depends on the
 +	 *							  format of szText.
 +	 *
 +	 * @return	nothing
 +	 **/
 +	VOID SetTextParam(LPCTSTR szText, ...)
 +	{
 +		if (szText)
 +		{
 +			INT_PTR cch = mir_tcslen(szText);
 +			LPTSTR	fmt = (LPTSTR) mir_alloc((cch + 1) * sizeof(TCHAR));
 +			
 +			if (fmt)
 +			{
 +				TCHAR buf[MAXDATASIZE];
 +				va_list vl;
 +
 +				_tcscpy(fmt, szText);
 +
 +				// delete bbcodes
 +				if (!_bBBCode)
 +				{
 +					LPTSTR s, e;
 +
 +					for (s = fmt, e = fmt + cch; s[0] != 0; s++)
 +					{
 +						if (s[0] == '[')
 +						{
 +							// leading bbcode tag (e.g.: [b], [u], [i])
 +							if ((s[1] == 'b' || s[1] == 'u' || s[1] == 'i') && s[2] == ']')
 +							{
 +								memmove(s, s + 3, (e - s - 2) * sizeof(TCHAR));
 +								e -= 3;
 +							}
 +							// ending bbcode tag (e.g.: [/b], [/u], [/i])
 +							else if (s[1] == '/' && (s[2] == 'b' || s[2] == 'u' || s[2] == 'i') && s[3] == ']')
 +							{
 +								memmove(s, s + 4, (e - s - 3) * sizeof(TCHAR));
 +								e -= 4;
 +							}
 +						}
 +					}
 +				}
 +			
 +				va_start(vl, szText);
 +				if (mir_vsntprintf(buf, SIZEOF(buf), fmt, vl) != -1)
 +				{
 +					SetText(buf);	 
 +				}
 +				va_end(vl);
 +				mir_free(fmt);
 +			}
 +		}
 +	}
 +
 +	/**
 +	 *
 +	 *
 +	 **/
 +	CUpdProgress()
 +	{
 +		_hWnd = NULL;
 +		_pFnCallBack = NULL;
 +		_pData = NULL;
 +		_bIsCanceled = FALSE;
 +		_bBBCode = FALSE;
 +	}
 +
 +	/**
 +	 *
 +	 *
 +	 **/
 +	CUpdProgress(PVOID data)
 +	{
 +		_hWnd = NULL;
 +		_pFnCallBack = NULL;
 +		_pData = data;
 +		_bIsCanceled = FALSE;
 +		_bBBCode = FALSE;
 +	}
 +
 +	~CUpdProgress()
 +	{
 +	}
 +
 +};
 +
 +/***********************************************************************************************************
 + * class CDlgUpdProgress
 + ***********************************************************************************************************/
 +
 +class CDlgUpdProgress : public CUpdProgress
 +{
 +	/**
 +	 *
 +	 *
 +	 **/
 +	static INT_PTR CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 
 +	{
 +		switch (uMsg)
 +		{
 +		case WM_INITDIALOG:
 +			{
 +				const ICONCTRL idIcon[] = {
 +					{ ICO_BTN_UPDATE,		WM_SETICON,		NULL		},
 +					{ ICO_BTN_DOWNARROW,	BM_SETIMAGE,	IDSKIP		},
 +					{ ICO_BTN_CANCEL,		BM_SETIMAGE,	IDCANCEL	}
 +				};
 +				IcoLib_SetCtrlIcons(hWnd, idIcon, DB::Setting::GetByte(SET_ICONS_BUTTONS, 1) ? 2 : 1);
 +	
 +				SendDlgItemMessage(hWnd, IDCANCEL,	BUTTONTRANSLATE, NULL, NULL);
 +				SendDlgItemMessage(hWnd, IDSKIP,	BUTTONTRANSLATE, NULL, NULL);
 +				SetUserData(hWnd, lParam);
 +				
 +				TranslateDialogDefault(hWnd);
 +			}
 +			return TRUE;
 +
 +		case WM_CTLCOLORSTATIC:
 +			{
 +				switch (GetWindowLongPtr((HWND)lParam, GWLP_ID)) 
 +				{
 +					case STATIC_WHITERECT:
 +					case IDC_INFO:
 +						{
 +							SetTextColor((HDC)wParam, GetSysColor(COLOR_WINDOWTEXT));
 +							return GetSysColor(COLOR_WINDOW);
 +						}
 +				}
 +			}
 +			return FALSE;
 +
 +		}
 +		return CUpdProgress::DefWndProc((CUpdProgress *) GetUserData(hWnd), hWnd, uMsg, wParam, lParam);
 +	}
 +
 +public:
 +
 +	/**
 +	 *
 +	 *
 +	 **/
 +	CDlgUpdProgress(PVOID data)
 +		: CUpdProgress(data)
 +	{
 +	}
 +
 +	/**
 +	 *
 +	 *
 +	 **/
 +	virtual HWND Create(LPCTSTR szTitle, PUpdCallback pFnCallBack)
 +	{
 +		_pFnCallBack = pFnCallBack;
 +		_hWnd = CreateDialogParam(ghInst, 
 +							MAKEINTRESOURCE(IDD_REFRESHDETAILS), 
 +							0, 
 +							(DLGPROC)CDlgUpdProgress::WndProc, 
 +							(LPARAM) this);
 +		if (_hWnd)
 +		{
 +			SetTitle(szTitle);
 +			ShowWindow(_hWnd, SW_SHOW);
 +		}
 +		return _hWnd;
 +	}
 +
 +	/**
 +	 *
 +	 *
 +	 **/
 +	virtual VOID Destroy()
 +	{
 +		if (_hWnd)
 +		{
 +			SetUserData(_hWnd, NULL);
 +			EndDialog(_hWnd, IDOK);
 +			_hWnd = NULL;
 +		}
 +	}
 +
 +	/**
 +	 *
 +	 *
 +	 **/
 +	virtual VOID SetTitle(LPCTSTR szText)
 +	{
 +		SetWindowText(_hWnd, szText);
 +	}
 +
 +	/**
 +	 *
 +	 *
 +	 **/
 +	virtual VOID SetText(LPCTSTR szText)
 +	{
 +		SetDlgItemText(_hWnd, IDC_INFO, szText);
 +	}
 +
 +};
 +
 +/***********************************************************************************************************
 + * class CPopupUpdProgress
 + ***********************************************************************************************************/
 +
 +class CPopupUpdProgress : public CUpdProgress
 +{
 +	LPCTSTR			_szText;
 +	POPUPACTION		_popupButtons[2];
 +
 +	/**
 +	 *
 +	 *
 +	 **/
 +	VOID UpdateText()
 +	{
 +		if (_szText)
 +		{
 +			INT_PTR cb = mir_tcslen(_szText) + 8;
 +			LPTSTR	pb = (LPTSTR) mir_alloc(cb * sizeof(TCHAR));
 +
 +			if (pb)
 +			{
 +				mir_tcscpy(pb, _szText);
 +
 +				SendMessage(_hWnd, UM_CHANGEPOPUP, CPT_TITLET, (LPARAM) pb);
 +			}
 +		}
 +	}
 +
 +	/**
 +	 * This static member is the window procedure, which is used to modify the behaviour of
 +	 * a popup dialog, so that it can act as a replacement for the progress dialog.
 +	 * The major task of this method is to filter out some messages, who would lead to a crash,
 +	 * if passed to the default windows procedure.
 +	 *
 +	 **/
 +	static INT_PTR CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 
 +	{
 +		// Filter out messages, which must not be passed to default windows procedure or even
 +		// to the callback function!
 +		switch (uMsg)
 +		{
 +		case UM_INITPOPUP:
 +		case UM_CHANGEPOPUP:
 +		case UM_FREEPLUGINDATA:
 +			break;
 +		default:
 +			CUpdProgress::DefWndProc((CUpdProgress *) PUGetPluginData(hWnd), hWnd, uMsg, wParam, lParam);
 +		}
 +		return DefWindowProc(hWnd, uMsg, wParam, lParam);
 +	}
 +
 +public:
 +
 +	/**
 +	 *
 +	 *
 +	 **/
 +	CPopupUpdProgress(PVOID data)
 +		: CUpdProgress(data)
 +	{
 +		_szText = NULL;
 +		_bBBCode = DB::Setting::GetByte("PopUp", "UseMText", FALSE);
 +
 +		_popupButtons[0].cbSize = sizeof(POPUPACTION);
 +		_popupButtons[0].flags = PAF_ENABLED;
 +		_popupButtons[0].lchIcon = IcoLib_GetIcon(ICO_BTN_DOWNARROW);
 +		_popupButtons[0].wParam = MAKEWORD(IDSKIP, BN_CLICKED);
 +		_popupButtons[0].lParam = NULL;
 +		strcpy(_popupButtons[0].lpzTitle, MODNAME"/Hide");
 +
 +		// cancel button
 +		_popupButtons[1].cbSize = sizeof(POPUPACTION);
 +		_popupButtons[1].flags = PAF_ENABLED;
 +		_popupButtons[1].lchIcon = IcoLib_GetIcon(ICO_BTN_CANCEL);
 +		_popupButtons[1].wParam = MAKEWORD(IDCANCEL, BN_CLICKED);
 +		_popupButtons[1].lParam = NULL;
 +		strcpy(_popupButtons[1].lpzTitle, MODNAME"/Cancel");
 +	}
 +
 +	/**
 +	 *
 +	 *
 +	 **/
 +	virtual HWND Create(LPCTSTR szTitle, PUpdCallback pFnCallBack)
 +	{
 +		POPUPDATAT_V2	pd;
 +		
 +		ZeroMemory(&pd, sizeof(POPUPDATAT_V2));
 +		pd.cbSize = sizeof(POPUPDATAT_V2);
 +		pd.lchIcon = IcoLib_GetIcon(ICO_BTN_UPDATE);
 +		pd.iSeconds = -1;
 +		pd.PluginData = this;
 +		pd.PluginWindowProc = (WNDPROC)CPopupUpdProgress::WndProc;
 +		pd.actionCount = SIZEOF(_popupButtons);
 +		pd.lpActions = _popupButtons;
 +
 +		// dummy text
 +		_szText = mir_tcsdup(szTitle);
 +		mir_tcscpy(pd.lptzContactName, _szText);
 +		
 +		_tcscpy(pd.lptzText, _T(" "));
 +		
 +		_pFnCallBack = pFnCallBack;
 +		_hWnd = (HWND) CallService(MS_POPUP_ADDPOPUPT, (WPARAM) &pd, APF_RETURN_HWND|APF_NEWDATA);
 +		return _hWnd;
 +	}
 +
 +	/**
 +	 *
 +	 *
 +	 **/
 +	virtual VOID Destroy()
 +	{
 +		if (_hWnd)
 +		{
 +			PUDeletePopUp(_hWnd);
 +			_hWnd = NULL;
 +		}
 +		MIR_FREE(_szText);
 +	}
 +
 +	/**
 +	 *
 +	 *
 +	 **/
 +	virtual VOID SetTitle(LPCTSTR szText)
 +	{
 +		MIR_FREE(_szText);
 +		_szText = mir_tcsdup(szText);
 +		UpdateText();
 +	}
 +
 +	/**
 +	 *
 +	 *
 +	 **/
 +	virtual VOID SetText(LPCTSTR szText)
 +	{
 +		SendMessage(_hWnd, UM_CHANGEPOPUP, CPT_TEXTT, (LPARAM) mir_tcsdup(szText));
 +	}
 +};
 +
 +
 +/***********************************************************************************************************
 + * class CContactUpdater
 + ***********************************************************************************************************/
 +
 +class CContactUpdater : public CContactQueue
 +{
 +	CUpdProgress*		_pProgress;			// pointer to the progress dialog/popup
 +	HANDLE				_hProtoAckEvent;	// handle to protocol ack notification
 +	HANDLE				_hContact;			// current contact being refreshed
 +	PBYTE				_hContactAcks;		// array of acknoledgements for the current contact to wait for
 +	INT_PTR				_nContactAcks;		// number of acknoledgements for the current contact to wait for
 +
 +	MIRANDA_CPP_PLUGIN_API(CContactUpdater);
 +
 +	/**
 +	 * This is a callback dialog procedure, which is assigned the update progress dialog to
 +	 * gain control over certain messages.
 +	 *
 +	 * @param	hWnd		- HWND window handle of the progress dialog
 +	 * @param	uMsg		- message sent to the dialog
 +	 * @param	wParam		- message specific parameter
 +	 * @param	lParam		- message specific parameter
 +	 * @param	u			- This is a parameter assigned by the CUpdProgress' constructur.
 +	 *						  In this case it is a pointer to this class's object.
 +	 *
 +	 * @return	This method returns 0.
 +	 **/
 +	static INT DlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, CContactUpdater* u) 
 +	{
 +		switch (uMsg) 
 +		{
 +
 +		/**
 +		 * User has clicked on the skip or cancel button.
 +		 **/
 +		case UM_POPUPACTION:
 +		case WM_COMMAND: 
 +			{
 +				if (PtrIsValid(u))
 +				{
 +					switch (LOWORD(wParam))
 +					{
 +					case IDCANCEL:
 +						{
 +							if (HIWORD(wParam) == BN_CLICKED) 
 +							{
 +								u->Cancel();
 +							}
 +						}
 +					}
 +				}
 +			}
 +		}
 +		return 0;
 +	}
 +
 +	/**
 +	 * This method handles ack broadcasts from the protocols to determine,
 +	 * whether a contact's information set's update is complete to continue
 +	 * with the next faster. By default the queue is configured to wait
 +	 * about 15s between contacts. If the protocol sends a complete ack,
 +	 * the time is shortend to 4s.
 +	 *
 +	 * @param	wParam		- not used
 +	 * @param	ack			- pointer to an ACKDATA structure containing all 
 +	 *						  data for the acknoledgement.
 +	 *
 +	 * @return	nothing
 +	 **/
 +	INT __cdecl OnProtoAck(WPARAM wParam, ACKDATA *ack)
 +	{
 +		if (ack && ack->cbSize == sizeof(ACKDATA) && ack->hContact == _hContact && ack->type == ACKTYPE_GETINFO)
 +		{
 +			if (ack->hProcess || ack->lParam) 
 +			{
 +				if (!_hContactAcks)
 +				{
 +					_nContactAcks = (INT_PTR)ack->hProcess;
 +					_hContactAcks = (PBYTE)mir_calloc(sizeof(BYTE) * (INT_PTR)ack->hProcess);
 +				}
 +				if (ack->result == ACKRESULT_SUCCESS || ack->result == ACKRESULT_FAILED)
 +				{
 +					_hContactAcks[ack->lParam] = 1;
 +				}
 +
 +				for (INT i = 0; i < _nContactAcks; i++)
 +				{
 +					if (_hContactAcks[i] == 0)
 +					{
 +						return 0;
 +					}
 +				}
 +			}
 +			// don't wait the full time, but continue immitiatly
 +			ContinueWithNext();
 +		}
 +		return 0;
 +	}
 +
 +	/**
 +	 * This method is called just before the worker thread is going to suspend,
 +	 * if the queue is empty.
 +	 *
 +	 * @param	none
 +	 *
 +	 * @return	nothing
 +	 **/
 +	virtual VOID OnEmpty() 
 +	{
 +		// This was the last contact, so destroy the progress window.
 +		if (_hProtoAckEvent)
 +		{
 +			UnhookEvent(_hProtoAckEvent);
 +			_hProtoAckEvent = NULL;
 +		}
 +
 +		// free up last ackresult array
 +		MIR_FREE(_hContactAcks);
 +		_nContactAcks = 0;
 +		_hContact = NULL;
 +
 +		// close progress bar
 +		if (_pProgress)
 +		{
 +			_pProgress->Destroy();
 +
 +			delete _pProgress;
 +			_pProgress = NULL;
 +		}
 +
 +		// reset menu
 +		if (hMenuItemRefresh)
 +		{
 +			CLISTMENUITEM clmi;
 +
 +			clmi.cbSize = sizeof(CLISTMENUITEM);
 +			clmi.flags = CMIM_NAME|CMIM_ICON;
 +			clmi.pszName = LPGEN("Refresh Contact Details");
 +			clmi.hIcon = IcoLib_GetIcon(ICO_BTN_UPDATE);
 +			CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hMenuItemRefresh, (LPARAM)&clmi);
 +		}
 +	}
 +
 +	/**
 +	 * This virtual method is called by the derived CContactQueue class,
 +	 * if an action is requested for a queue item.
 +	 *
 +	 * @param	hContact	- the handle of the contact an action is requested for
 +	 * @param	param		- not used here
 +	 *
 +	 * @return	nothing
 +	 **/
 +	virtual VOID Callback(HANDLE hContact, PVOID param)
 +	{
 +		LPSTR	pszProto	= DB::Contact::Proto(hContact);
 +
 +		if (pszProto && pszProto[0])
 +		{
 +			MIR_FREE(_hContactAcks);
 +			_nContactAcks = 0;
 +			_hContact = hContact;
 +
 +			if (!_hProtoAckEvent)
 +			{
 +				_hProtoAckEvent = (HANDLE) ThisHookEvent(ME_PROTO_ACK, (EVENTHOOK) &CContactUpdater::OnProtoAck);
 +			}
 +
 +			if (_pProgress)
 +			{
 +				_pProgress->SetTextParam(TranslateT("[b]%s (%S)...[/b]\n%d Contacts remaning"),
 +					DB::Contact::DisplayName(_hContact), pszProto, Size());
 +			}
 +			if (IsProtoOnline(pszProto))
 +			{
 +				INT i;
 +				for (i = 0; i < 3 && CallContactService(hContact, PSS_GETINFO, 0, 0); i++)
 +				{
 +					Sleep(3000);
 +				}
 +			}
 +		}
 +	}
 +
 +public:
 +
 +	/**
 +	 * This is the default constructor
 +	 *
 +	 **/
 +	CContactUpdater() : CContactQueue()
 +	{
 +		_hContactAcks	= NULL;
 +		_nContactAcks	= 0;
 +		_hContact		= NULL;
 +		_pProgress		= NULL;
 +		_hProtoAckEvent	= NULL;
 +	}
 +
 +	/**
 +	 *
 +	 *
 +	 **/
 +	~CContactUpdater()
 +	{
 +		RemoveAll();
 +		OnEmpty();
 +	}
 +
 +	/**
 +	 *
 +	 *
 +	 **/
 +	BOOL QueueAddRefreshContact(HANDLE hContact, INT iWait)
 +	{
 +		LPSTR pszProto = DB::Contact::Proto(hContact);
 +
 +		if ((mir_strcmp(pszProto, "Weather")!=0) && 
 +				(mir_strcmp(pszProto, "MetaContacts")!=0) && 
 +				IsProtoOnline(pszProto))
 +		{
 +			return Add(iWait, hContact);
 +		}
 +		return 0;
 +	}
 +
 +	/**
 +	 *
 +	 *
 +	 **/
 +	VOID RefreshAll()
 +	{
 +		HANDLE		hContact;
 +		INT				iWait;
 +
 +		for (hContact = DB::Contact::FindFirst(),	iWait = 100;
 +				 hContact != NULL;
 +				 hContact = DB::Contact::FindNext(hContact))
 +		{
 +			if (QueueAddRefreshContact(hContact, iWait))
 +			{
 +				iWait += 5000;
 +			}
 +		}
 +		if (Size() && !_pProgress)
 +		{
 +			if (ServiceExists(MS_POPUP_CHANGETEXTT) && DB::Setting::GetByte("PopupProgress", FALSE))
 +			{
 +				_pProgress = new CPopupUpdProgress(this);
 +			}
 +			else
 +			{
 +				_pProgress = new CDlgUpdProgress(this);
 +			}
 +
 +			_pProgress->Create(TranslateT("Refresh Contact Details"), (PUpdCallback) CContactUpdater::DlgProc);
 +			_pProgress->SetText(TranslateT("Preparing..."));
 +		}
 +
 +		// if there are contacts in the queue, change the main menu item to indicate it is meant for canceling.
 +		if (hMenuItemRefresh && Size() > 0)
 +		{
 +			CLISTMENUITEM clmi;
 +
 +			clmi.cbSize = sizeof(CLISTMENUITEM);
 +			clmi.flags = CMIM_NAME|CMIM_ICON;
 +			clmi.pszName = LPGEN("Abort Refreshing Contact Details");
 +			clmi.hIcon = IcoLib_GetIcon(ICO_BTN_CANCEL);
 +			CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hMenuItemRefresh, (LPARAM) &clmi);
 +		}
 +	}
 +
 +	/**
 +	 *
 +	 *
 +	 **/
 +	VOID Cancel()
 +	{
 +		RemoveAll();
 +		ContinueWithNext();
 +	}
 +
 +};
 +
 +static CContactUpdater	*ContactUpdater = NULL;
 +
 +/***********************************************************************************************************
 + * common helper functions
 + ***********************************************************************************************************/
 +
 +/**
 + * This function checks, whether at least one protocol is online!
 + *
 + * @param	none
 + *
 + * @retval	TRUE		- At least one protocol is online.
 + * @retval	FALSE		- All protocols are offline.
 + **/
 +static BOOL IsMirandaOnline()
 +{
 +	PROTOACCOUNT **pAcc;
 +	INT i, nAccCount;
 +	BOOL bIsOnline = FALSE;
 +
 +	if (MIRSUCCEEDED(ProtoEnumAccounts(&nAccCount, &pAcc)))
 +	{
 +		for (i = 0; (i < nAccCount) && !bIsOnline; i++) 
 +		{
 +			bIsOnline |= (IsProtoAccountEnabled(pAcc[i]) && IsProtoOnline(pAcc[i]->szModuleName));
 +		}
 +	}
 +	return bIsOnline;
 +}
 +
 +/***********************************************************************************************************
 + * services
 + ***********************************************************************************************************/
 +
 +/**
 + * This is the service function being called by MS_USERINFO_REFRESH.
 + * It adds each contact, whose protocol is online, to the queue of contacts to refresh.
 + * The queue is running a separate thread, which is responsible for requesting the contact information
 + * one after another with a certain time to wait in between.
 + *
 + * @param	wParam		- not used
 + * @param	lParam		- not used
 + *
 + * @return	This service function always returns 0.
 + **/
 +static INT_PTR RefreshService(WPARAM wParam, LPARAM lParam)
 +{
 +	try
 +	{
 +		if (IsMirandaOnline())
 +		{
 +			if (!ContactUpdater)
 +			{
 +				ContactUpdater = new CContactUpdater();
 +			}
 +
 +			if (ContactUpdater->Size() == 0)
 +			{
 +				ContactUpdater->RefreshAll();
 +			}
 +			else if (IDYES == MsgBox(NULL, MB_YESNO|MB_ICON_QUESTION, LPGENT("Refresh Contact Details"), NULL, 
 +				LPGENT("Do you want to cancel the current refresh procedure?")))
 +			{
 +				ContactUpdater->Cancel();
 +			}
 +		}
 +		else
 +		{
 +			MsgErr(NULL, LPGENT("Miranda must be online for refreshing contact information!"));
 +		}
 +	}
 +	catch(...)
 +	{
 +		MsgErr(NULL, LPGENT("The function caused an exception!"));
 +	}
 +	return 0;
 +}
 +
 +/***********************************************************************************************************
 + * events
 + ***********************************************************************************************************/
 +
 +/**
 + *
 + *
 + **/
 +static INT OnContactAdded(WPARAM wParam, LPARAM lParam)
 +{
 +	try
 +	{
 +		DWORD dwStmp;
 +
 +		dwStmp = DB::Setting::GetDWord((HANDLE)wParam, USERINFO, SET_CONTACT_ADDEDTIME, 0);
 +		if (!dwStmp)
 +		{
 +			MTime mt;
 +			
 +			mt.GetLocalTime();
 +			mt.DBWriteStamp((HANDLE)wParam, USERINFO, SET_CONTACT_ADDEDTIME);
 +
 +			// create updater, if not yet exists
 +			if (!ContactUpdater)
 +			{
 +				ContactUpdater = new CContactUpdater();
 +			}
 +			
 +			// add to the end of the queue
 +			ContactUpdater->AddIfDontHave(
 +				(ContactUpdater->Size() > 0) 
 +					? max(ContactUpdater->Get(ContactUpdater->Size() - 1)->check_time + 15000, 4000) 
 +					: 4000,
 +					(HANDLE)wParam);
 +		}
 +	}
 +	catch(...)
 +	{
 +		MsgErr(NULL, LPGENT("The function caused an exception!"));
 +	}
 +	return 0;
 +}
 +
 +/**
 + * Miranda is going to shutdown soon, so any panding contact information refresh is to be terminated
 + * and the queue object must be deleted. Further refresh requests will be ignored.
 + *
 + * @param	wParam		- not used
 + * @param	lParam		- not used
 + *
 + * @return	This function always returns 0.
 + **/
 +static INT OnPreShutdown(WPARAM, LPARAM)
 +{
 +	if(ContactUpdater) {
 +		delete ContactUpdater;
 +		ContactUpdater = 0;
 +	}
 +	//MIR_DELETE(ContactUpdater);
 +	return 0;
 +}
 +
 +/***********************************************************************************************************
 + * initialization
 + ***********************************************************************************************************/
 +
 +/**
 + * This function initially loads the module uppon startup.
 + **/
 +VOID SvcRefreshContactInfoLoadModule(VOID)
 +{
 +	CreateServiceFunction(MS_USERINFO_REFRESH, RefreshService);
 +	HookEvent(ME_SYSTEM_PRESHUTDOWN, OnPreShutdown);
 +	HookEvent(ME_DB_CONTACT_ADDED, OnContactAdded);
 +
 +	HOTKEYDESC hk = { 0 };
 +	hk.cbSize = sizeof(HOTKEYDESC);
 +	hk.pszSection = MODNAME;
 +	hk.pszName = "RefreshContactDetails";
 +	hk.pszDescription = LPGEN("Refresh Contact Details");
 +	hk.pszService = MS_USERINFO_REFRESH;
 +	Hotkey_Register(&hk);
 +}
  | 
