private void handleTag(string tagName, ICQDataStream streamContent)
        {
            switch(tagName)
            {
                case "AnswerFlags":
                    byte[] baAnswerFlags = streamContent.readFixedBinary(5);
                    break;
                case "SendFlags":
                case "Status":
                case "Read":
                    byte[] baSendFlags = streamContent.readFixedBinary(5); // TODO: this is a special Uint format: First 0x69 then four bytes of uint
                    break;
                case "Body":
                    byte[] baBodyHeader = new byte[0x13];
                    streamContent.Read(baBodyHeader, 0, 0x3);

                    if (baBodyHeader[1] == 0xEB)    // as opposed to 0xEA
                    {
                        streamContent.Read(baBodyHeader, 3, 0x10);
                        //if (isOutgoing != ((baBodyHeader[4] & 0x01) == 0x01))
                        //    throw new InvalidDataException("Outgoing flag is different in packet head and body head");
                    }

                    byte[] bodyContent = streamContent.readBinary();
                    if (null != bodyContent)
                    {
                        // bodyContent contains 0x58 as start tag for only ANSI text (ICQ Pro 2003a) or RTF and ANSI (ICQ Pro 2003b)
                        ICQDataStream strmBody = new ICQDataStream(bodyContent);
                        if (0x58 == bodyContent[0])     // maybe bodyContent[0] and bodyContent[1] together are a package type number?
                        {
                            strmBody.Seek(2, SeekOrigin.Begin);
                            UInt32 iICQ2003bIndicator = strmBody.readUInt32();
                            if (0 != iICQ2003bIndicator)    // this means the RTF starts immediately. ICQ Pro 2003b style.
                            {
                                strmBody.Seek(-4, SeekOrigin.Current);
                                parseICQ2003bBody(strmBody);
                            }
                        }
                        else if (0x23 == bodyContent[0])    // this means:  ANSI + RTF + UTF-8
                            strmBody.Seek(2, SeekOrigin.Begin);
                        else if (0x03 == bodyContent[0])    // observed cases are only "The user has added you to his/her Contact list"
                                                            // + "You added him/her to your Contact List"
                        {
                            MessageType = "Added2ContactList";
                            return;
                        }
                        else if (0x01 == bodyContent[0])
                        {
                            MessageType = "AuthorizationRequestAccepted";
                            return;
                        }
                        else if (0x00 == bodyContent[0])
                        {
                            MessageType = "Contacts";
                            return;
                        }
                        else if (0x11 == bodyContent[0])
                        {
                            MessageType = "BirthdayReminder";
                            return;
                        }
                        else
                            throw new NotImplementedException("Message start tag " + bodyContent[0].ToString() + " not implemented");

                        parseICQ2003aBody(strmBody);
                    }

                    break;
                case "CLSID":
                    byte clsidStartTag = Convert.ToByte(streamContent.ReadByte());
                    byte[] clsidData = streamContent.readBinary();
                    break;
                case "ExtId":
                    byte[] extidContent = streamContent.readFixedBinary(3);
                    break;
                case "ExtName":
                    byte extName = Convert.ToByte(streamContent.ReadByte());
                    MessageType = streamContent.readString();
                    break;
                case "ExtVers":
                    byte[] extVers = streamContent.readFixedBinary( 5);
                    break;
                case "Folder":
                    byte[] folder = streamContent.readFixedBinary(3);
                    break;
                case "MsgUserName":
                    byte startTag = (byte)streamContent.ReadByte();
                    OtherPartyName = streamContent.readString();
                    break;
                case "Time":
                    byte timeStartTag = (byte)streamContent.ReadByte();
                    if (timeStartTag != 0x69)
                        throw new ArgumentException("Time tag expected to be 0x69, but it was " + timeStartTag.ToString());
                    TimeOfMessage = streamContent.readUnixTime();
                    break;
                default:
                    throw new NotImplementedException("Unknown tag \"" + tagName + "\"");
            }
        }
Example #2
0
        private void parseStream(ICQDataStream streamContent)
        {
            UInt32 iMessageLength = streamContent.readUInt32();
            if (0 == iMessageLength || iMessageLength > 65536)
                throw new InvalidDataException("This is not a message packet. Unusal packet length: " + iMessageLength.ToString());

            byte[] strangeHeading = streamContent.readFixedBinary(0x1E);
            // Byte strangeHeading[0x1a], lowest bit seems to indicate incoming messages
            // strangeHeading is either 00 00 00 00 xx xx xx xx 50 3b c1 5c 5c 95 d3 11 8d d7 00 10 4b 06 46 2e xx 02 xx 00 00 00
            //                   or                             e0 23 a3 db df b8 d1 11 8a 65 00 60 08 71 a3 91
            byte[] comparisonBits = new byte[] {
                0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
                0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                0x00, 0xF0, 0x00, 0xFF, 0xFF, 0xFF
            };

            if (!byteArraysAreEqual(strangeHeading,
                new byte[]
                {
                    0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, // the latter four bytes are disregared because of comparisonBits
                    0x50, 0x3b, 0xc1, 0x5c, 0x5c, 0x95, 0xd3, 0x11,
                    0x8d, 0xd7, 0x00, 0x10, 0x4b, 0x06, 0x46, 0x2e,
                    0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00
                },
                comparisonBits)
                &&
                !byteArraysAreEqual(strangeHeading,
                new byte[]
                {
                    0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, // the latter four bytes are disregared because of comparisonBits
                    0xe0, 0x23, 0xa3, 0xdb, 0xdf, 0xb8, 0xd1, 0x11,
                    0x8a, 0x65, 0x00, 0x60, 0x08, 0x71, 0xa3, 0x91,
                    0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00
                },
                comparisonBits)
               )
                throw new InvalidDataException("This is not a message packet. The identifying bits do not match!");

            MessageNumber = BitConverter.ToUInt32(strangeHeading, 4);   // this number increases, although not linearly
            UInt16 SessionNumber = BitConverter.ToUInt16(strangeHeading, 24);   // this number also increases, but more slowly

            iMessageType = streamContent.readUInt16();
            UIN = streamContent.readUInt32();

            if (1 == iMessageType)
            {
                parseMessagePacket(streamContent);
                return;
            }

            if (0x0d == iMessageType)
            {
                // it's an internet message. The UIN will not be correct (I've seen 0x0a as UIN)
                parseMessagePacket(streamContent);
                return;
            }

            if (0x13 == iMessageType)
            {
                UInt16 contactListLength = streamContent.readUInt16();
                //byte[] contacts = streamContent.readFixedBinary(contactListLength);

                // this is a list of contacts. First comes the number of contacts in decimal.
                // Then, for each contact, the UIN and then the display name is stored.
                // Different contacts as well as UIN and display name and the number of contacts are separated with 0xFE.
                // Contact names are encoded in the current code page
                return;
            }

            if (0x04 == iMessageType)   // URL
            {
                parseMessagePacket(streamContent);  // Looks almost like a message, except that a 0xFE separates a custom message from the URL
                return;
            }

            if (0x0c == iMessageType)   // only seen once and then it was mostly empty
            {
                parseMessagePacket(streamContent);
                return;
            }

            if (0x0e == iMessageType)   // Email
            {
                parseMessagePacket(streamContent);  // contains some specials: 0xFEs separate Sender Displayname, ?, ?, Sender mail address, ?, Message with HTML tags
                return;
            }

            Debugger.Break();   // TODO: this is an interesting special case. Let's have a look at it!
        }