File: linklocalmanager.h

package info (click to toggle)
gloox 1.0.18-1
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 6,732 kB
  • ctags: 6,556
  • sloc: cpp: 45,479; sh: 11,344; makefile: 1,018
file content (356 lines) | stat: -rw-r--r-- 14,667 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
/*
  Copyright (c) 2012-2016 by Jakob Schröter <js@camaya.net>
  This file is part of the gloox library. http://camaya.net/gloox

  This software is distributed under a license. The full license
  agreement can be found in the file LICENSE in this distribution.
  This software may not be copied, modified, sold or distributed
  other than expressed in the named license agreement.

  This software is distributed without any warranty.
*/

#ifndef LINKLOCALMANAGER_H___
#define LINKLOCALMANAGER_H___

#include "config.h"

#ifdef HAVE_MDNS

#include "linklocal.h"
#include "macros.h"
#include "gloox.h"
#include "util.h"
#include "logsink.h"
#include "connectiontcpserver.h"
#include "mutex.h"
#include "jid.h"

#include <string>

#include <dns_sd.h>

namespace gloox
{

  class ConnectionHandler;
  class ConnectionTCPClient;

  namespace LinkLocal
  {

    class Handler;

    /**
     * @brief This is a manager for server-less messaging (@xep{0174}).
     *
     * Enable compilation of this code with the @c \-\-enable-mdns switch to @c configure, or add
     * @c \#define @c HAVE_MDNS to your platform's @c config.h. @c dns_sd.h, @c libdns_sd.so, as well
     * as the @c mdnsd daemon from Apple's bonjour distribution are required. The @c mdnsd daemon has
     * to be running on the local host.
     *
     * ### Browsing the local network for XMPP services
     *
     * You can use the Manager to browse the local network for XMPP services.
     * First, create a new instance, register a LinkLocal::Handler, and call startBrowsing().
     * @code
     * m_mdns = new LinkLocal::Manager( ... );
     * m_mdns->registerLinkLocalHandler( yourHandler );
     * m_mdns->startBrowsing();
     * @endcode
     *
     * Then you will need to call @c recv() periodcally. The handler will then receive lists of available
     * or removed services. Check the @c flag member of the Service struct.
     *
     * @code
     * void MyClass::handleBrowseReply( const Service& service )
     * {
     *   LinkLocal::ServiceList::const_iterator it = services.begin();
     *   for( ; it != services.end(); ++it )
     *   {
     *     if( (*it).flag == LinkLocal::AddService )
     *     {
     *       // new service
     *     }
     *     else
     *     {
     *       // service removed
     *     }
     *   }
     * }
     * @endcode
     *
     * @note Note that your own service may show up in the list, too.
     *
     * ### Connecting to an XMPP service
     *
     * Using the info from the Service struct you can initiate a connection to the remote entity.
     * First, create a new instance of LinkLocal::Client and register some basic handlers like you
     * would with a normal gloox::Client:
     *
     * @code
     * LinkLocal::Client c( JID( "romeo@montague.net" ) );
     * c.logInstance().registerLogHandler( LogLevelDebug, LogAreaAll, this ); // optional
     * c.registerConnectionListener( yourConnectionListener );
     * @endcode
     *
     * Then call @link gloox::LinkLocal::Client::connect( const std::string&, const std::string&, const std::string&, int ) connect() @endlink
     * and pass the paramters from the Service struct that you received in handleBrowseReply().
     *
     * @code
     * c.connect( "juliet@laptop", "_presence._tcp", ".local", 4 ); // don't use literal values
     * @endcode
     *
     * Put your LinkLocal::Client instance in your event loop (or in a separate thread) and call
     * @link gloox::LinkLocal::Client::recv() recv() @endlink periodically.
     *
     * ### Advertising an XMPP service on the local network
     *
     * To advertise your own XMPP service you can (re-)use the same Manager instance from 'browsing the local network'
     * above.
     *
     * You can publish some basic info about your service in a DNS TXT record. The Manager offers the addTXTData() function
     * for that. See http://xmpp.org/registrar/linklocal.html for a list of official parameters.
     *
     * @code
     * m_mdns->addTXTData("nick","July");
     * m_mdns->addTXTData("1st","Juliet");
     * m_mdns->addTXTData("last","Capulet");
     * m_mdns->addTXTData("msg","Hanging out");
     * m_mdns->addTXTData("jid","julia@capulet.com");
     * m_mdns->addTXTData("status","avail");
     * @endcode
     *
     * Then, to start publishing the availability of your service as well as the TXT record with the additional info
     * you just call @c registerService().
     *
     * @code
     * m_mdns->registerService();
     * @endcode
     *
     * Other entities on the network will now be informed about the availability of your service.
     *
     * ### Listening for incoming connections
     *
     * The second argument to Manager's constructor is a ConnectionHandler-derived class that
     * will receive incoming connections.
     *
     * When registerService() gets called, the Manager will also start a local server that will
     * accept incoming connections. By default, it will listen on port 5562.
     *
     * In @link gloox::ConnectionHandler::handleIncomingConnection() handleIncomingConnection() @endlink
     * you should create a new LinkLocal::Client and register some basic handlers:
     *
     * @code
     * LinkLocal::Client c( JID( "romeo@desktop" ) );
     * c.logInstance().registerLogHandler( LogLevelDebug, LogAreaAll, this );
     * c.registerMessageHandler( this );
     * c.registerConnectionListener( this );
     * @endcode
     *
     * Finally you have to attach the incoming connection to the Client instance, and call connect().
     *
     * @code
     * connection->registerConnectionDataHandler( &c );
     * c.setConnectionImpl( connection );
     * c.connect();
     * @endcode
     *
     * Add the Client to your event loop to call recv() periodically.
     *
     * @see @c linklocal_example.cpp in @c src/examples/ for a (very) simple implementation of a bot
     * handling both incoming and outgoing connections.
     *
     * @author Jakob Schröter <js@camaya.net>
     * @since 1.0.x
     */
    class GLOOX_API Manager
    {

      public:

        /**
         * Creates a new Link-local Manager instance. You can call @c registerService() and/or @c startBrowsing()
         * immediately on a new Manager object, it will use sane defaults.
         * @param user The username to advertise, preferably (as per @xep{0174}) the locally
         * logged-in user. This is just the local part of the local JID.
         * @param connHandler A pointer to a ConnectionHandler that will receive incoming connections.
         * @param logInstance The log target. Obtain it from ClientBase::logInstance().
         */
        Manager( const std::string& user, ConnectionHandler* connHandler, const LogSink &logInstance );

        /**
         * Virtual destructor.
         * @note @c deregisterService() and @c stopBrowsing() will be called automatically if necessary.
         */
        virtual ~Manager();

        /**
         * Lets you add additional data to the published TXT record.
         * @note The @c txtvers=1 parameter is included by default and cannot be changed.
         * @param key The key of a key=value parameter pair. Must be non-empty. If the given key
         * has been set before, its value will be overwritten by the new value.
         * @param value The value of a @c key=value parameter pair. Must be non-empty.
         * @note If either parameter is empty, this function is a NOOP.
         * @note The additional data will not be automatically published if you have already called
         * @c registerService(). You will have to call @c registerService() again to update the
         * TXT record.
         */
        void addTXTData( const std::string& key, const std::string& value );

        /**
         * Lets you remove TXT record data by key.
         * @note The @c txtvers=1 parameter is included by default and cannot be removed.
         * @param key The key of the @c key=value parameter pair that should be removed. Must be non-empty.
         * @note A published TXT record will not be automatically updated if you have already called
         * @c registerService(). You will have to call @c registerService() again to update the TXT record.
         */
        void removeTXTData( const std::string& key );

        /**
         * Starts advertising link-local messaging capabilities by publishing a number of DNS records,
         * as per @xep{0174}.
         * You can call this function again to publish any values you updated after the first call.
         */
        void registerService();

        /**
         * Removes the published DNS records and thereby stops advertising link-local messaging
         * capabilities.
         */
        void deregisterService();

        /**
         * Lets you specify a new username.
         * @param user The new username.
         * @note The new username will not be automatically advertised if you have already called
         * @c registerService(). You will have to call @c registerService() again to update the username.
         */
        void setUser( const std::string& user ) { m_user = user; }

        /**
         * Lets you specify an alternate host name to advertise. By default the local machine's hostname
         * as returned by @c gethostname() will be used.
         * @param host The hostname to use.
         * @note The new hostname will not be automatically advertised if you have already called
         * @c registerService(). You will have to call @c registerService() again to update the hostname.
         */
        void setHost( const std::string& host ) { m_host = host; }

        /**
         * This function lets you set an alternate browse domain. The default domain should work in most cases.
         * @param domain The browse domain to set.
         * @note The new domain will not be automatically used if you have already called
         * @c registerService(). You will have to call @c registerService() again to update the domain.
         * The same applies to @c startBrowsing().
         */
        void setDomain( const std::string& domain ) { m_domain = domain; }

        /**
         * Lets you specifiy a port to listen on for incoming server-less XMPP connections. Default
         * for this implementation is port 5562, but any unused port can be used.
         * @note In addition to the SRV record, the port will also be published in the TXT record
         * automaticlly, you do not have to add it manually. That is, you should not call
         * @c addTXTData() with a key of @c "port.p2pj".
         * @param port The port to use.
         * @note The new port will not be automatically advertised if you have already called
         * @c registerService(). You will have to call @c registerService() again to update the port.
         */
        void setPort( const int port ) { m_port = port; addTXTData( "port.p2pj", util::int2string( m_port ) ); }

        /**
         * This function can be used to set a non-default interface. Use @c if_nametoindex() to
         * find the index for a specific named device.
         * By default DNS records will be broadcast on all available interfaces, and all available
         * interfaces will be used or browsing services.
         * @param iface The interface to use. If you set an interface here, and want to change it
         * back to 'any', use @b 0. Use @b -1 to broadcast only on the local machine.
         * @note The new interface will not be automatically used if you have already called
         * @c registerService(). You will have to call @c registerService() again to use the new
         * interface. The same applies to @c startBrowsing().
         */
        void setInterface( unsigned iface ) { m_interface = iface; }

        /**
         * Starts looking for other entities of type @c _presence. You have to set a handler for
         * results using @c registerLinkLocalHandler() before calling this function.
         * You can call this function again to re-start browsing with updated parameters.
         * @return Returns @b true if browsing could be started successfully, @b false otherwise.
         */
        bool startBrowsing();

        /**
         * Stops the browsing.
         */
        void stopBrowsing();

        /**
         * Exposes the socket used for browsing so you can have it checked in your own event loop,
         * if desired. If your event loop signals new data on the socket, you should NOT
         * try to read from it directly. Instead you should call @c recv().
         * As an alternative to using the raw socket you could also put the Manager in a
         * separate thread and call @c recv() repeatedly, or achieve this in any other way.
         */
        int socket() const { return m_browseFd; }

        /**
         * Checks once for new data on the socket used for browsing.
         * @param timeout The timeout for @c select() in microseconds. Use @b -1 if blocking behavior
         * is acceptable.
         */
        void recv( int timeout );

        /**
         * Registers a handler that will be notfied about entities found on the network that match
         * the given browse criteria.
         * @param handler The handler to register.
         */
        void registerLinkLocalHandler( Handler* handler ) { m_linkLocalHandler = handler; }

//         /**
//          *
//          */
//         static const StringMap decodeTXT( const std::string& txt );

      private:
        static void handleBrowseReply( DNSServiceRef sdRef, DNSServiceFlags flags, unsigned interfaceIndex,
                                       DNSServiceErrorType errorCode, const char* serviceName, const char* regtype,
                                       const char* replyDomain, void* context );

        void handleBrowse( Flag flag, const std::string& service, const std::string& regtype, const std::string& domain, int iface, bool moreComing );

        typedef std::list<ConnectionTCPClient*> ConnectionList;
        typedef std::map<ConnectionTCPClient*, const JID> ConnectionMap;

        DNSServiceRef m_publishRef;
        DNSServiceRef m_browseRef;

        ServiceList m_tmpServices;
//         ServiceList m_services;

        std::string m_user;
        std::string m_host;
        std::string m_domain;
        unsigned m_interface;
        int m_port;

        const LogSink& m_logInstance;

        int m_browseFd;

        StringMap m_txtData;

        ConnectionTCPServer m_server;

        Handler* m_linkLocalHandler;
        ConnectionHandler* m_connectionHandler;

    };

  }

}

#endif // HAVE_MDNS

#endif // LINKLOCALMANAGER_H___