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/ICQ/Protocol_ICQ.pas

900 lines
29 KiB
Plaintext

{
This file is part of R&Q.
Under same license
}
unit Protocol_ICQ;
{$I RnQConfig.inc}
interface
uses
Windows, Classes, outboxlib, events, ICQCommon, ICQContacts, ICQConsts, ICQSession, RDGLobal;
{$I NoRTTI.inc}
procedure SendICQContacts(cnt: TICQContact; flags:integer; cl:TRnQCList);
procedure ChangeXStatus(pICQ: TICQSession; const st: Byte; const StName: String = ''; const StText: String = '');
procedure LoggaICQPkt(const Prefix: String; What: TWhatLog; Data: String = '');
// function findICQViewInfo(c:TRnQContact):TviewInfoFrm;
procedure ProcessICQEvents(var thisICQ: TICQSession; ev: TICQEvent);
//function statusName(s:Tstatus):string;
function StatusNameExt2(s: Byte; extSts: Byte = 0; const Xsts: String = ''; const sts6: String = ''): String;
function Status2ImgName(s: Byte; inv: Boolean = False): TPicName;
function Status2ImgNameExt(s: byte; inv:boolean=FALSE; extSts: byte= 0):TPicName;
// function Visibility2ImgName(vi:Tvisibility):String;
function VisibilityName(vi: TVisibility): String;
function GetRnQVerFor(c: TICQContact): Integer;
procedure UpdateClients;
procedure SetProgBar(const proto: TICQSession; v: Double);
implementation
uses
Forms, SysUtils,
Types,
{$IFDEF UNICODE}
AnsiStrings,
{$ENDIF}
RnQBinUtils, RnQNet, RQUtil, RnQDialogs, RQlog,
rnqLangs, RnQStrings, RQThemes, RDFileUtil, RDUtils,
RnQTips, RnQTrayLib, RnQGlobal, RnQPics, RnQ_Avatars,
globalLib, utilLib, iniLib,
Protocols_all, ICQClients,
pluginutil, pluginLib,
roasterLib, themesLib, history,
wpDlg, mainDlg, chatDlg;
const
LogWhatNames: Array [TWhatLog] of String = ('CONNECTED', 'DISCONNECTED', 'CLIENT', 'SERVER', 'DC RCVD', 'DC SENT',
'CONNECTING', 'CLIENT', 'SERVER'{$IFDEF DEBUG_PACKETS}, 'UNKNOWN'{$ENDIF DEBUG_PACKETS});
procedure sendICQcontacts(cnt: TICQContact; flags:integer; cl:TRnQCList);
var
ev:THevent;
// c:Tcontact;
begin
// c:=Tcontact(contactsDB.get(TICQContact, uin));
plugins.castEv( PE_CONTACTS_SENT, cnt.uid, flags, cl);
Account.AccProto.SendContacts(cnt, flags, cl);
ev := Thevent.new(EK_CONTACTS, cnt, Account.AccProto.GetMyInfo, now, cl.tostring, [], flags);
ev.outgoing := True;
if logpref.writehistory and (BE_save in behaviour[ev.kind].trig) then
WriteToHistory(ev);
chatFrm.addEvent_openchat(cnt, ev);
end; // sendICQcontacts
procedure ChangeXStatus(pICQ: TICQSession; const st: Byte; const StName: String = ''; const StText: String = '');
var
b: Boolean;
begin
if (st in [Low(XStatusArray)..High(XStatusArray)])
// and ((UseOldXSt and (xsf_Old in XStatusArray[st].flags))
// or (xsf_6 in XStatusArray[st].flags))
then
begin
b := pICQ.curXStatus <> st;
// if StName > '' then
if StName <> getTranslation(ExtStsStrings[st].Cap) then
begin
b := True;
ExtStsStrings[st].Cap := Copy(StName, 1, 100);
end;
// if StText > '' then
if StText <> getTranslation(ExtStsStrings[st].Desc) then
begin
b := True;
ExtStsStrings[st].Desc := Copy(StText, 1, 255);
end;
// RnQmain.sBar.Repaint;
if b then
begin
// saveListsDelayed := True;
saveCfgDelayed := True;
// SaveExtSts;
end;
end;
end;
procedure LoggaICQPkt(const Prefix: String; What: TWhatLog; Data: String = '');
var
Head, s: String;
begin
if Prefix > '' then
s := Prefix + ' '
else
s := '';
s := s + LogWhatNames[What];
if not (Data = '') then
if What in [WL_connected, WL_disconnected, WL_connecting] then
begin
s := s + ' ' + Data;
Data := '';
end else
s := s + ' size: ' + IntToStr(Length(Data), 4);
Head := LogTimestamp + s;
LogProtoPkt(What, Head, Data)
end; // loggaPkt
function StatusNameExt2(s: Byte; extSts: Byte = 0; const Xsts: String = ''; const sts6: String = ''): String;
begin
if (s = Byte(SC_ONLINE)) and (extSts > 0) then
begin
if XSts > '' then
// result := getTranslation(Xsts)
result := Xsts
else
if sts6 > '' then
result := sts6
else
// result := getTranslation(XStatusArray[extSts].Caption)
result := getTranslation(status2ShowStr[TICQstatus(s)])
end
else
// if sts6 > '' then
// result := sts6
// else
result := getTranslation(status2ShowStr[TICQstatus(s)])
end;
function Status2ImgName(s: Byte; inv: Boolean = False): TPicName;
const
Prefix = 'status.';
begin
if s in [Byte(LOW(status2Img))..Byte(HIGH(status2Img))] then
Result := Prefix + status2Img[s]
else
Result := Prefix + status2Img[byte(SC_UNK)];
if inv then
Result := INVIS_PREFIX + Result;
end; // status2imgdx
function Status2ImgNameExt(s: byte; inv:boolean=FALSE; extSts: byte= 0):TPicName;
const
prefix = 'status.';
begin
if False{XStatusAsMain} and (extSts > 0) then
result := XStatusArray[extSts].PicName
else
begin
if s in [byte(SC_ONLINE)..byte(SC_Last)] then
result := prefix + status2Img[s]
else
result := prefix + status2Img[Byte(SC_UNK)];
if inv then
result := INVIS_PREFIX + result;
end;
end; // status2imgdx
function VisibilityName(vi: TVisibility): String;
begin
Result := GetTranslation(Visibility2ShowStr[vi])
end;
function findICQViewInfo(c: TICQContact): TRnQViewInfoForm;
var
i: Integer;
begin
with childWindows do
begin
i := 0;
while i < count do
begin
if TObject(items[i]) is TRnQViewInfoForm then
begin
result := TRnQViewInfoForm(items[i]);
if result.contact.equals(c) then
Exit;
end;
inc(i);
end;
end;
result := nil;
end; // findViewInfo
procedure ProcessICQEvents(var thisICQ: TICQSession; ev: TICQEvent);
var
c: TICQContact;
b: Boolean;
i: Integer;
sU, Temp: String;
e, TempEv: Thevent;
TempCh: TchatInfo;
TempHist: Thistory;
vS: AnsiString;
cuid: TUID;
DlgType: TMsgDlgType;
begin
c := thisICQ.eventContact;
thisICQ.eventContact := nil;
if Assigned(c) then
cuid := c.uid2cmp
else
cuid := '';
// these icqevents are associated with hevents
if ev in [TICQEvent(IE_msg), IE_buzz, IE_contacts, IE_authReq, IE_addedyou,
TICQEvent(IE_incoming), TICQEvent(IE_outgoing), IE_auth, IE_authDenied,
IE_statuschanged, IE_ack, IE_TYPING, IE_ackXStatus, IE_MultiChat] then
begin
e := Thevent.new(EK_null, nil, c, thisICQ.eventTime, '', [], thisICQ.eventFlags, thisICQ.eventMsgID, thisICQ.eventWID);
e.otherpeer := c;
if ev in [IE_contacts] then
begin
e.cl := thisICQ.eventContacts.clone;
e.cl.remove(thisICQ.getMyInfo);
e.cl.remove(c);
end else if ev in [IE_authreq] then
e.setText(UnUTF(thisICQ.eventMsgA))
else if ev in [IE_authDenied] then
e.setData(UnUTF(thisICQ.eventMsgA), []);
end else
e := nil;
with RnQmain do
case ev of
IE_serverAck:
begin
i := Account.acks.FindID(thisICQ.eventData);
if i >= 0 then // exploit only for automsgreq
// if Account.acks.getAt(i).kind = OE_msg then
begin
c := Account.acks.GetAt(i).whom;
TempHist := Thistory.Create(c.UID);
TempEv := TempHist.GetByMsgID(Account.acks.GetAt(i).ID);
if Assigned(TempEv) and TempEv.outgoing then
begin
TempEv.WID := thisICQ.eventWID;
TempEv.ID := thisICQ.eventMsgID;
if thisICQ.eventMsgA = 'sent' then
TempEv.flags := TempEv.flags or IF_Server_Accept
else if thisICQ.eventMsgA = 'failed' then
TempEv.flags := TempEv.flags or IF_Not_Delivered
else if thisICQ.eventMsgA = 'delivered' then
TempEv.flags := TempEv.flags or IF_Delivered;
TempHist.WriteMsgIDs(Account.acks.GetAt(i).ID, thisICQ.eventMsgID, thisICQ.eventWID);
TempHist.WriteMsgFlags(TempEv.ID, TempEv.flags);
chatFrm.ChatBox.UpdateMsgStatus(TempEv);
FreeAndNil(TempEv);
Account.acks.Delete(i);
end;
FreeAndNil(TempHist);
// if (Account.acks.getAt(i).flags and IF_Simple > 0) then
// Account.acks.Delete(i);
end
end;
IE_srvSomeInfo:
begin
i := Account.acks.FindID(thisICQ.eventData);
if i >= 0 then
with Account.acks.GetAt(i) do
begin
if kind = OE_MSG then
Account.acks.Delete(i);
end;
end;
IE_msgError:
begin
i := Account.acks.FindID(thisICQ.eventData);
if i >= 0 then
with Account.acks.GetAt(i) do
begin
if kind = OE_MSG then
begin
c := TICQContact(whom);
if not c.isOnline then
begin
if (info = 'MSG') then
begin
TempHist := Thistory.Create(c.UID);
TempEv := TempHist.getByMsgID(thisICQ.eventMsgID);
if Assigned(TempEv) and TempEv.outgoing then
begin
TempEv.flags := TempEv.flags or IF_not_delivered;// IF_MSG_OK;
TempHist.WriteMsgFlags(TempEv.ID, TempEv.flags);
chatFrm.ChatBox.UpdateMsgStatus(TempEv);
FreeAndNil(TempEv);
end;
FreeAndNil(TempHist);
end;
end else if (info = 'MSG') then
begin
TempHist := Thistory.Create(c.UID);
TempEv := TempHist.getByMsgID(thisICQ.eventMsgID);
if Assigned(TempEv) and TempEv.outgoing then
begin
TempEv.flags := TempEv.flags or IF_not_delivered;// IF_MSG_OK;
TempHist.WriteMsgFlags(TempEv.ID, TempEv.flags);
chatFrm.ChatBox.UpdateMsgStatus(TempEv);
FreeAndNil(TempEv);
end;
FreeAndNil(TempHist);
end;
// if not thisICQ.imVisibleTo(c) then
// addTempVisibleFor(5, c);
// Do not remove and wait for received ack if sending to offline client,
// because sometimes it's not even an error when multiple clients are connected
if not (thisICQ.eventInt = 4) then
Account.acks.Delete(i);
end;
end;
end;
IE_Missed_MSG:
begin
sU := getTranslation('You have missed %d messages from %s!', [thisICQ.eventMsgID, c.displayed]);
sU := sU + CRLF + getTranslation('Reason') + ': ' + getTranslation(icq_missed_msgs[thisICQ.eventInt]);
MsgDlg(sU, False, mtWarning);
end;
IE_sendingXStatus:
begin
thisICQ.eventMsgA := UTF(getXStatusMsgFor(c));
Vs := plugins.castEv(PE_XSTATUSMSG_SENDING, cuid, thisICQ.eventInt, UTF(thisICQ.eventNameA), thisICQ.eventMsgA);
if not isAbort(vS) then
begin
if (vS > '') then
if (ord(vS[1]) = PM_DATA) then
try
i := _int_at(vS, 2);
// thisICQ.eventName := UnUTF(_istring_at(vS, 2));
thisICQ.eventNameA := _istring_at(vS, 2); // In UTF8
if length(vS)>2+4+ i then
// thisICQ.eventMsg := UnUTF(_istring_at(vS, 2+4+ i))
thisICQ.eventMsgA := _istring_at(vS, 2+4 + i) // In UTF8
// else
// oe.info := send_msg;
except
thisICQ.eventNameA := '';
thisICQ.eventMsgA := UTF(getXStatusMsgFor(c));
end else if (ord(vS[1]) = PM_ABORT) then
begin
thisICQ.eventMsgA := '';
thisICQ.eventNameA := '';
end;
end else
begin
thisICQ.eventMsgA := '';
thisICQ.eventNameA := '';
end
end;
IE_ack:
begin
TempHist := Thistory.Create(c.UID);
// TODO: Make all previous messages delivered, not only last? Won't be necessary when server history is fully implemented
TempEv := TempHist.getByMsgID(thisICQ.eventMsgID);
if Assigned(TempEv) and TempEv.outgoing then
begin
TempEv.flags := TempEv.flags or IF_delivered;
TempHist.WriteMsgFlags(TempEv.ID, TempEv.flags);
chatFrm.ChatBox.UpdateMsgStatus(TempEv);
FreeAndNil(TempEv);
end;
FreeAndNil(TempHist);
end;
IE_authDenied:
begin
// case thisICQ.eventAccept of
// AC_OK:
begin
plugins.castEv( PE_AUTH_GOT, cUID);
MsgDlg(getTranslation('%s was grant you an autorization', [c.displayed]), False, mtInformation);
end;
// AC_denied:
begin
plugins.castEv( PE_AUTHDENIED_GOT, cUID);
MsgDlg(getTranslation('%s was declined you an autorization', [c.displayed]), False, mtInformation);
end;
// end;
end;
IE_toofast: MsgDlg('You''re sending too fast!', True, mtWarning);
IE_pause: MsgDlg('You''ll be disconnected soon because server was paused.', True, mtWarning);
IE_resume: MsgDlg('Server was resumed, you may not be disconnected after all.', True, mtWarning);
IE_MyInfoAck: MsgDlg('Your information has been saved', True, mtInformation);
IE_wpEnd:
if (wpFrm<>NIL) then
begin
if thisICQ.eventInt > 0 then
begin
wpFrm.N_Allresults := thisICQ.eventInt;
wpFrm.updateNumResults;
// CLBox.MsgDlg(getTranslation('End of search\nThere are %d more results but ICQ server shows only first ones, sorry.', [thisICQ.eventInt]),mtInformation);
end;
wpFrm.stopSearch;
end;
IE_userSimpleInfo:
begin
end;
IE_wpResult: if (wpFrm <> nil) then wpFrm.addResult(thisICQ.eventWP);
IE_serverSent: LoggaICQPkt(thisICQ.eventNameA, WL_serverSent, thisICQ.eventData);
IE_serverGot: LoggaICQPkt(thisICQ.eventNameA, WL_serverGot, thisICQ.eventData);
IE_connecting:
begin
LoggaICQPkt('', WL_connecting, thisICQ.eventAddress);
DisableSounds := False;
SetProgBar(thisICQ, 1/progLogonTotal);
end;
IE_loggin: SetProgBar(thisICQ, 2/progLogonTotal);
IE_connected: SetProgBar(thisICQ, 3/progLogonTotal);
IE_redirecting:
begin
LoggaICQPkt('', WL_connecting, thisICQ.eventAddress);
SetProgBar(thisICQ, 4/progLogonTotal);
end;
IE_redirected: SetProgBar(thisICQ, 5/progLogonTotal);
IE_almostonline: SetProgBar(thisICQ, 6/progLogonTotal);
IE_visibilityChanged:
if Assigned(c) then
begin
plugins.castEv(PE_VISIBILITY_CHANGED, cuid);
roasterLib.Update(c)
end
else
begin
plugins.castEv(PE_VISIBILITY_CHANGED, '');
end;
IE_error:
if thisICQ.eventError = EC_Login_Seq_Failed then
begin
SetProgBar(thisICQ, 0);
MsgDlg(GetTranslation(icqerror2str[thisICQ.eventError], [thisICQ.eventMsgA]), False, mtError)
end else if
(thisICQ.eventError = EC_MalformedMsg) or
(thisICQ.eventError = EC_FailedDecrypt) or
(thisICQ.eventError = EC_StoreProblem) then
MsgDlg(GetTranslation(icqerror2str[thisICQ.eventError], [thisICQ.eventMsgA]), False, mtError)
else if thisICQ.eventError = EC_AddContact_Error then
begin
DlgType := mtError;
case thisICQ.eventInt of
1: thisICQ.eventMsgA := 'Server DB error';
3: begin thisICQ.eventMsgA := 'Contact was already in your CL, fetching it again'; DlgType := mtInformation; end;
13: thisICQ.eventMsgA := 'Request was not executed';
17: thisICQ.eventMsgA := 'Too many contacts in your CL';
26: thisICQ.eventMsgA := 'Operation timed out on server';
end;
MsgDlg(getTranslation(icqerror2str[thisICQ.eventError], [getTranslation(thisICQ.eventMsgA)]), False, DlgType);
end else if thisICQ.eventError = EC_badContact then
loggaEvtS(format('ERROR: bad contact: %s',[cuid]))
else
begin
SetProgBar(thisICQ, 0);
theme.PlaySound(Str_Error); //sounds.onError);
if (thisICQ.eventError in [
EC_badUIN,
EC_badPwd,
EC_proxy_badPwd,
// EC_anotherLogin,
EC_invalidFlap,
EC_rateExceeded,
EC_missingLogin
]) then
StayConnected := False;
if thisICQ.eventError = EC_missingLogin then
if thisICQ.enterPWD then
if not (thisICQ.eventData = 'pwdonly') then
DoConnect
else
else
else if thisICQ.eventError = EC_badPwd then
begin
sU := thisICQ.pwd;
thisICQ.pwd := '';
if thisICQ.enterPWD then
begin
thisICQ.Disconnect;
DoConnect;
end else
thisICQ.pwd := sU;
end else if thisICQ.eventError = EC_other then
begin
case TICQAuthError(thisICQ.eventInt) of
EAC_Not_Enough_Data: Temp := 'Failed to get all the data required for starting a new session';
EAC_Unknown: Temp := 'Unknown error';
EAC_Wrong_Login: Temp := 'Wrong login';
EAC_Invalid_Request: Temp := 'Invalid request';
EAC_Auth_Required: Temp := 'Authorization required';
EAC_Req_Timeout: Temp := 'Request timeout';
EAC_Wrong_DevKey: Temp := 'Wrong DevId key';
EAC_Missing_Param: Temp := 'Missing required parameter';
EAC_Param_Error: Temp := 'Parameter error';
EAC_Rate_Limit: Temp := 'Request was rate limited';
EAC_SMS_Rate_Limit: Temp := 'SMS code is being requested too often';
end;
Temp := GetTranslation(Temp);
if not (thisICQ.eventMsgA = '') then
Temp := Temp + #13#10 + String(thisICQ.eventMsgA);
SetProgBar(thisICQ, 0);
MsgDlg(Temp, False, mtError);
end else if ShowDisconnectedDlg and not (thisICQ.eventError in [
EC_rateExceeded,
EC_cantConnect,
EC_socket,
EC_serverDisconnected,
EC_loginDelay,
EC_invalidFlap
]) then
MsgDlg(getTranslation(icqerror2str[thisICQ.eventError], [thisICQ.eventInt, thisICQ.eventMsgA]), False, mtError);
end;
IE_statuschanged:
begin
if not Assigned(c) then //or thisICQ.isMyAcc(c) then
begin
plugins.castEv(PE_STATUS_CHANGED, cuid, thisICQ.GetStatus, byte(thisICQ.eventOldStatus), thisICQ.IsInvisible, thisICQ.eventOldInvisible);
UpdateViewInfo(thisICQ.GetMyInfo);
if thisICQ.GetStatus <> Byte(SC_OFFLINE) then
LastStatus := thisICQ.GetStatus;
RnQmain.UpdateStatusGlyphs;
// roasterLib.Redraw;
end
else
begin
plugins.castEv(PE_STATUS_CHANGED, cuid, Byte(c.status), byte(thisICQ.eventOldStatus), False, thisICQ.eventOldInvisible);
UpdateViewInfo(c);
if c.IsInRoster then
begin
if c.status = SC_OFFLINE then
c.SetOffline;
roasterLib.Update(c);
e.setBinary([Integer(c.status), False, c.xStatus]);
if //(c.xStatus > 0) or
(c.xStatusDesc > '') then
begin
e.setText(c.xStatusDesc);
e.flags := e.flags or IF_XTended_EVENT;
end;
if incomingOnAway
and (thisICQ.eventOldStatus in [SC_AWAY, SC_NA])
and not (c.status in [SC_AWAY, SC_NA])
and (NoIncomingCounter = 0) then
behave(e, EK_incoming)
else
behave(e, EK_statuschange);
end;
if autoRequestXsts
and thisICQ.ImVisibleTo(c)
{and Assigned(reqXStatusQ)} then
//reqXStatusQ.Add(c);
end;
AutosizeDelayed := True;
if Assigned(chatFrm) then
chatFrm.RefreshTaskbarButtons;
end;
IE_avatar_changed:
if thisICQ.AvatarsSupport then
begin
if thisICQ.AvatarsAutoGet then
reqAvatarsQ.add(c)
else if c.icon.ToShow = IS_AVATAR then
ClearAoP(c, 0);
// if ShowAvt then
if TO_SHOW_ICON[CNT_ICON_AVT] then
UpdateInPlace(c);
end;
IE_ackXStatus:
begin
c.xStatusStr := excludeTrailingCRLF(UnUTF(thisICQ.eventMsgA));
c.xStatusDesc := excludeTrailingCRLF(UnUTF(thisICQ.eventData));
if c.xStatus > 0 then
e.setData(c.xStatusStr + dword_Zero + c.xStatusDesc, [c.xStatus])
else
e.setBinary([Byte(0)]);
behave(e, EK_XstatusMsg);
updateViewInfo(c);
end;
IE_online,
IE_offline:
begin
outboxCount := -1;
b := false;
// b := myStatus <> byte(SC_OFFLINE);
// myStatus:= thisICQ.getStatus;
b := b or (thisICQ.getStatus <> byte(SC_OFFLINE));
b := b or (ev = IE_offline);
setProgBar(thisICQ, 0);
if ev = IE_online then
begin
// <20><> <20><>
{$IFDEF Use_Baloons}
statusIcon.showballoon(2000, getTranslation('Online'), Application.MainForm.Caption, bitInfo{, 'status.' + status2Img[thisICQ.getStatus]});
{$ENDIF Use_Baloons}
checkupdate.checking := False;
outboxCount := timeBetweenMsgs;
StayConnected := AutoReconnect;
CleanDisconnect := False;
plugins.castEv(PE_CONNECTED);
toReconnectTime := 50;
// if Assigned(chatFrm) then
// chatFrm.CheckChatServerHistory;
end
else
begin
incDBTimer;
if clearPwdOnDSNCT and dontSavePwd then
if Assigned(thisICQ) and thisICQ.IsOffline then
thisICQ.pwd := '';
with TICQSession.contactsDB.clone do
begin
ForEach(procedure(cnt: TICQContact)
begin
TICQcontact(cnt).OfflineClear;
TICQcontact(cnt).Status := SC_UNK;
end);
Free;
end;
Account.acks.Clear;
plugins.castEv(PE_DISCONNECTED);
end;
NoIncomingCounter := 150;
if Assigned(chatFrm) then
with chatFrm do begin
RedrawTabs;
RefreshTaskbarButtons;
end;
RecentList.Add(thisICQ.GetICQContact('230490'));
RnQmain.UpdateStatusGlyphs;
if b then
roasterLib.rebuild;
UpdatePrefsFrm
end;
IE_numOfContactsChanged:
begin
contactsPnlStr := IntToStr(thisICQ.eventInt);
RnQmain.UpdateContactCount;
end;
IE_userinfo, IE_userinfoCP:
begin
plugins.castEv(PE_USERINFO_CHANGED, cuid);
UpdateViewInfo(c);
chatFrm.UpdateGraphics(c);
if thisICQ.IsMyAcc(c) then
RnQmain.UpdateCaption
else
roasterLib.UpdateInPlace(c);
TipsUpdateByCnt(c);
dbUpdateDelayed := True;
end;
IE_incoming:
begin
plugins.castEv(PE_STATUS_CHANGED, cuid, Byte(c.status), Byte(thisICQ.eventOldStatus), False, False);
c.OnlineSince := Now;
roasterLib.Update(c);
TCE(c.data^).lastIncoming := thisICQ.eventTime;
UpdateViewInfo(c);
// Update caps when contact goes online (min 2 sec between updates to ignore blinking contacts)
if (c.LastCapsUpdate = 0) or (Now - c.LastCapsUpdate > 2 * DTseconds) then
begin
c.LastCapsUpdate := Now;
thisICQ.GetContactInfo(c.UID2cmp, 'capabilities');
end;
e.setBinary([Integer(c.status), False, c.xStatus]);
if NoIncomingCounter = 0 then
behave(e, EK_INCOMING)
else if NoIncomingCounter < 50 then
begin
Inc(NoIncomingCounter, 10);
BoundInt(NoIncomingCounter, 0, 50);
end;
if autoRequestXsts
and thisICQ.imVisibleTo(c)
{and Assigned(reqXStatusQ)} then
//reqXStatusQ.Add(c)
;
autosizeDelayed := True;
end;
IE_outgoing:
begin
plugins.castEv(PE_STATUS_CHANGED, cuid, byte(c.status), byte(thisICQ.eventOldStatus), False, thisICQ.eventOldInvisible);
c.OfflineClear;
roasterLib.Update(c);
updateViewInfo(c);
behave(e, EK_OUTGOING);
AutosizeDelayed := True;
end;
IE_contactupdate:
begin
roasterLib.Update(c);
updateViewInfo(c);
if Assigned(chatFrm) then
chatFrm.RefreshTaskbarButtons;
// autosizeDelayed := True;
end;
IE_contactSelfDeleted:
begin
MsgDlg(getTranslation('Contact %s [%s] Deleted himself from your Contact List', [c.displayed, c.uin2Show]),
false, mtInformation, c.UID);
roasterLib.Update(c);
updateViewInfo(c);
// autosizeDelayed:=TRUE;
end;
IE_contactupdateInPlace:
roasterLib.UpdateInPlace(c);
IE_typing:
if not filterRefuse(c) then
begin
roasterLib.UpdateInPlace(c);
if thisICQ.eventInt = MTN_CLOSED then
begin
// <20><> <20><>
end;
if c.typing.bIsTyping then
behave(e, EK_typingBeg)
else
behave(e, EK_typingFin);
end;
IE_contacts:
if not e.cl.empty
and not isAbort(plugins.castEv( PE_CONTACTS_GOT,cuid,e.flags,e.when,e.cl )) then
begin
e.setText(e.cl.tostring);
if behave(e, EK_contacts) then
NILifNIL(c);
end;
IE_authReq:
if not filterRefuse(c, '',IF_auth) and not isAbort(plugins.castEv(PE_AUTHREQ_GOT, cuid, e.flags, e.when, thisICQ.eventMsgA))
and behave(e, EK_authReq) then
begin
UpdateInPlace(c);
NILifNIL(c);
end;
IE_addedYou:
if not isAbort(plugins.castEv( PE_ADDEDYOU_GOT, cuid, e.flags, e.when ))
and behave(e, EK_addedyou) then
begin
UpdateInPlace(c);
NILifNIL(c);
end;
IE_buzz:
begin
if behave(e, EK_buzz) then
NILifNIL(c);
end;
IE_msg, IE_MultiChat:
begin
if (ev = IE_MultiChat) and (thisICQ.eventAddress > '') then
e.who := thisICQ.getICQContact(thisICQ.eventAddress);
if thisICQ.eventEncoding = TEncoding.BigEndianUnicode then
begin
Temp := WideBEToStr(thisICQ.eventMsgA);
vS := plugins.castEv(PE_MSG_GOT, cuid, e.flags, e.when, Temp);
end else if thisICQ.eventEncoding = TEncoding.UTF8 then
begin
Temp := UnUTF(thisICQ.eventMsgA);
vS := plugins.castEv(PE_MSG_GOT, cuid, e.flags, e.when, Temp);
end else
begin
Temp := thisICQ.eventData;
vS := plugins.castEv(PE_MSG_GOT, cuid, e.flags, e.when, thisICQ.eventData);
end;
if not isAbort(vS) then
begin
if (vS > '') and (ord(vS[1]) = PM_DATA) then
begin
Temp := _istring_at(vS, 2);
e.flags := e.flags and not IF_CODEPAGE_MASK; // Clear Encodings flags
e.flags := e.flags and not IF_Bin; // Clear bin flag
end;
if Length(Temp) > 0 then
e.parseData(Temp);
if Length(thisICQ.eventBinData) > 0 then
e.setImgBin(thisICQ.eventBinData);
if behave(e, EK_msg) then
NILifNIL(c);
end;
end;
IE_serverHistoryReady:
begin
if Assigned(chatFrm) then
chatFrm.ChatBox.ShowServerHistoryNotif(c.UID2Cmp);
end;
IE_stickersupdate:
begin
if Assigned(chatFrm) then
chatFrm.LoadChatStickers;
end;
IE_stickersearchupdate:
begin
if Assigned(chatFrm) then
chatFrm.LoadSearchResults;
end;
end;
if thisICQ.eventFlags and IF_offline > 0 then
if ev in [IE_msg, IE_MultiChat, IE_addedYou, IE_authReq, IE_contacts] then
// we already played a sound for the first offline message, let's make no other sound
disableSounds :=TRUE;
if Assigned(e) then
e.free;
if Assigned(statusIcon) then
statusIcon.update;
end; // icqEvent
function GetRnQVerFor(c: TICQContact): Integer;
var
s: RawByteString;
capa: RawByteString;
i:integer;
begin
result:=0;
if c=NIL then exit;
if result > 0 then exit;
s:= TICQcontact(c).extracapabilities;
while s > '' do
begin
capa:=chop(17,0,s);
if pos(AnsiString('R&Qinside'),capa) > 0 then
begin
{ result:='R&Q ';
if capa[14] = #1 then
result:=result + 'lite '
else if capa[14] = #2 then
result:=result + 'test ';
}
i := (Byte(capa[15]) shl 8) + Byte(capa[16]);
if i > 0 then
result := i
else
result := Byte(@capa[14]);
end;
end;
{if result > 0 then exit;
for I := CAPS_Ext_CLI_First to CAPS_Ext_CLI_Last do
if i in c.capabilitiesBig then
begin
result := BigCapability[i].s;
if i = CAPS_big_QIP then
if c.lastupdate_dw > 0 then
result := result + ' (' +ip2str(c.lastupdate_dw) + ')';
Exit;
end;
if CAPS_big_SecIM in c.capabilitiesBig then
begin
result := PIC_CLI_TRIL;
exit;
end;
}
end; // getRnQVerFor
procedure UpdateClients;
var
cnt: TICQContact;
begin
if Assigned(Account.AccProto) and (Account.AccProto is TICQSession) then
for cnt in Account.AccProto.readList(LT_ROSTER) do
Account.AccProto.GetClientPicAndDesc4(cnt, cnt.ClientPic, cnt.ClientDesc)
end;
procedure SetProgBar(const proto: TICQSession; v: Double);
begin
if Assigned(proto) then
proto.progLogon := v
else
progStart := v;
if Assigned(RnQmain.CLBox) then
RnQmain.CLBox.SetProgress(v);
if assigned(StatusIcon) and assigned(StatusIcon.TrayIcon) then
StatusIcon.TrayIcon.Update;
end;
end.