#include <QtHistoryState>
#include <QtState>
+#include <QCoreApplication>
#include <QUdpSocket>
#include <QTimer>
authCommand = new AuthCommand(this);
QObject::connect(authCommand, SIGNAL(replyReady(bool)), this, SLOT(doAuthenticate(bool)));
+ logoutCommand = new LogoutCommand(this);
+
setFloodInterval(5);
stateMachine = new QtStateMachine(this);
connectingState = new QtState;
connectedState = new QtState;
authenticatingState = new QtState(connectedState);
- authenticatedState = new QtState(connectedState);
+// authenticatedState = new QtState(connectedState);
idleState = new QtState(connectedState);
idleTimeoutState = new QtState(connectedState);
- logoutState = new QtState(connectedState);
- loggedOutState = new QtState(connectedState);
+// logoutState = new QtState(connectedState);
+// loggedOutState = new QtState(connectedState);
sendState = new QtState(connectedState);
waitState = new QtState(connectedState);
recieveState = new QtState;
stateMachine->setInitialState(disconnectedState);
stateMachine->setErrorState(errorState);
- connectedState->setInitialState(authenticatingState);
- connectedHistoryState->setDefaultState(authenticatingState);
- // ------------- Transitions ---------------------
+ connectedState->setInitialState(sendState);
+ connectedHistoryState->setDefaultState(sendState);
+ // ------------- Transitions ---------------------
connectedState->addTransition(this, SIGNAL(startDisconnecting()), disconnectedState);
connectedState->addTransition(socket, SIGNAL(readyRead()), recieveState);
connectedState->addTransition(this, SIGNAL(sendFailed()), recieveFailState);
idleState->addTransition(this, SIGNAL(startSending()), sendState);
idleState->addTransition(idleTimer, SIGNAL(timeout()), idleTimeoutState);
+// idleState->addTransition(this, SIGNAL(startLogout()), logoutState);
- idleTimeoutState->addTransition(this, SIGNAL(startLogout()), logoutState);
-
- logoutState->addTransition(this, SIGNAL(loggedOut()), loggedOutState);
+ idleTimeoutState->addTransition(this, SIGNAL(startSending()), sendState);
+//
+// logoutState->addTransition(this, SIGNAL(loggedOut()), loggedOutState);
- recieveState->addTransition(this, SIGNAL(authenticated()), sendState);
- recieveState->addTransition(this, SIGNAL(loggedOut()), loggedOutState);
+ recieveState->addTransition(this, SIGNAL(authenticated()), authenticatingState);
+// recieveState->addTransition(this, SIGNAL(loggedOut()), loggedOutState);
recieveState->addTransition(connectedHistoryState);
- recieveFailState->addTransition(connectedHistoryState);
+ recieveFailState->addTransition(sendState);
// ------------ END Transitions -------------------
// ------------- Methods ---------------------
idleState->invokeMethodOnEntry(this, "enterIdleState");
idleState->invokeMethodOnExit(this, "exitIdleState");
idleTimeoutState->invokeMethodOnEntry(this, "enterIdleTiemoutState");
- logoutState->invokeMethodOnEntry(this, "enterLogoutState");
- loggedOutState->invokeMethodOnEntry(this, "enterLoggedOutState");
+// logoutState->invokeMethodOnEntry(this, "enterLogoutState");
+// loggedOutState->invokeMethodOnEntry(this, "enterLoggedOutState");
recieveState->invokeMethodOnExit(this, "exitRecieveState");
recieveFailState->invokeMethodOnEntry(this, "enterRecieveFailState");
{
disconnect();
clearCommandQueue();
+
+ if (!m_sessionId.isEmpty())
+ {
+ while (commandTimer->isActive())
+ QCoreApplication::processEvents();
+
+ sendCommand(new LogoutCommand);
+ socket->waitForBytesWritten(5);
+ }
}
QString AniDBUdpClient::host() const
void AniDBUdpClient::setUser(const QString &user)
{
- // All usernames are lowercaase
- m_user = user.toLower();
+ m_user = user;
}
QString AniDBUdpClient::pass() const
return m_errorString;
}
+// ------------------------------------------------------------------------------
+
void AniDBUdpClient::enterErrorState()
{
qDebug() << "Entering Error State";
void AniDBUdpClient::enterDisconnectedState()
{
qDebug() << "Entering Disconnected State";
+ if (socket->state() == QAbstractSocket::BoundState)
+ socket->disconnectFromHost();
}
void AniDBUdpClient::enterConnectingState()
{
qDebug() << "Successful connection";
emit connected();
+ return;
}
- else
- {
- m_error = BindError;
- m_errorString = socket->errorString();
+
+ m_error = BindError;
+ m_errorString = socket->errorString();
qDebug() << QString("Bind on Address: %1 port: %2 failed").arg(m_hostAddress.toString()).arg(m_localPort);
- emit connectionError();
- }
+ emit connectionError();
return;
}
QHostInfo::lookupHost(m_host, this, SLOT(lookedUp(QHostInfo)));
}
m_hostAddress = hostInfo.addresses()[0];
- // TODO
+ // TODO could it be nicer?
enterConnectingState();
}
qDebug() << "Entering Authenticating State";
authCommand->setUser(m_user);
authCommand->setPass(m_pass);
+ authCommand->setCompression(m_compression);
- enqueueCommand(authCommand, true);
+ enqueueControlCommand(authCommand, true);
emit startSending();
}
qDebug() << "success!";
m_sessionId = authCommand->sessionId().toUtf8();
emit authenticated();
+ return;
}
- else
- {
- m_error = AuthenticationError;
- emit connectionError();
- }
- authenticateOnConnect = false;
+ m_error = AuthenticationError;
+ m_errorString = authCommand->errorString();
+
+ emit connectionError();
}
void AniDBUdpClient::enterSendState()
{
qDebug() << "Entering Send State";
+
+ // Do not send commands if wait time didn't end
+ // Happens if sendState is entered from recv* states.
+ if (commandTimer->isActive())
+ {
+ emit commandSent();
+ return;
+ }
+
+ // Control commands (auth and such) have priority over any other commands.
+ if (!controlCommandQueue.isEmpty())
+ {
+ sendCommand(controlCommandQueue.dequeue());
+ emit commandSent();
+ return;
+ }
+
if (commandQueue.isEmpty())
{
emit queueEmpty();
void AniDBUdpClient::enterWaitState()
{
qDebug() << "Entering Wait State";
- commandTimer->start(m_floodInterval);
+
+ // Do not restart timer if it is active
+ if (!commandTimer->isActive())
+ commandTimer->start(m_floodInterval);
}
void AniDBUdpClient::enterIdleState()
{
qDebug() << "Entering Idle State";
+qDebug() << m_idlePolicy;
+
+ // If not loogged in, nothing has to be done in idle state.
+ if (m_sessionId.isEmpty())
+ return;
+
switch (m_idlePolicy)
{
case KeepAliveIdlePolicy:
+ case LogoutIdlePolicy:
idleTimer->start(UDP_API_INACTIVITY_LOGOUT * 1000);
break;
- case LogoutIdlePolicy:
- emit startLogout();
+ case KeepAliveIdlePolicy:
+ idleTimer->start(UDP_API_INACTIVITY_UPDATE * 1000);
+ break;
+ case ImmediateLogoutIdlePolicy:
+ enqueueControlCommand(logoutCommand);
+ emit startSending();
break;
default:
break;
qDebug() << "Entering IdleTiemout State";
switch (m_idlePolicy)
{
+ case DoNothingIdlePolicy:
+ m_sessionId = "";
+ break;
case KeepAliveIdlePolicy:
+ enqueueControlCommand(updateCommand);
+ emit startSending();
default:
break;
}
}
-
+/*
void AniDBUdpClient::enterLogoutState()
{
qDebug() << "Entering Logout State";
- enqueueCommand(new LogoutCommand);
- emit startSending();
+ logout();
}
void AniDBUdpClient::enterLoggedOutState()
qDebug() << "Entering LoggedOut State";
m_sessionId = "";
}
+*/
void AniDBUdpClient::exitRecieveState()
{
QByteArray commandId = tmp.mid(0, 5);
- // Do not parse reply for commands not waiting for a reply.
+ // Do not parse reply for unknown commands.
if (!sentCommands.contains(commandId))
{
-qDebug() << QString("Command with id: %1 is not waiting for a reply, discarding").arg(commandId.constData());
+ qDebug() << QString("Unknown command with id: %1, discarding")
+ .arg(commandId.constData());
continue;
}
-qDebug() << QString("Sending reply to command with id: %1").arg(commandId.constData());
+ qDebug() << QString("Sending reply to command with id: %1")
+ .arg(commandId.constData());
// tag + space = 5 + 1
QByteArray replyCodeText = tmp.mid(6, 3);
replyCode = AbstractCommand::ReplyCode(replyCodeInt);
}
- AbstractCommand *cmd = sentCommands.take(commandId);
+ CommandData *commandData = sentCommands.take(commandId);
+ AbstractCommand *cmd = commandData->command;
+ delete commandData;
// Requeue command and reauthenticate if not logged in.
switch (replyCode)
void AniDBUdpClient::logout()
{
- emit startLogout();
+ enqueueCommand(new LogoutCommand);
+ emit startSending();
}
void AniDBUdpClient::enqueueCommand(AbstractCommand *command, bool first)
emit startSending();
}
-void AniDBUdpClient::sendCommand(AbstractCommand *command)
+void AniDBUdpClient::enqueueControlCommand(AbstractCommand *command, bool first)
+{
+ if (first)
+ {
+ controlCommandQueue.push_front(command);
+ }
+ else
+ {
+ controlCommandQueue.enqueue(command);
+ }
+
+ emit startSending();
+}
+
+void AniDBUdpClient::sendCommand(AbstractCommand *command, bool controlCommand)
{
+ if (m_sessionId.isEmpty() && command->requiresSession())
+ {
+ if (controlCommand)
+ enqueueControlCommand(command, true);
+ else
+ enqueueCommand(command, true);
+ emit startAuthentication();
+ return;
+ }
+
Command cmdPair = command->rawCommand();
QByteArray datagram = buildCmd(cmdPair.first, cmdPair.second);
if (command->waitForResult())
{
- sentCommands[commandId] = command;
+ sentCommands[commandId] = new CommandData(command, controlCommand);
}
else
{
qDebug() << QString("Generated id %1").arg(result.constData());
return result;
}
+
+AniDBUdpClient::CommandData::CommandData(AbstractCommand *command, bool controlCommand)
+{
+ this->command = command;
+ this->controlCommand = controlCommand;
+}
+
+