more work on ripper

freedb support
This commit is contained in:
chudov
2008-12-10 19:44:09 +00:00
parent 905414db84
commit 02b08c8d5b
22 changed files with 10031 additions and 50 deletions

View File

@@ -29,7 +29,7 @@ using System.Diagnostics;
namespace Bwg.Scsi
{
public class Command : IDisposable
class Command : IDisposable
{
[DllImport("ntdll.dll")]
internal static extern void RtlZeroMemory(IntPtr dest, int size);

View File

@@ -23,7 +23,7 @@
namespace Bwg.Scsi
{
public enum ScsiCommandCode
enum ScsiCommandCode
{
TestUnitReady = 0x00,
RequestSense = 0x03,

View File

@@ -939,7 +939,7 @@ namespace Bwg.Scsi
}
#endregion
public CommandStatus SendCommand(Command cmd)
private CommandStatus SendCommand(Command cmd)
{
return (m_ossize == 32) ? SendCommand32(cmd) : SendCommand64(cmd);
}
@@ -2131,29 +2131,51 @@ namespace Bwg.Scsi
return CommandStatus.Success;
}
/// <summary>
///
/// </summary>
public enum SubChannelMode
{
/// <summary></summary>
None,
QOnly, /// + 16 bytes
RWMode /// + 96 bytes
/// <summary>+ 16 bytes</summary>
QOnly,
/// <summary>+ 96 bytes</summary>
RWMode
};
/// <summary>
///
/// </summary>
public enum C2ErrorMode
{
/// <summary></summary>
None,
Mode294, /// +294 bytes
Mode296, /// +296 bytes
/// <summary> +294 bytes</summary>
Mode294,
/// <summary> +296 bytes</summary>
Mode296,
};
/// <summary>
///
/// </summary>
public enum MainChannelSelection
{
/// <summary>
///
/// </summary>
UserData,
/// <summary>
///
/// </summary>
F8h
};
/// <summary>
///
/// </summary>
/// <param name="mainmode">main channel mode</param>
/// <param name="submode">subchannel mode</param>
/// <param name="c2mode">C2 errors report mode</param>
/// <param name="exp">expected sector type</param>
@@ -2309,6 +2331,7 @@ namespace Bwg.Scsi
/// <param name="length"></param>
/// <param name="data"></param>
/// <param name="mode">the subchannel mode</param>
/// <param name="timeout">timeout (in seconds)</param>
/// <returns></returns>
public CommandStatus ReadSubChannel(byte mode, uint sector, uint length, ref byte[] data, int timeout)
{

View File

@@ -73,6 +73,7 @@
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.configuration" />
<Reference Include="System.Data" />
<Reference Include="System.Deployment" />
<Reference Include="System.Drawing" />
@@ -111,6 +112,7 @@
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<None Include="app.config" />
<None Include="Properties\DataSources\MusicBrainz.Release.datasource" />
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
@@ -143,6 +145,10 @@
<Project>{8CF07381-BEA2-4AFC-B3DD-9B2F21C65A3A}</Project>
<Name>CUETools.Ripper.SCSI</Name>
</ProjectReference>
<ProjectReference Include="..\Freedb\Freedb.csproj">
<Project>{5ADCFD6D-BFEA-4B10-BB45-9083BBB56AF4}</Project>
<Name>Freedb</Name>
</ProjectReference>
<ProjectReference Include="..\MusicBrainz\MusicBrainz.csproj">
<Project>{74C2036B-2C9B-4FC8-B7BD-AE81A8DCE533}</Project>
<Name>MusicBrainz</Name>

View File

@@ -8,23 +8,31 @@
// </auto-generated>
//------------------------------------------------------------------------------
namespace CUERipper.Properties
{
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "8.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
{
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default
{
get
{
return defaultInstance;
}
}
}
namespace CUERipper.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "8.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default {
get {
return defaultInstance;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("freedb.org")]
public string MAIN_FREEDB_SITEADDRESS {
get {
return ((string)(this["MAIN_FREEDB_SITEADDRESS"]));
}
set {
this["MAIN_FREEDB_SITEADDRESS"] = value;
}
}
}
}

View File

@@ -1,7 +1,9 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="CUERipper.Properties" GeneratedClassName="Settings">
<Profiles />
<Settings>
<Setting Name="MAIN_FREEDB_SITEADDRESS" Type="System.String" Scope="User">
<Value Profile="(Default)">freedb.org</Value>
</Setting>
</Settings>
</SettingsFile>

View File

@@ -8,12 +8,14 @@ using System.IO;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using System.Configuration;
using CUETools.AccurateRip;
using CUETools.CDImage;
using CUETools.Codecs;
using CUETools.Processor;
using CUETools.Ripper.SCSI;
using MusicBrainz;
using Freedb;
namespace CUERipper
{
@@ -228,6 +230,35 @@ namespace CUERipper
});
}
private CUESheet CreateCUESheet(CDDriveReader audioSource, Release release, CDEntry cdEntry)
{
CUESheet cueSheet = new CUESheet(_config);
cueSheet.OpenCD(audioSource);
General.SetCUELine(cueSheet.Attributes, "REM", "DISCID", AccurateRipVerify.CalculateCDDBId(audioSource.TOC), false);
General.SetCUELine(cueSheet.Attributes, "REM", "COMMENT", CDDriveReader.RipperVersion(), true);
if (release != null)
cueSheet.FillFromMusicBrainz(release);
else if (cdEntry != null)
{
cueSheet.Artist = cdEntry.Artist;
cueSheet.Title = cdEntry.Title;
General.SetCUELine(cueSheet.Attributes, "REM", "DATE", cdEntry.Year, false);
General.SetCUELine(cueSheet.Attributes, "REM", "GENRE", cdEntry.Genre, true);
for (int i = 0; i < audioSource.TOC.AudioTracks; i++)
cueSheet.Tracks[i].Title = cdEntry.Tracks[i].Title;
}
else
{
cueSheet.Artist = "Unknown Artist";
cueSheet.Title = "Unknown Title";
for (int i = 0; i < audioSource.TOC.AudioTracks; i++)
cueSheet.Tracks[i].Title = string.Format("Track {0:00}", i + 1);
}
cueSheet.AccurateRip = AccurateRipMode.VerifyAndConvert;
cueSheet.ArVerify.ContactAccurateRip(AccurateRipVerify.CalculateAccurateRipId(audioSource.TOC));
return cueSheet;
}
private void Lookup(object o)
{
CDDriveReader audioSource = (CDDriveReader)o;
@@ -242,11 +273,7 @@ namespace CUERipper
{
release.GetEvents();
release.GetTracks();
CUESheet cueSheet = new CUESheet(_config);
cueSheet.OpenCD(audioSource);
cueSheet.FillFromMusicBrainz(release);
cueSheet.AccurateRip = AccurateRipMode.VerifyAndConvert;
cueSheet.ArVerify.ContactAccurateRip(AccurateRipVerify.CalculateAccurateRipId(audioSource.TOC));
CUESheet cueSheet = CreateCUESheet(audioSource, release, null);
this.BeginInvoke((MethodInvoker)delegate()
{
comboRelease.Items.Add(cueSheet);
@@ -257,20 +284,63 @@ namespace CUERipper
{
}
MusicBrainzService.XmlRequest -= new EventHandler<XmlRequestEventArgs>(MusicBrainz_LookupProgress);
FreedbHelper m_freedb = new FreedbHelper();
m_freedb.UserName = "gchudov";
m_freedb.Hostname = "gmail.com";
m_freedb.ClientName = "CUERipper";
m_freedb.Version = "1.0";
m_freedb.SetDefaultSiteAddress(Properties.Settings.Default.MAIN_FREEDB_SITEADDRESS);
QueryResult queryResult;
QueryResultCollection coll;
string code = string.Empty;
try
{
code = m_freedb.Query(AccurateRipVerify.CalculateCDDBQuery(audioSource.TOC), out queryResult, out coll);
if (code == FreedbHelper.ResponseCodes.CODE_200)
{
CDEntry cdEntry;
code = m_freedb.Read(queryResult, out cdEntry);
if (code == FreedbHelper.ResponseCodes.CODE_210)
{
CUESheet cueSheet = CreateCUESheet(audioSource, null, cdEntry);
this.BeginInvoke((MethodInvoker)delegate()
{
comboRelease.Items.Add(cueSheet);
});
}
}
else
if (code == FreedbHelper.ResponseCodes.CODE_210 ||
code == FreedbHelper.ResponseCodes.CODE_211 )
{
foreach (QueryResult qr in coll)
{
CDEntry cdEntry;
code = m_freedb.Read(qr, out cdEntry);
if (code == FreedbHelper.ResponseCodes.CODE_210)
{
CUESheet cueSheet = CreateCUESheet(audioSource, null, cdEntry);
this.BeginInvoke((MethodInvoker)delegate()
{
comboRelease.Items.Add(cueSheet);
});
}
}
}
}
catch (Exception)
{
}
this.BeginInvoke((MethodInvoker)delegate()
{
if (comboRelease.Items.Count == 0)
{
CUESheet cueSheet = new CUESheet(_config);
cueSheet.OpenCD(audioSource);
General.SetCUELine(cueSheet.Attributes, "REM", "DISCID", AccurateRipVerify.CalculateCDDBId(audioSource.TOC), false);
General.SetCUELine(cueSheet.Attributes, "REM", "COMMENT", CDDriveReader.RipperVersion(), true);
cueSheet.Artist = "Unknown Artist";
cueSheet.Title = "Unknown Title";
cueSheet.AccurateRip = AccurateRipMode.VerifyAndConvert;
cueSheet.ArVerify.ContactAccurateRip(AccurateRipVerify.CalculateAccurateRipId(audioSource.TOC));
for (int i = 0; i < audioSource.TOC.AudioTracks; i++)
cueSheet.Tracks[i].Title = string.Format("Track {0:00}", i + 1);
CUESheet cueSheet = CreateCUESheet(audioSource, null, null);
comboRelease.Items.Add(cueSheet);
}
});
@@ -332,7 +402,8 @@ namespace CUERipper
private void listTracks_AfterLabelEdit(object sender, LabelEditEventArgs e)
{
CUESheet cueSheet = (CUESheet)comboRelease.SelectedItem;
cueSheet.Tracks[e.Item].Title = e.Label;
if (e.Label != null)
cueSheet.Tracks[e.Item].Title = e.Label;
}
private void editToolStripMenuItem_Click(object sender, EventArgs e)

View File

@@ -491,6 +491,16 @@ namespace CUETools.AccurateRip
return false;
}
public static string CalculateCDDBQuery(CDImageLayout toc)
{
StringBuilder query = new StringBuilder(CalculateCDDBId(toc));
query.AppendFormat("+{0}", toc.TrackCount);
for (int iTrack = 1; iTrack <= toc.TrackCount; iTrack++)
query.AppendFormat("+{0}", toc[iTrack].Start + 150);
query.AppendFormat("+{0}", toc.Length / 75 - toc[1].Start / 75);
return query.ToString();
}
public static string CalculateCDDBId(CDImageLayout toc)
{
uint cddbDiscId = 0;

View File

@@ -516,8 +516,6 @@ namespace CUETools.Processor
public void FillFromMusicBrainz(Release release)
{
General.SetCUELine(_attributes, "REM", "DISCID", AccurateRipVerify.CalculateCDDBId(_toc), false);
General.SetCUELine(_attributes, "REM", "COMMENT", CDDriveReader.RipperVersion(), true);
if (release.GetEvents().Count > 0)
General.SetCUELine(_attributes, "REM", "DATE", release.GetEvents()[0].Date.Substring(0, 4), false);
Artist = release.GetArtist();

View File

@@ -75,6 +75,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MusicBrainz", "..\MusicBrai
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CUERipper", "..\CUERipper\CUERipper.csproj", "{39B43BBB-BAFC-4D85-9BEA-3BCB7EFED89C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Freedb", "..\Freedb\Freedb.csproj", "{5ADCFD6D-BFEA-4B10-BB45-9083BBB56AF4}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -370,6 +372,17 @@ Global
{39B43BBB-BAFC-4D85-9BEA-3BCB7EFED89C}.Release|x64.Build.0 = Release|x64
{39B43BBB-BAFC-4D85-9BEA-3BCB7EFED89C}.Release|x86.ActiveCfg = Release|x86
{39B43BBB-BAFC-4D85-9BEA-3BCB7EFED89C}.Release|x86.Build.0 = Release|x86
{5ADCFD6D-BFEA-4B10-BB45-9083BBB56AF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5ADCFD6D-BFEA-4B10-BB45-9083BBB56AF4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5ADCFD6D-BFEA-4B10-BB45-9083BBB56AF4}.Debug|x64.ActiveCfg = Debug|x64
{5ADCFD6D-BFEA-4B10-BB45-9083BBB56AF4}.Debug|x64.Build.0 = Debug|x64
{5ADCFD6D-BFEA-4B10-BB45-9083BBB56AF4}.Debug|x86.ActiveCfg = Debug|Any CPU
{5ADCFD6D-BFEA-4B10-BB45-9083BBB56AF4}.Debug|x86.Build.0 = Debug|Any CPU
{5ADCFD6D-BFEA-4B10-BB45-9083BBB56AF4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5ADCFD6D-BFEA-4B10-BB45-9083BBB56AF4}.Release|Any CPU.Build.0 = Release|Any CPU
{5ADCFD6D-BFEA-4B10-BB45-9083BBB56AF4}.Release|x64.ActiveCfg = Release|x64
{5ADCFD6D-BFEA-4B10-BB45-9083BBB56AF4}.Release|x64.Build.0 = Release|x64
{5ADCFD6D-BFEA-4B10-BB45-9083BBB56AF4}.Release|x86.ActiveCfg = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

58
Freedb/AssemblyInfo.cs Normal file
View File

@@ -0,0 +1,58 @@
using System.Reflection;
using System.Runtime.CompilerServices;
//
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
//
[assembly: AssemblyTitle("")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
//
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Revision and Build Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("1.0.0.1")]
//
// In order to sign your assembly you must specify a key to use. Refer to the
// Microsoft .NET Framework documentation for more information on assembly signing.
//
// Use the attributes below to control which key is used for signing.
//
// Notes:
// (*) If no key is specified, the assembly is not signed.
// (*) KeyName refers to a key that has been installed in the Crypto Service
// Provider (CSP) on your machine. KeyFile refers to a file which contains
// a key.
// (*) If the KeyFile and the KeyName values are both specified, the
// following processing occurs:
// (1) If the KeyName can be found in the CSP, that key is used.
// (2) If the KeyName does not exist and the KeyFile does exist, the key
// in the KeyFile is installed into the CSP and used.
// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility.
// When specifying the KeyFile, the location of the KeyFile should be
// relative to the project output directory which is
// %Project Directory%\obj\<configuration>. For example, if your KeyFile is
// located in the project directory, you would specify the AssemblyKeyFile
// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")]
// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework
// documentation for more information on this.
//
[assembly: AssemblyDelaySign(false)]
[assembly: AssemblyKeyFile("")]
[assembly: AssemblyKeyName("")]

385
Freedb/CDEntry.cs Normal file
View File

@@ -0,0 +1,385 @@
#region COPYRIGHT (c) 2004 by Brian Weeres
/* Copyright (c) 2004 by Brian Weeres
*
* Email: bweeres@protegra.com; bweeres@hotmail.com
*
* Permission to use, copy, modify, and distribute this software for any
* purpose is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* If you modify it then please indicate so.
*
* The software is provided "AS IS" and there are no warranties or implied warranties.
* In no event shall Brian Weeres and/or Protegra Technology Group be liable for any special,
* direct, indirect, or consequential damages or any damages whatsoever resulting for any reason
* out of the use or performance of this software
*
*/
#endregion
using System;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Text;
namespace Freedb
{
/// <summary>
/// Summary description for CDEntry.
/// </summary>
public class CDEntry
{
#region Private Member Variables
private string m_Discid;
private string m_Artist;
private string m_Title;
private string m_Year;
private string m_Genre;
private TrackCollection m_Tracks = new TrackCollection(); // 0 based - first track is at 0 last track is at numtracks - 1
private string m_ExtendedData;
private string m_PlayOrder;
/// <summary>
/// Property NumberOfTracks (int)
/// </summary>
#endregion
#region Public Member Variables
/// <summary>
/// Property Discid (string)
/// </summary>
public string Discid
{
get
{
return this.m_Discid;
}
set
{
this.m_Discid = value;
}
}
/// <summary>
/// Property Artist (string)
/// </summary>
public string Artist
{
get
{
return this.m_Artist;
}
set
{
this.m_Artist = value;
}
}
/// <summary>
/// Property Title (string)
/// </summary>
public string Title
{
get
{
return this.m_Title;
}
set
{
this.m_Title = value;
}
}
/// <summary>
/// Property Year (string)
/// </summary>
public string Year
{
get
{
return this.m_Year;
}
set
{
this.m_Year = value;
}
}
/// <summary>
/// Property Genre (string)
/// </summary>
public string Genre
{
get
{
return this.m_Genre;
}
set
{
this.m_Genre = value;
}
}
/// <summary>
/// Property Tracks (StringCollection)
/// </summary>
public TrackCollection Tracks
{
get
{
return this.m_Tracks;
}
set
{
this.m_Tracks = value;
}
}
/// <summary>
/// Property ExtendedData (string)
/// </summary>
public string ExtendedData
{
get
{
return this.m_ExtendedData;
}
set
{
this.m_ExtendedData = value;
}
}
/// <summary>
/// Property PlayOrder (string)
/// </summary>
public string PlayOrder
{
get
{
return this.m_PlayOrder;
}
set
{
this.m_PlayOrder = value;
}
}
public int NumberOfTracks
{
get
{
return m_Tracks.Count;
}
}
#endregion
public CDEntry(StringCollection data)
{
if (!Parse(data))
{
throw new Exception("Unable to Parse CDEntry.");
}
}
private bool Parse(StringCollection data)
{
foreach (string line in data)
{
// check for comment
if (line[0] == '#')
continue;
int index = line.IndexOf('=');
if (index == -1) // couldn't find equal sign have no clue what the data is
continue;
string field = line.Substring(0,index);
index++; // move it past the equal sign
switch (field)
{
case "DISCID":
{
this.m_Discid = line.Substring(index);
continue;
}
case "DTITLE": // artist / title
{
this.m_Artist += line.Substring(index);
continue;
}
case "DYEAR":
{
this.m_Year = line.Substring(index);
continue;
}
case "DGENRE":
{
this.m_Genre += line.Substring(index);
continue;
}
case "EXTD":
{
// may be more than one - just concatenate them
this.m_ExtendedData += line.Substring(index);
continue;
}
case "PLAYORDER":
{
this.m_PlayOrder += line.Substring(index);
continue;
}
default:
//get track info or extended track info
if (field.StartsWith("TTITLE"))
{
int trackNumber = -1;
// Parse could throw an exception
try
{
trackNumber = int.Parse(field.Substring("TTITLE".Length));
}
catch (Exception ex)
{
Debug.WriteLine("Failed to parse track Number. Reason: " + ex.Message);
continue;
}
//may need to concatenate track info
if (trackNumber < m_Tracks.Count )
m_Tracks[trackNumber].Title += line.Substring(index);
else
{
Track track = new Track(line.Substring(index));
this.m_Tracks.Add(track);
}
continue;
}
else if (field.StartsWith("EXTT"))
{
int trackNumber = -1;
// Parse could throw an exception
try
{
trackNumber = int.Parse(field.Substring("EXTT".Length));
}
catch (Exception ex)
{
Debug.WriteLine("Failed to parse track Number. Reason: " + ex.Message);
continue;
}
if (trackNumber < 0 || trackNumber > m_Tracks.Count -1)
continue;
m_Tracks[trackNumber].ExtendedData += line.Substring(index);
}
continue;
} //end of switch
}
//split the title and artist from DTITLE;
// see if we have a slash
int slash = this.m_Artist.IndexOf(" / ");
if (slash == -1)
{
this.m_Title= m_Artist;
}
else
{
string titleArtist = m_Artist;
this.m_Artist = titleArtist.Substring(0,slash);
slash +=3; // move past " / "
this.m_Title = titleArtist.Substring(slash );
}
return true;
}
public override string ToString()
{
StringBuilder builder = new StringBuilder();
builder.Append("Title: ");
builder.Append(this.m_Title);
builder.Append("\n");
builder.Append("Artist: ");
builder.Append(this.m_Artist);
builder.Append("\n");
builder.Append("Discid: ");
builder.Append(this.m_Discid);
builder.Append("\n");
builder.Append("Genre: ");
builder.Append(this.m_Genre);
builder.Append("\n");
builder.Append("Year: ");
builder.Append(this.m_Year);
builder.Append("\n");
builder.Append("Tracks:");
foreach (Track track in this.m_Tracks)
{
builder.Append("\n");
builder.Append(track.Title);
}
return builder.ToString();
}
}
}

154
Freedb/Freedb.csproj Normal file
View File

@@ -0,0 +1,154 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectType>Local</ProjectType>
<ProductVersion>8.0.50727</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{5ADCFD6D-BFEA-4B10-BB45-9083BBB56AF4}</ProjectGuid>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ApplicationIcon>
</ApplicationIcon>
<AssemblyKeyContainerName>
</AssemblyKeyContainerName>
<AssemblyName>Freedb</AssemblyName>
<AssemblyOriginatorKeyFile>
</AssemblyOriginatorKeyFile>
<DefaultClientScript>JScript</DefaultClientScript>
<DefaultHTMLPageLayout>Grid</DefaultHTMLPageLayout>
<DefaultTargetSchema>IE50</DefaultTargetSchema>
<DelaySign>false</DelaySign>
<OutputType>Library</OutputType>
<RootNamespace>Freedb</RootNamespace>
<RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
<StartupObject>
</StartupObject>
<FileUpgradeFlags>
</FileUpgradeFlags>
<UpgradeBackupLocation>
</UpgradeBackupLocation>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<OutputPath>..\bin\win32\Debug\</OutputPath>
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
<BaseAddress>285212672</BaseAddress>
<CheckForOverflowUnderflow>false</CheckForOverflowUnderflow>
<ConfigurationOverrideFile>
</ConfigurationOverrideFile>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>
</DocumentationFile>
<DebugSymbols>true</DebugSymbols>
<FileAlignment>4096</FileAlignment>
<NoStdLib>false</NoStdLib>
<NoWarn>
</NoWarn>
<Optimize>false</Optimize>
<RegisterForComInterop>false</RegisterForComInterop>
<RemoveIntegerChecks>false</RemoveIntegerChecks>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<WarningLevel>4</WarningLevel>
<DebugType>full</DebugType>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<OutputPath>..\bin\win32\Release\</OutputPath>
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
<BaseAddress>285212672</BaseAddress>
<CheckForOverflowUnderflow>false</CheckForOverflowUnderflow>
<ConfigurationOverrideFile>
</ConfigurationOverrideFile>
<DefineConstants>TRACE</DefineConstants>
<DocumentationFile>
</DocumentationFile>
<DebugSymbols>false</DebugSymbols>
<FileAlignment>4096</FileAlignment>
<NoStdLib>false</NoStdLib>
<NoWarn>
</NoWarn>
<Optimize>true</Optimize>
<RegisterForComInterop>false</RegisterForComInterop>
<RemoveIntegerChecks>false</RemoveIntegerChecks>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<WarningLevel>4</WarningLevel>
<DebugType>none</DebugType>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x64' ">
<DebugSymbols>true</DebugSymbols>
<OutputPath>..\bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<BaseAddress>285212672</BaseAddress>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<CodeAnalysisRuleAssemblies>C:\Program Files (x86)\Microsoft Visual Studio 8\Team Tools\Static Analysis Tools\FxCop\\rules</CodeAnalysisRuleAssemblies>
<CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression>
<CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' ">
<OutputPath>..\bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<BaseAddress>285212672</BaseAddress>
<Optimize>true</Optimize>
<DebugType>
</DebugType>
<PlatformTarget>x64</PlatformTarget>
<CodeAnalysisRuleAssemblies>C:\Program Files (x86)\Microsoft Visual Studio 8\Team Tools\Static Analysis Tools\FxCop\\rules</CodeAnalysisRuleAssemblies>
<CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression>
<CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<ItemGroup>
<Reference Include="System">
<Name>System</Name>
</Reference>
<Reference Include="System.Data">
<Name>System.Data</Name>
</Reference>
<Reference Include="System.Drawing">
<Name>System.Drawing</Name>
</Reference>
<Reference Include="System.Windows.Forms">
<Name>System.Windows.Forms</Name>
</Reference>
<Reference Include="System.Xml">
<Name>System.XML</Name>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="CDEntry.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="FreedbHelper.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="QueryResult.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="QueryResultCollection.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Site.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="SiteCollection.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Track.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="TrackCollection.cs">
<SubType>Code</SubType>
</Compile>
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PreBuildEvent>
</PreBuildEvent>
<PostBuildEvent>
</PostBuildEvent>
</PropertyGroup>
</Project>

685
Freedb/FreedbHelper.cs Normal file
View File

@@ -0,0 +1,685 @@
#region COPYRIGHT (c) 2004 by Brian Weeres
/* Copyright (c) 2004 by Brian Weeres
*
* Email: bweeres@protegra.com; bweeres@hotmail.com
*
* Permission to use, copy, modify, and distribute this software for any
* purpose is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* If you modify it then please indicate so.
*
* The software is provided "AS IS" and there are no warranties or implied warranties.
* In no event shall Brian Weeres and/or Protegra Technology Group be liable for any special,
* direct, indirect, or consequential damages or any damages whatsoever resulting for any reason
* out of the use or performance of this software
*
*/
#endregion
using System;
using System.Text;
using System.Net;
using System.IO;
using System.Collections.Specialized;
using System.Diagnostics;
namespace Freedb
{
/// <summary>
/// Summary description for FreedbHelper.
/// </summary>
public class FreedbHelper
{
public const string MAIN_FREEDB_ADDRESS = "freedb.freedb.org";
public const string DEFAULT_ADDITIONAL_URL_INFO = "/~cddb/cddb.cgi";
private Site m_mainSite = new Site(MAIN_FREEDB_ADDRESS,"http",DEFAULT_ADDITIONAL_URL_INFO);
private string m_UserName;
private string m_Hostname;
private string m_ClientName;
private string m_Version;
private string m_ProtocolLevel = "6"; // default to level 6 support
private Site m_CurrentSite = null;
#region Constants for Freedb commands
public class Commands
{
public const string CMD_HELLO = "hello";
public const string CMD_READ = "cddb+read";
public const string CMD_QUERY = "cddb+query";
public const string CMD_SITES = "sites";
public const string CMD_PROTO = "proto";
public const string CMD_CATEGORIES = "cddb+lscat";
public const string CMD = "cmd="; // will never use without the equals so put it here
public const string CMD_TERMINATOR = ".";
}
#endregion
#region Constants for Freedb ResponseCodes
public class ResponseCodes
{
public const string CODE_210 = "210"; // Okay // or in a query multiple exact matches
public const string CODE_401 = "401"; // sites: no site information available
public const string CODE_402 = "402"; // Server Error
public const string CODE_500 = "500"; // Invalid command, invalid parameters, etc.
//query codes
public const string CODE_200 = "200"; // Exact match
public const string CODE_211 = "211"; // InExact matches found - list follows
public const string CODE_202 = "202"; // No match
public const string CODE_403 = "403"; // Database entry is corrupt
public const string CODE_409 = "409"; // No Handshake
// our own code
public const string CODE_INVALID = "-1"; // Invalid code
}
#endregion
#region Public Properties
/// <summary>
/// Property Version (string)
/// </summary>
public string Version
{
get
{
return this.m_Version;
}
set
{
this.m_Version = value;
}
}
/// <summary>
/// Property MainSite(string)
/// </summary>
public Site MainSite
{
get
{
return this.m_mainSite ;
}
}
/// <summary>
/// Property ClientName (string)
/// </summary>
public string ClientName
{
get
{
return this.m_ClientName;
}
set
{
this.m_ClientName = value;
}
}
/// <summary>
/// Property Hostname (string)
/// </summary>
public string Hostname
{
get
{
return this.m_Hostname;
}
set
{
this.m_Hostname = value;
}
}
/// <summary>
/// Property UserName (string)
/// </summary>
public string UserName
{
get
{
return this.m_UserName;
}
set
{
this.m_UserName = value;
}
}
/// <summary>
/// Property ProtocolLevel (string)
/// </summary>
public string ProtocolLevel
{
get
{
return this.m_ProtocolLevel;
}
set
{
this.m_ProtocolLevel = value;
}
}
/// <summary>
/// Property CurrentSite (Site)
/// </summary>
public Site CurrentSite
{
get
{
return this.m_CurrentSite;
}
set
{
this.m_CurrentSite = value;
}
}
public FreedbHelper()
{
m_ProtocolLevel = "6"; // default it
}
/// <summary>
/// Retrieve all Freedb servers from the main server site
/// </summary>
/// <param name="sites">SiteCollection that is populated with the site information</param>
/// <returns>Response Code</returns>
public string GetSites(out SiteCollection sites)
{
return GetSites(Site.PROTOCOLS.ALL, out sites);
}
#endregion
/// <summary>
/// Get the Freedb sites
/// </summary>
/// <param name="protocol"></param>
/// <param name="sites">SiteCollection that is populated with the site information</param>
/// <returns>Response Code</returns>
///
public string GetSites(string protocol, out SiteCollection sites)
{
if (protocol != Site.PROTOCOLS.CDDBP && protocol != Site.PROTOCOLS.HTTP)
protocol = Site.PROTOCOLS.ALL;
StringCollection coll;
try
{
coll= Call(Commands.CMD_SITES,m_mainSite.GetUrl());
}
catch (Exception ex)
{
Debug.WriteLine("Error retrieving Sites." + ex.Message);
Exception newEx = new Exception("FreedbHelper.GetSites: Error retrieving Sites.",ex);
throw newEx;
}
sites = null;
// check if results came back
if (coll.Count < 0)
{
string msg = "No results returned from sites request.";
Exception ex = new Exception(msg,null);
throw ex;
}
string code = GetCode(coll[0]);
if (code == ResponseCodes.CODE_INVALID)
{
string msg = "Unable to process results Sites Request. Returned Data: " + coll[0];
Exception ex = new Exception(msg,null);
throw ex;
}
switch (code)
{
case ResponseCodes.CODE_500:
return ResponseCodes.CODE_500;
case ResponseCodes.CODE_401:
return ResponseCodes.CODE_401;
case ResponseCodes.CODE_210:
{
coll.RemoveAt(0);
sites = new SiteCollection();
foreach (String line in coll)
{
Debug.WriteLine("line: " + line);
Site site = new Site(line);
if (protocol == Site.PROTOCOLS.ALL)
sites.Add(new Site(line));
else if (site.Protocol == protocol)
sites.Add(new Site(line));
}
return ResponseCodes.CODE_210;
}
default:
return ResponseCodes.CODE_500;
}
}
/// <summary>
/// Read Entry from the database.
/// </summary>
/// <param name="qr">A QueryResult object that is created by performing a query</param>
/// <param name="cdEntry">out parameter - CDEntry object</param>
/// <returns></returns>
public string Read(QueryResult qr, out CDEntry cdEntry)
{
Debug.Assert(qr != null);
cdEntry = null;
StringCollection coll = null;
StringBuilder builder = new StringBuilder(FreedbHelper.Commands.CMD_READ);
builder.Append("+");
builder.Append(qr.Category);
builder.Append("+");
builder.Append(qr.Discid);
//make call
try
{
coll = Call(builder.ToString());
}
catch (Exception ex)
{
string msg = "Error performing cddb read.";
Exception newex = new Exception(msg,ex);
throw newex ;
}
// check if results came back
if (coll.Count < 0)
{
string msg = "No results returned from cddb read.";
Exception ex = new Exception(msg,null);
throw ex;
}
string code = GetCode(coll[0]);
if (code == ResponseCodes.CODE_INVALID)
{
string msg = "Unable to process results for cddb read. Returned Data: " + coll[0];
Exception ex = new Exception(msg,null);
throw ex;
}
switch (code)
{
case ResponseCodes.CODE_500:
return ResponseCodes.CODE_500;
case ResponseCodes.CODE_401: // entry not found
case ResponseCodes.CODE_402: // server error
case ResponseCodes.CODE_403: // Database entry is corrupt
case ResponseCodes.CODE_409: // No handshake
return code;
case ResponseCodes.CODE_210: // good
{
coll.RemoveAt(0); // remove the 210
cdEntry = new CDEntry(coll);
return ResponseCodes.CODE_210;
}
default:
return ResponseCodes.CODE_500;
}
}
/// <summary>
/// Query the freedb server to see if there is information on this cd
/// </summary>
/// <param name="querystring"></param>
/// <param name="queryResult"></param>
/// <param name="queryResultsColl"></param>
/// <returns></returns>
public string Query(string querystring, out QueryResult queryResult, out QueryResultCollection queryResultsColl)
{
queryResult = null;
queryResultsColl = null;
StringCollection coll = null;
StringBuilder builder = new StringBuilder(FreedbHelper.Commands.CMD_QUERY);
builder.Append("+");
builder.Append(querystring);
//make call
try
{
coll = Call(builder.ToString());
}
catch (Exception ex)
{
string msg = "Unable to perform cddb query.";
Exception newex = new Exception(msg,ex);
throw newex ;
}
// check if results came back
if (coll.Count < 0)
{
string msg = "No results returned from cddb query.";
Exception ex = new Exception(msg,null);
throw ex;
}
string code = GetCode(coll[0]);
if (code == ResponseCodes.CODE_INVALID)
{
string msg = "Unable to process results returned for query: Data returned: " + coll[0];
Exception ex = new Exception (msg,null);
throw ex;
}
switch (code)
{
case ResponseCodes.CODE_500:
return ResponseCodes.CODE_500;
// Multiple results were returned
// Put them into a queryResultCollection object
case ResponseCodes.CODE_211:
case ResponseCodes.CODE_210:
{
queryResultsColl = new QueryResultCollection();
//remove the 210 or 211
coll.RemoveAt(0);
foreach (string line in coll)
{
QueryResult result = new QueryResult(line,true);
queryResultsColl.Add(result);
}
return ResponseCodes.CODE_211;
}
// exact match
case ResponseCodes.CODE_200:
{
queryResult = new QueryResult(coll[0]);
return ResponseCodes.CODE_200;
}
//not found
case ResponseCodes.CODE_202:
return ResponseCodes.CODE_202;
//Database entry is corrupt
case ResponseCodes.CODE_403:
return ResponseCodes.CODE_403;
//no handshake
case ResponseCodes.CODE_409:
return ResponseCodes.CODE_409;
default:
return ResponseCodes.CODE_500;
} // end of switch
}
/// <summary>
/// Retrieve the categories
/// </summary>
/// <param name="strings"></param>
/// <returns></returns>
public string GetCategories(out StringCollection strings)
{
StringCollection coll;
strings = null;
try
{
coll = Call(FreedbHelper.Commands.CMD_CATEGORIES);
}
catch (Exception ex)
{
string msg = "Unable to retrieve Categories.";
Exception newex = new Exception(msg,ex);
throw newex;
}
// check if results came back
if (coll.Count < 0)
{
string msg = "No results returned from categories request.";
Exception ex = new Exception(msg,null);
throw ex;
}
string code = GetCode(coll[0]);
if (code == ResponseCodes.CODE_INVALID)
{
string msg = "Unable to retrieve Categories. Data Returned: " + coll[0];
Exception ex = new Exception(msg,null);
throw ex;
}
switch (code)
{
case ResponseCodes.CODE_500:
return ResponseCodes.CODE_500;
case ResponseCodes.CODE_210:
{
strings = coll;
coll.RemoveAt(0);
return ResponseCodes.CODE_210;
}
default:
{
string msg = "Unknown code returned from GetCategories: " + coll[0];
Exception ex = new Exception(msg,null);
throw ex;
}
}
}
/// <summary>
/// Call the Freedb server using the specified command and the current site
/// If the current site is null use the default server
/// </summary>
/// <param name="command">The command to be exectued</param>
/// <returns>StringCollection</returns>
private StringCollection Call(string command)
{
if (m_CurrentSite != null)
return Call(command,m_CurrentSite.GetUrl());
else
return Call(command,m_mainSite.GetUrl());
}
/// <summary>
/// Call the Freedb server using the specified command and the specified url
/// The command should not include the cmd= and hello and proto parameters.
/// They will be added automatically
/// </summary>
/// <param name="command">The command to be exectued</param>
/// <param name="url">The Freedb server to use</param>
/// <returns>StringCollection</returns>
private StringCollection Call(string commandIn, string url)
{
StreamReader reader = null;
HttpWebResponse response = null;
StringCollection coll = new StringCollection();
try
{
//create our HttpWebRequest which we use to call the freedb server
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
req.ContentType = "text/plain";
// we are using th POST method of calling the http server. We could have also used the GET method
req.Method="POST";
//add the hello and proto commands to the request
string command = BuildCommand(Commands.CMD + commandIn);
//using Unicode
byte[] byteArray = Encoding.UTF8.GetBytes(command);
//get our request stream
Stream newStream= req.GetRequestStream();
//write our command data to it
newStream.Write(byteArray,0,byteArray.Length);
newStream.Close();
//Make the call. Note this is a synchronous call
response = (HttpWebResponse) req.GetResponse();
//put the results into a StreamReader
reader = new StreamReader(response.GetResponseStream(),System.Text.Encoding.UTF8);
// add each line to the StringCollection until we get the terminator
string line;
while ((line = reader.ReadLine()) != null)
{
if (line.StartsWith(Commands.CMD_TERMINATOR))
break;
else
coll.Add(line);
}
}
catch (Exception ex)
{
throw ex;
}
finally
{
if (response != null)
response.Close();
if (reader != null)
reader.Close();
}
return coll;
}
/// <summary>
/// Given a specific command add on the hello and proto which are requied for an http call
/// </summary>
/// <param name="command"></param>
/// <returns></returns>
private string BuildCommand(string command)
{
StringBuilder builder = new StringBuilder(command);
builder.Append("&");
builder.Append(Hello());
builder.Append("&");
builder.Append(Proto());
return builder.ToString();
}
/// <summary>
/// Build the hello part of the command
/// </summary>
/// <returns></returns>
public string Hello()
{
StringBuilder builder = new StringBuilder(Commands.CMD_HELLO);
builder.Append("=");
builder.Append(m_UserName);
builder.Append("+");
builder.Append(this.m_Hostname);
builder.Append("+");
builder.Append(this.ClientName);
builder.Append("+");
builder.Append(this.m_Version);
return builder.ToString();
}
/// <summary>
/// Build the Proto part of the command
/// </summary>
/// <returns></returns>
public string Proto()
{
StringBuilder builder = new StringBuilder(Commands.CMD_PROTO);
builder.Append("=");
builder.Append(m_ProtocolLevel );
return builder.ToString();
}
/// <summary>
/// given the first line of a result set return the CDDB code
/// </summary>
/// <param name="firstLine"></param>
/// <returns></returns>
private string GetCode(string firstLine)
{
firstLine = firstLine.Trim();
//find first white space after start
int index = firstLine.IndexOf(' ');
if (index != -1)
firstLine = firstLine.Substring(0,index);
else
{
return ResponseCodes.CODE_INVALID;
}
return firstLine;
}
/// <summary>
/// If a different default site address is preferred over "freedb.freedb.org"
/// set it here
/// NOTE: Only set the ip address
/// </summary>
/// <param name="ipAddress"></param>
public void SetDefaultSiteAddress(string siteAddress)
{
//sanity check on the url
if (siteAddress.IndexOf("http") != -1 ||
siteAddress.IndexOf("cgi") != -1)
throw new Exception("Invalid Site Address specified");
this.m_mainSite.SiteAddress = siteAddress;
}
}
}

213
Freedb/QueryResult.cs Normal file
View File

@@ -0,0 +1,213 @@
#region COPYRIGHT (c) 2004 by Brian Weeres
/* Copyright (c) 2004 by Brian Weeres
*
* Email: bweeres@protegra.com; bweeres@hotmail.com
*
* Permission to use, copy, modify, and distribute this software for any
* purpose is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* If you modify it then please indicate so.
*
* The software is provided "AS IS" and there are no warranties or implied warranties.
* In no event shall Brian Weeres and/or Protegra Technology Group be liable for any special,
* direct, indirect, or consequential damages or any damages whatsoever resulting for any reason
* out of the use or performance of this software
*
*/
#endregion
using System;
namespace Freedb
{
/// <summary>
/// Summary description for QueryResult.
/// </summary>
public class QueryResult
{
private string m_ResponseCode;
private string m_Category;
private string m_Discid;
private string m_Artist;
private string m_Title;
#region Public Properties
/// <summary>
/// Property ResponseCode (string)
/// </summary>
public string ResponseCode
{
get
{
return this.m_ResponseCode;
}
set
{
this.m_ResponseCode = value;
}
}
/// <summary>
/// Property Category (string)
/// </summary>
public string Category
{
get
{
return this.m_Category;
}
set
{
this.m_Category = value;
}
}
/// <summary>
/// Property Discid (string)
/// </summary>
public string Discid
{
get
{
return this.m_Discid;
}
set
{
this.m_Discid = value;
}
}
/// <summary>
/// Property Artist (string)
/// </summary>
public string Artist
{
get
{
return this.m_Artist;
}
set
{
this.m_Artist = value;
}
}
/// <summary>
/// Property Title (string)
/// </summary>
public string Title
{
get
{
return this.m_Title;
}
set
{
this.m_Title = value;
}
}
#endregion
public QueryResult(string queryResult)
{
if (!Parse(queryResult,false))
{
throw new Exception("Unable to Parse QueryResult. Input: " + queryResult);
}
}
/// <summary>
/// The parsing for a queryresult returned as part of a number of matches is slightly different
/// There is no response code
/// </summary>
/// <param name="queryResult"></param>
/// <param name="multiMatchInput"> true if the result is part of multi-match which means it will not contain a response code</param>
public QueryResult(string queryResult, bool multiMatchInput)
{
if (!Parse(queryResult,multiMatchInput))
{
throw new Exception("Unable to Parse QueryResult. Input: " + queryResult);
}
}
/// <summary>
/// Parse the query result line from the cddb server
/// </summary>
/// <param name="queryResult"></param>
public bool Parse(string queryResult,bool match)
{
queryResult.Trim();
int secondIndex =0;
// get first white space
int index = queryResult.IndexOf(' ');
//if we are parsing a matched queryresult there is no responsecode so skip it
if (!match)
{
m_ResponseCode = queryResult.Substring(0,index);
index++;
secondIndex = queryResult.IndexOf(' ',index);
}
else
{
secondIndex = index;
index=0;
}
m_Category = queryResult.Substring(index,secondIndex-index);
index = secondIndex;
index++;
secondIndex = queryResult.IndexOf(' ',index);
m_Discid = queryResult.Substring(index,secondIndex-index);
index = secondIndex;
index++;
secondIndex = queryResult.IndexOf('/',index);
m_Artist = queryResult.Substring(index,secondIndex-index-1); // -1 because there is a space at the end of artist
index = secondIndex;
index+=2; //skip past / and space
m_Title = queryResult.Substring(index);
return true;
}
// public bool Parse(string queryResult)
// {
// queryResult.Trim();
// string [] values = queryResult.Split(' ');
// if (values.Length <6)
// return false;
// this.m_ResponseCode = values[0];
// m_Category = values[1];
// m_Discid = values[2];
//
// // now we need to look for a slash
// bool artist = true;
// for (int i = 3; i < values.Length;i++)
// {
// if (values[i] == "/")
// {
// artist = false;
// continue;
// }
// if (artist)
// this.m_Artist += values[i];
// else
// this.m_Title += values[i];
//
// }
// return true;
// }
public override string ToString()
{
return this.m_Artist + ", " + this.m_Title;
}
}
}

File diff suppressed because it is too large Load Diff

237
Freedb/Site.cs Normal file
View File

@@ -0,0 +1,237 @@
#region COPYRIGHT (c) 2004 by Brian Weeres
/* Copyright (c) 2004 by Brian Weeres
*
* Email: bweeres@protegra.com; bweeres@hotmail.com
*
* Permission to use, copy, modify, and distribute this software for any
* purpose is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* If you modify it then please indicate so.
*
* The software is provided "AS IS" and there are no warranties or implied warranties.
* In no event shall Brian Weeres and/or Protegra Technology Group be liable for any special,
* direct, indirect, or consequential damages or any damages whatsoever resulting for any reason
* out of the use or performance of this software
*
*/
#endregion
using System;
namespace Freedb
{
/// <summary>
/// Summary description for Site.
/// </summary>
public class Site
{
private string m_SiteAddress;
private string m_Protocol;
private string m_AdditionalAddressInfo;
private string m_Port;
private string m_Latitude;
private string m_Longitude;
private string m_Description;
public class PROTOCOLS
{
public const string HTTP = "http";
public const string CDDBP = "cddbp";
public const string ALL = "all";
}
/// <summary>
/// Property AdditionalAddressInfo (string)
/// Any additional addressing information needed to access the server.
/// For example, for HTTP protocol servers, this would be the path to the CCDB server CGI script.
/// This field will be "-" if no additional addressing information is needed.
/// </summary>
public string AdditionalAddressInfo
{
get
{
return this.m_AdditionalAddressInfo;
}
set
{
this.m_AdditionalAddressInfo = value;
}
}
#region Public Properties
/// <summary>
/// Property Site (string) - Internet address of the remote site
/// </summary>
public string SiteAddress
{
get
{
return this.m_SiteAddress;
}
set
{
this.m_SiteAddress = value;
}
}
/// <summary>
/// Property Protocol (string)
/// The transfer protocol used to access the site
/// </summary>
public string Protocol
{
get
{
return this.m_Protocol;
}
set
{
this.m_Protocol = value;
}
}
/// <summary>
/// Property Port (string)- The port at which the server resides on that site.
/// </summary>
public string Port
{
get
{
return this.m_Port;
}
set
{
this.m_Port = value;
}
}
/// <summary>
/// Property Description (string)
/// A short description of the geographical location of the site.
/// </summary>
public string Description
{
get
{
return this.m_Description;
}
set
{
this.m_Description = value;
}
}
/// <summary>
/// Property Latitude (string)
/// The latitude of the server site. The format is as follows:
/// CDDD.MM
/// Where "C" is the compass direction (N, S), "DDD" is the
/// degrees, and "MM" is the minutes.
/// </summary>
public string Latitude
{
get
{
return this.m_Latitude;
}
set
{
this.m_Latitude = value;
}
}
/// <summary>
/// Property Longitude (string)
/// The longitude of the server site. Format is as above, except
/// the compass direction must be one of (E, W).
/// </summary>
public string Longitude
{
get
{
return this.m_Longitude;
}
set
{
this.m_Longitude = value;
}
}
#endregion
public Site(string siteFromCDDB)
{
if (!Parse(siteFromCDDB))
{
throw new Exception("Unable to Parse Site. Input: " + siteFromCDDB);
}
}
/// <summary>
/// Builds a site from an address, protocol and addition info
/// </summary>
/// <param name="siteAddress"></param>
/// <param name="protocol"></param>
/// <param name="additionAddressInfo"></param>
public Site(string siteAddress, string protocol, string additionAddressInfo)
{
m_SiteAddress = siteAddress;
m_Protocol = protocol;
m_AdditionalAddressInfo = additionAddressInfo;
}
public bool Parse(string siteAsString)
{
siteAsString.Trim();
string [] values = siteAsString.Split(' ');
if (values.Length <5)
return false;
m_SiteAddress = values[0];
this.m_Protocol = values[1];
m_Port = values[2];
if (values[3].Trim() != "-")
m_AdditionalAddressInfo = values[3];
m_Latitude = values[4];
m_Longitude = values[5];
// description could be split over many because it could have spaces
for (int i= 6; i < values.Length;i++)
{
m_Description += values[i];
m_Description += " ";
}
m_Description.Trim();
return true;
}
public string GetUrl()
{
if (this.m_Protocol == Site.PROTOCOLS.HTTP)
return "http://" + this.m_SiteAddress + this.m_AdditionalAddressInfo;
else
return this.m_SiteAddress;
}
public override string ToString()
{
return m_SiteAddress + ", " + this.m_Description;
}
}
}

2378
Freedb/SiteCollection.cs Normal file

File diff suppressed because it is too large Load Diff

99
Freedb/Track.cs Normal file
View File

@@ -0,0 +1,99 @@
#region COPYRIGHT (c) 2004 by Brian Weeres
/* Copyright (c) 2004 by Brian Weeres
*
* Email: bweeres@protegra.com; bweeres@hotmail.com
*
* Permission to use, copy, modify, and distribute this software for any
* purpose is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* If you modify it then please indicate so.
*
* The software is provided "AS IS" and there are no warranties or implied warranties.
* In no event shall Brian Weeres and/or Protegra Technology Group be liable for any special,
* direct, indirect, or consequential damages or any damages whatsoever resulting for any reason
* out of the use or performance of this software
*
*/
#endregion
using System;
namespace Freedb
{
/// <summary>
/// Summary description for Track.
/// </summary>
public class Track
{
private string m_Title;
private string m_ExtendedData;
#region Public Properties
/// <summary>
/// Property ExtendedData (string)
/// </summary>
public string ExtendedData
{
get
{
return this.m_ExtendedData;
}
set
{
this.m_ExtendedData = value;
}
}
/// <summary>
/// Property Title (string)
/// </summary>
public string Title
{
get
{
return this.m_Title;
}
set
{
this.m_Title = value;
}
}
#endregion
/// <summary>
/// Create an instance of a Track
/// </summary>
/// <param name="title"></param>
public Track()
{
}
/// <summary>
/// Create an instance of a Track passing in a title
/// </summary>
/// <param name="title"></param>
public Track(string title)
{
m_Title = title;
}
/// <summary>
/// Create an instance of a Track passing in a title and extended data
/// </summary>
/// <param name="title"></param>
public Track(string title, string extendedData)
{
m_Title = title;
m_ExtendedData = extendedData;
}
}
}

2378
Freedb/TrackCollection.cs Normal file

File diff suppressed because it is too large Load Diff

885
Freedb/cddbproto.txt Normal file
View File

@@ -0,0 +1,885 @@
CDDB Server Protocol
--------------------
By Steve Scherf and Ti Kan
(Protocol level 5 & 6 specification by freedb developer team)
Applies to cddbd 1.5.2
$Id: CDDBPROTO,v 1.10.2.2 2006/04/14 14:45:06 joerg78 Exp $
Notation:
-> : client to server
<- : server to client
terminating marker: `.' character in the beginning of a line
Server response code (three digit code):
First digit:
1xx Informative message
2xx Command OK
3xx Command OK so far, continue
4xx Command OK, but cannot be performed for some specified reasons
5xx Command unimplemented, incorrect, or program error
Second digit:
x0x Ready for further commands
x1x More server-to-client output follows (until terminating marker)
x2x More client-to-server input follows (until terminating marker)
x3x Connection will close
Third digit:
xx[0-9] Command-specific code
Note: "*" means, that the command is only for administrative use and requires
special access permissions, that normal users don't have.
CDDB Protocol Level 1:
----------------------
Server sign-on banner:
----------------------
<- code hostname CDDBP server version ready at date
code:
200 OK, read/write allowed
201 OK, read only
432 No connections allowed: permission denied
433 No connections allowed: X users allowed, Y currently active
434 No connections allowed: system load too high
hostname:
Server host name. Example: xyz.fubar.com
version:
Version number of server software. Example: v1.0PL0
date:
Current date and time. Example: Wed Mar 13 00:41:34 1996
Initial client-server handshake:
--------------------------------
Note: This handshake must occur before other cddb commands
are accepted by the server.
Client command:
-> cddb hello username hostname clientname version
username:
Login name of user. Example: johndoe
hostname:
Host name of client. Example: abc.fubar.com
clientname:
The name of the connecting client. Example: xmcd, cda, EasyCD,
et cetera. Do not use the name of another client which already
exists.
version:
Version number of client software. Example: v1.0PL0
Server response:
<- code hello and welcome username@hostname running clientname version
code:
200 Handshake successful
431 Handshake not successful, closing connection
402 Already shook hands
List the genre categories:
--------------------------
Client command:
-> cddb lscat
Server response:
<- code Okay category list follows (until terminating marker)
<- category
<- category
<- (more categories...)
<- .
code:
210 Okay category list follows
category:
CD category. Example: rock
Query database for matching entries:
------------------------------------
Client command:
-> cddb query discid ntrks off1 off2 ... nsecs
discid:
CD disc ID number. Example: f50a3b13
ntrks:
Total number of tracks on CD.
off1, off2, ...:
Frame offset of the starting location of each track.
nsecs:
Total playing length of CD in seconds.
Server response:
<- code categ discid dtitle
or
<- code close matches found
<- categ discid dtitle
<- categ discid dtitle
<- (more matches...)
<- .
code:
200 Found exact match
211 Found inexact matches, list follows (until terminating marker)
202 No match found
403 Database entry is corrupt
409 No handshake
categ:
CD category. Example: rock
discid:
CD disc ID number of the found entry. Example: f50a3b13
dtitle:
The Disc Artist and Disc Title (The DTITLE line). For example:
Pink Floyd / The Dark Side of the Moon
Read entry from database:
-------------------------
Client command:
-> cddb read categ discid
categ:
CD category. Example: rock
discid:
CD disc ID number. Example: f50a3b13
Server response:
<- code categ discid
<- # xmcd 2.0 CD database file
<- # ...
<- (CDDB data...)
<- .
or
<- code categ discid No such CD entry in database.
code:
210 OK, CDDB database entry follows (until terminating marker)
401 Specified CDDB entry not found.
402 Server error.
403 Database entry is corrupt.
409 No handshake.
categ:
CD category. Example: rock
discid:
CD disc ID number. Example: f50a3b13
* Delete entry from database:
-----------------------------
Client command:
-> cddb unlink categ discid
categ:
CD category. Example: rock
discid:
CD disc ID number. Example: f50a3b13
Server response:
<- code and message
code and message:
200 OK, file has been deleted.
401 Permission denied.
402 File access failed.
501 Invalid category: categ.
categ:
CD category. Example: rock
* Write entry to database:
--------------------------
Client command:
-> cddb write categ discid
categ:
CD category. Example: rock
discid:
CD disc ID number. Example: f50a3b13
Server response:
<- code and message
code and message:
320 OK, input CDDB data (until terminating marker)
401 Permission denied.
402 Server file system full/file access failed.
409 No handshake.
Client data:
-> # xmcd 2.0 CD database file
-> # ...
-> (CDDB data)
-> .
Server response:
<- code message
code:
200 CDDB entry accepted
501 Entry rejected: reason for rejection.
message:
Message string to indicate write status:
CDDB entry accepted, or CDDB entry rejected.
Discid calculation:
------------------
Client command:
-> discid ntrks off_1 off_2 ... off_n nsecs
ntrks:
total number of tracks on CD.
off_X:
frame offset of track X.
nsecs:
total playing length of the CD in seconds.
Server response:
<- code Disc ID is discid
code:
200 Calculated disc ID properly
500 Command Syntax error
discid:
CD disc ID number calculated from the given arguments.
* Get a server system file:
---------------------------
Client command:
-> get file
file:
filename of the file to get
Server response:
<- code message
or
<- code OK, (filename) follows (until terminating `.')
<- (file content)
<- .
code and message:
210 OK, %s follows (until terminating `.')
401 Permission denied.
402 File access failed.
402 File not found.
Help information:
-----------------
Client command:
-> help
or
-> help cmd
cmd:
CDDB command. Example: quit
or
-> help cmd subcmd
cmd:
CDDB command. Example: cddb
subcmd:
CDDB command argument. Example: query
Server response:
<- code Help information follows
<- (help data ...)
<- .
or
<- code no help information available
code:
210 OK, help information follows (until terminating marker)
401 No help information available
* Log statistics:
-----------------
Client command:
-> log [[-l lines] [start date [end date]] | [day [days]] | [get]]
lines:
The maximum number of lines to print for each data list in the
log statistics.
start date:
The date after which statistics should be calculated. Date is
of the format: hh[mm[ss[MM[DD[[CC]YY]]]]]
E.g.: 201200053196 for 8:12 PM on May 31, 1996.
20120005312096 for 8:12 PM on May 31, 2096.
080530 for today at at 8:15 and 30 seconds.
If the century ("CC") is omitted, a reasonable guess is made. If
this argument is omitted, all messages are considered.
end date:
The date after which statistics should not be calculated. If
omitted, the end date is assumed to be the current date.
day:
The string "day". This solitary argument will cause a log search
of messages generated within the last day.
days:
A positive numerical argument which modifies the number of days'
messages to searh. If this argument is left out, the default is 1.
get:
The string "get". This solitary argument will cause the server
to send the contents of the log file.
Server response:
<- code Log summary follows
<- (log stats)
<- .
or
<- code Log follows
<- (log stats)
<- .
code:
210 OK, log summary follows (until terminating marker)
211 OK, log follows (until terminating marker)
401 Permission denied
402 No log information available
501 Invalid start/end date
Message of the day:
------------------
Client command:
-> motd
Server response:
<- code Last modified: date MOTD follows (until terminating marker)
<- (message text)
<- .
code:
210 Last modified: 05/31/96 06:31:14 MOTD follows (until terminating marker)
401 No message of the day available
date:
The date the text of the message of the day was modified. The date
appears in the following format:
05/31/96 06:31:14
This value may be used by client software as a message timestamp
for purposes of determining if it has already been displayed. This
format was chosen because it is more easily parsed than the standard
ctime() format.
Server protocol level:
----------------------
Client command:
-> proto [level]
level:
The (numerical) protocol level to set the server to.
Server response:
<- code CDDB protocol level: current cur_level, supported supported_level
or
<- code OK, protocol version now: cur_level
code:
200 CDDB protocol level: current cur_level, supported supp_level
201 OK, protocol version now: cur_level
501 Illegal protocol level.
502 Protocol level already cur_level.
cur_level:
The current protocol level at which the server is running.
supported_level:
The maximum supported protocol level.
* Put a server system file:
---------------------------
Client command:
-> put file
file:
sites or motd
Server response:
<- code message
code and message:
320 OK, input file data (terminate with `.')
401 Permission denied.
402 File access failed.
402 Not a regular file.
Client data:
-> (file data of the file, that shall be written)
-> .
Server response
<- code message
code and message:
200 Put successful.
402 File access failed.
501 Input too long.
Close connection to server:
---------------------------
Client command:
-> quit
Server response:
<- code hostname message
code and message:
230 Closing connection. Goodbye.
530 error, closing connection.
hostname:
Server host name. Example: xyz.fubar.com
Server sites:
--------------
Client command:
-> sites
Server response:
<- code OK, site information follows (until terminating `.')
<- (data)
<- .
code:
210 Ok, site information follows
401 No site information available.
The data format is as follows:
site port latitude longitude description
The fields are as follows:
site:
The Internet address of the remote site.
port:
The port at which the server resides on that site.
latitude:
The latitude of the server site. The format is as follows:
CDDD.MM
Where "C" is the compass direction (N, S), "DDD" is the
degrees, and "MM" is the minutes.
longitude:
The longitude of the server site. Format is as above, except
the compass direction must be one of (E, W).
description:
A short description of the geographical location of the site.
Example:
us.freedb.org 8880 N037.21 W121.55 San Jose, CA USA
Server status:
--------------
Client command:
-> stat
Server response:
<- code OK, status information follows (until terminating `.')
<- (data)
<- .
code:
210 Ok, status information follows
The possible data is as follows:
current proto: <current_level>
An integer representing the server's current operating protocol
level.
max proto: <max_level>
The maximum supported protocol level.
gets: <yes | no>
Whether or not the client is allowed to get log information,
according to the string "yes" or "no".
updates: <yes | no>
Whether or not the client is allowed to initiate a database
update, according to the string "yes" or "no".
posting: <yes | no>
Whether or not the client is allowed to post new entries,
according to the string "yes" or "no".
quotes: <yes | no>
Whether or not quoted arguments are enabled, according to
the string "yes" or "no".
current users: <num_users>
The number of users currently connected to the server.
max users: <num_max_users>
The number of users that can concurrently connect to the server.
strip ext: <yes | no>
Whether or not extended data is stripped by the server before
presented to the user.
Database entries: <num_db_entries>
The total number of entries in the database.
Database entries by category:
This field is followed by a list of catgories and the number
of entries in that category. Each entry is of the following
format:
<white space>catgory: <num_db_entries>
The list of entries is terminated by the first line that does
not begin with white space.
* Pending file transmissions:
This field is followed by a list of sites that are fed new
database entries at periodic intervals, and the number of
entries that have yet to be transmitted to that site.
Each entry is of the following format:
<white space>site: <num_db_entries>
The list of entries is terminated by the first line that does
not begin with white space.
This list may grow as needed, so clients must expect possible
unrecognizable data. Also, additional fields may be added to
the currently existing lines, although no existing fields will
be removed or change position.
* Database update:
----------------
Client command:
-> update
Server response:
<- code Updating the database.
or
<- code Permission denied.
or
<- code Unable to update the database.
code:
200 Updating the database.
401 Permission denied.
402 Unable to update the database.
* Perform user validation:
--------------------------
Client command:
-> validate
Server response:
<- 503 Validation not required.
or
<- 320 OK, input validation string, salt=saltvalue (terminate with newline)
Client data:
-> validation string
Server response:
-> code message
code and message:
200 Validation successful.
501 Incorrect validation string length.
502 Invalid validation string.
Server version:
---------------
Client command:
-> ver
Server response:
<- code servername version copyright
or
<- code Version information follows
code:
200 Version information.
211 OK, version information follows (until terminating marker)
version:
Server version. Example: v1.5PL0
copyright:
Copyright string. Example: Copyright (c) 1996-2001 Steve Scherf et al.
* Server users:
---------------
Client command:
-> whom
Server response:
<- code message
code and message:
210 OK, user list follows (until terminating marker)
401 No user information available.
General errors:
---------------
Server response:
<- code error
code:
402 Server error.
408 CGI environment error.
500 Command syntax error, command unknown, command unimplemented.
530 Server error, server timeout.
Reserved errors:
----------------
The following error codes are reserved, and will never be returned as a
response to a CDDB protocol command. They are intended to be used internally
by clients that have a need for generating pseudo-responses.
600-699
CDDB Protocol Level 2:
----------------------
In all respects, protocol level 2 is the same as level 1, with the exceptions
listed below.
Arguments to commands may be surrounded by double quotes. All characters
within the quotes, including white space, are included in the argument. All
white space is replaced by the `_' (2Dh) character by the server. White space
is defined as ` ' (20h) and `^I' (control-I, or 09h).
Arguments containing quotes that should not be interpreted with the special
meaning described above should be escaped with a preceding backslash character,
or '\' (5Ch). If an actual backslash appears in an argument, it should be
escaped with a preceding backslash. In both cases, the preceding backslash
will be removed from the input before being interpreted.
CDDB Protocol Level 3:
----------------------
Protocol level 3 is the same as level 2, with the exception listed below.
The output of the "sites" command has changed to meet the folowing description:
The data format is as follows:
site protocol port address latitude longitude description
The fields are as follows:
site:
The Internet address of the remote site.
protocol:
The transfer protocol used to access the site.
port:
The port at which the server resides on that site.
address:
Any additional addressing information needed to access the
server. For example, for HTTP protocol servers, this would be
the path to the CDDB server CGI script. This field will be
"-" if no additional addressing information is needed.
latitude:
The latitude of the server site. The format is as follows:
CDDD.MM
Where "C" is the compass direction (N, S), "DDD" is the
degrees, and "MM" is the minutes.
longitude:
The longitude of the server site. Format is as above, except
the compass direction must be one of (E, W).
description:
A short description of the geographical location of the site.
Example:
us.freedb.org cddbp 8880 - N037.21 W121.55 San Jose, CA USA
us.freedb.org http 80 /~cddb/cddb.cgi N037.21 W121.55 San Jose, CA USA
Note that a site may appear once for each type of protocol it supports for
accessing the server.
CDDB Protocol Level 4:
----------------------
Protocol level 4 is the same as level 3, with the exception listed below.
The output of the "cddb query" command may result in multiple exact matches.
A new response code, 210, has been added to indicate that more than one
exact match has been found.
Server response:
----------------
<- code exact matches found
<- categ discid dtitle
<- categ discid dtitle
<- (more matches...)
<- .
code:
210 Found exact matches, list follows (until terminating marker)
CDDB Protocol Level 5:
----------------------
Protocol level 5 is the same as level 4, with the following exception:
The database entries returned when issuing the "cddb read" command now also
contain DYEAR and DGENRE fields (between the DTITLE and the TTITLE's).
For more info on the new database entry fields take a look at the
database format specification
CDDB Protocol Level 6:
----------------------
Protocol level 6 is the same as level 5 except that the character set
is now UTF-8 instead of ISO-8859-1. Note that UTF-8 is an extension of
US-ASCII, just like ISO-8859-1 is an extension of US-ASCII, so there
is no difference between levels 5 and 6 as far as 7-bit ASCII data is
concerned.
Clients can convert data between UTF-8 and the user's preferred
character set using the iconv program and library function which are
provided by glibc-2.2 or by the portable library libiconv. (They are
also provided by the C library on some non-glibc systems, but often in
a buggy or incompatible form.) For example, to convert data to UTF-8
from the character set of the current locale in a shell script use
"iconv -t utf-8 < in > out".
For more information about Unicode and UTF-8 see:
ftp://ftp.ilog.fr/pub/Users/haible/utf8/Unicode-HOWTO.html
http://www.cl.cam.ac.uk/~mgk25/unicode.html
Addendum A: Proper use of CDDBP:
--------------------------------
There are a few guidelines that must be followed in order to make proper use
of CDDBP:
- When handshaking with the server via the "cddb hello" command, the client
must specify its own name and version, not that of some other client (such
as xmcd).
- Before performing a "cddb read", the client program MUST perform a
"cddb query". Failure to do so may result in the client program receiving
incorrect data from the server. Also, without performing a query, the
client program will not benefit from close matches in the event of the
lack of an exact match in the database.
Addendum B: CDDBP under HTTP:
-----------------------------
Accessing a server as a CGI script is done in much the same way as through
direct interaction. The command set is identical, though the method of
communication is through CDDBP commands encapsulated in the HTTP protocol.
The only limitation is that a single command may be executed per connection,
since HTTP is not truly interactive. For the server to be accessed in this
way, it must reside on the target host at a known URL which is accessible by
the host HTTP server. The client program must connect to the HTTP server on
the target host and issue an HTTP command with the appropriate CDDBP command
encapsulated within.
Commands may be submitted to servers in CGI mode using either the "GET" or
"POST" HTTP commands. Both methods are supported, and there is no real
difference between how both are to be used other than the syntactical
difference between the two methods. The "POST" method may provide the ability
to issue longer commands, though, depending on the architecture of the system
on which the server resides.
The server command must be sent as part of the "Request-URL" in the case
of the "GET" method, and as the "Entity-Body" in the case of the "POST"
method. In both cases, the command must be of the following form:
cmd=server+command&hello=joe+my.host.com+clientname+version&proto=6
Where the text following the "cmd=" represents the CDDBP command to be
executed, the text following the "hello=" represents the arguments to
the "cddb hello" command that is implied by this operation, and the
text following the "proto=" represents the argument to the "proto" command
that is implied by this operation.
The "+" characters in the input represent spaces, and will be translated
by the server before performing the request. Special characters may be
represented by the sequence "%XX" where "XX" is a two-digit hex number
corresponding to the ASCII (ISO-8859-1) sequence of that character. The
"&" characters denote separations between the command, hello and proto
arguments. Newlines and carriage returns must not appear anywhere in the
string except at the end.
All CDDBP commands are supported under HTTP, except for "cddb hello",
"cddb write", "proto", "put", "validate" and "quit".
For example, should user "joe" on system "my.host.com" be running xmcd 2.1,
a read request for his currenly playing CD might look like this:
cmd=cddb+read+rock+12345678&hello=joe+my.host.com+xmcd+2.1&proto=3
The server will perform the implied "proto" and "cddb hello" commands,
and then perform the requested "cddb read" command.
Server response to the command is encapsulated in the HTTP server response,
and appears in the "Entity-Body" exactly as it would appear using the CDDBP
protocol. Note that the HTTP response "Entity-Header" is not guaranteed to
contain a "Content-Length" field, so clients should be prepared to accept
variable length input. This is no different from operation under CDDBP. The
header will always contain a Mime "Content-Type" field which describes the
body of data as "text/plain".
For more detailed information on HTTP and Mime, see RFC 1945 and RFC 1521.
Addendum C: CDDBP under SMTP:
-----------------------------
The use of e-mail mode (SMTP) commands is simple. A special subject line
lets the server know that the e-mail contains a command, and somewhere in the
body there should be a HTTP-style server command; the server will execute
only one such commands per e-mail.
The subject for e-mail commands should look like this:
Subject: cddb #command arbitrary_string
The "arbitrary_string" should be some randomly-chosen string. The server
will include this string in the subject of the response. The rest of the
subject should appear literally as it does here.
Somewhere in the body of the e-mail should be exactly one server command. For
example:
cmd=motd&hello=joe+my.host.com+xmcd_via_email+v1.0&proto=6
As you might have noticed, this command is exactly the same as a HTTP-mode
CDDBP command. The command response will be mailed to the sender. Upon
successful completion of an e-mail command request (even if the command
itself was not successful), the reply will contain a subject which looks
like this:
Subject cddb #response ok arbitrary_string
Should the server be unable to process the e-mail command for some reason, the
subject will look like this:
Subject cddb #response failed arbitrary_string
In both cases, the "arbitrary_string" is the same as the one specified in the
initial command e-mail.