Пример #1
0
INT_PTR __cdecl CVkProto::SvcBanUser(WPARAM hContact, LPARAM)
{
	debugLogA("CVkProto::SvcBanUser");
	LONG userID = getDword(hContact, "ID", -1);
	if (!IsOnline() || userID == -1 || userID == VK_FEED_USER)
		return 1;

	CMStringA code(FORMAT, "var userID=\"%d\";API.account.banUser({\"user_id\":userID});", userID);
	CMString tszVarWarning;

	if (m_bReportAbuse) {
		debugLogA("CVkProto::SvcBanUser m_bReportAbuse = true");
		code += "API.users.report({\"user_id\":userID,type:\"spam\"});";
		tszVarWarning = TranslateT(" report abuse on him/her");
	}
	if (m_bClearServerHistory) {
		debugLogA("CVkProto::SvcBanUser m_bClearServerHistory = true");
		code += "API.messages.deleteDialog({\"user_id\":userID,count:10000});";
		if (!tszVarWarning.IsEmpty())
			tszVarWarning.AppendChar(L',');
		tszVarWarning += TranslateT(" clear server history with him/her");
	}
	if (m_bRemoveFromFrendlist) {
		debugLogA("CVkProto::SvcBanUser m_bRemoveFromFrendlist = true");
		code += "API.friends.delete({\"user_id\":userID});";
		if (!tszVarWarning.IsEmpty())
			tszVarWarning.AppendChar(L',');
		tszVarWarning += TranslateT(" remove him/her from your friend list");
	}
	if (m_bRemoveFromClist) {
		debugLogA("CVkProto::SvcBanUser m_bRemoveFromClist = true");
		if (!tszVarWarning.IsEmpty())
			tszVarWarning.AppendChar(L',');
		tszVarWarning += TranslateT(" remove him/her from your contact list");
	}

	if (!tszVarWarning.IsEmpty())
		tszVarWarning += ".\n";
	code += "return 1;";

	ptrT ptszNick(db_get_tsa(hContact, m_szModuleName, "Nick"));
	CMString ptszMsg(FORMAT, TranslateT("Are you sure to ban %s? %s%sContinue?"),
		IsEmpty(ptszNick) ? TranslateT("(Unknown contact)") : ptszNick, 
		tszVarWarning.IsEmpty() ? _T(" ") : TranslateT("\nIt will also"),
		tszVarWarning.IsEmpty() ? _T("\n") : tszVarWarning);

	if (IDNO == MessageBox(NULL, ptszMsg, TranslateT("Attention!"), MB_ICONWARNING | MB_YESNO))
		return 1;
	
	Push(new AsyncHttpRequest(this, REQUEST_GET, "/method/execute.json", true, &CVkProto::OnReceiveSmth)
		<< CHAR_PARAM("code", code)
		<< VER_API);

	if (m_bRemoveFromClist)
		CallService(MS_DB_CONTACT_DELETE, (WPARAM)hContact);

	return 0;
}
Пример #2
0
void FormatTime(const SYSTEMTIME *st, const TCHAR *szFormat, TCHAR *szDest, size_t cbDest)
{
	if (szDest == NULL || cbDest == 0) return;

	CMString tszTemp;

	for (const TCHAR* pFormat = szFormat; *pFormat; ++pFormat) {
		DWORD fmt = 0;
		bool date = false, iso = false;
		switch (*pFormat) {
		case 't':
			fmt = TIME_NOSECONDS;
			date = false;
			break;

		case 's':
			fmt = 0;
			date = false;
			break;

		case 'm':
			fmt = TIME_NOMINUTESORSECONDS;
			date = false;
			break;

		case 'd':
			fmt = DATE_SHORTDATE;
			date = true;
			break;

		case 'D':
			fmt = DATE_LONGDATE;
			date = true;
			break;

		case 'I':
			iso = true;
			break;

		default:
			tszTemp.AppendChar(*pFormat);
			continue;
		}

		TCHAR dateTimeStr[64];
		if (iso)
			tszTemp.AppendFormat(_T("%d-%02d-%02dT%02d:%02d:%02dZ"), st->wYear, st->wMonth, st->wDay, st->wHour, st->wMinute, st->wSecond);
		else if (date) {
			GetDateFormat(LOCALE_USER_DEFAULT, fmt, st, NULL, dateTimeStr, _countof(dateTimeStr));
			tszTemp.Append(dateTimeStr);
		}
		else {
			GetTimeFormat(LOCALE_USER_DEFAULT, fmt, st, NULL, dateTimeStr, _countof(dateTimeStr));
			tszTemp.Append(dateTimeStr);
		}
	}

	_tcsncpy_s(szDest, cbDest, tszTemp, _TRUNCATE);
}
Пример #3
0
void CVkProto::OnReceiveUserInfo(NETLIBHTTPREQUEST *reply, AsyncHttpRequest *pReq)
{
	debugLogA("CVkProto::OnReceiveUserInfo %d", reply->resultCode);
	if (reply->resultCode != 200)
		return;

	JSONROOT pRoot;
	JSONNODE *pResponse = CheckJsonResponse(pReq, reply, pRoot);
	if (pResponse == NULL)
		return;

	for (size_t i=0; ; i++) {
		JSONNODE *pRecord = json_at(pResponse, i);
		if (pRecord == NULL) break;

		LONG userid = json_as_int( json_get(pRecord, "uid"));
		if (userid == 0)
			return;

		MCONTACT hContact;
		if (userid == m_myUserId)
			hContact = NULL;
		else if ((hContact = FindUser(userid, false)) == NULL)
			return;

		CMString tszNick;
		ptrT szValue( json_as_string( json_get(pRecord, "first_name")));
		if (szValue) {
			setTString(hContact, "FirstName", szValue);
			tszNick.Append(szValue);
			tszNick.AppendChar(' ');
		}

		if (szValue = json_as_string( json_get(pRecord, "last_name"))) {
			setTString(hContact, "LastName", szValue);
			tszNick.Append(szValue);
		}

		if (!tszNick.IsEmpty())
			setTString(hContact, "Nick", tszNick);
	
		setByte(hContact, "Gender", json_as_int( json_get(pRecord, "sex")) == 2 ? 'M' : 'F');
	
		if (szValue = json_as_string( json_get(pRecord, "bdate"))) {
			int d, m, y;
			if ( _stscanf(szValue, _T("%d.%d.%d"), &d, &m, &y) == 3) {
				setByte(hContact, "BirthDay", d);
				setByte(hContact, "BirthMonth", m);
				setWord(hContact, "BirthYear", y);
			}
		}

		szValue = json_as_string( json_get(pRecord, "photo_medium"));
		SetAvatarUrl(hContact, szValue);
	}
}
Пример #4
0
NFAUNode* WCPattern::parseQuote()
{
	bool done = 0;
	CMString s;

	while (!done) {
		if (curInd >= pattern.GetLength()) {
			raiseError();
			done = 1;
		}
		else if (pattern.Mid(curInd, 2) == L"\\E") {
			curInd += 2;
			done = 1;
		}
		else if (pattern[curInd] == '\\') {
			s.AppendChar(pattern[++curInd]);
			++curInd;
		}
		else s.AppendChar(pattern[curInd++]);
	}
	if ((flags & WCPattern::CASE_INSENSITIVE) != 0) return registerNode(new NFACIQuoteUNode(s));
	return registerNode(new NFAQuoteUNode(s));
}
Пример #5
0
/**
 * normalize the status message with proper cr/lf sequences.
 * @param src TCHAR*:		original status message
 * @param fStripAll bool:	strip all cr/lf sequences and replace them with spaces (use for title bar)
 * @return TCHAR*:			converted status message. CALLER is responsible to mir_free it, MUST use mir_free()
 */
TCHAR* CContactCache::getNormalizedStatusMsg(const TCHAR *src, bool fStripAll)
{
	if (src == 0 || mir_tstrlen(src) < 2)
		return 0;

	CMString dest;

	for (int i = 0; src[i] != 0; i++) {
		if (src[i] == 0x0d || src[i] == '\t')
			continue;
		if (i && src[i] == (TCHAR)0x0a) {
			if (fStripAll) {
				dest.AppendChar(' ');
				continue;
			}
			dest.AppendChar('\n');
			continue;
		}
		dest.AppendChar(src[i]);
	}

	return mir_tstrndup(dest, dest.GetLength());
}
Пример #6
0
LPTSTR CJabberProto::GetResourceList(LPCTSTR jid)
{
	if (jid == NULL)
		return NULL;

	mir_cslock lck(m_csLists);
	JABBER_LIST_ITEM *item = NULL;
	if ((item = ListGetItemPtr(LIST_VCARD_TEMP, jid)) == NULL)
		item = ListGetItemPtr(LIST_ROSTER, jid);
	if (item == NULL)
		return NULL;

	if (!item->arResources.getCount())
		return NULL;

	CMString res;
	for (int i=0; i < item->arResources.getCount(); i++) {
		res.Append(item->arResources[i]->m_tszResourceName);
		res.AppendChar(0);
	}
	res.AppendChar(0);

	return mir_tstrndup(res, res.GetLength());
}
Пример #7
0
static void FilterGetStrings(CMString &filter, BOOL xml, BOOL swf)
{
	filter.AppendFormat(_T("%s (*.bmp;*.jpg;*.gif;*.png"), TranslateT("All Files"));
	if (swf) filter.Append(_T(";*.swf"));
	if (xml) filter.Append(_T(";*.xml"));

	filter.AppendFormat(_T(")%c*.BMP;*.RLE;*.JPG;*.JPEG;*.GIF;*.PNG"), 0);
	if (swf) filter.Append(_T(";*.SWF"));
	if (xml) filter.Append(_T(";*.XML"));
	filter.AppendChar(0);

	filter.AppendFormat(_T("%s (*.bmp;*.rle)%c*.BMP;*.RLE%c"), TranslateT("Windows Bitmaps"), 0, 0);
	filter.AppendFormat(_T("%s (*.jpg;*.jpeg)%c*.JPG;*.JPEG%c"), TranslateT("JPEG Bitmaps"), 0, 0);
	filter.AppendFormat(_T("%s (*.gif)%c*.GIF%c"), TranslateT("GIF Bitmaps"), 0, 0);
	filter.AppendFormat(_T("%s (*.png)%c*.PNG%c"), TranslateT("PNG Bitmaps"), 0, 0);

	if (swf)
		filter.AppendFormat(_T("%s (*.swf)%c*.SWF%c"), TranslateT("Flash Animations"), 0, 0);

	if (xml)
		filter.AppendFormat(_T("%s (*.xml)%c*.XML%c"), TranslateT("XML Files"), 0, 0);

	filter.AppendChar(0);
}
Пример #8
0
MIR_CORE_DLL(void) Bitmap_GetFilter(TCHAR *dest, size_t destLen)
{
	if (dest == NULL)
		return;

	CMString filter;
	filter.AppendFormat(_T("%s (*.bmp;*.jpg;*.gif;*.png)%c*.BMP;*.RLE;*.JPG;*.JPEG;*.GIF;*.PNG%c"), TranslateT("All Files"), 0, 0);
	filter.AppendFormat(_T("%s (*.bmp;*.rle)%c*.BMP;*.RLE%c"), TranslateT("Windows bitmaps"), 0, 0);
	filter.AppendFormat(_T("%s (*.jpg;*.jpeg)%c*.JPG;*.JPEG%c"), TranslateT("JPEG bitmaps"), 0, 0);
	filter.AppendFormat(_T("%s (*.gif)%c*.GIF%c"), TranslateT("GIF bitmaps"), 0, 0);
	filter.AppendFormat(_T("%s (*.png)%c*.PNG%c"), TranslateT("PNG bitmaps"), 0, 0);
	filter.AppendChar(0);

	_tcsncpy_s(dest, destLen, filter, filter.GetLength());
}
Пример #9
0
// look behind should interpret everything as a literal (except \\) since the
// pattern must have a concrete length
NFAUNode* WCPattern::parseBehind(const bool pos, NFAUNode ** end)
{
	CMString t;
	while (curInd < pattern.GetLength() && pattern[curInd] != ')') {
		wchar_t ch = pattern[curInd++];
		if (ch == '\\') {
			if (curInd + 1 >= pattern.GetLength()) {
				raiseError();
				return *end = registerNode(new NFACharUNode(' '));
			}
			ch = pattern[curInd++];
		}
		t.AppendChar(ch);
	}
	if (curInd >= pattern.GetLength() || pattern[curInd] != ')') raiseError();
	else ++curInd;
	return *end = registerNode(new NFALookBehindUNode(t, pos));
}
Пример #10
0
CMString WCPattern::parseEscape(bool & inv, bool & quo)
{
	wchar_t ch = pattern[curInd++];
	CMString classes;

	if (curInd > pattern.GetLength()) {
		raiseError();
		return "";
	}

	quo = 0;
	inv = 0;
	switch (ch) {
		case 'p': classes = parsePosix();                                                         break;
		case 'P': classes = L"!!"; classes += parsePosix();                                        break;
		case 'd': classes = L"0123456789";                                                         break;
		case 'D': classes = L"!!0123456789";                                                       break;
		case 's': classes = L" \t\r\n\f";                                                          break;
		case 'S': classes = L"!! \t\r\n\f";                                                        break;
		case 'w': classes = L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";    break;
		case 'W': classes = L"!!abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";  break;
		case '0': classes = parseOctal(); break;
		case 'x': classes = parseHex();   break;
		  
		case 'Q': quo = 1;        break;
		case 't': classes = L"\t"; break;
		case 'r': classes = L"\r"; break;
		case 'n': classes = L"\n"; break;
		case 'f': classes = L"\f"; break;
		case 'a': classes = L"\a"; break;
		case 'e': classes = L"\r"; break;
		default:  classes.AppendChar(ch); break;
	}

	if (classes.Mid(0, 2) == L"!!") {
		classes = classes.Mid(2);
		inv = 1;
	}
	return classes;
}
Пример #11
0
CMString CVkProto::GetAttachmentDescr(JSONNODE *pAttachments)
{
	CMString res;
	res.AppendChar('\n');
	res += TranslateT("Attachments:");
	res.AppendChar('\n');
	JSONNODE *pAttach;
	for (int k = 0; (pAttach = json_at(pAttachments, k)) != NULL; k++) {
		res.AppendChar('\t');
		ptrT ptszType(json_as_string(json_get(pAttach, "type")));
		if (!lstrcmp(ptszType, _T("photo"))) {
			JSONNODE *pPhoto = json_get(pAttach, "photo");
			if (pPhoto == NULL) continue;

			ptrT ptszLink;
			for (int i = 0; i < SIZEOF(szImageTypes); i++) {
				JSONNODE *n = json_get(pPhoto, szImageTypes[i]);
				if (n != NULL) {
					ptszLink = json_as_string(n);
					break;
				}
			}

			int iWidth = json_as_int(json_get(pPhoto, "width"));
			int iHeight = json_as_int(json_get(pPhoto, "height"));
			res.AppendFormat(_T("%s: %s (%dx%d)"), TranslateT("Photo"), ptszLink, iWidth, iHeight);
		}
		else if (!lstrcmp(ptszType, _T("audio"))) {
			JSONNODE *pAudio = json_get(pAttach, "audio");
			if (pAudio == NULL) continue;

			int  aid = json_as_int(json_get(pAudio, "aid"));
			int  ownerID = json_as_int(json_get(pAudio, "owner_id"));
			ptrT ptszArtist(json_as_string(json_get(pAudio, "artist")));
			ptrT ptszTitle(json_as_string(json_get(pAudio, "title")));
			res.AppendFormat(_T("%s: (%s - %s) - http://vk.com/audio%d_%d"),
				TranslateT("Audio"), ptszArtist, ptszTitle, ownerID, aid);
		}
		else if (!lstrcmp(ptszType, _T("video"))) {
			JSONNODE *pVideo = json_get(pAttach, "video");
			if (pVideo == NULL) continue;

			ptrT ptszTitle(json_as_string(json_get(pVideo, "title")));
			int  vid = json_as_int(json_get(pVideo, "vid"));
			int  ownerID = json_as_int(json_get(pVideo, "owner_id"));
			res.AppendFormat(_T("%s: %s - http://vk.com/video%d_%d"),
				TranslateT("Video"), ptszTitle, ownerID, vid);
		}
		else if (!lstrcmp(ptszType, _T("doc"))) {
			JSONNODE *pDoc = json_get(pAttach, "doc");
			if (pDoc == NULL) continue;

			ptrT ptszTitle(json_as_string(json_get(pDoc, "title")));
			ptrT ptszUrl(json_as_string(json_get(pDoc, "url")));
			res.AppendFormat(_T("%s: (%s) - %s"),
				TranslateT("Document"), ptszTitle, ptszUrl);
		}
		else if (!lstrcmp(ptszType, _T("wall"))) {
			JSONNODE *pWall = json_get(pAttach, "wall");
			if (pWall == NULL) continue;

			ptrT ptszText(json_as_string(json_get(pWall, "text")));
			int  id = json_as_int(json_get(pWall, "id"));
			int  fromID = json_as_int(json_get(pWall, "from_id"));
			res.AppendFormat(_T("%s: %s - http://vk.com/wall%d_%d"),
				TranslateT("Wall post"), ptszText, fromID, id);
		}
		else res.AppendFormat(TranslateT("Unsupported or unknown attachment type: %s"), ptszType);

		res.AppendChar('\n');
	}

	return res;
}
Пример #12
0
void CVkProto::OnReceiveFriends(NETLIBHTTPREQUEST *reply, AsyncHttpRequest *pReq)
{
	debugLogA("CVkProto::OnReceiveFriends %d", reply->resultCode);
	if (reply->resultCode != 200)
		return;

	JSONROOT pRoot;
	JSONNODE *pResponse = CheckJsonResponse(pReq, reply, pRoot), *pInfo;
	if (pResponse == NULL)
		return;

	bool bCleanContacts = getByte("AutoClean", 0) != 0;
	LIST<void> arContacts(10, PtrKeySortT);
	if (bCleanContacts)
		for (MCONTACT hContact = db_find_first(m_szModuleName); hContact; hContact = db_find_next(hContact, m_szModuleName))
			if (!isChatRoom(hContact))
				arContacts.insert((HANDLE)hContact);

	for (int i = 0; (pInfo = json_at(pResponse, i)) != NULL; i++) {
		ptrT szValue(json_as_string(json_get(pInfo, "uid")));
		if (szValue == NULL)
			continue;

		CMString tszNick;
		MCONTACT hContact = FindUser(_ttoi(szValue), true);
		arContacts.remove((HANDLE)hContact);
		szValue = json_as_string(json_get(pInfo, "first_name"));
		if (szValue) {
			setTString(hContact, "FirstName", szValue);

			tszNick.Append(szValue);
			tszNick.AppendChar(' ');
		}

		if (szValue = json_as_string(json_get(pInfo, "last_name"))) {
			setTString(hContact, "LastName", szValue);
			tszNick.Append(szValue);
		}

		if (!tszNick.IsEmpty())
			setTString(hContact, "Nick", tszNick);

		szValue = json_as_string(json_get(pInfo, "photo_medium"));
		SetAvatarUrl(hContact, szValue);

		setWord(hContact, "Status", (json_as_int(json_get(pInfo, "online")) == 0) ? ID_STATUS_OFFLINE : ID_STATUS_ONLINE);

		int iValue = json_as_int(json_get(pInfo, "sex"));
		if (iValue)
			setByte(hContact, "Gender", (iValue == 2) ? 'M' : 'F');

		if ((iValue = json_as_int(json_get(pInfo, "timezone"))) != 0)
			setByte(hContact, "Timezone", iValue * -2);

		szValue = json_as_string(json_get(pInfo, "mobile_phone"));
		if (szValue && *szValue)
			setTString(hContact, "Cellular", szValue);
		szValue = json_as_string(json_get(pInfo, "home_phone"));
		if (szValue && *szValue)
			setTString(hContact, "Phone", szValue);
	}

	if (bCleanContacts)
		for (int i = 0; i < arContacts.getCount(); i++)
			CallService(MS_DB_CONTACT_DELETE, (WPARAM)arContacts[i], 0);
}
Пример #13
0
static LRESULT CALLBACK PluginListWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch (msg) {
	case WM_CHAR:
		if (wParam == '\b') {
			if (szFilter.GetLength() > 0)
				szFilter.Truncate(szFilter.GetLength() - 1);
		}
		else {
			szFilter.AppendChar(wParam);

			for (int i = 0; i < arPluginList.getCount(); i++) {
				PluginListItemData *p = arPluginList[i];
				if (!_tcsnicmp(szFilter, p->fileName, szFilter.GetLength())) {
					LVFINDINFO lvfi;
					lvfi.flags = LVFI_PARAM;
					lvfi.lParam = (LPARAM)p;
					int idx = ListView_FindItem(hwnd, 0, &lvfi);
					if (idx != -1) {
						ListView_SetItemState(hwnd, idx, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED);
						ListView_EnsureVisible(hwnd, idx, FALSE);
						if (timerID != 0)
							KillTimer(hwnd, timerID);
						timerID = SetTimer(hwnd, 1, 1500, 0);
						return TRUE;
					}
				}
			}

			szFilter.Truncate(szFilter.GetLength() - 1);
			MessageBeep((UINT)-1);
		}
		return TRUE;

	case WM_TIMER:
		if (wParam == 1) {
			KillTimer(hwnd, timerID);
			timerID = 0;
			szFilter.Empty();
		}
		break;

	case WM_LBUTTONDOWN:
		LVHITTESTINFO hi;
		hi.pt.x = LOWORD(lParam);
		hi.pt.y = HIWORD(lParam);
		ListView_SubItemHitTest(hwnd, &hi);
		// Dynamically load/unload a plugin
		if ((hi.iSubItem == 0) && (hi.flags & LVHT_ONITEMICON)) {
			LVITEM lvi = { 0 };
			lvi.mask = LVIF_IMAGE | LVIF_PARAM;
			lvi.stateMask = -1;
			lvi.iItem = hi.iItem;
			lvi.iSubItem = 0;
			if (ListView_GetItem(hwnd, &lvi)) {
				lvi.mask = LVIF_IMAGE;
				PluginListItemData *dat = (PluginListItemData*)lvi.lParam;
				if (lvi.iImage == 3) {
					// load plugin
					if (LoadPluginDynamically(dat)) {
						lvi.iImage = 2;
						ListView_SetItem(hwnd, &lvi);
					}
				}
				else if (lvi.iImage == 2) {
					// unload plugin
					if (UnloadPluginDynamically(dat)) {
						lvi.iImage = 3;
						ListView_SetItem(hwnd, &lvi);
					}
				}
				LoadStdPlugins();
			}
		}
	}

	return mir_callNextSubclass(hwnd, PluginListWndProc, msg, wParam, lParam);
}
Пример #14
0
TCHAR* fnTrayIconMakeTooltip(const TCHAR *szPrefix, const char *szProto)
{
	initcheck NULL;

	mir_cslock lck(trayLockCS);
	TCHAR *szSeparator = _T("\n");

	if (szProto == NULL) {
		if (accounts.getCount() == 0)
			return NULL;

		if (accounts.getCount() == 1)
			return cli.pfnTrayIconMakeTooltip(szPrefix, accounts[0]->szModuleName);

		CMString tszTip;

		if (szPrefix && szPrefix[0]) {
			if (!db_get_b(NULL, "CList", "AlwaysStatus", SETTING_ALWAYSSTATUS_DEFAULT)) {
				_tcsncpy_s(cli.szTip, MAX_TIP_SIZE, szPrefix, _TRUNCATE);
				return cli.szTip;
			}
			tszTip.Append(szPrefix);
		}

		for (int t = 0; t < accounts.getCount(); t++) {
			int i = cli.pfnGetAccountIndexByPos(t);
			if (i == -1)
				continue;

			PROTOACCOUNT *pa = accounts[i];
			if (!cli.pfnGetProtocolVisibility(pa->szModuleName))
				continue;

			TCHAR *szStatus = cli.pfnGetStatusModeDescription(CallProtoServiceInt(NULL, pa->szModuleName, PS_GETSTATUS, 0, 0), 0);
			if (!szStatus)
				continue;

			if (!tszTip.IsEmpty())
				tszTip.AppendChar('\n');
			if (mToolTipTrayTips) {
				tszTip.AppendFormat(_T("<b>%-12.12s</b>\t%s"), pa->tszAccountName, szStatus);

				ptrT ProtoXStatus(sttGetXStatus(pa->szModuleName));
				if (ProtoXStatus != NULL) {
					if (!tszTip.IsEmpty())
						tszTip.AppendChar('\n');
					tszTip.AppendFormat(_T("%-24.24s\n"), ProtoXStatus);
				}
			}
			else tszTip.AppendFormat(_T("%s %s"), pa->tszAccountName, szStatus);
		}

		_tcsncpy_s(cli.szTip, MAX_TIP_SIZE, tszTip, _TRUNCATE);
	}
	else {
		PROTOACCOUNT *pa = Proto_GetAccount(szProto);
		if (pa != NULL) {
			ptrT ProtoXStatus(sttGetXStatus(szProto));
			TCHAR *szStatus = cli.pfnGetStatusModeDescription(CallProtoServiceInt(NULL, szProto, PS_GETSTATUS, 0, 0), 0);
			if (szPrefix && szPrefix[0]) {
				if (db_get_b(NULL, "CList", "AlwaysStatus", SETTING_ALWAYSSTATUS_DEFAULT)) {
					if (mToolTipTrayTips) {
						if (ProtoXStatus != NULL)
							mir_sntprintf(cli.szTip, MAX_TIP_SIZE, _T("%s%s<b>%-12.12s</b>\t%s%s%-24.24s"), szPrefix, szSeparator, pa->tszAccountName, szStatus, szSeparator, ProtoXStatus);
						else
							mir_sntprintf(cli.szTip, MAX_TIP_SIZE, _T("%s%s<b>%-12.12s</b>\t%s"), szPrefix, szSeparator, pa->tszAccountName, szStatus);
					}
					else mir_sntprintf(cli.szTip, MAX_TIP_SIZE, _T("%s%s%s %s"), szPrefix, szSeparator, pa->tszAccountName, szStatus);
				}
				else mir_tstrncpy(cli.szTip, szPrefix, MAX_TIP_SIZE);
			}
			else {
				if (mToolTipTrayTips) {
					if (ProtoXStatus != NULL)
						mir_sntprintf(cli.szTip, MAX_TIP_SIZE, _T("<b>%-12.12s</b>\t%s\n%-24.24s"), pa->tszAccountName, szStatus, ProtoXStatus);
					else
						mir_sntprintf(cli.szTip, MAX_TIP_SIZE, _T("<b>%-12.12s</b>\t%s"), pa->tszAccountName, szStatus);
				}
				else mir_sntprintf(cli.szTip, MAX_TIP_SIZE, _T("%s %s"), pa->tszAccountName, szStatus);
			}
		}
	}

	return cli.szTip;
}