// // Mozilla.cs // // Copyright (C) 2004 Novell, Inc. // // Authors: // Fredrik Hedberg (fredrik.hedberg@avafan.com) // // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // using System; using System.IO; using System.Text; using System.Collections; using System.Globalization; namespace Beagle.Util.Mozilla { /// /// Class representing a Mozilla profile, used to get a user's profiles and accounts /// public class Profile { // FIXME: Move Beagle.Daemon.PathFinder to Beagle.Platform namespace dammit #if true static string datadir = Environment.GetEnvironmentVariable("HOME"); // Unix #else static string datadir = Environment.GetEnvironmentVariable("APPDATA"); // Win32 #endif // FIXME: Add more default locations static string[] dirs = { "Thunderbird", ".thunderbird", "Mozilla" + System.IO.Path.DirectorySeparatorChar + "Firefox", ".mozilla" + System.IO.Path.DirectorySeparatorChar + "firefox" }; Hashtable accounts = new Hashtable (); public ICollection Accounts { get { return accounts.Values; } } string name; public string Name { get { return name; } } string path; public string Path { get { return path; } } /// /// Creates and parses a profile from a prefs.js file /// public Profile (string name, string path) { this.name = name; this.path = path; Preferences prefs = new Preferences ( System.IO.Path.Combine (path, "prefs.js")); string accountlist = prefs ["mail.accountmanager.accounts"]; if (accountlist != null) { string[] accounts = accountlist.Split (','); foreach (string accountname in accounts) { Account account = new Account (); string servername = prefs [String.Format ("mail.account.{0}.server", accountname)]; account.Path = prefs [String.Format ("mail.server.{0}.directory", servername)]; account.Name = prefs [String.Format ("mail.server.{0}.name", servername)]; account.Type = prefs [String.Format ("mail.server.{0}.type", servername)]; this.accounts.Add (accountname, account); } } } /// /// Fetch a users profiles from default locations /// public static ICollection ReadProfiles () { ArrayList profiles = new ArrayList (); foreach (string subdir in dirs) { string dir = System.IO.Path.Combine (datadir, subdir); if (!Directory.Exists (dir)) continue; profiles.AddRange (ReadProfiles (dir)); } return profiles; } /// /// Fetch a users profiles from a specific profiles.ini file /// public static ICollection ReadProfiles (string path) { ArrayList profiles = new ArrayList (); StreamReader reader = new StreamReader (new FileStream (System.IO.Path.Combine (path, "profiles.ini"), FileMode.Open, FileAccess.Read, FileShare.Read)); string lname = null; string lpath = null; string data = null; while ((data = reader.ReadLine ()) != null) { if (data.StartsWith ("[") && lname != null && lpath != null) profiles.Add (new Profile (lname, System.IO.Path.Combine(path, lpath))); if (data.IndexOf ("=") == -1) continue; string[] fields = data.Split ('='); switch (fields[0].ToLower ()) { case "name": lname = fields[1]; break; case "path": lpath = fields[1]; break; } } if (lname != null && lpath != null) profiles.Add (new Profile (lname, System.IO.Path.Combine (path, lpath))); return profiles; } } /// /// Class representing a Mozilla account /// public class Account { public string Path; public string Name; public string Type; } /// /// Class for parsing Mozilla preferences files - prefs.js /// public class Preferences { Hashtable properties = new Hashtable (); public Preferences (string path) { StreamReader reader = new StreamReader (new FileStream (path, FileMode.Open, FileAccess.Read, FileShare.Read)); string data; while ((data = reader.ReadLine ()) != null) { if (!data.StartsWith ("user_pref")) continue; // FIXME: Use regexps int index = data.IndexOf ('"',11); string key = data.Substring (11, index-11); string value = data.Substring (index, data.IndexOf (')')-index).Substring (3).Trim ('"'); properties[key] = value; } reader.Close (); } public IDictionary Properties { get { return properties; } } public ICollection Keys { get { return properties.Keys; } } virtual public string this [string key] { get { return (string) properties [key]; } set { if (value == null || value == "") { if (properties.Contains (key)) properties.Remove (key); return; } properties [key] = value as string; } } } /// /// Message (mail, rss whatever) in Mozilla /// public class Message { public string Id; public string Date; public string Subject; public string From; public string To; public string Path; public int Offset; StringBuilder body = new StringBuilder (); public string Body { get { return body.ToString (); } } public void AppendBody (string str) { body.Append (str); } public Hashtable Headers = new Hashtable (); } public class Address { public string Name; public string Email; public override string ToString () { if (Name != null && Name != "") return String.Format ("{0} <{1}>", Name, Email); else return Email; } public static ICollection Parse (string str) { return null; } } /// /// FIXME: This is a hack and does not comply with any RFC, nor does it support attachments, encodings and other fancy shit /// FIXME: Use a lib like gmime to parse messages, must be available on Linux, Win32 & MacOSX. /// public class MessageReader { StreamReader reader; bool hasMore = true; Message message; string path; public MessageReader (string path) : this (path, -1) { Console.WriteLine ("Doing: " + path); } public MessageReader (string path, int offset) { this.path = path; FileStream stream; try { stream = new FileStream (path, FileMode.Open, FileAccess.Read, FileShare.Read); if (offset > 0) stream.Seek (offset, SeekOrigin.Begin); reader = new StreamReader (stream); } catch (Exception e) { Console.WriteLine ("Could not open '{0}' (offset={1})", path, offset); Console.WriteLine (e); } reader.ReadLine (); } public bool HasMoreMessages { get { return hasMore; } } public Message NextMessage { get { Read (); return message; } } private void Read () { message = new Message (); message.Path = path; string data; bool isBody = false; try { while ((data = reader.ReadLine ()) != null) { // Break for new message if (data.StartsWith ("From - ")) { return; } // Add body to message if (isBody) { message.AppendBody (data); continue; } // Break for message content if (data.Length == 0) { isBody = true; continue; } // It's a header int index = data.IndexOf (":"); if (index != -1 && !data.StartsWith (" ")) { if (data.Length < index +2) continue; string key = data.Substring (0, index); string value = data.Substring (index + 2); message.Headers [key] = value; switch (key.ToLower ()) { case "subject": message.Subject = value; break; case "from": message.From = value; break; case "to": message.To = value; break; case "date": message.Date = value; break; case "message-id": char[] shit = {'<', '>'}; message.Id = value.Trim (shit); break; } } } hasMore = false; } catch (Exception e) { Console.WriteLine (e); return; } } } #if false public class Test { public static void Main (string[] args) { foreach (Profile profile in Profile.ReadProfiles ()) { Console.WriteLine("Profile: {0} - {1}", profile.Name, profile.Path); foreach (Account account in profile.Accounts) { Console.WriteLine ("\t{0} ({1}) - {2}", account.Name, account.Type, account.Path); } } } } #endif }