1cfe6baf6SAxel Dörfler /* 2cfe6baf6SAxel Dörfler * Copyright 2008-2015, Haiku, Inc. All Rights Reserved. 3cfe6baf6SAxel Dörfler * Distributed under the terms of the MIT License. 4cfe6baf6SAxel Dörfler * 5cfe6baf6SAxel Dörfler * Authors: 6cfe6baf6SAxel Dörfler * Bruno Albuquerque, bga@bug-br.org.br 7cfe6baf6SAxel Dörfler */ 8cfe6baf6SAxel Dörfler 9cfe6baf6SAxel Dörfler 10cfe6baf6SAxel Dörfler #include "cddb_server.h" 11cfe6baf6SAxel Dörfler 12cfe6baf6SAxel Dörfler #include <errno.h> 13cfe6baf6SAxel Dörfler #include <stdio.h> 14cfe6baf6SAxel Dörfler #include <stdlib.h> 15cfe6baf6SAxel Dörfler #include <unistd.h> 16cfe6baf6SAxel Dörfler 17cfe6baf6SAxel Dörfler 18cfe6baf6SAxel Dörfler static const char* kDefaultLocalHostName = "unknown"; 19cfe6baf6SAxel Dörfler static const uint32 kDefaultPortNumber = 80; 20cfe6baf6SAxel Dörfler 21cfe6baf6SAxel Dörfler static const uint32 kFramesPerSecond = 75; 22cfe6baf6SAxel Dörfler static const uint32 kFramesPerMinute = kFramesPerSecond * 60; 23cfe6baf6SAxel Dörfler 24cfe6baf6SAxel Dörfler 25cfe6baf6SAxel Dörfler CDDBServer::CDDBServer(const BString& cddbServer) 26cfe6baf6SAxel Dörfler : 27cfe6baf6SAxel Dörfler fInitialized(false), 28cfe6baf6SAxel Dörfler fConnected(false) 29cfe6baf6SAxel Dörfler { 30cfe6baf6SAxel Dörfler 31cfe6baf6SAxel Dörfler // Set up local host name. 32cfe6baf6SAxel Dörfler char localHostName[MAXHOSTNAMELEN + 1]; 33cfe6baf6SAxel Dörfler if (gethostname(localHostName, MAXHOSTNAMELEN + 1) == 0) { 34cfe6baf6SAxel Dörfler fLocalHostName = localHostName; 35cfe6baf6SAxel Dörfler } else { 36cfe6baf6SAxel Dörfler fLocalHostName = kDefaultLocalHostName; 37cfe6baf6SAxel Dörfler } 38cfe6baf6SAxel Dörfler 39cfe6baf6SAxel Dörfler // Set up local user name. 40cfe6baf6SAxel Dörfler char* user = getenv("USER"); 41cfe6baf6SAxel Dörfler if (user == NULL) { 42cfe6baf6SAxel Dörfler fLocalUserName = "unknown"; 43cfe6baf6SAxel Dörfler } else { 44cfe6baf6SAxel Dörfler fLocalUserName = user; 45cfe6baf6SAxel Dörfler } 46cfe6baf6SAxel Dörfler 47cfe6baf6SAxel Dörfler // Set up server address; 48cfe6baf6SAxel Dörfler if (_ParseAddress(cddbServer) == B_OK) 49cfe6baf6SAxel Dörfler fInitialized = true; 50cfe6baf6SAxel Dörfler } 51cfe6baf6SAxel Dörfler 52cfe6baf6SAxel Dörfler 53cfe6baf6SAxel Dörfler status_t 54cfe6baf6SAxel Dörfler CDDBServer::Query(uint32 cddbId, const scsi_toc_toc* toc, BList* queryResponse) 55cfe6baf6SAxel Dörfler { 56cfe6baf6SAxel Dörfler if (_OpenConnection() != B_OK) 57cfe6baf6SAxel Dörfler return B_ERROR; 58cfe6baf6SAxel Dörfler 59cfe6baf6SAxel Dörfler // Convert CDDB id to hexadecimal format. 60cfe6baf6SAxel Dörfler char hexCddbId[9]; 61cfe6baf6SAxel Dörfler sprintf(hexCddbId, "%08" B_PRIx32 "", cddbId); 62cfe6baf6SAxel Dörfler 63cfe6baf6SAxel Dörfler // Assemble the Query command. 64cfe6baf6SAxel Dörfler int32 numTracks = toc->last_track + 1 - toc->first_track; 65cfe6baf6SAxel Dörfler 66cfe6baf6SAxel Dörfler BString cddbCommand("cddb query "); 67cfe6baf6SAxel Dörfler cddbCommand << hexCddbId << " " << numTracks << " "; 68cfe6baf6SAxel Dörfler 69cfe6baf6SAxel Dörfler // Add track offsets in frames. 70cfe6baf6SAxel Dörfler for (int32 i = 0; i < numTracks; ++i) { 71cfe6baf6SAxel Dörfler const scsi_cd_msf& start = toc->tracks[i].start.time; 72cfe6baf6SAxel Dörfler 73cfe6baf6SAxel Dörfler uint32 startFrameOffset = start.minute * kFramesPerMinute + 74cfe6baf6SAxel Dörfler start.second * kFramesPerSecond + start.frame; 75cfe6baf6SAxel Dörfler 76cfe6baf6SAxel Dörfler cddbCommand << startFrameOffset << " "; 77cfe6baf6SAxel Dörfler } 78cfe6baf6SAxel Dörfler 79cfe6baf6SAxel Dörfler // Add total disc time in seconds. Last track is lead-out. 80cfe6baf6SAxel Dörfler const scsi_cd_msf& lastTrack = toc->tracks[numTracks].start.time; 81cfe6baf6SAxel Dörfler uint32 totalTimeInSeconds = lastTrack.minute * 60 + lastTrack.second; 82cfe6baf6SAxel Dörfler cddbCommand << totalTimeInSeconds; 83cfe6baf6SAxel Dörfler 84cfe6baf6SAxel Dörfler BString output; 85cfe6baf6SAxel Dörfler status_t result; 86cfe6baf6SAxel Dörfler result = _SendCddbCommand(cddbCommand, &output); 87cfe6baf6SAxel Dörfler 88cfe6baf6SAxel Dörfler if (result == B_OK) { 89cfe6baf6SAxel Dörfler // Remove the header from the reply. 90cfe6baf6SAxel Dörfler output.Remove(0, output.FindFirst("\r\n\r\n") + 4); 91cfe6baf6SAxel Dörfler 92cfe6baf6SAxel Dörfler // Check status code. 93cfe6baf6SAxel Dörfler BString statusCode; 94cfe6baf6SAxel Dörfler output.MoveInto(statusCode, 0, 3); 95cfe6baf6SAxel Dörfler if (statusCode == "210" || statusCode == "211") { 96cfe6baf6SAxel Dörfler // TODO(bga): We can get around with returning the first result 97cfe6baf6SAxel Dörfler // in case of multiple matches, but we most definitely need a 98cfe6baf6SAxel Dörfler // better handling of inexact matches. 99cfe6baf6SAxel Dörfler if (statusCode == "211") 100cfe6baf6SAxel Dörfler printf("Warning : Inexact match found.\n"); 101cfe6baf6SAxel Dörfler 102cfe6baf6SAxel Dörfler // Multiple results, remove the first line and parse the others. 103cfe6baf6SAxel Dörfler output.Remove(0, output.FindFirst("\r\n") + 2); 104cfe6baf6SAxel Dörfler } else if (statusCode == "200") { 105cfe6baf6SAxel Dörfler // Remove the first char which is a left over space. 106cfe6baf6SAxel Dörfler output.Remove(0, 1); 107cfe6baf6SAxel Dörfler } else if (statusCode == "202") { 108cfe6baf6SAxel Dörfler // No match found. 109cfe6baf6SAxel Dörfler printf("Error : CDDB entry for id %s not found.\n", hexCddbId); 110cfe6baf6SAxel Dörfler 111cfe6baf6SAxel Dörfler return B_ENTRY_NOT_FOUND; 112cfe6baf6SAxel Dörfler } else { 113cfe6baf6SAxel Dörfler // Something bad happened. 114cfe6baf6SAxel Dörfler if (statusCode.Trim() != "") { 115cfe6baf6SAxel Dörfler printf("Error : CDDB server status code is %s.\n", 116cfe6baf6SAxel Dörfler statusCode.String()); 117cfe6baf6SAxel Dörfler } else { 118cfe6baf6SAxel Dörfler printf("Error : Could not find any status code.\n"); 119cfe6baf6SAxel Dörfler } 120cfe6baf6SAxel Dörfler 121cfe6baf6SAxel Dörfler return B_ERROR; 122cfe6baf6SAxel Dörfler } 123cfe6baf6SAxel Dörfler 124cfe6baf6SAxel Dörfler // Process all entries. 125cfe6baf6SAxel Dörfler bool done = false; 126cfe6baf6SAxel Dörfler while (!done) { 127cfe6baf6SAxel Dörfler QueryResponseData* responseData = new QueryResponseData; 128cfe6baf6SAxel Dörfler 129cfe6baf6SAxel Dörfler output.MoveInto(responseData->category, 0, output.FindFirst(" ")); 130cfe6baf6SAxel Dörfler output.Remove(0, 1); 131cfe6baf6SAxel Dörfler 132cfe6baf6SAxel Dörfler output.MoveInto(responseData->cddbId, 0, output.FindFirst(" ")); 133cfe6baf6SAxel Dörfler output.Remove(0, 1); 134cfe6baf6SAxel Dörfler 135cfe6baf6SAxel Dörfler output.MoveInto(responseData->artist, 0, output.FindFirst(" / ")); 136cfe6baf6SAxel Dörfler output.Remove(0, 3); 137cfe6baf6SAxel Dörfler 138cfe6baf6SAxel Dörfler output.MoveInto(responseData->title, 0, output.FindFirst("\r\n")); 139cfe6baf6SAxel Dörfler output.Remove(0, 2); 140cfe6baf6SAxel Dörfler 141cfe6baf6SAxel Dörfler queryResponse->AddItem(responseData); 142cfe6baf6SAxel Dörfler 143cfe6baf6SAxel Dörfler if (output == "" || output == ".\r\n") { 144cfe6baf6SAxel Dörfler // All returned data was processed exit the loop. 145cfe6baf6SAxel Dörfler done = true; 146cfe6baf6SAxel Dörfler } 147cfe6baf6SAxel Dörfler } 148cfe6baf6SAxel Dörfler } else { 149cfe6baf6SAxel Dörfler printf("Error sending CDDB command : \"%s\".\n", cddbCommand.String()); 150cfe6baf6SAxel Dörfler } 151cfe6baf6SAxel Dörfler 152cfe6baf6SAxel Dörfler _CloseConnection(); 153cfe6baf6SAxel Dörfler return result; 154cfe6baf6SAxel Dörfler } 155cfe6baf6SAxel Dörfler 156cfe6baf6SAxel Dörfler 157cfe6baf6SAxel Dörfler status_t 158cfe6baf6SAxel Dörfler CDDBServer::Read(QueryResponseData* diskData, ReadResponseData* readResponse) 159cfe6baf6SAxel Dörfler { 160cfe6baf6SAxel Dörfler if (_OpenConnection() != B_OK) 161cfe6baf6SAxel Dörfler return B_ERROR; 162cfe6baf6SAxel Dörfler 163cfe6baf6SAxel Dörfler // Assemble the Read command. 164cfe6baf6SAxel Dörfler BString cddbCommand("cddb read "); 165cfe6baf6SAxel Dörfler cddbCommand << diskData->category << " " << diskData->cddbId; 166cfe6baf6SAxel Dörfler 167cfe6baf6SAxel Dörfler BString output; 168cfe6baf6SAxel Dörfler status_t result; 169cfe6baf6SAxel Dörfler result = _SendCddbCommand(cddbCommand, &output); 170cfe6baf6SAxel Dörfler 171cfe6baf6SAxel Dörfler if (result == B_OK) { 172cfe6baf6SAxel Dörfler // Remove the header from the reply. 173cfe6baf6SAxel Dörfler output.Remove(0, output.FindFirst("\r\n\r\n") + 4); 174cfe6baf6SAxel Dörfler 175cfe6baf6SAxel Dörfler // Check status code. 176cfe6baf6SAxel Dörfler BString statusCode; 177cfe6baf6SAxel Dörfler output.MoveInto(statusCode, 0, 3); 178cfe6baf6SAxel Dörfler if (statusCode == "210") { 179cfe6baf6SAxel Dörfler // Remove first line and parse the others. 180cfe6baf6SAxel Dörfler output.Remove(0, output.FindFirst("\r\n") + 2); 181cfe6baf6SAxel Dörfler } else { 182cfe6baf6SAxel Dörfler // Something bad happened. 183cfe6baf6SAxel Dörfler return B_ERROR; 184cfe6baf6SAxel Dörfler } 185cfe6baf6SAxel Dörfler 186cfe6baf6SAxel Dörfler // Process all entries. 187cfe6baf6SAxel Dörfler bool done = false; 188cfe6baf6SAxel Dörfler while (!done) { 189cfe6baf6SAxel Dörfler if (output[0] == '#') { 190cfe6baf6SAxel Dörfler // Comment. Remove it. 191cfe6baf6SAxel Dörfler output.Remove(0, output.FindFirst("\r\n") + 2); 192cfe6baf6SAxel Dörfler continue; 193cfe6baf6SAxel Dörfler } 194cfe6baf6SAxel Dörfler 195cfe6baf6SAxel Dörfler // Extract one line to reduce the scope of processing to it. 196cfe6baf6SAxel Dörfler BString line; 197cfe6baf6SAxel Dörfler output.MoveInto(line, 0, output.FindFirst("\r\n")); 198cfe6baf6SAxel Dörfler output.Remove(0, 2); 199cfe6baf6SAxel Dörfler 200cfe6baf6SAxel Dörfler // Obtain prefix. 201cfe6baf6SAxel Dörfler BString prefix; 202cfe6baf6SAxel Dörfler line.MoveInto(prefix, 0, line.FindFirst("=")); 203cfe6baf6SAxel Dörfler line.Remove(0, 1); 204cfe6baf6SAxel Dörfler 205cfe6baf6SAxel Dörfler if (prefix == "DTITLE") { 206cfe6baf6SAxel Dörfler // Disk title. 207cfe6baf6SAxel Dörfler BString artist; 208cfe6baf6SAxel Dörfler line.MoveInto(artist, 0, line.FindFirst(" / ")); 209cfe6baf6SAxel Dörfler line.Remove(0, 3); 210cfe6baf6SAxel Dörfler readResponse->title = line; 211cfe6baf6SAxel Dörfler readResponse->artist = artist; 212cfe6baf6SAxel Dörfler } else if (prefix == "DYEAR") { 213cfe6baf6SAxel Dörfler // Disk year. 214cfe6baf6SAxel Dörfler char* firstInvalid; 215cfe6baf6SAxel Dörfler errno = 0; 216cfe6baf6SAxel Dörfler uint32 year = strtoul(line.String(), &firstInvalid, 10); 217cfe6baf6SAxel Dörfler if ((errno == ERANGE && 218cfe6baf6SAxel Dörfler (year == (uint32)LONG_MAX || year == (uint32)LONG_MIN)) 219cfe6baf6SAxel Dörfler || (errno != 0 && year == 0)) { 220cfe6baf6SAxel Dörfler // Year out of range. 221cfe6baf6SAxel Dörfler printf("Year out of range: %s\n", line.String()); 222cfe6baf6SAxel Dörfler year = 0; 223cfe6baf6SAxel Dörfler } 224cfe6baf6SAxel Dörfler 225cfe6baf6SAxel Dörfler if (firstInvalid == line.String()) { 226cfe6baf6SAxel Dörfler printf("Invalid year: %s\n", line.String()); 227cfe6baf6SAxel Dörfler year = 0; 228cfe6baf6SAxel Dörfler } 229cfe6baf6SAxel Dörfler 230cfe6baf6SAxel Dörfler readResponse->year = year; 231cfe6baf6SAxel Dörfler } else if (prefix == "DGENRE") { 232cfe6baf6SAxel Dörfler // Disk genre. 233cfe6baf6SAxel Dörfler readResponse->genre = line; 234cfe6baf6SAxel Dörfler } else if (prefix.FindFirst("TTITLE") == 0) { 235cfe6baf6SAxel Dörfler // Track title. 236cfe6baf6SAxel Dörfler BString index; 237cfe6baf6SAxel Dörfler prefix.MoveInto(index, 6, prefix.Length() - 6); 238cfe6baf6SAxel Dörfler 239cfe6baf6SAxel Dörfler TrackData* trackData = new TrackData; 240cfe6baf6SAxel Dörfler 241cfe6baf6SAxel Dörfler char* firstInvalid; 242cfe6baf6SAxel Dörfler errno = 0; 243cfe6baf6SAxel Dörfler uint32 track = strtoul(index.String(), &firstInvalid, 10); 244cfe6baf6SAxel Dörfler if ((errno == ERANGE && 245cfe6baf6SAxel Dörfler (track == (uint32)LONG_MAX || track == (uint32)LONG_MIN)) 246cfe6baf6SAxel Dörfler || (errno != 0 && track == 0)) { 247cfe6baf6SAxel Dörfler // Track out of range. 248cfe6baf6SAxel Dörfler printf("Track out of range: %s\n", index.String()); 249cfe6baf6SAxel Dörfler delete trackData; 250cfe6baf6SAxel Dörfler return B_ERROR; 251cfe6baf6SAxel Dörfler } 252cfe6baf6SAxel Dörfler 253cfe6baf6SAxel Dörfler if (firstInvalid == index.String()) { 254cfe6baf6SAxel Dörfler printf("Invalid track: %s\n", index.String()); 255cfe6baf6SAxel Dörfler delete trackData; 256cfe6baf6SAxel Dörfler return B_ERROR; 257cfe6baf6SAxel Dörfler } 258cfe6baf6SAxel Dörfler 259cfe6baf6SAxel Dörfler trackData->trackNumber = track; 260cfe6baf6SAxel Dörfler 261cfe6baf6SAxel Dörfler int32 pos = line.FindFirst(" / " ); 262cfe6baf6SAxel Dörfler if (pos != B_ERROR && diskData->artist.ICompare("Various") == 0) { 263cfe6baf6SAxel Dörfler // Disk is set to have a compilation artist and 264cfe6baf6SAxel Dörfler // we have track specific artist information. 265cfe6baf6SAxel Dörfler BString artist; 266cfe6baf6SAxel Dörfler line.MoveInto(artist, 0, pos); 267cfe6baf6SAxel Dörfler // Move artist information from line to artist. 268cfe6baf6SAxel Dörfler line.Remove(0, 3); 269cfe6baf6SAxel Dörfler // Remove " / " from line. 270cfe6baf6SAxel Dörfler trackData->artist = artist; 271cfe6baf6SAxel Dörfler } else { 272cfe6baf6SAxel Dörfler trackData->artist = diskData->artist; 273cfe6baf6SAxel Dörfler } 274cfe6baf6SAxel Dörfler 275cfe6baf6SAxel Dörfler trackData->title = line; 276cfe6baf6SAxel Dörfler 277cfe6baf6SAxel Dörfler (readResponse->tracks).AddItem(trackData); 278cfe6baf6SAxel Dörfler } 279cfe6baf6SAxel Dörfler 280cfe6baf6SAxel Dörfler if (output == "" || output == ".\r\n") { 281cfe6baf6SAxel Dörfler // All returned data was processed exit the loop. 282cfe6baf6SAxel Dörfler done = true; 283cfe6baf6SAxel Dörfler } 284cfe6baf6SAxel Dörfler } 285cfe6baf6SAxel Dörfler } else { 286cfe6baf6SAxel Dörfler printf("Error sending CDDB command : \"%s\".\n", cddbCommand.String()); 287cfe6baf6SAxel Dörfler } 288cfe6baf6SAxel Dörfler 289cfe6baf6SAxel Dörfler _CloseConnection(); 290cfe6baf6SAxel Dörfler return B_OK; 291cfe6baf6SAxel Dörfler } 292cfe6baf6SAxel Dörfler 293cfe6baf6SAxel Dörfler 294cfe6baf6SAxel Dörfler status_t 295cfe6baf6SAxel Dörfler CDDBServer::_ParseAddress(const BString& cddbServer) 296cfe6baf6SAxel Dörfler { 297cfe6baf6SAxel Dörfler // Set up server address. 298cfe6baf6SAxel Dörfler int32 pos = cddbServer.FindFirst(":"); 299cfe6baf6SAxel Dörfler if (pos == B_ERROR) { 300cfe6baf6SAxel Dörfler // It seems we do not have the address:port format. Use hostname as-is. 301cfe6baf6SAxel Dörfler fCddbServerAddr.SetTo(cddbServer.String(), kDefaultPortNumber); 302cfe6baf6SAxel Dörfler if (fCddbServerAddr.InitCheck() == B_OK) 303cfe6baf6SAxel Dörfler return B_OK; 304cfe6baf6SAxel Dörfler } else { 305cfe6baf6SAxel Dörfler // Parse address:port format. 306cfe6baf6SAxel Dörfler int32 port; 307cfe6baf6SAxel Dörfler BString newCddbServer(cddbServer); 308cfe6baf6SAxel Dörfler BString portString; 309cfe6baf6SAxel Dörfler newCddbServer.MoveInto(portString, pos + 1, 310cfe6baf6SAxel Dörfler newCddbServer.CountChars() - pos + 1); 311cfe6baf6SAxel Dörfler if (portString.CountChars() > 0) { 312cfe6baf6SAxel Dörfler char* firstInvalid; 313cfe6baf6SAxel Dörfler errno = 0; 314cfe6baf6SAxel Dörfler port = strtol(portString.String(), &firstInvalid, 10); 315*fc1d8972SMurai Takashi if ((errno == ERANGE && (port == INT32_MAX || port == INT32_MIN)) 316cfe6baf6SAxel Dörfler || (errno != 0 && port == 0)) { 317cfe6baf6SAxel Dörfler return B_ERROR; 318cfe6baf6SAxel Dörfler } 319cfe6baf6SAxel Dörfler if (firstInvalid == portString.String()) { 320cfe6baf6SAxel Dörfler return B_ERROR; 321cfe6baf6SAxel Dörfler } 322cfe6baf6SAxel Dörfler 323cfe6baf6SAxel Dörfler newCddbServer.RemoveAll(":"); 324cfe6baf6SAxel Dörfler fCddbServerAddr.SetTo(newCddbServer.String(), port); 325cfe6baf6SAxel Dörfler if (fCddbServerAddr.InitCheck() == B_OK) 326cfe6baf6SAxel Dörfler return B_OK; 327cfe6baf6SAxel Dörfler } 328cfe6baf6SAxel Dörfler } 329cfe6baf6SAxel Dörfler 330cfe6baf6SAxel Dörfler return B_ERROR; 331cfe6baf6SAxel Dörfler } 332cfe6baf6SAxel Dörfler 333cfe6baf6SAxel Dörfler 334cfe6baf6SAxel Dörfler status_t 335cfe6baf6SAxel Dörfler CDDBServer::_OpenConnection() 336cfe6baf6SAxel Dörfler { 337cfe6baf6SAxel Dörfler if (!fInitialized) 338cfe6baf6SAxel Dörfler return B_ERROR; 339cfe6baf6SAxel Dörfler 340cfe6baf6SAxel Dörfler if (fConnected) 341cfe6baf6SAxel Dörfler return B_OK; 342cfe6baf6SAxel Dörfler 343cfe6baf6SAxel Dörfler if (fConnection.Connect(fCddbServerAddr) == B_OK) { 344cfe6baf6SAxel Dörfler fConnected = true; 345cfe6baf6SAxel Dörfler return B_OK; 346cfe6baf6SAxel Dörfler } 347cfe6baf6SAxel Dörfler 348cfe6baf6SAxel Dörfler return B_ERROR; 349cfe6baf6SAxel Dörfler } 350cfe6baf6SAxel Dörfler 351cfe6baf6SAxel Dörfler 352cfe6baf6SAxel Dörfler void 353cfe6baf6SAxel Dörfler CDDBServer::_CloseConnection() 354cfe6baf6SAxel Dörfler { 355cfe6baf6SAxel Dörfler if (!fConnected) 356cfe6baf6SAxel Dörfler return; 357cfe6baf6SAxel Dörfler 358cfe6baf6SAxel Dörfler fConnection.Close(); 359cfe6baf6SAxel Dörfler fConnected = false; 360cfe6baf6SAxel Dörfler } 361cfe6baf6SAxel Dörfler 362cfe6baf6SAxel Dörfler 363cfe6baf6SAxel Dörfler status_t 364cfe6baf6SAxel Dörfler CDDBServer::_SendCddbCommand(const BString& command, BString* output) 365cfe6baf6SAxel Dörfler { 366cfe6baf6SAxel Dörfler if (!fConnected) 367cfe6baf6SAxel Dörfler return B_ERROR; 368cfe6baf6SAxel Dörfler 369cfe6baf6SAxel Dörfler // Assemble full command string. 370cfe6baf6SAxel Dörfler BString fullCommand; 371cfe6baf6SAxel Dörfler fullCommand << command << "&hello=" << fLocalUserName << " " << 372cfe6baf6SAxel Dörfler fLocalHostName << " cddb_lookup 1.0&proto=6"; 373cfe6baf6SAxel Dörfler 374cfe6baf6SAxel Dörfler // Replace spaces by + signs. 375cfe6baf6SAxel Dörfler fullCommand.ReplaceAll(" ", "+"); 376cfe6baf6SAxel Dörfler 377cfe6baf6SAxel Dörfler // And now add command header and footer. 378cfe6baf6SAxel Dörfler fullCommand.Prepend("GET /~cddb/cddb.cgi?cmd="); 379cfe6baf6SAxel Dörfler fullCommand << " HTTP 1.0\n\n"; 380cfe6baf6SAxel Dörfler 381cfe6baf6SAxel Dörfler int32 result = fConnection.Send((void*)fullCommand.String(), 382cfe6baf6SAxel Dörfler fullCommand.Length()); 383cfe6baf6SAxel Dörfler if (result == fullCommand.Length()) { 384cfe6baf6SAxel Dörfler BNetBuffer netBuffer; 385cfe6baf6SAxel Dörfler while (fConnection.Receive(netBuffer, 1024) != 0) { 386cfe6baf6SAxel Dörfler // Do nothing. Data is automatically appended to the NetBuffer. 387cfe6baf6SAxel Dörfler } 388cfe6baf6SAxel Dörfler 389cfe6baf6SAxel Dörfler // AppendString automatically adds the terminating \0. 390cfe6baf6SAxel Dörfler netBuffer.AppendString(""); 391cfe6baf6SAxel Dörfler 392cfe6baf6SAxel Dörfler output->SetTo((char*)netBuffer.Data(), netBuffer.Size()); 393cfe6baf6SAxel Dörfler return B_OK; 394cfe6baf6SAxel Dörfler } 395cfe6baf6SAxel Dörfler 396cfe6baf6SAxel Dörfler return B_ERROR; 397cfe6baf6SAxel Dörfler } 398