status_t CDDBServer::Read(const BString& category, const BString& cddbID, const BString& artist, ReadResponseData& readResponse, bool verbose) { if (_OpenConnection() != B_OK) return B_ERROR; // Assemble the Read command. BString cddbCommand("cddb read "); cddbCommand << category << " " << cddbID; BString output; status_t result = _SendCommand(cddbCommand, output); if (result == B_OK) { if (verbose) puts(output); // Remove the header from the reply. output.Remove(0, output.FindFirst("\r\n\r\n") + 4); // Check status code. BString statusCode; output.MoveInto(statusCode, 0, 3); if (statusCode == "210") { // Remove first line and parse the others. output.Remove(0, output.FindFirst("\r\n") + 2); } else { // Something bad happened. return B_ERROR; } // Process all entries. bool done = false; while (!done) { if (output[0] == '#') { // Comment. Remove it. output.Remove(0, output.FindFirst("\r\n") + 2); continue; } // Extract one line to reduce the scope of processing to it. BString line; output.MoveInto(line, 0, output.FindFirst("\r\n")); output.Remove(0, 2); // Obtain prefix. BString prefix; line.MoveInto(prefix, 0, line.FindFirst("=")); line.Remove(0, 1); if (prefix == "DTITLE") { // Disk title. BString artist; line.MoveInto(artist, 0, line.FindFirst(" / ")); line.Remove(0, 3); readResponse.title = line; readResponse.artist = artist; } else if (prefix == "DYEAR") { // Disk year. char* firstInvalid; errno = 0; uint32 year = strtoul(line.String(), &firstInvalid, 10); if ((errno == ERANGE && (year == (uint32)LONG_MAX || year == (uint32)LONG_MIN)) || (errno != 0 && year == 0)) { // Year out of range. printf("Year out of range: %s\n", line.String()); year = 0; } if (firstInvalid == line.String()) { printf("Invalid year: %s\n", line.String()); year = 0; } readResponse.year = year; } else if (prefix == "DGENRE") { // Disk genre. readResponse.genre = line; } else if (prefix.FindFirst("TTITLE") == 0) { // Track title. BString index; prefix.MoveInto(index, 6, prefix.Length() - 6); char* firstInvalid; errno = 0; uint32 track = strtoul(index.String(), &firstInvalid, 10); if (errno != 0 || track > 99) { // Track out of range. printf("Track out of range: %s\n", index.String()); return B_ERROR; } if (firstInvalid == index.String()) { printf("Invalid track: %s\n", index.String()); return B_ERROR; } BString trackArtist; int32 pos = line.FindFirst(" / "); if (pos >= 0 && artist.ICompare("Various") == 0) { // Disk is set to have a compilation artist and // we have track specific artist information. line.MoveInto(trackArtist, 0, pos); // Move artist information from line to artist. line.Remove(0, 3); // Remove " / " from line. } else { trackArtist = artist; } TrackData* trackData = _Track(readResponse, track); trackData->artist += trackArtist; trackData->title += line; } if (output == "" || output == ".\r\n") { // All returned data was processed exit the loop. done = true; } } } else { printf("Error sending CDDB command : \"%s\".\n", cddbCommand.String()); } _CloseConnection(); return B_OK; }
status_t CDDBServer::Query(uint32 cddbID, const scsi_toc_toc* toc, QueryResponseList& queryResponses) { if (_OpenConnection() != B_OK) return B_ERROR; // Convert CDDB id to hexadecimal format. char hexCddbId[9]; sprintf(hexCddbId, "%08" B_PRIx32, cddbID); // Assemble the Query command. int32 numTracks = toc->last_track + 1 - toc->first_track; BString cddbCommand("cddb query "); cddbCommand << hexCddbId << " " << numTracks << " "; // Add track offsets in frames. for (int32 i = 0; i < numTracks; ++i) { const scsi_cd_msf& start = toc->tracks[i].start.time; uint32 startFrameOffset = start.minute * kFramesPerMinute + start.second * kFramesPerSecond + start.frame; cddbCommand << startFrameOffset << " "; } // Add total disc time in seconds. Last track is lead-out. const scsi_cd_msf& lastTrack = toc->tracks[numTracks].start.time; uint32 totalTimeInSeconds = lastTrack.minute * 60 + lastTrack.second; cddbCommand << totalTimeInSeconds; BString output; status_t result = _SendCommand(cddbCommand, output); if (result == B_OK) { // Remove the header from the reply. output.Remove(0, output.FindFirst("\r\n\r\n") + 4); // Check status code. BString statusCode; output.MoveInto(statusCode, 0, 3); if (statusCode == "210" || statusCode == "211") { // TODO(bga): We can get around with returning the first result // in case of multiple matches, but we most definitely need a // better handling of inexact matches. if (statusCode == "211") printf("Warning : Inexact match found.\n"); // Multiple results, remove the first line and parse the others. output.Remove(0, output.FindFirst("\r\n") + 2); } else if (statusCode == "200") { // Remove the first char which is a left over space. output.Remove(0, 1); } else if (statusCode == "202") { // No match found. printf("Error : CDDB entry for id %s not found.\n", hexCddbId); return B_ENTRY_NOT_FOUND; } else { // Something bad happened. if (statusCode.Trim() != "") { printf("Error : CDDB server status code is %s.\n", statusCode.String()); } else { printf("Error : Could not find any status code.\n"); } return B_ERROR; } // Process all entries. bool done = false; while (!done) { QueryResponseData* responseData = new QueryResponseData; output.MoveInto(responseData->category, 0, output.FindFirst(" ")); output.Remove(0, 1); output.MoveInto(responseData->cddbID, 0, output.FindFirst(" ")); output.Remove(0, 1); output.MoveInto(responseData->artist, 0, output.FindFirst(" / ")); output.Remove(0, 3); output.MoveInto(responseData->title, 0, output.FindFirst("\r\n")); output.Remove(0, 2); queryResponses.AddItem(responseData); if (output == "" || output == ".\r\n") { // All returned data was processed exit the loop. done = true; } } } else { printf("Error sending CDDB command : \"%s\".\n", cddbCommand.String()); } _CloseConnection(); return result; }
status_t CDDBServer::Read(QueryResponseData* diskData, ReadResponseData* readResponse) { if (_OpenConnection() != B_OK) return B_ERROR; // Assemble the Read command. BString cddbCommand("cddb read "); cddbCommand << diskData->category << " " << diskData->cddbId; BString output; status_t result; result = _SendCddbCommand(cddbCommand, &output); if (result == B_OK) { // Remove the header from the reply. output.Remove(0, output.FindFirst("\r\n\r\n") + 4); // Check status code. BString statusCode; output.MoveInto(statusCode, 0, 3); if (statusCode == "210") { // Remove first line and parse the others. output.Remove(0, output.FindFirst("\r\n") + 2); } else { // Something bad happened. return B_ERROR; } // Process all entries. bool done = false; while (!done) { if (output[0] == '#') { // Comment. Remove it. output.Remove(0, output.FindFirst("\r\n") + 2); continue; } // Extract one line to reduce the scope of processing to it. BString line; output.MoveInto(line, 0, output.FindFirst("\r\n")); output.Remove(0, 2); // Obtain prefix. BString prefix; line.MoveInto(prefix, 0, line.FindFirst("=")); line.Remove(0, 1); if (prefix == "DTITLE") { // Disk title. BString artist; line.MoveInto(artist, 0, line.FindFirst(" / ")); line.Remove(0, 3); readResponse->title = line; readResponse->artist = artist; } else if (prefix == "DYEAR") { // Disk year. char* firstInvalid; errno = 0; uint32 year = strtoul(line.String(), &firstInvalid, 10); if ((errno == ERANGE && (year == (uint32)LONG_MAX || year == (uint32)LONG_MIN)) || (errno != 0 && year == 0)) { // Year out of range. printf("Year out of range: %s\n", line.String()); return B_ERROR; } if (firstInvalid == line.String()) { printf("Invalid year: %s\n", line.String()); return B_ERROR; } readResponse->year = year; } else if (prefix == "DGENRE") { // Disk genre. readResponse->genre = line; } else if (prefix.FindFirst("TTITLE") == 0) { // Track title. BString index; prefix.MoveInto(index, 6, prefix.Length() - 6); TrackData* trackData = new TrackData; char* firstInvalid; errno = 0; uint32 track = strtoul(index.String(), &firstInvalid, 10); if ((errno == ERANGE && (track == (uint32)LONG_MAX || track == (uint32)LONG_MIN)) || (errno != 0 && track == 0)) { // Track out of range. printf("Track out of range: %s\n", index.String()); delete trackData; return B_ERROR; } if (firstInvalid == index.String()) { printf("Invalid track: %s\n", index.String()); delete trackData; return B_ERROR; } trackData->trackNumber = track; int32 pos = line.FindFirst(" / " ); if (pos != B_ERROR) { // We have track specific artist information. BString artist; line.MoveInto(artist, 0, pos); line.Remove(0, 3); trackData->artist = artist; } else { trackData->artist = diskData->artist; } trackData->title = line; (readResponse->tracks).AddItem(trackData); } if (output == "" || output == ".\r\n") { // All returned data was processed exit the loop. done = true; } } } else { printf("Error sending CDDB command : \"%s\".\n", cddbCommand.String()); } _CloseConnection(); return B_OK; }