home *** CD-ROM | disk | FTP | other *** search
/ Chip 2002 November / Chip_2002-11_cd1.bin / zkuste / delphi / kompon / d3456 / ICQ.ZIP / ICQ / Component / ICQClient.pas < prev    next >
Pascal/Delphi Source File  |  2002-08-16  |  81KB  |  2,094 lines

  1. unit ICQClient {v. 1.17};
  2. {************************************************
  3.     For updates checkout: http://www.cobans.net
  4.       (C) Alex Demchenko(alex@ritlabs.com)
  5. *************************************************}
  6. {$R-} //Remove range checking
  7.  
  8. interface
  9. uses
  10.   Windows, Classes {StringLists}, ExtCtrls {Used for TTimer}, WinSock,
  11.   ICQWorks, MySocket, ICQDirect;
  12.  
  13. type
  14.   //UIN Entry used in direct connections
  15.   PUINEntry = ^TUINEntry;
  16.   TUINEntry = record
  17.     UIN: LongWord;
  18.     Nick: ShortString;
  19.     CType: Word;
  20.     CTag: Word;
  21.     CGroupID: Word;
  22.     CGroup: ShortString;
  23.   end;
  24.  
  25.   //Callback function types
  26.   THandlePkt = procedure(Flap: TFlapHdr; Buffer: Pointer) of object;  
  27.   TOnMsgProc = procedure(Sender: TObject; Msg, UIN: String) of object;
  28.   TOnURLProc = procedure(Sender: TObject; Description, URL, UIN: String) of object;
  29.   TOnStatusChange = procedure(Sender: TObject; UIN: String; Status: LongWord) of object;
  30.   TOnOnlineInfo = procedure(Sender: TObject; UIN: String; Port: Word; InternalIP, ExternalIP: String; ProtoVer: Byte) of object;
  31.   TOnUserEvent = procedure(Sender: TObject; UIN: String) of object;
  32.   TOnUserGeneralInfo = procedure(Sender: TObject; UIN, NickName, FirstName, LastName, Email, City, State, Phone, Fax, Street, Cellular, Zip, Country: String; TimeZone: Byte; PublishEmail: Boolean) of object;
  33.   TOnUserWorkInfo = procedure (Sender: TObject; UIN, WCity, WState, WPhone, WFax, FAddress, WZip, WCountry, WCompany, WDepartment, WPosition, WOccupation, WHomePage: String) of object;
  34.   TOnUserInfoMore = procedure (Sender: TObject; UIN: String; Age: Word; Gender: Byte; HomePage: String; BirthYear: Word; BirthMonth: Word; BirthDay: Word; Lang1, Lang2, Lang3: String) of object;
  35.   TOnUserInfoAbout = procedure(Sender: TObject; UIN, About: String) of object;
  36.   TOnUserInfoInterests = procedure(Sender: TObject; UIN: String; Interests: TStringList) of object;
  37.   TOnUserInfoMoreEmails = procedure(Sender: TObject; UIN: String; Emails: TStringList) of object;
  38.   TOnUserInfoBackground = procedure(Sender: TObject; UIN: String; Pasts, Affiliations: TStringList) of object;
  39.   TOnUserFound = procedure(Sender: TObject; UIN, Nick, FirstName, LastName, Email: String; Status: Word; Gender, Age: Byte; SearchComplete: Boolean; Authorize: Boolean) of object;
  40.   TOnServerListRecv = procedure(Sender: TObject; SrvContactList: TList) of object;
  41.   TOnAdvMsgAck = procedure(Sender: TObject; UIN: String; ID: Word; AcceptType: Byte; AcceptMsg: String) of object;
  42.   TOnAutoMsgResponse = procedure(Sender: TObject; UIN: String; ID: Word; RespStatus: Byte; Msg: String) of object;
  43.   TOnContactListRecv = procedure(Sender: TObject; UIN: String; ContactList: TStringList) of object;
  44.   TOnContactListReq = procedure(Sender: TObject; UIN, Reason: String) of object;
  45.   TOnDirectPktAck = procedure(Sender: TObject; ID: Word) of object;
  46.   TOnSMSAck = procedure(Sender: TObject; Source, Network, MsgId: String; Deliverable: Boolean) of object;
  47.   TOnSMSReply = procedure(Sender: TObject; Source, SmsSender, Time, Text: String) of object;
  48.   TOnInfoChanged = procedure(Sender: TObject; InfoType: TInfoType) of object;
  49.   TOnAuthResponse = procedure(Sender: TObject; UIN: String; Granted: Boolean; Reason: String) of object;
  50.   TOnChangeResponse = procedure(Sender: TObject; ErrorCode: Word) of object;
  51.  
  52.  
  53.   {TICQNet -- Object implementing sending/receiving packets between Client and ICQ Server.}
  54.   TICQNet = class(TMySock)
  55.   private
  56.     FSrcBuf: array[0..MAX_DATA_LEN - 1] of Byte;        //.              .
  57.     FSrcLen: Word;                                      //.PACKET READING.
  58.     FNewFlap: TFlapHdr;                                 //.     DATA     .
  59.     FFlapSet: Boolean;                                  //.              .
  60.     FHandlePkt: THandlePkt;
  61.   protected
  62.     procedure OnReceive(Buffer: Pointer; BufLen: LongWord); override;
  63.   public
  64.     constructor Create;
  65.     destructor Destroy; override;
  66.     procedure Connect; override;
  67.   published
  68.     property OnConnectError;
  69.     property OnDisconnect;
  70.     property OnPktParseA;
  71.     property OnError;    
  72.     property OnHandlePkt: THandlePkt read FHandlePkt write FHandlePkt;
  73.   end;
  74.  
  75.   {TICQClient -- ICQ Component}  
  76.   TICQClient = class(TComponent)
  77.   private
  78.     FSock: TICQNet;                                     //Client's socket
  79.     FLUIN: LongWord;                                    //Client's UIN
  80.     FLPass: String;                                     //Client's password
  81.     FFirstConnect: Boolean;                             //Flag, used in login sequence
  82.     FSeq: Word;                                         //Main Flap Seq
  83.     FSeq2: Word;                                        //TO_ICQSRV Seq
  84.     FDSeq: Word;                                        //Direct connection Seq
  85.     FCookie: String;                                    //Temporary cookie, used in login sequence, we can use String type, becouse ICQ server doesn't send 0x00 chars in it's Cookie part
  86.     FIp: String;                                        //Ip to connect to
  87.     FPort: Word;                                        //Port to connect to
  88.     FDConnCookie: LongWord;                             //Direct connection cookie
  89.     FDirect: TDirectControl;                            //Direct control
  90.     //-- Proxy settings
  91.     FProxyType: TProxyType;                             //.
  92.     FProxyHost: String;                                 //.
  93.     FProxyPort: Word;                                   //. Proxy Configaration
  94.     FProxyAuth: Boolean;                                //.        Data
  95.     FProxyPass: String;                                 //.
  96.     FUserID: String;                                    //.
  97.     FResolve: Boolean;
  98.     //-- Events & other stuff --
  99.     FContactLst: TStrings;
  100.     FVisibleLst: TStrings;
  101.     FInvisibleLst: TStrings;
  102.     FOnMsg: TOnMsgProc;
  103.     FOnURL: TOnURLProc;
  104.     FOnOffMsg: TOnMsgProc;
  105.     FOnOffURL: TOnURLProc;
  106.     FOnLogin: TNotifyEvent;
  107.     FOnPktParse: TOnAdvPktParse;
  108.     FOnDPktParse: TOnAdvPktParse;
  109.     FOnConnectionFailed: TNotifyEvent;
  110.     FOnStatusChange: TOnStatusChange;
  111.     FOnUserOffline: TOnUserEvent;
  112.     FOnAddedYou: TOnUserEvent;
  113.     FOnUserGeneralInfo: TOnUserGeneralInfo;
  114.     FOnUserWorkInfo: TOnUserWorkInfo;
  115.     FOnUserInfoMore: TOnUserInfoMore;
  116.     FOnUserInfoAbout: TOnUserInfoAbout;
  117.     FOnUserInfoInterests: TOnUserInfoInterests;
  118.     FOnUserInfoMoreEmails: TOnUserInfoMoreEmails;
  119.     FOnUserInfoBackground: TOnUserInfoBackground;
  120.     FStatus: LongWord;
  121.     FDoPlain: Boolean;
  122.     FInfoChain: TStringList;
  123.     FLastInfoUin: String;
  124.     FLoggedIn: Boolean;
  125.     FRegisteringUIN: Boolean;
  126.     FRegPassword: String;
  127.     FOnUserFound: TOnUserFound;
  128.     FOnUserNotFound: TNotifyEvent;
  129.     FOnServerListRecv: TOnServerListRecv;
  130.     FOnAdvMsgAck: TOnAdvMsgAck;
  131.     FOnNewUINRegistered: TOnUserEvent;
  132.     FOnNewUINRefused: TNotifyEvent;
  133.     FOnAutoMsgResponse: TOnAutoMsgResponse;
  134.     FAutoAwayMsg: String;
  135.     FOnUnregisterOk: TNotifyEvent;
  136.     FOnUnregBadPass: TNotifyEvent;
  137.     FOnChangePasswordOk: TNotifyEvent;
  138.     FOnContactListRecv: TOnContactListRecv;
  139.     FOnContactListReq: TOnContactListReq;
  140.     FOnDirectPktAck: TOnDirectPktAck;
  141.     FOnSmsRefused: TNotifyEvent;
  142.     FOnSMSAck: TOnSMSAck;
  143.     FOnOnlineInfo: TOnOnlineInfo;
  144.     FUseDirect: Boolean;
  145.     FOnError: TOnError;
  146.     FTimer: TTimer;
  147.     FTimeout: Byte;
  148.     FOnSMSReply: TOnSMSReply;
  149.     FOnInfoChanged: TOnInfoChanged;
  150.     FOnChangePasswordError: TNotifyEvent;
  151.     FOnAuthSet: TNotifyEvent;
  152.     FOnAuthResponse: TOnAuthResponse;
  153.     FOnChangeResponse: TOnChangeResponse;
  154.     procedure InitNetICQ;
  155.     procedure OnIntError(Sender: TObject; ErrorType: TErrorType; ErrorMsg: String);
  156.     procedure HandlePacket(Flap: TFlapHdr; Data: Pointer);
  157.     procedure SetStatus(NewStatus: LongWord);
  158.     //-- Handling Snac packet procedures
  159.     procedure HSnac0407(Flap: TFlapHdr; Snac: TSnacHdr; Pkt: PRawPkt);
  160.     procedure HSnac1503(Flap: TFlapHdr; Snac: TSnacHdr; Pkt: PRawPkt);
  161.     procedure HSnac030B(Flap: TFlapHdr; Snac: TSnacHdr; Pkt: PRawPkt);
  162.     procedure HSnac131C(Flap: TFlapHdr; Snac: TSnacHdr; Pkt: PRawPkt);
  163.     procedure HSnac1319(Flap: TFlapHdr; Snac: TSnacHdr; Pkt: PRawPkt);
  164.     procedure HSnac1306(Flap: TFlapHdr; Snac: TSnacHdr; Pkt: PRawPkt);
  165.     procedure HSnac040b(Flap: TFlapHdr; Snac: TSnacHdr; Pkt: PRawPkt);
  166.     procedure HSnac1705(Flap: TFlapHdr; Snac: TSnacHdr; Pkt: PRawPkt);
  167.     procedure HSnac131b(Flap: TFlapHdr; Snac: TSnacHdr; Pkt: PRawPkt);
  168.     procedure HSnac130e(Flap: TFlapHdr; Snac: TSnacHdr; Pkt: PRawPkt);
  169.     procedure HDirectMsg(Sender: TObject; UIN: LongWord; Pak: PRawPkt; Len: LongWord);
  170.     procedure FTOnConnectError(Sender: TObject);
  171.     procedure FTOnDisconnect(Sender: TObject);
  172.     procedure FTOnDirectParse(Sender: TObject; Buffer: Pointer; BufLen: LongWord; Incoming: Boolean);
  173.     procedure FTOnPktParse(Sender: TObject; Buffer: Pointer; BufLen: LongWord; Incoming: Boolean);
  174.     procedure SetContactList(Value: TStrings);
  175.     procedure SetVisibleList(Value: TStrings);
  176.     procedure SetInvisibleList(Value: TStrings);
  177.     procedure OnTimeout(Sender: TObject);
  178.   public
  179.     constructor Create(AOwner: TComponent); override;
  180.     destructor Destroy; override;
  181.     procedure Login(Status: LongWord = S_ONLINE);
  182.     procedure RegisterNewUIN(const Password: String);
  183.     procedure Disconnect;
  184.     procedure SendMessage(UIN: LongWord; const Msg: String);
  185.     procedure SendURL(UIN: LongWord; const URL, Description: String);
  186.     function AddContact(UIN: LongWord): Boolean;
  187.     procedure RemoveContact(UIN: LongWord);
  188.     procedure RemoveContactVisible(UIN: LongWord);
  189.     procedure RemoveContactInvisible(UIN: LongWord);
  190.     procedure RequestInfo(UIN: LongWord);
  191.     procedure SearchByMail(const Email: String);
  192.     procedure SearchByUIN(UIN: LongWord);
  193.     procedure SearchByName(const FirstName, LastName, NickName, Email: String);
  194.     procedure SearchRandom(Group: Word);
  195.     procedure SearchWhitePages(const First, Last, Nick, Email: String; MinAge, MaxAge: Word; Gender: Byte; const Language, City, State, Country, Company, Department, Position, Occupation, Past, PastDesc, Interests, InterDesc, Affiliation, AffiDesc, HomePage: String; Online: Boolean);
  196.     procedure SetSelfInfoGeneral(NickName, FirstName, LastName, Email, City, State, Phone, Fax, Street, Cellular, Zip, Country: String; TimeZone: Byte; PublishEmail: Boolean);
  197.     procedure SetSelfInfoMore(Age: Word; Gender: Byte; const HomePage: String; BirthYear: Word; BirthMonth, BirthDay: Byte; Language1, Language2, Language3: String);
  198.     procedure SetSelfInfoAbout(const About: String);
  199.     procedure RequestContactList;
  200.     procedure DestroyUINList(var List: TList);
  201.     procedure SendSMS(const Destination, Text: String);
  202.     procedure SendMessageAdvanced(UIN: LongWord; const Msg: String; ID: Word; RTFFormat: Boolean);
  203.     function SendMessageDirect(UIN: LongWord; const Msg: String; RTFFormat: Boolean): Word;
  204.     procedure RequestAwayMsg(UIN: LongWord; ID: Word; ReqStatus: Byte);
  205.     procedure UnregisterUIN(const Password: String);
  206.     procedure ChangePassword(const NewPassword: String);
  207.     function DirectConnectionEstabilished(UIN: LongWord): Boolean;
  208.     function SendContacts(UIN: LongWord; Contacts: TStringList): Word;
  209.     function RequestContacts(UIN: LongWord; const Reason: String): Word;
  210.     procedure SendKeepAlive;
  211.     procedure SetAuthorization(AuthorizationRequired, WebAware: Boolean);
  212.     procedure SendAuthRequest(UIN: LongWord; Msg: String);
  213.     procedure SSLChangeStart(FirstUpload: Boolean);
  214.     procedure SSLChangeEnd;
  215.     procedure SSLAddGroup(GroupName: String; GroupID: Word);
  216.     procedure SSLAddUser(GroupID, UserID: Word; UIN, Name, SMSNumber: String; Authorize, UpdateUser: Boolean);
  217.     procedure SSLDelUser(GroupID, UserID: Word; UIN, Name, SMSNumber: String; Authorize: Boolean);
  218.     procedure SSLDelGroup(GroupName: String; GroupID: Word);
  219.     procedure SSLUpdateGroup(GroupName: String; GroupID: Word; UserIDs: TStringList);
  220.     procedure SSLAddUserIntoList(UserID: Word; UIN: String; BuddyType: Word);
  221.     procedure SSLDelUserFromList(UserID: Word; UIN: String; BuddyType: Word);
  222.     property Status: LongWord read FStatus write SetStatus;
  223.     property LoggedIn: Boolean read FLoggedIn;
  224.   published
  225.     property DisableDirectConnections: Boolean read FUseDirect write FUseDirect default False;
  226.     property ProxyType: TProxyType read FProxyType write FProxyType default P_NONE;
  227.     property ProxyHost: String read FProxyHost write FProxyHost;
  228.     property ProxyPort: Word read FProxyPort write FProxyPort;
  229.     property ProxyUserID: String read FUserID write FUserID;
  230.     property ProxyResolve: Boolean read FResolve write FResolve default False;
  231.     property ProxyAuth: Boolean read FProxyAuth write FProxyAuth default False;
  232.     property ProxyPass: String read FProxyPass write FProxyPass;
  233.     property UIN: LongWord read FLUIN write FLUIN;
  234.     property Password: String read FLPass write FLPass;
  235.     property ICQServer: String read FIp write FIp;
  236.     property ICQPort: Word read FPort write FPort;
  237.     property ConvertToPlaintext: Boolean read FDoPlain write FDoPlain;
  238.     property ContactList: TStrings read FContactLst write SetContactList;
  239.     property VisibleList: TStrings read FVisibleLst write SetVisibleList;
  240.     property InvisibleList: TStrings read FInvisibleLst write SetInvisibleList;
  241.     property AutoAwayMessage: String read FAutoAwayMsg write FAutoAwayMsg;
  242.     property OnLogin: TNotifyEvent read FOnLogin write FOnLogin;
  243.     property OnMessageRecv: TOnMsgProc read FOnMsg write FOnMsg;
  244.     property OnURLRecv: TOnURLProc read FOnURL write FOnURL;
  245.     property OnOfflineMsgRecv: TOnMsgProc read FOnOffMsg write FOnOffMsg;
  246.     property OnOfflineURLRecv: TOnURLProc read FOnOffURL write FOnOffURL;
  247.     property OnPktParse: TOnAdvPktParse read FOnPktParse write FOnPktParse;
  248.     property OnPktDirectParse: TOnAdvPktParse read FOnDPktParse write FOnDPktParse;
  249.     property OnConnectionFailed: TNotifyEvent read FOnConnectionFailed write FOnConnectionFailed;
  250.     property OnStatusChange: TOnStatusChange read FOnStatusChange write FOnStatusChange;
  251.     property OnUserOffline: TOnUserEvent read FOnUserOffline write FOnUserOffline;
  252.     property OnAddedYou: TOnUserEvent read FOnAddedYou write FOnAddedYou;
  253.     property OnUserGeneralInfo: TOnUserGeneralInfo read FOnUserGeneralInfo write FOnUserGeneralInfo;
  254.     property OnUserWorkInfo: TOnUserWorkInfo read FOnUserWorkInfo write FOnUserWorkInfo;
  255.     property OnUserInfoMore: TOnUserInfoMore read FOnUserInfoMore write FOnUserInfoMore;
  256.     property OnUserInfoAbout: TOnUserInfoAbout read FOnUserInfoAbout write FOnUserInfoAbout;
  257.     property OnUserInfoInterests: TOnUserInfoInterests read FOnUserInfoInterests write FOnUserInfoInterests;
  258.     property OnUserInfoMoreEmails: TOnUserInfoMoreEmails read FOnUserInfoMoreEmails write FOnUserInfoMoreEmails;
  259.     property OnUserInfoBackground: TOnUserInfoBackground read FOnUserInfoBackground write FOnUserInfoBackground;
  260.     property OnUserFound: TOnUserFound read FOnUserFound write FOnUserFound;
  261.     property OnUserNotFound: TNotifyEvent read FOnUserNotFound write FOnUserNotFound;
  262.     property OnServerListRecv: TOnServerListRecv read FOnServerListRecv write FOnServerListRecv;
  263.     property OnAdvancedMsgAck: TOnAdvMsgAck read FOnAdvMsgAck write FOnAdvMsgAck;
  264.     property OnNewUINRegistered: TOnUserEvent read FOnNewUINRegistered write FOnNewUINRegistered;
  265.     property OnNewUINRefused: TNotifyEvent read FOnNewUINRefused write FOnNewUINRefused;
  266.     property OnAutoMsgResponse: TOnAutoMsgResponse read FOnAutoMsgResponse write FOnAutoMsgResponse;
  267.     property OnUnregisterOk: TNotifyEvent read FOnUnregisterOk write FOnUnregisterOk;
  268.     property OnUnregisterBadPassword: TNotifyEvent read FOnUnregBadPass write FOnUnregBadPass;
  269.     property OnChangePasswordOk: TNotifyEvent read FOnChangePasswordOk write FOnChangePasswordOk;
  270.     property OnContactListRecv: TOnContactListRecv read FOnContactListRecv write FOnContactListRecv;
  271.     property OnContactListRequest: TOnContactListReq read FOnContactListReq write FOnContactListReq;
  272.     property OnDirectPacketAck: TOnDirectPktAck read FOnDirectPktAck write FOnDirectPktAck;
  273.     property OnSMSRefused: TNotifyEvent read FOnSmsRefused write FOnSmsRefused;
  274.     property OnSMSAck: TOnSMSAck read FOnSMSAck write FOnSMSAck;
  275.     property OnOnlineInfo: TOnOnlineInfo read FOnOnlineInfo write FOnOnlineInfo;
  276.     property OnError: TOnError read FOnError write FOnError;
  277.     property ConnectionTimeout: Byte read FTimeout write FTimeout;
  278.     property OnSMSReply: TOnSMSReply read FOnSMSReply write FOnSMSReply;
  279.     property OnInfoChanged: TOnInfoChanged read FOnInfoChanged write FOnInfoChanged;
  280.     property OnChangePasswordError: TNotifyEvent read FOnChangePasswordError write FOnChangePasswordError;
  281.     property OnAuthorizationChangedOk: TNotifyEvent read FOnAuthSet write FOnAuthSet;
  282.     property OnAuthResponse: TOnAuthResponse read FOnAuthResponse write FOnAuthResponse;
  283.     property OnSSLChangeResponse: TOnChangeResponse read FOnChangeResponse write FOnChangeResponse;
  284.   end;
  285.  
  286. procedure Register;
  287.  
  288. implementation
  289. {-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-}
  290.  
  291. {*** CONSTRUCTOR ***}
  292. constructor TICQNet.Create;
  293. begin
  294.   inherited Create;
  295. end;
  296.  
  297. {*** DESTRUCTOR ***}
  298. destructor TICQNet.Destroy;
  299. begin
  300.   inherited;
  301. end;
  302.  
  303. procedure TICQNet.Connect;
  304. begin
  305.   FSrcLen := 0;
  306.   FFlapSet := False;
  307.   inherited;
  308. end;
  309.  
  310. procedure TICQNet.OnReceive(Buffer: Pointer; BufLen: LongWord);
  311. var
  312.   i: LongWord;
  313. begin
  314.   inherited;
  315.   for i := 0 to BufLen - 1 do
  316.   begin
  317.     FSrcBuf[FSrcLen] := PByte(LongWord(Buffer) + i)^;
  318.     Inc(FSrcLen);
  319.     //Searching for the Flap header
  320.     if (FSrcLen >= TFLAPSZ) and (not FFlapSet) then
  321.     begin
  322.       FFlapSet := True;
  323.       FNewFlap := PFlapHdr(@FSrcBuf)^;
  324.       FNewFlap.DataLen := Swap16(FNewFlap.DataLen);
  325.       FNewFlap.Seq := Swap16(FNewFlap.Seq);
  326.       if FNewFlap.DataLen > 8192 then
  327.       begin
  328.         if Assigned(OnError) then
  329.           OnError(Self, ERR_PROTOCOL, 'Length of received packet exceeds maximum supported by protocol');
  330.         Disconnect;
  331.         Exit;
  332.       end;
  333.     end;
  334.     //Whole packet was received
  335.     if FSrcLen = FNewFlap.DataLen + TFLAPSZ then
  336.     begin
  337.       if FNewFlap.Ident <> $2a then
  338.       begin
  339.         if Assigned(OnError) then
  340.           OnError(Self, ERR_PROTOCOL, 'Received malformed packet');
  341.         Disconnect;
  342.         Exit;
  343.       end;
  344.       //Dump packet (if needed)
  345.       if Assigned(OnPktParseA) then
  346.         OnPktParseA(Self, @FSrcBuf, FSrcLen, True);
  347.       //Handling packet
  348.       if Assigned(OnHandlePkt) then
  349.         FHandlePkt(FNewFlap, Ptr(LongWord(@FSrcBuf) + TFLAPSZ));
  350.       //Preparing structures for receiving the next packet
  351.       FNewFlap.DataLen := 0;
  352.       FSrcLen := 0;
  353.       FFlapSet := False;
  354.     end;
  355.   end;
  356. end;
  357.  
  358.  
  359. {-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-}
  360.  
  361. {*** CONSTRUCTOR ***}
  362. constructor TICQClient.Create(AOwner: TComponent);
  363. var
  364.   WSA: TWSAData;
  365. begin
  366.   inherited;
  367.   InitMySocket(WSA);                    //Starting WSA
  368.  
  369.   FContactLst := TStringList.Create;    //Contact list
  370.   FVisibleLst := TStringList.Create;    //Visible list
  371.   FInvisibleLst := TStringList.Create;  //Invisible list
  372.  
  373.   FInfoChain := TStringList.Create;     //Info request chaing
  374.  
  375.   //Socket for working with TCP
  376.   FSock := TICQNet.Create;
  377.   FSock.OnError := OnIntError;
  378.  
  379.   FTimer := TTimer.Create(nil);         //Timeout timer
  380.   FTimer.OnTimer := OnTimeout;          //Set timeout event
  381.   FTimer.Enabled := False;              //Disable timer by default
  382.  
  383.   Randomize;                            //Initialize random generator
  384.   FSeq := Random($AAAA);                //Choose random seq, which is used in Flap header
  385.  
  386.   FDirect := nil;                       //Do not initialize direct control until we connect
  387. end;
  388.  
  389. {*** DESTRUCTOR ***}
  390. destructor TICQClient.Destroy;
  391. begin
  392.   if FDirect <> nil then
  393.     FDirect.Free;
  394.  
  395.   FSock.OnError := nil;
  396.   FSock.Free;
  397.  
  398.   FTimer.OnTimer := nil;
  399.   FTimer.Enabled := False;
  400.   FTimer.Free;
  401.  
  402.   //Free TStringList objects
  403.   FContactLst.Free;
  404.   FVisibleLst.Free;
  405.   FInvisibleLst.Free;
  406.   FInfoChain.Free;
  407.  
  408.   FinalMySocket; //Cleanup WSA
  409.   inherited;
  410. end;
  411.  
  412. {Set NetICQ's properties}
  413. procedure TICQClient.InitNetICQ;
  414. begin
  415.   //Assign properties
  416.   FSock.Host := FIp;
  417.   FSock.Port := FPort;
  418.   FSock.ProxyType := FProxyType;
  419.   FSock.ProxyHost := FProxyHost;
  420.   FSock.ProxyPort := FProxyPort;
  421.   FSock.ProxyUserID := FUserID;
  422.   FSock.ProxyAuth := FProxyAuth;
  423.   FSock.ProxyPass := FProxyPass;
  424.   FSock.UseProxyResolve := ProxyResolve;
  425.  
  426.   //Assign events
  427.   FSock.OnHandlePkt := HandlePacket;
  428.   FSock.OnDisconnect := FTOnDisconnect;
  429.   FSock.OnConnectError := FTOnConnectError;
  430.   FSock.OnPktParseA := FTOnPktParse;
  431. end;
  432.  
  433. {Called when error happened.}
  434. procedure TICQClient.OnIntError(Sender: TObject; ErrorType: TErrorType; ErrorMsg: String);
  435. begin
  436.   if Assigned(OnError) then
  437.     FOnError(Self, ErrorType, ErrorMsg);
  438. end;
  439.  
  440. {Logins to server.}
  441. procedure TICQClient.Login(Status: LongWord = S_ONLINE);
  442. begin
  443.   if FDirect <> nil then
  444.   begin
  445.     FDirect.OnError := nil;
  446.     FDirect.OnHandle := nil;
  447.     FDirect.OnPktDump := nil;
  448.     FDirect.Free;
  449.   end;
  450.  
  451.   if not DisableDirectConnections then
  452.   begin
  453.     FDirect := TDirectControl.Create(FLUIN);
  454.     FDirect.OnPktDump := FTOnDirectParse;
  455.     FDirect.OnHandle := HDirectMsg;
  456.     FDirect.OnError := OnIntError;
  457.  
  458.     //Assign proxy settings
  459.     FDirect.ProxyType := ProxyType;
  460.     FDirect.ProxyHost := ProxyHost;
  461.     FDirect.ProxyPort := ProxyPort;
  462.     FDirect.ProxyUserID := ProxyUserID;
  463.     FDirect.ProxyAuth := ProxyAuth;
  464.     FDirect.ProxyPass := ProxyPass;
  465.     FDirect.UseProxyResolve := ProxyResolve;
  466.   end;
  467.  
  468.   FDSeq := Random(High(Word));
  469.   FSeq2 := 2;
  470.   FCookie := '';
  471.   FFirstConnect := True;
  472.   FStatus := Status;
  473.   FLoggedIn := False;
  474.   FRegisteringUIN := False;
  475.  
  476.   InitNetICQ;
  477.   FTimer.Interval := FTimeout * 1000;
  478.   FTimer.Enabled := False;
  479.   if FTimeout <> 0 then
  480.     FTimer.Enabled := True;
  481.   FSock.Connect;
  482. end;
  483.  
  484. {Registers a new UIN.}
  485. procedure TICQClient.RegisterNewUIN(const Password: String);
  486. begin
  487.   FRegisteringUIN := True;
  488.   FRegPassword := Password;
  489.   FLoggedIn := False;
  490.   InitNetICQ;
  491.   FTimer.Interval := FTimeout * 1000;
  492.   FTimer.Enabled := True;
  493.   FSock.Connect;
  494. end;
  495.  
  496. {Disconnect user from server.}
  497. procedure TICQClient.Disconnect;
  498. begin
  499.   FTimer.Enabled := False;
  500.   FSock.Disconnect;
  501. end;
  502.  
  503. {Send a message to UIN.}
  504. procedure TICQClient.SendMessage(UIN: LongWord; const Msg: String);
  505. var
  506.   pkt: TRawPkt;
  507. begin
  508.   if not LoggedIn then Exit;
  509.   CreateCLI_SENDMSG(@pkt, 0, Random($FFFFAA), UIN, Msg, FSeq);
  510.   FSock.SendData(pkt, pkt.Len);
  511. end;
  512.  
  513. {Send an URL message to UIN.}
  514. procedure TICQClient.SendURL(UIN: LongWord; const URL, Description: String);
  515. var
  516.   pkt: TRawPkt;
  517. begin
  518.   if not LoggedIn then Exit;
  519.   CreateCLI_SENDURL(@pkt, 0, Random($FFFFAA), FLUIN, UIN, URL, Description, FSeq);
  520.   FSock.SendData(pkt, pkt.Len);
  521. end;
  522.  
  523. {Adds UIN to contact list after logon(when you are online), UIN automaticly
  524. added to ContactList TStrings. After adding the UIN you will receive status
  525. notifications. Returns True when UIN is added to the list(it wasn't there before).}
  526. function TICQClient.AddContact(UIN: LongWord): Boolean;
  527. var
  528.   pkt: TRawPkt;
  529. begin
  530.   Result := False;
  531.   if FContactLst.IndexOf(IntToStr(UIN)) < 0 then
  532.   begin
  533.     FContactLst.Add(IntToStr(UIN));
  534.     Result := True;
  535.   end else
  536.     Exit;
  537.   if not LoggedIn then Exit;
  538.   CreateCLI_ADDCONTACT(@pkt, IntToStr(UIN), FSeq);           {SNAC(x03/x04)}
  539.   FSock.SendData(pkt, pkt.Len);
  540. end;
  541.  
  542. {Removes UIN from contact list. Use while you are online.}
  543. procedure TICQClient.RemoveContact(UIN: LongWord);
  544. var
  545.   idx: Integer;
  546.   pkt: TRawPkt;
  547. begin
  548.   idx := FContactLst.IndexOf(IntToStr(UIN));
  549.   if idx > -1 then
  550.     FContactLst.Delete(idx);
  551.   if not LoggedIn then Exit;
  552.   CreateCLI_REMOVECONTACT(@pkt, UIN, FSeq);
  553.   FSock.SendData(pkt, pkt.Len);
  554. end;
  555.  
  556. {Removes UIN from the visible list. Use while you are online.}
  557. procedure TICQClient.RemoveContactVisible(UIN: LongWord);
  558. var
  559.   idx: Integer;
  560.   pkt: TRawPkt;
  561. begin
  562.   idx := FVisibleLst.IndexOf(IntToStr(UIN));
  563.   if idx > -1 then
  564.     FVisibleLst.Delete(idx);
  565.   if not LoggedIn then Exit;
  566.   CreateCLI_REMVISIBLE(@pkt, UIN, FSeq);
  567.   FSock.SendData(pkt, pkt.Len);
  568. end;
  569.  
  570. {Removes UIN from the invisible list. Use while you are online.}
  571. procedure TICQClient.RemoveContactInvisible(UIN: LongWord);
  572. var
  573.   idx: Integer;
  574.   pkt: TRawPkt;
  575. begin
  576.   idx := FInvisibleLst.IndexOf(IntToStr(UIN));
  577.   if idx > -1 then
  578.     FInvisibleLst.Delete(idx);
  579.   if not LoggedIn then Exit;
  580.   CreateCLI_REMINVISIBLE(@pkt, UIN, FSeq);
  581.   FSock.SendData(pkt, pkt.Len);
  582. end;
  583.  
  584. {Query info about UIN. As answer you will recieve theese events: OnUserWorkInfo,
  585. OnUserInfoMore, OnUserInfoAbout, OnUserInfoInterests, OnUserInfoMoreEmails,
  586. OnUserFound.}
  587. procedure TICQClient.RequestInfo(UIN: LongWord);
  588. var
  589.   pkt: TRawPkt;
  590. begin
  591.   if not LoggedIn then Exit;
  592.   FInfoChain.Add(IntToStr(UIN));
  593.   CreateCLI_METAREQINFO(@pkt, FLUIN, UIN, FSeq, FSeq2);
  594.   FSock.SendData(pkt, pkt.Len);
  595. end;
  596.  
  597. {Searches user by Mail}
  598. procedure TICQClient.SearchByMail(const Email: String);
  599. var
  600.   pkt: TRawPkt;
  601. begin
  602.   if not LoggedIn then Exit;
  603.   CreateCLI_SEARCHBYMAIL(@pkt, FLUIN, Email, FSeq, FSeq2);
  604.   FSock.SendData(pkt, pkt.Len);
  605. end;
  606.  
  607. {Searches user by UIN}
  608. procedure TICQClient.SearchByUIN(UIN: LongWord);
  609. var
  610.   pkt: TRawPkt;
  611. begin
  612.   if not LoggedIn then Exit;
  613.   CreateCLI_SEARCHBYUIN(@pkt, FLUIN, UIN, FSeq, FSeq2);
  614.   FSock.SendData(pkt, pkt.Len);
  615. end;
  616.  
  617. {Searches user by Name and other data}
  618. procedure TICQClient.SearchByName(const FirstName, LastName, NickName, Email: String);
  619. var
  620.   pkt: TRawPkt;
  621. begin
  622.   if not LoggedIn then Exit;
  623.   CreateCLI_SEARCHBYNAME(@pkt, FLUIN, FirstName, LastName, NickName, Email, FSeq, FSeq2);
  624.   FSock.SendData(pkt, pkt.Len);
  625. end;
  626.  
  627. {Searches random user from Group, where Group id could be found in RandGroups:
  628. array[1..11]...(ICQWorks.pas) constant. As answer you will receive OnUserFound
  629. notification, only one user will be found.}
  630. procedure TICQClient.SearchRandom(Group: Word);
  631. var
  632.   pkt: TRawPkt;
  633. begin
  634.   if not LoggedIn then Exit;
  635.   CreateCLI_SEARCHRANDOM(@pkt, FLUIN, Group, FSeq, FSeq2);
  636.   FSock.SendData(pkt, pkt.Len);
  637. end;
  638.  
  639. {Searches user in 'White Pages'. As answer you will receive OnUserFound notification
  640. when at least one user found or OnUserNotFound if such user does not exist.}
  641. procedure TICQClient.SearchWhitePages(const First, Last, Nick, Email: String; MinAge, MaxAge: Word;
  642.   Gender: Byte; const Language, City, State, Country, Company, Department, Position, Occupation,
  643.   Past, PastDesc, Interests, InterDesc, Affiliation, AffiDesc, HomePage: String; Online: Boolean);
  644. var
  645.   pkt: TRawPkt;
  646. begin
  647.   if not LoggedIn then Exit;
  648.   CreateCLI_SEARCHWP(@pkt, FLUIN, First, Last, Nick, Email, MinAge, MaxAge, Gender, StrToLanguageI(Language), City, State, StrToCountryI(Country),  Company,  Department, Position, StrToOccupationI(Occupation), StrToPastI(Past), PastDesc, StrToInterestI(Interests), InterDesc, StrToAffiliationI(Affiliation), AffiDesc, HomePage, Ord(Online), FSeq, FSeq2);
  649.   FSock.SendData(pkt, pkt.Len);
  650. end;
  651.  
  652. {Set general info about yourself. You can skip some parameters (eg. use '' -
  653. empty strings) to unspecify some info. }
  654. procedure TICQClient.SetSelfInfoGeneral(NickName, FirstName, LastName, Email, City, State, Phone, Fax, Street, Cellular, Zip, Country: String; TimeZone: Byte; PublishEmail: Boolean);
  655. var
  656.   pkt: TRawPkt;
  657. begin
  658.   if not LoggedIn then Exit;
  659.   //Truncate state if more then 3 chars
  660.   if Length(State) > 3 then
  661.     State := Copy(State, 0, 3);
  662.   CreateCLI_METASETGENERAL(@pkt, FLUIN, NickName, FirstName, LastName, Email, City, State, Phone, Fax, Street, Cellular, Zip, StrToCountryI(Country), TimeZone, PublishEmail, FSeq, FSeq2);
  663.   FSock.SendData(pkt, pkt.Len);
  664. end;
  665.  
  666. {Set more info about yourself.}
  667. procedure TICQClient.SetSelfInfoMore(Age: Word; Gender: Byte; const HomePage: String; BirthYear: Word; BirthMonth, BirthDay: Byte; Language1, Language2, Language3: String);
  668. var
  669.   pkt: TRawPkt;
  670. begin
  671.   if not LoggedIn then Exit;
  672.   CreateCLI_METASETMORE(@pkt, FLUIN, Age, Gender, HomePage, BirthYear, BirthMonth, BirthDay, StrToLanguageI(Language1), StrToLanguageI(Language2), StrToLanguageI(Language3), FSeq, FSeq2);
  673.   FSock.SendData(pkt, pkt.Len);
  674. end;
  675.  
  676. {Set info about yourself.}
  677. procedure TICQClient.SetSelfInfoAbout(const About: String);
  678. var
  679.   pkt: TRawPkt;
  680. begin
  681.   if not LoggedIn then Exit;
  682.   CreateCLI_METASETABOUT(@pkt, FLUIN, About, FSeq, FSeq2);
  683.   FSock.SendData(pkt, pkt.Len);
  684. end;
  685.  
  686. {Requests server side contact list. For more info look at OnServerListRecv event.}
  687. procedure TICQClient.RequestContactList;
  688. var
  689.   pkt: TRawPkt;
  690. begin
  691.   if not LoggedIn then Exit;
  692.   CreateCLI_REQROSTER(@pkt, FSeq);
  693.   FSock.SendData(pkt, pkt.Len);
  694. end;
  695.  
  696. {Releases memory used while parsing the server side contact list.}
  697. procedure TICQClient.DestroyUINList(var List: TList);
  698. var
  699.   i: Word;
  700. begin
  701.   if List = nil then Exit;
  702.   if List.Count > 0 then
  703.     for i := 0 to List.Count - 1 do
  704.       FreeMem(List.Items[i], SizeOf(TUINEntry)); //Free allocated memory for TUINEntry
  705.   List.Free;
  706.   List := nil;
  707. end;
  708.  
  709. {Sends sms message to Destination with Text.}
  710. procedure TICQClient.SendSMS(const Destination, Text: String);
  711. var
  712.   pkt: TRawPkt;
  713. begin
  714.   if (Length(Text) = 0) or (not LoggedIn) then Exit;
  715.   CreateCLI_SENDSMS(@pkt, FLUIN, Destination, Text, GetACP, GetSMSTime, FSeq, FSeq2);
  716.   FSock.SendData(pkt, pkt.Len);
  717. end;
  718.  
  719. {Sends Msg to UIN with advanced options, after UIN has got your message you will
  720. receive confirmation. ID - randomly generated value, may be used for packet acknowledgements
  721. (see OnAdvancedMsgAck event). If your Msg is in the RTF(RichText Format), then RTFFormat
  722. parameter should be True, otherwise - False. Beware of using the RTF Format, some clients
  723. (old versions of ICQ, linux & windows clones) don't support it.}
  724. procedure TICQClient.SendMessageAdvanced(UIN: LongWord; const Msg: String; ID: Word; RTFFormat: Boolean);
  725. var
  726.   pkt: TRawPkt;
  727. begin
  728.   if (Length(Msg) = 0) or (not LoggedIn) then Exit;
  729.   CreateCLI_SENDMSG_ADVANCED(@pkt, 0, ID, UIN, Msg, RTFFormat, FSeq);
  730.   FSock.SendData(pkt, pkt.Len);
  731. end;
  732.  
  733. {Send message to client dirrectly when it's possible}
  734. function TICQClient.SendMessageDirect(UIN: LongWord; const Msg: String; RTFFormat: Boolean): Word;
  735. var
  736.   lpkt: TRawPkt;
  737. begin
  738.   Result := 0;
  739.   if FDirect = nil then Exit;
  740.   if (FDSeq = 0) then Inc(FSeq);
  741.   Result := CreatePEER_MSG(@lpkt, Msg, RTFFormat, FDSeq);
  742.   if not FDirect.SendData(UIN, @lpkt) then
  743.     Result := 0;
  744. end;
  745.  
  746. {Request an away messages, set when user changes status.}
  747. procedure TICQClient.RequestAwayMsg(UIN: LongWord; ID: Word; ReqStatus: Byte);
  748. var
  749.   pkt: TRawPkt;
  750. begin
  751.   if (not LoggedIn) then Exit;
  752.   CreateCLI_REQAWAYMSG(@pkt, 0, ID, UIN, ReqStatus, FSeq);
  753.   FSock.SendData(pkt, pkt.Len);
  754. end;
  755.  
  756. {Unregister an UIN number.}
  757. procedure TICQClient.UnregisterUIN(const Password: String);
  758. var
  759.   pkt: TRawPkt;
  760. begin
  761.   if (not LoggedIn) then Exit;
  762.   CreateCLI_UNREGUIN(@pkt, FLUIN, Password, FSeq, FSeq2);
  763.   FSock.SendData(pkt, pkt.Len);
  764. end;
  765.  
  766. {Change current password to NewPassword.}
  767. procedure TICQClient.ChangePassword(const NewPassword: String);
  768. var
  769.   pkt: TRawPkt;
  770. begin
  771.   if (not LoggedIn) then Exit;
  772.   CreateCLI_METASETPASS(@pkt, FLUIN, NewPassword, FSeq, FSeq2);
  773.   FSock.SendData(pkt, pkt.Len);
  774. end;
  775.  
  776. {Returns True if direct connection with UIN has been estabilished.}
  777. function TICQClient.DirectConnectionEstabilished(UIN: LongWord): Boolean;
  778. begin
  779.   Result := False;
  780.   if FDirect = nil then Exit;
  781.   Result := FDirect.ConnectionEstabilished(UIN);
  782. end;
  783.  
  784. {Sends contacts to UIN. Returns True if contacts were send.}
  785. function TICQClient.SendContacts(UIN: LongWord; Contacts: TStringList): Word;
  786. var
  787.   pkt: TRawPkt;
  788. begin
  789.   Result := 0;
  790.   if FDirect = nil then Exit;
  791.   if (FDSeq = 0) then Inc(FSeq);
  792.   Result := CreatePEER_CONTACTS(@pkt, Contacts, FDSeq);
  793.   if not FDirect.SendData(UIN, @pkt) then
  794.     Result := 0;
  795. end;
  796.  
  797. {Request contacts from user.}
  798. function TICQClient.RequestContacts(UIN: LongWord; const Reason: String): Word;
  799. var
  800.   lpkt: TRawPkt;
  801. begin
  802.   Result := 0;
  803.   if FDirect = nil then Exit;
  804.   if (FDSeq = 0) then Inc(FSeq);
  805.   Result := CreatePEER_CONTACTREQ(@lpkt, Reason, FDSeq);
  806.   if not FDirect.SendData(UIN, @lpkt) then
  807.     Result := 0;
  808. end;
  809.  
  810. {Send keep alive packet.}
  811. procedure TICQClient.SendKeepAlive;
  812. var
  813.   lpkt: TRawPkt;
  814. begin
  815.   if (not LoggedIn) then Exit;
  816.   CreateCLI_KEEPALIVE(@lpkt, FSeq);
  817.   FSock.SendData(lpkt, lpkt.Len);
  818. end;
  819.  
  820. {Set AuthorizationRequired and WebAware options.}
  821. procedure TICQClient.SetAuthorization(AuthorizationRequired, WebAware: Boolean);
  822. var
  823.   lpkt: TRawPkt;
  824. begin
  825.   if (not LoggedIn) then Exit;
  826.   CreateCLI_METASETPERMISSIONS(@lpkt, FLUIN, AuthorizationRequired, WebAware, FSeq, FSeq2);
  827.   FSock.SendData(lpkt, lpkt.Len);
  828. end;
  829.  
  830. {Request authorization.}
  831. procedure TICQClient.SendAuthRequest(UIN: LongWord; Msg: String);
  832. var
  833.   lpkt: TRawPkt;
  834. begin
  835.   if (not LoggedIn) then Exit;
  836.   CreateCLI_REQAUTH(@lpkt, UIN, Msg, FSeq);
  837.   FSock.SendData(lpkt, lpkt.Len);
  838. end;
  839.  
  840. {Start changes of SSL.}
  841. procedure TICQClient.SSLChangeStart(FirstUpload: Boolean);
  842. var
  843.   lpkt: TRawPkt;
  844. begin
  845.   if (not LoggedIn) then Exit;
  846.   CreateCLI_ADDSTART(@lpkt, FirstUpload, FSeq);
  847.   FSock.SendData(lpkt, lpkt.Len);
  848. end;
  849.  
  850. {End changes of SSL.}
  851. procedure TICQClient.SSLChangeEnd;
  852. var
  853.   lpkt: TRawPkt;
  854. begin
  855.   if (not LoggedIn) then Exit;
  856.   CreateCLI_ADDEND(@lpkt, FSeq);
  857.   FSock.SendData(lpkt, lpkt.Len);
  858. end;
  859.  
  860. {Add group to SSL.}
  861. procedure TICQClient.SSLAddGroup(GroupName: String; GroupID: Word);
  862. var
  863.   lpkt: TRawPkt;
  864. begin
  865.   if (not LoggedIn) then Exit;
  866.   CreateCLI_ADDBUDDY(@lpkt, GroupName, '', '', GroupID, 0, BUDDY_GROUP, False, FSeq);
  867.   FSock.SendData(lpkt, lpkt.Len);
  868. end;
  869.  
  870. {Add user to SSL.}
  871. procedure TICQClient.SSLAddUser(GroupID, UserID: Word; UIN, Name, SMSNumber: String; Authorize, UpdateUser: Boolean);
  872. var
  873.   lpkt: TRawPkt;
  874. begin
  875.   if (not LoggedIn) then Exit;
  876.   if not UpdateUser then
  877.     CreateCLI_ADDBUDDY(@lpkt, UIN, Name, SMSNumber, GroupID, UserID, BUDDY_NORMAL, Authorize, FSeq)
  878.   else
  879.     CreateCLI_UPDATEBUDDY(@lpkt, UIN, Name, SMSNumber, GroupID, UserID, BUDDY_NORMAL, Authorize, FSeq);
  880.   FSock.SendData(lpkt, lpkt.Len);
  881. end;
  882.  
  883. {Remove user from SSL.}
  884. procedure TICQClient.SSLDelUser(GroupID, UserID: Word; UIN, Name, SMSNumber: String; Authorize: Boolean);
  885. var
  886.   lpkt: TRawPkt;
  887. begin
  888.   if (not LoggedIn) then Exit;
  889.   CreateCLI_DELETEBUDDY(@lpkt, UIN, Name, SMSNumber, GroupID, UserID, BUDDY_NORMAL, Authorize, True, FSeq);
  890.   FSock.SendData(lpkt, lpkt.Len);
  891. end;
  892.  
  893. {Remove group from SSL.}
  894. procedure TICQClient.SSLDelGroup(GroupName: String; GroupID: Word);
  895. var
  896.   lpkt: TRawPkt;
  897. begin
  898.   if (not LoggedIn) then Exit;
  899.   CreateCLI_DELETEBUDDY(@lpkt, GroupName, '', '', GroupID, 0, BUDDY_GROUP, False, True, FSeq);
  900.   FSock.SendData(lpkt, lpkt.Len);
  901. end;
  902.  
  903. {Update group's ids.}
  904. procedure TICQClient.SSLUpdateGroup(GroupName: String; GroupID: Word; UserIDs: TStringList);
  905. var
  906.   lpkt: TRawPkt;
  907. begin
  908.   if (not LoggedIn) then Exit;
  909.   CreateCLI_UPDATEGROUP(@lpkt, GroupName, GroupID, UserIDs, FSeq);
  910.   FSock.SendData(lpkt, lpkt.Len);
  911. end;
  912.  
  913. {Add user to the specified SSL's list.}
  914. procedure TICQClient.SSLAddUserIntoList(UserID: Word; UIN: String; BuddyType: Word);
  915. var
  916.   lpkt: TRawPkt;
  917. begin
  918.   if (not LoggedIn) then Exit;
  919.   CreateCLI_ADDBUDDY(@lpkt, UIN, '', '', $0000, UserID, BuddyType, False, FSeq);
  920.   FSock.SendData(lpkt, lpkt.Len);
  921. end;
  922.  
  923. {Remove user from the specified SSL's list.}
  924. procedure TICQClient.SSLDelUserFromList(UserID: Word; UIN: String; BuddyType: Word);
  925. var
  926.   lpkt: TRawPkt;
  927. begin
  928.   if (not LoggedIn) then Exit;
  929.   CreateCLI_DELETEBUDDY(@lpkt, UIN, '', '', $0000, UserID, BuddyType, False, True, FSeq);
  930.   FSock.SendData(lpkt, lpkt.Len);
  931. end;
  932.  
  933. {@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@}
  934. {Handling of all incoming packets}
  935. procedure TICQClient.HandlePacket(Flap: TFlapHdr; Data: Pointer);
  936. var
  937.   FUIN: String;
  938.   FData: String;
  939.   pkt: TRawPkt;
  940.   T: Word;
  941.   Snac: TSnacHdr;
  942.   i: Word;
  943. begin
  944.   case Flap.ChID of
  945.     1: //Channel 1
  946.     begin
  947.       {SRV_HELLO}
  948.       if Flap.DataLen = 4 then
  949.       begin
  950.         if FRegisteringUIN then
  951.         begin
  952.           //Send CLI_HELLO
  953.           CreateCLI_HELLO(@pkt, FSeq);
  954.           FSock.SendData(pkt, pkt.Len);
  955.           //Register a new UIN.
  956.           CreateCLI_REGISTERUSER(@pkt, FRegPassword, FSeq);
  957.           FSock.SendData(pkt, pkt.Len);
  958.           Exit;
  959.         end;
  960.         if FFirstConnect then
  961.         begin
  962.           //Send login packet
  963.           CreateCLI_IDENT(@pkt, FLUIN, FLPass, FSeq);
  964.           FSock.SendData(pkt, pkt.len);
  965.         end
  966.         else
  967.         begin
  968.           //Sending the cookie(second stage of login sequence)
  969.           CreateCLI_COOKIE(@pkt, FCookie, FSeq);
  970.           FSock.SendData(pkt, pkt.Len);
  971.         end;
  972.       end;
  973.       FFirstConnect := False;
  974.     end;
  975.     2: //Channel 2
  976.     begin
  977.       Move(Data^, pkt.Data, Flap.DataLen); pkt.Len := 0;
  978.       GetSnac(@pkt, Snac);
  979.       case Snac.Family of
  980.         $01: //Family x01
  981.           case Snac.SubType of
  982.             $03: {SRV_FAMILIES}
  983.             begin
  984.               CreateCLI_FAMILIES(@pkt, FSeq);           {SNAC(x01/x17)}
  985.               FSock.SendData(pkt, pkt.Len);
  986.             end;
  987.             $07: {SRV_RATES}
  988.             begin
  989.               CreateCLI_ACKRATES(@pkt, FSeq);           {SNAC(x01/x08)}
  990.               FSock.SendData(pkt, pkt.Len);
  991.               CreateCLI_SETICBM(@pkt, FSeq);            {SNAC(x04/x02)}
  992.               FSock.SendData(pkt, pkt.Len);
  993.               CreateCLI_REQINFO(@pkt, FSeq);            {SNAC(x01/x0E)}
  994.               FSock.SendData(pkt, pkt.Len);
  995.               CreateCLI_REQLOCATION(@pkt, FSeq);        {SNAC(x02/x02)}
  996.               FSock.SendData(pkt, pkt.Len);
  997.               CreateCLI_REQBUDDY(@pkt, FSeq);           {SNAC(x03/x02)}
  998.               FSock.SendData(pkt, pkt.Len);
  999.               CreateCLI_REQICBM(@pkt, FSeq);            {SNAC(x04/x04)}
  1000.               FSock.SendData(pkt, pkt.Len);
  1001.               CreateCLI_REQBOS(@pkt, FSeq);             {SNAC(x09/x02)}
  1002.               FSock.SendData(pkt, pkt.Len);
  1003.               //FConnecting := False;
  1004.             end;
  1005.             $13: {SRV_MOTD}
  1006.             begin
  1007.               CreateCLI_RATESREQUEST(@pkt, FSeq);       {SNAC(x01/x06)}
  1008.               FSock.SendData(pkt, pkt.Len);
  1009.             end;
  1010.           end;
  1011.         $03: //Family x03
  1012.         begin
  1013.           case Snac.SubType of
  1014.             $0B: {SRV_USERONLINE}
  1015.               HSnac030B(Flap, Snac, @pkt);
  1016.             $0C: {SRV_USEROFFLINE}
  1017.             begin
  1018.               FData := GetStr(@pkt, GetInt(@pkt, 1));
  1019.               if Assigned(OnUserOffline) then
  1020.                 FOnUserOffline(Self, FData);
  1021.             end;
  1022.           end;
  1023.         end;
  1024.         $04: //Family x04
  1025.           if Snac.SubType = $07 then {SRV_MSG}
  1026.             HSnac0407(Flap, Snac, @pkt)
  1027.           else if Snac.SubType = $0b then {SRV_MSGACK}
  1028.             HSnac040b(Flap, Snac, @pkt);
  1029.         $09: //Family x09
  1030.         begin
  1031.           if Snac.SubType = $03 then
  1032.           begin
  1033.             CreateCLI_SETUSERINFO(@pkt, FSeq);                                  {SNAC(x02/x04)}
  1034.             FSock.SendData(pkt, pkt.Len);
  1035.             if FContactLst.Count > 0 then
  1036.               for i := 0 to FContactLst.Count - 1 do
  1037.               begin
  1038.                 CreateCLI_ADDCONTACT(@pkt, FContactLst.Strings[i], FSeq);       {SNAC(x03/x04)}
  1039.                 FSock.SendData(pkt, pkt.Len);
  1040.               end;
  1041.             if StatusToStr(FStatus) <> 'Invisible' then
  1042.             begin
  1043.               CreateCLI_ADDINVISIBLE(@pkt, FInvisibleLst, FSeq);                {SNAC(x09/x07)}
  1044.               FSock.SendData(pkt, pkt.Len);
  1045.             end else
  1046.             begin
  1047.               CreateCLI_ADDVISIBLE(@pkt, FVisibleLst, FSeq);                    {SNAC(x09/x05)}
  1048.               FSock.SendData(pkt, pkt.Len);
  1049.             end;
  1050.             FDConnCookie := Random(High(Integer));
  1051.             if FDirect <> nil then
  1052.             begin
  1053.               if ProxyType = P_NONE then
  1054.                 i := FDirect.BindPort
  1055.               else
  1056.                 i := 0;
  1057.               CreateCLI_SETSTATUS(@pkt, FStatus, GetLocalIP, i, FDConnCookie, FProxyType, FSeq)  {SNAC(x01/x1E)}
  1058.             end else
  1059.               CreateCLI_SETSTATUS(@pkt, FStatus, 0, 0, 0, FProxyType, FSeq);    {SNAC(x01/x1E)}
  1060.             FSock.SendData(pkt, pkt.Len);
  1061.             CreateCLI_READY(@pkt, FSeq);                                        {SNAC(x01/x02)}
  1062.             FSock.SendData(pkt, pkt.Len);
  1063.             CreateCLI_TOICQSRV(@pkt, FLUIN, CMD_REQOFFMSG, nil, 0, FSeq, FSeq2);{SNAC(x15/x02)}
  1064.             FSock.SendData(pkt, pkt.Len);
  1065.             {OnLogin Event}
  1066.             FLoggedIn := True;
  1067.             if Assigned(OnLogin) then
  1068.               FOnLogin(Self);
  1069.             FTimer.Enabled := False;
  1070.           end;
  1071.         end;
  1072.         $13: //Family x13
  1073.         begin
  1074.           if Snac.SubType = $0e then
  1075.             HSnac130e(Flap, Snac, @pkt) {SRV_UPDATE_ACK}
  1076.           else if Snac.SubType = $1B then
  1077.             HSnac131b(Flap, Snac, @pkt) {SRV_AUTH}
  1078.           else if Snac.SubType = $1C then {SRV_ADDEDYOU}
  1079.             HSnac131C(Flap, Snac, @pkt)
  1080.           else if Snac.SubType = $19 then {SRV_AUTH_REQ}
  1081.             HSnac1319(Flap, Snac, @pkt)
  1082.           else if Snac.SubType = $06 then {SRV_REPLYROSTER}
  1083.             HSnac1306(Flap, Snac, @pkt);
  1084.         end;
  1085.         $15: //Family x15
  1086.         begin
  1087.           if Snac.SubType = $03 then {SRV_FROMICQSRV}
  1088.             HSnac1503(Flap, Snac, @pkt);
  1089.         end;
  1090.         $17:
  1091.         begin
  1092.           if Snac.SubType = $01 then {SRV_REGREFUSED}
  1093.           begin
  1094.             if Assigned(OnNewUINRefused) then
  1095.               FOnNewUINRefused(Self);
  1096.           end else
  1097.           if Snac.SubType = $05 then
  1098.             HSnac1705(Flap, Snac, @pkt);
  1099.         end;
  1100.       end;
  1101.     end;
  1102.     4: //Channel 4
  1103.     begin
  1104.       if FLoggedIn then
  1105.       begin
  1106.         if Assigned(OnConnectionFailed) then
  1107.           FOnConnectionFailed(Self);
  1108.         FSock.Disconnect;
  1109.         Exit;
  1110.       end;
  1111.       Move(Data^, pkt.Data, Flap.DataLen); pkt.Len := 0;
  1112.       //SRV_COOKIE
  1113.       FUIN  := GetTLVStr(@pkt, T);                //Client's UIN in ASCII format
  1114.       if T <> 1 then
  1115.       begin
  1116.         OnIntError(nil, ERR_PROTOCOL, 'Received malformed login packet');
  1117.         if Assigned(OnConnectionFailed) then
  1118.           FOnConnectionFailed(Self);
  1119.         FSock.Disconnect;
  1120.         Exit;
  1121.       end;
  1122.       FData := GetTLVStr(@pkt, T);                //IP, Port to connect to
  1123.       if T = 4 then
  1124.       begin
  1125.         OnIntError(nil, ERR_LOGIN, 'Bad password');
  1126.         if Assigned(OnConnectionFailed) then
  1127.           FOnConnectionFailed(Self);
  1128.         Exit;
  1129.       end else
  1130.       if T = 8 then
  1131.       begin
  1132.         OnIntError(nil, ERR_LOGIN, 'Too often logins');
  1133.         if Assigned(OnConnectionFailed) then
  1134.           FOnConnectionFailed(Self);
  1135.         Exit;
  1136.       end;
  1137.       if T <> 5 then
  1138.       begin
  1139.         OnIntError(nil, ERR_PROTOCOL, 'Received malformed login packet');
  1140.         if Assigned(OnConnectionFailed) then
  1141.           FOnConnectionFailed(Self);
  1142.         FSock.Disconnect;
  1143.         Exit;
  1144.       end;
  1145.       FCookie := GetTLVStr(@pkt, T);              //Cookie used in second stage of login
  1146.       if T <> 6 then
  1147.       begin
  1148.         OnIntError(nil, ERR_PROTOCOL, 'Received malformed login packet');
  1149.         if Assigned(OnConnectionFailed) then
  1150.           FOnConnectionFailed(Self);
  1151.         FSock.Disconnect;
  1152.         Exit;
  1153.       end;
  1154.       //Sending CLI_GOODBYE
  1155.       PktInit(@pkt, 4, FSeq);
  1156.       PktFinal(@pkt);
  1157.       FSock.SendData(pkt, pkt.Len);
  1158.       FSock.Disconnect;
  1159.       //Assigning new IP and Port to connect to in second attemp
  1160.       InitNetICQ;
  1161.       FSock.Host := Copy(FData, 0, Pos(':', FData) - 1);
  1162.       FSock.Port := StrToInt(Copy(FData, Pos(':', FData) + 1, Length(FData) - Pos(':', FData)));
  1163.       if (FSock.Port = 0) then
  1164.       begin
  1165.         OnIntError(nil, ERR_PROTOCOL, 'Received malformed login packet');
  1166.         if Assigned(OnConnectionFailed) then
  1167.           FOnConnectionFailed(Self);
  1168.         Exit;
  1169.       end;
  1170.       FSock.Connect;
  1171.     end;
  1172.   end;
  1173. end;
  1174.  
  1175. {////////////////////////////////////////////////////////////////////////////////////////////////////}
  1176. procedure TICQClient.SetStatus(NewStatus: LongWord);
  1177. var
  1178.   pkt: TRawPkt;
  1179. begin
  1180.   if not LoggedIn then Exit;
  1181.   if (StatusToStr(FStatus) = 'Invisible') and (StatusToStr(NewStatus) <> 'Invisible') then
  1182.   begin
  1183.     CreateCLI_ADDINVISIBLE(@pkt, FInvisibleLst, FSeq);
  1184.     FSock.SendData(pkt, pkt.Len);
  1185.   end else
  1186.   if (StatusToStr(NewStatus) = 'Invisible') and (StatusToStr(FStatus) <> 'Invisible') then
  1187.   begin
  1188.     CreateCLI_ADDVISIBLE(@pkt, FVisibleLst, FSeq);
  1189.     FSock.SendData(pkt, pkt.Len);
  1190.   end;
  1191.   CreateCLI_SETSTATUS_SHORT(@pkt, NewStatus, FSeq);
  1192.   FSock.SendData(pkt, pkt.Len);
  1193.   FStatus := NewStatus;
  1194. end;
  1195.  
  1196. {Handling packet with messages}
  1197. procedure TICQClient.HSnac0407(Flap: TFlapHdr; Snac: TSnacHdr; Pkt: PRawPkt);
  1198. var
  1199.   ULen: Word;
  1200.   c, i: Word;
  1201.   ack_pkt: TRawPkt;
  1202.   chunks: array[0..49] of Byte;
  1203.   Msg, UIN: String;
  1204.   MsgType: Word;
  1205.   Desc, URL: String;
  1206.   v: Byte;
  1207.   atype: String;
  1208.   XML: String;
  1209.   XMLTime, XMLSource, XMLSender, XMLText: String;
  1210. begin
  1211.   Inc(Pkt^.Len, 8);                             //Time, RandomID
  1212.   Msg := '';
  1213.   {Subtypes}
  1214.   case GetInt(Pkt, 2) of
  1215.     1:                                          //Simply(old-type) message
  1216.     begin
  1217.       Uin := GetStr(Pkt, GetInt(Pkt, 1));
  1218.       Inc(Pkt^.Len, 2);
  1219.       c := GetInt(Pkt, 2);                      //A count of the number of following TLVs.
  1220.       for i := 0 to c - 1 do                    //Skip all TLVs
  1221.       begin
  1222.         Inc(Pkt^.Len, 2);
  1223.         Inc(Pkt^.Len, GetInt(Pkt, 2));
  1224.       end;
  1225.       if GetInt(Pkt, 2) = 2 then                //TLV with message remain
  1226.       begin
  1227.         Inc(Pkt^.Len, 4);                       //TLV length + Unknown const
  1228.         Inc(Pkt^.Len, GetInt(Pkt, 2));          //Counts of following bytes + following bytes
  1229.         Inc(Pkt^.Len, 2);                       //x0101, Unknown, constant
  1230.         ULen := GetInt(Pkt, 2) - 4;             //Length of the message + 4
  1231.         Inc(Pkt^.Len, 4);                       //Unknown seems to be constant
  1232.         Msg := GetStr(Pkt, ULen);               //The actual message text. There will be no ending NULL.
  1233.         if (Length(Msg) > 0) and Assigned(OnMessageRecv) then
  1234.           FOnMsg(Self, Msg, Uin);
  1235.       end;
  1236.     end;
  1237.     2:                                          //Complicate(new-type)
  1238.     begin
  1239.       Uin := GetStr(Pkt, GetInt(Pkt, 1));
  1240.       for c := 0 to 5 do
  1241.       begin
  1242.         if GetInt(Pkt, 2) = 5 then
  1243.         begin
  1244.           Inc(Pkt^.Len, 2);
  1245.           if GetInt(Pkt, 2) <> 0 then           //ACKTYPE: 0x0000 - This is a normal message
  1246.             Exit;
  1247.           Inc(Pkt^.Len, 16);                    //09 46 13 49 4C 7F 11 D1 82 22 44 45 53 54 00 00
  1248.           Inc(Pkt^.Len, 8);                     //TIME + RANDOM
  1249.           for i := 0 to 5 do
  1250.           begin
  1251.             if GetInt(Pkt, 2) = $2711 then      //Searching for TLV(2711) (with sources)
  1252.             begin
  1253.               Inc(Pkt^.Len, 2);                 //TLV Length
  1254.               Move(Ptr(LongWord(Pkt) + Pkt^.Len)^, chunks, 47);
  1255.               if GetInt(Pkt, 1) <> $1B then     //If this value is not present, this is not a message packet. Also, ICQ2001b does not send an ACK, SNAC(4,B), if this is not 0x1B.
  1256.                 Exit;
  1257.               Inc(Pkt^.Len, 44);
  1258.               MsgType := GetInt(Pkt, 1);
  1259.               Inc(Pkt^.Len, 5);
  1260.               Msg := GetLNTS(Pkt);              //The actual message text. There will be ending NULL.
  1261.  
  1262.               {Sending ACK of the message}
  1263.               PktInit(@ack_pkt, 2, FSeq);               //Channel 2
  1264.               PktSnac(@ack_pkt, $04, $0B, 0, 0);        //SNAC(x04/x0B)
  1265.               Move(Ptr(LongWord(Pkt) + TSNACSZ)^, Ptr(LongWord(@ack_pkt) + ack_pkt.Len)^, 10); //First 10 bytes of TLV(2711)
  1266.               Inc(ack_pkt.Len, 10);                     //Skip first 10 bytes copied from TLV(2711) which were added before
  1267.               PktLStr(@ack_pkt, UIN);                   //User's UIN
  1268.               PktInt(@ack_pkt, $0003, 2);               //00 03
  1269.               PktAddArrBuf(@ack_pkt, @chunks, 47);      //First 47 bytes of source packet (with message)
  1270.               PktInt(@ack_pkt, $00000000, 4);           //00 00 00 00
  1271.               //If it's an auto-away message request
  1272.               if MsgType and $E0 = $E0 then
  1273.                 PktLNTS(@ack_pkt, FAutoAwayMsg)         //Auto-away message
  1274.               else begin
  1275.                 PktInt(@ack_pkt, 1, 1);                 //01
  1276.                 PktInt(@ack_pkt, 0, 4);                 //00 00 00 00
  1277.                 PktInt(@ack_pkt, 0, 2);                 //00 00
  1278.                 PktInt(@ack_pkt, $FFFFFF00, 4);         //FF FF FF 00
  1279.               end;
  1280.               PktFinal(@ack_pkt);
  1281.               FSock.SendData(ack_pkt, ack_pkt.Len);
  1282.  
  1283.               if (Length(Msg) > 0) then
  1284.               begin
  1285.                 if MsgType = M_PLAIN then
  1286.                 begin
  1287.                   if FDoPlain then Msg := Rtf2Txt(Msg);   //Convert message from RTF to plaintext when needed
  1288.                   if Assigned(OnMessageRecv) then
  1289.                     FOnMsg(Self, Msg, Uin)
  1290.                 end else
  1291.                 if MsgType = M_URL then
  1292.                 begin
  1293.                   Desc := Copy(Msg, 0, Pos(#$fe, Msg) - 1);
  1294.                   URL := Copy(Msg, Pos(#$fe, Msg) + 1, Length(Msg) - Pos(#$fe, Msg));
  1295.                   if Assigned(OnURLRecv) then
  1296.                     FOnURL(Self, Desc, URL, Uin);
  1297.                 end;
  1298.               end;
  1299.               Exit;
  1300.             end else
  1301.               Inc(Pkt^.Len, GetInt(Pkt, 2));
  1302.           end;
  1303.         end else
  1304.           Inc(Pkt^.Len, GetInt(Pkt, 2));
  1305.       end;
  1306.     end;
  1307.     4:
  1308.     begin
  1309.       Uin := GetStr(Pkt, GetInt(Pkt, 1));
  1310.       for i := 0 to 4 do
  1311.       begin
  1312.         v := GetInt(Pkt, 1);
  1313.         if (v = 5) or ((GetInt(Pkt, 1) = 5) and (v = 0)) then    //TLV(5) was found
  1314.         begin
  1315.           if v = 5 then                                 //Some modifications for MAC clients
  1316.             Inc(Pkt^.Len, 40)
  1317.           else
  1318.             Inc(Pkt^.Len, 2);
  1319.           GetLInt(Pkt, 4);                              //UIN
  1320.           MsgType := GetLInt(Pkt, 2);                   //Message-type
  1321.           Msg := GetLNTS(Pkt);                          //Message
  1322.           if MsgType = $1a then                         //Probably advanced msg format
  1323.           begin
  1324.             Inc(Pkt^.Len, 20);                          //20 unknown bytes
  1325.             atype := GetStr(Pkt, GetLInt(Pkt, 4) - 1);  //Advanced msg sub-type
  1326.             if atype = 'ICQSMS' then
  1327.             begin
  1328.               Inc(Pkt^.Len, 1);                         //sub-type null terminator
  1329.               Inc(Pkt^.Len, 3);                         //00 00 00
  1330.               Inc(Pkt^.Len, 4);                         //4-byte little endian length of the following data
  1331.               XML := GetStr(Pkt, GetLInt(Pkt, 4));      //XML entry of SMS response
  1332.               XMLSource := GetXMLEntry('source', XML);  //Source, usually: 'ICQ'
  1333.               XMLSender := GetXMLEntry('sender', XML);  //Source cellular number
  1334.               XMLText := GetXMLEntry('text', XML);      //Text of reply
  1335.               XMLTime := GetXMLEntry('time', XML);      //Time of sending reply
  1336.               if Assigned(OnSMSReply) then
  1337.                 FOnSMSReply(Self, XMLSource, XMLSender, XMLTime, UTF8ToStrSmart(XMLText));
  1338.             end;
  1339.             Exit;
  1340.           end;
  1341.  
  1342.           if (Length(Msg) > 0) then
  1343.           begin
  1344.             if MsgType = M_PLAIN then
  1345.             begin
  1346.               if FDoPlain then Msg := Rtf2Txt(Msg);     //Convert message from RTF to plaintext when needed
  1347.               if Assigned(OnMessageRecv) then
  1348.                 FOnMsg(Self, Msg, Uin)
  1349.             end
  1350.             else if MsgType = M_URL then
  1351.             begin
  1352.               Desc := Copy(Msg, 0, Pos(#$fe, Msg) - 1);
  1353.               URL := Copy(Msg, Pos(#$fe, Msg) + 1, Length(Msg) - Pos(#$fe, Msg));
  1354.               if Assigned(OnURLRecv) then
  1355.                 FOnURL(Self, Desc, URL, Uin);
  1356.             end;
  1357.           end;
  1358.           Exit;
  1359.         end else
  1360.           Inc(Pkt^.Len, GetInt(Pkt, 2));
  1361.       end;
  1362.     end;
  1363.   end;
  1364. end;
  1365.  
  1366. {Handling old type packets ICQ_FROMSRV}
  1367. procedure TICQClient.HSnac1503(Flap: TFlapHdr; Snac: TSnacHdr; Pkt: PRawPkt);
  1368. var
  1369.   FMsgType: Word;
  1370.   lpkt: TRawPkt;
  1371.   FNick, FFirst, FLast, FEmail, FCity,
  1372.   FState, FPhone, FFax, FStreet, FCellular,
  1373.   FZip, FCountry, FCompany, FDepartment,
  1374.   FPosition, FOccupation, FHomePage,
  1375.   FLang1, FLang2, FLang3, FAbout: String;
  1376.   FTimeZone: Byte;
  1377.   FPublishEmail: Boolean;
  1378.   FAge, FYear: Word;
  1379.   FGender, FMonth, FDay: Byte;
  1380.   Msg, UIN, URL, Desc: String;
  1381.   List, List2: TStringList;
  1382.   C, i: Byte;
  1383.   WW: Word;
  1384.   FStatus: Word;
  1385.   cmd: Word;
  1386.   FSmsSource, FSmsDeliverable, FSmsNetwork, FMsgId: String;
  1387.   FAuthorize: Byte;
  1388. begin
  1389.   if GetInt(Pkt, 2) = 1 then                      //TLV(1)
  1390.   begin
  1391.     Inc(Pkt^.Len, 8);
  1392.     case GetInt(Pkt, 2) of
  1393.       $4100:                                      //SRV_OFFLINEMSG
  1394.       begin
  1395.         Inc(Pkt^.Len, 2);                         //The sequence number this packet is a response to.
  1396.         UIN := IntToStr(GetLInt(Pkt, 4));         //Source UIN
  1397.         Inc(Pkt^.Len, 6);                         //Date/time etc...
  1398.         FMsgType := GetLInt(Pkt, 2);              //The type of message sent, like URL message or the like.
  1399.         Msg := GetLNTS(Pkt);
  1400.         if FDoPlain then Msg := Rtf2Txt(Msg);     //Convert message from RTF to plaintext when needed
  1401.         if FMsgType = M_PLAIN then
  1402.         begin
  1403.           if Assigned(OnOfflineMsgRecv) then
  1404.             FOnOffMsg(Self, Msg, UIN);
  1405.         end else
  1406.         if FMsgType = M_URL then
  1407.         begin
  1408.           Desc := Copy(Msg, 0, Pos(#$fe, Msg) - 1);
  1409.           URL := Copy(Msg, Pos(#$fe, Msg) + 1, Length(Msg) - Pos(#$fe, Msg));
  1410.           if Assigned(OnOfflineURLRecv) then
  1411.             FOnOffURL(Self, Desc, URL, UIN);
  1412.         end;
  1413.       end;
  1414.       $4200: //All offline messages were sent, so we ACKING them
  1415.       begin
  1416.         FSeq2 := 2;
  1417.         CreateCLI_ACKOFFLINEMSGS(@lpkt, FLUIN, FSeq, FSeq2);
  1418.         FSock.SendData(lpkt, lpkt.Len);
  1419.       end;
  1420.       $da07: //SRV_META
  1421.       begin
  1422.         Inc(Pkt^.Len, 2);
  1423.         cmd := GetInt(Pkt, 2);
  1424.         case cmd of
  1425.           $0100: //SRV_SMSREFUSED
  1426.           begin
  1427.             if Assigned(OnSMSRefused) then
  1428.               FOnSMSRefused(Self);
  1429.           end;
  1430.           $9600: //SRV_SMSACK
  1431.           begin
  1432.             if GetInt(Pkt, 1) <> $0a then Exit;
  1433.             Inc(Pkt^.Len, 12);
  1434.             Msg := GetStr(Pkt, GetLInt(Pkt, 2));
  1435.             FSmsSource := GetXMLEntry('source', Msg);
  1436.             FSmsDeliverable := GetXMLEntry('deliverable', Msg);
  1437.             FSmsNetwork := GetXMLEntry('network', Msg);
  1438.             FMsgId := GetXMLEntry('message_id', Msg);
  1439.             if Assigned(OnSMSAck) then
  1440.               FOnSMSAck(Self, FSmsSource, FSmsNetwork, FMsgId, FSmsDeliverable = 'Yes');
  1441.           end;
  1442.           $b400: //SRV_METAUNREG_BADPASS Channel: 2, Snac(0x15, 0x03) 2010/180
  1443.           begin
  1444.             case GetInt(Pkt, 1) of
  1445.               $0a:
  1446.               begin
  1447.                 if Assigned(OnUnregisterOk) then
  1448.                   FOnUnregisterOk(Self);
  1449.                 CreateCLI_GOODBYE(@lpkt, FSeq);     //Send CLI_GOODBYE, it forces server to disconnect us
  1450.                 FSock.SendData(lpkt, lpkt.Len);
  1451.               end;
  1452.               $14: if Assigned(OnUnregisterBadPassword) then
  1453.                 FOnUnregBadPass(Self);
  1454.             end;
  1455.           end;
  1456.           $c800: //SRV_METAGENERAL Channel: 2, SNAC(0x15,0x3) 2010/200
  1457.           begin
  1458.             if GetInt(Pkt, 1) <> $0a then Exit;
  1459.             FNick := GetLNTS(Pkt);
  1460.             FFirst := GetLNTS(Pkt);
  1461.             FLast := GetLNTS(Pkt);
  1462.             FEmail := GetLNTS(Pkt);
  1463.             FCity := GetLNTS(Pkt);
  1464.             FState := GetLNTS(Pkt);
  1465.             FPhone := GetLNTS(Pkt);
  1466.             FFax := GetLNTS(Pkt);
  1467.             FStreet := GetLNTS(Pkt);
  1468.             FCellular := GetLNTS(Pkt);
  1469.             FZip := GetLNTS(Pkt);
  1470.             FCountry := CountryToStr(GetLInt(Pkt, 2));
  1471.             FTimeZone := GetInt(Pkt, 1);
  1472.             if GetInt(Pkt, 1) = 1 then
  1473.               FPublishEmail := True
  1474.             else
  1475.               FPublishEmail := False;
  1476.             if FInfoChain.Count > 0 then
  1477.             begin
  1478.               FLastInfoUin := FInfoChain.Strings[0];
  1479.               FInfoChain.Delete(0);
  1480.             end else
  1481.               FLastInfoUin := '0';
  1482.             if Assigned(OnUserGeneralInfo) then
  1483.               FOnUserGeneralInfo(Self, FLastInfoUin, FNick, FFirst,
  1484.                 FLast, FEmail, FCity, FState, FPhone,
  1485.                 FFax, FStreet, FCellular, FZip, FCountry,
  1486.                 FTimeZone, FPublishEmail
  1487.               );
  1488.           end;
  1489.           $d200: //SRV_METAWORK Channel: 2, SNAC(0x15,0x3) 2010/210
  1490.           begin
  1491.             if GetInt(Pkt, 1) <> $0a then Exit;
  1492.             FCity := GetLNTS(Pkt);
  1493.             FState := GetLNTS(Pkt);
  1494.             FPhone := GetLNTS(Pkt);
  1495.             FFax := GetLNTS(Pkt);
  1496.             FStreet := GetLNTS(Pkt);
  1497.             FZip := GetLNTS(Pkt);
  1498.             FCountry := CountryToStr(GetLInt(Pkt, 2));
  1499.             FCompany := GetLNTS(Pkt);
  1500.             FDepartment := GetLNTS(Pkt);
  1501.             FPosition := GetLNTS(Pkt);
  1502.             FOccupation := OccupationToStr(GetLInt(Pkt, 2));
  1503.             FHomePage := GetLNTS(Pkt);
  1504.             if Assigned(OnUserWorkInfo) then
  1505.               FOnUserWorkInfo(Self, FLastInfoUin, FCity, FState, FPhone,
  1506.                 FFax, FStreet, FZip, FCountry, FCompany, FDepartment, FPosition,
  1507.                 FOccupation, FHomePage
  1508.               );
  1509.           end;
  1510.           $dc00: //SRV_METAMORE Channel: 2, SNAC(0x15,0x3) 2010/220
  1511.           begin
  1512.             if GetInt(Pkt, 1) <> $0a then Exit;
  1513.             FAge := GetLInt(Pkt, 2);
  1514.             if Integer(FAge) < 0 then
  1515.               FAge := 0;
  1516.             FGender := GetInt(Pkt, 1);
  1517.             FHomePage := GetLNTS(Pkt);
  1518.             FYear := GetLInt(Pkt, 2);
  1519.             FMonth := GetInt(Pkt, 1);
  1520.             FDay := GetInt(Pkt, 1);
  1521.             FLang1 := LanguageToStr(GetInt(Pkt, 1));
  1522.             FLang2 := LanguageToStr(GetInt(Pkt, 1));
  1523.             FLang3 := LanguageToStr(GetInt(Pkt, 1));
  1524.             if Assigned(OnUserInfoMore) then
  1525.               FOnUserInfoMore(Self, FLastInfoUin, FAge, FGender, FHomePage,
  1526.                 FYear, FMonth, FDay, FLang1, FLang2, FLang3
  1527.               );
  1528.           end;
  1529.           $e600: //Channel: 2, SNAC(0x15,0x3) 2010/230
  1530.           begin
  1531.             if GetInt(Pkt, 1) <> $0a then Exit;
  1532.             FAbout := GetLNTS(Pkt);
  1533.             if Assigned(OnUserInfoAbout) then
  1534.               FOnUserInfoAbout(Self, FLastInfoUin, FAbout);
  1535.           end;
  1536.           $eb00: //Channel: 2, SNAC(21,3) 2010/235
  1537.           begin
  1538.             if GetInt(Pkt, 1) <> $0a then Exit;
  1539.             c := GetInt(Pkt, 1);        //The number of email addresses to follow. May be zero. Each consist of the following parameters:
  1540.             List := TStringList.Create;
  1541.             if c > 0 then
  1542.               for i := 0 to c - 1 do
  1543.               begin
  1544.                 GetInt(Pkt, 1); //Publish email address? 1 = yes, 0 = no.
  1545.                 List.Add(GetLNTS(Pkt)); //The email address.
  1546.               end;
  1547.             if Assigned(OnUserInfoMoreEmails) then
  1548.               FOnUserInfoMoreEmails(Self, FLastInfoUin, List)
  1549.             else
  1550.               List.Free;
  1551.           end;
  1552.           $f000: //Channel: 2, SNAC(21,3) 2010/240
  1553.           begin
  1554.             if GetInt(Pkt, 1) <> $0a then Exit;
  1555.             c := GetInt(Pkt, 1);
  1556.             List := TStringList.Create;
  1557.             if c > 0 then
  1558.               for i := 0 to c - 1 do
  1559.               begin
  1560.                 WW := GetLInt(Pkt, 2);
  1561.                 List.Add(InterestToStr(WW) + '=' + GetLNTS(Pkt))
  1562.               end;
  1563.             if Assigned(OnUserInfoInterests) then
  1564.               FOnUserInfoInterests(Self, FLastInfoUin, List)
  1565.             else
  1566.               List.Free;
  1567.           end;
  1568.           $a401, $ae01: //Channel: 2, SNAC(21,3) 2010/420 or Channel: 2, SNAC(21,3) 2010/430
  1569.           begin
  1570.             if GetInt(Pkt, 1) <> $0a then
  1571.             begin
  1572.               if Assigned(OnUserNotFound) then
  1573.                 FOnUserNotFound(Self);
  1574.               Exit;
  1575.             end;
  1576.             Inc(Pkt^.Len, 2);                   //Length of the following data.
  1577.             UIN := IntToStr(GetLInt(Pkt, 4));   //The user's UIN.
  1578.             FNick := GetLNTS(Pkt);              //The user's nick name.
  1579.             FFirst := GetLNTS(Pkt);             //The user's first name.
  1580.             FLast := GetLNTS(Pkt);              //The user's last name.
  1581.             FEmail := GetLNTS(Pkt);             //The user's email address.
  1582.             FAuthorize := GetInt(Pkt, 1);       //Publish email address? 1 = yes, 0 = no.
  1583.             FStatus := GetLInt(Pkt, 2);         //0 = Offline, 1 = Online, 2 = not Webaware.
  1584.             FGender := GetInt(Pkt, 1);          //The user's gender. 1 = female, 2 = male, 0 = not specified.
  1585.             FAge := GetInt(Pkt, 1);             //The user's age.
  1586.             if Assigned(OnUserFound) then
  1587.               FOnUserFound(Self, UIN, FNick, FFirst, FLast, FEmail, FStatus, FGender, FAge, cmd = $ae01, FAuthorize = $00);
  1588.           end;
  1589.           $6603:
  1590.           begin
  1591.             if GetInt(Pkt, 1) <> $0a then
  1592.             begin
  1593.               if Assigned(OnUserNotFound) then
  1594.                 FOnUserNotFound(Self);
  1595.               Exit;
  1596.             end;
  1597.             UIN := IntToStr(GetLInt(Pkt, 4));   //The user's UIN.
  1598.             if Assigned(OnUserFound) then
  1599.               FOnUserFound(Self, UIN, '', '', '', '', 0, 0, 0, True, False);
  1600.           end;
  1601.           $aa00:
  1602.           begin
  1603.             if GetInt(Pkt, 1) <> $0a then
  1604.             begin
  1605.               if Assigned(OnChangePasswordError) then
  1606.                 FOnChangePasswordError(Self);
  1607.               Exit;
  1608.             end;
  1609.             if Assigned(OnChangePasswordOk) then
  1610.               FOnChangePasswordOk(Self);
  1611.           end;
  1612.           $fa00:
  1613.           begin
  1614.             if GetInt(Pkt, 1) <> $0a then Exit;
  1615.             List := TStringList.Create;
  1616.             List.Duplicates := dupIgnore;            
  1617.             c := GetInt(Pkt, 1);                             //The number of background items to follow. May be zero. Each background item consists of the following two parameters
  1618.             if c > 0 then
  1619.               for i := 0 to c - 1 do
  1620.               begin
  1621.                 WW := GetLInt(Pkt, 2);                       //The group this background is in, according to a table.
  1622.                 List.Add(PastToStr(WW) + '=' + GetLNTS(Pkt)) //A longer description of this background item.
  1623.               end;
  1624.             List2 := TStringList.Create;
  1625.             List2.Duplicates := dupIgnore;            
  1626.             c := GetInt(Pkt, 1);                             //The number of affiliations to follow. May be zero. Each affiliation consists of the following parameters:
  1627.             if c > 0 then
  1628.               for i := 0 to c - 1 do
  1629.               begin
  1630.                 WW := GetLInt(Pkt, 2);                       //The group this affiliation is in, according to a table.
  1631.                 List2.Add(AffiliationToStr(WW) + '=' + GetLNTS(Pkt)) //A longer description of the affiliation.
  1632.               end;
  1633.             if Assigned(OnUserInfoBackground) then
  1634.               FOnUserInfoBackground(Self, FLastInfoUin, List, List2)
  1635.             else
  1636.             begin
  1637.               List.Free;
  1638.               List2.Free;
  1639.             end;
  1640.           end;
  1641.           $6400: //SRV_METAGENERALDONE Channel: 2, SNAC(21,3) 2010/100 
  1642.             if Assigned(OnInfoChanged) then
  1643.               FOnInfoChanged(Self, INFO_GENERAl);
  1644.           $7800: //SRV_METAMOREDONE Channel: 2, SNAC(21,3) 2010/120
  1645.             if Assigned(OnInfoChanged) then
  1646.               FOnInfoChanged(Self, INFO_MORE);
  1647.           $8200: //SRV_METAABOUTDONE Channel: 2, SNAC(21,3) 2010/130
  1648.             if Assigned(OnInfoChanged) then
  1649.               FOnInfoChanged(Self, INFO_ABOUT);
  1650.           $a000: //SRV_AUTHSET Channel: 2, SNAC(21, 3) 2010/160
  1651.             if Assigned(OnAuthorizationChangedOk) then
  1652.               FOnAuthSet(Self);
  1653.         end;
  1654.       end;
  1655.     end;
  1656.   end;
  1657. end;
  1658.  
  1659. {Handling packet with status changes}
  1660. {$WARNINGS OFF}
  1661. procedure TICQClient.HSnac030B(Flap: TFlapHdr; Snac: TSnacHdr; Pkt: PRawPkt);
  1662.   function NumToIp(Addr: LongWord): String;
  1663.   var
  1664.     inaddr: in_addr;
  1665.   begin
  1666.     inaddr.S_addr := Addr;
  1667.     Result := inet_ntoa(inaddr);
  1668.   end;
  1669. var
  1670.   c, i: Word;
  1671.   UIN: String;
  1672.   Status: LongWord;
  1673.   FIntIP, FExtIP: LongWord;
  1674.   FIntPort: Word;
  1675.   FConnFlag: Byte;
  1676.   FDconCookie: LongWord;
  1677.   FProtoVer: Word;
  1678. begin
  1679.   UIN := GetStr(Pkt, GetInt(Pkt, 1));
  1680.   Inc(Pkt^.Len, 2);
  1681.   c := GetInt(Pkt, 2);
  1682.   if c < 1 then Exit;
  1683.   for i := 0 to c - 1 do
  1684.   begin
  1685.     case GetInt(Pkt, 2) of
  1686.     $0c:
  1687.     begin
  1688.       Inc(Pkt^.Len, 2);                 //TLV's Length
  1689.       FIntIP := GetLInt(Pkt, 4);        //Internal IP
  1690.       FIntPort := GetInt(Pkt, 4);       //Internal port
  1691.       FConnFlag := GetInt(Pkt, 1);      //Connection flag
  1692.       FProtoVer := GetInt(Pkt, 2);      //Protocol version
  1693.       FDconCookie := GetLInt(Pkt, 4);   //Direct connection cookie
  1694.       Inc(Pkt^.Len, 22);                //Skip remaining data
  1695.     end;
  1696.     $0a:
  1697.     begin
  1698.       Inc(Pkt^.Len, 2);                 //TLV's Length
  1699.       FExtIP := GetLInt(Pkt, 4);        //External IP
  1700.       if (FConnFlag = $04) or (FConnFlag = $02) then
  1701.       begin
  1702.         if FDirect <> nil then
  1703.           FDirect.AddUser(StrToInt(UIN), FDConCookie, FExtIP, FIntIP, FIntPort);
  1704.       end else
  1705.         OnIntError(nil, ERR_WARNING, 'Cannot estabilish direct connection because remote client uses unknown proxy type');
  1706.     end;
  1707.     $06:
  1708.     begin
  1709.       Inc(Pkt^.Len, 2);                 //TLV's Length
  1710.       Status := GetInt(Pkt, 4);         //Online status
  1711.       if not DisableDirectConnections then
  1712.         FDirect.EstabilishConnection(StrToInt(UIN));
  1713.       if Assigned(OnStatusChange) then
  1714.         FOnStatusChange(Self, UIN, Status);
  1715.       if Assigned(OnOnlineInfo) then
  1716.         FOnOnlineInfo(Self, UIN, FIntPort, NumToIp(FIntIP), NumToIp(FExtIP), FProtoVer);
  1717.       Exit;
  1718.     end else
  1719.       Inc(Pkt^.Len, GetInt(Pkt, 2));
  1720.     end;
  1721.   end;
  1722. end;
  1723. {$WARNINGS ON}
  1724.  
  1725. {Handling AddedYou packet}
  1726. procedure TICQClient.HSnac131C(Flap: TFlapHdr; Snac: TSnacHdr; Pkt: PRawPkt);
  1727. var
  1728.   T: Word;
  1729.   UIN: String;
  1730. begin
  1731.   Inc(Pkt^.Len, 2);
  1732.   GetTLVInt(Pkt, T);
  1733.   if T <> 1 then Exit;
  1734.   UIN := GetLStr(Pkt);
  1735.   if Assigned(OnAddedYou) then
  1736.     FOnAddedYou(Self, UIN);
  1737. end;
  1738.  
  1739. {Authorization request, we are automaticly authorizing the user}
  1740. procedure TICQClient.HSnac1319(Flap: TFlapHdr; Snac: TSnacHdr; Pkt: PRawPkt);
  1741. var
  1742.   FUin: String;
  1743.   FReason: String;
  1744.   opkt: TRawPkt;
  1745. begin
  1746.   Inc(Pkt^.Len, 8);
  1747.   FUin := GetLStr(Pkt);
  1748.   FReason := GetStr(Pkt, Swap16(GetInt(Pkt, 2)));
  1749.   CreateCLI_AUTHORIZE(@opkt, StrToInt(FUin), 1, '', FSeq);
  1750.   FSock.SendData(opkt, opkt.Len);
  1751. end;
  1752.  
  1753. {This packet contains your complete server side contact list.}
  1754. procedure TICQClient.HSnac1306(Flap: TFlapHdr; Snac: TSnacHdr; Pkt: PRawPkt);
  1755. var
  1756.   GroupIdents: TStringList;
  1757.   UINList: TList;
  1758.   procedure ReadChunk;
  1759.   var
  1760.     Len: Word;
  1761.     FGroup: ShortString;
  1762.     CTag, CId, CType: Word;
  1763.     TLen: Word;
  1764.     TType: Word;
  1765.     FNick: ShortString;
  1766.     lpEntry: PUINEntry;
  1767.   begin
  1768.     FGroup := GetWStr(Pkt);             //The name of the group.
  1769.     CTag := GetInt(Pkt, 2);             //This field seems to be a tag or marker associating different groups together into a larger group such as the Ignore List or 'General' contact list group, etc.
  1770.     CId := GetInt(Pkt, 2);              //This is a random number generated when the user is added to the contact list, or when the user is ignored.
  1771.     CType := GetInt(Pkt, 2);            //This field seems to indicate what type of group this is.
  1772.     Len := GetInt(Pkt, 2);              //The length in bytes of the following TLVs.
  1773.     FNick := '';
  1774.     while Integer(Len) > 0 do
  1775.     begin
  1776.       TType := GetInt(Pkt, 2);          //TLV Type
  1777.       TLen := GetInt(Pkt, 2);           //TLV Len
  1778.       if TType = $0131 then
  1779.         FNick := UTF8ToStrSmart(GetStr(Pkt, TLen))
  1780.       else
  1781.         Inc(Pkt^.Len, TLen);            //Skip this TLV
  1782.       Dec(len, TLen + 4);               //TLV length + 2 bytes type + 2 bytes length
  1783.     end;
  1784.  
  1785.     //Group header
  1786.     if (FGroup <> '') and (CType = 1) and (CTag <> 0) and (CId = 0) then
  1787.       GroupIdents.Values[IntToStr(CTag)] := UTF8ToStrSmart(FGroup);
  1788.  
  1789.     //UIN entry
  1790.     if (CType = 0) or (CType = 2) or (CType = 3) or (CType = $e) then
  1791.     begin
  1792.       GetMem(lpEntry, SizeOf(lpEntry^));
  1793.       lpEntry^.UIN := StrToInt(FGroup);
  1794.       lpEntry^.Nick := FNick;
  1795.       lpEntry^.CType := CType;
  1796.       lpEntry^.CTag := CId;
  1797.       lpEntry^.CGroupID := CTag;
  1798.       UINList.Add(lpEntry);
  1799.     end;
  1800.   end;
  1801. var
  1802.   count, T: Word;
  1803.   i: Word;
  1804. begin
  1805.   GetTLVInt(Pkt, T); if T <> 6 then Exit;
  1806.   Inc(Pkt^.Len, 4);                     //02 00 02 00 - UNKNOWNs
  1807.   count := GetInt(Pkt, 2);              //Total count of following groups. This is the size of the server side contact list and should be saved and sent with CLI_CHECKROSTER.
  1808.   if count < 1 then Exit;
  1809.   GroupIdents := TStringList.Create;
  1810.   UINList := TList.Create;
  1811.   for i := 0 to count - 1 do
  1812.     ReadChunk;
  1813.  
  1814.   if UINList.Count > 0 then
  1815.     for i := 0 to UINList.Count - 1 do
  1816.       PUINEntry(UINList.Items[i])^.CGroup := GroupIdents.Values[IntToStr(PUINEntry(UINList.Items[i])^.CGroupID)];
  1817.   GroupIdents.Free;
  1818.  
  1819.   if Assigned(OnServerListRecv) then
  1820.     FOnServerListRecv(Self, UINList)
  1821.   else
  1822.     DestroyUINList(UINList);
  1823. end;
  1824.  
  1825. {This packet contains ack to message you've sent.}
  1826. procedure TICQClient.HSnac040b(Flap: TFlapHdr; Snac: TSnacHdr; Pkt: PRawPkt);
  1827. var
  1828.   RetCode: Word;
  1829.   RetAcc: Byte;
  1830.   RetMsg: String;
  1831.   MsgType: Byte;
  1832.   FUIN: String;
  1833. begin
  1834.   Inc(Pkt^.Len, 4);                     //Time
  1835.   RetCode := GetInt(Pkt, 2);            //Random ID
  1836.   Inc(Pkt^.Len, 4);                     //Other data :)
  1837.   FUIN := GetLStr(Pkt);                 //User's UIN
  1838.   Inc(Pkt^.Len, 2);                     //00 03
  1839.   Inc(Pkt^.Len, 45);                    //Skip 50 bytes of packet
  1840.   MsgType := GetInt(Pkt, 1);            //Msg-type
  1841.   Inc(Pkt^.Len, 1);                     //Msg-flags
  1842.   RetAcc := GetInt(Pkt, 1);             //Accept type
  1843.   Inc(Pkt^.Len, 3);                     //Unknown
  1844.   if (RetAcc <> ACC_NORMAL) and (RetAcc <> ACC_NO_OCCUPIED) and
  1845.      (RetAcc <> ACC_NO_DND) and (RetAcc <> ACC_AWAY) and
  1846.      (RetAcc <> ACC_NA) and (RetAcc <> ACC_CONTACTLST) then Exit;
  1847.   if MsgType and $E0 = $E0 then
  1848.   begin
  1849.     RetMsg := GetLNTS(Pkt);
  1850.     if Assigned(OnAutoMsgResponse) then
  1851.       FOnAutoMsgResponse(Self, FUIN, RetCode, MsgType, RetMsg);
  1852.     Exit;
  1853.   end;
  1854.   if RetAcc <> ACC_NORMAL then
  1855.   begin
  1856.     RetMsg := GetLNTS(Pkt);
  1857.   end else
  1858.     RetMsg := '';
  1859.   if Assigned(OnAdvancedMsgAck) then
  1860.     FOnAdvMsgAck(Self, FUIN, RetCode, RetAcc, RetMsg);
  1861. end;
  1862.  
  1863. {This packet contains response with new UIN created.}
  1864. procedure TICQClient.HSnac1705(Flap: TFlapHdr; Snac: TSnacHdr; Pkt: PRawPkt);
  1865. var
  1866.   UIN: String;
  1867. begin
  1868.   if GetInt(Pkt, 2) <> $01 then Exit;   //TLV(01)
  1869.   Inc(Pkt^.Len, 2);                     //TLV's length
  1870.   Inc(Pkt^.Len, 2);                     //The length of the following data in bytes.
  1871.   Inc(Pkt^.Len, 4);                     //Unknown: empty.
  1872.   Inc(Pkt^.Len, 4);                     //Unknown: 0x2d000300 = 754975488.
  1873.   Inc(Pkt^.Len, 4);                     //Your port number as the server sees it.
  1874.   Inc(Pkt^.Len, 4);                     //Your IP address as the server sees it.
  1875.   Inc(Pkt^.Len, 4);                     //Unknown: 0x4 = 4.
  1876.   Inc(Pkt^.Len, 4);                     //The same UNKNOWN2 as sent in CLI_REGISTERUSER.
  1877.   Inc(Pkt^.Len, 16);                    //16 empty bytes
  1878.   UIN := IntToStr(GetLInt(Pkt, 4));     //New UIN
  1879.   if Assigned(OnNewUINRegistered) then  //Call associated event
  1880.     FOnNewUINRegistered(Self, UIN);
  1881. end;
  1882.  
  1883. {This packet contains reponse to CLI_REQAUTH.}
  1884. procedure TICQClient.HSnac131b(Flap: TFlapHdr; Snac: TSnacHdr; Pkt: PRawPkt);
  1885. var
  1886.   T: Word;
  1887.   UIN, Reason: String;
  1888.   Granted: Boolean;
  1889. begin
  1890.   Inc(Pkt^.Len, 2);                     //Unknown: 6.
  1891.   GetTLVInt(Pkt, T);                    //Unknown.
  1892.   UIN := GetLStr(Pkt);                  //The UIN that granted authorization.
  1893.   Granted := GetInt(Pkt, 1) = $01;      //00 - Rejected, 01 - Granted.
  1894.   Reason := GetWStr(Pkt);               //Reason, can be null.
  1895.   if Assigned(OnAuthResponse) then
  1896.     FOnAuthResponse(Self, UIN, Granted, Reason);
  1897. end;
  1898.  
  1899. {This command is sent as what is perhaps an acknowledgement reply to at least CLI_ADDBUDDY and CLI_UPDATEGROUP.}
  1900. procedure TICQClient.HSnac130e(Flap: TFlapHdr; Snac: TSnacHdr; Pkt: PRawPkt);
  1901. var
  1902.   T: Word;
  1903.   ErrCode: Word;
  1904. begin
  1905.   Inc(Pkt^.Len, 2);                     //Unknown: 6. Guess: The length in bytes of the following data.
  1906.   GetTLVInt(Pkt, T);                    //Unknown.
  1907.   ErrCode := GetInt(Pkt, 2);            //ErrorCode
  1908.   if (ErrCode <> ERRSSL_AUTH) and (ErrCode <> ERRSSL_NOERROR) and (ErrCode <> ERRSSL_NOTFOUND) and
  1909.      (ErrCode <> ERRSSL_EXISTS) then
  1910.     ErrCode := ERRSSL_OTHER;
  1911.   if Assigned(OnSSLChangeResponse) then
  1912.     FOnChangeResponse(Self, ErrCode);
  1913. end;
  1914.  
  1915. {Handle packet with message sent directly}
  1916. procedure TICQClient.HDirectMsg(Sender: TObject; UIN: LongWord; Pak: PRawPkt; Len: LongWord);
  1917.   procedure ParseContacts(Value: String; var List: TStringList);
  1918.   var
  1919.     i, l: Word;
  1920.     FName, FUIN: String;
  1921.   begin
  1922.     l := 0; FName := ''; FUIN := '';
  1923.     if Length(Value) > Pos(#$fe, Value) + 1 then
  1924.       for i := Pos(#$fe, Value) + 1 to Length(Value) do
  1925.       begin
  1926.         if Value[i] = #$fe then
  1927.           Inc(l)
  1928.         else
  1929.         begin
  1930.           if l mod 2 = 0 then
  1931.             FName := FName + Value[i]
  1932.           else
  1933.             FUIN := FUIN + Value[i];
  1934.         end;
  1935.         if l = 2 then
  1936.         begin
  1937.           if (FName <> '') and (FUIN <> '') then
  1938.             List.Add(FName + '=' + FUIN);
  1939.           FName := '';
  1940.           FUIN := '';
  1941.           l := 0;
  1942.         end;
  1943.       end;
  1944.   end;
  1945. var
  1946.   Msg: String;
  1947.   lpkt: TRawPkt;
  1948.   LSeq: Word;
  1949.   cmd, scmd: Word;
  1950.   S, Desc, URL: String;
  1951.   List: TStringList;
  1952. begin
  1953.   if not DecryptPak(Ptr(LongWord(Pak) + 2), Pak^.Len - 2, 8) then Exit;
  1954.  
  1955.   Pak.Len := 2;
  1956.   if GetInt(Pak, 1) <> $02 then Exit;   //02 - PEER_MSG
  1957.   Inc(Pak^.Len, 4);                     //Packet checksum
  1958.   cmd := GetLInt(Pak, 2);
  1959.   Inc(Pak^.Len, 2);                     //Unknown: 0xe = 14.
  1960.   LSeq := GetLInt(Pak, 2);              //Sequence number.
  1961.   Inc(Pak^.Len, 12);                    //Unknown: 12 empty bytes
  1962.   scmd := GetLInt(Pak, 2);              //Sub command
  1963.   case cmd of
  1964.     $07ee:                              //2030 - normal message.
  1965.     begin
  1966.       if scmd = $0001 then              //Simple message
  1967.       begin
  1968.         Inc(Pak^.Len, 2);               //Unknown: empty.
  1969.         Inc(Pak^.Len, 2);               //Our status.
  1970.         Msg := GetLNTS(Pak);            //Finally the message.
  1971.  
  1972.         if Assigned(OnMessageRecv) then
  1973.           FOnMsg(Self, Msg, IntToStr(UIN));
  1974.       end else
  1975.       if scmd and $03e0 = $03e0 then    //Read auto-away message
  1976.       begin
  1977.         CreatePEER_AUTOMSG_ACK(@lpkt, FAutoAwayMsg, scmd, LSeq);        //Send ACK with auto msg reponse
  1978.         FDirect.SendData(UIN, @lpkt);
  1979.         Exit;                           //Do not send another ACK
  1980.       end else
  1981.       if scmd = $001a then              //Contacts
  1982.       begin
  1983.         Inc(Pak^.Len, 27);              //Skip 28 bytes of mostly unknown data
  1984.         S := GetStr(Pak, GetLInt(Pak, 4));
  1985.         if S = 'Contacts' then          //Receive contacts
  1986.         begin
  1987.           Inc(Pak^.Len, 19);            //Skip another 19 bytes of empty data + some lengths
  1988.           S := GetStr(Pak, GetLInt(Pak, 4));
  1989.           List := TStringList.Create;
  1990.           ParseContacts(S, List);
  1991.           if Assigned(OnContactListRecv) then
  1992.             FOnContactListRecv(Self, IntToStr(UIN), List)
  1993.           else
  1994.             List.Free;
  1995.         end else
  1996.         if S = 'Send Web Page Address (URL)' then
  1997.         begin
  1998.           Inc(Pak^.Len, 19);            //Skip another 19 bytes of empty data + some lengths
  1999.           S := GetStr(Pak, GetLInt(Pak, 4));
  2000.           if Assigned(OnURLRecv) then
  2001.           begin
  2002.             Desc := Copy(S, 0, Pos(#$fe, S) - 1);
  2003.             URL := Copy(S, Pos(#$fe, S) + 1, Length(S) - Pos(#$fe, S));
  2004.             if Assigned(OnURLRecv) then
  2005.               FOnURL(Self, Desc, URL, IntToStr(Uin));
  2006.           end;
  2007.         end else
  2008.         if S = 'Request For Contacts' then
  2009.         begin
  2010.           Inc(Pak^.Len, 19);            //Skip another 19 bytes of empty data + some lengths
  2011.           S := GetStr(Pak, GetLInt(Pak, 4));
  2012.           if Assigned(OnContactListRequest) then
  2013.             FOnContactListReq(Self, IntToStr(UIN), S)
  2014.         end;
  2015.       end;
  2016.     end;
  2017.     $07da:                              //Packet acks
  2018.     begin
  2019.       if Assigned(OnDirectPacketAck) then
  2020.         FOnDirectPktAck(Self, LSeq);
  2021.     end;
  2022.   end;
  2023.   //ACK received packet, if this packet isn't a "cancel given message" or "acknowledge message"
  2024.   if (cmd <> $07da) and (cmd <> $07d0) then
  2025.   begin
  2026.     if FDirect <> nil then
  2027.       begin
  2028.         CreatePEER_MSGACK(@lpkt, LSeq);
  2029.         FDirect.SendData(UIN, @lpkt);
  2030.       end;
  2031.   end;
  2032. end;
  2033.  
  2034. procedure TICQClient.FTOnConnectError(Sender: TObject);
  2035. begin
  2036.   if Assigned(OnConnectionFailed) then
  2037.     FOnConnectionFailed(Self);
  2038.   FLoggedIn := False;
  2039.   FTimer.Enabled := False;
  2040. end;
  2041.  
  2042. procedure TICQClient.FTOnDisconnect(Sender: TObject);
  2043. begin
  2044.   if FLoggedIn then
  2045.     if Assigned(OnConnectionFailed) then
  2046.       FOnConnectionFailed(Self);
  2047.   FLoggedIn := False;
  2048. end;
  2049.  
  2050. procedure TICQClient.FTOnDirectParse(Sender: TObject; Buffer: Pointer; BufLen: LongWord; Incoming: Boolean);
  2051. begin
  2052.   if Assigned(OnPktDirectParse) then
  2053.     FOnDPktParse(Self, Buffer, BufLen, Incoming);
  2054. end;
  2055.  
  2056. procedure TICQClient.FTOnPktParse(Sender: TObject; Buffer: Pointer; BufLen: LongWord; Incoming: Boolean);
  2057. begin
  2058.   if Assigned(OnPktParse) then
  2059.     FOnPktParse(Self, Buffer, BufLen, Incoming);
  2060. end;
  2061.  
  2062. procedure TICQClient.SetContactList(Value: TStrings);
  2063. begin
  2064.   FContactLst.Assign(Value);
  2065. end;
  2066.  
  2067. procedure TICQClient.SetVisibleList(Value: TStrings);
  2068. begin
  2069.   FVisibleLst.Assign(Value);
  2070. end;
  2071.  
  2072. procedure TICQClient.SetInvisibleList(Value: TStrings);
  2073. begin
  2074.   FInvisibleLst.Assign(Value);
  2075. end;
  2076.  
  2077. procedure TICQClient.OnTimeout;
  2078. begin
  2079.   FTimer.Enabled := False;
  2080.   FSock.Disconnect;
  2081.   if Assigned(OnConnectionFailed) then
  2082.     FOnConnectionFailed(Self);
  2083.   OnIntError(Self, ERR_CONNTIMEOUT, 'Connection timed out');  
  2084. end;
  2085.  
  2086. procedure Register;
  2087. begin
  2088.   RegisterComponents('Standard', [TICQClient]);
  2089. end;
  2090.  
  2091.  
  2092.  
  2093. end.
  2094.