/// /// Copyright © 2003-2008 JetBrains s.r.o. /// You may distribute under the terms of the GNU General Public License, as published by the Free Software Foundation, version 2 (see License.txt in the repository root folder). /// using System; using System.Collections; using System.Diagnostics; using System.IO; using System.Text; using System.Web; using System.Xml; using JetBrains.Interop.WinApi; using Microsoft.Win32; namespace JetBrains.Omea.InstantMessaging.Trillian { /// /// Manages working with Trillian profile and log files. /// public class TrillianProfileManager { private readonly ArrayList _profiles = new ArrayList(); /// /// Finds the Trillian installation path and initializes the list of available profiles. /// public TrillianProfileManager() { string trillianDir = GetTrillianDirectory(); if(trillianDir == null) return; string globalDir = GetGlobalDirectory(trillianDir); if(globalDir != null) { string iniPath = Path.Combine(globalDir, "profiles.ini"); var profileCount = (int)Kernel32Dll.GetPrivateProfileIntW("Profiles", "num", 0, iniPath); for(int i = 0; i < profileCount; i++) { string section = "Profile" + i.ToString("D3"); string sName = Kernel32Dll.Helpers.GetProfileString(iniPath, section, "Name", ""); if(sName.Length > 0) { var prefType = (int)Kernel32Dll.GetPrivateProfileIntW(section, "Preferences Type", 0, iniPath); var profile = new TrillianProfile(trillianDir, sName, prefType); if(profile.IsValid()) _profiles.Add(profile); } } } } /// /// Returns the number of available Trillian profiles. /// public int ProfileCount { get { return _profiles.Count; } } /// /// Returns the enumerator for the collection of profiles. /// public IEnumerable Profiles { get { return _profiles; } } /// /// Returns the directory where the global profiles.ini file is stored. /// private static string GetGlobalDirectory(string trillianDir) { string iniPath = Path.Combine(trillianDir, "trillian.ini"); string globalDir = Kernel32Dll.Helpers.GetProfileString(iniPath, "General", "Global Directory", ""); if(!Directory.Exists(globalDir)) return null; return globalDir; } /// /// Returns the Trillian installation directory. /// private static string GetTrillianDirectory() { string uninstKey = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Trillian"; using(RegistryKey regKey = Registry.LocalMachine.OpenSubKey(uninstKey)) { if(regKey == null) return null; var uninstString = (string)regKey.GetValue("UninstallString"); if(uninstString == null) return null; int index = uninstString.LastIndexOf('\\'); if(index < 0) return null; return uninstString.Substring(0, index); } } } /// /// A single Trillian profile. /// public class TrillianProfile { private readonly string _name; private readonly string _profileDir; private TrillianBuddyGroup _buddies; private readonly string _aimIniPath; private string _icqProfileSection; /// /// Initializes the root directory for the specified profile. /// public TrillianProfile(string trillianDir, string name, int prefType) { _name = name; string profileRoot = null; switch(prefType) { case 0: profileRoot = Path.Combine(trillianDir, "users"); break; case 1: profileRoot = Environment.GetFolderPath(Environment.SpecialFolder.Personal); // My Documents profileRoot = Path.Combine(Path.GetDirectoryName(profileRoot), "Trillian\\User Settings"); break; } if(profileRoot != null) { _profileDir = Path.Combine(profileRoot, name); _aimIniPath = Path.Combine(_profileDir, "aim.ini"); } } /// /// Checks if the profile directory actually points to a valid Trillian profile. /// public bool IsValid() { return _profileDir != null && Directory.Exists(_profileDir); } public string Name { get { return _name; } } /// /// Returns the root buddy group of the Trillian contact list. /// public TrillianBuddyGroup Buddies { get { if(_buddies == null) LoadBuddies(); return _buddies; } } /// /// Returns the list of logs for the specified protocol. /// public TrillianLog[] GetLogs(string protocol) { string logDir = Path.Combine(_profileDir, Path.Combine("Logs", protocol)); if(!Directory.Exists(logDir)) return new TrillianLog[] {}; string[] fileNames = Directory.GetFiles(logDir); var result = new TrillianLog[fileNames.Length]; for(int i = 0; i < fileNames.Length; i++) result[i] = new TrillianLog(Path.Combine(logDir, fileNames[i])); return result; } /// /// Reads the specified setting for the ICQ profile from aim.ini. /// public string ReadICQSetting(string setting) { if(_icqProfileSection == null) _icqProfileSection = FindAIMorICQProfile(true); if(_icqProfileSection.Length == 0) return ""; return Kernel32Dll.Helpers.GetProfileString(_aimIniPath, _icqProfileSection, setting, ""); } /** * Loads and parses the buddy.xml file. */ private void LoadBuddies() { string buddyFile = Path.Combine(_profileDir, "Buddies.xml"); Trace.WriteLine("Loading buddy file " + buddyFile); try { var doc = new XmlDocument(); doc.Load(buddyFile); _buddies = new TrillianBuddyGroup(doc.SelectSingleNode("/buddies/section")); } catch(Exception ex) { Trace.WriteLine("Failed to load Buddies.xml: " + ex); } } /// /// Scans through aim.ini to find which of the profiles in it is an AIM or ICQ profile. /// private string FindAIMorICQProfile(bool needICQ) { for(int nProfile = 0; nProfile < 0x100; nProfile++) { string section = "profile " + nProfile; string sName = Kernel32Dll.Helpers.GetProfileString(_aimIniPath, section, "name", ""); if(sName.Length == 0) break; // An all-numeric name is the UIN of an ICQ account; any other name is // a screen name of an AIM account. int tmp; bool isICQ = Int32.TryParse(sName, out tmp); if(isICQ == needICQ) return section; } return ""; } } /** * A group of buddies in a Trillian profile. */ public class TrillianBuddyGroup { private string _title; private readonly ArrayList _buddies = new ArrayList(); private readonly ArrayList _groups = new ArrayList(); /** * Loads the buddy group data from the specified XML node. */ internal TrillianBuddyGroup(XmlNode node) { if(node == null) return; foreach(XmlNode childNode in node.ChildNodes) { Debug.WriteLine("Buddy group child node: " + childNode.Name); if(childNode.Name == "buddy") { var buddy = new TrillianBuddy(childNode); if(buddy.IsValid()) _buddies.Add(buddy); } else if(childNode.Name == "group") _groups.Add(new TrillianBuddyGroup(childNode)); else if(childNode.Name == "title") _title = childNode.Value; } } /** * Returns the IEnumerable for enumerating the buddies in a group. */ public IEnumerable Buddies { get { return _buddies; } } /** * Returns the IEnumerable for enumerating the child groups of a group. */ public IEnumerable Groups { get { return _groups; } } } /** * A single buddy in a Trillian profile. */ public class TrillianBuddy { private readonly string _protocol; private readonly string _address; private readonly string _nick; /** * Loads the buddy data from the specified XML node. */ internal TrillianBuddy(XmlNode node) { XmlAttribute url = node.Attributes["uri"]; if(url == null) return; var separators = new[] {':'}; string[] parts = url.Value.Split(separators, 2); if(parts.Length != 2) return; _protocol = parts[0]; byte[] partBytes = HttpUtility.UrlDecodeToBytes(parts[1]); parts = Encoding.Default.GetString(partBytes).Split(separators, 3); if(parts.Length != 3) return; _address = parts[1]; _nick = parts[2]; } public bool IsValid() { return _protocol != null && _address != null && _nick != null; } public string Protocol { get { return _protocol; } } public string Address { get { return _address; } } public string Nick { get { return _nick; } } } }