Index: src/Extensions/Banshee.AudioCd/Banshee.AudioCd/AudioCdDiscModel.cs
===================================================================
--- src/Extensions/Banshee.AudioCd/Banshee.AudioCd/AudioCdDiscModel.cs	(revision 4938)
+++ src/Extensions/Banshee.AudioCd/Banshee.AudioCd/AudioCdDiscModel.cs	(working copy)
@@ -89,17 +89,18 @@
             if (mb_disc == null) {
                 throw new ApplicationException ("Could not read contents of the disc. Platform may not be supported.");
             }
-            
-            for (int i = 0, n = mb_disc.TrackDurations.Length; i < n; i++) {
+
+            TimeSpan[] durations = mb_disc.GetTrackDurations ();
+            for (int i = 0, n = durations.Length; i < n; i++) {
                 AudioCdTrackInfo track = new AudioCdTrackInfo (this, volume.DeviceNode, i);
                 track.TrackNumber = i + 1;
                 track.TrackCount = n;
                 track.DiscNumber = 1;
-                track.Duration = TimeSpan.FromSeconds (mb_disc.TrackDurations[i]);
+                track.Duration = durations[i];
                 track.ArtistName = Catalog.GetString ("Unknown Artist");
                 track.AlbumTitle = Catalog.GetString ("Unknown Album");
                 track.TrackTitle = String.Format (Catalog.GetString ("Track {0}"), track.TrackNumber);
-                track.FileSize = mb_disc.TrackDurations[i] * PCM_FACTOR;
+                track.FileSize = PCM_FACTOR * (uint)track.Duration.TotalSeconds;
                 Add (track);
                 
                 duration += track.Duration;
@@ -120,67 +121,55 @@
             OnMetadataQueryStarted (mb_disc);
             
             Release release = Release.Query (mb_disc).PerfectMatch ();
-            if (release == null || release.Tracks.Count != Count) {
+            if (release == null || release.GetTracks ().Count != Count) {
                 OnMetadataQueryFinished (false);
                 return;
             }
                         
-            disc_title = release.Title;
+            disc_title = release.GetTitle ();
             
-            int disc_number = 1;
-            int i = 0;
-            
-            foreach (Disc disc in release.Discs) {
-                i++;
-                if (disc.Id == mb_disc.Id) {
-                    disc_number = i;
+            DateTime release_date = DateTime.MaxValue;
+
+            foreach (Event release_event in release.GetEvents ()) {
+                if (release_event.Date != null) {
+                    try {
+                        DateTime date = DateTime.Parse (release_event.Date, ApplicationContext.InternalCultureInfo);
+                        if (date < release_date) {
+                            release_date = date;
+                        }
+                    } catch {
+                        continue;
+                    }
                 }
             }
             
-            DateTime release_date = DateTime.MinValue;
-            int release_event_index = -1;
-            
-            for (i = 0; i < release.Events.Count; i++) {
-                // FIXME: This is sort of lame, but from what I've seen, 
-                // the US releases generally contain more info
-                if (release.Events[i].Country == "US") {
-                    release_event_index = i;
-                    break;
-                }
-            }
-            
-            if (release_event_index >= 0) {
-                release_date = DateTime.Parse (release.Events[release_event_index].Date, 
-                    ApplicationContext.InternalCultureInfo);
-            }
-            
             DatabaseArtistInfo artist = new DatabaseArtistInfo ();
-            artist.Name = release.Artist.Name;
-            artist.MusicBrainzId = release.Artist.Id;
+            artist.Name = release.GetArtist ().GetName ();
+            artist.MusicBrainzId = release.GetArtist ().Id;
             
             DatabaseAlbumInfo album = new DatabaseAlbumInfo ();
-            album.Title = release.Title;
+            album.Title = release.GetTitle ();
             album.ArtistName = artist.Name;
             album.MusicBrainzId = release.Id;
-            album.ReleaseDate = release_date;
+            album.ReleaseDate = release_date == DateTime.MaxValue ? DateTime.MinValue : release_date;
             
-            i = 0;
+            int i = 0;
             
-            foreach (Track track in release.Tracks) {
+            foreach (Track track in release.GetTracks ()) {
                 AudioCdTrackInfo model_track = (AudioCdTrackInfo)this[i++];
                 
                 model_track.MusicBrainzId = track.Id;
-                model_track.TrackTitle = track.Title;
-                model_track.ArtistName = track.Artist.Name;
-                model_track.AlbumTitle = release.Title;
-                model_track.DiscNumber = disc_number;
+                model_track.TrackTitle = track.GetTitle ();
+                model_track.ArtistName = track.GetArtist ().GetName ();
+                model_track.AlbumTitle = release.GetTitle ();
+                //model_track.DiscNumber = disc_number;
                 
                 model_track.Album = album;
                 model_track.Artist = new DatabaseArtistInfo ();
-                model_track.Artist.Name = track.Artist.Name;
-                model_track.Artist.MusicBrainzId = track.Artist.Id;
+                model_track.Artist.Name = track.GetArtist ().GetName ();
+                model_track.Artist.MusicBrainzId = track.GetArtist ().Id;
                 
-                if (!release_date.Equals (DateTime.MinValue)) {
+                if (release_date != DateTime.MinValue) {
                     model_track.Year = release_date.Year;
                 }
             }
Index: src/Libraries/MusicBrainz/MusicBrainz.csproj
===================================================================
--- src/Libraries/MusicBrainz/MusicBrainz.csproj	(revision 4938)
+++ src/Libraries/MusicBrainz/MusicBrainz.csproj	(working copy)
@@ -33,7 +33,6 @@
     <Compile Include="MusicBrainz\Disc.cs" />
     <Compile Include="MusicBrainz\Event.cs" />
     <Compile Include="MusicBrainz\Label.cs" />
-    <Compile Include="MusicBrainz\LinuxDisc.cs" />
     <Compile Include="MusicBrainz\LocalDisc.cs" />
     <Compile Include="MusicBrainz\MusicBrainzEntity.cs" />
     <Compile Include="MusicBrainz\MusicBrainzException.cs" />
@@ -45,8 +44,9 @@
     <Compile Include="MusicBrainz\Release.cs" />
     <Compile Include="MusicBrainz\Track.cs" />
     <Compile Include="MusicBrainz\Utils.cs" />
-    <Compile Include="MusicBrainz\Win32Disc.cs" />
     <Compile Include="MusicBrainz\XmlRequestEventArgs.cs" />
+    <Compile Include="MusicBrainz\DiscLinux.cs" />
+    <Compile Include="MusicBrainz\DiscWin32.cs" />
   </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <ProjectExtensions>
Index: src/Libraries/MusicBrainz/MusicBrainz/MusicBrainzObject.cs
===================================================================
--- src/Libraries/MusicBrainz/MusicBrainz/MusicBrainzObject.cs	(revision 4938)
+++ src/Libraries/MusicBrainz/MusicBrainz/MusicBrainzObject.cs	(working copy)
@@ -1,5 +1,3 @@
-#region License
-
 // MusicBrainzObject.cs
 //
 // Copyright (c) 2008 Scott Peterson <lunchtimemama@gmail.com>
@@ -22,8 +20,6 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-#endregion
-
 using System;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
@@ -67,10 +63,10 @@
         
         #region Constructors
 
-        internal MusicBrainzObject (string mbid, string parameters)
+        internal MusicBrainzObject (string id, string parameters)
         {
             all_data_loaded = true;
-            CreateFromMbid (mbid, parameters ?? CreateInc ());
+            CreateFromId (id, parameters ?? CreateInc ());
         }
 
         internal MusicBrainzObject (XmlReader reader, bool all_rels_loaded)
@@ -83,10 +79,10 @@
         
         #region Private Methods
         
-        void CreateFromMbid (string mbid, string parameters)
+        void CreateFromId (string id, string parameters)
         {
             XmlProcessingClosure (
-                CreateUrl (UrlExtension, mbid, parameters),
+                CreateUrl (UrlExtension, id, parameters),
                 delegate (XmlReader reader) {
                     reader.ReadToFollowing ("metadata");
                     reader.Read ();
@@ -107,24 +103,16 @@
                     all_rels_loaded = true;
                     switch (reader ["target-type"]) {
                     case "Artist":
-                        List<Relation<Artist>> artist_rels = new List<Relation<Artist>> ();
-                        CreateRelation (reader.ReadSubtree (), artist_rels);
-                        this.artist_rels = artist_rels.AsReadOnly ();
+                        artist_rels = CreateRelation<Artist> (reader.ReadSubtree ());
                         break;
                     case "Release":
-                        List<Relation<Release>> release_rels = new List<Relation<Release>> ();
-                        CreateRelation (reader.ReadSubtree (), release_rels);
-                        this.release_rels = release_rels.AsReadOnly ();
+                        release_rels = CreateRelation<Release> (reader.ReadSubtree ());
                         break;
                     case "Track":
-                        List<Relation<Track>> track_rels = new List<Relation<Track>> ();
-                        CreateRelation (reader.ReadSubtree (), track_rels);
-                        this.track_rels = track_rels.AsReadOnly ();
+                        track_rels = CreateRelation<Track> (reader.ReadSubtree ());
                         break;
                     case "Label":
-                        List<Relation<Label>> label_rels = new List<Relation<Label>> ();
-                        CreateRelation (reader.ReadSubtree (), label_rels);
-                        this.label_rels = label_rels.AsReadOnly ();
+                        label_rels = CreateRelation<Label> (reader.ReadSubtree ());
                         break;
                     case "Url":
                         if (!reader.ReadToDescendant ("relation")) break;
@@ -158,35 +146,35 @@
         
         #region Protected
         
-        protected bool AllDataLoaded {
+        internal bool AllDataLoaded {
             get { return all_data_loaded; }
         }
         
-        protected bool AllRelsLoaded {
+        internal bool AllRelsLoaded {
             get { return all_rels_loaded; }
             set { all_rels_loaded = value; }
         }
         
-        protected string CreateInc ()
+        internal string CreateInc ()
         {
             StringBuilder builder = new StringBuilder ();
             CreateIncCore (builder);
             return builder.ToString ();
         }
         
-        protected virtual void CreateIncCore (StringBuilder builder)
+        internal virtual void CreateIncCore (StringBuilder builder)
         {
             if (!all_rels_loaded)
                 AppendIncParameters (builder, rels_params);
         }
         
-        protected void AppendIncParameters (StringBuilder builder, string parameter)
+        internal static void AppendIncParameters (StringBuilder builder, string parameter)
         {
             builder.Append (builder.Length == 0 ? "&inc=" : "+");
             builder.Append (parameter);
         }
         
-        protected void AppendIncParameters (StringBuilder builder, string parameter1, string parameter2)
+        internal static void AppendIncParameters (StringBuilder builder, string parameter1, string parameter2)
         {
             builder.Append (builder.Length == 0 ? "&inc=" : "+");
             builder.Append (parameter1);
@@ -194,13 +182,13 @@
             builder.Append (parameter2);
         }
 
-        protected void AppendIncParameters (StringBuilder builder, string [] parameters)
+        internal static void AppendIncParameters (StringBuilder builder, string [] parameters)
         {
             foreach (string parameter in parameters)
                 AppendIncParameters (builder, parameter);
         }
         
-        protected void LoadMissingData ()
+        internal void LoadMissingData ()
         {
             if (!all_data_loaded) {
                 LoadMissingDataCore ();
@@ -208,48 +196,48 @@
             }
         }
 
-        protected void LoadMissingDataCore (MusicBrainzObject obj)
+        internal void LoadMissingDataCore (MusicBrainzObject obj)
         {
             if (!all_rels_loaded) {
-                artist_rels = obj.ArtistRelations;
-                release_rels = obj.ReleaseRelations;
-                track_rels = obj.TrackRelations;
-                label_rels = obj.LabelRelations;
-                url_rels = obj.UrlRelations;
+                artist_rels = obj.GetArtistRelations ();
+                release_rels = obj.GetReleaseRelations ();
+                track_rels = obj.GetTrackRelations ();
+                label_rels = obj.GetLabelRelations ();
+                url_rels = obj.GetUrlRelations ();
             }
         }
         
-        protected abstract void LoadMissingDataCore ();
-        protected abstract bool ProcessAttributes (XmlReader reader);
-        protected abstract bool ProcessXml (XmlReader reader);
-        protected abstract string UrlExtension { get; }
-        
-        #endregion
-        
-        #region Properties
-        
-        protected T GetPropertyOrNull<T> (ref T field_reference) where T : class
+        internal T GetPropertyOrNull<T> (ref T field_reference) where T : class
         {
             if (field_reference == null) LoadMissingData ();
             return field_reference;
         }
         
-        protected T GetPropertyOrDefault<T> (ref T? field_reference, T default_value) where T : struct
+        internal T GetPropertyOrDefault<T> (ref T? field_reference, T default_value) where T : struct
         {
             if (field_reference == null) LoadMissingData ();
             return field_reference ?? default_value;
         }
         
-        protected ReadOnlyCollection<T> GetPropertyOrNew<T> (ref ReadOnlyCollection<T> field_reference)
+        internal ReadOnlyCollection<T> GetPropertyOrNew<T> (ref ReadOnlyCollection<T> field_reference)
         {
             return GetPropertyOrNew (ref field_reference, true);
         }
         
-        protected ReadOnlyCollection<T> GetPropertyOrNew<T> (ref ReadOnlyCollection<T> field_reference, bool condition)
+        internal ReadOnlyCollection<T> GetPropertyOrNew<T> (ref ReadOnlyCollection<T> field_reference, bool condition)
         {
             if (field_reference == null && condition) LoadMissingData ();
             return field_reference ?? new ReadOnlyCollection<T> (new T [0]);
         }
+        
+        internal abstract void LoadMissingDataCore ();
+        internal abstract bool ProcessAttributes (XmlReader reader);
+        internal abstract bool ProcessXml (XmlReader reader);
+        internal abstract string UrlExtension { get; }
+        
+        #endregion
+        
+        #region Public
 
         public virtual string Id {
             get { return id; }
@@ -259,35 +247,45 @@
             get { return score; }
         }
 
-        public virtual ReadOnlyCollection<Relation<Artist>> ArtistRelations {
-            get { return GetPropertyOrNew (ref artist_rels, !all_rels_loaded); }
+        public virtual ReadOnlyCollection<Relation<Artist>> GetArtistRelations ()
+        {
+            return GetPropertyOrNew (ref artist_rels, !all_rels_loaded);
         }
 
-        public virtual ReadOnlyCollection<Relation<Release>> ReleaseRelations {
-            get { return GetPropertyOrNew (ref release_rels, !all_rels_loaded); }
+        public virtual ReadOnlyCollection<Relation<Release>> GetReleaseRelations ()
+        {
+            return GetPropertyOrNew (ref release_rels, !all_rels_loaded);
         }
 
-        public virtual ReadOnlyCollection<Relation<Track>> TrackRelations {
-            get { return GetPropertyOrNew (ref track_rels, !all_rels_loaded); }
+        public virtual ReadOnlyCollection<Relation<Track>> GetTrackRelations ()
+        {
+            return GetPropertyOrNew (ref track_rels, !all_rels_loaded);
         }
 
-        public virtual ReadOnlyCollection<Relation<Label>> LabelRelations {
-            get { return GetPropertyOrNew (ref label_rels, !all_rels_loaded); }
+        public virtual ReadOnlyCollection<Relation<Label>> GetLabelRelations ()
+        {
+            return GetPropertyOrNew (ref label_rels, !all_rels_loaded);
         }
 
-        public virtual ReadOnlyCollection<UrlRelation> UrlRelations {
-            get { return GetPropertyOrNew (ref url_rels, !all_rels_loaded); }
+        public virtual ReadOnlyCollection<UrlRelation> GetUrlRelations ()
+        {
+            return GetPropertyOrNew (ref url_rels, !all_rels_loaded);
         }
-
-        #endregion
         
-        #region Public
-        
         public override bool Equals (object obj)
         {
-            MusicBrainzObject mbobj = obj as MusicBrainzObject;
-            return mbobj != null && mbobj.GetType ().Equals (GetType ()) && mbobj.Id == Id;
+            return this == obj as MusicBrainzObject;
         }
+        
+        public static bool operator ==(MusicBrainzObject obj1, MusicBrainzObject obj2)
+        {
+            return (Object.ReferenceEquals (obj1, null) && Object.ReferenceEquals (obj2, null)) || (!Object.ReferenceEquals (obj1, null) && !Object.ReferenceEquals (obj2, null) && obj1.GetType () == obj2.GetType () && obj1.Id == obj2.Id);
+        }
+        
+        public static bool operator !=(MusicBrainzObject obj1, MusicBrainzObject obj2)
+        {
+            return !(obj1 == obj2);
+        }
 
         public override int GetHashCode ()
         {
@@ -298,8 +296,9 @@
 
         #region Static
 
-        static void CreateRelation<T> (XmlReader reader, List<Relation<T>> relations) where T : MusicBrainzObject
+        static ReadOnlyCollection<Relation<T>> CreateRelation<T> (XmlReader reader) where T : MusicBrainzObject
         {
+            List<Relation<T>> relations = new List<Relation<T>> ();
             while (reader.ReadToFollowing ("relation")) {
                 string type = reader ["type"];
                 RelationDirection direction = RelationDirection.Forward;
@@ -322,6 +321,7 @@
                     attributes));
             }
             reader.Close ();
+            return relations.AsReadOnly ();
         }
 
         static string CreateUrl (string url_extension, int limit, int offset, string parameters)
@@ -339,14 +339,14 @@
             return CreateUrl (url_extension, string.Empty, builder.ToString ());
         }
 
-        static string CreateUrl (string url_extension, string mbid, string parameters)
+        static string CreateUrl (string url_extension, string id, string parameters)
         {
             StringBuilder builder = new StringBuilder (
-                MusicBrainzService.ServiceUrl.Length + mbid.Length + parameters.Length + 9);
-            builder.Append (MusicBrainzService.ServiceUrl);
+                MusicBrainzService.ServiceUrl.AbsoluteUri.Length + id.Length + parameters.Length + 9);
+            builder.Append (MusicBrainzService.ServiceUrl.AbsoluteUri);
             builder.Append (url_extension);
             builder.Append ('/');
-            builder.Append (mbid);
+            builder.Append (id);
             builder.Append ("?type=xml");
             builder.Append (parameters);
             return builder.ToString ();
@@ -411,13 +411,16 @@
 
         #region Query
 
-        protected static byte QueryLimit {
+        internal static byte QueryLimit {
             get { return 100; }
         }
 
-        protected static string CreateLuceneParameter (string query)
+        internal static string CreateLuceneParameter (string query)
         {
-            return "&query=" + Utils.PercentEncode (query);
+            StringBuilder builder = new StringBuilder (query.Length + 7);
+            builder.Append ("&query=");
+            Utils.PercentEncode (builder, query);
+            return builder.ToString ();
         }
 
         internal static List<T> Query<T> (string url_extension,
Index: src/Libraries/MusicBrainz/MusicBrainz/Relation.cs
===================================================================
--- src/Libraries/MusicBrainz/MusicBrainz/Relation.cs	(revision 4938)
+++ src/Libraries/MusicBrainz/MusicBrainz/Relation.cs	(working copy)
@@ -1,5 +1,3 @@
-#region License
-
 // Relation.cs
 //
 // Copyright (c) 2008 Scott Peterson <lunchtimemama@gmail.com>
@@ -22,9 +20,8 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-#endregion
-
 using System;
+using System.Collections.ObjectModel;
 
 namespace MusicBrainz
 {
@@ -32,7 +29,7 @@
     {
         T target;
         string type;
-        string [] attributes;
+        ReadOnlyCollection<string> attributes;
         RelationDirection direction;
         string begin;
         string end;
@@ -45,7 +42,7 @@
             this.direction = direction;
             this.begin = begin;
             this.end = end;
-            this.attributes = attributes;
+            this.attributes = new ReadOnlyCollection<string> (attributes ?? new string [0]);
         }
 
         public T Target {
@@ -56,7 +53,7 @@
             get { return type; }
         }
 
-        public string [] Attributes {
+        public ReadOnlyCollection<string> Attributes {
             get { return attributes; }
         }
 
@@ -86,7 +83,7 @@
         }
     }
 
-    public sealed class UrlRelation : RelationBase<string>
+    public sealed class UrlRelation : RelationBase<Uri>
     {
         internal UrlRelation(string type,
                              string target,
@@ -94,7 +91,7 @@
                              string begin,
                              string end,
                              string [] attributes)
-            : base (type, target, direction, begin, end, attributes)
+            : base (type, new Uri (target), direction, begin, end, attributes)
         {
         }
     }
Index: src/Libraries/MusicBrainz/MusicBrainz/Label.cs
===================================================================
--- src/Libraries/MusicBrainz/MusicBrainz/Label.cs	(revision 4938)
+++ src/Libraries/MusicBrainz/MusicBrainz/Label.cs	(working copy)
@@ -1,5 +1,3 @@
-#region License
-
 // Label.cs
 //
 // Copyright (c) 2008 Scott Peterson <lunchtimemama@gmail.com>
@@ -22,8 +20,6 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-#endregion
-
 using System;
 using System.Text;
 using System.Xml;
@@ -43,11 +39,11 @@
         
         #region Constructors
 
-        Label (string mbid) : base (mbid, null)
+        Label (string id) : base (id, null)
         {
         }
 
-        Label (string mbid, string parameters) : base (mbid, parameters)
+        Label (string id, string parameters) : base (id, parameters)
         {
         }
 
@@ -59,24 +55,25 @@
         
         #region Protected
         
-        protected override string UrlExtension {
+        internal override string UrlExtension {
             get { return EXTENSION; }
         }
 
-        protected override void LoadMissingDataCore ()
+        internal override void LoadMissingDataCore ()
         {
             Label label = new Label (Id, CreateInc ());
-            type = label.Type;
+            type = label.GetLabelType ();
+            country = label.GetCountry ();
             base.LoadMissingDataCore (label);
         }
 
-        protected override bool ProcessAttributes (XmlReader reader)
+        internal override bool ProcessAttributes (XmlReader reader)
         {
             type = Utils.StringToEnum<LabelType> (reader ["type"]);
             return this.type != null;
         }
 
-        protected override bool ProcessXml (XmlReader reader)
+        internal override bool ProcessXml (XmlReader reader)
         {
             reader.Read ();
             bool result = base.ProcessXml (reader);
@@ -84,8 +81,9 @@
                 if (reader.Name == "country") {
                     result = true;
                     reader.Read ();
-                    if (reader.NodeType == XmlNodeType.Text)
+                    if (reader.NodeType == XmlNodeType.Text){
                         country = reader.ReadContentAsString ();
+                    }
                 } else reader.Skip (); // FIXME this is a workaround for Mono bug 334752
             }
             reader.Close ();
@@ -94,24 +92,26 @@
         
         #endregion
 
-        #region Properties
+        #region Public
         
-        public string Country {
-            get { return GetPropertyOrNull (ref country); }
+        public string GetCountry ()
+        {
+            return GetPropertyOrNull (ref country);
         }
 
-        public LabelType Type {
-            get { return GetPropertyOrDefault (ref type, LabelType.None); }
+        public LabelType GetLabelType ()
+        {
+            return GetPropertyOrDefault (ref type, LabelType.None);
         }
         
         #endregion
         
         #region Static
 
-        public static Label Get (string mbid)
+        public static Label Get (string id)
         {
-            if (mbid == null) throw new ArgumentNullException ("mbid");
-            return new Label (mbid);
+            if (id == null) throw new ArgumentNullException ("id");
+            return new Label (id);
         }
 
         public static Query<Label> Query (string name)
Index: src/Libraries/MusicBrainz/MusicBrainz/Release.cs
===================================================================
--- src/Libraries/MusicBrainz/MusicBrainz/Release.cs	(revision 4938)
+++ src/Libraries/MusicBrainz/MusicBrainz/Release.cs	(working copy)
@@ -1,5 +1,3 @@
-#region License
-
 // Release.cs
 //
 // Copyright (c) 2008 Scott Peterson <lunchtimemama@gmail.com>
@@ -22,8 +20,6 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-#endregion
-
 using System;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
@@ -52,11 +48,11 @@
         
         #region Constructors
 
-        Release (string mbid) : base (mbid, null)
+        Release (string id) : base (id, null)
         {
         }
 
-        Release (string mbid, string parameters) : base (mbid, parameters)
+        Release (string id, string parameters) : base (id, parameters)
         {
         }
 
@@ -66,15 +62,15 @@
         
         #endregion
         
-        #region Protected Overrides
+        #region Protected
         
-        protected override string UrlExtension {
+        internal override string UrlExtension {
             get { return EXTENSION; }
         }
         
         static readonly string [] track_params = new string [] { "tracks", "track-level-rels", "artist" };
         
-        protected override void CreateIncCore (StringBuilder builder)
+        internal override void CreateIncCore (StringBuilder builder)
         {
             AppendIncParameters (builder, "release-events", "labels");
             if (discs == null) AppendIncParameters (builder, "discs");
@@ -85,21 +81,21 @@
             base.CreateIncCore (builder);
         }
 
-        protected override void LoadMissingDataCore ()
+        internal override void LoadMissingDataCore ()
         {
             Release release = new Release (Id, CreateInc ());
-            type = release.Type;
-            status = release.Status;
-            language = release.Language;
-            script = release.Script;
-            asin = release.Asin;
-            events = release.Events;
-            if (discs == null) discs = release.Discs;
-            if (tracks == null) tracks = release.Tracks;
+            type = release.GetReleaseType ();
+            status = release.GetReleaseStatus ();
+            language = release.GetLanguage ();
+            script = release.GetScript ();
+            asin = release.GetAsin ();
+            events = release.GetEvents ();
+            if (discs == null) discs = release.GetDiscs ();
+            if (tracks == null) tracks = release.GetTracks ();
             base.LoadMissingDataCore (release);
         }
 
-        protected override bool ProcessAttributes (XmlReader reader)
+        internal override bool ProcessAttributes (XmlReader reader)
         {
             // How sure am I about getting the type and status in the "Type Status" format?
             // MB really ought to specify these two things seperatly.
@@ -115,7 +111,7 @@
             return this.type != null || this.status != null;
         }
 
-        protected override bool ProcessXml (XmlReader reader)
+        internal override bool ProcessXml (XmlReader reader)
         {
             reader.Read ();
             bool result = base.ProcessXml (reader);
@@ -155,7 +151,7 @@
                         track_number = int.Parse (offset) + 1;
                     if (reader.ReadToDescendant ("track")) {
                         List<Track> tracks = new List<Track> ();
-                        do tracks.Add (new Track (reader.ReadSubtree (), Artist, AllDataLoaded));
+                        do tracks.Add (new Track (reader.ReadSubtree (), GetArtist (), AllDataLoaded));
                         while (reader.ReadToNextSibling ("track"));
                         this.tracks = tracks.AsReadOnly ();
                     }
@@ -173,7 +169,7 @@
         
         #endregion
 
-        #region Properties
+        #region Public
 
         [Queryable ("reid")]
         public override string Id {
@@ -181,46 +177,55 @@
         }
 
         [Queryable ("release")]
-        public override string Title {
-            get { return base.Title; }
+        public override string GetTitle ()
+        { 
+            return base.GetTitle ();
         }
 
-        [Queryable]
-        public ReleaseType Type {
-            get { return GetPropertyOrDefault (ref type, ReleaseType.None); }
+        [Queryable ("type")]
+        public ReleaseType GetReleaseType ()
+        {
+            return GetPropertyOrDefault (ref type, ReleaseType.None);
         }
 
-        [Queryable]
-        public ReleaseStatus Status {
-            get { return GetPropertyOrDefault (ref status, ReleaseStatus.None); }
+        [Queryable ("status")]
+        public ReleaseStatus GetReleaseStatus ()
+        {
+            return GetPropertyOrDefault (ref status, ReleaseStatus.None);
         }
 
-        public string Language {
-            get { return GetPropertyOrNull (ref language); }
+        public string GetLanguage ()
+        {
+            return GetPropertyOrNull (ref language);
         }
 
-        [Queryable]
-        public string Script {
-            get { return GetPropertyOrNull (ref script); }
+        [Queryable ("script")]
+        public string GetScript ()
+        {
+            return GetPropertyOrNull (ref script);
         }
 
-        [Queryable]
-        public string Asin {
-            get { return GetPropertyOrNull (ref asin); }
+        [Queryable ("asin")]
+        public string GetAsin ()
+        {
+            return GetPropertyOrNull (ref asin);
         }
 
         [QueryableMember("Count", "discids")]
-        public ReadOnlyCollection<Disc> Discs {
-            get { return GetPropertyOrNew (ref discs); }
+        public ReadOnlyCollection<Disc> GetDiscs ()
+        { 
+            return GetPropertyOrNew (ref discs);
         }
 
-        public ReadOnlyCollection<Event> Events {
-            get { return GetPropertyOrNew (ref events); }
+        public ReadOnlyCollection<Event> GetEvents ()
+        {
+            return GetPropertyOrNew (ref events);
         }
 
         [QueryableMember ("Count", "tracks")]
-        public ReadOnlyCollection<Track> Tracks {
-            get { return GetPropertyOrNew (ref tracks); }
+        public ReadOnlyCollection<Track> GetTracks ()
+        {
+            return GetPropertyOrNew (ref tracks);
         }
 
         internal int TrackNumber {
@@ -231,10 +236,10 @@
         
         #region Static
 
-        public static Release Get (string mbid)
+        public static Release Get (string id)
         {
-            if (mbid == null) throw new ArgumentNullException ("mbid");
-            return new Release (mbid);
+            if (id == null) throw new ArgumentNullException ("id");
+            return new Release (id);
         }
 
         public static Query<Release> Query (string title)
@@ -373,9 +378,8 @@
             set { script = value; }
         }
 
-        public override string ToString ()
+        internal override void ToStringCore (StringBuilder builder)
         {
-            StringBuilder builder = new StringBuilder ();
             if (disc_id != null) {
                 builder.Append ("&discid=");
                 builder.Append (disc_id);
@@ -396,8 +400,6 @@
                 builder.Append ("&script=");
                 builder.Append (script);
             }
-            AppendBaseToBuilder (builder);
-            return builder.ToString ();
         }
     }
     
Index: src/Libraries/MusicBrainz/MusicBrainz/Event.cs
===================================================================
--- src/Libraries/MusicBrainz/MusicBrainz/Event.cs	(revision 4938)
+++ src/Libraries/MusicBrainz/MusicBrainz/Event.cs	(working copy)
@@ -1,5 +1,3 @@
-#region License
-
 // Event.cs
 //
 // Copyright (c) 2008 Scott Peterson <lunchtimemama@gmail.com>
@@ -22,8 +20,6 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-#endregion
-
 using System;
 using System.Xml;
 
Index: src/Libraries/MusicBrainz/MusicBrainz/Disc.cs
===================================================================
--- src/Libraries/MusicBrainz/MusicBrainz/Disc.cs	(revision 4938)
+++ src/Libraries/MusicBrainz/MusicBrainz/Disc.cs	(working copy)
@@ -1,5 +1,3 @@
-#region License
-
 // Disc.cs
 //
 // Copyright (c) 2008 Scott Peterson <lunchtimemama@gmail.com>
@@ -22,8 +20,6 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-#endregion
-
 using System;
 using System.Xml;
 
Index: src/Libraries/MusicBrainz/MusicBrainz/MusicBrainzEntity.cs
===================================================================
--- src/Libraries/MusicBrainz/MusicBrainz/MusicBrainzEntity.cs	(revision 4938)
+++ src/Libraries/MusicBrainz/MusicBrainz/MusicBrainzEntity.cs	(working copy)
@@ -1,5 +1,3 @@
-#region License
-
 // MusicBrainzEntity.cs
 //
 // Copyright (c) 2008 Scott Peterson <lunchtimemama@gmail.com>
@@ -22,8 +20,6 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-#endregion
-
 using System;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
@@ -49,7 +45,7 @@
         
         #region Constructors
         
-        internal MusicBrainzEntity (string mbid, string parameters) : base (mbid, parameters)
+        internal MusicBrainzEntity (string id, string parameters) : base (id, parameters)
         {
         }
 
@@ -61,24 +57,24 @@
         
         #region Protected
 
-        protected override void CreateIncCore (StringBuilder builder)
+        internal override void CreateIncCore (StringBuilder builder)
         {
             if (aliases == null) AppendIncParameters (builder, "aliases");
             base.CreateIncCore (builder);
         }
 
-        protected void LoadMissingDataCore (MusicBrainzEntity entity)
+        internal void LoadMissingDataCore (MusicBrainzEntity entity)
         {
-            name = entity.Name;
-            sort_name = entity.SortName;
-            disambiguation = entity.Disambiguation;
-            begin_date = entity.BeginDate;
-            end_date = entity.EndDate;
-            if (aliases == null) aliases = entity.Aliases;
+            name = entity.GetName ();
+            sort_name = entity.GetSortName ();
+            disambiguation = entity.GetDisambiguation ();
+            begin_date = entity.GetBeginDate ();
+            end_date = entity.GetEndDate ();
+            if (aliases == null) aliases = entity.GetAliases ();
             base.LoadMissingDataCore (entity);
         }
 
-        protected override bool ProcessXml (XmlReader reader)
+        internal override bool ProcessXml (XmlReader reader)
         {
             bool result = true;
             switch (reader.Name) {
@@ -119,47 +115,52 @@
             return result;
         }
         
-        protected static string CreateNameParameter (string name)
+        internal static string CreateNameParameter (string name)
         {
-            return "&name=" + Utils.PercentEncode (name);
+            StringBuilder builder = new StringBuilder (name.Length + 6);
+            builder.Append ("&name=");
+            Utils.PercentEncode (builder, name);
+            return builder.ToString ();
         }
         
         #endregion
 
-        #region Properties
+        #region Public
 
-        public virtual string Name {
-            get { return GetPropertyOrNull (ref name); }
+        public virtual string GetName ()
+        {
+            return GetPropertyOrNull (ref name);
         }
 
-        [Queryable]
-        public virtual string SortName {
-            get { return GetPropertyOrNull (ref sort_name); }
+        [Queryable ("sortname")]
+        public virtual string GetSortName ()
+        {
+            return GetPropertyOrNull (ref sort_name);
         }
 
         [Queryable ("comment")]
-        public virtual string Disambiguation {
-            get { return GetPropertyOrNull (ref disambiguation); }
+        public virtual string GetDisambiguation ()
+        {
+            return GetPropertyOrNull (ref disambiguation);
         }
 
         [Queryable ("begin")]
-        public virtual string BeginDate {
-            get { return GetPropertyOrNull (ref begin_date); }
+        public virtual string GetBeginDate ()
+        {
+            return GetPropertyOrNull (ref begin_date);
         }
 
         [Queryable ("end")]
-        public virtual string EndDate {
-            get { return GetPropertyOrNull (ref end_date); }
+        public virtual string GetEndDate ()
+        {
+            return GetPropertyOrNull (ref end_date);
         }
 
         [QueryableMember ("Contains", "alias")]
-        public virtual ReadOnlyCollection<string> Aliases {
-            get { return GetPropertyOrNew (ref aliases); }
+        public virtual ReadOnlyCollection<string> GetAliases ()
+        {
+            return GetPropertyOrNew (ref aliases);
         }
-
-        #endregion
-
-        #region Public
         
         public override string ToString ()
         {
Index: src/Libraries/MusicBrainz/MusicBrainz/XmlRequestEventArgs.cs
===================================================================
--- src/Libraries/MusicBrainz/MusicBrainz/XmlRequestEventArgs.cs	(revision 4938)
+++ src/Libraries/MusicBrainz/MusicBrainz/XmlRequestEventArgs.cs	(working copy)
@@ -1,5 +1,3 @@
-#region License
-
 // XmlRequestEventArgs.cs
 //
 // Copyright (c) 2008 Scott Peterson <lunchtimemama@gmail.com>
@@ -22,18 +20,16 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-#endregion
-
 using System;
 
 namespace MusicBrainz
 {
     public sealed class XmlRequestEventArgs : EventArgs
     {
-        public readonly string Uri;
+        public readonly Uri Uri;
         public readonly bool FromCache;
         
-        public XmlRequestEventArgs (string uri, bool fromCache)
+        internal XmlRequestEventArgs (Uri uri, bool fromCache)
         {
             Uri = uri;
             FromCache = fromCache;
Index: src/Libraries/MusicBrainz/MusicBrainz/LocalDisc.cs
===================================================================
--- src/Libraries/MusicBrainz/MusicBrainz/LocalDisc.cs	(revision 4938)
+++ src/Libraries/MusicBrainz/MusicBrainz/LocalDisc.cs	(working copy)
@@ -1,5 +1,3 @@
-#region License
-
 // LocalDisc.cs
 //
 // Copyright (c) 2008 Scott Peterson <lunchtimemama@gmail.com>
@@ -22,8 +20,6 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-#endregion
-
 using System;
 using System.Security.Cryptography;
 using System.Text;
@@ -32,25 +28,37 @@
 {
     public abstract class LocalDisc : Disc
     {
-        public static string SubmissionServiceUrl = "http://mm.musicbrainz.org/bare/cdlookup.html";
+        static readonly object submission_service_url_mutex = new object ();
+        static Uri submission_service_url = new Uri("http://mm.musicbrainz.org/bare/cdlookup.html");
+        public static Uri SubmissionServiceUrl {
+            get {
+                lock (submission_service_url_mutex) {
+                    return submission_service_url;
+                }
+            }
+            set {
+                if (value == null) throw new ArgumentNullException ("value");
+                lock (submission_service_url_mutex) {
+                    submission_service_url = value;
+                }
+            }
+        }
         
-        byte first_track;
-        byte last_track;
-        int [] track_durations;
-        int [] track_offsets = new int [100];
+        internal byte first_track;
+        internal byte last_track;
+        internal int [] track_offsets = new int [100];
+        TimeSpan [] track_durations;
         
         internal LocalDisc()
         {
         }
         
-        protected void Init ()
+        internal void Init ()
         {
-            track_durations = new int [last_track];
+            track_durations = new TimeSpan [last_track];
             for (int i = 1; i <= last_track; i++) {
-                track_durations [i - 1] = i < last_track
-                    ? track_offsets [i + 1] - track_offsets [i]
-                    : track_offsets [0] - track_offsets [i];
-                track_durations [i - 1] /= 75; // 75 frames in a second
+                track_durations [i - 1] = TimeSpan.FromSeconds (
+                    ((i < last_track ? track_offsets [i + 1] : track_offsets [0]) - track_offsets [i]) / 75); // 75 frames in a second
             }
             GenerateId ();
         }
@@ -59,8 +67,8 @@
         {
             StringBuilder input_builder = new StringBuilder (804);
             
-            input_builder.Append (string.Format ("{0:X2}", FirstTrack));
-            input_builder.Append (string.Format ("{0:X2}", LastTrack));
+            input_builder.Append (string.Format ("{0:X2}", first_track));
+            input_builder.Append (string.Format ("{0:X2}", last_track));
             
             for (int i = 0; i < track_offsets.Length; i++)
                 input_builder.Append (string.Format ("{0:X8}", track_offsets [i]));
@@ -79,33 +87,19 @@
             Id = hash_builder.ToString ();
         }
         
-        protected byte FirstTrack {
-            get { return first_track; }
-            set { first_track = value; }
-        }
-
-        protected byte LastTrack {
-            get { return last_track; }
-            set { last_track = value; }
-        }
-
-        protected int [] TrackOffsets {
-            get { return track_offsets; }
-        }
-        
-        public int [] TrackDurations {
-            get { return track_durations; }
-        }
-        
-        string submission_url;
-        public string SubmissionUrl {
+        public TimeSpan [] GetTrackDurations ()
+        {
+            return (TimeSpan []) track_durations.Clone ();
+         }
+        Uri submission_url;
+        public Uri SubmissionUrl {
             get { return submission_url ?? (submission_url = BuildSubmissionUrl ()); }
         }
         
-        string BuildSubmissionUrl ()
+        Uri BuildSubmissionUrl ()
         {
             StringBuilder builder = new StringBuilder ();
-            builder.Append (SubmissionServiceUrl);
+            builder.Append (SubmissionServiceUrl.AbsoluteUri);
             builder.Append ("?id=");
             builder.Append (Id);
             builder.Append ("&tracks=");
@@ -120,15 +114,26 @@
                 builder.Append ('+');
                 builder.Append (track_offsets [i]);
             }
-            return builder.ToString ();
+            return new Uri(builder.ToString());
         }
         
         public static LocalDisc GetFromDevice (string device)
         {
             if (device == null) throw new ArgumentNullException ("device");
-            return Environment.OSVersion.Platform != PlatformID.Unix
-                ? (LocalDisc)new Win32Disc (device)
-                : new LinuxDisc (device);
+            try {
+                return Environment.OSVersion.Platform != PlatformID.Unix
+                    ? (LocalDisc)new DiscWin32 (device)
+                    : new DiscLinux (device);
+            } catch (Exception exception) {
+                throw new LocalDiscException (exception);
+            }
         }
     }
+    
+    public class LocalDiscException : Exception
+    {
+        public LocalDiscException (Exception exception) : base ("Could not load local disc from device.", exception)
+        {
+        }
+    }
 }
Index: src/Libraries/MusicBrainz/MusicBrainz/Utils.cs
===================================================================
--- src/Libraries/MusicBrainz/MusicBrainz/Utils.cs	(revision 4938)
+++ src/Libraries/MusicBrainz/MusicBrainz/Utils.cs	(working copy)
@@ -1,5 +1,3 @@
-#region License
-
 // Utils.cs
 //
 // Copyright (c) 2008 Scott Peterson <lunchtimemama@gmail.com>
@@ -22,8 +20,6 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-#endregion
-
 using System;
 using System.Text;
 
@@ -63,13 +59,6 @@
             return null;
         }
         
-        public static string PercentEncode (string value)
-        {
-            StringBuilder builder = new StringBuilder ();
-            PercentEncode (builder, value);
-            return builder.ToString ();
-        }
-        
         public static void PercentEncode (StringBuilder builder, string value)
         {
             foreach (char c in value) {
Index: src/Libraries/MusicBrainz/MusicBrainz/LinuxDisc.cs
===================================================================
--- src/Libraries/MusicBrainz/MusicBrainz/LinuxDisc.cs	(revision 4938)
+++ src/Libraries/MusicBrainz/MusicBrainz/LinuxDisc.cs	(working copy)
@@ -1,166 +0,0 @@
-#region License
-
-// LinuxDisc.cs
-//
-// Copyright (c) 2008 Scott Peterson <lunchtimemama@gmail.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.
-
-#endregion
-
-using System;
-using System.Runtime.InteropServices;
-
-namespace MusicBrainz
-{
-    internal sealed class LinuxDisc : LocalDisc
-    {
-        const int O_RDONLY = 0x0;
-        const int O_NONBLOCK = 0x4000;
-        const int CDROMREADTOCHDR = 0x5305;
-        const int CDROMREADTOCENTRY = 0x5306;
-        const int CDROMMULTISESSION = 0x5310;
-        const int CDROM_LBA = 0x01;
-        const int CDROM_LEADOUT = 0xAA;
-        const int CD_FRAMES = 75;
-        const int XA_INTERVAL = ((60 + 90 + 2) * CD_FRAMES);
-        
-        [DllImport ("libc.so.6")]
-        static extern int open (string path, int flags);
-        
-        [DllImport ("libc.so.6")]
-        static extern int close (int fd);
-        
-        [DllImport ("libc.so.6", EntryPoint = "ioctl")]
-        static extern int read_toc_header (int fd, int request, ref cdrom_tochdr header);
-        static int read_toc_header (int fd, ref cdrom_tochdr header)
-        {
-            return read_toc_header (fd, CDROMREADTOCHDR, ref header);
-        }
-        
-        [DllImport ("libc.so.6", EntryPoint = "ioctl")]
-        static extern int read_multisession (int fd, int request, ref cdrom_multisession multisession);
-        static int read_multisession (int fd, ref cdrom_multisession multisession)
-        {
-            return read_multisession (fd, CDROMMULTISESSION, ref multisession);
-        }
-        
-        [DllImport ("libc.so.6", EntryPoint = "ioctl")]
-        static extern int read_toc_entry (int fd, int request, ref cdrom_tocentry entry);
-        static int read_toc_entry (int fd, ref cdrom_tocentry entry)
-        {
-            return read_toc_entry (fd, CDROMREADTOCENTRY, ref entry);
-        }
-        
-        struct cdrom_tochdr
-        {
-            public byte cdth_trk0;
-            public byte cdth_trk1;
-        }
-        
-        struct cdrom_tocentry
-        {
-            public byte cdte_track;
-            public byte adr_ctrl;
-            public byte cdte_format;
-            public int lba;
-            public byte cdte_datamode;
-        }
-        
-        struct cdrom_multisession
-        {
-            public int lba;
-            public byte xa_flag;
-            public byte addr_format;
-        }
-        
-        int ReadTocHeader (int fd)
-        {
-            cdrom_tochdr th = new cdrom_tochdr ();
-            cdrom_multisession ms = new cdrom_multisession ();
-            
-            int ret = read_toc_header (fd, ref th);
-            
-            if (ret < 0) return ret;
-            
-            FirstTrack = th.cdth_trk0;
-            LastTrack = th.cdth_trk1;
-            
-            ms.addr_format = CDROM_LBA;
-            ret = read_multisession (fd, ref ms);
-            
-            if(ms.xa_flag != 0) LastTrack--;
-            
-            return ret;
-        }
-        
-        int ReadTocEntry (int fd, byte track_number, ref ulong lba)
-        {
-            cdrom_tocentry te = new cdrom_tocentry ();
-            te.cdte_track = track_number;
-            te.cdte_format = CDROM_LBA;
-            
-            int ret = read_toc_entry (fd, ref te);
-            
-            if(ret == 0) lba = (ulong)te.lba;
-            
-            return ret;
-        }
-        
-        int ReadLeadout (int fd, ref ulong lba)
-        {
-            cdrom_multisession ms = new cdrom_multisession ();
-            ms.addr_format = CDROM_LBA;
-            
-            int ret = read_multisession (fd, ref ms);
-            
-            if (ms.xa_flag != 0) {
-                lba = (ulong)(ms.lba - XA_INTERVAL);
-                return ret;
-            }
-            
-            return ReadTocEntry (fd, CDROM_LEADOUT, ref lba);
-        }
-        
-        internal LinuxDisc (string device)
-        {
-            int fd = open (device, O_RDONLY | O_NONBLOCK);
-            
-            if (fd < 0) throw new Exception (String.Format ("Cannot open device '{0}'", device));
-            
-            try {
-                if (ReadTocHeader(fd) < 0) throw new Exception ("Cannot read table of contents");
-                if (LastTrack == 0) throw new Exception ("This disc has no tracks");
-                
-                ulong lba = 0;
-                ReadLeadout (fd, ref lba);
-                TrackOffsets [0] = (int)lba + 150;
-                
-                for (byte i = FirstTrack; i <= LastTrack; i++) {
-                    ReadTocEntry (fd, i, ref lba);
-                    TrackOffsets[i] = (int)lba + 150;
-                }
-            } finally {
-                close (fd);
-            }
-            
-            Init ();
-        }
-    }
-}
Index: src/Libraries/MusicBrainz/MusicBrainz/DiscLinux.cs
===================================================================
--- src/Libraries/MusicBrainz/MusicBrainz/DiscLinux.cs	(revision 0)
+++ src/Libraries/MusicBrainz/MusicBrainz/DiscLinux.cs	(revision 0)
@@ -0,0 +1,162 @@
+// LinuxDisc.cs
+//
+// Copyright (c) 2008 Scott Peterson <lunchtimemama@gmail.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.Runtime.InteropServices;
+
+namespace MusicBrainz
+{
+    internal sealed class DiscLinux : LocalDisc
+    {
+        const int O_RDONLY = 0x0;
+        const int O_NONBLOCK = 0x4000;
+        const int CDROMREADTOCHDR = 0x5305;
+        const int CDROMREADTOCENTRY = 0x5306;
+        const int CDROMMULTISESSION = 0x5310;
+        const int CDROM_LBA = 0x01;
+        const int CDROM_LEADOUT = 0xAA;
+        const int CD_FRAMES = 75;
+        const int XA_INTERVAL = ((60 + 90 + 2) * CD_FRAMES);
+        
+        [DllImport ("libc.so.6", CharSet = CharSet.Auto)]
+        static extern int open (string path, int flags);
+        
+        [DllImport ("libc.so.6")]
+        static extern int close (int fd);
+        
+        [DllImport ("libc.so.6", EntryPoint = "ioctl")]
+        static extern int read_toc_header (int fd, int request, ref cdrom_tochdr header);
+        static int read_toc_header (int fd, ref cdrom_tochdr header)
+        {
+            return read_toc_header (fd, CDROMREADTOCHDR, ref header);
+        }
+        
+        [DllImport ("libc.so.6", EntryPoint = "ioctl")]
+        static extern int read_multisession (int fd, int request, ref cdrom_multisession multisession);
+        static int read_multisession (int fd, ref cdrom_multisession multisession)
+        {
+            return read_multisession (fd, CDROMMULTISESSION, ref multisession);
+        }
+        
+        [DllImport ("libc.so.6", EntryPoint = "ioctl")]
+        static extern int read_toc_entry (int fd, int request, ref cdrom_tocentry entry);
+        static int read_toc_entry (int fd, ref cdrom_tocentry entry)
+        {
+            return read_toc_entry (fd, CDROMREADTOCENTRY, ref entry);
+        }
+        
+        struct cdrom_tochdr
+        {
+            public byte cdth_trk0;
+            public byte cdth_trk1;
+        }
+        
+        struct cdrom_tocentry
+        {
+            public byte cdte_track;
+            public byte adr_ctrl;
+            public byte cdte_format;
+            public int lba;
+            public byte cdte_datamode;
+        }
+        
+        struct cdrom_multisession
+        {
+            public int lba;
+            public byte xa_flag;
+            public byte addr_format;
+        }
+        
+        int ReadTocHeader (int fd)
+        {
+            cdrom_tochdr th = new cdrom_tochdr ();
+            cdrom_multisession ms = new cdrom_multisession ();
+            
+            int ret = read_toc_header (fd, ref th);
+            
+            if (ret < 0) return ret;
+            
+            first_track = th.cdth_trk0;
+            last_track = th.cdth_trk1;
+            
+            ms.addr_format = CDROM_LBA;
+            ret = read_multisession (fd, ref ms);
+            
+            if(ms.xa_flag != 0) last_track--;
+            
+            return ret;
+        }
+        
+        static int ReadTocEntry (int fd, byte track_number, ref ulong lba)
+        {
+            cdrom_tocentry te = new cdrom_tocentry ();
+            te.cdte_track = track_number;
+            te.cdte_format = CDROM_LBA;
+            
+            int ret = read_toc_entry (fd, ref te);
+            
+            if(ret == 0) lba = (ulong)te.lba;
+            
+            return ret;
+        }
+        
+        int ReadLeadout (int fd, ref ulong lba)
+        {
+            cdrom_multisession ms = new cdrom_multisession ();
+            ms.addr_format = CDROM_LBA;
+            
+            int ret = read_multisession (fd, ref ms);
+            
+            if (ms.xa_flag != 0) {
+                lba = (ulong)(ms.lba - XA_INTERVAL);
+                return ret;
+            }
+            
+            return ReadTocEntry (fd, CDROM_LEADOUT, ref lba);
+        }
+        
+        internal DiscLinux (string device)
+        {
+            int fd = open (device, O_RDONLY | O_NONBLOCK);
+            
+            if (fd < 0) throw new Exception (String.Format ("Cannot open device '{0}'", device));
+            
+            try {
+                if (ReadTocHeader(fd) < 0) throw new Exception ("Cannot read table of contents");
+                if (last_track == 0) throw new Exception ("This disc has no tracks");
+                
+                ulong lba = 0;
+                ReadLeadout (fd, ref lba);
+                track_offsets [0] = (int)lba + 150;
+                
+                for (byte i = first_track; i <= last_track; i++) {
+                    ReadTocEntry (fd, i, ref lba);
+                    track_offsets[i] = (int)lba + 150;
+                }
+            } finally {
+                close (fd);
+            }
+            
+            Init ();
+        }
+    }
+}
Index: src/Libraries/MusicBrainz/MusicBrainz/Track.cs
===================================================================
--- src/Libraries/MusicBrainz/MusicBrainz/Track.cs	(revision 4938)
+++ src/Libraries/MusicBrainz/MusicBrainz/Track.cs	(working copy)
@@ -1,5 +1,3 @@
-#region License
-
 // Track.cs
 //
 // Copyright (c) 2008 Scott Peterson <lunchtimemama@gmail.com>
@@ -22,8 +20,6 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-#endregion
-
 using System;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
@@ -46,11 +42,11 @@
         
         #region Constructors
 
-        Track (string mbid) : base (mbid, null)
+        Track (string id) : base (id, null)
         {
         }
 
-        Track (string mbid, string parameters) : base (mbid, parameters)
+        Track (string id, string parameters) : base (id, parameters)
         {
         }
 
@@ -64,34 +60,34 @@
         
         #endregion
 
-        #region Protected Overrides
+        #region Protected
         
-        protected override string UrlExtension {
+        internal override string UrlExtension {
             get { return EXTENSION; }
         }
         
-        protected override void CreateIncCore (StringBuilder builder)
+        internal override void CreateIncCore (StringBuilder builder)
         {
             if (releases == null) AppendIncParameters (builder, "releases");
             if (puids == null) AppendIncParameters (builder, "puids");
             base.CreateIncCore (builder);
         }
 
-        protected override void LoadMissingDataCore ()
+        internal override void LoadMissingDataCore ()
         {
             Track track = new Track (Id, CreateInc ());
-            duration = track.Duration;
-            if (releases == null) releases = track.Releases;
-            if (puids == null) puids = track.Puids;
+            duration = track.GetDuration ();
+            if (releases == null) releases = track.GetReleases ();
+            if (puids == null) puids = track.GetPuids ();
             base.LoadMissingDataCore (track);
         }
 
-        protected override bool ProcessAttributes (XmlReader reader)
+        internal override bool ProcessAttributes (XmlReader reader)
         {
             return true;
         }
 
-        protected override bool ProcessXml (XmlReader reader)
+        internal override bool ProcessXml (XmlReader reader)
         {
             reader.Read ();
             bool result = base.ProcessXml (reader);
@@ -131,7 +127,7 @@
         
         #endregion
 
-        #region Properties
+        #region Public
 
         [Queryable ("trid")]
         public override string Id {
@@ -139,30 +135,33 @@
         }
 
         [Queryable ("track")]
-        public override string Title {
-            get { return base.Title; }
+        public override string GetTitle ()
+        {
+            return base.GetTitle ();
         }
 
         [Queryable ("dur")]
-        public uint Duration {
-            get { return duration; }
+        public uint GetDuration ()
+        {
+            return duration;
         }
 
         [QueryableMember ("Contains", "release")]
-        public ReadOnlyCollection<Release> Releases {
-            get { return GetPropertyOrNew (ref releases); }
-
+        public ReadOnlyCollection<Release> GetReleases ()
+        {
+            return GetPropertyOrNew (ref releases);
         }
 
-        public ReadOnlyCollection<string> Puids {
-            get { return GetPropertyOrNew (ref puids); }
+        public ReadOnlyCollection<string> GetPuids ()
+        {
+            return GetPropertyOrNew (ref puids);
         }
 
         public int GetTrackNumber (Release release)
         {
             if (release == null) throw new ArgumentNullException ("release");
             
-            foreach (Release r in Releases)
+            foreach (Release r in GetReleases ())
                 if (r.Equals (release))
                     return r.TrackNumber;
             return -1;
@@ -172,10 +171,10 @@
         
         #region Static
 
-        public static Track Get (string mbid)
+        public static Track Get (string id)
         {
-            if (mbid == null) throw new ArgumentNullException ("mbid");
-            return new Track (mbid);
+            if (id == null) throw new ArgumentNullException ("id");
+            return new Track (id);
         }
 
         public static Query<Track> Query (string title)
@@ -187,27 +186,27 @@
             return Query (parameters);
         }
 
-        public static Query<Track> Query (string title, string release)
+        public static Query<Track> Query (string title, string artist)
         {
             if (title == null) throw new ArgumentNullException ("title");
-            if (release == null) throw new ArgumentNullException ("release");
+            if (artist == null) throw new ArgumentNullException ("artist");
             
             TrackQueryParameters parameters = new TrackQueryParameters ();
             parameters.Title = title;
-            parameters.Release = release;
+            parameters.Artist = artist;
             return Query (parameters);
         }
         
-        public static Query<Track> Query (string title, string release, string artist)
+        public static Query<Track> Query (string title, string artist, string release)
         {
             if (title == null) throw new ArgumentNullException ("title");
-            if (release == null) throw new ArgumentNullException ("release");
             if (artist == null) throw new ArgumentNullException ("artist");
+            if (release == null) throw new ArgumentNullException ("release");
             
             TrackQueryParameters parameters = new TrackQueryParameters ();
             parameters.Title = title;
-            parameters.Release = release;
             parameters.Artist = artist;
+            parameters.Release = release;
             return Query (parameters);
         }
 
@@ -266,9 +265,8 @@
             set { puid = value; }
         }
 
-        public override string ToString ()
+        internal override void ToStringCore (StringBuilder builder)
         {
-            StringBuilder builder = new StringBuilder ();
             if (release != null) {
                 builder.Append ("&release=");
                 Utils.PercentEncode (builder, release);
@@ -289,8 +287,6 @@
                 builder.Append ("&puid=");
                 builder.Append (puid);
             }
-            AppendBaseToBuilder (builder);
-            return builder.ToString ();
         }
     }
     
Index: src/Libraries/MusicBrainz/MusicBrainz/MusicBrainzException.cs
===================================================================
--- src/Libraries/MusicBrainz/MusicBrainz/MusicBrainzException.cs	(revision 4938)
+++ src/Libraries/MusicBrainz/MusicBrainz/MusicBrainzException.cs	(working copy)
@@ -1,5 +1,3 @@
-#region License
-
 // MusicBrainzException.cs
 //
 // Copyright (c) 2008 Scott Peterson <lunchtimemama@gmail.com>
@@ -22,8 +20,6 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-#endregion
-
 using System;
 
 namespace MusicBrainz
@@ -31,7 +27,7 @@
     public sealed class MusicBrainzInvalidParameterException : Exception
     {
         public MusicBrainzInvalidParameterException ()
-            : base ("One of the parameters is invalid. The MBID may be invalid, or you may be using an illegal parameter for this resource type.")
+            : base ("One of the parameters is invalid. The ID may be invalid, or you may be using an illegal parameter for this resource type.")
         {
         }
     }
Index: src/Libraries/MusicBrainz/MusicBrainz/MusicBrainzItem.cs
===================================================================
--- src/Libraries/MusicBrainz/MusicBrainz/MusicBrainzItem.cs	(revision 4938)
+++ src/Libraries/MusicBrainz/MusicBrainz/MusicBrainzItem.cs	(working copy)
@@ -1,5 +1,3 @@
-#region License
-
 // MusicBrainzItem.cs
 //
 // Copyright (c) 2008 Scott Peterson <lunchtimemama@gmail.com>
@@ -22,8 +20,6 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-#endregion
-
 using System;
 using System.Text;
 using System.Xml;
@@ -43,7 +39,7 @@
         
         #region Constructors
         
-        internal MusicBrainzItem (string mbid, string parameters) : base (mbid, parameters)
+        internal MusicBrainzItem (string id, string parameters) : base (id, parameters)
         {
         }
 
@@ -56,20 +52,20 @@
         
         #region Protected Overrides
 
-        protected override void CreateIncCore (StringBuilder builder)
+        internal override void CreateIncCore (StringBuilder builder)
         {
             if (artist == null) AppendIncParameters(builder, "artist");
             base.CreateIncCore (builder);
         }
 
-        protected void LoadMissingDataCore (MusicBrainzItem item)
+        internal void LoadMissingDataCore (MusicBrainzItem item)
         {
-            title = item.Title;
-            if (artist == null) artist = item.Artist;
+            title = item.GetTitle ();
+            if (artist == null) artist = item.GetArtist ();
             base.LoadMissingDataCore (item);
         }
 
-        protected override bool ProcessXml (XmlReader reader)
+        internal override bool ProcessXml (XmlReader reader)
         {
             bool result = true;
             switch (reader.Name) {
@@ -90,21 +86,19 @@
         
         #endregion
 
-        #region Properties
+        #region Public
         
-        public virtual string Title {
-            get { return GetPropertyOrNull (ref title); }
+        public virtual string GetTitle ()
+        {
+            return GetPropertyOrNull (ref title);
         }
 
         [Queryable ("artist")]
-        public virtual Artist Artist {
-            get { return GetPropertyOrNull (ref artist); }
+        public virtual Artist GetArtist ()
+        {
+            return GetPropertyOrNull (ref artist);
         }
         
-        #endregion
-
-        #region Public
-        
         public override string ToString ()
         {
             return title;
@@ -158,8 +152,12 @@
             set { count = value; }
         }
 
-        protected void AppendBaseToBuilder (StringBuilder builder)
+        internal abstract void ToStringCore (StringBuilder builder);
+        
+        public override string ToString ()
         {
+            StringBuilder builder = new StringBuilder ();
+            ToStringCore (builder);
             if (title != null) {
                 builder.Append ("&title=");
                 Utils.PercentEncode (builder, title);
@@ -184,6 +182,7 @@
                 builder.Append ("&count=");
                 builder.Append (count.Value);
             }
+            return builder.ToString ();
         }
     }
     
Index: src/Libraries/MusicBrainz/MusicBrainz/Query.cs
===================================================================
--- src/Libraries/MusicBrainz/MusicBrainz/Query.cs	(revision 4938)
+++ src/Libraries/MusicBrainz/MusicBrainz/Query.cs	(working copy)
@@ -1,5 +1,3 @@
-#region License
-
 // Query.cs
 //
 // Copyright (c) 2008 Scott Peterson <lunchtimemama@gmail.com>
@@ -22,8 +20,6 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-#endregion
-
 using System;
 using System.Collections;
 using System.Collections.Generic;
@@ -96,7 +92,7 @@
 
         public T this [int i] {
             get {
-                if (i < 0 || i >= Count) throw new IndexOutOfRangeException ();
+                if (i < 0 || i >= Count) throw new ArgumentOutOfRangeException ("i");
                 if (i <= offset || i >= offset + limit) 
                     Offset = i;
                 return ResultsWindow [i - offset];
@@ -129,33 +125,14 @@
             return Best (100);
         }
         
-        public IEnumerable<T> Best (int score_threshold)
+        public IEnumerable<T> Best (int scoreThreshold)
         {
             foreach (T result in this) {
-                if (result.Score < score_threshold) yield break;
+                if (result.Score < scoreThreshold) yield break;
                 yield return result;
             }
         }
         
-        public List<T> ToList ()
-        {
-            return ToList (0);
-        }
-        
-        public List<T> ToList (int score_threshold)
-        {
-            List<T> list = new List<T> (score_threshold == 0 ? Count : 0);
-            foreach (T result in Best(score_threshold)) list.Add (result);
-            return list;
-        }
-        
-        public T [] ToArray ()
-        {
-            T [] array = new T [Count];
-            for(int i = 0; i < Count; i++) array [i] = this [i];
-            return array;
-        }
-        
         public IEnumerator<T> GetEnumerator ()
         {
             for (int i = 0; i < Count; i++) yield return this [i];
@@ -175,31 +152,39 @@
         
     }
 
-    [AttributeUsage (AttributeTargets.Property)]
+    [AttributeUsage (AttributeTargets.Method | AttributeTargets.Property)]
     internal sealed class QueryableAttribute : Attribute
     {
-        public readonly string Name;
+        readonly string name;
         
-        public QueryableAttribute ()
+        public QueryableAttribute (string name)
         {
+            this.name = name;
         }
         
-        public QueryableAttribute (string name)
-        {
-            Name = name;
+        public string Name {
+            get { return name; }
         }
     }
 
-    [AttributeUsage (AttributeTargets.Property)]
+    [AttributeUsage (AttributeTargets.Method | AttributeTargets.Property)]
     internal sealed class QueryableMemberAttribute : Attribute
     {
-        public readonly string Name;
-        public readonly string Member;
+        readonly string member;
+        readonly string name;
         
         public QueryableMemberAttribute (string member, string name)
         {
-            Member = member;
-            Name = name;
+            this.member = member;
+            this.name = name;
         }
+        
+        public string Member {
+            get { return member; }
+        }
+        
+        public string Name {
+            get { return name; }
+        }
     }
 }
Index: src/Libraries/MusicBrainz/MusicBrainz/Win32Disc.cs
===================================================================
--- src/Libraries/MusicBrainz/MusicBrainz/Win32Disc.cs	(revision 4938)
+++ src/Libraries/MusicBrainz/MusicBrainz/Win32Disc.cs	(working copy)
@@ -1,118 +0,0 @@
-#region License
-
-// Win32Disc.cs
-//
-// Copyright (c) 2008 Scott Peterson <lunchtimemama@gmail.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.
-
-#endregion
-
-using System;
-using System.Runtime.InteropServices;
-using System.Text;
-using System.Threading;
-
-namespace MusicBrainz
-{
-    internal sealed class Win32Disc : LocalDisc
-    {
-        [DllImport ("winmm")]
-        static extern Int32 mciSendString (String command, StringBuilder buffer, Int32 bufferSize, IntPtr hwndCallback);
-
-        [DllImport ("winmm")]
-        static extern Int32 mciGetErrorString (Int32 errorCode, StringBuilder errorText, Int32 errorTextSize);
-        
-        delegate void MciCall (string result);
-
-        internal Win32Disc (string device)
-        {
-            string device_string = device.Length == 0 ? "cdaudio" : string.Format ("{0} type cdaudio", device);
-
-            string alias = string.Format ("musicbrainz_cdio_{0}_{1}",
-                Environment.TickCount, Thread.CurrentThread.ManagedThreadId);
-
-            MciClosure (
-                "sysinfo cdaudio quantity wait",
-                "Could not get the list of CD audio devices",
-                delegate (string result) {
-                    if (int.Parse (result.ToString ()) <= 0)
-                        throw new Exception ("No CD audio devices present.");
-                });
-
-            MciClosure (
-                string.Format ("open {0} shareable alias {1} wait", device_string, alias),
-                string.Format ("Could not open device {0}", device),
-                null);
-
-            MciClosure (
-                string.Format ("status {0} number of tracks wait", alias),
-                "Could not read number of tracks",
-                delegate (string result) {
-                    FirstTrack = 1;
-                    LastTrack = byte.Parse (result);
-                });
-
-            MciClosure (
-                string.Format ("set {0} time format msf wait", alias),
-                "Could not set time format",
-                null);
-
-            for (int i = 1; i <= LastTrack; i++)
-                MciClosure (
-                    string.Format ("status {0} position track {1} wait", alias, i),
-                    string.Format ("Could not get position for track {0}", i),
-                    delegate (string result) {
-                        TrackOffsets [i] =
-                            int.Parse (result.Substring (0,2)) * 4500 +
-                            int.Parse (result.Substring (3,2)) * 75 +
-                            int.Parse (result.Substring (6,2));
-                    });
-
-            MciClosure (
-                string.Format ("status {0} length track {1} wait", alias, LastTrack),
-                "Could not read the length of the last track",
-                delegate (string result) {
-                    TrackOffsets [0] =
-                        int.Parse (result.Substring (0, 2)) * 4500 +
-                        int.Parse (result.Substring (3, 2)) * 75 +
-                        int.Parse (result.Substring (6, 2)) +
-                        TrackOffsets [LastTrack] + 1;
-                });
-
-            MciClosure (
-                string.Format ("close {0} wait", alias),
-                string.Format ("Could not close device {0}", device),
-                null);
-            
-            Init ();
-        }
-
-        static StringBuilder mci_result = new StringBuilder (128);
-        static StringBuilder mci_error = new StringBuilder (256);
-        static void MciClosure (string command, string failure_message, MciCall code)
-        {
-            int ret = mciSendString (command, mci_result, mci_result.Capacity, IntPtr.Zero);
-            if (ret != 0) {
-                mciGetErrorString (ret, mci_error, mci_error.Capacity);
-                throw new Exception (string.Format ("{0} : {1}", failure_message, mci_error.ToString ()));
-            } else if (code != null) code (mci_result.ToString ());
-        }
-    }
-}
Index: src/Libraries/MusicBrainz/MusicBrainz/DiscWin32.cs
===================================================================
--- src/Libraries/MusicBrainz/MusicBrainz/DiscWin32.cs	(revision 0)
+++ src/Libraries/MusicBrainz/MusicBrainz/DiscWin32.cs	(revision 0)
@@ -0,0 +1,114 @@
+// Win32Disc.cs
+//
+// Copyright (c) 2008 Scott Peterson <lunchtimemama@gmail.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.Runtime.InteropServices;
+using System.Text;
+using System.Threading;
+
+namespace MusicBrainz
+{
+    internal sealed class DiscWin32 : LocalDisc
+    {
+        [DllImport ("winmm")]
+        static extern Int32 mciSendString ([MarshalAs(UnmanagedType.LPTStr)] String command, [MarshalAs(UnmanagedType.LPTStr)] StringBuilder buffer, Int32 bufferSize, IntPtr hwndCallback);
+
+        [DllImport ("winmm")]
+        static extern Int32 mciGetErrorString (Int32 errorCode, [MarshalAs(UnmanagedType.LPTStr)] StringBuilder errorText, Int32 errorTextSize);
+        
+        delegate void MciCall (string result);
+
+        internal DiscWin32 (string device)
+        {
+            string device_string = device.Length == 0 ? "cdaudio" : string.Format ("{0} type cdaudio", device);
+
+            string alias = string.Format ("musicbrainz_cdio_{0}_{1}",
+                Environment.TickCount, Thread.CurrentThread.ManagedThreadId);
+
+            MciClosure (
+                "sysinfo cdaudio quantity wait",
+                "Could not get the list of CD audio devices",
+                delegate (string result) {
+                    if (int.Parse (result.ToString ()) <= 0)
+                        throw new Exception ("No CD audio devices present.");
+                });
+
+            MciClosure (
+                string.Format ("open {0} shareable alias {1} wait", device_string, alias),
+                string.Format ("Could not open device {0}", device),
+                null);
+
+            MciClosure (
+                string.Format ("status {0} number of tracks wait", alias),
+                "Could not read number of tracks",
+                delegate (string result) {
+                    first_track = 1;
+                    last_track = byte.Parse (result);
+                });
+
+            MciClosure (
+                string.Format ("set {0} time format msf wait", alias),
+                "Could not set time format",
+                null);
+
+            for (int i = 1; i <= last_track; i++)
+                MciClosure (
+                    string.Format ("status {0} position track {1} wait", alias, i),
+                    string.Format ("Could not get position for track {0}", i),
+                    delegate (string result) {
+                        track_offsets [i] =
+                            int.Parse (result.Substring (0,2)) * 4500 +
+                            int.Parse (result.Substring (3,2)) * 75 +
+                            int.Parse (result.Substring (6,2));
+                    });
+
+            MciClosure (
+                string.Format ("status {0} length track {1} wait", alias, last_track),
+                "Could not read the length of the last track",
+                delegate (string result) {
+                    track_offsets [0] =
+                        int.Parse (result.Substring (0, 2)) * 4500 +
+                        int.Parse (result.Substring (3, 2)) * 75 +
+                        int.Parse (result.Substring (6, 2)) +
+                        track_offsets [last_track] + 1;
+                });
+
+            MciClosure (
+                string.Format ("close {0} wait", alias),
+                string.Format ("Could not close device {0}", device),
+                null);
+            
+            Init ();
+        }
+
+        static StringBuilder mci_result = new StringBuilder (128);
+        static StringBuilder mci_error = new StringBuilder (256);
+        static void MciClosure (string command, string failure_message, MciCall code)
+        {
+            int ret = mciSendString (command, mci_result, mci_result.Capacity, IntPtr.Zero);
+            if (ret != 0) {
+                mciGetErrorString (ret, mci_error, mci_error.Capacity);
+                throw new Exception (string.Format ("{0} : {1}", failure_message, mci_error.ToString ()));
+            } else if (code != null) code (mci_result.ToString ());
+        }
+    }
+}
Index: src/Libraries/MusicBrainz/MusicBrainz/Artist.cs
===================================================================
--- src/Libraries/MusicBrainz/MusicBrainz/Artist.cs	(revision 4938)
+++ src/Libraries/MusicBrainz/MusicBrainz/Artist.cs	(working copy)
@@ -1,5 +1,3 @@
-#region License
-
 // Artist.cs
 //
 // Copyright (c) 2008 Scott Peterson <lunchtimemama@gmail.com>
@@ -22,8 +20,6 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-#endregion
-
 using System;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
@@ -47,16 +43,16 @@
         
         #region Constructors
 
-        Artist (string mbid) : base (mbid, null)
+        Artist (string id) : base (id, null)
         {
         }
 
-        Artist (string mbid, string parameters) : base (mbid, parameters)
+        Artist (string id, string parameters) : base (id, parameters)
         {
         }
 
-        Artist (string mbid, ArtistReleaseType artist_release_type)
-            : this (mbid, "&inc=" + artist_release_type.ToString ())
+        Artist (string id, ArtistReleaseType artist_release_type)
+            : this (id, "&inc=" + artist_release_type.ToString ())
         {
             have_all_releases = true;
             this.artist_release_type = artist_release_type;
@@ -70,24 +66,24 @@
         
         #region Protected
         
-        protected override string UrlExtension {
+        internal override string UrlExtension {
             get { return EXTENSION; }
         }
 
-        protected override void CreateIncCore (StringBuilder builder)
+        internal override void CreateIncCore (StringBuilder builder)
         {
             AppendIncParameters (builder, artist_release_type.ToString ());
             base.CreateIncCore (builder);
         }
 
-        protected override void LoadMissingDataCore ()
+        internal override void LoadMissingDataCore ()
         {
             Artist artist = new Artist (Id, CreateInc ());
-            type = artist.Type;
+            type = artist.GetArtistType ();
             base.LoadMissingDataCore (artist);
         }
 
-        protected override bool ProcessAttributes (XmlReader reader)
+        internal override bool ProcessAttributes (XmlReader reader)
         {
             switch (reader ["type"]) {
             case "Group":
@@ -100,7 +96,7 @@
             return type != ArtistType.Unknown;
         }
 
-        protected override bool ProcessXml (XmlReader reader)
+        internal override bool ProcessXml (XmlReader reader)
         {
             reader.Read ();
             bool result = base.ProcessXml (reader);
@@ -127,14 +123,30 @@
         
         #endregion
 
-        #region Properties
+        #region Public
         
-        public static ArtistReleaseType DefaultArtistReleaseType =
-            new ArtistReleaseType (ReleaseStatus.Official, false);
+        static readonly object default_artist_release_type_mutex = new object ();
+        static ArtistReleaseType default_artist_release_type = new ArtistReleaseType (ReleaseStatus.Official, ReleaseArtistType.SingleArtist);
+        public static ArtistReleaseType DefaultArtistReleaseType {
+            get {
+                lock (default_artist_release_type_mutex) {
+                    return default_artist_release_type;
+                }
+            }
+            set {
+                if (value == null) throw new ArgumentNullException ("value");
+                lock (default_artist_release_type_mutex) {
+                    default_artist_release_type = value;
+                }
+            }
+        }
         
         public ArtistReleaseType ArtistReleaseType {
             get { return artist_release_type; }
             set {
+                if (artist_release_type == value) {
+                    return;
+                }
                 artist_release_type = value;
                 releases = null;
                 have_all_releases = false;
@@ -147,31 +159,37 @@
         }
 
         [Queryable ("artist")]
-        public override string Name {
-            get { return base.Name; }
+        public override string GetName ()
+        {
+            return base.GetName ();
         }
 
         [Queryable ("artype")]
-        public ArtistType Type {
-            get { return GetPropertyOrDefault (ref type, ArtistType.Unknown); }
+        public ArtistType GetArtistType ()
+        {
+            return GetPropertyOrDefault (ref type, ArtistType.Unknown);
         }
+        
+        public ReadOnlyCollection<Release> GetReleases ()
+        {
+            return releases ?? (have_all_releases
+                ? releases = new ReadOnlyCollection<Release> (new Release [0])
+                : new Artist (Id, ArtistReleaseType).GetReleases ());
+        }
 
-        public ReadOnlyCollection<Release> Releases {
-            get {
-                return releases ?? (have_all_releases
-                    ? releases = new ReadOnlyCollection<Release> (new Release [0])
-                    : new Artist (Id, artist_release_type).Releases);
-            }
+        public ReadOnlyCollection<Release> GetReleases (ArtistReleaseType artistReleaseType)
+        {
+            return new Artist (Id, artistReleaseType).GetReleases ();
         }
 
         #endregion
         
         #region Static
 
-        public static Artist Get (string mbid)
+        public static Artist Get (string id)
         {
-            if (mbid == null) throw new ArgumentNullException ("mbid");
-            return new Artist (mbid);
+            if (id == null) throw new ArgumentNullException ("id");
+            return new Artist (id);
         }
 
         public static Query<Artist> Query (string name)
@@ -204,37 +222,43 @@
         Person
     }
     
+    public enum ReleaseArtistType
+    {
+        VariousArtists,
+        SingleArtist
+    }
+    
     public sealed class ArtistReleaseType
     {
         string str;
 
-        public ArtistReleaseType (ReleaseType type, bool various) : this ((Enum)type, various)
+        public ArtistReleaseType (ReleaseType type, ReleaseArtistType artistType) : this ((Enum)type, artistType)
         {
         }
 
-        public ArtistReleaseType (ReleaseStatus status, bool various) : this ((Enum)status, various)
+        public ArtistReleaseType (ReleaseStatus status, ReleaseArtistType artistType) : this ((Enum)status, artistType)
         {
         }
         
-        public ArtistReleaseType (ReleaseType type, ReleaseStatus status, bool various)
+        public ArtistReleaseType (ReleaseType type, ReleaseStatus status, ReleaseArtistType artistType)
         {
             StringBuilder builder = new StringBuilder ();
-            Format (builder, type, various);
+            Format (builder, type, artistType);
             builder.Append ('+');
-            Format (builder, status, various);
+            Format (builder, status, artistType);
             str = builder.ToString ();
         }
 
-        ArtistReleaseType (Enum enumeration, bool various)
+        ArtistReleaseType (Enum enumeration, ReleaseArtistType artistType)
         {
             StringBuilder builder = new StringBuilder ();
-            Format (builder, enumeration, various);
+            Format (builder, enumeration, artistType);
             str = builder.ToString ();
         }
         
-        void Format (StringBuilder builder, Enum enumeration, bool various)
+        static void Format (StringBuilder builder, Enum enumeration, ReleaseArtistType artistType)
         {
-            builder.Append (various ? "va-" : "sa-");
+            builder.Append (artistType == ReleaseArtistType.VariousArtists ? "va-" : "sa-");
             Utils.EnumToString (builder, enumeration.ToString ());
         }
 
@@ -243,6 +267,26 @@
             return str;
         }
 
+        public override bool Equals (object o)
+        {
+            return this == o as ArtistReleaseType;
+        }
+        
+        public static bool operator ==(ArtistReleaseType artistReleaseType1, ArtistReleaseType artistReleaseType2)
+        {
+            return (Object.ReferenceEquals (artistReleaseType1, null) && Object.ReferenceEquals (artistReleaseType2, null)) ||
+                (!Object.ReferenceEquals (artistReleaseType1, null) && !Object.ReferenceEquals (artistReleaseType2, null) && artistReleaseType1.str == artistReleaseType2.str);
+        }
+        
+        public static bool operator !=(ArtistReleaseType artistReleaseType1, ArtistReleaseType artistReleaseType2)
+        {
+            return !(artistReleaseType1 == artistReleaseType2);
+        }
+        
+        public override int GetHashCode ()
+        {
+            return str.GetHashCode ();
+        }
     }
     
     #endregion
Index: src/Libraries/MusicBrainz/MusicBrainz/MusicBrainzService.cs
===================================================================
--- src/Libraries/MusicBrainz/MusicBrainz/MusicBrainzService.cs	(revision 4938)
+++ src/Libraries/MusicBrainz/MusicBrainz/MusicBrainzService.cs	(working copy)
@@ -1,5 +1,3 @@
-#region License
-
 // MusicBrainzService.cs
 //
 // Copyright (c) 2008 Scott Peterson <lunchtimemama@gmail.com>
@@ -22,8 +20,6 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-#endregion
-
 using System;
 using System.Net.Cache;
 
@@ -31,14 +27,43 @@
 {
     public static class MusicBrainzService
     {
-        public static string ServiceUrl = @"http://musicbrainz.org/ws/1/";
-        public static RequestCachePolicy CachePolicy;
+        static readonly object service_url_mutex = new object ();
+        static Uri service_url = new Uri(@"http://musicbrainz.org/ws/1/");
+        public static Uri ServiceUrl {
+            get {
+                lock (service_url_mutex) {
+                    return service_url;
+                }
+            }
+            set {
+                if (value == null) throw new ArgumentNullException ("value");
+                lock (service_url_mutex) {
+                    service_url = value;
+                }
+            }
+        }
+        
+        static readonly object cache_policy_mutex = new object ();
+        static RequestCachePolicy cache_policy;
+        public static RequestCachePolicy CachePolicy {
+            get {
+                lock (cache_policy_mutex) {
+                    return cache_policy;
+                }
+            }
+            set {
+                lock (cache_policy_mutex) {
+                    cache_policy = value;
+                }
+            }
+        }
+        
         public static event EventHandler<XmlRequestEventArgs> XmlRequest;
         
         internal static void OnXmlRequest (string url, bool fromCache)
         {
             EventHandler<XmlRequestEventArgs> handler = XmlRequest;
-            if (handler != null) handler (null, new XmlRequestEventArgs (url, fromCache));
+            if (handler != null) handler (null, new XmlRequestEventArgs (new Uri (url), fromCache));
         }
     }
 }
Index: src/Libraries/MusicBrainz/Makefile.am
===================================================================
--- src/Libraries/MusicBrainz/Makefile.am	(revision 4938)
+++ src/Libraries/MusicBrainz/Makefile.am	(working copy)
@@ -4,9 +4,10 @@
 SOURCES =  \
 	MusicBrainz/Artist.cs \
 	MusicBrainz/Disc.cs \
+	MusicBrainz/DiscLinux.cs \
+	MusicBrainz/DiscWin32.cs \
 	MusicBrainz/Event.cs \
 	MusicBrainz/Label.cs \
-	MusicBrainz/LinuxDisc.cs \
 	MusicBrainz/LocalDisc.cs \
 	MusicBrainz/MusicBrainzEntity.cs \
 	MusicBrainz/MusicBrainzException.cs \
@@ -18,7 +19,6 @@
 	MusicBrainz/Release.cs \
 	MusicBrainz/Track.cs \
 	MusicBrainz/Utils.cs \
-	MusicBrainz/Win32Disc.cs \
 	MusicBrainz/XmlRequestEventArgs.cs 
 
 include $(top_srcdir)/build/build.mk
