/// <summary> /// Enqueues a SNAC for transmission with the appropriate RateClass /// </summary> /// <param name="dp">A <see cref="DataPacket"/> object</param> /// <remarks> /// <para> /// This needs commenting /// </para> /// </remarks> public static void BuildFLAP(DataPacket dp) { // Build the FLAP header dp.FLAP.DatagramSequenceNumber = 0; // Gets bound in Connection.cs dp.FLAP.Channel = (byte)FLAPChannels.SNACData; // Build the size of the data to be sent, not counting the FLAP header dp.FLAP.DataSize = (ushort)(10 + dp.Data.GetByteCount()); dp.Data.PrependOscarHeaders(dp.FLAP, dp.SNAC); SNACFunctions.DispatchToRateClass(dp); }
/// <summary> /// Notifies the BOS server that the client is ready to go online -- SNAC(01,02) /// </summary> /// <param name="dp">A <see cref="DataPacket"/> object</param> /// <remarks> /// The client sends this SNAC to notify the server that it is ready to receive messages /// and online notifications. /// <para> /// This SNAC must be sent within 30 seconds of connection, or the connection /// will drop. This should not ever be a problem, barring a lost connection well below /// the level of this library. /// </para> /// </remarks> public static void SendReadyNotification(DataPacket dp) { // Build SNAC(01,02) SNACHeader sh = new SNACHeader(); sh.FamilyServiceID = (ushort)SNACFamily.BasicOscarService; sh.FamilySubtypeID = (ushort)GenericServiceControls.ClientOnline; sh.Flags = 0x0000; sh.RequestID = Session.GetNextRequestID(); ConnectionManager cm = dp.ParentSession.Connections; ushort[] families = cm.GetFamilies(dp.ParentConnection); FamilyManager fm = dp.ParentSession.Families; Array.Sort(families); Array.Reverse(families); bool isChatRoomConnection = false; bool isChatNavService = false; ByteStream stream = new ByteStream(); DataPacket[][] delayedframes = new DataPacket[families.Length][]; for (int i = 0; i < families.Length; i++) { ushort family = families[i]; delayedframes[i] = dp.ParentSession.Connections.GetDelayedPackets(family); stream.WriteUshort(family); stream.WriteUshort(fm.GetFamilyVersion(family)); stream.WriteUshort(fm.GetFamilyToolID(family)); stream.WriteUshort(fm.GetFamilyToolVersion(family)); if (family == 0x000D) { isChatNavService = true; } else if (family == 0x000E) { isChatRoomConnection = true; } } /* * The initial service connection has to send SNAC(01,1E) before it actually * sends SNAC(01,02), thus the check for the initial connection here * and after it gets sent. */ Session sess = dp.ParentSession; if (!sess.LoggedIn) { SetExtendedStatus(sess, sess.AvailableMessage, ICQFlags.Normal, ICQStatus.Online); SetExtendedStatus(sess, null, ICQFlags.Normal, ICQStatus.Online); } // The connection is done, so start sending keepalives dp.ParentConnection.Connecting = false; dp.ParentConnection.ReadyForData = true; // Build and send a new DataPacket instead of re-using the originator; // SNAC(01,02) was getting sent twice on child connections with the reuse DataPacket dp2 = Marshal.BuildDataPacket(dp.ParentSession, sh, stream); dp2.ParentConnection = dp.ParentConnection; SNACFunctions.BuildFLAP(dp2); /* * If this is a new service connection, there is probably at least one * delayed packet. Process those packets. Additionally, if the new connection is * a chatroom connection, query the Rendezvous manager to see if it is the result * of an explict room creation (or implict, as in accepting a chat invitation). * If it was explict, notify the user that it's done */ if (sess.LoggedIn) { foreach (DataPacket[] list in delayedframes) { if (list == null) { continue; } foreach (DataPacket dp_delay in list) { dp_delay.ParentConnection = dp.ParentConnection; SNACFunctions.DispatchToRateClass(dp_delay); } } if (isChatNavService) { sess.ChatRooms.OnChatNavServiceAvailable(); } else if (isChatRoomConnection) { ChatRoom chatRoom = sess.Connections.GetChatByConnection((ChatConnection)dp.ParentConnection); sess.ChatRooms.OnChatRoomConnectionAvailable(chatRoom); } } }