cdcache.c
上传用户:xmgzy123
上传日期:2007-01-07
资源大小:373k
文件大小:25k
源码类别:

SCSI/ASPI

开发平台:

WINDOWS

  1. /*
  2.  * cdcache.c - Copyright (C) 1999 Jay A. Key
  3.  *
  4.  * Interface to CD title/artist/track names cache, and cddb.  Can optionally
  5.  * use cdplayer.ini when internet access is not available.
  6.  *
  7.  * The code to access CDPLAYER.INI was adapted from code submitted by
  8.  * Blair Thompson
  9.  *
  10.  **********************************************************************
  11.  *
  12.  * This program is free software; you can redistribute it and/or modify
  13.  * it under the terms of the GNU Lesser General Public License as published
  14.  * by the Free Software Foundation; either version 2 of the License, or
  15.  * (at your option) any later version.
  16.  *
  17.  * This program is distributed in the hope that it will be useful,
  18.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  20.  * GNU General Public License for more details.
  21.  *
  22.  * You should have received a copy of the GNU Lesser General Public License
  23.  * along with this program; if not, write to the Free Software
  24.  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  25.  *
  26.  **********************************************************************
  27.  *
  28.  * $Id: cdcache.c,v 1.2 2000/02/25 10:47:37 akey Exp $
  29.  * $Date: 2000/02/25 10:47:37 $
  30.  * $Locker:  $
  31.  * $Log: cdcache.c,v $
  32.  * Revision 1.2  2000/02/25 10:47:37  akey
  33.  * sync'ed with akrip32.dll v0.94
  34.  *
  35.  * Revision 1.6  2000/02/14 09:56:25  akey
  36.  * cleaned up #ifdef _DEBUG code considerably
  37.  *
  38.  * Revision 1.5  2000/02/11 10:00:35  akey
  39.  * added access to cdplayer.ini
  40.  *
  41.  * Revision 1.4  2000/01/31 15:35:40  akey
  42.  * v0.93: added CDDBGetServerList and fixed problem with Win2000 scsi pass through code
  43.  *
  44.  * Revision 1.3  2000/01/06 16:38:35  akey
  45.  * Added missing CDDB_OPT_HTTPPORT
  46.  *
  47.  * Revision 1.2  2000/01/03 12:29:43  akey
  48.  * v0.91 release -- added CDDB and bug fixes
  49.  *
  50.  *
  51.  */
  52. #include <windows.h>
  53. #include <stdio.h>
  54. #include <time.h>
  55. #include <string.h>
  56. #include <stdlib.h>
  57. #include <ctype.h>
  58. #include <winsock.h>
  59. #include "aspilib.h"
  60. #include "cdcache.h"
  61. DWORD CDDBPostCmd( char *cmd, char *retBuf, int retLen );
  62. DWORD CDDBPostCmdProxy( char *cmd, char *retBuf, int retLen );
  63. void urlEncodeString( char *s );
  64. void GetLineFromBuf( char **src, char *tgt, int len );
  65. void processCDDBQuery( char *buf, LPCDDBQUERY lpq );
  66. void SkipHTTPHeaders( char **buf );
  67. int extractCDDBQueryInfo( LPCDDBQUERYITEM lpq, char *linebuf );
  68. void processSites( char *buf, LPCDDBSITELIST lps );
  69. int extractCDDBSiteInfo( LPCDDBSITE s, char *buf );
  70. void getWord( char **inBuf, char *outBuf, int len );
  71. DWORD genCDPlayerIniIndex( HCDROM hCD );
  72. void MSB2DWORD( DWORD *d, BYTE *b );
  73. DWORD getDiskInfoCDPlayerIni( LPCDDBQUERYITEM lpq, char *szCDDBEntry, int maxLen );
  74. BOOL isCDinCDPlayerIni( char *s );
  75. void addCDPlayerCDDBIndex( DWORD cdpIdx, DWORD cddbId, DWORD numTracks );
  76. void writeCDPlayerIniEntry( LPCDDBQUERYITEM lpq, char *szCDDBEntry );
  77. extern CDHANDLEREC *cdHandles;
  78. extern HANDLE *cdMutexes;
  79. extern HANDLE hCacheMutex;
  80. extern CRITICAL_SECTION csCache;
  81. extern CRITICAL_SECTION getHandle;
  82. extern int alErrCode;
  83. extern BYTE alAspiErr;
  84. extern DWORD (*pfnSendASPI32Command)(LPSRB);
  85. //static BOOL bCacheInitMutex = FALSE;
  86. static BOOL bCacheInit = FALSE;
  87. static char szCacheDir[MAX_PATH+1];
  88. //static HANDLE hCacheMutex = NULL;
  89. //static CRITICAL_SECTION csCache;
  90. static char szCDPlayerIni[] = "cdplayer.ini";
  91. static char szProxyAddr[256] = "";
  92. static int  iProxyPort = 0;
  93. static char szCDDBServer[256] = "www.freedb.org";
  94. static BOOL bUseProxy = FALSE;
  95. static char szAgent[61] = "akrip32dll 0.91";
  96. static char szUser[65] = "user@akrip.sourceforge.net";
  97. static char szCGI[81] = "/~cddb/cddb.cgi";
  98. static int  iHTTPPort = 80;
  99. static BOOL bUseCDPlayerIni = TRUE;
  100. static DWORD dwCDDB2CDPlayer[20][3];
  101. static int iNextIndex = -1;
  102. DWORD CDDBSum( DWORD n )
  103. {
  104.   DWORD retVal = 0;
  105.   while( n > 0 )
  106.     {
  107.       retVal += ( n % 10 );
  108.       n /= 10;
  109.     }
  110.   return retVal;
  111. }
  112. /*
  113.  * Computes the CDDBID for the CD in the drive represented by hCD.
  114.  * Data which can be used to construct a CDDB query is stored in the 
  115.  * array pID.
  116.  *   pID[0] = CDDBID
  117.  *   pID[1] = number of tracks
  118.  *   pID[2..(2+n)] = starting MSF offset of tracks on CD
  119.  * pID will need to have (at least) n+3 entries, where n is the number
  120.  * of tracks on the CD.  102 should always be enough.  Note that the lead-out
  121.  * track is included.
  122.  */
  123. DWORD GetCDDBDiskID( HCDROM hCD, DWORD *pID, int numEntries )
  124. {
  125.   TOC toc;
  126.   TOCTRACK *t1, *t2;
  127.   int idx = (int)hCD - 1;
  128.   DWORD t;
  129.   DWORD n;
  130.   int i;
  131.   int j;
  132.   BOOL bMSF;
  133.   *pID = 0;
  134.   if ( (idx<0) || (idx>=MAXCDHAND) || !cdHandles[idx].used )
  135.     {
  136.       alErrCode = ALERR_INVHANDLE;
  137.       return SS_ERR;
  138.     }
  139.   if ( WaitForSingleObject( cdMutexes[idx], TIMEOUT ) != WAIT_OBJECT_0 )
  140.     {
  141.       alErrCode = ALERR_LOCK;
  142.       return SS_ERR;
  143.     }
  144.   bMSF = cdHandles[idx].bMSF;
  145.   cdHandles[idx].bMSF = TRUE;
  146.   memset( &toc, 0, sizeof(toc) );
  147.   ReadTOC( hCD, &toc );
  148.   n = t = 0;
  149.   for( j = 2, i = toc.firstTrack - 1; i < toc.lastTrack; i++, j++ )
  150.     {
  151.       t1 = &(toc.tracks[i]);
  152.       pID[j] = (((t1->addr[1]*60)+t1->addr[2])*75)+t1->addr[3];
  153.       n += CDDBSum( 60 * toc.tracks[i].addr[1] + toc.tracks[i].addr[2] );
  154.     }
  155.   t2 = &(toc.tracks[toc.lastTrack-toc.firstTrack+1]);
  156.   t = 60 * t2->addr[1] + t2->addr[2];
  157.   t2 = &(toc.tracks[0]);
  158.   t -= ( 60 * t2->addr[1] + t2->addr[2] );
  159.   pID[toc.lastTrack+2] = t;
  160. #if 0
  161.   // fudge the total time to generate inexact matches for testing
  162.   t += 2;
  163. #endif
  164.   pID[1] = toc.lastTrack - toc.firstTrack + 1;
  165.   *pID = ((n%0xFF) << 24) | (t << 8) | (toc.lastTrack - toc.firstTrack + 1);
  166.   cdHandles[idx].bMSF = bMSF;
  167.   n = genCDPlayerIniIndex( hCD );
  168.   addCDPlayerCDDBIndex( n, pID[0], pID[1] );
  169.   ReleaseMutex( cdMutexes[idx] );
  170.   return SS_COMP;
  171. }
  172. BOOL InitCache( LPCTSTR lpszDir )
  173. {
  174.   if ( bCacheInit )
  175.     return FALSE;
  176.   if ( !lpszDir )
  177.     lpszDir = "";
  178.   strncpy( szCacheDir, lpszDir, MAX_PATH+1 );
  179.   bCacheInit = TRUE;
  180.   return TRUE;
  181. }
  182. /*
  183.  * Sends an HTTP POST command and waits for a response.  The response is
  184.  * copied to retBuf.  Uses a proxy.  Returns the number of bytes copied to
  185.  * retBuf.  Before returning, it should strip the HTTP header from the info
  186.  * if present.
  187.  */
  188. DWORD CDDBPostCmdProxy( char *cmd, char *retBuf, int retLen )
  189. {
  190.   WSADATA wsaData;
  191.   SOCKET s;
  192.   struct hostent *h;
  193.   struct sockaddr_in sin;
  194.   int i;
  195.   char *postcmd, *p;
  196.   DWORD retVal = 0;
  197.   p = retBuf;
  198.   ZeroMemory( p, retLen );
  199.   retLen--;                 // reserve room for the terminating NULL
  200.   WSAStartup( MAKEWORD(1,1), &wsaData );
  201.   s = socket( AF_INET, SOCK_STREAM, 0 );
  202.   h = gethostbyname( szProxyAddr );
  203.   if ( !h )
  204.     {
  205. #ifdef _DEBUG
  206.       dbprintf( "akrip32: CDDBPostCmdProxy: unable to resolve hostname" );
  207. #endif
  208.       return 0;
  209.     }
  210.   sin.sin_family = AF_INET;
  211.   sin.sin_port = htons( iProxyPort );
  212.   memcpy( &sin.sin_addr, h->h_addr, h->h_length );
  213.   i = connect( s, (struct sockaddr *)&sin, sizeof(sin) );
  214.   p = postcmd = (char *)GlobalAlloc( GPTR, lstrlen(cmd) + 512 );
  215.   wsprintf( postcmd, "POST http://%s%s HTTP/1.0rnContent-Type: application/x-www-form-urlencodedrnUser-Agent: AKRip090rn", szCDDBServer, szCGI );
  216.   p += lstrlen( postcmd );
  217.   wsprintf( p, "Content-Length: %drnrn", lstrlen( cmd ) + 2 );
  218.   strcat( p, cmd );
  219.   strcat( p, "rn" );
  220. #if 0
  221.   i = 120;
  222.   i = setsockopt( s, IPPROTO_TCP, SO_SNDTIMEO, &i, sizeof(int) );
  223.   if ( i == SOCKET_ERROR )
  224.     dbprintf( "Error setting rcv timeout on socket" );
  225.   else
  226.     dbprintf( "Receive socket timeout set" );
  227. #endif
  228.   send( s, postcmd, lstrlen( postcmd ), 0 );
  229.   p = retBuf;
  230.   ZeroMemory( p, retLen );
  231.   i = -1;
  232.   while( (i != 0) && (retLen >= 128) )
  233.     {
  234.       i = recv( s, p, 128, 0 );
  235.       p += i;
  236.       retLen -= i;
  237.       retVal += (DWORD)i;
  238.       if ( i < 0 )
  239. i = 0;
  240.     }
  241.   closesocket( s );
  242.   
  243.   GlobalFree( (HGLOBAL)postcmd );
  244.   WSACleanup();
  245.   return retVal;
  246. }
  247. /*
  248.  * Sends an HTTP POST command and waits for a response.  The response is
  249.  * copied to retBuf.  Returns the number of bytes copied to retBuf.
  250.  */
  251. DWORD CDDBPostCmd( char *cmd, char *retBuf, int retLen )
  252. {
  253.   WSADATA wsaData;
  254.   SOCKET s;
  255.   struct hostent *h;
  256.   struct sockaddr_in sin;
  257.   int i;
  258.   char *postcmd, *p;
  259.   DWORD retVal = 0;
  260.   p = retBuf;
  261.   ZeroMemory( p, retLen );
  262.   retLen--;                 // reserve room for the terminating NULL
  263.   WSAStartup( MAKEWORD(1,1), &wsaData );
  264.   s = socket( AF_INET, SOCK_STREAM, 0 );
  265.   h = gethostbyname( szCDDBServer );
  266.   if ( !h )
  267.     return 0;
  268.   sin.sin_family = AF_INET;
  269.   sin.sin_port = htons( iHTTPPort );
  270.   memcpy( &sin.sin_addr, h->h_addr, h->h_length );
  271.   i = connect( s, (struct sockaddr *)&sin, sizeof(sin) );
  272.   p = postcmd = (char *)GlobalAlloc( GPTR, lstrlen(cmd) + 512 );
  273.   
  274.   wsprintf( p, "GET %s?", szCGI );
  275.   strcat( p, cmd );
  276.   strcat( p, "rn" );
  277.   send( s, postcmd, lstrlen( postcmd ), 0 );
  278.   p = retBuf;
  279.   ZeroMemory( p, retLen );
  280.   i = -1;
  281.   while( (i != 0) && (retLen >= 128) )
  282.     {
  283.       i = recv( s, p, 128, 0 );
  284.       p += i;
  285.       retLen -= i;
  286.       retVal += (DWORD)i;
  287.       if ( i < 0 )
  288. i = 0;
  289.     }
  290.   closesocket( s );
  291.   
  292.   GlobalFree( (HGLOBAL)postcmd );
  293.   WSACleanup();
  294.   return retVal;
  295. }
  296. /*
  297.  * Queries the CDDB for a given disk.
  298.  * Returns SS_COMP on success, SS_ERR on error.  numEntries on entry contains
  299.  * the number of elements in the lpq array, and on return is set to the 
  300.  * number of entries returned.
  301.  *
  302.  * If the no items are returned, or if no network connection is available,
  303.  * returns one item with category "cdplayerini" and the index stored in
  304.  * cddbId;
  305.  */
  306. DWORD CDDBQuery( HCDROM hCD, LPCDDBQUERY lpq )
  307. {
  308.   //int numRead = 0;
  309.   LPDWORD pdwId;
  310.   char *cmd, *p, *retBuf;
  311.   int i;
  312.   if ( !lpq )
  313.     {
  314.       return SS_ERR;
  315.     }
  316.   pdwId = GlobalAlloc( GPTR, 103 * sizeof(DWORD) );
  317.   cmd = GlobalAlloc( GPTR, 1024 );
  318.   retBuf = GlobalAlloc( GPTR, 2048 );
  319.   if ( !cmd || !pdwId || !retBuf )
  320.     {
  321.       if ( cmd ) GlobalFree( (HGLOBAL)cmd );
  322.       if ( pdwId ) GlobalFree( (HGLOBAL)pdwId );
  323.       if ( retBuf ) GlobalFree( (HGLOBAL)retBuf );
  324.       lpq->num = 0;
  325.       return SS_ERR;
  326.     }
  327.   // Generate the cddb ID for the disc
  328.   if ( GetCDDBDiskID( hCD, pdwId, 102 ) == SS_COMP )
  329.     {
  330.       // Generate a query string
  331.       p = cmd;
  332.       wsprintf( p, "cmd=cddb+query+%08x+%d+", pdwId[0], pdwId[1] );
  333.       p += lstrlen( p );
  334.       for( i = 0; i < pdwId[1]; i++ )
  335. {
  336.   wsprintf( p, "%d+", pdwId[i+2] );
  337.   p += lstrlen(p);
  338. }
  339.       wsprintf( p, "%d&hello=%s+%s&proto=3",
  340. pdwId[pdwId[1]+2], szUser, szAgent );
  341.       urlEncodeString( p );
  342.       // send the query string
  343.       if ( bUseProxy )
  344. CDDBPostCmdProxy( cmd, retBuf, 2048 );
  345.       else
  346. CDDBPostCmd( cmd, retBuf, 2048 );
  347.       // process the returned data (if any)
  348.       processCDDBQuery( retBuf, lpq );
  349.       // fall back to cdplayer.ini if no items matched
  350.       if ( (lpq->num == 0) && bUseCDPlayerIni )
  351. {
  352.   pdwId[0] = genCDPlayerIniIndex( hCD );
  353.   wsprintf( retBuf, "%X", pdwId[0] );
  354.   if ( isCDinCDPlayerIni( retBuf ) )
  355.     {
  356.       wsprintf( lpq->q[0].cddbId, "%X", pdwId[0] );
  357.       lstrcpy( lpq->q[0].categ, "cdplayerini" );
  358.       lpq->q[0].bExact = TRUE;
  359.       lpq->num = 1;
  360.     }
  361. }
  362.       // else if we have multiple/partial matches from CDDB, we need to 
  363.       // associate all of the cddbId values with the cdPlayer.ini index
  364.       else if ( (lpq->num > 1) && bUseCDPlayerIni )
  365. {
  366.   pdwId[2] = genCDPlayerIniIndex( hCD );
  367.   for( i = 0; i < lpq->num; i++ )
  368.     {
  369.       pdwId[0] = (DWORD)strtoul( lpq->q[i].cddbId, NULL, 16 );
  370.       addCDPlayerCDDBIndex( pdwId[2], pdwId[0], pdwId[1] );
  371.     }
  372. }
  373.     }
  374.   GlobalFree( (HGLOBAL)pdwId );
  375.   GlobalFree( (HGLOBAL)cmd );
  376.   GlobalFree( (HGLOBAL)retBuf );
  377.   return SS_COMP;
  378. }
  379. void CDDBSetOption( int what, char *szVal, int iVal )
  380. {
  381.   switch( what )
  382.     {
  383.     case CDDB_OPT_PROXY:
  384.       if ( szVal )
  385. lstrcpyn( szProxyAddr, szVal, 255 );
  386.       szProxyAddr[255] = 0;
  387.       break;
  388.     case CDDB_OPT_SERVER:
  389.       if ( szVal )
  390. lstrcpyn( szCDDBServer, szVal, 255 );
  391.       szCDDBServer[255] = 0;
  392.       break;
  393.     case CDDB_OPT_CGI:
  394.       if ( szVal )
  395. lstrcpyn( szCGI, szVal, 80 );
  396.       szCGI[80] = 0;
  397.       break;
  398.     case CDDB_OPT_PROXYPORT:
  399.       iProxyPort = iVal;
  400.       break;
  401.     case CDDB_OPT_AGENT:
  402.       if ( szVal )
  403. lstrcpyn( szAgent, szVal, 60 );
  404.       szAgent[60] = 0;
  405.       urlEncodeString( szAgent );
  406.       break;
  407.     case CDDB_OPT_USER:
  408.       if ( szVal )
  409. lstrcpyn( szUser, szVal, 64 );
  410.       szUser[64] = 0;
  411.       urlEncodeString( szUser );
  412.       break;
  413.     case CDDB_OPT_USEPROXY:
  414.       bUseProxy = (BOOL)iVal;
  415.       break;
  416.     case CDDB_OPT_HTTPPORT:
  417.       iHTTPPort = iVal;
  418.       break;
  419.     case CDDB_OPT_USECDPLAYERINI:
  420.       bUseCDPlayerIni = (BOOL)iVal;
  421.       break;
  422.     }
  423. }
  424. void urlEncodeString( char *s )
  425. {
  426.   if ( !s || !*s )
  427.     return;
  428.   while( *++s )
  429.     {
  430.       if ( (*s == '@') || (*s == ' ') )
  431. *s = '+';
  432.     }
  433. }
  434. /*
  435.  * Process the return buffer from a cddb query.  Verify that the return code
  436.  * is one that we expect (200, 211, 202, 403, 409).
  437.  */
  438. void processCDDBQuery( char *buf, LPCDDBQUERY lpq )
  439. {
  440.   int total = 0;
  441.   int iRetCode, i;
  442.   int maxLines = 100;
  443.   char retCode[4] = "100";
  444.   char linebuf[81];
  445.   char *p = buf;
  446.   FILE *fp;
  447.   fp = fopen( "retbuf.txt", "wb" );
  448.   fwrite( p, 1, lstrlen( p ), fp );
  449.   SkipHTTPHeaders( &p );
  450.   GetLineFromBuf( &p, linebuf, 81 );
  451.   strncpy( retCode, linebuf, 3 );
  452.   iRetCode = atoi( retCode );
  453.   switch( iRetCode )
  454.     {
  455.     case 200:
  456.       // one exact match
  457.       if ( extractCDDBQueryInfo( &lpq->q[0], linebuf+4 ) )
  458. {
  459.   lpq->q[0].bExact = TRUE;
  460.   total++;
  461. }
  462.       break;
  463.     case 211:
  464.       // inexact match(es)
  465.       i = 0;
  466.       while ( p && lpq->num && (maxLines-- > 0) )
  467. {
  468.   GetLineFromBuf( &p, linebuf, 81 );
  469.   if ( !strcmp( linebuf, "." ) )
  470.     break;
  471.   if ( extractCDDBQueryInfo( &lpq->q[i], linebuf ) )
  472.     {
  473.       lpq->q[i].bExact = FALSE;
  474.       total++;
  475.       i++;
  476.       lpq->num--;
  477.     }
  478. }
  479.       break;
  480.     case 202:
  481.       // no matches;
  482.       break;
  483.     case 403:
  484.       // database entry corrupt
  485.       break;
  486.     case 409:
  487.       // no handshake (probably won't happen with HTTP)
  488.       break;
  489.     }
  490.   lpq->num = total;
  491.   fclose( fp );
  492. }
  493. void GetLineFromBuf( char **src, char *tgt, int len )
  494. {
  495.   char *s, *t;
  496.   if ( !src || !*src || !tgt )
  497.     {
  498.       return;
  499.     }
  500.   ZeroMemory( tgt, len );
  501.   s = strstr( *src, "rn" );
  502.   if ( !s )
  503.     {
  504.       *src += lstrlen( *src );
  505.       return;
  506.     }
  507.   lstrcpyn( tgt, *src, len-1 );
  508.   t = strstr( tgt, "rn" );
  509.   if ( t && ((t - tgt) < len) )
  510.     tgt[t-tgt] = '';
  511.   
  512.   *src = s + 2;
  513. }
  514. /*
  515.  * extracts the category, cddbid and title(artist/album) from linebuf and 
  516.  * stores it in lpq
  517.  */
  518. int extractCDDBQueryInfo( LPCDDBQUERYITEM lpq, char *linebuf )
  519. {
  520.   int i;
  521.   char *p = linebuf;
  522.   char *t;
  523.   if ( !lpq || !linebuf || !*linebuf )
  524.     return 0;
  525.   ZeroMemory( lpq, sizeof(CDDBQUERY) );
  526.   // extract the category
  527.   i = 11;
  528.   t = lpq->categ;
  529.   while( *p && (*p != ' ') && i )
  530.     {
  531.       *t++ = *p++;
  532.       i--;
  533.     }
  534.   if ( *p != ' ' )
  535.     {
  536.       return 0;
  537.     }
  538.   // extract the cddbid
  539.   while ( *p && (*++p == ' ') );    // skip space
  540.   i = 8;
  541.   t = lpq->cddbId;
  542.   while ( *p && ( isxdigit( *p ) ) && i )
  543.     {
  544.       *t++ = *p++;
  545.       i--;
  546.     }
  547.   if ( *p != ' ' )
  548.     {
  549.       return 0;
  550.     }
  551.   // get artist and title
  552.   while ( *p && (*++p == ' ') );    // skip space
  553.   i = 80;
  554.   t = lpq->artist;
  555.   t[0] = '';
  556.   while ( *p && (*p != '/') && i )
  557.     {
  558.       *t++ = *p++;
  559.       i--;
  560.     }
  561.   i = lstrlen( lpq->artist );
  562.   if ( i > 0 )
  563.     {
  564.       if ( lpq->artist[i-1] == ' ' )
  565. lpq->artist[i-1] = '';
  566.     }
  567.   if ( *p != '/' )
  568.     {
  569.       return 0;
  570.     }
  571.   while ( *p && (*++p == ' ') );    // skip space
  572.   t = lpq->title;
  573.   lstrcpyn( t, p, 80 );
  574.   i = lstrlen( lpq->title );
  575.   if ( i > 0 )
  576.     {
  577.       if ( lpq->title[i-1] == 'r' || lpq->title[i-1] == 'n' )
  578. lpq->title[i-1] = '';
  579.     }
  580.   return 1;
  581. }
  582. void SkipHTTPHeaders( char **buf )
  583. {
  584.   char *p;
  585.   if ( !buf || !*buf || !**buf )
  586.     return;
  587.   p = *buf;
  588.   if ( strncmp( p, "HTTP", 4 ) )
  589.     {
  590.       return;
  591.     }
  592.   p = strstr( p, "rnrn" );
  593.   if ( p )
  594.     {
  595.       p += 4;
  596.       *buf = p;
  597.     }
  598. }
  599. /*
  600.  * Returns the CDDB entry verbatim from the CDDB database.  If not large
  601.  * enough, no data is copied.  Verifies that the return code from CDDB is
  602.  * 210 -- CDDB entry follows...
  603.  *
  604.  * If the use of CDPLAYER.INI is enabled and the category is for the query
  605.  * is "cdplayerini", then an attempt is made to read the information from
  606.  * CDPLAYER.INI.
  607.  */
  608. DWORD CDDBGetDiskInfo( LPCDDBQUERYITEM lpq, char *szCDDBEntry, int maxLen )
  609. {
  610.   char *cmd, *p;
  611.   char *retBuf;
  612.   DWORD retVal = SS_ERR;
  613.   if ( !lpq || !szCDDBEntry )
  614.     return retVal;
  615.   if ( !lstrcmp( lpq->categ, "cdplayerini" ) )
  616.     return getDiskInfoCDPlayerIni( lpq, szCDDBEntry, maxLen );
  617.   cmd = (char *)GlobalAlloc( GPTR, 512 );
  618.   retBuf = (char *)GlobalAlloc( GPTR, maxLen );
  619.   p = cmd;
  620.   wsprintf( p, "cmd=cddb+read+%s+%s", lpq->categ, lpq->cddbId );
  621.   p += lstrlen( p );
  622.   wsprintf( p, "&hello=%s+%s&proto=3", szUser, szAgent );
  623.   urlEncodeString( p );
  624.       // send the query string
  625.   if ( bUseProxy )
  626.     CDDBPostCmdProxy( cmd, retBuf, maxLen );
  627.   else
  628.     CDDBPostCmd( cmd, retBuf, maxLen );
  629.   // strip any HTTP headers
  630.   p = retBuf;
  631.   SkipHTTPHeaders( &p );
  632.   if ( !strncmp( p, "210", 3 ) )
  633.     {
  634.       p = strstr( p, "n" );
  635.       if ( p )
  636. {
  637.   p += 1;  // skip the 'n'
  638.   if ( lstrlen(p) < maxLen )
  639.     {
  640.       strcpy( szCDDBEntry, p );
  641.       retVal = SS_COMP;
  642.       // should we add it to cdplayer.ini?
  643.       if ( bUseCDPlayerIni && *p )
  644. {
  645.   writeCDPlayerIniEntry( lpq, p );
  646. }
  647.     }
  648. }
  649.     }
  650.   GlobalFree( (HGLOBAL)cmd );
  651.   GlobalFree( (HGLOBAL)retBuf );
  652.   return retVal;
  653. }
  654. DWORD CDDBGetServerList( LPCDDBSITELIST lps )
  655. {
  656.   char *cmd, *p;
  657.   char *retBuf;
  658.   DWORD retVal = SS_ERR;
  659.   if ( !lps || !lps->s )
  660.     return retVal;
  661.   cmd = (char *)GlobalAlloc( GPTR, 512 );
  662.   retBuf = (char *)GlobalAlloc( GPTR, 4096 );
  663.   p = cmd;
  664.   wsprintf( p, "cmd=sites&hello=%s+%s&proto=3", szUser, szAgent );
  665.   urlEncodeString( p );
  666.   // send the query string
  667.   if ( bUseProxy )
  668.     CDDBPostCmdProxy( cmd, retBuf, 4096 );
  669.   else
  670.     CDDBPostCmd( cmd, retBuf, 4096 );
  671.   if ( retBuf[0] )
  672.     {
  673.       // look for the "210 OK, site information..." message
  674.       processSites( retBuf, lps );
  675.       retVal = SS_COMP;
  676.     }
  677.   GlobalFree( (HGLOBAL)cmd );
  678.   GlobalFree( (HGLOBAL)retBuf );
  679.   return retVal;
  680. }
  681. void processSites( char *buf, LPCDDBSITELIST lps )
  682. {
  683.   char linebuf[81];
  684.   char retCode[4] = "";
  685.   char *p;
  686.   int total, iRetCode;
  687.   int maxLines = 100;
  688.   // strip any HTTP headers
  689.   p = buf;
  690.   SkipHTTPHeaders( &p );
  691.   GetLineFromBuf( &p, linebuf, 81 );
  692.   strncpy( retCode, linebuf, 3 );
  693.   iRetCode = atoi( retCode );
  694.   total = 0;
  695.   switch( iRetCode )
  696.     {
  697.     case 210:   // normal return code, site list follows
  698.       while ( p && lps->num && (maxLines-- > 0) )
  699. {
  700.   GetLineFromBuf( &p, linebuf, 81 );
  701.   if ( !strcmp( linebuf, "." ) )
  702.     break;
  703.   if ( extractCDDBSiteInfo( &lps->s[total], linebuf ) )
  704.     {
  705.       total++;
  706.       lps->num--;
  707.     }
  708. }
  709.       break;
  710.     case 401: // no site info available
  711.       break;
  712.     }
  713.   lps->num = total;
  714. }
  715. int extractCDDBSiteInfo( LPCDDBSITE lps, char *linebuf )
  716. {
  717.   char *p;
  718.   char buf[6];
  719.   if ( !lps || !linebuf || !*linebuf )
  720.     return 0;
  721.   ZeroMemory( lps, sizeof(CDDBSITE) );
  722.   p = linebuf;
  723.   // extract the server
  724.   getWord( &p, lps->szServer, 81 );
  725.   if ( *p != ' ' )
  726.     return 0;
  727.   // extract the protocol
  728.   getWord( &p, buf, 6 );
  729.   if ( *p != ' ' )
  730.     return 0;
  731.   if ( !lstrcmpi( buf, "http" ) )
  732.     lps->bHTTP = TRUE;
  733.   // extract the port number
  734.   getWord( &p, buf, 6 );
  735.   if ( *p != ' ' )
  736.     return 0;
  737.   lps->iPort = atoi( buf );
  738.   if ( lps->bHTTP && !lps->iPort )
  739.     lps->iPort = 80;
  740.   // extract the CGI
  741.   getWord( &p, lps->szCGI, 81 );
  742.   if ( *p != ' ' )
  743.     return 0;
  744.   // extract north coordinate
  745.   getWord( &p, lps->szNorth, 16 );
  746.   if ( *p != ' ' )
  747.     return 0;
  748.   // extract north coordinate
  749.   getWord( &p, lps->szSouth, 16 );
  750.   if ( *p != ' ' )
  751.     return 0;
  752.   // extract the location
  753.   while( *p && (*p == ' ') ) p++; 
  754.   lstrcpyn( lps->szLocation, p, 80 );
  755.   return -1;
  756. }
  757. void getWord( char **inBuf, char *outBuf, int len )
  758. {
  759.   char *p = *inBuf;
  760.   ZeroMemory( outBuf, len );
  761.   len--;
  762.   // skip space
  763.   while( *p && (*p == ' ') ) p++;
  764. #if 0
  765.   while( TRUE )
  766.     {
  767.       if ( !*p )
  768. break;
  769.       if ( !isalnum( *p ) && (*p != '.') )
  770. break;
  771.       if ( !len )
  772. break;
  773.       *outBuf++ = *p++;
  774.       len--;
  775.     }
  776. #else
  777.   while( *p &&
  778.  ( isalnum( *p ) || ( *p == '.') || ( *p == '/' ) || ( *p == '~' ) ) &&
  779.  len )
  780.     {
  781.       *outBuf++ = *p++;
  782.       len--;
  783.     }
  784. #endif
  785.   *inBuf = p;
  786. }
  787. DWORD genCDPlayerIniIndex( HCDROM hCD )
  788. {
  789.   DWORD retVal = 0;
  790.   BOOL bMSF;
  791.   int idx = (int)hCD - 1;
  792.   int i;
  793.   TOC toc;
  794.   DWORD dwAddr;
  795.   bMSF = cdHandles[idx].bMSF;
  796.   cdHandles[idx].bMSF = TRUE;
  797.   memset( &toc, 0, sizeof(toc) );
  798.   ReadTOC( hCD, &toc );
  799.   for( i = 0; i <= (toc.lastTrack - toc.firstTrack); i++ )
  800.     {
  801.       MSB2DWORD( &dwAddr, toc.tracks[i].addr );
  802.       retVal += dwAddr;
  803.     }
  804.   return retVal;
  805. }
  806. void MSB2DWORD( DWORD *d, BYTE *b )
  807. {
  808.   DWORD retVal;
  809.   retVal = (DWORD)b[0];
  810.   retVal = (retVal<<8) + (DWORD)b[1];
  811.   retVal = (retVal<<8) + (DWORD)b[2];
  812.   retVal = (retVal<<8) + (DWORD)b[3];
  813.   *d = retVal;
  814. }
  815. DWORD getDiskInfoCDPlayerIni( LPCDDBQUERYITEM lpq, char *szCDDBEntry, int maxLen )
  816. {
  817.   UINT i, numRead;
  818.   char buf[256];
  819.   char idx[5];
  820.   char defaultName[13];
  821.   char *p;
  822.   if ( !lpq || !szCDDBEntry )
  823.     return SS_ERR;
  824.   numRead = GetPrivateProfileInt( lpq->cddbId, "NUMTRACKS", 0, szCDPlayerIni );
  825.   if ( numRead )
  826.     {
  827.       lstrcpy( lpq->categ, "rock" );
  828.       lpq->bExact = TRUE;
  829.       GetPrivateProfileString( lpq->cddbId, "ARTIST", "", buf, 256, szCDPlayerIni );
  830.       lstrcpyn( lpq->artist, buf, 80 ); lpq->artist[80] = '';
  831.       GetPrivateProfileString( lpq->cddbId, "TITLE", "", buf, 256, szCDPlayerIni );
  832.       lstrcpyn( lpq->title, buf, 80 ); lpq->title[80] = '';
  833.       p = szCDDBEntry;
  834.       wsprintf( buf, "DTITLE=%s / %srn", lpq->artist, lpq->title );
  835.       if ( maxLen > lstrlen(buf) )
  836. {
  837.   lstrcpy( p, buf );
  838.   p += lstrlen( buf );
  839.   maxLen -= lstrlen( buf );
  840. }
  841.       for( i = 0; i < numRead; i++ )
  842. {
  843.   wsprintf( idx, "%d", i );
  844.   wsprintf( defaultName, "Track %d", i+1 );
  845.   GetPrivateProfileString( lpq->cddbId, idx, defaultName, buf, 256, szCDPlayerIni );
  846.   if ( maxLen > (lstrlen( buf )+12) )
  847.     {
  848.       wsprintf( p, "TTITLE%d=%srn", i, buf );
  849.       maxLen -= lstrlen( p );
  850.       p += lstrlen( p );
  851.     }
  852.   else
  853.     break;
  854. }
  855.     }
  856.   if ( numRead )
  857.     return SS_COMP;
  858.   return SS_ERR;
  859. }
  860. BOOL isCDinCDPlayerIni( char *s )
  861. {
  862.   UINT uiVal;
  863.   uiVal = GetPrivateProfileInt( s, "NUMTRACKS", 0, "cdplayer.ini" );
  864.   return (BOOL)uiVal;
  865. }
  866. void addCDPlayerCDDBIndex( DWORD cdpIdx, DWORD cddbId, DWORD numTracks )
  867. {
  868.   if ( iNextIndex == -1 )
  869.     ZeroMemory( dwCDDB2CDPlayer, sizeof(dwCDDB2CDPlayer) );
  870.   if ( (++iNextIndex % 20) == 0 )
  871.     iNextIndex = 0;
  872.   dwCDDB2CDPlayer[iNextIndex][0] = cdpIdx;        // cdplayer.ini index
  873.   dwCDDB2CDPlayer[iNextIndex][1] = cddbId;        // cddb id
  874.   dwCDDB2CDPlayer[iNextIndex][2] = numTracks;     // number of tracks
  875. }
  876. DWORD CDDBIndex2CDPlayerIni( char *szCDDBId, DWORD *dwRetVal, DWORD *numTracks )
  877. {
  878.   int i;
  879.   DWORD dwIdx = (DWORD)strtoul( szCDDBId, NULL, 16 );
  880.   for( i = 0; i < 20; i++ )
  881.     {
  882.       if ( dwCDDB2CDPlayer[i][1] == dwIdx )
  883. {
  884.   *dwRetVal = dwCDDB2CDPlayer[i][0];
  885.   *numTracks = dwCDDB2CDPlayer[i][2];
  886.   return *dwRetVal;
  887. }
  888.     }
  889.   return 0;
  890. }
  891. /*
  892.  * Stores a CDDB entry in cdplayer.ini.  NOTE: the buffer pointed to
  893.  * by szCDDBEntry may be modified by this function.  Do not count on the
  894.  * buffer being unmodified!!!
  895.  */
  896. void writeCDPlayerIniEntry( LPCDDBQUERYITEM lpq, char *szCDDBEntry )
  897. {
  898.   DWORD dwCDPlayerIdx, dwNumTracks;
  899.   char section[24];
  900.   char buf[128];
  901.   char *p1, *p2;
  902.   CDDBIndex2CDPlayerIni( lpq->cddbId, &dwCDPlayerIdx, &dwNumTracks );
  903.   if ( !dwCDPlayerIdx )
  904.     {
  905.       return;
  906.     }
  907.   wsprintf( section, "%X", dwCDPlayerIdx );
  908.   WritePrivateProfileString( section, "EntryType", "1", szCDPlayerIni );
  909.   WritePrivateProfileString( section, "artist", lpq->artist, szCDPlayerIni );
  910.   WritePrivateProfileString( section, "title", lpq->title, szCDPlayerIni );
  911.   WritePrivateProfileString( section, "genre", lpq->categ, szCDPlayerIni );
  912.   wsprintf( buf, "%d", dwNumTracks );
  913.   WritePrivateProfileString( section, "numtracks", buf, szCDPlayerIni );
  914.   while (*szCDDBEntry)
  915.     {
  916.       GetLineFromBuf( &szCDDBEntry, buf, 128 );
  917.       if ( !szCDDBEntry )
  918. break;
  919.       if ( !strncmp( "TTITLE", buf, 6 ) )
  920. {
  921.   p1 = buf + 6;
  922.   p2 = strstr( buf, "=" );
  923.   if ( *p1 && p2 )
  924.     {
  925.       *p2 = '';
  926.       p2++;
  927.       WritePrivateProfileString( section, p1, p2, szCDPlayerIni );
  928.     }
  929. }
  930.     }
  931. }