[cstdstring] removal of Trim/TrimLeft/TrimRight
[vuplus_xbmc] / xbmc / network / cddb.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20 //-----------------------------------------------------------------------------
21 // File: cddb.cpp
22 //
23 // Desc: Make a connection to the CDDB database
24 //
25 // Hist: 00.00.01
26 //
27 //-----------------------------------------------------------------------------
28
29 #include "system.h"
30
31 #ifdef HAS_DVD_DRIVE
32
33 #include <taglib/id3v1genres.h>
34 #include "cddb.h"
35 #include "network/DNSNameCache.h"
36 #include "settings/AdvancedSettings.h"
37 #include "utils/StringUtils.h"
38 #include "utils/URIUtils.h"
39 #include "filesystem/File.h"
40 #include "GUIInfoManager.h"
41 #include "utils/CharsetConverter.h"
42 #include "utils/log.h"
43
44 #include <sys/socket.h>
45 #include <netinet/in.h>
46 #include <netdb.h>
47
48 using namespace std;
49 using namespace MEDIA_DETECT;
50 using namespace AUTOPTR;
51 using namespace CDDB;
52
53 //-------------------------------------------------------------------------------------------------------------------
54 Xcddb::Xcddb()
55     : m_cddb_socket(INVALID_SOCKET)
56 {
57   m_lastError = 0;
58   m_cddb_ip_adress = g_advancedSettings.m_cddbAddress;
59   cCacheDir = "";
60   m_strNull = "";
61 }
62
63 //-------------------------------------------------------------------------------------------------------------------
64 Xcddb::~Xcddb()
65 {
66   closeSocket();
67 }
68
69 //-------------------------------------------------------------------------------------------------------------------
70 bool Xcddb::openSocket()
71 {
72   char     namebuf[NI_MAXHOST], portbuf[NI_MAXSERV];
73   struct   addrinfo hints;
74   struct   addrinfo *result, *addr;
75   char     service[33];
76   int      res;
77   SOCKET   fd = INVALID_SOCKET;
78
79   memset(&hints, 0, sizeof(hints));
80   hints.ai_family   = AF_UNSPEC;
81   hints.ai_socktype = SOCK_STREAM;
82   hints.ai_protocol = IPPROTO_TCP;
83   sprintf(service, "%d", CDDB_PORT);
84
85   res = getaddrinfo(m_cddb_ip_adress.c_str(), service, &hints, &result);
86   if(res)
87   {
88     CLog::Log(LOGERROR, "Xcddb::openSocket - failed to lookup %s with error %s", m_cddb_ip_adress.c_str(), gai_strerror(res));
89     res = getaddrinfo("130.179.31.49", service, &hints, &result);
90     if(res)
91       return false;
92   }
93
94   for(addr = result; addr; addr = addr->ai_next)
95   {
96     if(getnameinfo(addr->ai_addr, addr->ai_addrlen, namebuf, sizeof(namebuf), portbuf, sizeof(portbuf),NI_NUMERICHOST))
97     {
98       strcpy(namebuf, "[unknown]");
99       strcpy(portbuf, "[unknown]");
100         }
101     CLog::Log(LOGDEBUG, "Xcddb::openSocket - connecting to: %s:%s ...", namebuf, portbuf);
102
103     fd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
104     if(fd == INVALID_SOCKET)
105       continue;
106
107     if(connect(fd, addr->ai_addr, addr->ai_addrlen) != SOCKET_ERROR)
108       break;
109
110     closesocket(fd);
111     fd = INVALID_SOCKET;
112   }
113
114   freeaddrinfo(result);
115   if(fd == INVALID_SOCKET)
116   {
117     CLog::Log(LOGERROR, "Xcddb::openSocket - failed to connect to cddb");
118     return false;
119   }
120
121   m_cddb_socket.attach(fd);
122   return true;
123 }
124
125 //-------------------------------------------------------------------------------------------------------------------
126 bool Xcddb::closeSocket()
127 {
128   if ( m_cddb_socket.isValid() )
129   {
130     m_cddb_socket.reset();
131   }
132   return true;
133 }
134
135 //-------------------------------------------------------------------------------------------------------------------
136 bool Xcddb::Send( const void *buffer, int bytes )
137 {
138   auto_aptr<char> tmp_buffer (new char[bytes + 10]);
139   strcpy(tmp_buffer.get(), (const char*)buffer);
140   tmp_buffer.get()[bytes] = '.';
141   tmp_buffer.get()[bytes + 1] = 0x0d;
142   tmp_buffer.get()[bytes + 2] = 0x0a;
143   tmp_buffer.get()[bytes + 3] = 0x00;
144   int iErr = send((SOCKET)m_cddb_socket, (const char*)tmp_buffer.get(), bytes + 3, 0);
145   if (iErr <= 0)
146   {
147     return false;
148   }
149   return true;
150 }
151
152 //-------------------------------------------------------------------------------------------------------------------
153 bool Xcddb::Send( const char *buffer)
154 {
155   int iErr = Send(buffer, strlen(buffer));
156   if (iErr <= 0)
157   {
158     return false;
159   }
160   return true;
161 }
162
163 //-------------------------------------------------------------------------------------------------------------------
164 string Xcddb::Recv(bool wait4point)
165 {
166   char tmpbuffer[1];
167   char prevChar;
168   int counter = 0;
169   string str_buffer;
170
171
172   //##########################################################
173   // Read the buffer. Character by character
174   tmpbuffer[0]=0;
175   do
176   {
177     int lenRead;
178
179     prevChar=tmpbuffer[0];
180     lenRead = recv((SOCKET)m_cddb_socket, (char*) & tmpbuffer, 1, 0);
181
182     //Check if there was any error reading the buffer
183     if(lenRead == 0 || lenRead == SOCKET_ERROR  || WSAGetLastError() == WSAECONNRESET)
184     {
185       CLog::Log(LOGERROR, "Xcddb::Recv Error reading buffer. lenRead = [%d] and WSAGetLastError = [%d]", lenRead, WSAGetLastError());
186       break;
187     }
188
189     //Write received data to the return string
190     str_buffer.push_back(tmpbuffer[0]);
191     counter++;
192   }while(wait4point ? prevChar != '\n' || tmpbuffer[0] != '.' : tmpbuffer[0] != '\n');
193
194
195   //##########################################################
196   // Write captured data information to the xbmc log file
197   CLog::Log(LOGDEBUG,"Xcddb::Recv Captured %d bytes // Buffer= %"PRIdS" bytes. Captured data follows on next line\n%s", counter, str_buffer.size(),(char *)str_buffer.c_str());
198
199
200   return str_buffer;
201 }
202
203 //-------------------------------------------------------------------------------------------------------------------
204 bool Xcddb::queryCDinfo(CCdInfo* pInfo, int inexact_list_select)
205 {
206   if ( pInfo == NULL )
207   {
208     m_lastError = E_PARAMETER_WRONG;
209     return false;
210   }
211
212   uint32_t discid = pInfo->GetCddbDiscId();
213
214
215   //##########################################################
216   // Compose the cddb query string
217   CStdString read_buffer = getInexactCommand(inexact_list_select);
218   if (read_buffer.size() == 0)
219   {
220     CLog::Log(LOGERROR, "Xcddb::queryCDinfo_inexaxt_list_select Size of inexaxt_list_select are 0");
221     m_lastError = E_PARAMETER_WRONG;
222     return false;
223   }
224
225
226   //##########################################################
227   // Read the data from cddb
228   Recv(false); //erstmal den Müll abholen
229   if ( !Send(read_buffer) )
230   {
231     CLog::Log(LOGERROR, "Xcddb::queryCDinfo_inexaxt_list_select Error sending \"%s\"", read_buffer.c_str());
232     CLog::Log(LOGERROR, "Xcddb::queryCDinfo_inexaxt_list_select pInfo == NULL");
233     m_lastError = E_NETWORK_ERROR_SEND;
234     return false;
235   }
236   CStdString recv_buffer = Recv(true);
237   m_lastError = atoi(recv_buffer.c_str());
238   switch(m_lastError)
239   {
240   case 210: //OK, CDDB database entry follows (until terminating marker)
241     // Cool, I got it ;-)
242     writeCacheFile( recv_buffer.c_str(), discid );
243     parseData(recv_buffer.c_str());
244     break;
245
246   case 401: //Specified CDDB entry not found.
247   case 402: //Server error.
248   case 403: //Database entry is corrupt.
249   case 409: //No handshake.
250   default:
251     CLog::Log(LOGERROR, "Xcddb::queryCDinfo_inexaxt_list_select Error: \"%s\"", recv_buffer.c_str());
252     return false;
253   }
254
255
256   //##########################################################
257   // Quit
258   if ( ! Send("quit") )
259   {
260     CLog::Log(LOGERROR, "Xcddb::queryCDinfo_inexaxt_list_select Error sending \"%s\"", "quit");
261     m_lastError = E_NETWORK_ERROR_SEND;
262     return false;
263   }
264   recv_buffer = Recv(false);
265   m_lastError = atoi(recv_buffer.c_str());
266   switch(m_lastError)
267   {
268   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
269   case 230: //Closing connection.  Goodbye.
270     break;
271
272   case 530: //error, closing connection.
273   default:
274     CLog::Log(LOGERROR, "Xcddb::queryCDinfo_inexaxt_list_select Error: \"%s\"", recv_buffer.c_str());
275     return false;
276   }
277
278
279   //##########################################################
280   // Close connection
281   if ( !closeSocket() )
282   {
283     CLog::Log(LOGERROR, "Xcddb::queryCDinfo_inexaxt_list_select Error closing socket");
284     m_lastError = E_NETWORK_ERROR_SEND;
285     return false;
286   }
287   return true;
288 }
289
290
291 //-------------------------------------------------------------------------------------------------------------------
292 int Xcddb::getLastError() const
293 {
294   return m_lastError;
295 }
296
297
298 //-------------------------------------------------------------------------------------------------------------------
299 const char *Xcddb::getLastErrorText() const
300 {
301   switch (getLastError())
302   {
303   case E_TOC_INCORRECT:
304     return "TOC Incorrect";
305     break;
306   case E_NETWORK_ERROR_OPEN_SOCKET:
307     return "Error open Socket";
308     break;
309   case E_NETWORK_ERROR_SEND:
310     return "Error send PDU";
311     break;
312   case E_WAIT_FOR_INPUT:
313     return "Wait for Input";
314     break;
315   case E_PARAMETER_WRONG:
316     return "Error Parameter Wrong";
317     break;
318   case 202: return "No match found";
319   case 210: return "Found exact matches, list follows (until terminating marker)";
320   case 211: return "Found inexact matches, list follows (until terminating marker)";
321   case 401: return "Specified CDDB entry not found";
322   case 402: return "Server error";
323   case 403: return "Database entry is corrupt";
324   case 408: return "CGI environment error";
325   case 409: return "No handshake";
326   case 431: return "Handshake not successful, closing connection";
327   case 432: return "No connections allowed: permission denied";
328   case 433: return "No connections allowed: X users allowed, Y currently active";
329   case 434: return "No connections allowed: system load too high";
330   case 500: return "Command syntax error, command unknown, command unimplemented";
331   case 501: return "Illegal protocol level";
332   case 530: return "error, closing connection, Server error, server timeout";
333   default:  return "Unknown Error";
334   }
335 }
336
337
338 //-------------------------------------------------------------------------------------------------------------------
339 int Xcddb::cddb_sum(int n)
340 {
341   int ret;
342
343   /* For backward compatibility this algorithm must not change */
344
345   ret = 0;
346
347   while (n > 0)
348   {
349     ret = ret + (n % 10);
350     n = n / 10;
351   }
352
353   return (ret);
354 }
355
356 //-------------------------------------------------------------------------------------------------------------------
357 uint32_t Xcddb::calc_disc_id(int tot_trks, toc cdtoc[])
358 {
359   int i = 0, t = 0, n = 0;
360
361   while (i < tot_trks)
362   {
363
364     n = n + cddb_sum((cdtoc[i].min * 60) + cdtoc[i].sec);
365     i++;
366   }
367
368   t = ((cdtoc[tot_trks].min * 60) + cdtoc[tot_trks].sec) - ((cdtoc[0].min * 60) + cdtoc[0].sec);
369
370   return ((n % 0xff) << 24 | t << 8 | tot_trks);
371 }
372
373 //-------------------------------------------------------------------------------------------------------------------
374 void Xcddb::addTitle(const char *buffer)
375 {
376   char value[2048];
377   int trk_nr = 0;
378   //TTITLEN
379   if (buffer[7] == '=')
380   { //Einstellig
381     trk_nr = buffer[6] - 47;
382     strcpy(value, buffer + 8);
383   }
384   else if (buffer[8] == '=')
385   { //Zweistellig
386     trk_nr = ((buffer[6] - 48) * 10) + buffer[7] - 47;
387     strcpy(value, buffer + 9);
388   }
389   else if (buffer[9] == '=')
390   { //Dreistellig
391     trk_nr = ((buffer[6] - 48) * 100) + ((buffer[7] - 48) * 10) + buffer[8] - 47;
392     strcpy(value, buffer + 10);
393   }
394   else
395   {
396     return ;
397   }
398
399   // track artist" / "track title
400   CStdString strValue = value;
401   CStdStringArray values;
402   StringUtils::SplitString(value, " / ", values);
403   if (values.size() > 1)
404   {
405     g_charsetConverter.unknownToUTF8(values[0]);
406     m_mapArtists[trk_nr] += values[0];
407     g_charsetConverter.unknownToUTF8(values[1]);
408     m_mapTitles[trk_nr] += values[1];
409   }
410   else
411   {
412     g_charsetConverter.unknownToUTF8(values[0]);
413     m_mapTitles[trk_nr] += values[0];
414   }
415 }
416
417 //-------------------------------------------------------------------------------------------------------------------
418 const CStdString& Xcddb::getInexactCommand(int select) const
419 {
420   typedef map<int, CStdString>::const_iterator iter;
421   iter i = m_mapInexact_cddb_command_list.find(select);
422   if (i == m_mapInexact_cddb_command_list.end())
423     return m_strNull;
424   return i->second;
425 }
426
427 //-------------------------------------------------------------------------------------------------------------------
428 const CStdString& Xcddb::getInexactArtist(int select) const
429 {
430   typedef map<int, CStdString>::const_iterator iter;
431   iter i = m_mapInexact_artist_list.find(select);
432   if (i == m_mapInexact_artist_list.end())
433     return m_strNull;
434   return i->second;
435 }
436
437 //-------------------------------------------------------------------------------------------------------------------
438 const CStdString& Xcddb::getInexactTitle(int select) const
439 {
440   typedef map<int, CStdString>::const_iterator iter;
441   iter i = m_mapInexact_title_list.find(select);
442   if (i == m_mapInexact_title_list.end())
443     return m_strNull;
444   return i->second;
445 }
446
447 //-------------------------------------------------------------------------------------------------------------------
448 const CStdString& Xcddb::getTrackArtist(int track) const
449 {
450   typedef map<int, CStdString>::const_iterator iter;
451   iter i = m_mapArtists.find(track);
452   if (i == m_mapArtists.end())
453     return m_strNull;
454   return i->second;
455 }
456
457 //-------------------------------------------------------------------------------------------------------------------
458 const CStdString& Xcddb::getTrackTitle(int track) const
459 {
460   typedef map<int, CStdString>::const_iterator iter;
461   iter i = m_mapTitles.find(track);
462   if (i == m_mapTitles.end())
463     return m_strNull;
464   return i->second;
465 }
466
467 //-------------------------------------------------------------------------------------------------------------------
468 void Xcddb::getDiskTitle(CStdString& strdisk_title) const
469 {
470   strdisk_title = m_strDisk_title;
471 }
472
473 //-------------------------------------------------------------------------------------------------------------------
474 void Xcddb::getDiskArtist(CStdString& strdisk_artist) const
475 {
476   strdisk_artist = m_strDisk_artist;
477 }
478
479 //-------------------------------------------------------------------------------------------------------------------
480 void Xcddb::parseData(const char *buffer)
481 {
482   //writeLog("parseData Start");
483
484   std::map<CStdString, CStdString> keywords;
485   std::list<CStdString> keywordsOrder; // remember order of keywords as it appears in data received from CDDB
486
487   // Collect all the keywords and put them in map. 
488   // Multiple occurrences of the same keyword indicate that 
489   // the data contained on those lines should be concatenated
490   char *line;
491   const char trenner[3] = {'\n', '\r', '\0'};
492   strtok((char*)buffer, trenner); // skip first line
493   while ((line = strtok(0, trenner)))
494   {
495     // Lines that begin with # are comments, should be ignored
496     if (line[0] != '#')
497     {
498       char *s = strstr(line, "=");
499       if (s != NULL)
500       {
501         CStdString strKeyword(line, s - line);
502         StringUtils::TrimRight(strKeyword);
503
504         CStdString strValue(s+1);
505         strValue.Replace("\\n", "\n"); 
506         strValue.Replace("\\t", "\t"); 
507         strValue.Replace("\\\\", "\\"); 
508
509         std::map<CStdString, CStdString>::const_iterator it = keywords.find(strKeyword);
510         if (it != keywords.end())
511           strValue = it->second + strValue; // keyword occured before, concatenate
512         else
513           keywordsOrder.push_back(strKeyword);
514
515         keywords[strKeyword] = strValue;
516       }
517     }
518   }
519
520   // parse keywords 
521   for (std::list<CStdString>::const_iterator it = keywordsOrder.begin(); it != keywordsOrder.end(); ++it)
522   {
523     CStdString strKeyword = *it;
524     CStdString strValue = keywords[strKeyword];
525
526     if (strKeyword == "DTITLE")
527     {
528       // DTITLE may contain artist and disc title, separated with " / ",
529       // for example: DTITLE=Modern Talking / Album: Victory (The 11th Album)
530       bool found = false;
531       for (int i = 0; i < strValue.GetLength() - 2; i++)
532       {
533         if (strValue[i] == ' ' && strValue[i + 1] == '/' && strValue[i + 2] == ' ')
534         {
535           m_strDisk_artist = TrimToUTF8(strValue.Left(i));
536           m_strDisk_title = TrimToUTF8(strValue.Mid(i+3));
537           found = true;
538           break;
539         }
540       }
541
542       if (!found)
543         m_strDisk_title = TrimToUTF8(strValue);
544     }
545     else if (strKeyword == "DYEAR")
546       m_strYear = TrimToUTF8(strValue);
547     else if (strKeyword== "DGENRE")
548       m_strGenre = TrimToUTF8(strValue);
549     else if (strKeyword.Left(6) == "TTITLE")
550       addTitle(strKeyword + "=" + strValue);
551     else if (strKeyword == "EXTD")
552     {
553       CStdString strExtd(strValue);
554
555       if (m_strYear.empty())
556       {
557         // Extract Year from extended info
558         // as a fallback
559         int iPos = strExtd.Find("YEAR:");
560         if (iPos > -1) // You never know if you really get UTF-8 strings from cddb
561           g_charsetConverter.unknownToUTF8(strExtd.Mid(iPos + 6, 4), m_strYear);
562       }
563
564       if (m_strGenre.empty())
565       {
566         // Extract ID3 Genre
567         // as a fallback
568         int iPos = strExtd.Find("ID3G:");
569         if (iPos > -1)
570         {
571           CStdString strGenre = strExtd.Mid(iPos + 5, 4);
572           StringUtils::TrimLeft(strGenre);
573           if (StringUtils::IsNaturalNumber(strGenre))
574           {
575             int iGenre = strtol(strGenre, NULL, 10);
576             m_strGenre = TagLib::ID3v1::genre(iGenre).to8Bit(true);
577           }
578         }
579       }
580     }
581     else if (strKeyword.Left(4) == "EXTT")
582       addExtended(strKeyword + "=" + strValue);
583   }
584
585   //writeLog("parseData Ende");
586 }
587
588 //-------------------------------------------------------------------------------------------------------------------
589 void Xcddb::addExtended(const char *buffer)
590 {
591   char value[2048];
592   int trk_nr = 0;
593   //TTITLEN
594   if (buffer[5] == '=')
595   { //Einstellig
596     trk_nr = buffer[4] - 47;
597     strcpy(value, buffer + 6);
598   }
599   else if (buffer[6] == '=')
600   { //Zweistellig
601     trk_nr = ((buffer[4] - 48) * 10) + buffer[5] - 47;
602     strcpy(value, buffer + 7);
603   }
604   else if (buffer[7] == '=')
605   { //Dreistellig
606     trk_nr = ((buffer[4] - 48) * 100) + ((buffer[5] - 48) * 10) + buffer[6] - 47;
607     strcpy(value, buffer + 8);
608   }
609   else
610   {
611     return ;
612   }
613
614   CStdString strValue;
615   CStdString strValueUtf8=value;
616   // You never know if you really get UTF-8 strings from cddb
617   g_charsetConverter.unknownToUTF8(strValueUtf8, strValue);
618   m_mapExtended_track[trk_nr] = strValue;
619 }
620
621 //-------------------------------------------------------------------------------------------------------------------
622 const CStdString& Xcddb::getTrackExtended(int track) const
623 {
624   typedef map<int, CStdString>::const_iterator iter;
625   iter i = m_mapExtended_track.find(track);
626   if (i == m_mapExtended_track.end())
627     return m_strNull;
628   return i->second;
629 }
630
631 //-------------------------------------------------------------------------------------------------------------------
632 void Xcddb::addInexactList(const char *list)
633 {
634   /*
635   211 Found inexact matches, list follows (until terminating `.')
636   soundtrack bf0cf90f Modern Talking / Victory - The 11th Album
637   rock c90cf90f Modern Talking / Album: Victory (The 11th Album)
638   misc de0d020f Modern Talking / Ready for the victory
639   rock e00d080f Modern Talking / Album: Victory (The 11th Album)
640   rock c10d150f Modern Talking / Victory (The 11th Album)
641   .
642   */
643
644   /*
645   m_mapInexact_cddb_command_list;
646   m_mapInexact_artist_list;
647   m_mapInexact_title_list;
648   */
649   int start = 0;
650   int end = 0;
651   bool found = false;
652   int line_counter = 0;
653   // //writeLog("addInexactList Start");
654   for (unsigned int i = 0;i < strlen(list);i++)
655   {
656     if (list[i] == '\n')
657     {
658       end = i;
659       found = true;
660     }
661     if (found)
662     {
663       if (line_counter > 0)
664       {
665         addInexactListLine(line_counter, list + start, end - start - 1);
666       }
667       start = i + 1;
668       line_counter++;
669       found = false;
670     }
671   }
672   // //writeLog("addInexactList End");
673 }
674
675 //-------------------------------------------------------------------------------------------------------------------
676 void Xcddb::addInexactListLine(int line_cnt, const char *line, int len)
677 {
678   // rock c90cf90f Modern Talking / Album: Victory (The 11th Album)
679   int search4 = 0;
680   char genre[100]; // 0
681   char discid[10]; // 1
682   char artist[1024]; // 2
683   char title[1024];
684   char cddb_command[1024];
685   int start = 0;
686   // //writeLog("addInexactListLine Start");
687   for (int i = 0;i < len;i++)
688   {
689     switch (search4)
690     {
691     case 0:
692       if (line[i] == ' ')
693       {
694         strncpy(genre, line, i);
695         genre[i] = 0x00;
696         search4 = 1;
697         start = i + 1;
698       }
699       break;
700     case 1:
701       if (line[i] == ' ')
702       {
703         strncpy(discid, line + start, i - start);
704         discid[i - start] = 0x00;
705         search4 = 2;
706         start = i + 1;
707       }
708       break;
709     case 2:
710       if (i + 2 <= len && line[i] == ' ' && line[i + 1] == '/' && line[i + 2] == ' ')
711       {
712         strncpy(artist, line + start, i - start);
713         artist[i - start] = 0x00;
714         strncpy(title, line + (i + 3), len - (i + 3));
715         title[len - (i + 3)] = 0x00;
716       }
717       break;
718     }
719   }
720   sprintf(cddb_command, "cddb read %s %s", genre, discid);
721
722   m_mapInexact_cddb_command_list[line_cnt] = cddb_command;
723
724   CStdString strArtist=artist;
725   // You never know if you really get UTF-8 strings from cddb
726   g_charsetConverter.unknownToUTF8(artist, strArtist);
727   m_mapInexact_artist_list[line_cnt] = strArtist;
728
729   CStdString strTitle=title;
730   // You never know if you really get UTF-8 strings from cddb
731   g_charsetConverter.unknownToUTF8(title, strTitle);
732   m_mapInexact_title_list[line_cnt] = strTitle;
733   // char log_string[1024];
734   // sprintf(log_string,"%u: %s - %s",line_cnt,artist,title);
735   // //writeLog(log_string);
736   // //writeLog("addInexactListLine End");
737 }
738
739 //-------------------------------------------------------------------------------------------------------------------
740 void Xcddb::setCDDBIpAdress(const CStdString& ip_adress)
741 {
742   m_cddb_ip_adress = ip_adress;
743 }
744
745 //-------------------------------------------------------------------------------------------------------------------
746 void Xcddb::setCacheDir(const CStdString& pCacheDir )
747 {
748   cCacheDir = pCacheDir;
749 }
750
751 //-------------------------------------------------------------------------------------------------------------------
752 bool Xcddb::queryCache( uint32_t discid )
753 {
754   if (cCacheDir.size() == 0)
755     return false;
756
757   XFILE::CFile file;
758   if (file.Open(GetCacheFile(discid)))
759   {
760     // Got a cachehit
761     char buffer[4096];
762     OutputDebugString ( "cddb local cache hit.\n" );
763     file.Read(buffer, 4096);
764     file.Close();
765     parseData( buffer );
766     return true;
767   }
768
769   return false;
770 }
771
772 //-------------------------------------------------------------------------------------------------------------------
773 bool Xcddb::writeCacheFile( const char* pBuffer, uint32_t discid )
774 {
775   if (cCacheDir.size() == 0)
776     return false;
777
778   XFILE::CFile file;
779   if (file.OpenForWrite(GetCacheFile(discid), true))
780   {
781     OutputDebugString ( "Current cd saved to local cddb.\n" );
782     file.Write( (void*) pBuffer, strlen( pBuffer ) + 1 );
783     file.Close();
784     return true;
785   }
786
787   return false;
788 }
789
790 //-------------------------------------------------------------------------------------------------------------------
791 bool Xcddb::isCDCached( int nr_of_tracks, toc cdtoc[] )
792 {
793   if (cCacheDir.size() == 0)
794     return false;
795
796   return XFILE::CFile::Exists(GetCacheFile(calc_disc_id(nr_of_tracks, cdtoc)));
797 }
798
799 //-------------------------------------------------------------------------------------------------------------------
800 const CStdString& Xcddb::getYear() const
801 {
802   return m_strYear;
803 }
804
805 //-------------------------------------------------------------------------------------------------------------------
806 const CStdString& Xcddb::getGenre() const
807 {
808   return m_strGenre;
809 }
810
811 //-------------------------------------------------------------------------------------------------------------------
812 bool Xcddb::queryCDinfo(CCdInfo* pInfo)
813 {
814   if ( pInfo == NULL )
815   {
816     CLog::Log(LOGERROR, "Xcddb::queryCDinfo pInfo == NULL");
817     m_lastError = E_PARAMETER_WRONG;
818     return false;
819   }
820
821   int lead_out = pInfo->GetTrackCount();
822   int real_track_count = pInfo->GetTrackCount();
823   uint32_t discid = pInfo->GetCddbDiscId();
824   unsigned long frames[100];
825
826
827   //##########################################################
828   //
829   if ( queryCache(discid) )
830   {
831     CLog::Log(LOGDEBUG, "Xcddb::queryCDinfo discid [%08x] already cached", discid);
832     return true;
833   }
834
835   //##########################################################
836   //
837   for (int i = 0;i < lead_out;i++)
838   {
839     frames[i] = pInfo->GetTrackInformation( i + 1 ).nFrames;
840     if (i > 0 && frames[i] < frames[i - 1])
841     {
842       CLog::Log(LOGERROR, "Xcddb::queryCDinfo E_TOC_INCORRECT");
843       m_lastError = E_TOC_INCORRECT;
844       return false;
845     }
846   }
847   unsigned long complete_length = pInfo->GetDiscLength();
848
849
850   //##########################################################
851   // Open socket to cddb database
852   if ( !openSocket() )
853   {
854     CLog::Log(LOGERROR, "Xcddb::queryCDinfo Error opening socket");
855     m_lastError = E_NETWORK_ERROR_OPEN_SOCKET;
856     return false;
857   }
858   CStdString recv_buffer = Recv(false);
859   m_lastError = atoi(recv_buffer.c_str());
860   switch(m_lastError)
861   {
862   case 200: //OK, read/write allowed
863   case 201: //OK, read only
864     break;
865
866   case 432: //No connections allowed: permission denied
867   case 433: //No connections allowed: X users allowed, Y currently active
868   case 434: //No connections allowed: system load too high
869   default:
870     CLog::Log(LOGERROR, "Xcddb::queryCDinfo Error: \"%s\"", recv_buffer.c_str());
871     return false;
872   }
873
874
875   //##########################################################
876   // Send the Hello message
877   CStdString version = g_infoManager.GetLabel(SYSTEM_BUILD_VERSION);
878   if (version.Find(" ") >= 0) 
879     version = version.Left(version.Find(" "));
880   CStdString strGreeting = "cddb hello xbmc xbmc.org XBMC " + version;
881   if ( ! Send(strGreeting.c_str()) )
882   {
883     CLog::Log(LOGERROR, "Xcddb::queryCDinfo Error sending \"%s\"", strGreeting.c_str());
884     m_lastError = E_NETWORK_ERROR_SEND;
885     return false;
886   }
887   recv_buffer = Recv(false);
888   m_lastError = atoi(recv_buffer.c_str());
889   switch(m_lastError)
890   {
891   case 200: //Handshake successful
892   case 402: //Already shook hands
893     break;
894
895   case 431: //Handshake not successful, closing connection
896   default:
897     CLog::Log(LOGERROR, "Xcddb::queryCDinfo Error: \"%s\"", recv_buffer.c_str());
898     return false;
899   }
900
901
902   //##########################################################
903   // Set CDDB protocol-level to 5
904   if ( ! Send("proto 5"))
905   {
906     CLog::Log(LOGERROR, "Xcddb::queryCDinfo Error sending \"%s\"", "proto 5");
907     m_lastError = E_NETWORK_ERROR_SEND;
908     return false;
909   }
910   recv_buffer = Recv(false);
911   m_lastError = atoi(recv_buffer.c_str());
912   switch(m_lastError)
913   {
914   case 200: //CDDB protocol level: current cur_level, supported supp_level
915   case 201: //OK, protocol version now: cur_level
916   case 502: //Protocol level already cur_level
917     break;
918
919   case 501: //Illegal protocol level.
920   default:
921     CLog::Log(LOGERROR, "Xcddb::queryCDinfo Error: \"%s\"", recv_buffer.c_str());
922     return false;
923   }
924
925
926   //##########################################################
927   // Compose the cddb query string
928   char query_buffer[1024];
929   strcpy(query_buffer, "");
930   strcat(query_buffer, "cddb query");
931   {
932     char tmp_buffer[256];
933     sprintf(tmp_buffer, " %08x", discid);
934     strcat(query_buffer, tmp_buffer);
935   }
936   {
937     char tmp_buffer[256];
938     sprintf(tmp_buffer, " %i", real_track_count);
939     strcat(query_buffer, tmp_buffer);
940   }
941   for (int i = 0;i < lead_out;i++)
942   {
943     char tmp_buffer[256];
944     sprintf(tmp_buffer, " %lu", frames[i]);
945     strcat(query_buffer, tmp_buffer);
946   }
947   {
948     char tmp_buffer[256];
949     sprintf(tmp_buffer, " %lu", complete_length);
950     strcat(query_buffer, tmp_buffer);
951   }
952
953
954   //##########################################################
955   // Query for matches
956   if ( ! Send(query_buffer))
957   {
958     CLog::Log(LOGERROR, "Xcddb::queryCDinfo Error sending \"%s\"", query_buffer);
959     m_lastError = E_NETWORK_ERROR_SEND;
960     return false;
961   }
962   // 200 rock d012180e Soundtrack / Hackers
963   CStdString read_buffer;
964   recv_buffer = Recv(false);
965   m_lastError = atoi(recv_buffer.c_str());
966   switch(m_lastError)
967   {
968   case 200: //Found exact match
969     strtok((char *)recv_buffer.c_str(), " ");
970     read_buffer = StringUtils::Format("cddb read %s %08x", strtok(NULL, " "), discid);
971     break;
972
973   case 210: //Found exact matches, list follows (until terminating marker)
974   case 211: //Found inexact matches, list follows (until terminating marker)
975     /*
976     soundtrack bf0cf90f Modern Talking / Victory - The 11th Album
977     rock c90cf90f Modern Talking / Album: Victory (The 11th Album)
978     misc de0d020f Modern Talking / Ready for the victory
979     rock e00d080f Modern Talking / Album: Victory (The 11th Album)
980     rock c10d150f Modern Talking / Victory (The 11th Album)
981     .
982     */
983     recv_buffer += Recv(true);
984     addInexactList(recv_buffer.c_str());
985     m_lastError=E_WAIT_FOR_INPUT;
986     return false; //This is actually good. The calling method will handle this
987
988   case 202: //No match found
989     CLog::Log(LOGNOTICE, "Xcddb::queryCDinfo No match found in CDDB database when doing the query shown below:\n%s",query_buffer);
990   case 403: //Database entry is corrupt
991   case 409: //No handshake
992   default:
993     CLog::Log(LOGERROR, "Xcddb::queryCDinfo Error: \"%s\"", recv_buffer.c_str());
994     return false;
995   }
996
997
998   //##########################################################
999   // Read the data from cddb
1000   if ( !Send(read_buffer) )
1001   {
1002     CLog::Log(LOGERROR, "Xcddb::queryCDinfo Error sending \"%s\"", read_buffer.c_str());
1003     m_lastError = E_NETWORK_ERROR_SEND;
1004     return false;
1005   }
1006   recv_buffer = Recv(true);
1007   m_lastError = atoi(recv_buffer.c_str());
1008   switch(m_lastError)
1009   {
1010   case 210: //OK, CDDB database entry follows (until terminating marker)
1011     // Cool, I got it ;-)
1012     writeCacheFile( recv_buffer.c_str(), discid );
1013     parseData(recv_buffer.c_str());
1014     break;
1015
1016   case 401: //Specified CDDB entry not found.
1017   case 402: //Server error.
1018   case 403: //Database entry is corrupt.
1019   case 409: //No handshake.
1020   default:
1021     CLog::Log(LOGERROR, "Xcddb::queryCDinfo Error: \"%s\"", recv_buffer.c_str());
1022     return false;
1023   }
1024
1025
1026   //##########################################################
1027   // Quit
1028   if ( ! Send("quit") )
1029   {
1030     CLog::Log(LOGERROR, "Xcddb::queryCDinfo Error sending \"%s\"", "quit");
1031     m_lastError = E_NETWORK_ERROR_SEND;
1032     return false;
1033   }
1034   recv_buffer = Recv(false);
1035   m_lastError = atoi(recv_buffer.c_str());
1036   switch(m_lastError)
1037   {
1038   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
1039   case 230: //Closing connection.  Goodbye.
1040     break;
1041
1042   case 530: //error, closing connection.
1043   default:
1044     CLog::Log(LOGERROR, "Xcddb::queryCDinfo Error: \"%s\"", recv_buffer.c_str());
1045     return false;
1046   }
1047
1048
1049   //##########################################################
1050   // Close connection
1051   if ( !closeSocket() )
1052   {
1053     CLog::Log(LOGERROR, "Xcddb::queryCDinfo Error closing socket");
1054     m_lastError = E_NETWORK_ERROR_SEND;
1055     return false;
1056   }
1057   return true;
1058 }
1059
1060 //-------------------------------------------------------------------------------------------------------------------
1061 bool Xcddb::isCDCached( CCdInfo* pInfo )
1062 {
1063   if (cCacheDir.size() == 0)
1064     return false;
1065   if ( pInfo == NULL )
1066     return false;
1067
1068   return XFILE::CFile::Exists(GetCacheFile(pInfo->GetCddbDiscId()));
1069 }
1070
1071 CStdString Xcddb::GetCacheFile(uint32_t disc_id) const
1072 {
1073   CStdString strFileName;
1074   strFileName = StringUtils::Format("%x.cddb", disc_id);
1075   return URIUtils::AddFileToFolder(cCacheDir, strFileName);
1076 }
1077
1078 CStdString Xcddb::TrimToUTF8(const CStdString &untrimmedText)
1079 {
1080   CStdString text(untrimmedText);
1081   StringUtils::Trim(text);
1082   // You never know if you really get UTF-8 strings from cddb
1083   g_charsetConverter.unknownToUTF8(text);
1084   return text;
1085 }
1086
1087 #endif
1088