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 + "\""); } }
private void parseMessagePacket(ICQDataStream streamContent) { Text = streamContent.readString(); byte[] messageFlags = streamContent.readFixedBinary(0x0a); // Mostly 0x00, but last two bytes are 0xC8 0x01(?), 0x23 0x02, or 0x19 0x02, or 0x03 0x02 // the first four bytes seem to be ff ff ff ff in case of an SMS isOutgoing = ((messageFlags[4] & 0x01) == 0x01); TimeOfMessage = streamContent.readUnixTime(); byte[] zeroes = streamContent.readFixedBinary(0x13); if (streamContent.Position > streamContent.Length - 8) return; // not enough to read anymore if (zeroes.Any(zeroByte => zeroByte != 0x00)) return; // The RTF messages are always zero in these bytes UInt16 possibleRTFLength = streamContent.readUInt16(); byte[] baPossibleStrangeHeader = streamContent.readFixedBinary(6); // in later versions of ICQ, these prepend the RTF AfterTextFooterType footerType = parseFooterTypeFromStrangeHeader(possibleRTFLength, baPossibleStrangeHeader); if (footerType == AfterTextFooterType.NoFooterBeforeRTF || footerType == AfterTextFooterType.SMSText) streamContent.Seek(-8, SeekOrigin.Current); // seek back the 8 bytes for the header //else if () // streamContent.Seek(-6, SeekOrigin.Current); // Only an UTF-8 text will be provided else if (footerType == AfterTextFooterType.NoFooterAtAll) return; //else //{ // UInt32 nextStrangeNumber = streamContent.readUInt32(); // if (0x00c0c0c0 != (0xFFc0c0c0 & nextStrangeNumber)) // return; // // if the two strange numbers are there... just go on and parse the RTF :-) //} try { string textUTF8Temp; string textRTFTemp; streamContent.parsePossiblyRemainingRTFandUTF8(out textRTFTemp, out textUTF8Temp); TextRTF = textRTFTemp; // TextRTF will be null before that operation anyway if (null != textUTF8Temp) Text = textUTF8Temp; } catch (Exception ex) { throw new InvalidDataException("Parsed message text \"" + Text + "\" after which a footer of type " + footerType.ToString() + " was detected, but there was a problem parsing the RTF/UTF-8 footer.", ex); } // byte[] tail = streamContent.readFixedBinary(0x08); // zeroes for incoming messages, E4 04 00 00 00 80 80 00 for outgoing }