Session.cpp

Go to the documentation of this file.
00001 /*
00002 
00003 Copyright (C) 2006-2007 by Peter Dimov.
00004 
00005 This file is part of Calitko (http://www.calitko.org).
00006 
00007 Calitko is free software; you can redistribute it and/or modify
00008 it under the terms of the GNU General Public License as published by
00009 the Free Software Foundation; either version 2 of the License, or
00010 (at your option) any later version.
00011 
00012 Calitko is distributed in the hope that it will be useful,
00013 but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 GNU General Public License for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Calitko; if not, write to the Free Software
00019 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020 
00021 */
00022 
00023 #include "Qt.h"
00024 #include "Session.h"
00025 #include "Imports.cpp"
00026 
00027 namespace Protocols {
00028 namespace BitTorrent {
00029 namespace Transfers {
00030 namespace Constants {
00031 
00032     const QByteArray ProtocolVersion        = "BitTorrent protocol";
00033     const QByteArray ProtocolReservedBytes  = QByteArray (8, '\0');
00034     const uchar ProtocolVersionLength       = (uchar) ProtocolVersion.length();
00035     const int ReservedBytesCount            = 8;
00036     const int RawSha1HashLength             = 20;
00037     const int PeerIdLength                  = 20;
00038     const int HandshakeLength               = sizeof (ProtocolVersionLength) +
00039                                               ProtocolVersionLength +
00040                                               ReservedBytesCount +
00041                                               RawSha1HashLength + PeerIdLength;
00042 
00043 } // namespace Constants
00044 } // namespace Transfers
00045 } // namespace BitTorrent
00046 } // namespace Protocols
00047 
00048 Session::Session (Transport &t, SessionStatus &s, const PeerInfo &lpi)
00049   : transport (t), status (s), localPeerInfo (lpi),
00050     fileId (QByteArray (Constants::RawSha1HashLength, '\0')),
00051     state (DisconnectedTransport)
00052 {
00053 }
00054 
00055 Session::~Session()
00056 {
00057 }
00058 
00059 void Session::establish (const PeerInfo &pi, const FileId &fi)
00060 {
00061     state = EstablishingSession;
00062     remotePeerInfo = pi;
00063     fileId = fi;
00064     status.establishingSession();
00065     writeHandshake();
00066     if (readHandshake()) {
00067         state = ExchangingPackets;
00068         status.sessionEstablished();
00069         // \todo readPacket() ??
00070     }
00071 }
00072 
00073 void Session::waitRequest()
00074 {
00075     state = AwaitingRequest;
00076     status.establishingSession();
00077     if (readHandshake()) {
00078         state = AwaitingAccept;
00079         status.sessionRequestedFor (fileId);
00080     }
00081 }
00082 
00083 void Session::accept()
00084 {
00085     Q_ASSERT (state == AwaitingAccept);
00086     writeHandshake();
00087     state = ExchangingPackets;
00088     status.sessionEstablished();
00089     // \todo readPacket() ??
00090 }
00091 
00092 void Session::connected()
00093 {
00094     Q_ASSERT (false); // Should never be called. Should have been connected.
00095 }
00096 
00097 void Session::readyRead()
00098 {
00099     Q_ASSERT (state != DisconnectedTransport);
00100     switch (state)
00101     {
00102     case EstablishingSession:
00103         if (!readHandshake())
00104             break;
00105         state = ExchangingPackets;
00106         status.sessionEstablished();
00107         // \todo readPacket() ??
00108         break;
00109     case AwaitingRequest:
00110         if (!readHandshake())
00111             break;
00112         state = AwaitingAccept;
00113         status.sessionRequestedFor (fileId);
00114         break;
00115     case AwaitingAccept:
00116         // Ignore data until accept() is called.
00117         break;
00118     case ExchangingPackets:
00119         readPacket();
00120         break;
00121     case DisconnectedTransport:
00122         Q_ASSERT (false);
00123         break;
00124     }
00125 }
00126 
00127 void Session::readyWrite()
00128 {
00129 }
00130 
00131 void Session::disconnected()
00132 {
00133     state = DisconnectedTransport;
00134     status.sessionClosed();
00135 }
00136 
00138 
00146 bool Session::readHandshake()
00147 {
00148     using namespace Constants;
00149     QByteArray handshakeBytes = transport.read (HandshakeLength);
00150     // The complete handshake is not fully buffered by transport.
00151     if (handshakeBytes.size() == 0)
00152         return false;
00153     // Parse the handshake:
00154     BinaryReader reader (handshakeBytes, BinaryReader::BigEndian);
00155     // Verify protocol:
00156     uchar protocolVersionLength = reader.readByte();
00157     if (protocolVersionLength != ProtocolVersionLength)
00158         return false;
00159     QByteArray protocol = reader.readBytes (ProtocolVersionLength);
00160     if (protocol != ProtocolVersion)
00161         return false;
00162     QByteArray reserved = reader.readBytes (ReservedBytesCount);
00163     fileId = reader.readBytes (RawSha1HashLength);
00164     remotePeerInfo.id = reader.readBytes (PeerIdLength);
00165     // Successfully parsed the handshake.
00166     return true;
00167 }
00168 
00170 void Session::writeHandshake()
00171 {
00172     using namespace Constants;
00173     // \todo do we really need the BinaryReader? Seems like not. Better wait
00174     // to see whether we'll need some special case for fileId and peerId though.
00175     BinaryWriter writer (BinaryWriter::BigEndian);
00176     writer.reserve (HandshakeLength);
00177 
00178     writer.writeByte (ProtocolVersionLength);
00179     writer.writeBytes (ProtocolVersion);
00180     writer.writeBytes (ProtocolReservedBytes);
00181     writer.writeBytes (fileId);
00182     writer.writeBytes (localPeerInfo.id);
00183     Q_ASSERT (writer.hasWrittenAll() && !writer.hasWrittenPastEnd());
00184 
00185     bool hasWritten = transport.write (writer.buffer());
00186     // The handshake is the first thing we write and the buffer should be empty:
00187     Q_ASSERT (hasWritten == true);
00188 }
00189 
00190 void Session::readPacket()
00191 {
00192     //const SharedPointer <Packet> packet = packetFactory.readPacket (transport);
00193     // \todo use a PacketDispatcher:
00194     //packetDispatcher.dispatch (*packet);
00195     /*if (typeid (*packet) == typeid (Choke))
00196         status.sessionChoked();
00197     if (typeid (*packet) == typeid (Unchoke))
00198         status.sessionUnchoked();
00199     if (typeid (*packet) == typeid (Interested))
00200         status.sessionInterested();
00201     if (typeid (*packet) == typeid (NotInterested))
00202         status.sessionNotInterested();*/
00203 }
00204 
00205 void Session::writePacket()
00206 {
00207 }