You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
RnQ/RnQ/utilLib.pas

3301 lines
92 KiB
Plaintext

{
This file is part of R&Q.
Under same license
}
unit utilLib;
{$I RnQConfig.inc}
interface
uses
Windows, SysUtils, Graphics, Dialogs, Classes, ExtCtrls, SyncObjs, Threading,
Forms, StdCtrls, Controls, Menus, ComCtrls, Messages, Types, JSON, DateUtils,
StrUtils, globalLib, outboxLib, RnQNet, RDGlobal, RnQZip,
events, ICQCommon, ICQConsts, ICQContacts, ICQSession, Murmur2, GR32;
{$I PubRTTI.inc}
type
TParams = TArray;
TAlphaWindows = (AW_CL, AW_CHAT, AW_BOTH);
TUpdateInfo = record
hasNew, beta: Boolean;
error, text, changelog, distrib: String;
end;
TPageControl = class(comctrls.TPageControl)
protected
procedure CNDrawitem(var Message: TWMDrawItem); message CN_DRAWITEM;
end;
TImgBytes = array of TBytes;
TJSONHelper = class helper for TJSONValue
public
function GetValueSafe(const Key: String; out Data: T): Boolean;
end;
function OnlFeature(const pr: TICQSession; check: Boolean = True): Boolean;
procedure UnsupportedFeature;
procedure ProcessOevent(oe: Toevent);
function GetShiftState: Integer;
function ExitFromAutoaway: Boolean;
function binToStatus(const bin: TBytes): byte;
function binToXStatus(const bin: TBytes): byte;
function unexistant(const uin: TUID): Boolean;
function isAbort(const pluginReply: AnsiString): Boolean;
procedure reloadCurrentLang();
function ShowUsers(ShowConflictMsg: Boolean = False): TUID;
function CheckAccPass(const uid: TUID; const db: String; var pPass: String): Boolean;
function CheckHistPass: Boolean;
function getLeadingInMsg(const s: string; ofs: integer = 1): string;
procedure applyUserCharset(f: Tfont);
{$IFDEF Use_Baloons}
procedure ShowBalloonEv(ev: Thevent);
{$ENDIF Use_Baloons}
function AddToNIL(c: TICQContact; IsBulk: Boolean = False): Boolean;
procedure NILifNIL(c: TICQContact; IsBulk: Boolean = False);
function behactionName(a: Tbehaction): string;
function DoLock: Boolean;
procedure DoUnlock;
procedure trayAction;
function chopAndRealizeEvent: Boolean;
procedure realizeEvents(const kind_: integer; c: TICQContact);
procedure realizeEvent(ev: Thevent);
procedure RemoveSWHotkeys;
function UpdateSWHotkeys: Boolean;
procedure hideTaskButtonIfUhave2;
function behave(ev: Thevent; kind: integer = -1 { ; const info: Ansistring='' } ): Boolean;
procedure StartMainTimer;
procedure StopMainTimer;
procedure contactCreation(c: TICQContact);
procedure contactDestroying(c: TICQContact);
procedure clearDB(db: TRnQCList);
procedure freeDB(var db: TRnQCList);
function IsSpam(var Wrd: String; c: TICQContact; const Msg: String = ''; Flags: DWord = 0): Boolean;
function FilterRefuse(c: TICQContact; const Msg: String = ''; Flags: DWord = 0; ev: THevent = nil): Boolean;
function rosterImgNameFor(c: TICQContact): TPicName;
procedure setAppBarSize;
procedure Check4Update;
function AutoCheckUpdates: Boolean;
function CheckUpdates(var UpdateInfo: TUpdateInfo; Auto: Boolean = False): Boolean;
function applyVars(c: TICQContact; const s: String; fromAM: Boolean = False): String;
function getXStatusMsgFor(c: TICQContact): string;
procedure toggleOnlyOnline;
procedure OpenURL(const pURL: String); OverLoad;
procedure OpenICQURL(const pURL: String);
function enterUinDlg(const proto: TICQSession; var uin: TUID; const title: string = ''): Boolean;
procedure SendProtoMsg(var oe: Toevent);
procedure AddOutgoingMessage(Cnt: TICQContact; const Text: String; const Binary: RawByteString; Flags: DWord);
procedure SendEmail2Mail(const email: String);
function childParent(child, parent: integer): Boolean;
procedure myBeep;
procedure sortCL(cl: TRnQCList);
procedure sortCLbyGroups(cl: TRnQCList);
procedure UpdateViewInfo(c: TICQContact);
procedure openSendContacts(dest: TICQContact);
function isEmailAddress(const s: string; start: integer): integer;
procedure notAvailable;
// strings
function mb(q: extended): string;
procedure onlyDigits(obj: Tobject); overload;
// icq communication
function AddToIgnoreList(c: TICQContact; const LocalOnly: Boolean = False): Boolean;
procedure RemoveFromIgnorelist(c: TICQContact);
function addToQuietlist(c: TICQContact): Boolean;
procedure removeFromQuietlist(c: TICQContact);
procedure RemoveFromRoster(c: TICQContact; const WithHistory: Boolean = False);
function AddToRoster(c: TICQContact; IsLocal: Boolean = False): boolean; overload;
function AddToRoster(c: TICQContact; group: Integer; IsLocal: Boolean = True): Boolean; overload;
function DoConnect: Boolean;
// convert
function ints2cl(proto: TICQSession; a: TintegerDynArray): TRnQCList;
function statusDrawExt(const DC: HDC; const x, y: integer; const s: byte; const inv: Boolean = False): TSize;
function beh2str(kind: integer): RawByteString;
procedure str2beh(const b, s: RawByteString); overload;
function str2beh(s: AnsiString): Tbehaviour; overload;
function str2status(const s: AnsiString): byte;
function str2visibility(const s: AnsiString): Tvisibility;
function db2strU(db: TRnQCList): RawByteString;
// window management
procedure ToggleMainFormBorder(SetBorder: Boolean = False; HasBorder: Boolean = True);
procedure MainFormHandleUpdate;
procedure dockSet(var r: Trect); overload;
procedure dockSet; overload;
procedure showAuthreq(c: TICQContact; msg: string);
procedure HideForm(Frm: Tform);
procedure ShowForm(WhatForm: TWhatForm; const Page: String = ''; Mode: TFrmViewMode = vmFull; Who: TICQContact = nil);
function PrefIsVisiblePage(const pf: String): Boolean;
procedure RestoreForegroundWindow;
procedure ApplyTransparency(Window: TAlphaWindows = AW_BOTH; Forced: Integer = -1);
//procedure applyDocking(Undock: Boolean = False);
function whatStatusPanel(statusbar: Tstatusbar; x: integer): integer;
// file management
function delSUBtree(subPath: string): Boolean;
function deltree(path: string): Boolean;
function deleteFromTo(const fn: string; from, to_: integer): Boolean;
procedure saveAllListsSync(const uPath: String; const pr: TICQSession; pProxys: Tarrproxy);
procedure saveAllListsAsync(const uPath: String; const pr: TICQSession; pProxys: Tarrproxy);
function saveAllLists(const uPath: String; const pr: TICQSession; pProxys: Tarrproxy): Boolean;
function loadDB(zp: TZipFile; pCheckGroups: Boolean): Boolean;
procedure loadLists(const pr: TICQSession; zp: TZipFile; const uPath: String);
procedure LoadExtSts(zp: TZipFile);
procedure loadSpamQuests(zp: TZipFile);
procedure LoadProxies(zp: TZipFile; var pProxys: Tarrproxy);
procedure loadOutInBox(zp: TZipFile);
function openSaveDlg(parent: Tform; const Cptn: String; IsOpen: Boolean; const ext: string = ''; const extCptn: String = '';
const defFile: String = ''; MultiSelect: Boolean = False): string;
procedure incDBTimer;
function UnFakeUIN(uin: Int64): TUID;
function str2sortby(const s: AnsiString): TsortBy;
procedure CheckBDays;
function GetWidth(chk: TCheckBox): integer;
function StringFromFile(const FileName: TFileName): RawByteString;
function parseMsgImages(const imgStr: RawByteString): TBytes;
procedure getMsgImages(imgs: TBytes; var imgList: TImgBytes);
procedure CacheType(const url, ctype: RawByteString);
function CacheImage(var mem: TMemoryStream; const url, ext: RawByteString): Boolean;
procedure UpdatePrefsFrm;
function ParseJSON(const RespStr: String; out JSON: TJSONObject): Boolean; overload;
function ParseJSON(const RespStr: String; out JSON: TJSONArray): Boolean; overload;
procedure ProcessICQLink(Data: String);
function AvatarUsePalette10: Boolean;
function IsTen: Boolean;
function IsElevated: Boolean;
function GetActiveMonitorCount: Integer;
procedure CloseAllChildWindows;
// costants for files
const
DBFK_OLDUIN = 00;
DBFK_NICK = 01;
DBFK_FIRST = 02;
DBFK_LAST = 03;
DBFK_EMAIL = 04;
DBFK_CITY = 05;
DBFK_STATE = 06;
DBFK_ABOUT = 07;
DBFK_DISPLAY = 08;
DBFK_QUERY = 09;
DBFK_ZIP = 10;
// DBFK_COUNTRY = 11;
DBFK_BIRTH = 12;
// DBFK_LANG = 13;
DBFK_HOMEPAGE = 14;
DBFK_CELLULAR = 15;
DBFK_IP = 16;
DBFK_AGE = 17;
DBFK_GMT = 18;
DBFK_GENDER = 19;
DBFK_GROUP = 20;
DBFK_LASTUPDATE = 21;
DBFK_LASTONLINE = 22;
// DBFK_LASTMSG = 23; DON'T USE, it was badly updated
DBFK_LASTMSG = 24;
DBFK_NOTES = 25;
DBFK_DONTDELETE = 26;
DBFK_ASKEDAUTH = 27;
DBFK_MEMBERSINCE = 28;
DBFK_ONLINESINCE = 29;
DBFK_SMSABLE = 30;
DBFK_NODB = 31;
DBFK_SENDTRANSL = 32;
DBFK_INTERESTS = 33;
DBFK_WORKPAGE = 34;
DBFK_WORKSTNT = 35; // <20><>
DBFK_WORKDEPT = 36; // <20><>
DBFK_WORKCOMPANY = 37; // <20><>
DBFK_WORKCOUNTRY = 38;
DBFK_WORKZIP = 39;
DBFK_WORKADDRESS = 40;
DBFK_WORKPHONE = 41;
DBFK_WORKSTATE = 42;
DBFK_WORKCITY = 43;
DBFK_SMSMOBILE = 44;
DBFK_UTYPE = 110;
DBFK_UID = 111;
DBFK_BIRTHL = 112;
DBFK_SSIID = 113;
DBFK_Authorized = 114;
DBFK_ssNoteStr = 115;
DBFK_ICONSHOW = 116;
DBFK_ICONMD5 = 117;
DBFK_ssNickname = 118;
DBFK_ssCell = 119;
DBFK_MARSTATUS = 120;
DBFK_lclNoteStr = 121;
DBFK_ZODIAC = 122;
DBFK_qippwd = 123;
DBFK_LASTBDINFORM = 124;
DBFK_LASTINFOCHG = 125;
DBFK_ADDRESS = 126;
// DBFK_BIRTHCOUNTRY = 127;
DBFK_BIRTHSTATE = 128;
DBFK_BIRTHCITY = 129;
DBFK_REGULAR = 130;
DBFK_ssCell2 = 131;
DBFK_ssCell3 = 132;
DBFK_ssCell4 = 142;
// New types
DBFK_COUNTRY = 45;
DBFK_LANG1 = 46;
DBFK_LANG2 = 47;
DBFK_LANG3 = 48;
DBFK_BIRTHCOUNTRY = 133;
DBFK_BIRTHADDRESS = 134;
DBFK_BIRTHZIP = 135;
DBFK_ISLOCAL = 136;
DBFK_OFFICIAL = 137;
DBFK_DELETED = 143;
DBFK_BOT = 144;
DBFK_ICONCOLOR_AB = 138;
DBFK_ICONCOLOR_AT = 139;
DBFK_ICONCOLOR_PB = 140;
DBFK_ICONCOLOR_PT = 141;
var
ListsCS: TCriticalSection;
implementation
uses
Math, UITypes,
{$IFDEF UNICODE}
AnsiStrings, Character,
{$ENDIF UNICODE}
Base64, RQUtil, RDFileUtil, RDUtils, RnQSysUtils,
RQThemes, RQLog, RnQdbDlg, RnQDialogs,
RnQLangs, RnQButtons, RnQBinUtils, RnQGlobal, RnQCrypt, RnQPics,
RnQTrayLib, RnQTips, Hook,
prefSheet, RnQPrefsLib,
mainDlg, roasterLib, iniLib, pluginutil,
selectContactsDlg, incapsulate,
pluginLib, authreqDlg,
langLib, groupsLib, outboxDlg, // msgsDlg,
history,
RnQMacros,
usersDlg, ThemesLib, RnQStrings,
Protocols_All, Protocol_ICQ, // ICQClients,
RnQGraphics32, Stickers, Nodes, SciterLib, HiddenForm,
// AsyncCalls,
HistAllSearch, SynCrypto;
function str2sortby(const s: AnsiString): TsortBy;
begin
result := low(TsortBy);
while result <= high(TsortBy) do
if s = sortby2str[result] then
exit
else
inc(result);
result := SB_EVENT;
end;
procedure OpenURL(const pURL: String);
begin
RnQSysUtils.OpenURL(pURL, UseDefaultBrowser, BrowserCmdLine);
end;
procedure OpenICQURL(const pURL: String);
begin
Account.AccProto.OpenICQURL(pURL);
end;
procedure onlyDigits(obj: Tobject);
var
i: integer;
begin
if obj is Tcustomedit then
with (obj as Tcustomedit) do
begin
i := selstart;
text := onlyDigits(text);
if i > length(text) then
i := length(text);
selstart := i;
end;
end;
procedure loadLists(const pr: TICQSession; zp: TZipFile; const uPath: String);
var
zipLists: Boolean;
function LoadZorF(const fn: String): RawByteString;
var
i: integer;
begin
if zipLists then
begin
i := zp.IndexOf(fn);
if i >= 0 then
result := zp.data[i]
else
result := '';
end else
result := loadFileA(uPath + fn);
end;
var
s: RawByteString;
i: integer;
begin
// backward compatibility
renamefile(uPath + 'uin.list', uPath + uinlistFilename);
i := -1;
zipLists := False;
if Assigned(zp) then
try
i := zp.IndexOf(rosterFileName1);
if i >= 0 then
s := zp.data[i];
except
i := -1;
s := '';
end;
if i >= 0 then
begin
zipLists := True;
end else
s := loadFileA(uPath + rosterFileName1);
pr.readList(LT_ROSTER).fromString(s, pr.contactsDB);
notInlist.fromString(LoadZorF(nilFilename1), TICQSession.ContactsDB);
notInlist.remove(pr.readList(LT_ROSTER));
IgnoreList.fromString(LoadZorF(ignoreFilename1), TICQSession.ContactsDB);
quietList.fromString(LoadZorF(quietFileName1), TICQSession.ContactsDB);
uinlists.fromString(LoadZorF(uinlistFilename));
retrieveQ.fromString(LoadZorF(retrieveFileName1), TICQSession.ContactsDB);
end;
procedure LoadExtSts(zp: TZipFile);
var
k, line, s: RawByteString;
i: integer;
begin
// clear;
// s := loadFile(userPath + extstatusesFilename);
s := loadFromZipOrFile(zp, Account.ProtoPath, extstatusesFilename);
i := 0;
while s > '' do
begin
line := chopLine(s);
k := chop(AnsiString('='), line);
k := trim(k);
line := trim(line);
if isOnlyDigits(k) then
try
i := strToInt(k);
if i < length(ExtStsStrings) then
begin
ExtStsStrings[i].Cap := '';
ExtStsStrings[i].Desc := '';
end;
except
i := -1;
// setlength(a,length(a)-1);
end
else
// if (i >= Low(XStatus6))and(i <= High(XStatus6)) then
if (i >= Low(ExtStsStrings)) and (i <= High(ExtStsStrings)) then
if k = 'caption' then
try
ExtStsStrings[i].Cap := Copy(UnUTF(line), 1, MaxXStatusLen);
except
end
else if k = 'desc' then
try
ExtStsStrings[i].Desc := StringReplace(Copy(UnUTF(line), 1, MaxXStatusDescLen), AnsiString('\n'), CRLF,
[rfReplaceAll]);
except
end;
end;
end;
procedure loadSpamQuests(zp: TZipFile);
var
k, line, s: RawByteString;
i, j: integer;
begin
// clear;
s := loadFromZipOrFile(zp, Account.ProtoPath, SpamQuestsFilename);
i := -1;
// i := 0;
while s > '' do
begin
line := chopLine(s);
k := trim(chop('=', line));
line := trim(line);
{ if isOnlyDigits(k) then
try
i := strToInt(k);
except
// setlength(a,length(a)-1);
end
else }
if k = 'question' then
try
i := length(SpamFilter.Quests);
SetLength(SpamFilter.Quests, i + 1);
SpamFilter.Quests[i].q := StringReplace(UnUTF(line), AnsiString('\n'), CRLF, [rfReplaceAll]);
except
end
else if k = 'answer' then
try
if i >= 0 then
try
j := Length(SpamFilter.Quests[i].a);
SetLength(SpamFilter.Quests[i].a, j + 1);
SpamFilter.Quests[i].a[j] := UnUTF(line);
except
end;
except
end;
end;
end;
procedure LoadProxies(zp: TZipFile; var pProxys: Tarrproxy);
var
cfg, l, h: RawByteString;
i, ppp: integer;
function yesno: Boolean;
begin
result := comparetext(l, AnsiString('yes')) = 0
end;
// var
// pp : TproxyProto;
begin
// cfg := loadfile(userPath + proxiesFileName);
cfg := loadFromZipOrFile(zp, Account.ProtoPath, proxiesFileName);
i := 0;
ClearProxyArr(pProxys);
// SetLength(pProxys, 0);
// ProxyIDBox.ItemIndex := 0;
while cfg > '' do
begin
l := chop(CRLF, cfg);
h := chop('=', l);
if h = 'proxy-name' then
begin
i := length(pProxys);
SetLength(pProxys, i + 1);
ClearProxy(pProxys[i]);
pProxys[i].name := UnUTF(l);
end
else if length(pProxys) > 0 then
begin
if h = 'proxy-ver5' then
if yesno then
pProxys[i].proto := PP_SOCKS5
else
pProxys[i].proto := PP_SOCKS4
// else if h='proxy' then pProxys[i].enabled:=yesno
else if h = 'proxy-auth' then
pProxys[i].auth := yesno
else if h = 'proxy-user' then
pProxys[i].user := UnUTF(l)
else if h = 'proxy-ntlm' then
pProxys[i].NTLM := yesno
else if h = 'proxy-pass' then
pProxys[i].pwd := UnUTF(passDecrypt(l))
else if h = 'proxy-pass64' then
pProxys[i].pwd := UnUTF(passDecrypt(Base64DecodeString(l)))
else if h = 'proxy-host' then
pProxys[i].addr.host := UnUTF(l)
else if h = 'proxy-port' then
pProxys[i].addr.port := StrToIntDef(l, 0)
else if h = 'proxy-proto' then
begin
ppp := IndexStr(l, proxyproto2str);
if ppp < 0 then
begin
// pProxys[i].enabled:=FALSE;
// pProxys[i].proto:=PP_SOCKS5;
pProxys[i].proto := PP_NONE;
end
else
pProxys[i].proto := TproxyProto(ppp);
end
{ else if Pos('proxy-', h)>0 then
for pp:=low(pp) to high(pp) do
begin
if h='proxy-'+proxyproto2str[pp]+'-host' then proxyes[i].addr[pp].host:=l;
if h='proxy-'+proxyproto2str[pp]+'-port' then proxyes[i].addr[pp].port:=l;
end; }
end
end;
end;
procedure saveAllListsSync(const uPath: String; const pr: TICQSession; pProxys: Tarrproxy);
begin
ListsCS.WaitFor(5000);
ListsCS.Acquire;
saveAllLists(uPath, pr, pProxys);
ListsCS.Release;
end;
procedure saveAllListsAsync(const uPath: String; const pr: TICQSession; pProxys: Tarrproxy);
begin
TTask.Create(procedure
begin
if not running then
Exit;
ListsCS.Acquire;
try
saveAllLists(uPath, pr, pProxys);
finally
ListsCS.Release;
end;
end).Start;
end;
function saveAllLists(const uPath: String; const pr: TICQSession; pProxys: Tarrproxy): Boolean;
const
splitMsg = 'automsg: ';
var
zf: TZipFile;
// ZIP: TZIPWriter;
procedure AddFile2Zip(const fn: String; const cfg: RawByteString);
// var
// fIDX : Integer;
begin
// if cfg > '' then
begin
// fIDX := zf.AddFile(fn);
// fIDX := zf.AddFile(fn, 0, '123');
// fIDX := zf.AddFile(fn, 0, AccPass);
// zf.Data[fIDX] := cfg;
// fIDX :=
zf.AddFile(fn, 0, AccPass, cfg);
end;
// zf.Files[fIDX].CommonFileHeader.VersionNeededToExtract
end;
var
// s : string;
cfg: RawByteString;
sA: AnsiString;
// i : Integer;
k, l: integer;
Saved: Boolean;
memStream: TMemoryStream;
lFileOld, lFileNew, lFileBak: string;
begin
Result := False;
if fantomWork then
Exit;
{
groups.save; -- OK
saveLists(MainProto); -- OK
saveInbox; -- OK
saveOutbox; -- OK
saveCFG; -- OK
saveAutoMessages; -- OK
saveMacros; -- OK
savecommonCFG; -- OK
saveDB; -- OK
saveRetrieveQ; -- OK
if reopenchats then chatFrm.savePages; -- OK
SaveExtSts; -- OK
SaveSpamQuests; -- OK
}
zf := TZipFile.Create;
// i := 0;
// zf.AddFile(dbFileName);
// zf.Data[i] := db2str(contactsDB);
zf.ZipFileComment := 'DB file of R&Q ver.' + IntToStr(RnQBuild);
cfg := db2strU(TICQSession.ContactsDB);
AddFile2Zip(dbFileName, cfg);
cfg :=
AnsiString('account-id=') + UTF(pr.getMyInfo.UID2cmp) + CRLF +
AnsiString('account-name=') + UTF(pr.getMyInfo.displayed);
AddFile2Zip(AboutFileName, cfg);
cfg := '';
AddFile2Zip(groupsFilename, groups.toString);
AddFile2Zip(rosterFileName1, pr.readList(LT_ROSTER).toString);
// msgDlg(getTranslation('Error saving contact list'),mtError);
// msgDlg(getTranslation('Error saving visible list'),mtError);
// msgDlg(getTranslation('Error saving invisible list'),mtError);
AddFile2Zip(ignoreFilename1, IgnoreList.toString);
AddFile2Zip(quietFilename1, quietList.toString);
// msgDlg(getTranslation('Error saving ignore list'),mtError);
// if NILdoWith = 2 then
begin
AddFile2Zip(nilFilename1, notInlist.toString);
// msgDlg(getTranslation('Error saving not-in-list'),mtError);
end;
AddFile2Zip(uinlistFilename, uinlists.toString);
// msgDlg(getTranslation('Error saving uinlists'), mtError);
if Assigned(eventQ) then
AddFile2Zip(inboxFilename, eventQ.toString);
if Assigned(Account.outbox) then
AddFile2Zip(outboxFilename, Account.outbox.toString);
AddFile2Zip(configFileName, getCFG);
AddFile2Zip(macrosFileName, macros2str(macros));
AddFile2Zip(MainCSSFileName, MainCSS);
AddFile2Zip(ChatCSSFileName, ChatCSS);
// inc(i);
// zf.AddFile(commonFileName);
// zf.Data[i] := getCommonCFG;
if retrieveQ.empty then
// deleteFile(uPath+retrieveFileName+'.txt')
else
AddFile2Zip(retrieveFileName1, retrieveQ.toString);
if Assigned(UI.Chat) then
AddFile2Zip(reopenchatsFileName, UI.Chat.Pages2String);
cfg := '';
// for I := low(XStatus6) to High(XStatus6) do
for k := Low(ExtStsStrings) to High(ExtStsStrings) do
begin
cfg := cfg + format(AnsiString('%d=%s' + CRLF + 'caption=%s' + CRLF + 'desc=%s'),
[k, AnsiString(XStatusArray[k].Caption), UTF(ExtStsStrings[k].Cap),
UTF(newline2slashn(ExtStsStrings[k].Desc))]);
// f := f+format(CRLF+'ssi=%d', [a[i].ssiID]);
cfg := cfg + CRLF;
end;
AddFile2Zip(extstatusesFilename, cfg);
cfg := '';
for k := Low(SpamFilter.Quests) to High(SpamFilter.Quests) do
begin
sA := UTF(newline2slashn(SpamFilter.Quests[k].q));
if sA > '' then
begin
cfg := cfg + format(AnsiString('question=%s'), [sA]);
for l := low(SpamFilter.Quests[k].a) to High(SpamFilter.Quests[k].a) do
// cfg := cfg+format(AnsiString(CRLF + 'answer=%s'),[StrToUTF8(SpamFilter.Quests[k].ans[l])]);
cfg := cfg + CRLF + AnsiString('answer=') + UTF(SpamFilter.Quests[k].a[l]);
// f := f+format(CRLF+'ssi=%d', [a[i].ssiID]);
cfg := cfg + CRLF + '**********' + CRLF;
end;
end;
AddFile2Zip(SpamQuestsFilename, cfg);
cfg := '';
if Length(pProxys) > 0 then
for k := 0 to Length(pProxys) - 1 do
begin
if pProxys[k].name = '' then
pProxys[k].name := 'Proxy' + IntToStr(k + 1);
cfg := cfg +
'proxy-name=' + UTF(pProxys[k].name) + CRLF +
'proxy-auth=' + yesno[pProxys[k].auth] + CRLF +
'proxy-user=' + UTF(pProxys[k].user) + CRLF +
'proxy-pass64=' + Base64EncodeString(passCrypt(UTF(pProxys[k].pwd))) + CRLF +
'proxy-ntlm=' + yesno[pProxys[k].NTLM] + CRLF +
'proxy-proto=' + proxyproto2str[pProxys[k].proto] + CRLF +
'proxy-host=' + AnsiString(pProxys[k].addr.host) + CRLF +
'proxy-port=' + IntToStr(pProxys[k].addr.port) + CRLF;
cfg := cfg + '------------------' + CRLF;
end;
AddFile2Zip(proxiesFileName, cfg);
try
// zf.SaveToFile(uPath+dbFileName + '4.new');
memStream := TMemoryStream.Create;
zf.SaveToStream(memStream);
memStream.SaveToFile(uPath + dbFileName + '5.new');
memStream.Free;
Saved := True;
except
msgDlg('Error on saving DB5', True, mtError);
Saved := False;
end;
zf.Free;
if Saved then
try
if FileExists(uPath + dbFileName + '5') then
begin
lFileOld := uPath + dbFileName + '5';
lFileNew := uPath + dbFileName + '5.new';
lFileBak := uPath + dbFileName + '5.bak';
if MakeBackups then
ReplaceFile(PChar(lFileOld), PChar(lFileNew), PChar(lFileBak), REPLACEFILE_IGNORE_MERGE_ERRORS, NIL, NIL)
else
ReplaceFile(PChar(lFileOld), PChar(lFileNew), NIL, REPLACEFILE_IGNORE_MERGE_ERRORS, NIL, NIL)
end else
// DeleteFile(uPath+dbFileName + '5');
renamefile(uPath + dbFileName + '5.new', uPath + dbFileName + '5');
except
// RnQFileUtil.saveFile(userPath+dbFileName, s, True);
msgDlg('Error on saving DB', True, mtError);
end;
// if FileExists(userPath+dbFileName) then
// DeleteFile(userPath+dbFileName);
// if FileExists(userPath+dbFileName + '2') then
// DeleteFile(userPath+dbFileName + '2');
Result := True;
end;
function DoConnect: Boolean;
var
pr: TICQSession;
begin
Result := False;
if not Assigned(Account.AccProto) or not Account.AccProto.IsOffline then
Exit;
Result := True;
if not UseLastStatus then
LastStatus := RnQstartingStatus;
SetProgBar(Account.AccProto, 0.1 / progLogonTotal);
pr := Account.AccProto;
CopyProxy(pr.aProxy, MainProxy);
TICQSession(pr).Connect;
end;
function findAuthReq(c: TICQContact): TauthreqFrm;
var
i: integer;
begin
with childWindows do
begin
i := 0;
while i < count do
begin
if Tobject(items[i]) is TauthreqFrm then
begin
result := TauthreqFrm(items[i]);
if result.contact.equals(c) then
exit;
end;
inc(i);
end;
end;
result := NIL;
end;
procedure TPageControl.CNDrawitem(var Message: TWMDrawItem);
var
Color1: TColor;
Color2: TColor;
Rect: Trect;
Rgn: HRGN;
hnd: HDC;
p: TPicName;
ss: String;
begin
hnd := Message.DrawItemStruct.HDC;
Rect := Message.DrawItemStruct.rcItem;
SelectClipRgn(hnd, 0);
inc(Rect.Left, 2);
inc(Rect.Top, 2);
inc(Rect.Bottom, 4);
inc(Rect.Right, 2);
case Message.DrawItemStruct.itemState of
1:
begin
Color1 := theme.GetColor('button.bg.selected', clWebLightgrey);
Color2 := theme.GetColor('button.frame.selected', clWebGray);;
FillRect(hnd, Rect, CreateSolidBrush(Color1));
FrameRect(hnd, Rect, CreateSolidBrush(Color2));
end;
end;
with canvas do
begin
inc(Rect.Left, 4);
dec(Rect.Right, 2);
// inc(Rect.Top, 2);
// inc(Rect.bottom, 2);
SetBKMode(hnd, TRANSPARENT);
if (Message.DrawItemStruct.itemState = 1) then
p := 'chat.tab.active'
else
p := 'chat.tab.inactive';
theme.ApplyFont(p, canvas.Font);
ss := Self.Tabs.Strings[Message.DrawItemStruct.itemID];
DrawText(hnd, PChar(ss), length(ss), Rect, DT_SINGLELINE or DT_VCENTER);
end;
Rgn := CreateRectRgn(0, 0, 0, 0);
SelectClipRgn(Message.DrawItemStruct.HDC, Rgn);
DeleteObject(Rgn);
Message.result := 1;
inherited;
end;
function parseMsgImages(const imgStr: RawByteString): TBytes;
var
pos1, pos2: integer;
image: RawByteString;
str: TMemoryStream;
procedure putToStream(const img: RawByteString);
var
OutSize: LongWord;
PIn, POut: Pointer;
Buf: TBytes;
begin
PIn := @img[1];
OutSize := CalcDecodedSize(PIn, Length(img));
str.SetSize(str.Size + SizeOf(OutSize) + OutSize);
str.Write(OutSize, SizeOf(OutSize));
SetLength(Buf, OutSize);
POut := @Buf[0];
Base64Decode(PIn^, Length(img), POut^);
str.Write(Buf, Length(Buf));
end;
begin
str := TMemoryStream.Create;
str.Position := 0;
image := imgStr;
repeat
pos1 := PosEx(RnQImageTag, image);
if (pos1 > 0) then
begin
pos2 := PosEx(RnQImageUnTag, image, pos1 + length(RnQImageTag));
putToStream(Copy(image, pos1 + length(RnQImageTag), pos2 - (pos1 + length(RnQImageTag))));
image := Copy(image, pos2 + length(RnQImageUnTag), length(image));
end else
Break;
until pos1 <= 0;
image := imgStr;
repeat
pos1 := PosEx(RnQImageExTag, image);
if (pos1 > 0) then
begin
pos2 := PosEx(RnQImageExUnTag, image, pos1 + length(RnQImageExTag));
putToStream(Copy(image, pos1 + length(RnQImageExTag), pos2 - (pos1 + length(RnQImageExTag))));
image := Copy(image, pos2 + length(RnQImageExUnTag), length(image));
end else
Break;
until pos1 <= 0;
SetLength(Result, str.Size);
str.Position := 0;
str.Read(Result, str.Size);
str.Free;
end;
procedure getMsgImages(imgs: TBytes; var imgList: TImgBytes);
var
pos: Integer;
size: LongWord;
begin
pos := 0;
if Length(imgs) > 4 then
repeat
try
size := dword_LEat(@imgs[pos]);
if size = 0 then
Break;
Inc(pos, SizeOf(LongWord));
SetLength(imgList, Length(imgList) + 1);
imgList[Length(imgList) - 1] := Copy(imgs, pos, size);
Inc(pos, size);
except
Break;
end;
until pos <= Length(imgs);
end;
function openSaveDlg(parent: Tform; const Cptn: String; IsOpen: Boolean; const ext: String = ''; const extCptn: String = '';
const defFile: String = ''; MultiSelect: Boolean = False): string;
var
Filtr: String;
fn: String;
hndl: THandle;
// defDir : String;
begin
if ext > '' then
if extCptn > '' then
Filtr := getTranslation(extCptn) + '|*.' + ext + '|' + getTranslation('All files') + '|*.*'
else
Filtr := '*.' + ext + '|*.' + ext + '|' + getTranslation('All files') + '|*.*'
else
Filtr := getTranslation('All files') + '|*.*';
// if defFile = '' then
// defFile := myPath;
// dlg.options:=[ofFileMustExist,ofEnableSizing];
if parent <> NIL then
hndl := parent.Handle
else
hndl := 0;
fn := ExtractFileName(defFile);
if OpenSaveFileDialog(hndl, ext, Filtr, ExtractFileDir(defFile), getTranslation(Cptn), fn, IsOpen, MultiSelect) then
result := fn
else
result := '';
end;
procedure RestoreForegroundWindow;
begin
if oldForewindow = 0 then
Exit;
ForceForegroundWindow(oldForewindow);
oldForewindow := 0;
end;
procedure ApplyTransparency(Window: TAlphaWindows = AW_BOTH; Forced: Integer = -1);
var
ExStyle: Integer;
CLAlphaBlend, ChatAlphaBlend: Boolean;
AlphaValue: Integer;
begin
if not Running then
Exit;
if (Window = AW_CL) or (Window = AW_BOTH) then
begin
ExStyle := GetWindowLongPtr(UI.CL.Window, GWL_EXSTYLE);
CLAlphaBlend := transparency.forRoster or (Forced > 0);
if CLAlphaBlend then
begin
if (ExStyle and WS_EX_LAYERED) = 0 then
SetWindowLongPtr(UI.CL.Window, GWL_EXSTYLE, ExStyle or WS_EX_LAYERED);
if Forced >= 0 then
AlphaValue := Forced
else if UI.CL.Window = GetForegroundWindow then
AlphaValue := transparency.active
else
AlphaValue := transparency.inactive;
SetLayeredWindowAttributes(UI.CL.Window, 0, AlphaValue, LWA_ALPHA);
end else if (ExStyle and WS_EX_LAYERED) <> 0 then
SetWindowLongPtr(UI.CL.Window, GWL_EXSTYLE, ExStyle and not WS_EX_LAYERED);
end;
if (Window = AW_CHAT) or (Window = AW_BOTH) then
begin
ExStyle := GetWindowLongPtr(UI.Chat.Window, GWL_EXSTYLE);
ChatAlphaBlend := transparency.forChat or (Forced > 0);
if ChatAlphaBlend then
begin
if (ExStyle and WS_EX_LAYERED) = 0 then
SetWindowLongPtr(UI.Chat.Window, GWL_EXSTYLE, ExStyle or WS_EX_LAYERED);
if Forced >= 0 then
AlphaValue := Forced
else if UI.Chat.Window = GetForegroundWindow then
AlphaValue := transparency.active
else
AlphaValue := transparency.inactive;
SetLayeredWindowAttributes(UI.Chat.Window, 0, AlphaValue, LWA_ALPHA);
end else if (ExStyle and WS_EX_LAYERED) <> 0 then
SetWindowLongPtr(UI.Chat.Window, GWL_EXSTYLE, ExStyle and not WS_EX_LAYERED);
end;
end;
(*
procedure applyDocking(Undock: Boolean = False);
var
r: Trect;
begin
try
if docking.Dock2Chat then
begin
if Undock then
begin
if not RnQmain.Floating then
begin
if Assigned(RnQmain.parent) then
r.TopLeft := RnQmain.parent.ClientToScreen(Types.Point(RnQmain.Left, RnQmain.Top))
else
r.TopLeft := Types.Point(RnQmain.Left, RnQmain.Top);
end;
r.Right := r.Left + RnQmain.Width;
r.Bottom := r.Top + RnQmain.Height;
RnQmain.ManualFloat(r);
end
else
begin
mainDlg.RnQmain.DragKind := dkDock;
chatFrm.CLSplitter.Visible := True;
chatFrm.CLPanel.Visible := True;
if docking.Docked2chat and RnQmain.Floating then
if chatFrm.chats.Count > 0 then
begin
chatFrm.MainFormWidth := RnQmain.Width;
RnQmain.ManualDock(chatFrm.CLPanel);
RnQmain.Visible := True;
end;
end;
end
else
begin
if not RnQmain.Floating then
try
{ if Assigned(chatFrm.DockManager) then
chatFrm.DockManager.RemoveControl(RnQmain)
else
if Assigned(chatFrm.CLPanel.DockManager) then
chatFrm.CLPanel.DockManager.RemoveControl(RnQmain); }
if Assigned(RnQmain.parent) then
r.TopLeft := RnQmain.parent.ClientToScreen(Types.Point(RnQmain.Left, RnQmain.Top))
else
r.TopLeft := Types.Point(RnQmain.Left, RnQmain.Top);
r.Right := r.Left + RnQmain.Width;
r.Bottom := r.Top + RnQmain.Height;
RnQmain.ManualFloat(r);
except
end;
docking.Docked2chat := False;
mainDlg.RnQmain.DragKind := dkDrag;
chatFrm.CLPanel.Visible := False;
chatFrm.CLSplitter.Visible := False;
end;
finally
mainfrmHandleUpdate;
end;
end;
*)
function loadDB(zp: TZipFile; pCheckGroups: Boolean): Boolean;
var
s: RawByteString;
zf: TZipFile;
i: integer;
begin
freeDB(TICQSession.ContactsDB);
s := '';
if Assigned(zp) then
begin
// zf := TZipFile.Create;
try
i := zp.IndexOf(dbFileName);
if i >= 0 then
s := zp.data[i];
except
s := '';
end
end;
if s = '' then
if FileExists(Account.ProtoPath + dbFileName) then
s := loadFileA(Account.ProtoPath + dbFileName)
else
begin
zf := TZipFile.Create;
try
// if FileExists(userPath+dbFileName + '4') then
// begin
// zf.LoadFromFile(userPath+dbFileName + '4');
// i := zf.IndexOf(dbFileName);
// if i >=0 then
// s := zf.Uncompressed[i];
// end
// else
if FileExists(Account.ProtoPath + dbFileName + '3') then
begin
zf.LoadFromFile(Account.ProtoPath + dbFileName + '3');
i := zf.IndexOf(dbFileName);
if i >= 0 then
s := zf.data[i];
end
except
s := '';
end;
zf.Free;
end;
// if FileExists(userPath+dbFileName + '2') then
// s := ZDecompressStrEx(loadFile(userPath+dbFileName + '2'))
// else
// contactsDB:=str2db(Account.AccProto.getContactClass, s, result)
TICQSession.ContactsDB := str2db(Account.AccProto, s, result, pCheckGroups);
TICQSession.ContactsDB.Add(Account.AccProto.MyAccNum)
end;
function compContacts(Item1, Item2: Pointer): integer;
begin
result := comparetext(TICQContact(Item1).Displayed, TICQContact(Item2).Displayed)
end;
function compContactsByGroup(Item1, Item2: Pointer): integer;
var
c1, c2: TICQContact;
begin
c1 := TICQContact(Item1);
c2 := TICQContact(Item2);
if c1.group < c2.group then
result := -1
else if c1.group > c2.group then
result := +1
else
result := comparetext(c1.Displayed, c2.Displayed);
end;
procedure sortCL(cl: TRnQCList);
begin
cl.sort(compContacts)
end;
procedure sortCLbyGroups(cl: TRnQCList);
begin
cl.sort(compContactsByGroup)
end;
procedure HideForm(Frm: Tform);
begin
if Frm = nil then
Exit;
Frm.Hide;
ShowWindow(Application.Handle, SW_HIDE)
end;
function PrefIsVisiblePage(const pf: String): Boolean;
begin
Result := True;
if pf = 'ICQ' then
if not Assigned(Account.AccProto) then
Result := False;
end;
procedure ShowForm(WhatForm: TWhatForm; const Page: String = ''; Mode: TFrmViewMode = vmFull; Who: TICQContact = nil);
begin
case WhatForm of
WF_SHEET: UI.OpenPrefs(Page, Mode = vmFull);
WF_USERS: UI.SwitchUser;
end;
end;
function ShowUsers(ShowConflictMsg: Boolean = False): TUID;
begin
Result := UI.SwitchUser(ShowConflictMsg);
end;
function CheckAccPass(const uid: TUID; const db: String; var pPass: String): Boolean;
begin
Result := False;
pPass := UI.EnterPassword(GetTranslation('Account password') + ' (' + uid + ')', 16);
if not (pPass = '') then
begin
if CheckZipFilePass(db, dbFileName, pPass) then
Result := True
else
begin
pPass := '';
Result := False;
MsgDlg('Wrong password', True, mtWarning)
end
end
else
begin
Result := False;
//MsgDlg('Please enter password', True, mtWarning)
end;
end;
function CheckHistPass: Boolean;
begin
Result := False;
if histcrypt.enabled and (histcrypt.pwd = '') then
begin
histcrypt.pwd := UI.EnterPassword(GetTranslation('History password'));
if histcrypt.pwd = '' then
Exit;
end;
Result := True;
end;
procedure UpdateViewInfo(c: TICQContact);
begin
if not UpdateViewInfoQ.exists(c) then
UpdateViewInfoQ.Add(c);
end;
procedure loadOutInBox(zp: TZipFile);
var
s: RawByteString;
i: integer;
zipPref: Boolean;
begin
i := -1;
zipPref := False;
if Assigned(zp) then
try
i := zp.IndexOf(outboxFilename);
if i >= 0 then
s := zp.data[i];
except
i := -1;
s := '';
end;
if i < 0 then
s := loadFileA(Account.ProtoPath + outboxFilename)
else
zipPref := True;
Account.outbox.fromString(s);
if zipPref then
begin
i := zp.IndexOf(inboxFilename);
if i >= 0 then
s := zp.data[i];
end
else
s := loadFileA(Account.ProtoPath + inboxFilename);
eventQ.fromString(s);
eventQ.removeExpiringEvents;
end;
procedure openSendContacts(dest: TICQContact);
begin
UnsupportedFeature; Exit;
// if not Assigned(dest) then
// Exit;
// if not (dest is TICQContact) then
// Exit;
// wnd := TselectCntsFrm.doAll(nil, getTranslation('To %s', [dest.displayed]), getTranslation('Send selected contacts'),
// Account.AccProto, notInlist.clone.Add(Account.AccProto.ReadList(LT_ROSTER)), RnQmain.sendContactsAction,
// [sco_multi, sco_groups, sco_predefined], @wnd, False, False);
// // Theme.getIco2(PIC_CONTACTS, wnd.icon);
// theme.pic2ico(RQteFormIcon, PIC_CONTACTS, wnd.Icon);
// wnd.extra := Tincapsulate.aString(dest.uid);
end;
function isEmailAddress(const s: string; start: integer): integer;
// const
// emailChar=['a'..'z','A'..'Z','0'..'9','-','_','.'];
var
j: integer;
existsDot: Boolean;
begin
result := -1;
if CharInSet(s[start], EMAILCHARS) then // chi comincia bene...
begin
// try to find the @
j := start + 1;
while (j < length(s)) and (CharInSet(s[j], EMAILCHARS)) do
inc(j);
if s[j] = '@' then
begin
// @ found, now skip the @ and search for .
inc(j);
existsDot := False;
while (j < length(s)) and (CharInSet(s[j + 1], EMAILCHARS)) do
begin
if s[j] = '.' then
begin
existsDot := True;
Break;
end;
inc(j);
end;
if existsDot and (CharInSet(s[j], EMAILCHARS)) then
// at least a valid char after the . must exists
begin
repeat
inc(j);
until (j > length(s)) or not (CharInSet(s[j], EMAILCHARS));
// go forth till we're out or we meet an invalid char
result := j - 1;
end;
end;
end;
end;
procedure NotAvailable;
begin
msgDlg('This feature isn''t available yet.\nCome back tomorrow...', True, mtInformation)
end;
procedure UnsupportedFeature;
begin
msgDlg(Str_Unsupported, True, mtWarning)
end;
function childParent(child, parent: integer): Boolean;
begin
result := True;
repeat
if child = parent then
exit;
child := getParent(child);
until child = 0;
result := parent = 0;
end;
procedure myBeep;
begin
if playSounds then
beep
end;
function whatStatusPanel(statusbar: Tstatusbar; x: integer): integer;
var
x1: integer;
begin
result := 0;
x1 := statusbar.panels[0].Width;
while (x > x1) and (result < statusbar.panels.count - 1) do
begin
inc(result);
inc(x1, statusbar.panels[result].Width);
end;
end;
procedure SendProtoMsg(var oe: Toevent);
var
SendMsg: String;
i: integer;
vBin: RawByteString;
begin
oe.flags := oe.flags or IF_urgent;
if oe.flags and IF_multiple <> 0 then
oe.flags := oe.flags or IF_noblink and not IF_urgent;
vBin := plugins.castEv(PE_MSG_SENT, oe.whom.uid, oe.flags, oe.info);
if (vBin > '') then
if (byte(vBin[1]) = PM_DATA) then
begin
i := _int_at(vBin, 2);
SendMsg := UnUTF(_istring_at(vBin, 2));
if Length(vBin) >= 1 + 4 + i + 4 then
oe.info := UnUTF(_istring_at(vBin, 2 + 4 + i))
else
oe.info := SendMsg;
end else if (byte(vBin[1]) = PM_ABORT) then
Exit
else begin end
else
SendMsg := oe.info;
Account.AccProto.SendMsg(oe.whom, oe.kind, oe.flags, oe.info, SendMsg);
end;
procedure AddOutgoingMessage(Cnt: TICQContact; const Text: String; const Binary: RawByteString; Flags: DWord);
var
ev: Thevent;
begin
ev := Thevent.new(EK_MSG, Cnt, Account.AccProto.GetMyInfo, Now, UTF(Text), [], Flags, ReqID);
ev.outgoing := True;
if Length(Binary) > 0 then
ev.parseData(Binary);
if logpref.writehistory and (BE_save in behaviour[ev.kind].trig) and (Flags and IF_not_save_hist = 0) then
WriteToHistory(ev);
// if oe.flags and IF_not_show_chat = 0 then
// chatFrm.addEvent_openchat(c, ev.clone);
UI.Chat.AddEvent(Cnt, ev.clone);
ev.Free;
end;
procedure SendEmail2Mail(const email: String);
begin
if email > '' then
exec('mailto:' + email);
end;
function deleteFromTo(const fn: string; from, to_: integer): Boolean;
begin
result := partDeleteFile(fn, from, to_ - from)
end;
function enterUinDlg(const proto: TICQSession; var uin: TUID; const title: string = ''): Boolean;
var
res: TUID;
ttl: String;
s: String;
// e: integer;
// fUIN : Int64;
begin
if title = '' then
ttl := 'uin'
else
ttl := title;
res := '';
repeat
result := InputQuery(GetTranslation(ttl), GetTranslation('UIN'), s);
res := s;
if result then
begin
res := trim(res);
uin := res;
if proto = nil then
begin
result := False;
if TICQSession._isProtoUid(uin) then
begin
result := True;
uin := res;
end;
end else
result := proto.ValidICQ(uin);
if result then
begin
// uin := res;
Break
end
else
msgDlg('Invalid UIN', True, mtError)
end;
until not result;
end;
function AddToRoster(c: TICQContact; IsLocal: Boolean = False): Boolean;
begin
NotInList.Remove(c);
c.CntIsLocal := IsLocal;
Result := Account.AccProto.AddContact(c, IsLocal);
if not Result then
Exit;
roasterlib.Update(c);
roasterLib.Focus(c);
SaveListsDelayed := True;
plugins.castEvList(PE_LIST_ADD, PL_ROSTER, c);
end;
function AddToRoster(c: TICQContact; group: Integer; IsLocal: Boolean = True): Boolean;
begin
Result := False;
if c = nil then
Exit;
if group = 2000 then
group := 0;
c.group := group;
if c.group = 0 then
IsLocal := True;
SaveGroupsDelayed := True;
Result := AddToRoster(c, IsLocal);
if not Result then
roasterLib.Update(c);
end;
function AddToNIL(c: TICQContact; IsBulk: Boolean = False): Boolean;
begin
Result := False;
Account.AccProto.RemoveContact(TICQContact(c));
if not NotInList.Add(c) then
Exit;
if not IsBulk then
begin
roasterLib.update(c);
SaveListsDelayed := True;
end;
plugins.castEvList(PE_LIST_ADD, PL_NIL, c);
Result := True;
end;
procedure NILifNIL(c: TICQContact; IsBulk: Boolean = False);
begin
if Assigned(c) and not c.IsInRoster then
begin
AddToNIL(c, IsBulk);
if not IsBulk and (c is TICQContact) then
if c.InfoUpdatedTo = 0 then
Account.AccProto.GetProfile(c.UID2cmp);
end;
end;
function deltree(path: string): Boolean;
var
sr: TsearchRec;
begin
result := False;
if (path = '') or not directoryExists(path) then
exit;
path := includeTrailingPathDelimiter(path);
if findFirst(path + '*.*', faAnyFile, sr) = 0 then
repeat
if (sr.name <> '.') and (sr.name <> '..') then
if sr.Attr and faDirectory > 0 then
deltree(path + sr.name)
else
deleteFile(path + sr.name);
until findNext(sr) <> 0;
findClose(sr);
// path:=ExcludeTrailingPathDelimiter(path);
result := RemoveDir(path);
end;
function delSUBtree(subPath: string): Boolean;
var
sr: TsearchRec;
path: String;
begin
result := False;
path := myPath + subPath;
if (subPath = '') or (subPath = PathDelim) or (path = '') or not directoryExists(path) then
exit;
subPath := includeTrailingPathDelimiter(subPath);
path := includeTrailingPathDelimiter(path);
if findFirst(path + '*.*', faAnyFile, sr) = 0 then
repeat
if (sr.name <> '.') and (sr.name <> '..') then
if sr.Attr and faDirectory > 0 then
delSUBtree(subPath + sr.name)
else
deleteFile(path + sr.name);
until findNext(sr) <> 0;
findClose(sr);
// path:=ExcludeTrailingPathDelimiter(path);
result := RemoveDir(path);
end;
function rosterImgNameFor(c: TICQContact): TPicName;
begin
if notInlist.exists(c) then
Result := Status2ImgName(byte(SC_UNK), False)
else
Result := Account.AccProto.Statuses[c.getStatus].ImageName;
end;
function statusDrawExt(const DC: HDC; const x, y: integer; const s: byte; const inv: Boolean = False): TSize;
begin
if statusPics[s, inv].picName = '' then
begin
statusPics[s, inv].picName := status2imgName(s, inv);
statusPics[s, inv].pEnabled := True;
statusPics[s, inv].ThemeToken := -1;
statusPics[s, inv].Element := RQteDefault;
end;
if DC = 0 then
result := theme.GetPicSize(statusPics[s, inv])
else
result := theme.drawPic(DC, Types.Point(x, y), statusPics[s, inv])
end;
procedure showAuthreq(c: TICQContact; msg: string);
var
ar: TauthreqFrm;
begin
msg := dupString(msg);
ar := findAuthReq(c);
if ar = NIL then
TauthreqFrm.doAll(nil, c, msg)
else
begin
ar.msgBox.text := msg;
ar.BringToFront;
end;
end;
function countContactsIn(proto: TICQSession; const st: byte): integer;
var
cnt: TICQContact;
begin
Result := 0;
for cnt in proto.readList(LT_ROSTER) do
if cnt.getStatus = st then
Inc(Result);
end;
procedure ToggleOnlyOnline;
begin
roasterLib.SetOnlyOnline(not showOnlyOnline);
// design_fr.prefToggleOnlyOnline;
end;
function applyVars(c: TICQContact; const s: String; fromAM: Boolean = False): String;
var
h: Tdatetime;
s1, s2: String;
begin
if imAwaySince > 0 then
h := (Now - imAwaySince) * 24
else
h := 0;
result := template(s, ['%awaysince%', formatDatetime(timeformat.automsg, imAwaySince), '%awaysince-gmt%',
formatDatetime(timeformat.automsg, imAwaySince - GMToffset), '%elapsedhours%', IntToStr(trunc(h)), '%elapsedminutes%',
IntToStr(trunc(frac(h) * 60)), '%h%', IntToStr(hourof(Now)), '%m%', IntToStr(minuteof(Now)), '%s%', IntToStr(secondof(Now)),
'%D%', IntToStr(dayof(Now)), '%M%', IntToStr(monthof(Now)), '%Y%', IntToStr(yearof(Now)), '%hh%', IntToStr(hourof(Now), 2),
'%mm%', IntToStr(minuteof(Now), 2), '%ss%', IntToStr(secondof(Now), 2), '%DD%', IntToStr(dayof(Now), 2), '%MM%',
IntToStr(monthof(Now), 2),
'%onlinecontacts%', IntToStr(TList(Account.AccProto.readList(LT_ROSTER)).count - countContactsIn(Account.AccProto,
byte(SC_OFFLINE))), '%offlinecontacts%', IntToStr(countContactsIn(Account.AccProto, byte(SC_OFFLINE))), '%events%',
IntToStr(eventQ.count)
// '%AutoMess%', ifThen(fromAM, '', getAutomsgFor(c))
]);
if Assigned(c) then
begin
if (c is TICQContact) then
begin
if {TICQContact(c).connection.ip}0 = 0 then
s1 := getTranslation(Str_unk)
else
s1 := '';//ip2str(TICQContact(c).connection.ip);
if 0 = 0 then
s2 := getTranslation(Str_unk)
else
s2 := '0';
end
else
begin
s1 := getTranslation(Str_unk);
s2 := s1;
end;
result := template(result, ['%you%', c.displayed, '%nick%', c.nick, '%first%', c.first, '%last%', c.last, '%status%',
getTranslation(Account.AccProto.Statuses[c.getStatus].Cptn), '%ip%', s1, '%proto%', s2]);
end
else
result := template(result, ['%you%', '', '%nick%', '', '%first%', '', '%last%', '', '%ip%', getTranslation(Str_unk),
'%status%', getTranslation(Str_unk),
// statusNameExt2(byte(SC_OFFLINE)),
'%proto%', getTranslation(Str_unk)]);
end;
function getXStatusMsgFor(c: TICQContact): string;
begin
// result := applyVars(c, curXStatusDesc, True);
// result := applyVars(c, ExtStsStrings[ICQ.curXStatus][1], True);
// result := applyVars(c, ExtStsStrings[TicqSession(c.fProto).curXStatus], True);
if Assigned(c) then
result := applyVars(c, ExtStsStrings[Account.AccProto.CurXStatus].Desc, True)
else
result := applyVars(NIL, ExtStsStrings[Account.AccProto.CurXStatus].Desc, True)
end;
procedure Check4Update;
var
UpdateInfo: TUpdateInfo;
begin
if ConnectionAvailable then
if AutoCheckUpdates then
UI.OpenUpdater;
end;
function AutoCheckUpdates: Boolean;
var
UpdateInfo: TUpdateInfo;
begin
Result := CheckUpdates(UpdateInfo, True);
end;
function CheckUpdates(var UpdateInfo: TUpdateInfo; Auto: Boolean = False): Boolean;
var
RespStr, Tag, ReleasesPage, DistrLink, Changelog, Publish: String;
PublishedAt: TDateTime;
Version: TArray;
JSON: TJSONArray;
Release: TJSONValue;
Prerelease: Boolean;
Ver, Build: Integer;
TaskDlg: TTaskDialog;
begin
CheckUpdate.Checking := True;
Result := False;
UpdateInfo.error := '';
LoadFromURLAsString('https://code.highspec.ru/api/v1/repos/Mikanoshi/RnQ/releases', RespStr);
CheckUpdate.Checking := False;
if (Trim(RespStr) = '') or not ParseJSON(RespStr, JSON) then
begin
UpdateInfo.error := GetTranslation('Error checking for updates');
Exit;
end;
CheckUpdate.Last := Now;
try
if JSON.Count > 0 then
Release := JSON.Items[0]
else
Exit;
if not Assigned(Release) or not (Release is TJSONObject) then
Exit;
Release.GetValueSafe('tag_name', Tag);
Version := Tag.Split(['v']);
if Length(Version) < 2 then
Exit;
TryStrToInt(Version[0], Ver);
TryStrToInt(Version[1], Build);
Result := (Ver > RnQBuild) or (CheckUpdate.Betas and (Build > RnQBuildCustom));
if Auto then
Exit;
ReleasesPage := 'https://code.highspec.ru/Mikanoshi/RnQ/releases';
Release.GetValueSafe('prerelease', Prerelease);
Release.GetValueSafe('body', Changelog);
Release.GetValueSafe('published_at', Publish);
TJSONArray(TJSONObject(Release).GetValue('assets')).Items[0].GetValueSafe('browser_download_url', DistrLink);
PublishedAt := ISO8601ToDate(Publish, False);
UpdateInfo.beta := Prerelease;
UpdateInfo.changelog := Changelog;
UpdateInfo.distrib := DistrLink;
if Result then
begin
UpdateInfo.text := GetTranslation('There is a new build available (%s)\nPublished at %s', [Tag, FormatDatetime(TimeFormat.info, PublishedAt)]);
Result := True;
end else
UpdateInfo.text := GetTranslation('No new version available');
UpdateInfo.hasNew := Result;
SaveCfgDelayed := True;
finally
FreeAndNil(JSON);
end;
end;
procedure dockSet;
var
r: Trect;
begin
// r := RnQmain.boundsrect;
// dockSet(r);
// RnQmain.boundsrect := r;
end;
procedure dockSet(var r: Trect);
var
w: integer;
vOn: Boolean;
begin
// if not RnQmain.Visible or not running then
// exit;
// vOn := docking.appBar and docking.active and not docking.tempOff;
// if vOn <> docking.appBarFlag then
// begin
// docking.appBarFlag := vOn;
// RnQSysUtils.dockSet(RnQmain.Handle, vOn, WM_DOCK);
// end;
// if not docking.active then
// exit;
// w := r.Right - r.Left;
// r := desktopworkarea(mainDlg.RnQmain.Handle);
// if docking.appBar then
// begin
// r.Left := 0;
// r.Right := screen.Width;
// end;
// if docking.pos = DP_left then
// begin
// dec(r.Left, getsystemmetrics(SM_CXFRAME));
// r.Right := r.Left + w;
// end
// else
// begin
// inc(r.Right, getsystemmetrics(SM_CXFRAME));
// r.Left := r.Right - w;
// end;
// appbarResizeDelayed := True;
end;
procedure setAppBarSize;
// var
// r : TRect;
begin
// r := RnQmain.boundsrect;
/// / r.Right := r.Right + 10;
RnQSysUtils.setAppBarSize(UI.CL.Window, UI.CL.GetBounds, WM_DOCK, docking.pos = DP_left)
end;
function IsSpam(var Wrd: String; c: TICQContact; const Msg: String = ''; Flags: DWord = 0): Boolean;
var
b, filter: Boolean;
s: String;
i: Integer;
begin
if (flags and IF_auth > 0) and SpamFilter.IgnoreAuthNIL and (notInlist.exists(c) or not c.isInRoster) then
begin
Result := True;
Exit;
end;
if SpamFilter.IgnoreNIL and (notInlist.exists(c) or not c.IsInRoster) then
begin
Result := True;
Exit;
end;
Result := False;
filter := False;
if SpamFilter.UINgt > 0 then
if TryStrToInt(c.uid, i) and (i <= SpamFilter.UINgt) then
Exit
else
filter := True;
if SpamFilter.NotNIL then
if c.isInRoster then
Exit
else
filter := True;
if SpamFilter.MultiSend then
if flags and IF_multiple = 0 then
Exit
else
filter := True;
if SpamFilter.NotEmpty then
if ExistsHistWith(c.UID2cmp) then
Exit
else
filter := True;
if SpamFilter.NoBadWords then
begin
b := False;
s := SpamFilter.BadWords;
while not b and (s > '') do
begin
Wrd := chop(';', s);
b := ansiContainsText(msg, Wrd);
end;
if b then
filter := True
else
begin
Wrd := '';
filter := False;
end;
end;
Result := filter;
end;
function FilterRefuse(c: TICQContact; const Msg: String = ''; Flags: DWord = 0; ev: THevent = nil): Boolean;
var
Wrd: String;
SpamCnt: TICQContact;
begin
Result := True;
Wrd := '';
if IsSpam(wrd, c, msg, flags) then
begin
if SpamFilter.AddToHist then
if (msg > '') and (Assigned(ev)) then
begin
SpamCnt := Account.AccProto.GetContact(spamsFilename);
WriteToHistory(ev, SpamCnt);
if UI.Chat.IsChatOpen(SpamCnt) then
UI.Chat.AddEvent(SpamCnt, ev.clone);
end;
if SpamFilter.Warn then
if wrd > '' then
msgDlg(getTranslation('Spam filtered from %s \n by word %s', [c.displayed + ' (' + c.uid + ')', wrd]), False, mtInformation, c.uid)
else
msgDlg(getTranslation('Spam filtered from %s', [c.displayed + ' (' + c.uid + ')']), False, mtInformation, c.uid);
exit;
end;
Result := EnableIgnoreList and IgnoreList.exists(c);
end;
function db2strU(db: TRnQCList): RawByteString;
var
dim: integer;
procedure addStr(const s: RawByteString);
var
i: integer;
begin
if length(s) > 0 then
begin
i := length(result);
while dim + length(s) > i do
inc(i, 10000);
if i > length(result) then
SetLength(result, i);
system.move(Pointer(s)^, result[dim + 1], length(s));
inc(dim, length(s));
end;
end;
var
cnt: TICQContact;
begin
Result := '';
dim := 0;
for cnt in db do
if Assigned(cnt) then
addStr(cnt.GetDBrow);
SetLength(Result, dim);
end;
function str2status(const s: AnsiString): byte;
var
ss: TPicName;
begin
ss := LowerCase(s);
for result := byte(low(status2img)) to byte(high(status2img)) do
// if LowerCase(status2img[TICQStatus(result)]) = s then
if status2img[result] = ss then
exit;
result := byte(SC_ONLINE); // shut up compiler warning
end;
function str2visibility(const s: AnsiString): Tvisibility;
var
ss: TPicName;
begin
ss := LowerCase(s);
for result := low(result) to high(result) do
if visib2str[result] = ss then
exit;
result := VI_normal; // shut up compiler warning
end;
procedure clearDB(db: TRnQCList);
var
i: integer;
begin
for i := 0 to TList(db).count - 1 do
with db.getAt(i) do
Free;
db.clear;
end;
procedure freeDB(var db: TRnQCList);
begin
if not Assigned(db) then
exit;
clearDB(db);
db.Free;
db := NIL;
end;
procedure contactCreation(c: TICQContact);
begin
// getMem(c.data, sizeof(TCE));
// new(TCE(c.data));
c.data := AllocMem(sizeof(TCE));
fillChar(c.data^, sizeof(TCE), 0);
// TCE(c.data^).toquery := True; // Too many queries for loading large CLs for the first time!
end;
procedure contactDestroying(c: TICQContact);
begin
if Assigned(c.data) then
begin
{ if assigned(pTCE(c.data).history0) then
begin
Thistory(pTCE(c.data).history0).Free;
pTCE(c.data).history0 := NIL;
end; }
if Assigned(pTCE(c.data).node) then
FreeAndNil(pTCE(c.data).node);
SetLength(TCE(c.data^).notes, 0);
freeMem(c.data);
end;
end;
procedure StartMainTimer;
begin
MainTimer.OnTimer := TimerClass.OnTimer;
MainTimer.Interval := 100;
MainTimer.Enabled := True
end;
procedure StopMainTimer;
begin
MainTimer.Enabled := False
end;
function behave(ev: Thevent; kind: integer = -1 { ; const info: AnsiString='' } ): Boolean;
function IsAnswer(ans: array of string; const text: String): Boolean;
var
i: integer;
begin
Result := False;
if (length(ans) = 0) then
Exit;
for i := Low(ans) to High(ans) do
if (AnsiStartsText(ans[i], text) or AnsiStartsText('' + ans[i], text)) then
begin
Result := True;
Exit;
end;
end;
function IsUnverified(const text: String): Boolean;
begin
Result := ContainsText(text, 'https://icq.com') and ContainsText(text, 'verifyphone');
end;
const
SpamBotMsgFlags = IF_not_show_chat or IF_not_save_hist or IF_Simple;
var
ok: Boolean;
spmHist: Thistory;
i, j: integer;
ev0: Thevent;
s: string;
vProto: TICQSession;
vCnt: TICQContact;
tipsAllowed: Boolean;
SkipEvent: Boolean;
QuietEvent: Boolean;
picsFound: Boolean;
picsName: TPicName;
gr: TGroup;
dd: TDivisor;
events: Thevents;
forceadd: Boolean;
Activity: Boolean;
begin
Result := False;
// if info > '' then
// ev.setInfo(info);
if kind >= 0 then
ev.kind := kind;
case kind of
EK_MSG, EK_BUZZ:
ok := not FilterRefuse(ev.who, ev.getBodyText, 0, ev);
EK_AUTHREQ:
ok := (not(EnableIgnoreList and IgnoreList.exists(ev.who))) and (not FilterRefuse(ev.who, '', IF_auth));
else
ok := not FilterRefuse(ev.who);
end;
if not ok then
Exit;
tipsAllowed := IsCanShowNotifications;
if Assigned(ev.otherpeer) then
vCnt := ev.otherpeer
else
vCnt := ev.who;
vProto := Account.AccProto;
if SpamFilter.UseBot then
if not (vProto.isInList(LT_ROSTER, vCnt) or notInList.exists(vCnt) or UI.Chat.IsChatOpen(vCnt)) then
begin
if kind in [EK_typingBeg .. EK_XstatusMsg, EK_incoming, EK_outgoing, EK_auth, EK_authDenied, EK_statuschange] then
exit
else if (IF_offline and ev.flags > 1) then
begin
end
else if kind in [EK_XstatusMsg] then
begin
end
else if ((kind = EK_MSG) and (Length(vCnt.antispam.lastQuests) > 0)
and (IsAnswer(vCnt.antispam.lastQuests, ev.getBodyText)) or IsUnverified(ev.getBodyText)) then
begin
vCnt.antispam.tries := 0;
SetLength(vCnt.antispam.lastQuests, 0);
try
spmHist := Thistory.Create(spamsFilename);
events := spmHist.getBySender(ev.who.UID);
if Length(events) > 0 then
for i := 0 to Length(events) - 1 do
begin
ev0 := events[i];
if ev0 = nil then
Continue;
ev0.expires := -1;
// SAVE
if logpref.writehistory and (BE_save in behaviour[ev0.kind].trig) then
WriteToHistory(ev0, ev0.who);
forceadd := UI.Chat.IsChatOpen(ev0.who);
// OPENCHAT
if // not BossMode.isBossKeyOn and
(BE_openchat in behaviour[ev0.kind].trig) and not vProto.getStatusDisable.OpenChat then
begin
forceadd := not UI.Chat.OpenChat(ev0.who);
if not forceadd then
UI.Chat.SetUnreadEvent(ev0.who, ev0);
end;
// HISTORY
if BE_history in behaviour[ev0.kind].trig then
if forceadd then
UI.Chat.addEvent(ev0.who, ev0.clone);
// TRAY
// if (ev0.kind = EK_CONTACTS) and chatFrm.isVisible and (ev0.who = chatFrm.thisChat.who) then
// TselectCntsFrm.doAll(RnQmain, getTranslation('from %s', [ev0.who.displayed]),
// GetTranslation('Add selected contacts'), vProto, ev0.cl.clone, RnQmain.AddContactsAction, [sco_multi], @wnd, False, False)
// else
if BE_tray in behaviour[ev0.kind].trig then
eventQ.Add(ev0.clone);
// TIP
if tipsAllowed and not BossMode.isBossKeyOn and (BE_tip in behaviour[ev0.kind].trig) and
(ev0.flags and IF_offline = 0) and not vProto.getStatusDisable.tips then
try
// TipAdd(ev0);
TipAdd3(ev0);
except end;
FreeAndNil(ev0);
end;
SetLength(events, 0);
spmHist.deleteBySender(ev.who.UID);
spmHist.Free;
except end;
if not IsUnverified(ev.getBodyText) then
OutboxAdd(OE_msg, vCnt, SpamBotMsgFlags, getTranslation(AntiSpamMsgs[2]))
end
else
begin
SetLength(ev.who.antispam.lastQuests, 0);
if logpref.writehistory and (BE_save in behaviour[ev.kind].trig) then
WriteToHistory(ev, vProto.getContact(spamsFilename));
if (BE_history in behaviour[ev.kind].trig) then
// if UI.Chat.chats.idxOfUIN(spamsFilename) >= 0 then
if UI.Chat.IsChatOpen(vProto.getContact(spamsFilename)) then
UI.Chat.addEvent(vProto.getContact(spamsFilename), ev.clone);
if ev.who.antispam.tries = SpamFilter.BotTriesCount then
begin
inc(ev.who.antispam.tries);
OutboxAdd(OE_msg, vCnt, SpamBotMsgFlags, AnsiReplaceStr(getTranslation(AntiSpamMsgs[3]), '%uin%', ev.who.uid));
exit;
end
else if vCnt.antispam.tries > SpamFilter.BotTriesCount then
exit
else
begin
Randomize;
if SpamFilter.UseBotFromFile and (Length(SpamFilter.Quests) > 0) then
begin
i := RandomRange(0, length(SpamFilter.Quests));
// if i >0 then
begin
with SpamFilter.Quests[i] do
begin
SetLength(vCnt.antispam.lastQuests, length(a));
for j := 0 to length(a) - 1 do
vCnt.antispam.lastQuests[j] := a[j];
s := q;
end;
end
{ else
begin
s := '';
ev.who.antispam.lastQuest := '';
end; }
end
else
begin
i := RandomRange(100, 999);
SetLength(vCnt.antispam.lastQuests, 1);
vCnt.antispam.lastQuests[0] := IntToStr(i);
s := TxtFromInt(i)
end;
if length(vCnt.antispam.lastQuests) > 0 then
begin
inc(vCnt.antispam.tries);
if SpamFilter.UseBotFromFile and (length(SpamFilter.Quests) > 0) then
OutboxAdd(OE_msg, vCnt, SpamBotMsgFlags, AnsiReplaceStr(getTranslation(AntiSpamMsgs[5]), '%attempt%',
IntToStr(SpamFilter.BotTriesCount + 1 - ev.who.antispam.tries)) + CRLF + getTranslation(AntiSpamMsgs[6])
+ CRLF + s)
else
OutboxAdd(OE_msg, vCnt, SpamBotMsgFlags, AnsiReplaceStr(getTranslation(AntiSpamMsgs[5]), '%attempt%',
IntToStr(SpamFilter.BotTriesCount + 1 - ev.who.antispam.tries)) + CRLF + getTranslation(AntiSpamMsgs[4]) +
CRLF + s);
exit;
end;
end;
end;
end;
// prevent annoying fast incoming/outgoing sequences
if minOnOff then
if (ev.kind = EK_incoming) and (Now - vCnt.LastTimeSeenOnline < minOnOffTime * DTseconds) or
(ev.kind = EK_outgoing) and (Now - TCE(vCnt.data^).lastIncoming < minOnOffTime * DTseconds) then
Exit;
Result := True;
Activity := False;
if ev.kind in [EK_MSG .. EK_statuschange] then
begin
TCE(vCnt.data^).lastEventTime := Now;
Activity := True;
end;
if ev.kind in [EK_MSG, EK_BUZZ, EK_CONTACTS, EK_auth, EK_authDenied, EK_AUTHREQ] then
begin
TCE(vCnt.data^).lastMsgTime := ev.when;
Activity := True;
end;
if Activity then
UI.CL.UpdateContact(vCnt);
// SAVE
if logpref.writehistory and (BE_save in behaviour[ev.kind].trig) then
WriteToHistory(ev);
SkipEvent := False;
if DsblEvnt4ClsdGrp and (ev.kind in [EK_incoming, EK_outgoing, EK_statuschange, EK_typingBeg,
EK_typingFin, EK_XstatusMsg]) then
begin
// gr := vCnt.group;
gr := groups.Get(vCnt.group);
if OnlOfflInOne then
dd := d_contacts
else
dd := d_online;
SkipEvent := not gr.expanded[dd];
end;
QuietEvent := False;
if EnableQuietList and quietList.exists(ev.who) then
if ev.kind in [EK_AddedYou, EK_incoming, EK_outgoing, EK_statuschange, EK_typingBeg, EK_typingFin, EK_XstatusMsg, EK_buzz] then
begin
SkipEvent := True;
QuietEvent := True;
end;
// SOUND
if not BossMode.isBossKeyOn and (BE_sound in behaviour[ev.kind].trig) and not vProto.getStatusDisable.sounds and not SkipEvent
then
if ev.flags and IF_no_matter = 0 then
begin
picsFound := False;
if UseContactThemes and Assigned(ContactsTheme) then
begin
picsName := TPicName(vCnt.UID2cmp) + '.' + event2str[ev.kind];
picsFound := (ContactsTheme.GetSound(picsName) > '');
if picsFound then
ContactsTheme.PlaySound(picsName)
else
begin
picsName := TPicName('group.') + TPicName(AnsiLowerCase(groups.id2name(vCnt.group))) + '.' +
TPicName(event2str[ev.kind]);
picsFound := (ContactsTheme.GetSound(picsName) > '');
if picsFound then
ContactsTheme.PlaySound(picsName);
end;
end;
if not picsFound then
theme.PlaySound(event2str[ev.kind]);
end;
// TIP
if tipsAllowed and not BossMode.isBossKeyOn and (BE_tip in behaviour[ev.kind].trig) and (ev.flags and IF_offline = 0) and
not vProto.getStatusDisable.tips and not SkipEvent then
if ev.flags and IF_no_matter = 0 then
try
// TipAdd(ev);
TipAdd3(ev);
except
end;
{$IFDEF Use_Baloons}
if not BossMode.isBossKeyOn and (be_BALLOON in behaviour[ev.kind].trig) and (ev.flags and IF_offline = 0) and
not vProto.getStatusDisable.tips and not SkipEvent then
if ev.flags and IF_no_matter = 0 then
ShowBalloonEv(ev);
{$ENDIF Use_Baloons}
// TRAY
// if (ev.kind = EK_CONTACTS) and chatFrm.isVisible and (ev.who = chatFrm.thisChat.who) then
// TselectCntsFrm.doAll(RnQmain, getTranslation('from %s', [ev.who.displayed]), getTranslation('Add selected contacts'), vProto,
// ev.cl.clone, RnQmain.AddContactsAction, [sco_multi], @wnd, False, False)
// else
if (BE_tray in behaviour[ev.kind].trig) and not SkipEvent then
// if ev.flags and IF_no_matter = 0 then
eventQ.Add(ev.clone);
forceadd := UI.Chat.IsChatOpen(vCnt);
// OPENCHAT
if (BE_openchat in behaviour[ev.kind].trig) and not vProto.getStatusDisable.OpenChat and not QuietEvent then
if ev.flags and IF_no_matter = 0 then
if UI.Chat.OpenChat(vCnt, False, True) then
begin
forceadd := False;
UI.Chat.SetUnreadEvent(vCnt, ev);
if not BossMode.isBossKeyOn and (BE_flashchat in behaviour[ev.kind].trig) then
if not (ev.kind = EK_BUZZ) then
UI.Chat.Flash;
end;
// HISTORY
if BE_history in behaviour[ev.kind].trig then
if forceadd then
if UI.Chat.AddEvent(vCnt, ev.clone) then
if ev.flags and IF_no_matter = 0 then
if not vProto.getStatusDisable.OpenChat then
if not BossMode.isBossKeyOn and (BE_flashchat in behaviour[ev.kind].trig) then
if not (ev.kind = EK_BUZZ) and not QuietEvent then
UI.Chat.Flash;
// POP UP
if not BossMode.isBossKeyOn and (BE_popup in behaviour[ev.kind].trig) and not QuietEvent then
if not UI.Chat.IsVisible then
if not vProto.getStatusDisable.OpenChat then
if ev.flags and IF_no_matter = 0 then
UI.Chat.OpenOn(vCnt, focusOnChatPopup);
// SHAKE IT BABY!
if (ev.kind = EK_BUZZ) and not QuietEvent then
if not BossMode.isBossKeyOn and (BE_flashchat in behaviour[ev.kind].trig) then
if UI.Chat.Visible then
UI.Chat.Shake;
end;
function beh2str(kind: integer): RawByteString;
var
s: RawByteString;
begin
s := '';
if behaviour[kind].tiptimes then
s := s + 'times(' + IntToStr(behaviour[kind].tiptimeplus) + ')+';
if BE_tip in behaviour[kind].trig then
s := s + 'tip(' + IntToStr(behaviour[kind].tiptime) + ')+';
if BE_tray in behaviour[kind].trig then
s := s + 'tray+';
if BE_openchat in behaviour[kind].trig then
s := s + 'openchat+';
if BE_save in behaviour[kind].trig then
s := s + 'save+';
if BE_sound in behaviour[kind].trig then
s := s + 'sound+';
if BE_history in behaviour[kind].trig then
s := s + 'history+';
if BE_popup in behaviour[kind].trig then
s := s + 'popup+';
if BE_flashchat in behaviour[kind].trig then
s := s + 'flashchat+';
if be_BALLOON in behaviour[kind].trig then
s := s + 'balloon+';
Delete(s, length(s), 1);
result := s;
end;
procedure str2beh(const b, s: RawByteString);
var
i: byte;
begin
// for e:=EK_last downto 1 do
// for i:=0 to EK_last-1 do
for i := 1 to EK_last do
if b = event2str[i] + '-behaviour' then
behaviour[i] := str2beh(s)
end;
function str2beh(s: AnsiString): Tbehaviour;
const
tipstr = AnsiString('tip');
function extractPar(const lab: AnsiString): AnsiString;
var
i, j: integer;
begin
result := '';
i := AnsiPos(lab + '(', s);
if i > 0 then
begin
inc(i, length(lab) + 1);
j := PosEx(')', s, i);
// j:=i;
// while (length(s) > j) and (s[j]<>')') do
// inc(j);
if j > 0 then
result := Copy(s, i, j - i)
else
result := ''
end;
end;
var
tS: AnsiString;
begin
result.trig := [];
result.tiptime := 0;
result.tiptimes := False;
result.tiptimeplus := 0;
s := LowerCase(s);
result.tiptimes := ansiContainsText(s, AnsiString('times'));
try
tS := extractPar(AnsiString('times'));
if tS <> '' then
result.tiptimeplus := strToInt(tS)
except
end;
if ansiContainsText(s, tipstr) then
include(result.trig, BE_tip);
try
tS := extractPar(tipstr);
if tS <> '' then
result.tiptime := strToInt(tS)
except
end;
if ansiContainsText(s, AnsiString('tray')) then
include(result.trig, BE_tray);
if ansiContainsText(s, AnsiString('openchat')) then
include(result.trig, BE_openchat);
if ansiContainsText(s, AnsiString('save')) then
include(result.trig, BE_save);
if ansiContainsText(s, AnsiString('sound')) then
include(result.trig, BE_sound);
if ansiContainsText(s, AnsiString('history')) then
include(result.trig, BE_history);
if ansiContainsText(s, AnsiString('popup')) then
include(result.trig, BE_popup);
if ansiContainsText(s, AnsiString('flashchat')) then
include(result.trig, BE_flashchat);
if ansiContainsText(s, AnsiString('balloon')) then
include(result.trig, be_BALLOON);
end;
procedure hideTaskButtonIfUhave2;
begin
if not menuViaMacro then
ShowWindow(application.Handle, SW_HIDE)
end;
function RegisterHK(id: Integer; hk: Word): Boolean;
var
m: Integer;
begin
m := 0;
if hk and scCommand <> 0 then
Inc(m, MOD_WIN);
if hk and scShift <> 0 then
Inc(m, MOD_SHIFT);
if hk and scCtrl <> 0 then
Inc(m, MOD_CONTROL);
if hk and scAlt <> 0 then
Inc(m, MOD_ALT);
Result := RegisterHotKey(Hidden.Handle, id, m, LOBYTE(hk));
end;
function UpdateSWHotkeys: Boolean;
var
I: Integer;
begin
Result := False;
if (UI = nil) or (UI.CL = nil) then
Exit;
RemoveSWHotkeys;
Result := True;
for I := 0 to Length(Macros) - 1 do
if Macros[I].sw then
Result := RegisterHK(I, Macros[I].hk) and Result;
end;
procedure RemoveSWHotkeys;
var
I: Integer;
begin
for I := 0 to 200 do
UnregisterHotKey(Hidden.Handle, I);
end;
function AddToIgnoreList(c: TICQContact; const LocalOnly: Boolean = False): Boolean;
begin
Result := False;
if (c = nil) or IgnoreList.exists(c) then
Exit;
IgnoreList.Add(c);
Result := True;
if not LocalOnly then
Account.AccProto.AddToList(LT_SPAM, c);
SaveListsDelayed := True;
end;
function AddToQuietlist(c: TICQContact): Boolean;
begin
Result := False;
if (c = nil) or quietList.exists(c) then
Exit;
quietList.Add(c);
saveListsDelayed := True;
Result := True;
end;
procedure RemoveFromIgnorelist(c: TICQContact);
// var
// i : Byte;
begin
if (c = NIL) or not IgnoreList.exists(c) then
exit;
IgnoreList.remove(c);
// if ICQ.readList(LT_SPAM).exists(c) then
Account.AccProto.RemFromList(LT_SPAM, c);
// ICQ.SSI_DelVisItem(c.UID, FEEDBAG_CLASS_ID_IGNORE_LIST);
{ for i := Low(prefPages) to High(prefPages) do
if prefPages[i].Name = 'Ignore list' then
if Assigned(prefPages[i].frame) then
with TignoreFr(prefPages[i].frame).ignoreBox do
items.delete(items.indexOfObject(c));
}
saveListsDelayed := True;
end;
procedure removeFromQuietlist(c: TICQContact);
begin
if (c = nil) or not quietList.exists(c) then
Exit;
quietList.remove(c);
saveListsDelayed := True;
end;
procedure RemoveFromRoster(c: TICQContact; const WithHistory: Boolean = False);
var
grp: integer;
begin
if c = nil then
Exit;
if c.IsInRoster then
plugins.castEvList(PE_LIST_REMOVE, PL_ROSTER, c);
grp := c.group;
roasterLib.Remove(c);
// c.iProto.removeContact(c);
if withHistory then
DelHistWith(c.UID2cmp);
if (grp > 0) and (TRnQCList(Account.AccProto.ReadList(LT_ROSTER)).getCount(grp) = 0) then
if MessageDlg(GetTranslation('This group (%s) is empty! Do you want to delete it?', [groups.id2name(grp)]), mtConfirmation,
[mbYes, mbNo]) = mrYes then
roasterLib.removeGroup(grp);
c.group := 0;
end;
procedure realizeEvents(const kind_: integer; c: TICQContact);
var
k: integer;
ev0: Thevent;
begin
k := -1;
repeat
k := eventQ.getNextEventFor(c, k);
if (k >= 0) and (k < eventQ.count) then
begin
ev0 := Thevent(eventQ.items[k]);
if (kind_ < 0) or (ev0.kind = kind_) then
begin
eventQ.removeAt(k);
realizeEvent(ev0);
end;
end;
until (k < 0);
end;
procedure realizeEvent(ev: Thevent);
var
vCnt: TICQContact;
begin
if not Assigned(ev) then
Exit;
if Assigned(ev.otherpeer) then
vCnt := ev.otherpeer
else
vCnt := ev.who;
roasterLib.UpdateInPlace(vCnt);
TipRemove(ev);
if ev.kind in [EK_ADDEDYOU, EK_AUTHREQ, EK_MSG, EK_CONTACTS] then
NILifNIL(vCnt);
case ev.kind of
EK_ADDEDYOU:
if ev.who.isInList(LT_ROSTER) then
MsgDlg(GetTranslation('%s added you to his/her contact list.', [vCnt.displayed]), False, mtInformation)
else if MessageDlg(GetTranslation('%s added you to his/her contact list.\nDo you want to add him/her to your contact list?',
[vCnt.displayed]), mtConfirmation, [mbYes, mbNo]) = mrYes then
AddToRoster(vCnt, 0);
// EK_AUTHREQ:
// showAuthreq(vCnt, ev.getBodyText);
EK_incoming:
if showIncomingDlg then
msgDlg(getTranslation('%s is online', [vCnt.displayed]), False, mtInformation);
EK_MSG:
with UI.Chat do
begin
OpenOn(vCnt);
SetUnreadEvent(vCnt, ev);
MoveToEvent(vCnt, ev);
if not HasEvent(vCnt, ev) then
AddEvent(vCnt, ev.clone)
end;
// EK_CONTACTS:
// TselectCntsFrm.doAll(RnQmain, getTranslation('from %s', [vCnt.displayed]), getTranslation('Add selected contacts'),
// Account.AccProto, ev.cl.clone, RnQmain.AddContactsAction, [sco_multi, sco_selected], @wnd, False, False)
end;
try
// FreeAndNil(ev);
ev.Free;
except
end;
end;
function chopAndRealizeEvent: Boolean;
var
ev: Thevent;
begin
Result := False;
if eventQ = nil then
Exit;
ev := eventQ.pop;
if not Assigned(ev) then
Exit;
Result := True;
realizeEvent(ev);
saveInboxDelayed := True;
end;
procedure TrayAction;
begin
if not ChopAndRealizeEvent then
if UseSingleClickTray or not UI.CL.Visible then
UI.CL.ToggleVisible
else if not DoConnect then
UI.CL.ToggleVisible;
end;
function ints2cl(proto: TICQSession; a: types.TintegerDynArray): TRnQCList;
var
i: integer;
begin
result := TRnQCList.Create;
for i := 0 to Length(a) - 1 do
// result.add(contactsDB.get(TICQContact, IntToStr(a[i])));
// result.add(TICQSession.ContactsDB.get(TICQContact, a[i]));
result.add(proto.GetContact(IntToStr(a[i])));
end;
function DoLock: Boolean;
var
Pwd: String;
Res: Boolean;
begin
if (AccPass = '') and (Account.AccProto.pwd = '') then
begin
MsgDlg('Impossible to activate lock as no account or login password is defined', True, mtInformation);
Result := True;
end
else
begin
if UI.CL.Visible then
UI.CL.ToggleVisible;
UI.Chat.Hide;
locked := True;
Result := True;
if not startingLock then
saveCfgDelayed := True;
UI.EnterLockPassword(
GetTranslation('R&Q LOCKED'),
ReplaceText(GetTranslation('R&Q has been locked.\nYou need to type in your account or login password to unlock it.'), #10, '
')
);
end;
end;
procedure DoUnlock;
begin
locked := False;
if not startingLock then
saveCfgDelayed := True;
startingLock := False;
end;
function behactionName(a: Tbehaction): string;
begin
result := getTranslation(behactions2str[a])
end;
function mb(q: extended): string;
begin
result := floatToStrF(q / (1024 * 1024), ffFixed, 20, 1) + getTranslation('Mb')
end;
procedure applyUserCharset(f: Tfont);
begin
if userCharset >= 0 then
f.charset := userCharset
end;
function getLeadingInMsg(const s: string; ofs: integer = 1): string;
var
i: integer;
begin
i := 0;
while (i < length(s)) and (CharInSet(s[i + ofs], ['>', ' '])) do
inc(i);
result := Copy(s, ofs, i);
end;
procedure assignImgBmp(img: TImage; bmp: TBitmap);
begin
img.Picture.Bitmap.Destroy;
img.Picture.Bitmap.Assign(bmp);
// img.Picture.Bitmap.FreeImage;
img.TRANSPARENT := bmp.TRANSPARENT;
img.Height := bmp.Height;
img.Width := bmp.Width;
end;
procedure assignImgIco(img: TImage; ico: Ticon);
begin
img.Picture.Icon.Assign(ico);
img.Width := ico.Width * 2;
img.Height := ico.Height * 2;
end;
procedure MainFormHandleUpdate;
begin
if Assigned(StatusIcon) then
StatusIcon.handleChanged(UI.CL.Window);
updateSWhotkeys;
end;
procedure reloadCurrentLang();
begin
ClearLanguage;
LoadSomeLanguage;
TranslateWindows;
end;
procedure ToggleMainFormBorder(SetBorder: Boolean = False; HasBorder: Boolean = True);
begin
if SetBorder then
UI.CL.SetBorder(HasBorder)
else
begin
ShowMainBorder := not ShowMainBorder;
UI.CL.SetBorder(ShowMainBorder);
end;
end;
function unexistant(const uin: TUID): Boolean;
begin
result := not Account.AccProto.getMyInfo.equals(uin) and not Account.AccProto.readList(LT_ROSTER).exists(uin)
and not notInlist.exists(uin)
end;
function isAbort(const pluginReply: AnsiString): Boolean;
begin
result := (pluginReply > '') and (byte(pluginReply[1]) = PM_ABORT)
end;
procedure unroundWindow(hnd: THandle); inline;
begin
SetWindowRgn(hnd, 0, True)
end;
function binToStatus(const bin: TBytes): byte;
begin
if length(bin) < 4 then
result := byte(SC_UNK)
else
result := str2int(bin);
if not(result in [byte(SC_ONLINE) .. byte(SC_Last)]) then
result := byte(SC_UNK);
// if (resultSC_UNK) then result:=SC_UNK;
end;
function binToXStatus(const bin: TBytes): byte;
begin
if length(bin) < 6 then
result := 0
else
result := bin[5];
if result > High(XStatusArray) then
result := 0;
end;
function ExitFromAutoaway: Boolean;
begin
Result := False;
if autoaway.triggered = TR_none then
Exit;
if autoaway.setxstatus then
Account.AccProto.CurXStatus := autoaway.bakxstatus;
Account.AccProto.SetStatus(autoaway.bakstatus);
Result := True;
end;
function GetShiftState: Integer;
var
keys: TkeyboardState;
begin
result := 0;
if not GetKeyboardState(keys) then
exit;
if keys[VK_SHIFT] >= $80 then
inc(result, 1);
if keys[VK_CONTROL] >= $80 then
inc(result, 2);
if keys[VK_MENU] >= $80 then
inc(result, 4);
end;
procedure ProcessOevent(oe: Toevent);
begin
case oe.kind of
OE_msg:
SendProtoMsg(oe);
OE_CONTACTS:
SendICQContacts(oe.whom, oe.flags, oe.cl);
OE_AUTH:
oe.whom.auth;
OE_AUTHDENIED:
oe.whom.AuthDenied(oe.info);
// OE_ADDEDYOU:
// SendICQAddedYou(oe.whom);
end;
end;
function OnlFeature(const pr: TICQSession; check: Boolean = True): Boolean;
// True if online
begin
if check and (pr <> nil) then
result := pr.isOnline
else
result := False;
if not result then
MsgDlg('You must be online in order to use this feature', True, mtWarning)
end;
{$IFDEF Use_Baloons}
procedure ShowBalloonEv(ev: Thevent);
var
counter: Int64;
s: String;
begin
// str1:=ev.decrittedInfoOrg;
// if pos(#13,str1)<>0 then str1:=copy(str1,1,pos(#13,str1)-1);
counter := behaviour[ev.kind].tiptime;
// s := copy(ev.decrittedInfo,1,255);
s := Copy(ev.getBodyText, 1, 255);
if behaviour[ev.kind].tiptimes then
counter := counter * length(s) + behaviour[ev.kind].tiptimeplus * 100;
if counter < 100 then
counter := 100;
if (be_BALLOON in behaviour[ev.kind].trig) and (ev.flags and IF_offline = 0) and not Account.AccProto.getStatusDisable.tips then
case ev.kind of
EK_MSG, EK_AUTHREQ:
statusIcon.showballoon(counter, s, ev.who.displayed + ' ' + getTranslation(tipevent2str[ev.kind]), bitInfo{, event2imgName(ev.kind)});
else
statusIcon.showballoon(counter, ev.who.displayed, getTranslation(tipevent2str[ev.kind]), bitInfo{, event2imgName(ev.kind)});
end;
end;
{$ENDIF Use_Baloons}
function CheckAntispam(c: TICQContact): Boolean;
begin
result := False;
// if not (rosterLib.exists(c) or notInList.exists(c)) then
// if spam then
end;
procedure CheckBDays;
const
bds: TPicName = 'birthday';
PrefIsShowBDFirst = 'is-show-bd-first';
PrefShowBDFirst = 'show-bd-first';
PrefIsShowBDBefore = 'is-show-bd-before';
PrefShowBDBefore = 'show-bd-before';
var
bPrefIsShowBDFirst, bPrefIsShowBDBefore: Boolean;
iPrefShowBDFirst, iPrefShowBDBefore: integer;
cl: TRnQCList;
c: TICQContact;
k, l: integer;
ss: TPicName;
played, showInform: Boolean;
begin
// if not Assigned(Account.AccProto) then Exit;
iPrefShowBDFirst := 7;
iPrefShowBDBefore := 3;
bPrefIsShowBDFirst := MainPrefs.getPrefBoolDef(PrefIsShowBDFirst, True);
bPrefIsShowBDBefore := MainPrefs.getPrefBoolDef(PrefIsShowBDBefore, True);
if bPrefIsShowBDFirst then
MainPrefs.getPrefInt(PrefShowBDFirst, iPrefShowBDFirst);
if bPrefIsShowBDBefore then
MainPrefs.getPrefInt(PrefShowBDBefore, iPrefShowBDBefore);
if not bPrefIsShowBDFirst or not bPrefIsShowBDBefore then
exit;
cl := Account.AccProto.readList(LT_ROSTER).clone;
try
if Assigned(notInlist) then
cl.Add(notInlist);
cl.resetEnumeration;
while cl.hasMore do
begin
c := cl.getNext;
if c.uid = '' then
Continue;
k := c.Days2Bd;
if (k >= iPrefShowBDFirst) and (k >= iPrefShowBDBefore) then
Continue;
showInform := False;
if bPrefIsShowBDBefore and (k < iPrefShowBDBefore) then
showInform := True;
if bPrefIsShowBDFirst and not showInform then
begin
l := -1;
if trunc(c.LastBDInform) < trunc(Now) then
begin
l := trunc(Now) - trunc(c.LastBDInform);
end;
if l > iPrefShowBDFirst then
if k < iPrefShowBDFirst then
begin
showInform := True;
c.LastBDInform := Now;
end;
end;
if showInform then
begin
TipAdd3(NIL, NIL, c);
if k = 0 then // Play sound
begin
played := False;
if UseContactThemes and Assigned(ContactsTheme) then
begin
ss := TPicName(c.UID2cmp) + '.' + bds;
if (ContactsTheme.GetSound(ss) > '') then
begin
played := True;
ContactsTheme.PlaySound(ss)
end
else
begin
ss := TPicName('group.') + TPicName(AnsiLowerCase(groups.id2name(c.group))) + '.' + bds;
if (ContactsTheme.GetSound(ss) > '') then
begin
played := True;
ContactsTheme.PlaySound(ss)
end;
end;
end;
if not played then
theme.PlaySound(bds);
end;
end;
{ if not BossMode.isBossKeyOn and (BE_tip in behaviour[ev.kind].trig) and (ev.flags and IF_offline=0)
and not proto.getStatusDisable.tips then
if ev.flags and IF_no_matter = 0 then
try
TipAdd(ev);
except
end; }
end;
finally
cl.Free;
end;
end;
procedure ClearSpamFilter;
begin
SpamFilter.BadWords := '';
end;
function GetWidth(chk: TCheckBox): integer;
var
c: TBitmap;
begin
c := TBitmap.Create;
try
c.canvas.Font.Assign(chk.Font);
result := c.canvas.TextWidth(chk.Caption) + 16;
finally
c.Free;
end;
end;
function StringFromFile(const FileName: TFileName): RawByteString;
var
f: THandle;
Size: integer;
begin
result := '';
if FileName = '' then
exit;
f := FileOpen(FileName, fmOpenRead or fmShareDenyNone);
if PtrInt(f) >= 0 then
begin
Size := GetFileSize(f, nil);
SetLength(result, Size);
if FileRead(f, Pointer(result)^, Size) <> Size then
result := '';
FileClose(f);
end;
end;
procedure CacheType(const url, ctype: RawByteString);
begin
try
if not (ctype = '') then
begin
imgCacheInfo.WriteString(url, 'mime', ctype);
imgCacheInfo.UpdateFile;
end;
except end;
end;
function CacheImage(var mem: TMemoryStream; const url, ext: RawByteString): Boolean;
var
imgcache, fn: String;
hash: LongWord;
winimg: TWICImage;
begin
Result := False;
winimg := TWICImage.Create;
mem.Seek(0, 0);
try
winimg.LoadFromStream(mem);
except
if Assigned(winimg) then
winimg.Free;
Exit;
end;
if winimg.Empty then
begin
winimg.Free;
Exit;
end;
imgcache := myPath + 'Cache\Images\';
if not DirectoryExists(imgcache) then
ForceDirectories(imgcache);
hash := CalcMurmur2(BytesOf(url));
fn := imgcache + IntToStr(hash) + '.' + ext;
winimg.SaveToFile(fn);
try
imgCacheInfo.WriteString(url, 'ext', ext);
imgCacheInfo.WriteString(url, 'hash', IntToStr(hash));
imgCacheInfo.WriteInteger(url, 'width', winimg.Width);
imgCacheInfo.WriteInteger(url, 'height', winimg.Height);
imgCacheInfo.UpdateFile;
finally
winimg.Free;
end;
Result := True;
end;
procedure incDBTimer;
var
isSSRuning: BOOL;
begin
if saveDBtimer2 = 0 then
// Increase saveDBtimer to maximum. If ScreenSaver is running than it's 10 min
begin
SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, @isSSRuning, 0);
if (isSSRuning or isLocked) or not isMoved(4*(10*60)) or BossMode.isBossKeyOn then
saveDBtimer2 := max(saveDBtimer2, 600)
else
saveDBtimer2 := max(saveDBtimer2, 240);
end else
inc(saveDBtimer2, saveDBdelay);
end;
function UnFakeUIN(uin: Int64): TUID;
var
x: int64;
begin
// x := MaxLongint;
x := UIN;
while x > 4294967296 do
x := x - 4294967296;
result := IntToStr(x);
end;
procedure UpdatePrefsFrm;
begin
if Assigned(UI.Prefs) then
UI.Prefs.Reset;
end;
function ParseJSON(const RespStr: String; out JSON: TJSONObject): Boolean;
var
TmpJSON: TJSONValue;
begin
Result := False;
JSON := nil;
TmpJSON := TJSONObject.ParseJSONValue(TEncoding.UTF8.GetBytes(RespStr), 0);
if not Assigned(TmpJSON) then
Exit;
if TmpJSON is TJSONObject then
begin
JSON := TmpJSON as TJSONObject;
Result := True;
end else
FreeAndNil(TmpJSON);
end;
function ParseJSON(const RespStr: String; out JSON: TJSONArray): Boolean;
var
TmpJSON: TJSONValue;
begin
Result := False;
JSON := nil;
TmpJSON := TJSONObject.ParseJSONValue(TEncoding.UTF8.GetBytes(RespStr), 0);
if not Assigned(TmpJSON) then
Exit;
if TmpJSON is TJSONArray then
begin
JSON := TmpJSON as TJSONArray;
Result := True;
end else
FreeAndNil(TmpJSON);
end;
function TJSONHelper.GetValueSafe(const Key: String; out Data: T): Boolean;
var
ValVal: TJSONValue;
sTmp: String;
rTmp: RawByteString;
iTmp: Integer;
cTmp: Cardinal;
uTmp: UInt64;
bTmp: Boolean;
begin
Result := False;
if not (Self is TJSONObject) then
Exit;
ValVal := TJSONObject(Self).GetValue(Key);
if not Assigned(ValVal) then
Exit;
if TypeInfo(T) = TypeInfo(String) then
begin
// Decode UTF8
Result := ValVal.TryGetValue(sTmp);
PString(@Data)^ := UnUTF(sTmp);
end else if TypeInfo(T) = TypeInfo(RawByteString) then
begin
// Keep UTF8
Result := ValVal.TryGetValue(rTmp);
PRawByteString(@Data)^ := rTmp;
end else if TypeInfo(T) = TypeInfo(Integer) then
begin
Result := ValVal.TryGetValue(iTmp);
PInteger(@Data)^ := iTmp;
end else if TypeInfo(T) = TypeInfo(Cardinal) then
begin
Result := ValVal.TryGetValue(cTmp);
PCardinal(@Data)^ := cTmp;
end else if TypeInfo(T) = TypeInfo(UInt64) then
begin
Result := ValVal.TryGetValue(uTmp);
PUInt64(@Data)^ := uTmp;
end else if TypeInfo(T) = TypeInfo(Boolean) then
begin
Result := ValVal.TryGetValue(bTmp);
PBoolean(@Data)^ := bTmp;
end
// else raise Exception.Create('Unknown data type: ' + DataType.ToString);
end;
procedure ProcessICQLink(Data: String);
var
Contact: TICQContact;
DataArr: TArray;
begin
Data := Data.Replace('icq://', '').Replace('icq:', '');
DataArr := Data.Split(['/']);
if (DataArr[0] = 'people') or (DataArr[0] = 'info') or (DataArr[0] = 'profile') then
begin
Contact := Account.AccProto.getContact(DataArr[1]);
if Assigned(Contact) then
Contact.ViewInfo;
end else if DataArr[0] = 'chat' then
UI.Chat.OpenOn(Account.AccProto.GetContact(DataArr[1]))
else if (DataArr[0] = 's') or (DataArr[0] = 'sticker') or (DataArr[0] = 'stickers') then
begin
if OnlFeature(Account.AccProto) then
begin
UI.Chat.Call('openStickersManager', []);
UI.Chat.Call('startStickersSearch', ['storeid:' + DataArr[1]]);
end;
end;
end;
function IsTen: Boolean;
begin
Result := TOSVersion.Check(10)
end;
function AvatarUsePalette10: Boolean;
begin
Result := AvatarUsePalette and IsTen;
end;
function IsElevated: Boolean;
begin
Result := RnQSysUtils.IsElevated
end;
function GetActiveMonitorCount: Integer;
var
lpDisplayDevice: TDisplayDevice;
dwFlags: DWORD;
cc: DWORD;
begin
lpDisplayDevice.cb := sizeOf(lpDisplayDevice);
dwFlags := 0;
cc := 0;
Result := 0;
while EnumDisplayDevices(nil, cc, lpDisplayDevice, dwFlags) do
begin
inc(cc);
if lpDisplayDevice.StateFlags and $01 {AttachedToDesktop} <> 0 then
inc(Result)
end;
end;
procedure CloseAllChildWindows;
var
i: Integer;
c: Tcomponent;
begin
UI.CloseAllChildWindows;
i := childWindows.Count - 1;
while i >= 0 do
begin
c := childWindows.Items[i];
if c is TForm then
with c as TForm do
if visible then
begin
// childWindows.Items[i] := NIL;
close;
end;
dec(i);
end;
end;
initialization
ListsCS := TCriticalSection.Create;
finalization
FreeAndNil(ListsCS);
end.