File indexing completed on 2024-04-14 04:49:13

0001 /*
0002     SPDX-FileCopyrightText: 2002 Rik Hemsley (rikkus) <rik@kde.org>
0003     SPDX-FileCopyrightText: 2002 Benjamin Meyer <ben-devel@meyerhome.net>
0004     SPDX-FileCopyrightText: 2005 Richard Lärkäng <nouseforaname@home.se>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include "asynccddbplookup.h"
0010 #include "logging.h"
0011 
0012 namespace KCDDB
0013 {
0014   AsyncCDDBPLookup::AsyncCDDBPLookup()
0015     : CDDBPLookup(),
0016       state_(Idle)
0017   {
0018 
0019   }
0020 
0021   AsyncCDDBPLookup::~AsyncCDDBPLookup()
0022   {
0023   }
0024 
0025     Result
0026   AsyncCDDBPLookup::lookup
0027   (
0028     const QString         & hostname,
0029     uint                    port,
0030     const TrackOffsetList & trackOffsetList
0031   )
0032   {
0033     socket_ = new QTcpSocket;
0034     socket_->connectToHost(hostname, port);
0035 
0036     connect (socket_, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(slotGotError(QAbstractSocket::SocketError)));
0037 
0038     connect (socket_, &QAbstractSocket::connected,
0039       this, &AsyncCDDBPLookup::slotConnectionSuccess );
0040 
0041     connect (socket_, &QIODevice::readyRead, this, &AsyncCDDBPLookup::slotReadyRead );
0042 
0043     trackOffsetList_ = trackOffsetList;
0044 
0045     state_ = WaitingForConnection;
0046 
0047     return Success;
0048   }
0049 
0050     void
0051   AsyncCDDBPLookup::slotGotError(QAbstractSocket::SocketError error)
0052   {
0053     state_ = Idle;
0054 
0055     if ( error == QAbstractSocket::HostNotFoundError )
0056       Q_EMIT finished( HostNotFound );
0057     else if ( error == QAbstractSocket::SocketTimeoutError )
0058       Q_EMIT finished( NoResponse );
0059     else
0060       Q_EMIT finished( UnknownError );
0061   }
0062 
0063     void
0064   AsyncCDDBPLookup::slotConnectionSuccess()
0065   {
0066     qCDebug(LIBKCDDB) << "Connection successful";
0067     state_ = WaitingForGreeting;
0068   }
0069 
0070     void
0071   AsyncCDDBPLookup::slotReadyRead()
0072   {
0073     qCDebug(LIBKCDDB) << "Ready to read. State: " << stateToString();
0074 
0075     while ( Idle != state_ && isConnected() && socket_->canReadLine() )
0076       read();
0077   }
0078 
0079     void
0080   AsyncCDDBPLookup::read()
0081   {
0082     switch ( state_ )
0083     {
0084       case WaitingForGreeting:
0085 
0086         if ( !parseGreeting( readLine() ) )
0087         {
0088           result_ = ServerError;
0089           doQuit();
0090           return;
0091         }
0092 
0093         doHandshake();
0094 
0095         break;
0096 
0097       case WaitingForHandshake:
0098 
0099         if ( !parseHandshake( readLine() ) )
0100         {
0101           result_ = ServerError;
0102           doQuit();
0103           return;
0104         }
0105 
0106         doProto();
0107 
0108         break;
0109 
0110       case WaitingForProtoResponse:
0111 
0112         // Ignore the response for now
0113         readLine();
0114 
0115         doQuery();
0116 
0117         break;
0118 
0119       case WaitingForQueryResponse:
0120           result_ = parseQuery( readLine() );
0121 
0122           switch ( result_ )
0123           {
0124             case Success:
0125               requestCDInfoForMatch();
0126               break;
0127 
0128             case MultipleRecordFound:
0129               state_ = WaitingForMoreMatches;
0130               break;
0131 
0132             default: // Error :(
0133               doQuit();
0134               return;
0135           }
0136 
0137         break;
0138 
0139       case WaitingForMoreMatches:
0140         {
0141           QString line = readLine();
0142 
0143           if (line.startsWith(QLatin1String( "." )))
0144             requestCDInfoForMatch();
0145           else
0146             parseExtraMatch( line );
0147         }
0148 
0149         break;
0150 
0151       case WaitingForCDInfoResponse:
0152         {
0153           Result result = parseRead( readLine() );
0154 
0155           if ( Success != result )
0156           {
0157             result_ = result;
0158             doQuit();
0159             return;
0160           }
0161 
0162           state_ = WaitingForCDInfoData;
0163         }
0164 
0165         break;
0166 
0167       case WaitingForCDInfoData:
0168         {
0169           QString line = readLine();
0170 
0171           if (line.startsWith(QLatin1String( "." )))
0172           {
0173             parseCDInfoData();
0174             requestCDInfoForMatch();
0175           }
0176           else
0177             cdInfoBuffer_ << line;
0178         }
0179 
0180         break;
0181 
0182       case WaitingForQuitResponse:
0183 
0184         state_ = Idle;
0185 
0186         char c;
0187         while ( socket_->bytesAvailable() )
0188           socket_->getChar(&c);
0189 
0190         close();
0191 
0192         Q_EMIT finished( result_ );
0193 
0194         break;
0195 
0196       default:
0197 
0198         break;
0199     }
0200   }
0201 
0202     QString
0203   AsyncCDDBPLookup::readLine()
0204   {
0205     return QString::fromUtf8(socket_->readLine());
0206   }
0207 
0208     void
0209   AsyncCDDBPLookup::doHandshake()
0210   {
0211     sendHandshake();
0212 
0213     state_ = WaitingForHandshake;
0214   }
0215 
0216     void
0217   AsyncCDDBPLookup::doProto()
0218   {
0219     sendProto();
0220 
0221     state_ = WaitingForProtoResponse;
0222   }
0223 
0224     void
0225   AsyncCDDBPLookup::doQuery()
0226   {
0227     sendQuery();
0228 
0229     state_ = WaitingForQueryResponse;
0230   }
0231 
0232     void
0233   AsyncCDDBPLookup::requestCDInfoForMatch()
0234   {
0235     if (matchList_.isEmpty())
0236     {
0237       result_ = cdInfoList_.isEmpty()? NoRecordFound : Success;
0238       doQuit();
0239       return;
0240     }
0241 
0242     CDDBMatch match = matchList_.takeFirst();
0243 
0244     sendRead( match );
0245 
0246     state_ = WaitingForCDInfoResponse;
0247   }
0248 
0249     void
0250   AsyncCDDBPLookup::parseCDInfoData()
0251   {
0252     CDInfo info;
0253 
0254     if (info.load( cdInfoBuffer_ ))
0255     {
0256       info.set( QLatin1String( "category" ), category_ );
0257       info.set( QLatin1String( "discid" ), discid_ );
0258       info.set( QLatin1String( "source" ), QLatin1String( "freedb" ) );
0259       cdInfoList_.append( info );
0260     }
0261 
0262     cdInfoBuffer_.clear();
0263   }
0264 
0265     void
0266   AsyncCDDBPLookup::doQuit()
0267   {
0268     state_ = WaitingForQuitResponse;
0269 
0270     sendQuit();
0271   }
0272 
0273     QString
0274   AsyncCDDBPLookup::stateToString() const
0275   {
0276     switch (state_)
0277     {
0278       case Idle:
0279         return QLatin1String( "Idle" );
0280         break;
0281 
0282       case WaitingForConnection:
0283         return QLatin1String( "WaitingForConnection" );
0284         break;
0285 
0286       case WaitingForGreeting:
0287         return QLatin1String( "WaitingForGreeting" );
0288         break;
0289 
0290       case WaitingForProtoResponse:
0291         return QLatin1String( "WaitingForProtoResponse" );
0292         break;
0293 
0294       case WaitingForHandshake:
0295         return QLatin1String( "WaitingForHandshake" );
0296         break;
0297 
0298       case WaitingForQueryResponse:
0299         return QLatin1String( "WaitingForQueryResponse" );
0300         break;
0301 
0302       case WaitingForMoreMatches:
0303         return QLatin1String( "WaitingForMoreMatches" );
0304         break;
0305 
0306       case WaitingForCDInfoResponse:
0307         return QLatin1String( "WaitingForCDInfoResponse" );
0308         break;
0309 
0310       case WaitingForCDInfoData:
0311         return QLatin1String( "WaitingForCDInfoData" );
0312         break;
0313 
0314       case WaitingForQuitResponse:
0315         return QLatin1String( "WaitingForQuitResponse" );
0316         break;
0317 
0318       default:
0319         return QLatin1String( "Unknown" );
0320         break;
0321     }
0322   }
0323 }
0324 
0325 #include "moc_asynccddbplookup.cpp"
0326 
0327 // vim:tabstop=2:shiftwidth=2:expandtab:cinoptions=(s,U1,m1