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/events.pas

1061 lines
26 KiB
Plaintext

{
This file is part of R&Q.
Under same license
}
unit events;
{$I RnQConfig.inc}
{$WARN SYMBOL_PLATFORM OFF}
interface
uses
Winapi.Windows, System.Classes, System.Types, System.SysUtils, Vcl.Graphics, Vcl.ExtCtrls,
RDGlobal, RQThemes, RnQStrings, ICQCommon, ICQContacts;
const
EK_null = 00;
EK_msg = 01;
// EK_url = 02;
EK_contacts = 03;
// EK_file = 04;
EK_authReq = 05;
EK_AddedYou = 06;
EK_incoming = 07;
EK_outgoing = 08;
EK_auth = 09;
EK_authDenied = 10;
EK_statuschange = 11;
// EK_automsgreq = 12;
// EK_gcard = 13;
// EK_automsg = 14;
EK_typingBeg = 15;
EK_typingFin = 16;
EK_XstatusMsg = 17;
// EK_Xstatusreq = 18;
EK_BirthDay = 18;
EK_buzz = 19;
EK_last = 19;
// adding events remember to initialize supportedBehactions
const
ActiveEvents: array [0..14] of Integer = (EK_null, EK_msg, EK_contacts, EK_authReq, EK_AddedYou, EK_incoming, EK_outgoing,
EK_auth, EK_authDenied, EK_statuschange, EK_typingBeg, EK_typingFin, EK_XstatusMsg,
EK_BirthDay, EK_buzz);
event2str: array [0..EK_last] of String = (
'', 'msg', 'url', 'contacts', 'file', 'authreq', 'addedyou', 'incoming', 'outgoing', 'auth', 'authdenied',
'statuschange', 'automsgreq', 'gcard', 'automsg', 'begtyping', 'fintyping', 'xstatusmsg', 'xstatusreq', 'buzz'
);
event2ShowStr: array [0..EK_last] of String = (
'', Str_message, 'URL', 'Contacts', 'File', 'Authorization request',
'Added you', 'Incoming', 'Outgoing', 'Authorization given',
'Authorization denied', 'Status changed', 'Auto-message request',
'Green-card', 'Auto-message', 'Begin typing', 'Finish typing',
'XStatus message', 'XStatus request', 'Contact buzzing'
);
trayEvent2str: array [0..EK_last] of String = (
'', 'message from %s', 'URL from %s', 'contacts from %s','file',
'%s requires authorization', '%s added you', '%s is online','%s is offline',
'%s authorized you', '%s denied authorization', '%s changed status',
'auto-message requested by %s', 'greeting card from %s',
'auto-message for %s', 'Started typing', 'Finished typing', '%s changed status',
'XStatus requested by %s', ''
);
tipevent2str: array [0..EK_last] of String = (
'', Str_message, 'Sent you an URL', 'Sent you contacts', 'Sent you file',
'Requires authorization', 'Added you', 'is online', 'is offline',
'Authorized you', 'Denied authorization', 'Changed status',
'Requested your auto-message', 'Sent you a greeting card',
'Auto-message', 'Started typing', 'Finished typing', 'Changed status',
'Requested your XStatus', 'Tried to buzz you!'
);
tipBirth2str: array[0..2] of String = (
'Has a birthday!', 'Has a birthday tomorrow!', 'Has a birthday after tomorrow!'
);
histHeadevent2str: array [0..EK_last] of String = (
'', '', '', '', ' sent file', ': authorization request', '',
' is online', ' is offline', ': authorized', ': denied authorization', ': status %0:s',
': auto-message request', ': greeting card', ': auto-message', ' started typing',
' finished typing', ': status %0:s', ' requested your XStatus', '%0:s'
);
histBodyEvent2str: array [EK_null..EK_last] of String = (
'', '', '', '',
'Filename: %s\nCount: %d\nSize: %s\nMessage: %s', // EK_FILE
'%s', // EK_authReq
'Added you to his/her contact list', // EK_AddedYou
'', '', '', '%s', '', '',
'Watch the greeting card', '', '', '', '%s', '', ''
);
EI_flags=1;
// EI_shit=3;
EI_UID = 11;
EI_WID = 12;
HI_event=-1;
HI_hashed=-2;
HI_cryptMode=-3;
{$I PubRTTI.inc}
type
THeader = record
Prefix: String;
What: String;
Date: String;
end;
{$I NoRTTI.inc}
Phevent = ^Thevent;
Thevent = class
private
f_flags: Integer;
f_chat, f_who: TICQContact;
f_bin: TBytes;
f_txt: String;
class function toBytes(const bin: array of Variant): TBytes;
// function getAsUTF8: RawByteString;
public
old_f_info: RawByteString;
old_fIsMyEvent: Boolean;
outgoing: Boolean;
WID: String;
ID: UInt64;
kind: Integer;
when: TDateTime;
cryptMode: Byte;
cl: TRnQCList;
fImgElm: TRnQThemedElementDtls;
rowID: Integer;
HistoryToken: Cardinal;
PaintHeight: Integer;
otherpeer: TICQContact; // used to keep track of other peer when "who" is us
GUID: String;
class function new(kind_: Integer; chat_, who_: TICQContact; when_: TDateTime; const text_: String; const bin_: array of Variant; flags_: Integer; pID: UInt64 = 0; const guID: String = ''): Thevent;
class var hisFont: TFont;
class var myFont: TFont;
class var fntToken: Integer;
class constructor Create;
class destructor Destroy;
destructor Destroy; override;
function pic: TPicName;
function PicSize: TSize;
function Draw(DC: HDC; x, y: Integer): TSize;
procedure applyFont(font: Tfont);
function getFont: Tfont;
function Clone: Thevent;
function urgent: boolean;
function hasBody: Boolean;
procedure WriteToHistory(par: TUID = ''; patch: Boolean = False);
function old_getTextPart: String;
function old_getBinPart: TBytes;
procedure old_decryptInfo(const info: RawByteString);
function GetBodyBin: TBytes;
function GetBodyText: String;
function GetHeaderTexts: THeader;
function deleteBinary(const pMsg: String): String;
procedure parseData(const pMsg: String);
procedure setText(const txt: String);
procedure setBinary(const bin: array of Variant);
procedure setData(const txt: String; const bin: array of Variant);
procedure setFlags(f: integer);
procedure setWho(w: TICQContact);
function isHis(c: TICQContact): Boolean;
procedure setRawBin(bin: TBytes);
procedure setImgBin(bin: TBytes);
// published
property flags: Integer read f_flags write setFlags;
property chat: TICQContact read f_chat write f_chat;
property who: TICQContact read f_who write setWho;
property old_isMyEvent: Boolean read old_fIsMyEvent;
property binaryData: TBytes read f_bin;
property textData: String read f_txt;
property rawBin: TBytes write setRawBin;
end;
Thevents = TArray;
TeventQ = class(TList)
public
OnNewTop: procedure of object;
constructor Create;
destructor Destroy; override;
function Add(kind_: Integer; c: TICQContact; when: TDateTime; flags_: Integer): Thevent; overload;
procedure Add(ev: Thevent); overload;
function Get(Index: Integer): Thevent;
procedure Put(Index: Integer; Item: Thevent);
function Pop: Thevent;
function Top: Thevent;
function Empty: boolean;
function Chop: boolean;
function Find(kind_: Integer; c: TICQContact): Integer;
function RemoveAt(i: Integer): Boolean;
function FirstEventFor(c: TICQContact): Thevent;
function GetNextEventFor(c: TICQContact; idx: Integer): Integer;
function RemoveEvent(kind_: Integer; c: TICQContact): Boolean; overload;
function RemoveEvent(c: TICQContact): Boolean; overload;
procedure Clear; override;
procedure Notify(Ptr: Pointer; Action: TListNotification); override;
procedure FromString(const Qs: RawByteString);
function ToString: RawByteString;
procedure RemoveExpiredEvents;
property Items[Index: Integer]: Thevent read Get write Put; default;
end;
function Event2ImgName(e: Integer): TPicName;
var
HasMsgOK: Boolean;
HasMsgSRV: Boolean;
BlinkTimer: TTimer;
implementation
uses
System.StrUtils, System.DateUtils, Vcl.Forms,
{$IFDEF UNICODE}
System.AnsiStrings,
{$ENDIF UNICODE}
RDUtils, RnQBinUtils, RnQLangs, RnQCrypt, RnQGlobal, RnQPics,
utilLib, globalLib, roasterLib, pluginutil, history,
ICQConsts, protocol_ICQ, SQLiteDB;
function Thevent.Clone: Thevent;
begin
Result := Thevent.Create;
Result.rowID := rowID;
Result.ID := ID;
Result.WID := WID;
Result.kind := kind;
Result.chat := chat;
Result.who := who;
Result.otherpeer := otherpeer;
Result.when := when;
Result.old_fIsMyEvent := old_fIsMyEvent;
Result.outgoing := outgoing;
Result.f_txt := f_txt;
Result.f_bin := f_bin;
Result.old_f_info := old_f_info;
Result.flags := flags;
Result.cryptMode := cryptMode;
Result.HistoryToken := 0;
Result.fImgElm.ThemeToken := -1;
Result.GUID := GUID;
try
if cl <> nil then // By Rapid !
Result.cl := cl.clone
else
Result.cl := nil;
except
Result.cl := nil
end;
end;
destructor Thevent.Destroy;
begin
FreeAndNil(cl);
SetLength(f_txt, 0);
SetLength(f_bin, 0);
SetLength(old_f_info, 0);
inherited;
end;
procedure Thevent.WriteToHistory(par: TUID = ''; patch: Boolean = False);
begin
if (par = '') and Assigned(chat) then
par := chat.UID;
if (par = '') and Assigned(who) then
par := who.UID;
if patch then
SQLDB.PatchEvent(par, Self)
else
SQLDB.WriteEvent(par, Self);
end;
function Thevent.urgent: Boolean;
begin
Result := flags and IF_urgent > 0;
end;
procedure Thevent.applyFont(font:Tfont);
begin
if outgoing then
theme.ApplyFont('history.my', font) //history.myfont
else
theme.ApplyFont('history.his', font); //history.hisfont
end;
function Thevent.getFont:Tfont;
begin
if theme.token <> fntToken then
begin
if not Assigned(myFont) then
myFont := TFont.Create;
myFont.Assign(Screen.MenuFont);
theme.ApplyFont('history.my', myFont); //history.myfont
if not Assigned(hisFont) then
hisFont := TFont.Create;
hisFont.Assign(Screen.MenuFont);
theme.ApplyFont('history.his', hisFont); //history.hisFont
fntToken := theme.token;
end;
if outgoing then
Result := myFont
else
Result := hisFont;
end;
function Thevent.pic: TPicName;
begin
if (kind = EK_msg) then
begin
if HasMsgOK and ((Self.flags and IF_not_delivered) > 0)then
Result := PIC_MSG_BAD// + 'ok'
else
if HasMsgOK and
// ((Self.flags and (IF_delivered or IF_not_delivered)) > 0) then
// if
((Self.flags and IF_delivered) > 0) then
Result := PIC_MSG_OK// + 'ok'
else
if HasMsgSRV and ((Self.flags and IF_SERVER_ACCEPT) > 0)then
Result := PIC_MSG_SERVER// + 'ok'
else
Result := PIC_MSG// + 'ok'
end else if kind = EK_buzz then
Result := PIC_BUZZ
else
Result := event2imgName(kind)
end;
function Thevent.PicSize : TSize;
begin
if fImgElm.ThemeToken <> theme.token then
begin
fImgElm.picName := pic;
end;
PicSize := theme.GetPicSize(fImgElm);
end;
function Thevent.Draw(DC : HDC; x, y : Integer) : TSize;
begin
if fImgElm.ThemeToken <> theme.token then
begin
fImgElm.picName := pic;
end;
Draw := theme.drawPic(dc, Point(x, y), fImgElm);
end;
class constructor Thevent.Create;
begin
myFont := nil;
hisFont := nil;
HasMsgOK := False;
fntToken := -1;
end;
class destructor Thevent.Destroy;
begin
FreeAndNil(myFont);
FreeAndNil(hisFont);
end;
class function Thevent.new(kind_: Integer; chat_, who_: TICQContact; when_: TDateTime; const text_: String; const bin_: array of Variant; flags_: Integer; pID: UInt64 = 0; const guID: String = ''): Thevent;
begin
Result := Thevent.create;
Result.kind := kind_;
Result.chat := chat_;
Result.who := who_;
Result.when := when_;
Result.flags := flags_;
Result.outgoing := False;
Result.cl := nil;
Result.ID := pID;
Result.WID := guID;
Result.GUID := Account.AccProto.CreateNewGUID;
Result.f_txt := text_;
Result.f_bin := toBytes(bin_);
Result.HistoryToken := 0;
Result.fImgElm.ThemeToken := -1;
end;
function Thevent.deleteBinary(const pMsg: String): String;
var
i, k: Integer;
begin
Result := pMsg;
i := Pos(RnQImageExTag, Result);
while i > 0 do
begin
k := PosEx(RnQImageExUnTag, Result, i+12);
if k <= 0 then Break;
Delete(Result, i, k-i+13);
i := PosEx(RnQImageExTag, Result, i);
end;
i := Pos(RnQImageTag, Result);
while i > 0 do
begin
k := PosEx(RnQImageUnTag, Result, i+10);
if k <= 0 then Break;
Delete(Result, i, k-i+11);
i := PosEx(RnQImageTag, Result, i);
end;
end;
procedure Thevent.parseData(const pMsg: String);
begin
setText(deleteBinary(pMsg));
setRawBin(parseMsgImages(pMsg));
end;
function Thevent.GetHeaderTexts: THeader;
var
Dsp, Res: String;
s: TStrings;
begin
if not Assigned(Self) then
Exit;
if kind in [EK_INCOMING, EK_OUTGOING, EK_STATUSCHANGE] then
Res := statusNameExt2(binToStatus(f_bin), binToXStatus(f_bin))
else if kind = EK_XstatusMsg then
try
s := TStringList.Create;
str2strings(dword_Zero, f_txt, s);
Res := s[0];
s.Free;
except
Res := '';
end;
if Assigned(who) then
begin
if (kind = EK_buzz) then
if outgoing then
begin
Dsp := GetTranslation('You');
Res := ' ' + GetTranslation('tried to buzz this contact!');
end
else
begin
Dsp := who.displayed;
Res := ' ' + GetTranslation('tried to buzz you!');
end
else
Dsp := who.Displayed
end else
Dsp := '';
Result.Prefix := IfThen(IF_multiple and flags > 0, GetTranslation('(multi-send)'));
Result.What := Dsp + GetTranslation(histheadevent2str[kind], [Res]);
Result.Date := FormatDateTime(timeformat.chat, when);
end;
function Thevent.old_getTextPart: String;
var
sa: RawByteString;
i, k : integer;
begin
Result := '';
case kind of
EK_AUTH,
EK_ADDEDYOU,
EK_AUTHREQ,
EK_AUTHDENIED:
Result := old_UnUTF(old_f_info);
EK_statuschange:
if flags and IF_XTended_EVENT > 0 then
begin
if Length(old_f_info) > 6+4 then
Result := old_UnUTF(copy(old_f_info, 11, length(old_f_info)))
else
Result := '';
end;
EK_XstatusMsg:
if length(old_f_info) > 1+4 then
begin
i := _int_at(old_f_info, 2) + 1 + 4 + 1;
if (i > 0) and (length(old_f_info) > i+4) then
begin
k := _int_at(old_f_info, i);
Result := old_UnUTF(copy(old_f_info, i+4, k));
end;
end;
EK_CONTACTS:
begin
sa := old_f_info;
// backward compatibility (converts old format)
i := length(sa);
if i > 30 then i := 30;
while (i > 0) and (sa[i] <> #2) do dec(i);
if i <= 0 then
begin
Result := sa;
Exit;
end;
// s:=sa; Result:='';
while sa > '' do
begin
chop(#2, sa);
Result := Result + chop(', ', sa) + CRLF;
end;
end;
EK_MSG:
Result := old_UnUTF(deleteBinary(old_f_info));
end;
end;
function Thevent.old_getBinPart: TBytes;
begin
case kind of
EK_incoming,
EK_statuschange:
Result := bytesOf(copy(old_f_info, 1, 6));
EK_XstatusMsg:
Result := bytesOf(copy(old_f_info, 1, 1));
EK_MSG:
Result := parseMsgImages(old_f_info);
else
Result := nil;
end;
end;
procedure Thevent.old_decryptInfo(const info: RawByteString);
begin
case cryptMode of
CRYPT_SIMPLE:
old_f_info := decritted(info, StrToIntDef(who.uid, 0));
CRYPT_KEY1:
old_f_info := decritted(info, calculate_KEY1(AccPass));
end;
end;
function Thevent.GetBodyText: String;
var
str: TStrings;
begin
Result := '';
case kind of
EK_AUTH,
EK_ADDEDYOU:
Result := getTranslation(histBodyEvent2str[kind]);
EK_AUTHREQ,
EK_AUTHDENIED:
Result := getTranslation(histBodyEvent2str[kind], [f_txt]);
EK_statuschange:
if flags and IF_XTended_EVENT > 0 then
Result := f_txt;
EK_XstatusMsg:
try
str := TStringList.Create;
str2strings(dword_Zero, f_txt, str);
Result := str[1];
str.Free;
except end;
EK_CONTACTS, EK_MSG:
Result := f_txt;
end;
convertAllNewlinesToCRLF(Result);
end;
function Thevent.getBodyBin: TBytes;
begin
Result := f_bin;
end;
{
function Thevent.getAsUTF8: RawByteString;
begin
Result := UTF(f_txt);
end;
}
function Thevent.hasBody: Boolean;
begin
case kind of
EK_AUTH,
EK_ADDEDYOU,
EK_AUTHREQ,
EK_AUTHDENIED:
Result := True;
EK_statuschange:
Result := flags and IF_XTended_EVENT > 0;
EK_XstatusMsg,
EK_CONTACTS,
EK_MSG:
Result := Length(f_txt) > 0;
else
Result := False;
end;
end;
procedure Thevent.setText(const txt: String);
begin
setData(txt, []);
end;
procedure Thevent.setBinary(const bin: array of Variant);
begin
setData('', bin);
end;
class function Thevent.toBytes(const bin: array of Variant): TBytes;
var
v: Variant;
l, s: Integer;
function GetVarSize: Integer;
begin
case TVarData(v).VType of
varInteger: Result := SizeOf(Integer);
varByte: Result := SizeOf(Byte);
varBoolean: Result := SizeOf(Boolean);
else
Result := 0;
end;
end;
begin
SetLength(Result, 0);
if Length(bin) > 0 then
for v in bin do
with TVarData(v) do
begin
l := Length(Result);
s := GetVarSize;
SetLength(Result, l + s);
case VType of
varInteger:
Move(VInteger, Result[l], s);
varByte:
Move(VByte, Result[l], s);
varBoolean:
Move(VBoolean, Result[l], s);
end;
end;
end;
procedure Thevent.setRawBin(bin: TBytes);
begin
if (bin = nil) or (Length(bin) = 0) then
SetLength(f_bin, 0)
else
f_bin := bin;
end;
procedure Thevent.setImgBin(bin: TBytes);
var
OutSize: LongWord;
OutSizeBytes: array[0..3] of Byte;
begin
if (bin = nil) or (Length(bin) = 0) then
SetLength(f_bin, 0)
else begin
OutSize := Length(bin);
PInteger(@OutSizeBytes)^ := OutSize;
SetLength(f_bin, 4 + OutSize);
Move(OutSizeBytes[0], f_bin[0], 4);
Move(bin[0], f_bin[4], Length(bin));
end;
end;
procedure Thevent.setData(const txt: String; const bin: array of Variant);
begin
if not (txt = '') then
f_txt := txt;
if (Length(bin) > 0) then
f_bin := toBytes(bin);
end;
procedure Thevent.setFlags(f : integer);
begin
f_flags := f;
fImgElm.ThemeToken := -1;
fImgElm.Element := RQteDefault;
fImgElm.pEnabled := True;
end;
procedure Thevent.setWho(w : TICQContact);
begin
f_Who := w;
old_fIsMyEvent := (not Assigned(f_Who)) or Account.AccProto.IsMyAcc(w);
end;
function Thevent.isHis(c: TICQContact): Boolean;
begin
if Assigned(c) then
if Assigned(otherpeer) then
Result := c.equals(otherpeer)
else
Result := c.equals(who)
else
Result := False;
end;
constructor TeventQ.Create;
begin
inherited Create;
Blinking := False;
end;
function TeventQ.Find(kind_: Integer; c: TICQContact): Integer;
begin
Result := Count;
while Result > 0 do
begin
Dec(Result);
with Thevent(Items[Result]) do
if (kind = kind_) and isHis(c) then
Exit;
end;
Result := -1;
end;
function TeventQ.Get(Index: Integer): Thevent;
begin
Result := Thevent(inherited Get(Index));
end;
procedure TeventQ.Put(Index: Integer; Item: Thevent);
begin
inherited Put(Index, Item);
end;
procedure TeventQ.Add(ev:Thevent);
begin
// if sortBy = SB_event then
// roasterLib.sort(ev.who);
inherited Add(ev);
if (Count = 1) and Assigned(OnNewTop) then
OnNewTop;
ActionManager.Execute(AK_SAVEINBOX, SaveDelay);
roasterLib.UpdateInPlace(ev.who);
end;
function TeventQ.Add(kind_:integer; c: TICQContact; when:Tdatetime; flags_:integer):Thevent;
begin
Result := Thevent.create;
Result.kind := kind_;
Result.who := c;
Result.when := when;
Result.flags := flags_;
Add(Result);
end;
function TeventQ.Pop: Thevent;
begin
Result := Top;
RemoveAt(0);
end;
function TeventQ.Top: Thevent;
begin
if Count = 0 then
Result := nil
else
Result := First
end;
procedure TeventQ.Clear;
begin
while count > 0 do
pop.free;
end;
procedure TeventQ.Notify(Ptr: Pointer; Action: TListNotification);
var
State: Boolean;
begin
State := Count > 0;
if BlinkTimer.Enabled = State then
Exit;
Blinking := False;
BlinkTimer.Enabled := State;
if Assigned(StatusIcon) then
StatusIcon.Update;
end;
destructor TeventQ.Destroy;
begin
clear;
inherited;
end;
function TeventQ.Empty: Boolean;
begin
Result := Count = 0
end;
function TeventQ.Chop: Boolean;
begin
Result := False;
if not Empty then
begin
Pop.Free;
Result := True;
end;
end;
function TeventQ.RemoveAt(I: Integer): Boolean;
var
c, c2: TICQContact;
begin
Result := (I >= 0) and (I < Count);
if Result then
begin
c := Thevent(Items[I]).who;
c2 := Thevent(Items[I]).otherpeer;
Delete(I);
if I = 0 then
if Assigned(OnNewTop) then
OnNewTop;
if Assigned(c2) and (c2 <> c) then
roasterLib.UpdateInPlace(c2);
roasterLib.UpdateInPlace(c);
end;
end;
function TeventQ.FirstEventFor(c: TICQContact): Thevent;
var
I: Integer;
begin
Result := nil;
I := 0;
if Assigned(c) then
while I < Count do
begin
try
if Thevent(Items[I]).isHis(c) then
Exit(Thevent(Items[I]));
except end;
Inc(I);
end;
end;
function TeventQ.GetNextEventFor(c: TICQContact; idx: Integer): Integer;
var
i: integer;
begin
if idx >= 0 then
i:=idx
else
i := 0;
if Assigned(c) then
//Result := NIL;
while i < count do
begin
try
Result := i;
if Thevent(items[i]).isHis(c) then
exit;
except
Result := -1;
exit;
end;
inc(i);
end;
Result := -1;
end;
function TeventQ.RemoveEvent(kind_: integer; c: TICQContact): Boolean;
var
i: Integer;
begin
Result := False;
repeat
i := find(kind_, c);
if i >= 0 then
Result := RemoveAt(i);
until (i < 0);
end;
function TeventQ.RemoveEvent(c: TICQContact): Boolean;
var
I: Integer;
begin
Result := False;
I := Count;
while I > 0 do
begin
Dec(I);
if Thevent(Items[I]).isHis(c) then
begin
Result := True;
RemoveAt(i)
end
end;
end;
const
FK_KIND = 00;
FK_EXPIRES = 01;
FK_WHO = 02;
FK_CL = 03;
FK_WHEN = 04;
// FK_URGENT = 05; OBSOLETE
FK_BIN = 06;
FK_FLAGS = 07;
FK_WHO_STR = 12;
FK_TXT = 16; // UTF8 text
procedure TeventQ.FromString(const Qs: RawByteString);
var
t, l, uin, ofs: Integer;
e: Thevent;
s: RawByteString;
begin
ofs := 1;
try
clear;
e := nil;
while length(Qs) >= 8+ofs do
begin
t := integer((@Qs[ofs])^); // 1234
inc(ofs, 4);
l := integer((@Qs[ofs])^); // 5678
inc(ofs, 4);
if not within(0,l,1000000) or not within(0,t,100) or (length(Qs)-ofs < 8+l) then break; // corrupted file
s := Copy(Qs, ofs, l);
inc(ofs, l);
case t of
FK_KIND:
begin
if Assigned(e) then
try
if not Assigned(e.who) then
begin
Remove(e);
FreeAndNil(e);
end;
except end;
e := add(integer((@s[1])^), Account.AccProto.getmyInfo, 0, 0);
end;
FK_WHO:
begin
uin := integer((@s[1])^);
if uin > 0 then
e.who := Account.AccProto.getContact(uin)
else
e.who := nil;
if Assigned(e.who) then
NILifNIL(e.who, True)
else
e.who := Account.AccProto.getMyInfo;
end;
FK_WHO_STR:
begin
e.who := Account.AccProto.getContact(s);
if Assigned(e.who) then
NILifNIL(e.who, True)
else
e.who := Account.AccProto.getMyInfo;
end;
FK_WHEN: e.when := Tdatetime((@s[1])^);
FK_FLAGS: e.flags := integer((@s[1])^);
FK_BIN: e.rawBin := BytesOf(s);
FK_TXT: e.setText(s);
FK_CL:
if l > 0 then
begin
e.cl := TRnQCList.create;
e.cl.fromString(s, Account.AccProto.contactsDB);
end;
end;//case
end;
finally
ActionManager.Execute(AK_SAVECONFIG, SaveDelay);
end;
end;
function TeventQ.ToString: RawByteString;
var
i: Integer;
s: RawByteString;
begin
Result := '';
i := 0;
while i < count do
with Thevent(items[i]) do
begin
try
s := TLV2(FK_KIND, int2str(kind))
+ TLV2(FK_WHO, int2str(StrToIntDef(who.uid, 0)))
+ TLV2(FK_WHEN, dt2str(when))
+ TLV2(FK_FLAGS, int2str(flags))
+ TLV2(FK_BIN, StringOf(f_bin))
+ TLV2(FK_TXT, f_txt);
if assigned(cl) then s := s + TLV2(FK_cl, cl.toString);
if StrToIntDef(who.uid, 0) = 0 then
s := s + TLV2(FK_WHO_STR, who.uid);
Result := Result + s;
except
s := '';
end;
inc(i);
end;
end;
procedure TeventQ.RemoveExpiredEvents;
var
I: Integer;
Diff: Int64;
begin
I := 0;
while I < Count do
if Thevent(Items[I]).kind in [EK_incoming, EK_outgoing] then
begin
Diff := MilliSecondsBetween(Now, Thevent(Items[I]).when);
if Diff >= TempBlinkTime then
begin
Thevent(Items[I]).Free;
RemoveAt(I);
end else
Inc(I);
end else
Inc(I);
end;
// function event2imgidx(e:integer):integer;
function Event2ImgName(e: integer): TPicName;
begin
case e of
EK_MSG:
Result := PIC_MSG;
EK_CONTACTS:
Result := PIC_CONTACTS;
EK_ADDEDYOU:
Result := PIC_ADDEDYOU;
EK_AUTHREQ:
Result := PIC_AUTH_REQ;
EK_typingBeg:
Result := PIC_TYPING;
EK_typingFin:
Result := PIC_TYPING;
EK_incoming:
Result := PIC_INCOMING;
EK_outgoing:
Result := PIC_OUTGOING;
EK_BUZZ:
Result := PIC_BUZZ;
else
Result := PIC_OTHER_EVENT;
end;
end;
initialization
HasMsgOK := False;
BlinkTimer := TTimer.Create(nil);
finalization
FreeAndNil(BlinkTimer);
end.