1 /* 2 Copyright (c) 2007-2019 by Jakob Schröter <js@camaya.net> 3 This file is part of the gloox library. http://camaya.net/gloox 4 5 This software is distributed under a license. The full license 6 agreement can be found in the file LICENSE in this distribution. 7 This software may not be copied, modified, sold or distributed 8 other than expressed in the named license agreement. 9 10 This software is distributed without any warranty. 11 */ 12 13 14 #ifndef SIPROFILEFT_H__ 15 #define SIPROFILEFT_H__ 16 17 #include "iqhandler.h" 18 #include "socks5bytestreammanager.h" 19 #include "siprofilehandler.h" 20 #include "sihandler.h" 21 #include "simanager.h" 22 #include "bytestreamhandler.h" 23 24 #include <string> 25 #include <map> 26 27 namespace gloox 28 { 29 30 class ClientBase; 31 class InBandBytestream; 32 class IQ; 33 class JID; 34 class SIProfileFTHandler; 35 class SOCKS5Bytestream; 36 37 /** 38 * @brief An implementation of the file transfer SI profile (@xep{0096}). 39 * 40 * An SIProfileFT object acts as a 'plugin' to the SIManager. SIProfileFT 41 * manages most of the file transfer functionality. The naming comes from the fact that 42 * File Transfer (FT) is a profile of Stream Initiation (SI). 43 * 44 * Usage: 45 * 46 * Create a new SIProfileFT object. It needs a ClientBase -derived object (e.g. Client) 47 * as well as a SIProfileFTHandler -derived object that will receive file transfer-related events. 48 * If you already use SI and the SIManager somewhere else, you should pass a pointer to that 49 * SIManager object as third parameter to SIProfileFT's constructor. 50 * @code 51 * class MyFileTransferHandler : public SIProfileFTHandler 52 * { 53 * // ... 54 * }; 55 * 56 * Client* client = new Client( ... ); 57 * // ... 58 * MyFileTransferHandler* mh = new MyFileTransferHandler( ... ); 59 * 60 * SIProfileFT* ft = new SIProfileFT( client, mh ); 61 * @endcode 62 * 63 * You are now, basically, ready to send and receive files. 64 * 65 * A couple of notes: 66 * @li There are two (actually two and a half) possible "techniques" to transfer files 67 * using SI. The first is using a peer-to-peer SOCKS5 bytestream, optionally via a 68 * (special) SOCKS5 proxy. 69 * The second techniques is using an in-band bytestream, i.e. the data is encapsulated in XMPP stanzas 70 * and sent through the server. 71 * 72 * @li To be able to send files using the former method (SOCKS5 bytestreams), you may need 73 * access to a SOCKS5 bytestream proxy (called StreamHost). This is especially true if either 74 * or both of sender and receiver are behind NATs or are otherwise blocked from establishing 75 * direct TCP connections. You should use Disco to query a potential SOCKS5 proxy 76 * for its host and port parameters and feed that information into SIProfileFT: 77 * @code 78 * ft->addStreamHost( JID( "proxy.server.dom" ), "101.102.103.104", 6677 ); 79 * @endcode 80 * You should @b not hard-code this information (esp. host/IP and port) into your app since 81 * the proxy may go down occasionally or vanish completely. 82 * 83 * @li In addition to (or even instead of) using external SOCKS5 proxies, you can use a 84 * SOCKS5BytestreamServer object that gloox provides: 85 * @code 86 * SOCKS5BytestreamServer* server = new SOCKS5BytestreamServer( client->logInstance(), 1234 ); 87 * if( server->listen() != ConnNoError ) 88 * printf( "port in use\n" ); 89 * 90 * ft->addStreamHost( client->jid(), my_ip, 1234 ); 91 * ft->registerSOCKS5BytestreamServer( server ); 92 * @endcode 93 * This listening server should then be integrated into your mainloop to have its 94 * @link gloox::SOCKS5BytestreamServer::recv() recv() @endlink method called from time to time. 95 * It is safe to put the server into its own thread. 96 * 97 * @li When you finally receive a Bytestream via the SIProfileFTHandler, you will need 98 * to integrate this bytestream with your mainloop, or put it into a separate thread (if 99 * occasional blocking is not acceptable). You will need to call 100 * @link gloox::Bytestream::connect() connect() @endlink on that Bytestream. For SOCKS5 bytestreams, 101 * this function will try to connect to each of the given StreamHosts and block until it has established 102 * a connection with one of them (or until all attempts failed). Further, if you want to receive 103 * a file via the bytestream, you will have to call recv() on the object from time to time. 104 * For in-band bytestreams, @link gloox::Bytestream::connect() connect() @endlink will send an "open the 105 * bytestream" request to the contact. 106 * 107 * @li For both stream types, 108 * @link gloox::BytestreamDataHandler::handleBytestreamOpen() BytestreamDataHandler::handleBytestreamOpen() @endlink 109 * will announce the established bytestream. The stream then is ready to send and receive data. 110 * 111 * @li In general, both types of streams can be handled equally, i.e. there's no need to know whether 112 * the underlying stream really is a SOCKS5Bytestream or an InBandBytestream. 113 * @link gloox::Bytestream::type() Bytestream::type() @endlink tells anyway. Note, however, that 114 * sending large amounts of data using in-band bytestreams may trigger rate limiting in some servers. 115 * 116 * @li If you e.g. told Client to connect through a @link gloox::ConnectionHTTPProxy HTTP proxy @endlink 117 * or a @link gloox::ConnectionSOCKS5Proxy SOCKS5 proxy @endlink, or any other ConnectionBase -derived 118 * method, or even chains thereof, SIProfileFT will use the same connection types with the same 119 * configuration to connect to the Stream Host/SOCKS5 proxy. If this is inappropriate because you have 120 * e.g. a local SOCKS5 proxy inside your local network, use SOCKS5Bytestream::setConnectionImpl() to 121 * override the above default connection(s). 122 * 123 * @li Do @b not delete Bytestream objects manually. Use dispose() instead. 124 * 125 * @li When using the Client's JID as the first argument to addStreamHost() as in the code snippet 126 * above, make sure the JID is actually a full JID. If you let the server pick a resource, the call 127 * to Client::jid() needs to be made @b after the connection has been established and authenticated, 128 * because only then Client knows its full JID. This is generally a good idea, since the server 129 * may choose to change the resource, even if you provided one at login. 130 * 131 * @li The interal SOCKS5BytestreamServer will obviously not work across NATs. 132 * 133 * @li Using addStreamHost(), you can add as many potential StreamHosts as you like. However, you 134 * should add the best options (e.g. the local SOCKS5BytestreamServer) first. 135 * 136 * When cleaning up, delete the objectes you created above in the opposite order of 137 * creation: 138 * 139 * @code 140 * delete server 141 * delete ft; 142 * delete client; 143 * @endcode 144 * 145 * For usage examples see src/examples/ft_send.cpp and src/examples/ft_recv.cpp. 146 * 147 * @author Jakob Schröter <js@camaya.net> 148 * @since 0.9 149 */ 150 class GLOOX_API SIProfileFT : public SIProfileHandler, public SIHandler, 151 public BytestreamHandler, public IqHandler 152 { 153 public: 154 /** 155 * Supported stream types. 156 */ 157 enum StreamType 158 { 159 FTTypeS5B = 1, /**< SOCKS5 Bytestreams. */ 160 FTTypeIBB = 2, /**< In-Band Bytestreams. */ 161 FTTypeOOB = 4, /**< Out-of-Band Data. */ 162 FTTypeAll = 0xFF /**< All types. */ 163 }; 164 165 /** 166 * Constructor. 167 * @param parent The ClientBase to use for signaling. 168 * @param sipfth The SIProfileFTHandler to receive events. 169 * @param manager An optional SIManager to register with. If this is zero, SIProfileFT 170 * will create its own SIManager. You should pass a valid SIManager here if you are 171 * already using one with the @c parent ClientBase above. 172 * @param s5Manager An optional SOCKS5BytestreamManager to use. If this is zero, SIProfileFT 173 * will create its own SOCKS5BytestreamManager. You should pass a valid SOCKS5BytestreamManager 174 * here if you are already using one with the @c parent ClientBase above. 175 * @note If you passed a SIManager and/or SOCKS5BytestreamManager and/or InBandBytestreamManager 176 * to SIProfileFT's constructor, these objects will @b not be deleted on desctruction of SIProfileFT. 177 */ 178 SIProfileFT( ClientBase* parent, SIProfileFTHandler* sipfth, SIManager* manager = 0, 179 SOCKS5BytestreamManager* s5Manager = 0 ); 180 181 /** 182 * Virtual destructor. 183 */ 184 virtual ~SIProfileFT(); 185 186 /** 187 * Starts negotiating a file transfer with a remote entity. 188 * @param to The entity to send the file to. Must be a full JID. 189 * @param name The file's name. Mandatory and must not be empty. 190 * @param size The file's size. Mandatory and must be > 0. 191 * @param hash The file content's MD5 hash. 192 * @param desc A description. 193 * @param date The file's last modification date/time. See @xep{0082} for details. 194 * @param mimetype The file's mime-type. Defaults to 'binary/octet-stream' if empty. 195 * @param streamTypes ORed StreamType that can be used for this transfer. 196 * @param from An optional 'from' address to stamp outgoing requests with. 197 * Used in component scenario only. Defaults to empty JID. 198 * @param sid Optionally specify a stream ID (SID). If empty, one will be generated. 199 * @return The requested stream's ID (SID). Empty if conditions above (file name, size) 200 * are not met. 201 */ 202 const std::string requestFT( const JID& to, const std::string& name, long size, 203 const std::string& hash = EmptyString, 204 const std::string& desc = EmptyString, 205 const std::string& date = EmptyString, 206 const std::string& mimetype = EmptyString, 207 int streamTypes = FTTypeAll, 208 const JID& from = JID(), 209 const std::string& sid = EmptyString ); 210 211 /** 212 * Call this function to accept a file transfer request previously announced by means of 213 * @link gloox::SIProfileFTHandler::handleFTRequest() SIProfileFTHandler::handleFTRequest() @endlink. 214 * @param to The requestor. 215 * @param sid The request's sid, as passed to SIProfileHandler::handleFTRequest(). 216 * @param type The desired stream type to use for this file transfer. Defaults to 217 * SOCKS5 Bytestream. You should not use @c FTTypeAll here. 218 * @param from An optional 'from' address to stamp outgoing stanzas with. 219 * Used in component scenario only. Defaults to empty JID. 220 */ 221 void acceptFT( const JID& to, const std::string& sid, 222 StreamType type = FTTypeS5B, const JID& from = JID() ); 223 224 /** 225 * Call this function to decline a FT request previously announced by means of 226 * @link gloox::SIProfileFTHandler::handleFTRequest() SIProfileFTHandler::handleFTRequest() @endlink. 227 * @param to The requestor. 228 * @param sid The request's sid, as passed to SIProfileFTHandler::handleFTRequest(). 229 * @param reason The reason for the reject. 230 * @param text An optional human-readable text explaining the decline. 231 */ 232 void declineFT( const JID& to, const std::string& sid, SIManager::SIError reason, 233 const std::string& text = EmptyString ); 234 235 /** 236 * Cancels the given bytestream. Most useful for SOCKS5 bytestreams where no proxies could be found. 237 * The given Bytestream will be deleted. 238 * @param bs The Bytestream to cancel. 239 * @note Can also be used with IBB. 240 */ 241 void cancel( Bytestream* bs ); 242 243 /** 244 * To get rid of a bytestream (i.e., close and delete it), call this function. 245 * The remote entity will be notified about the closing of the stream. 246 * @param bs The bytestream to dispose. It will be deleted here. 247 */ 248 void dispose( Bytestream* bs ); 249 250 /** 251 * Registers a handler that will be informed about incoming file transfer 252 * requests, i.e. when a remote entity wishes to send a file. 253 * @param sipfth A SIProfileFTHandler to register. Only one handler can be registered 254 * at any one time. 255 */ registerSIProfileFTHandler(SIProfileFTHandler * sipfth)256 void registerSIProfileFTHandler( SIProfileFTHandler* sipfth ) { m_handler = sipfth; } 257 258 /** 259 * Removes the previously registered file transfer request handler. 260 */ removeSIProfileFTHandler()261 void removeSIProfileFTHandler() { m_handler = 0; } 262 263 /** 264 * Sets a list of StreamHosts that will be used for subsequent SOCKS5 bytestream requests. 265 * @note At least one StreamHost is required. 266 * @param hosts A list of StreamHosts. 267 */ 268 void setStreamHosts( StreamHostList hosts ); 269 270 /** 271 * Adds one StreamHost to the list of SOCKS5 StreamHosts. 272 * @param jid The StreamHost's JID. 273 * @param host The StreamHost's hostname. 274 * @param port The StreamHost's port. 275 */ 276 void addStreamHost( const JID& jid, const std::string& host, int port ); 277 278 /** 279 * Tells the interal SOCKS5BytestreamManager which SOCKS5BytestreamServer handles 280 * peer-2-peer SOCKS5 bytestreams. 281 * @param server The SOCKS5BytestreamServer to use. 282 */ registerSOCKS5BytestreamServer(SOCKS5BytestreamServer * server)283 void registerSOCKS5BytestreamServer( SOCKS5BytestreamServer* server ) 284 { if( m_socks5Manager ) m_socks5Manager->registerSOCKS5BytestreamServer( server ); } 285 286 /** 287 * Un-registers any local SOCKS5BytestreamServer. 288 */ removeSOCKS5BytestreamServer()289 void removeSOCKS5BytestreamServer() 290 { if( m_socks5Manager ) m_socks5Manager->removeSOCKS5BytestreamServer(); } 291 292 // reimplemented from SIProfileHandler 293 virtual void handleSIRequest( const JID& from, const JID& to, const std::string& id, 294 const SIManager::SI& si ); 295 296 // reimplemented from SIHandler 297 virtual void handleSIRequestResult( const JID& from, const JID& to, const std::string& sid, 298 const SIManager::SI& si ); 299 300 // reimplemented from SIHandler 301 virtual void handleSIRequestError( const IQ& iq, const std::string& sid ); 302 303 // reimplemented from BytestreamHandler 304 virtual void handleIncomingBytestreamRequest( const std::string& sid, const JID& from ); 305 306 // reimplemented from BytestreamHandler 307 virtual void handleIncomingBytestream( Bytestream* bs ); 308 309 // reimplemented from BytestreamHandler 310 virtual void handleOutgoingBytestream( Bytestream* bs ); 311 312 // reimplemented from BytestreamHandler 313 virtual void handleBytestreamError( const IQ& iq, const std::string& sid ); 314 315 // reimplemented from IqHandler. handleIq(const IQ & iq)316 virtual bool handleIq( const IQ& iq ) { (void)iq; return false; } 317 318 // reimplemented from IqHandler. 319 virtual void handleIqID( const IQ& iq, int context ); 320 321 private: 322 323 enum TrackEnum 324 { 325 OOBSent 326 }; 327 328 ClientBase* m_parent; 329 SIManager* m_manager; 330 SIProfileFTHandler* m_handler; 331 SOCKS5BytestreamManager* m_socks5Manager; 332 StreamHostList m_hosts; 333 StringMap m_id2sid; 334 bool m_delManager; 335 bool m_delS5Manager; 336 337 }; 338 339 } 340 341 #endif // SIPROFILEFT_H__ 342