[Windows] Fix driver version detection of AMD RDNA+ GPU on Windows 10
[xbmc.git] / xbmc / network / cddb.cpp
blob3d807963c52036d384da97416c1f887e5af2ea9d
1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "cddb.h"
11 #include "CompileInfo.h"
12 #include "ServiceBroker.h"
13 #include "filesystem/File.h"
14 #include "network/DNSNameCache.h"
15 #include "settings/AdvancedSettings.h"
16 #include "settings/SettingsComponent.h"
17 #include "utils/CharsetConverter.h"
18 #include "utils/StringUtils.h"
19 #include "utils/SystemInfo.h"
20 #include "utils/URIUtils.h"
21 #include "utils/log.h"
23 #include <memory>
25 #include <netdb.h>
26 #include <netinet/in.h>
27 #include <sys/socket.h>
28 #include <taglib/id3v1genres.h>
30 using namespace MEDIA_DETECT;
31 using namespace CDDB;
33 //-------------------------------------------------------------------------------------------------------------------
34 Xcddb::Xcddb()
35 #if defined(TARGET_WINDOWS)
36 : m_cddb_socket(closesocket, INVALID_SOCKET)
37 #else
38 : m_cddb_socket(close, -1)
39 #endif
40 , m_cddb_ip_address(CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_cddbAddress)
42 m_lastError = 0;
45 //-------------------------------------------------------------------------------------------------------------------
46 Xcddb::~Xcddb()
48 closeSocket();
51 //-------------------------------------------------------------------------------------------------------------------
52 bool Xcddb::openSocket()
54 char namebuf[NI_MAXHOST], portbuf[NI_MAXSERV];
55 struct addrinfo hints;
56 struct addrinfo *result, *addr;
57 char service[33];
58 int res;
59 SOCKET fd = INVALID_SOCKET;
61 memset(&hints, 0, sizeof(hints));
62 hints.ai_family = AF_UNSPEC;
63 hints.ai_socktype = SOCK_STREAM;
64 hints.ai_protocol = IPPROTO_TCP;
65 snprintf(service, sizeof(service), "%d", CDDB_PORT);
67 res = getaddrinfo(m_cddb_ip_address.c_str(), service, &hints, &result);
68 if(res)
70 std::string err;
71 #if defined(TARGET_WINDOWS)
72 g_charsetConverter.wToUTF8(gai_strerror(res), err);
73 #else
74 err = gai_strerror(res);
75 #endif
76 CLog::Log(LOGERROR, "Xcddb::openSocket - failed to lookup {} with error {}", m_cddb_ip_address,
77 err);
78 res = getaddrinfo("130.179.31.49", service, &hints, &result);
79 if(res)
80 return false;
83 for(addr = result; addr; addr = addr->ai_next)
85 if(getnameinfo(addr->ai_addr, addr->ai_addrlen, namebuf, sizeof(namebuf), portbuf, sizeof(portbuf),NI_NUMERICHOST))
87 strcpy(namebuf, "[unknown]");
88 strcpy(portbuf, "[unknown]");
90 CLog::Log(LOGDEBUG, "Xcddb::openSocket - connecting to: {}:{} ...", namebuf, portbuf);
92 fd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
93 if (fd == INVALID_SOCKET)
94 continue;
96 if (connect(fd, addr->ai_addr, addr->ai_addrlen) != SOCKET_ERROR)
97 break;
99 closesocket(fd);
100 fd = INVALID_SOCKET;
103 freeaddrinfo(result);
104 if(fd == INVALID_SOCKET)
106 CLog::Log(LOGERROR, "Xcddb::openSocket - failed to connect to cddb");
107 return false;
110 m_cddb_socket.attach(fd);
111 return true;
114 //-------------------------------------------------------------------------------------------------------------------
115 bool Xcddb::closeSocket()
117 if (m_cddb_socket)
119 m_cddb_socket.reset();
121 return true;
124 //-------------------------------------------------------------------------------------------------------------------
125 bool Xcddb::Send( const void *buffer, int bytes )
127 std::unique_ptr<char[]> tmp_buffer(new char[bytes + 10]);
128 strcpy(tmp_buffer.get(), (const char*)buffer);
129 tmp_buffer.get()[bytes] = '.';
130 tmp_buffer.get()[bytes + 1] = 0x0d;
131 tmp_buffer.get()[bytes + 2] = 0x0a;
132 tmp_buffer.get()[bytes + 3] = 0x00;
133 int iErr = send((SOCKET)m_cddb_socket, (const char*)tmp_buffer.get(), bytes + 3, 0);
134 if (iErr <= 0)
136 return false;
138 return true;
141 //-------------------------------------------------------------------------------------------------------------------
142 bool Xcddb::Send( const char *buffer)
144 int iErr = Send(buffer, strlen(buffer));
145 if (iErr <= 0)
147 return false;
149 return true;
152 //-------------------------------------------------------------------------------------------------------------------
153 std::string Xcddb::Recv(bool wait4point)
155 char tmpbuffer[1];
156 char prevChar;
157 int counter = 0;
158 std::string str_buffer;
161 //##########################################################
162 // Read the buffer. Character by character
163 tmpbuffer[0]=0;
166 int lenRead;
168 prevChar=tmpbuffer[0];
169 lenRead = recv((SOCKET)m_cddb_socket, (char*) & tmpbuffer, 1, 0);
171 //Check if there was any error reading the buffer
172 if(lenRead == 0 || lenRead == SOCKET_ERROR || WSAGetLastError() == WSAECONNRESET)
174 CLog::Log(LOGERROR,
175 "Xcddb::Recv Error reading buffer. lenRead = [{}] and WSAGetLastError = [{}]",
176 lenRead, WSAGetLastError());
177 break;
180 //Write received data to the return string
181 str_buffer.push_back(tmpbuffer[0]);
182 counter++;
183 }while(wait4point ? prevChar != '\n' || tmpbuffer[0] != '.' : tmpbuffer[0] != '\n');
186 //##########################################################
187 // Write captured data information to the xbmc log file
188 CLog::Log(LOGDEBUG,
189 "Xcddb::Recv Captured {0} bytes // Buffer= {1} bytes. Captured data follows on next "
190 "line\n{2}",
191 counter, str_buffer.size(), str_buffer);
194 return str_buffer;
197 //-------------------------------------------------------------------------------------------------------------------
198 bool Xcddb::queryCDinfo(CCdInfo* pInfo, int inexact_list_select)
200 if ( pInfo == NULL )
202 m_lastError = E_PARAMETER_WRONG;
203 return false;
206 uint32_t discid = pInfo->GetCddbDiscId();
209 //##########################################################
210 // Compose the cddb query string
211 std::string read_buffer = getInexactCommand(inexact_list_select);
212 if (read_buffer.empty())
214 CLog::Log(LOGERROR, "Xcddb::queryCDinfo_inexact_list_select Size of inexact_list_select are 0");
215 m_lastError = E_PARAMETER_WRONG;
216 return false;
220 //##########################################################
221 // Read the data from cddb
222 Recv(false); // Clear pending data on our connection
223 if (!Send(read_buffer.c_str()))
225 CLog::Log(LOGERROR, "Xcddb::queryCDinfo_inexact_list_select Error sending \"{}\"", read_buffer);
226 CLog::Log(LOGERROR, "Xcddb::queryCDinfo_inexact_list_select pInfo == NULL");
227 m_lastError = E_NETWORK_ERROR_SEND;
228 return false;
230 std::string recv_buffer = Recv(true);
231 m_lastError = atoi(recv_buffer.c_str());
232 switch(m_lastError)
234 case 210: //OK, CDDB database entry follows (until terminating marker)
235 // Cool, I got it ;-)
236 writeCacheFile( recv_buffer.c_str(), discid );
237 parseData(recv_buffer.c_str());
238 break;
240 case 401: //Specified CDDB entry not found.
241 case 402: //Server error.
242 case 403: //Database entry is corrupt.
243 case 409: //No handshake.
244 default:
245 CLog::Log(LOGERROR, "Xcddb::queryCDinfo_inexact_list_select Error: \"{}\"", recv_buffer);
246 return false;
250 //##########################################################
251 // Quit
252 if ( ! Send("quit") )
254 CLog::Log(LOGERROR, "Xcddb::queryCDinfo_inexact_list_select Error sending \"{}\"", "quit");
255 m_lastError = E_NETWORK_ERROR_SEND;
256 return false;
258 recv_buffer = Recv(false);
259 m_lastError = atoi(recv_buffer.c_str());
260 switch(m_lastError)
262 case 0: //By some reason, also 0 is a valid value. This is not documented, and might depend on that no string was found and atoi then returns 0
263 case 230: //Closing connection. Goodbye.
264 break;
266 case 530: //error, closing connection.
267 default:
268 CLog::Log(LOGERROR, "Xcddb::queryCDinfo_inexact_list_select Error: \"{}\"", recv_buffer);
269 return false;
273 //##########################################################
274 // Close connection
275 if ( !closeSocket() )
277 CLog::Log(LOGERROR, "Xcddb::queryCDinfo_inexact_list_select Error closing socket");
278 m_lastError = E_NETWORK_ERROR_SEND;
279 return false;
281 return true;
285 //-------------------------------------------------------------------------------------------------------------------
286 int Xcddb::getLastError() const
288 return m_lastError;
292 //-------------------------------------------------------------------------------------------------------------------
293 const char *Xcddb::getLastErrorText() const
295 switch (getLastError())
297 case E_TOC_INCORRECT:
298 return "TOC Incorrect";
299 break;
300 case E_NETWORK_ERROR_OPEN_SOCKET:
301 return "Error open Socket";
302 break;
303 case E_NETWORK_ERROR_SEND:
304 return "Error send PDU";
305 break;
306 case E_WAIT_FOR_INPUT:
307 return "Wait for Input";
308 break;
309 case E_PARAMETER_WRONG:
310 return "Error Parameter Wrong";
311 break;
312 case 202: return "No match found";
313 case 210: return "Found exact matches, list follows (until terminating marker)";
314 case 211: return "Found inexact matches, list follows (until terminating marker)";
315 case 401: return "Specified CDDB entry not found";
316 case 402: return "Server error";
317 case 403: return "Database entry is corrupt";
318 case 408: return "CGI environment error";
319 case 409: return "No handshake";
320 case 431: return "Handshake not successful, closing connection";
321 case 432: return "No connections allowed: permission denied";
322 case 433: return "No connections allowed: X users allowed, Y currently active";
323 case 434: return "No connections allowed: system load too high";
324 case 500: return "Command syntax error, command unknown, command unimplemented";
325 case 501: return "Illegal protocol level";
326 case 530: return "error, closing connection, Server error, server timeout";
327 default: return "Unknown Error";
332 //-------------------------------------------------------------------------------------------------------------------
333 int Xcddb::cddb_sum(int n)
335 int ret;
337 /* For backward compatibility this algorithm must not change */
339 ret = 0;
341 while (n > 0)
343 ret = ret + (n % 10);
344 n = n / 10;
347 return (ret);
350 //-------------------------------------------------------------------------------------------------------------------
351 uint32_t Xcddb::calc_disc_id(int tot_trks, toc cdtoc[])
353 int i = 0, t = 0, n = 0;
355 while (i < tot_trks)
358 n = n + cddb_sum((cdtoc[i].min * 60) + cdtoc[i].sec);
359 i++;
362 t = ((cdtoc[tot_trks].min * 60) + cdtoc[tot_trks].sec) - ((cdtoc[0].min * 60) + cdtoc[0].sec);
364 return ((n % 0xff) << 24 | t << 8 | tot_trks);
367 //-------------------------------------------------------------------------------------------------------------------
368 void Xcddb::addTitle(const char *buffer)
370 char value[2048];
371 int trk_nr = 0;
372 //TTITLEN
373 if (buffer[7] == '=')
374 { //Einstellig
375 trk_nr = buffer[6] - 47;
376 strncpy(value, buffer + 8, sizeof(value) - 1);
378 else if (buffer[8] == '=')
379 { //Zweistellig
380 trk_nr = ((buffer[6] - 48) * 10) + buffer[7] - 47;
381 strncpy(value, buffer + 9, sizeof(value) - 1);
383 else if (buffer[9] == '=')
384 { //Dreistellig
385 trk_nr = ((buffer[6] - 48) * 100) + ((buffer[7] - 48) * 10) + buffer[8] - 47;
386 strncpy(value, buffer + 10, sizeof(value) - 1);
388 else
390 return ;
392 value[sizeof(value) - 1] = '\0';
394 // track artist" / "track title
395 std::vector<std::string> values = StringUtils::Split(value, " / ");
396 if (values.size() > 1)
398 g_charsetConverter.unknownToUTF8(values[0]);
399 m_mapArtists[trk_nr] += values[0];
400 g_charsetConverter.unknownToUTF8(values[1]);
401 m_mapTitles[trk_nr] += values[1];
403 else if (!values.empty())
405 g_charsetConverter.unknownToUTF8(values[0]);
406 m_mapTitles[trk_nr] += values[0];
410 //-------------------------------------------------------------------------------------------------------------------
411 const std::string& Xcddb::getInexactCommand(int select) const
413 typedef std::map<int, std::string>::const_iterator iter;
414 iter i = m_mapInexact_cddb_command_list.find(select);
415 if (i == m_mapInexact_cddb_command_list.end())
416 return m_strNull;
417 return i->second;
420 //-------------------------------------------------------------------------------------------------------------------
421 const std::string& Xcddb::getInexactArtist(int select) const
423 typedef std::map<int, std::string>::const_iterator iter;
424 iter i = m_mapInexact_artist_list.find(select);
425 if (i == m_mapInexact_artist_list.end())
426 return m_strNull;
427 return i->second;
430 //-------------------------------------------------------------------------------------------------------------------
431 const std::string& Xcddb::getInexactTitle(int select) const
433 typedef std::map<int, std::string>::const_iterator iter;
434 iter i = m_mapInexact_title_list.find(select);
435 if (i == m_mapInexact_title_list.end())
436 return m_strNull;
437 return i->second;
440 //-------------------------------------------------------------------------------------------------------------------
441 const std::string& Xcddb::getTrackArtist(int track) const
443 typedef std::map<int, std::string>::const_iterator iter;
444 iter i = m_mapArtists.find(track);
445 if (i == m_mapArtists.end())
446 return m_strNull;
447 return i->second;
450 //-------------------------------------------------------------------------------------------------------------------
451 const std::string& Xcddb::getTrackTitle(int track) const
453 typedef std::map<int, std::string>::const_iterator iter;
454 iter i = m_mapTitles.find(track);
455 if (i == m_mapTitles.end())
456 return m_strNull;
457 return i->second;
460 //-------------------------------------------------------------------------------------------------------------------
461 void Xcddb::getDiskTitle(std::string& strdisk_title) const
463 strdisk_title = m_strDisk_title;
466 //-------------------------------------------------------------------------------------------------------------------
467 void Xcddb::getDiskArtist(std::string& strdisk_artist) const
469 strdisk_artist = m_strDisk_artist;
472 //-------------------------------------------------------------------------------------------------------------------
473 void Xcddb::parseData(const char *buffer)
475 //writeLog("parseData Start");
477 std::map<std::string, std::string> keywords;
478 std::list<std::string> keywordsOrder; // remember order of keywords as it appears in data received from CDDB
480 // Collect all the keywords and put them in map.
481 // Multiple occurrences of the same keyword indicate that
482 // the data contained on those lines should be concatenated
483 char *line;
484 const char trenner[3] = {'\n', '\r', '\0'};
485 strtok(const_cast<char*>(buffer), trenner); // skip first line
486 while ((line = strtok(0, trenner)))
488 // Lines that begin with # are comments, should be ignored
489 if (line[0] != '#')
491 char *s = strstr(line, "=");
492 if (s != NULL)
494 std::string strKeyword(line, s - line);
495 StringUtils::TrimRight(strKeyword);
497 std::string strValue(s+1);
498 StringUtils::Replace(strValue, "\\n", "\n");
499 StringUtils::Replace(strValue, "\\t", "\t");
500 StringUtils::Replace(strValue, "\\\\", "\\");
502 std::map<std::string, std::string>::const_iterator it = keywords.find(strKeyword);
503 if (it != keywords.end())
504 strValue = it->second + strValue; // keyword occurred before, concatenate
505 else
506 keywordsOrder.push_back(strKeyword);
508 keywords[strKeyword] = strValue;
513 // parse keywords
514 for (const std::string& strKeyword : keywordsOrder)
516 std::string strValue = keywords[strKeyword];
518 //! @todo STRING_CLEANUP
519 if (strKeyword == "DTITLE")
521 // DTITLE may contain artist and disc title, separated with " / ",
522 // for example: DTITLE=Modern Talking / Album: Victory (The 11th Album)
523 bool found = false;
524 for (int i = 0; i < (int)strValue.size() - 2; i++)
526 if (strValue[i] == ' ' && strValue[i + 1] == '/' && strValue[i + 2] == ' ')
528 m_strDisk_artist = TrimToUTF8(strValue.substr(0, i));
529 m_strDisk_title = TrimToUTF8(strValue.substr(i+3));
530 found = true;
531 break;
535 if (!found)
536 m_strDisk_title = TrimToUTF8(strValue);
538 else if (strKeyword == "DYEAR")
539 m_strYear = TrimToUTF8(strValue);
540 else if (strKeyword== "DGENRE")
541 m_strGenre = TrimToUTF8(strValue);
542 else if (StringUtils::StartsWith(strKeyword, "TTITLE"))
543 addTitle((strKeyword + "=" + strValue).c_str());
544 else if (strKeyword == "EXTD")
546 const std::string& strExtd(strValue);
548 if (m_strYear.empty())
550 // Extract Year from extended info
551 // as a fallback
552 size_t iPos = strExtd.find("YEAR: ");
553 if (iPos != std::string::npos) // You never know if you really get UTF-8 strings from cddb
554 g_charsetConverter.unknownToUTF8(strExtd.substr(iPos + 6, 4), m_strYear);
557 if (m_strGenre.empty())
559 // Extract ID3 Genre
560 // as a fallback
561 size_t iPos = strExtd.find("ID3G: ");
562 if (iPos != std::string::npos)
564 std::string strGenre = strExtd.substr(iPos + 5, 4);
565 StringUtils::TrimLeft(strGenre);
566 if (StringUtils::IsNaturalNumber(strGenre))
568 int iGenre = strtol(strGenre.c_str(), NULL, 10);
569 m_strGenre = TagLib::ID3v1::genre(iGenre).to8Bit(true);
574 else if (StringUtils::StartsWith(strKeyword, "EXTT"))
575 addExtended((strKeyword + "=" + strValue).c_str());
578 //writeLog("parseData Ende");
581 //-------------------------------------------------------------------------------------------------------------------
582 void Xcddb::addExtended(const char *buffer)
584 char value[2048];
585 int trk_nr = 0;
586 //TTITLEN
587 if (buffer[5] == '=')
588 { //Einstellig
589 trk_nr = buffer[4] - 47;
590 strncpy(value, buffer + 6, sizeof(value) - 1);
592 else if (buffer[6] == '=')
593 { //Zweistellig
594 trk_nr = ((buffer[4] - 48) * 10) + buffer[5] - 47;
595 strncpy(value, buffer + 7, sizeof(value) - 1);
597 else if (buffer[7] == '=')
598 { //Dreistellig
599 trk_nr = ((buffer[4] - 48) * 100) + ((buffer[5] - 48) * 10) + buffer[6] - 47;
600 strncpy(value, buffer + 8, sizeof(value) - 1);
602 else
604 return ;
606 value[sizeof(value) - 1] = '\0';
608 std::string strValue;
609 std::string strValueUtf8=value;
610 // You never know if you really get UTF-8 strings from cddb
611 g_charsetConverter.unknownToUTF8(strValueUtf8, strValue);
612 m_mapExtended_track[trk_nr] = strValue;
615 //-------------------------------------------------------------------------------------------------------------------
616 const std::string& Xcddb::getTrackExtended(int track) const
618 typedef std::map<int, std::string>::const_iterator iter;
619 iter i = m_mapExtended_track.find(track);
620 if (i == m_mapExtended_track.end())
621 return m_strNull;
622 return i->second;
625 //-------------------------------------------------------------------------------------------------------------------
626 void Xcddb::addInexactList(const char *list)
629 211 Found inexact matches, list follows (until terminating `.')
630 soundtrack bf0cf90f Modern Talking / Victory - The 11th Album
631 rock c90cf90f Modern Talking / Album: Victory (The 11th Album)
632 misc de0d020f Modern Talking / Ready for the victory
633 rock e00d080f Modern Talking / Album: Victory (The 11th Album)
634 rock c10d150f Modern Talking / Victory (The 11th Album)
639 m_mapInexact_cddb_command_list;
640 m_mapInexact_artist_list;
641 m_mapInexact_title_list;
643 int start = 0;
644 int end = 0;
645 bool found = false;
646 int line_counter = 0;
647 // //writeLog("addInexactList Start");
648 for (unsigned int i = 0;i < strlen(list);i++)
650 if (list[i] == '\n')
652 end = i;
653 found = true;
655 if (found)
657 if (line_counter > 0)
659 addInexactListLine(line_counter, list + start, end - start - 1);
661 start = i + 1;
662 line_counter++;
663 found = false;
666 // //writeLog("addInexactList End");
669 //-------------------------------------------------------------------------------------------------------------------
670 void Xcddb::addInexactListLine(int line_cnt, const char *line, int len)
672 // rock c90cf90f Modern Talking / Album: Victory (The 11th Album)
673 int search4 = 0;
674 char genre[100]; // 0
675 char discid[10]; // 1
676 char artist[1024]; // 2
677 char title[1024];
678 char cddb_command[1024];
679 int start = 0;
680 // //writeLog("addInexactListLine Start");
681 for (int i = 0;i < len;i++)
683 switch (search4)
685 case 0:
686 if (line[i] == ' ')
688 strncpy(genre, line, i);
689 genre[i] = 0x00;
690 search4 = 1;
691 start = i + 1;
693 break;
694 case 1:
695 if (line[i] == ' ')
697 strncpy(discid, line + start, i - start);
698 discid[i - start] = 0x00;
699 search4 = 2;
700 start = i + 1;
702 break;
703 case 2:
704 if (i + 2 <= len && line[i] == ' ' && line[i + 1] == '/' && line[i + 2] == ' ')
706 strncpy(artist, line + start, i - start);
707 artist[i - start] = 0x00;
708 strncpy(title, line + (i + 3), len - (i + 3));
709 title[len - (i + 3)] = 0x00;
711 break;
714 snprintf(cddb_command, sizeof(cddb_command), "cddb read %s %s", genre, discid);
716 m_mapInexact_cddb_command_list[line_cnt] = cddb_command;
718 std::string strArtist=artist;
719 // You never know if you really get UTF-8 strings from cddb
720 g_charsetConverter.unknownToUTF8(artist, strArtist);
721 m_mapInexact_artist_list[line_cnt] = strArtist;
723 std::string strTitle=title;
724 // You never know if you really get UTF-8 strings from cddb
725 g_charsetConverter.unknownToUTF8(title, strTitle);
726 m_mapInexact_title_list[line_cnt] = strTitle;
727 // char log_string[1024];
728 // snprintf(log_string, sizeof(log_string), "%u: %s - %s",line_cnt,artist,title);
729 // //writeLog(log_string);
730 // //writeLog("addInexactListLine End");
733 //-------------------------------------------------------------------------------------------------------------------
734 void Xcddb::setCDDBIpAddress(const std::string& ip_address)
736 m_cddb_ip_address = ip_address;
739 //-------------------------------------------------------------------------------------------------------------------
740 void Xcddb::setCacheDir(const std::string& pCacheDir )
742 cCacheDir = pCacheDir;
745 //-------------------------------------------------------------------------------------------------------------------
746 bool Xcddb::queryCache( uint32_t discid )
748 if (cCacheDir.empty())
749 return false;
751 XFILE::CFile file;
752 if (file.Open(GetCacheFile(discid)))
754 // Got a cachehit
755 char buffer[4096];
756 file.Read(buffer, 4096);
757 file.Close();
758 parseData( buffer );
759 return true;
762 return false;
765 //-------------------------------------------------------------------------------------------------------------------
766 bool Xcddb::writeCacheFile( const char* pBuffer, uint32_t discid )
768 if (cCacheDir.empty())
769 return false;
771 XFILE::CFile file;
772 if (file.OpenForWrite(GetCacheFile(discid), true))
774 const bool ret = ( (size_t) file.Write((const void*)pBuffer, strlen(pBuffer) + 1) == strlen(pBuffer) + 1);
775 file.Close();
776 return ret;
779 return false;
782 //-------------------------------------------------------------------------------------------------------------------
783 bool Xcddb::isCDCached( int nr_of_tracks, toc cdtoc[] )
785 if (cCacheDir.empty())
786 return false;
788 return XFILE::CFile::Exists(GetCacheFile(calc_disc_id(nr_of_tracks, cdtoc)));
791 //-------------------------------------------------------------------------------------------------------------------
792 const std::string& Xcddb::getYear() const
794 return m_strYear;
797 //-------------------------------------------------------------------------------------------------------------------
798 const std::string& Xcddb::getGenre() const
800 return m_strGenre;
803 //-------------------------------------------------------------------------------------------------------------------
804 bool Xcddb::queryCDinfo(CCdInfo* pInfo)
806 if ( pInfo == NULL )
808 CLog::Log(LOGERROR, "Xcddb::queryCDinfo pInfo == NULL");
809 m_lastError = E_PARAMETER_WRONG;
810 return false;
813 int lead_out = pInfo->GetTrackCount();
814 int real_track_count = pInfo->GetTrackCount();
815 uint32_t discid = pInfo->GetCddbDiscId();
816 unsigned long frames[100];
819 //##########################################################
821 if ( queryCache(discid) )
823 CLog::Log(LOGDEBUG, "Xcddb::queryCDinfo discid [{:08x}] already cached", discid);
824 return true;
827 //##########################################################
829 for (int i = 0;i < lead_out;i++)
831 frames[i] = pInfo->GetTrackInformation( i + 1 ).nFrames;
832 if (i > 0 && frames[i] < frames[i - 1])
834 CLog::Log(LOGERROR, "Xcddb::queryCDinfo E_TOC_INCORRECT");
835 m_lastError = E_TOC_INCORRECT;
836 return false;
839 unsigned long complete_length = pInfo->GetDiscLength();
842 //##########################################################
843 // Open socket to cddb database
844 if ( !openSocket() )
846 CLog::Log(LOGERROR, "Xcddb::queryCDinfo Error opening socket");
847 m_lastError = E_NETWORK_ERROR_OPEN_SOCKET;
848 return false;
850 std::string recv_buffer = Recv(false);
851 m_lastError = atoi(recv_buffer.c_str());
852 switch(m_lastError)
854 case 200: //OK, read/write allowed
855 case 201: //OK, read only
856 break;
858 case 432: //No connections allowed: permission denied
859 case 433: //No connections allowed: X users allowed, Y currently active
860 case 434: //No connections allowed: system load too high
861 default:
862 CLog::Log(LOGERROR, "Xcddb::queryCDinfo Error: \"{}\"", recv_buffer);
863 return false;
867 //##########################################################
868 // Send the Hello message
869 std::string version = CSysInfo::GetVersion();
870 std::string lcAppName = CCompileInfo::GetAppName();
871 StringUtils::ToLower(lcAppName);
872 if (version.find(' ') != std::string::npos)
873 version.resize(version.find(' '));
874 std::string strGreeting = "cddb hello " + lcAppName + " kodi.tv " + CCompileInfo::GetAppName() + " " + version;
875 if ( ! Send(strGreeting.c_str()) )
877 CLog::Log(LOGERROR, "Xcddb::queryCDinfo Error sending \"{}\"", strGreeting);
878 m_lastError = E_NETWORK_ERROR_SEND;
879 return false;
881 recv_buffer = Recv(false);
882 m_lastError = atoi(recv_buffer.c_str());
883 switch(m_lastError)
885 case 200: //Handshake successful
886 case 402: //Already shook hands
887 break;
889 case 431: //Handshake not successful, closing connection
890 default:
891 CLog::Log(LOGERROR, "Xcddb::queryCDinfo Error: \"{}\"", recv_buffer);
892 return false;
896 //##########################################################
897 // Set CDDB protocol-level to 5
898 if ( ! Send("proto 5"))
900 CLog::Log(LOGERROR, "Xcddb::queryCDinfo Error sending \"{}\"", "proto 5");
901 m_lastError = E_NETWORK_ERROR_SEND;
902 return false;
904 recv_buffer = Recv(false);
905 m_lastError = atoi(recv_buffer.c_str());
906 switch(m_lastError)
908 case 200: //CDDB protocol level: current cur_level, supported supp_level
909 case 201: //OK, protocol version now: cur_level
910 case 502: //Protocol level already cur_level
911 break;
913 case 501: //Illegal protocol level.
914 default:
915 CLog::Log(LOGERROR, "Xcddb::queryCDinfo Error: \"{}\"", recv_buffer);
916 return false;
920 //##########################################################
921 // Compose the cddb query string
922 char query_buffer[1024];
923 strcpy(query_buffer, "");
924 strcat(query_buffer, "cddb query");
926 char tmp_buffer[256];
927 snprintf(tmp_buffer, sizeof(tmp_buffer), " %08x", discid);
928 strcat(query_buffer, tmp_buffer);
931 char tmp_buffer[256];
932 snprintf(tmp_buffer, sizeof(tmp_buffer), " %i", real_track_count);
933 strcat(query_buffer, tmp_buffer);
935 for (int i = 0;i < lead_out;i++)
937 char tmp_buffer[256];
938 snprintf(tmp_buffer, sizeof(tmp_buffer), " %lu", frames[i]);
939 strcat(query_buffer, tmp_buffer);
942 char tmp_buffer[256];
943 snprintf(tmp_buffer, sizeof(tmp_buffer), " %lu", complete_length);
944 strcat(query_buffer, tmp_buffer);
948 //##########################################################
949 // Query for matches
950 if ( ! Send(query_buffer))
952 CLog::Log(LOGERROR, "Xcddb::queryCDinfo Error sending \"{}\"", query_buffer);
953 m_lastError = E_NETWORK_ERROR_SEND;
954 return false;
956 // 200 rock d012180e Soundtrack / Hackers
957 std::string read_buffer;
958 recv_buffer = Recv(false);
959 m_lastError = atoi(recv_buffer.c_str());
960 switch(m_lastError)
962 case 200: //Found exact match
963 strtok(const_cast<char *>(recv_buffer.c_str()), " ");
964 read_buffer = StringUtils::Format("cddb read {} {:08x}", strtok(NULL, " "), discid);
965 break;
967 case 210: //Found exact matches, list follows (until terminating marker)
968 case 211: //Found inexact matches, list follows (until terminating marker)
970 soundtrack bf0cf90f Modern Talking / Victory - The 11th Album
971 rock c90cf90f Modern Talking / Album: Victory (The 11th Album)
972 misc de0d020f Modern Talking / Ready for the victory
973 rock e00d080f Modern Talking / Album: Victory (The 11th Album)
974 rock c10d150f Modern Talking / Victory (The 11th Album)
977 recv_buffer += Recv(true);
978 addInexactList(recv_buffer.c_str());
979 m_lastError=E_WAIT_FOR_INPUT;
980 return false; //This is actually good. The calling method will handle this
982 case 202: //No match found
983 CLog::Log(
984 LOGINFO,
985 "Xcddb::queryCDinfo No match found in CDDB database when doing the query shown below:\n{}",
986 query_buffer);
987 [[fallthrough]];
988 case 403: //Database entry is corrupt
989 case 409: //No handshake
990 default:
991 CLog::Log(LOGERROR, "Xcddb::queryCDinfo Error: \"{}\"", recv_buffer);
992 return false;
996 //##########################################################
997 // Read the data from cddb
998 if ( !Send(read_buffer.c_str()) )
1000 CLog::Log(LOGERROR, "Xcddb::queryCDinfo Error sending \"{}\"", read_buffer);
1001 m_lastError = E_NETWORK_ERROR_SEND;
1002 return false;
1004 recv_buffer = Recv(true);
1005 m_lastError = atoi(recv_buffer.c_str());
1006 switch(m_lastError)
1008 case 210: //OK, CDDB database entry follows (until terminating marker)
1009 // Cool, I got it ;-)
1010 writeCacheFile( recv_buffer.c_str(), discid );
1011 parseData(recv_buffer.c_str());
1012 break;
1014 case 401: //Specified CDDB entry not found.
1015 case 402: //Server error.
1016 case 403: //Database entry is corrupt.
1017 case 409: //No handshake.
1018 default:
1019 CLog::Log(LOGERROR, "Xcddb::queryCDinfo Error: \"{}\"", recv_buffer);
1020 return false;
1024 //##########################################################
1025 // Quit
1026 if ( ! Send("quit") )
1028 CLog::Log(LOGERROR, "Xcddb::queryCDinfo Error sending \"{}\"", "quit");
1029 m_lastError = E_NETWORK_ERROR_SEND;
1030 return false;
1032 recv_buffer = Recv(false);
1033 m_lastError = atoi(recv_buffer.c_str());
1034 switch(m_lastError)
1036 case 0: //By some reason, also 0 is a valid value. This is not documented, and might depend on that no string was found and atoi then returns 0
1037 case 230: //Closing connection. Goodbye.
1038 break;
1040 case 530: //error, closing connection.
1041 default:
1042 CLog::Log(LOGERROR, "Xcddb::queryCDinfo Error: \"{}\"", recv_buffer);
1043 return false;
1047 //##########################################################
1048 // Close connection
1049 if ( !closeSocket() )
1051 CLog::Log(LOGERROR, "Xcddb::queryCDinfo Error closing socket");
1052 m_lastError = E_NETWORK_ERROR_SEND;
1053 return false;
1055 return true;
1058 //-------------------------------------------------------------------------------------------------------------------
1059 bool Xcddb::isCDCached( CCdInfo* pInfo )
1061 if (cCacheDir.empty())
1062 return false;
1063 if ( pInfo == NULL )
1064 return false;
1066 return XFILE::CFile::Exists(GetCacheFile(pInfo->GetCddbDiscId()));
1069 std::string Xcddb::GetCacheFile(uint32_t disc_id) const
1071 std::string strFileName;
1072 strFileName = StringUtils::Format("{:x}.cddb", disc_id);
1073 return URIUtils::AddFileToFolder(cCacheDir, strFileName);
1076 std::string Xcddb::TrimToUTF8(const std::string &untrimmedText)
1078 std::string text(untrimmedText);
1079 StringUtils::Trim(text);
1080 // You never know if you really get UTF-8 strings from cddb
1081 g_charsetConverter.unknownToUTF8(text);
1082 return text;