/*************************************************************************** applicationlist.h - description ------------------- begin : Mon 01 16 2005 copyright : (C) 2005 by Diederik van der Boor email : "vdboor" --at-- "codingdomain.com" ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef APPLICATIONLIST_H #define APPLICATIONLIST_H #include <QList> #include <QTimer> class Application; class MimeApplication; class P2PApplication; class P2PApplicationBase; class P2PMessage; class MimeMessage; class ChatMessage; class MsnObject; class DirectConnectionPool; class MsnDirectConnection; class MsnSwitchboardConnection; /** * @brief Maintenance of applications and direct connections for one contact. * * This class is used in the following message flow: * - the MsnSwitchboardConnection receives a P2P or mime message. * - the ChatMaster relays it to the correct contact. * - the ApplicationList of that contact relays it to the correct session (MimeApplication or P2PApplication). * - an Application instance of processes it. * * This class has the intermediate role between the ChatMaster and Application classes. * Each Contact has a reference to one ApplicationList object, created with Contact::createApplicationList(). * Each P2PApplication class has a reference to this class as well to access the direct connection. * The direct connection is re-used by applications of the same contact. * * The ChatMaster delivers the message with gotMessage(). * When an incoming message starts a new invitation (creating a new application object), * it's registered automatically in this class. The application signals putMsg() and deleteMe() * are handled internally by this class. Other signals (notably status messages) are not handled. * Instead, the newApplication() signal is fired so the ChatMaster can connect to those signals. * * When a mesage needs to be sent back, the application calls sendMessage(). * This method either delivers the message through the direct connection, or it emits a putMsg() signal. * This signal is picked up be the ChatMaster to deliver the message at one of the available switchboard connections. * Sometimes, a switchboard connection is unable to send more messages. * The ChatMaster calls pauseApplications() so the queue won't be flooded. * * When a P2PApplication needs to establish a direct connection (a MsnDirectConnection class), * it calls addConnection() or addServerConnection(). This class reports back whether * a connection attempt succeeded, or all connection attempts failed. * Internally, a DirectConnectionPool is used to handle multiple connection attempts. * * The "in-between role" of this class appeared to be a must for peer-to-peer applications (P2PApplication instances), * especially in combination with direct connections. The MSNP2P features pose some difficult design choices: * - P2P applications are not limited to one swithboard session (aka chat session). * - Clients may use any active switchboard or direct connection to deliver MSNP2P packets to the peer contact. * - A direct connection is even re-used by other invitations with the same contact, for both new and existing invitations! * - The initialized P2P applications can remain active through the direct connection after the switchboard is closed. * - Multiple P2P applications could require a direct connection at the same time, but only the first one creates the connection. The other applications have to wait until the connection attempt succeeds or fails. * * This protocol logic makes the client implementations heavier, but it's more efficient on network resources * (hence MSNC* is about moving protocol logic to the clients). * * The method calls to start a P2P data transfer are pretty complex. * This happens because each situation needs a fallback to keep the transfer running. * The usual path for transfers is: * - the application calls addConnection() or addServerConnection() * - the application receives the connectionEstablished() signal. * - the application authenticates the direct connection. * - the application receives the connectionAuthorized() signal. * - the application starts the data transfer by calling registerDataSendingApplication(). * - internally, slotConnectionReadyWrite() is called. * - the application is informed to send more data by the P2PApplication::sendNextDataParts() method. * * If the connection attempt or authenticaion fails, the connectionFailed() signal is called. * This causes the applications to start their transfer as well with registerDataSendingApplication(). * In this case, the transfer takes place over the switchboard. Internally, the slotSwitchboardTestReadyWrite() * invokes the calls to the P2PApplication::sendNextDataParts() method. * * If the connection is closed in any event, all transfers switch * from slotConnectionReadyWrite() to slotSwitchboardTestReadyWrite(). * The reverse happens when a different invitation manages to establish a direct connection. * This guarantees the transfer continues, or takes advantage of high speed whenever possible. * * @author Diederik van der Boor * @ingroup Applications */ 00105 class ApplicationList : public QObject { Q_OBJECT public: // The constructor ApplicationList(const QString &contactHandle); // The destructor virtual ~ApplicationList(); // Add a new application to the list. void addApplication(MimeApplication *application); // Add a new application to the list. void addApplication(P2PApplication *application); // Attempt to establish a direct connection at the given ipaddress/port. bool addConnection(const QString &ipAddress, const quint16 port); // Attempt to establish a direct connection by listening at an random port. int addServerConnection(); // Abort all applications, the contact is leaving a chat. bool contactLeavingChat(const MsnSwitchboardConnection *connection, bool userInitiated = false); // Abort all applications, the contact left a chat. bool contactLeftChat(bool userInitiated = false); // Find the application with uses the given cookie. Application * getApplicationByCookie(const QString &cookie) const; // Return the handle of the contact this class manages. const QString & getContactHandle() const; // Deliver a message to the correct application instance void gotMessage(const MimeMessage &message); // Deliver a message to the correct application instance void gotMessage(const P2PMessage &message); // Return the direct connection, if available. MsnDirectConnection * getDirectConnection() const; // Return whether the direct connection is connected and authorized. bool hasAuthorizedDirectConnection() const; // Return whether the application has data sending applications. bool hasDataSendingApplications() const; // Return whether there is an active direct connection. bool hasDirectConnection() const; // Return whether an MsnObject transfer for the given msn object is already running. bool hasMsnObjectTransfer( const MsnObject &msnObject ) const; // Return whether the transfer invitation has been sent. bool hasPendingConnectionInvitation() const; // Return whether there are pending connection attempts. bool hasPendingConnections() const; // Return whether the application list does not have any applications. bool isEmpty() const; // Pause all applications, avoid flooding the switchboard message queue. void pauseApplications(); // Register an application which is sending a file. void registerDataSendingApplication( P2PApplicationBase *application ); // Resume all applications, allowing messages to be sent again. void resumeApplications(bool isPrivateChat = true); // Send a message for a P2P application over one of the available connections/bridges. bool sendMessage(const P2PApplicationBase *source, const QByteArray &header, const QByteArray &messageData = 0, uint footerCode = 0); // Whether an application is adding connections, don't send "all failed" signal. void setAddingConnections(bool state); // Set whether the transfer invitation has been sent. void setPendingConnectionInvitation(bool state); // Remove the registration of an application which sends a file. void unregisterDataSendingApplication( P2PApplicationBase *application ); public slots: // Send a message for a Mime application. void sendMessage(const MimeApplication *source, const MimeMessage &messageData); private: // private methods // Abort all applications, the connection is closing or closed bool abortApplications(const MsnSwitchboardConnection *abortingConnection = 0, bool userInitiated = false); // Connect the application object signals. void connectApplication(Application *app); // Create the application instance for the given invitation message. MimeApplication * createApplication(const MimeMessage &message); // Create the application instance for the given invitation message. P2PApplication * createApplication(const P2PMessage &message); // Create a P2PApplication instance for the given Application-GUID. MimeApplication * createApplicationByGuid(const QString &applicationGuid); // Create a P2PApplication instance for the given EUF-GUID. P2PApplication * createApplicationByEufGuid(const QString &eufGuid); // Find the application which should receive the ACK message. P2PApplication * getApplicationByAckData(const P2PMessage &ackMessage) const; // Find the application which maintains the session for the given CallID. P2PApplication * getApplicationByCallId(const QString &callID) const; // Find the application which received the previous message fragment from the contact. P2PApplication * getApplicationByLastMessage(unsigned long messageID) const; // Find the application which authorizes the transfer for the given nonce. P2PApplication * getApplicationByNonce(const QString &nonce) const; // Find the application identified with the given session ID. P2PApplication * getApplicationBySessionId(unsigned long sessionID) const; // Create the direct connection pool void initializeDirectConnectionPool(); // Reject an invitation for a mime-application. void sendMimeRejectMessage( const QString &invitationCookie, bool notInstalled ); private slots: // The direct connection is authorized. void slotActiveConnectionAuthorized(); // The direct connection was closed. void slotActiveConnectionClosed(); // All direct connection attempts failed. void slotAllConnectionsFailed(); // The direct connection is established. void slotConnectionEstablished(); // The direct connection is ready to send more data. void slotConnectionReadyWrite(); // A message was received from the direct connection. void slotGotDirectConnectionMessage(const QByteArray &messageData); // The switchboard is ready to send more data. void slotSwitchboardTestReadyWrite(); // An application was finished and requested removal. void slotTerminateApplication(Application *application); private: // private attributes // Whether an application is adding connections, don't send "all failed" signal. bool addingConnections_; // A list of all applications that are being aborted. QList<Application*> abortingApplications_; // The contact this class was initiated for. QString contactHandle_; // A list of pointers to all applications which are sending a file. QList<P2PApplicationBase*> dataSendingApplications_; // The direct connection established with the contact MsnDirectConnection *directConnection_; // The list of direct connection attempts DirectConnectionPool *directConnectionPool_; // A list of pointers to all open mime transfers. QList<MimeApplication*> mimeApplications_; // Whether the application sending is paused. bool pauseApplications_; // A list of pointers to all open p2p transfers. QList<P2PApplication*> p2pApplications_; // The direct connection invitation is sent. bool pendingConnectionInvitation_; // The ready-write tester for the switchboard QTimer sbReadyWriteTester_; signals: /** * @brief This signal is fired when all applications aborted. * * This is used by the MsnSwitchboardConnection to close it's sockets after all applications aborted nicely. * @param contact The contact all applications have been aborted for. */ void applicationsAborted(const QString &contact); /** * @brief This signal is fired when the direct connection is authorized. * * This signal notifies other P2PApplication instances the * direct connection is ready to be used for data transfer. * * @see MsnDirectConnection::setAuthorized() */ void connectionAuthorized(); /** * @brief This signal is fired when the direct connection was closed. */ void connectionClosed(); /** * @brief This signal is fired when direct connection is established. * * The first P2PApplication can start to authenticate the connection. */ void connectionEstablished(); /** * @brief This signal is fired when all direct connection attempts failed. * * The P2PApplication objects should revert to the switchboard to send data. */ void connectionFailed(); /** * @brief This signal is fired when a new application was created. * * The ChatMaster uses it to connect to the signals of new applications. */ void newApplication(Application *application); /** * @brief This signal is fired when the ChatMaster should deliver a message to the switchboard. * @param message The message to deliver. It can be an <code>text/x-msmsgsinvite</code> message for MIME applications, or an <code>application/x-msnmsgrp2p</code> message for wrapped P2P data. * @param contactHandle The contact to deliver the message to. * @param privateChatRequired Whether the message can only be delivered in an 1-on-1 chat conversion. */ void putMsg(const MimeMessage &message, const QString &contactHandle, bool privateChatRequired); }; #endif