~ubuntu-branches/ubuntu/utopic/kadu/utopic

« back to all changes in this revision

Viewing changes to plugins/jabber_protocol/3rdparty/libiris-win/src/irisnet/corelib/netnames_jdns.cpp

  • Committer: Package Import Robot
  • Author(s): Patryk Cisek
  • Date: 2014-10-02 11:55:19 UTC
  • mfrom: (0.48.1) (0.44.2) (43.1.5 sid)
  • Revision ID: package-import@ubuntu.com-20141002115519-sxn21g16t596llc0
Tags: 1.0-2
* Using dpkg-buildflags to set compilation and linker flags.
* Added 03-no-CMAKE_CXX_FLAGS-overwrite.patch to make it possible to set
  compilation flags.
* Removed lintian override for RPATH in Kadu's plugins.
* Using chrpath to strip Kadu's plugins from RPATH, since they're not
  needed there.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2005-2008  Justin Karneges
 
3
 *
 
4
 * This library is free software; you can redistribute it and/or
 
5
 * modify it under the terms of the GNU Lesser General Public
 
6
 * License as published by the Free Software Foundation; either
 
7
 * version 2.1 of the License, or (at your option) any later version.
 
8
 *
 
9
 * This library is distributed in the hope that it will be useful,
 
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
12
 * Lesser General Public License for more details.
 
13
 *
 
14
 * You should have received a copy of the GNU Lesser General Public
 
15
 * License along with this library; if not, write to the Free Software
 
16
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
17
 *
 
18
 */
 
19
 
 
20
#include "irisnetplugin.h"
 
21
 
 
22
#include "objectsession.h"
 
23
#include "jdnsshared.h"
 
24
#include "netinterface.h"
 
25
 
 
26
//#define JDNS_DEBUG
 
27
 
 
28
Q_DECLARE_METATYPE(XMPP::NameRecord)
 
29
Q_DECLARE_METATYPE(XMPP::NameResolver::Error)
 
30
Q_DECLARE_METATYPE(XMPP::ServiceBrowser::Error)
 
31
Q_DECLARE_METATYPE(XMPP::ServiceResolver::Error)
 
32
Q_DECLARE_METATYPE(XMPP::ServiceLocalPublisher::Error)
 
33
 
 
34
namespace XMPP {
 
35
 
 
36
static NameRecord importJDNSRecord(const QJDns::Record &in)
 
37
{
 
38
        NameRecord out;
 
39
        switch(in.type)
 
40
        {
 
41
                case QJDns::A:     out.setAddress(in.address); break;
 
42
                case QJDns::Aaaa:  out.setAddress(in.address); break;
 
43
                case QJDns::Mx:    out.setMx(in.name, in.priority); break;
 
44
                case QJDns::Srv:   out.setSrv(in.name, in.port, in.priority, in.weight); break;
 
45
                case QJDns::Cname: out.setCname(in.name); break;
 
46
                case QJDns::Ptr:   out.setPtr(in.name); break;
 
47
                case QJDns::Txt:   out.setTxt(in.texts); break;
 
48
                case QJDns::Hinfo: out.setHinfo(in.cpu, in.os); break;
 
49
                case QJDns::Ns:    out.setNs(in.name); break;
 
50
                case 10:           out.setNull(in.rdata); break;
 
51
                default:
 
52
                        return out;
 
53
        }
 
54
        out.setOwner(in.owner);
 
55
        out.setTtl(in.ttl);
 
56
        return out;
 
57
}
 
58
 
 
59
static QJDns::Record exportJDNSRecord(const NameRecord &in)
 
60
{
 
61
        QJDns::Record out;
 
62
        switch(in.type())
 
63
        {
 
64
                case NameRecord::A:
 
65
                        out.type = QJDns::A;
 
66
                        out.haveKnown = true;
 
67
                        out.address = in.address();
 
68
                        break;
 
69
                case NameRecord::Aaaa:
 
70
                        out.type = QJDns::Aaaa;
 
71
                        out.haveKnown = true;
 
72
                        out.address = in.address();
 
73
                        break;
 
74
                case NameRecord::Mx:
 
75
                        out.type = QJDns::Mx;
 
76
                        out.haveKnown = true;
 
77
                        out.name = in.name();
 
78
                        out.priority = in.priority();
 
79
                        break;
 
80
                case NameRecord::Srv:
 
81
                        out.type = QJDns::Srv;
 
82
                        out.haveKnown = true;
 
83
                        out.name = in.name();
 
84
                        out.port = in.port();
 
85
                        out.priority = in.priority();
 
86
                        out.weight = in.weight();
 
87
                        break;
 
88
                case NameRecord::Cname:
 
89
                        out.type = QJDns::Cname;
 
90
                        out.haveKnown = true;
 
91
                        out.name = in.name();
 
92
                        break;
 
93
                case NameRecord::Ptr:
 
94
                        out.type = QJDns::Ptr;
 
95
                        out.haveKnown = true;
 
96
                        out.name = in.name();
 
97
                        break;
 
98
                case NameRecord::Txt:
 
99
                        out.type = QJDns::Txt;
 
100
                        out.haveKnown = true;
 
101
                        out.texts = in.texts();
 
102
                        break;
 
103
                case NameRecord::Hinfo:
 
104
                        out.type = QJDns::Hinfo;
 
105
                        out.haveKnown = true;
 
106
                        out.cpu = in.cpu();
 
107
                        out.os = in.os();
 
108
                        break;
 
109
                case NameRecord::Ns:
 
110
                        out.type = QJDns::Ns;
 
111
                        out.haveKnown = true;
 
112
                        out.name = in.name();
 
113
                        break;
 
114
                case NameRecord::Null:
 
115
                        out.type = 10;
 
116
                        out.rdata = in.rawData();
 
117
                        break;
 
118
                default:
 
119
                        return out;
 
120
        }
 
121
        out.owner = in.owner();
 
122
        out.ttl = in.ttl();
 
123
        return out;
 
124
}
 
125
 
 
126
static bool validServiceType(const QByteArray &in)
 
127
{
 
128
        // can't be empty, or start/end with a dot
 
129
        if(in.isEmpty() || in[0] == '.' || in[in.length() - 1] == '.')
 
130
                return false;
 
131
 
 
132
        // must contain exactly one dot
 
133
        int dotcount = 0;
 
134
        for(int n = 0; n < in.length(); ++n)
 
135
        {
 
136
                if(in[n] == '.')
 
137
                {
 
138
                        ++dotcount;
 
139
 
 
140
                        // no need to count more than 2
 
141
                        if(dotcount >= 2)
 
142
                                break;
 
143
                }
 
144
        }
 
145
        if(dotcount != 1)
 
146
                return false;
 
147
 
 
148
        return true;
 
149
}
 
150
 
 
151
static QByteArray escapeDomainPart(const QByteArray &in)
 
152
{
 
153
        QByteArray out;
 
154
        for(int n = 0; n < in.length(); ++n)
 
155
        {
 
156
                if(in[n] == '\\')
 
157
                        out += "\\\\";
 
158
                else if(in[n] == '.')
 
159
                        out += "\\.";
 
160
                else
 
161
                        out += in[n];
 
162
        }
 
163
        return out;
 
164
}
 
165
 
 
166
static QByteArray unescapeDomainPart(const QByteArray &in)
 
167
{
 
168
        QByteArray out;
 
169
        for(int n = 0; n < in.length(); ++n)
 
170
        {
 
171
                if(in[n] == '\\')
 
172
                {
 
173
                        if(n + 1 >= in.length())
 
174
                                return QByteArray();
 
175
 
 
176
                        out += in[n + 1];
 
177
                }
 
178
                else
 
179
                        out += in[n];
 
180
        }
 
181
        return out;
 
182
}
 
183
 
 
184
class IdManager
 
185
{
 
186
private:
 
187
        QSet<int> set;
 
188
        int at;
 
189
 
 
190
        inline void bump_at()
 
191
        {
 
192
                if(at == 0x7fffffff)
 
193
                        at = 0;
 
194
                else
 
195
                        ++at;
 
196
        }
 
197
 
 
198
public:
 
199
        IdManager() :
 
200
                at(0)
 
201
        {
 
202
        }
 
203
 
 
204
        int reserveId()
 
205
        {
 
206
                while(1)
 
207
                {
 
208
                        if(!set.contains(at))
 
209
                        {
 
210
                                int id = at;
 
211
                                set.insert(id);
 
212
                                bump_at();
 
213
                                return id;
 
214
                        }
 
215
 
 
216
                        bump_at();
 
217
                }
 
218
        }
 
219
 
 
220
        void releaseId(int id)
 
221
        {
 
222
                set.remove(id);
 
223
        }
 
224
 
 
225
        void clear()
 
226
        {
 
227
                set.clear();
 
228
                at = 0;
 
229
        }
 
230
};
 
231
 
 
232
//----------------------------------------------------------------------------
 
233
// JDnsGlobal
 
234
//----------------------------------------------------------------------------
 
235
class JDnsGlobal : public QObject
 
236
{
 
237
        Q_OBJECT
 
238
 
 
239
public:
 
240
        JDnsSharedDebug db;
 
241
        JDnsShared *uni_net, *uni_local, *mul;
 
242
        QHostAddress mul_addr4, mul_addr6;
 
243
        NetInterfaceManager netman;
 
244
        QList<NetInterface*> ifaces;
 
245
        QTimer *updateTimer;
 
246
 
 
247
        JDnsGlobal() :
 
248
                netman(this)
 
249
        {
 
250
                uni_net = 0;
 
251
                uni_local = 0;
 
252
                mul = 0;
 
253
 
 
254
                qRegisterMetaType<NameRecord>();
 
255
                qRegisterMetaType<NameResolver::Error>();
 
256
                qRegisterMetaType<ServiceBrowser::Error>();
 
257
                qRegisterMetaType<ServiceResolver::Error>();
 
258
                qRegisterMetaType<ServiceLocalPublisher::Error>();
 
259
 
 
260
                connect(&db, SIGNAL(readyRead()), SLOT(jdns_debugReady()));
 
261
 
 
262
                updateTimer = new QTimer(this);
 
263
                connect(updateTimer, SIGNAL(timeout()), SLOT(doUpdateMulticastInterfaces()));
 
264
                updateTimer->setSingleShot(true);
 
265
        }
 
266
 
 
267
        ~JDnsGlobal()
 
268
        {
 
269
                updateTimer->disconnect(this);
 
270
                updateTimer->setParent(0);
 
271
                updateTimer->deleteLater();
 
272
 
 
273
                qDeleteAll(ifaces);
 
274
 
 
275
                QList<JDnsShared*> list;
 
276
                if(uni_net)
 
277
                        list += uni_net;
 
278
                if(uni_local)
 
279
                        list += uni_local;
 
280
                if(mul)
 
281
                        list += mul;
 
282
 
 
283
                // calls shutdown on the list, waits for shutdownFinished, deletes
 
284
                JDnsShared::waitForShutdown(list);
 
285
 
 
286
                // get final debug
 
287
                jdns_debugReady();
 
288
        }
 
289
 
 
290
        JDnsShared *ensure_uni_net()
 
291
        {
 
292
                if(!uni_net)
 
293
                {
 
294
                        uni_net = new JDnsShared(JDnsShared::UnicastInternet, this);
 
295
                        uni_net->setDebug(&db, "U");
 
296
                        bool ok4 = uni_net->addInterface(QHostAddress::Any);
 
297
                        bool ok6 = uni_net->addInterface(QHostAddress::AnyIPv6);
 
298
                        if(!ok4 && !ok6)
 
299
                        {
 
300
                                delete uni_net;
 
301
                                uni_net = 0;
 
302
                        }
 
303
                }
 
304
                return uni_net;
 
305
        }
 
306
 
 
307
        JDnsShared *ensure_uni_local()
 
308
        {
 
309
                if(!uni_local)
 
310
                {
 
311
                        uni_local = new JDnsShared(JDnsShared::UnicastLocal, this);
 
312
                        uni_local->setDebug(&db, "L");
 
313
                        bool ok4 = uni_local->addInterface(QHostAddress::Any);
 
314
                        bool ok6 = uni_local->addInterface(QHostAddress::AnyIPv6);
 
315
                        if(!ok4 && !ok6)
 
316
                        {
 
317
                                delete uni_local;
 
318
                                uni_local = 0;
 
319
                        }
 
320
                }
 
321
                return uni_local;
 
322
        }
 
323
 
 
324
        JDnsShared *ensure_mul()
 
325
        {
 
326
                if(!mul)
 
327
                {
 
328
                        mul = new JDnsShared(JDnsShared::Multicast, this);
 
329
                        mul->setDebug(&db, "M");
 
330
 
 
331
                        connect(&netman, SIGNAL(interfaceAvailable(const QString &)), SLOT(iface_available(const QString &)));
 
332
 
 
333
                        // get the current network interfaces.  this initial
 
334
                        //   fetching should not trigger any calls to
 
335
                        //   updateMulticastInterfaces().  only future
 
336
                        //   activity should do that.
 
337
                        foreach(const QString &id, netman.interfaces())
 
338
                        {
 
339
                                NetInterface *iface = new NetInterface(id, &netman);
 
340
                                connect(iface, SIGNAL(unavailable()), SLOT(iface_unavailable()));
 
341
                                ifaces += iface;
 
342
                        }
 
343
 
 
344
                        updateMulticastInterfaces(false);
 
345
                }
 
346
                return mul;
 
347
        }
 
348
 
 
349
        bool haveMulticast4() const
 
350
        {
 
351
                return !mul_addr4.isNull();
 
352
        }
 
353
 
 
354
        bool haveMulticast6() const
 
355
        {
 
356
                return !mul_addr6.isNull();
 
357
        }
 
358
 
 
359
signals:
 
360
        void interfacesChanged();
 
361
 
 
362
private slots:
 
363
        void jdns_debugReady()
 
364
        {
 
365
                QStringList lines = db.readDebugLines();
 
366
#ifdef JDNS_DEBUG
 
367
                for(int n = 0; n < lines.count(); ++n)
 
368
                        printf("jdns: %s\n", qPrintable(lines[n]));
 
369
#else
 
370
                Q_UNUSED(lines);
 
371
#endif
 
372
        }
 
373
 
 
374
        void iface_available(const QString &id)
 
375
        {
 
376
                NetInterface *iface = new NetInterface(id, &netman);
 
377
                connect(iface, SIGNAL(unavailable()), SLOT(iface_unavailable()));
 
378
                ifaces += iface;
 
379
 
 
380
                updateTimer->start(100);
 
381
        }
 
382
 
 
383
        void iface_unavailable()
 
384
        {
 
385
                NetInterface *iface = (NetInterface *)sender();
 
386
                ifaces.removeAll(iface);
 
387
                delete iface;
 
388
 
 
389
                updateTimer->start(100);
 
390
        }
 
391
 
 
392
        void doUpdateMulticastInterfaces()
 
393
        {
 
394
                updateMulticastInterfaces(true);
 
395
        }
 
396
 
 
397
private:
 
398
        void updateMulticastInterfaces(bool useSignals)
 
399
        {
 
400
                QHostAddress addr4 = QJDns::detectPrimaryMulticast(QHostAddress::Any);
 
401
                QHostAddress addr6 = QJDns::detectPrimaryMulticast(QHostAddress::AnyIPv6);
 
402
 
 
403
                bool had4 = !mul_addr4.isNull();
 
404
                bool had6 = !mul_addr6.isNull();
 
405
 
 
406
                updateMulticastInterface(&mul_addr4, addr4);
 
407
                updateMulticastInterface(&mul_addr6, addr6);
 
408
 
 
409
                bool have4 = !mul_addr4.isNull();
 
410
                bool have6 = !mul_addr6.isNull();
 
411
 
 
412
                // did we gain/lose something?
 
413
                if(had4 != have4 || had6 != have6)
 
414
                {
 
415
                        if(useSignals)
 
416
                                emit interfacesChanged();
 
417
                }
 
418
        }
 
419
 
 
420
        void updateMulticastInterface(QHostAddress *curaddr, const QHostAddress &newaddr)
 
421
        {
 
422
                if(!(newaddr == *curaddr)) // QHostAddress doesn't have operator!=
 
423
                {
 
424
                        if(!curaddr->isNull())
 
425
                                mul->removeInterface(*curaddr);
 
426
                        *curaddr = newaddr;
 
427
                        if(!curaddr->isNull())
 
428
                        {
 
429
                                if(!mul->addInterface(*curaddr))
 
430
                                        *curaddr = QHostAddress();
 
431
                        }
 
432
                }
 
433
        }
 
434
};
 
435
 
 
436
//----------------------------------------------------------------------------
 
437
// JDnsNameProvider
 
438
//----------------------------------------------------------------------------
 
439
class JDnsNameProvider : public NameProvider
 
440
{
 
441
        Q_OBJECT
 
442
        Q_INTERFACES(XMPP::NameProvider)
 
443
 
 
444
public:
 
445
        enum Mode { Internet, Local };
 
446
 
 
447
        JDnsGlobal *global;
 
448
        Mode mode;
 
449
        IdManager idman;
 
450
        ObjectSession sess;
 
451
 
 
452
        class Item
 
453
        {
 
454
        public:
 
455
                int id;
 
456
                JDnsSharedRequest *req;
 
457
                int type;
 
458
                bool longLived;
 
459
                ObjectSession sess;
 
460
                bool useLocal;
 
461
                bool localResult;
 
462
                NameResolver::Error error;
 
463
                NameResolver::Error localError;
 
464
 
 
465
                Item(QObject *parent = 0) :
 
466
                        id(-1),
 
467
                        req(0),
 
468
                        sess(parent),
 
469
                        useLocal(false),
 
470
                        localResult(false)
 
471
                {
 
472
                }
 
473
 
 
474
                ~Item()
 
475
                {
 
476
                        delete req;
 
477
                }
 
478
        };
 
479
        QList<Item*> items;
 
480
 
 
481
        static JDnsNameProvider *create(JDnsGlobal *global, Mode mode, QObject *parent = 0)
 
482
        {
 
483
                if(mode == Internet)
 
484
                {
 
485
                        if(!global->ensure_uni_net())
 
486
                                return 0;
 
487
                }
 
488
                else
 
489
                {
 
490
                        if(!global->ensure_uni_local())
 
491
                                return 0;
 
492
                }
 
493
 
 
494
                return new JDnsNameProvider(global, mode, parent);
 
495
        }
 
496
 
 
497
        JDnsNameProvider(JDnsGlobal *_global, Mode _mode, QObject *parent = 0) :
 
498
                NameProvider(parent)
 
499
        {
 
500
                global = _global;
 
501
                mode = _mode;
 
502
        }
 
503
 
 
504
        ~JDnsNameProvider()
 
505
        {
 
506
                qDeleteAll(items);
 
507
        }
 
508
 
 
509
        Item *getItemById(int id)
 
510
        {
 
511
                for(int n = 0; n < items.count(); ++n)
 
512
                {
 
513
                        if(items[n]->id == id)
 
514
                                return items[n];
 
515
                }
 
516
 
 
517
                return 0;
 
518
        }
 
519
 
 
520
        Item *getItemByReq(JDnsSharedRequest *req)
 
521
        {
 
522
                for(int n = 0; n < items.count(); ++n)
 
523
                {
 
524
                        if(items[n]->req == req)
 
525
                                return items[n];
 
526
                }
 
527
 
 
528
                return 0;
 
529
        }
 
530
 
 
531
        void releaseItem(Item *i)
 
532
        {
 
533
                idman.releaseId(i->id);
 
534
                items.removeAll(i);
 
535
                delete i;
 
536
        }
 
537
 
 
538
        void tryError(Item *i)
 
539
        {
 
540
                // if we are doing dual resolves, make sure both are done
 
541
                if(!i->longLived && (i->req || (i->useLocal && !i->localResult)))
 
542
                        return;
 
543
 
 
544
                int id = i->id;
 
545
                NameResolver::Error error = i->error;
 
546
                releaseItem(i);
 
547
                emit resolve_error(id, error);
 
548
        }
 
549
 
 
550
        virtual bool supportsSingle() const
 
551
        {
 
552
                return true;
 
553
        }
 
554
 
 
555
        virtual bool supportsLongLived() const
 
556
        {
 
557
                if(mode == Local)
 
558
                        return true;  // we support long-lived local queries
 
559
                else
 
560
                        return false; // we do NOT support long-lived internet queries
 
561
        }
 
562
 
 
563
        virtual bool supportsRecordType(int type) const
 
564
        {
 
565
                // all record types supported
 
566
                Q_UNUSED(type);
 
567
                return true;
 
568
        }
 
569
 
 
570
        virtual int resolve_start(const QByteArray &name, int qType, bool longLived)
 
571
        {
 
572
                if(mode == Internet)
 
573
                {
 
574
                        bool isLocalName = false;
 
575
                        if(name.right(6) == ".local" || name.right(7) == ".local.")
 
576
                                isLocalName = true;
 
577
 
 
578
                        // if query ends in .local, switch to local resolver
 
579
                        /*if(isLocalName)
 
580
                        {
 
581
                                Item *i = new Item(this);
 
582
                                i->id = idman.reserveId();
 
583
                                i->longLived = longLived;
 
584
                                items += i;
 
585
                                i->sess.defer(this, "do_local", Q_ARG(int, i->id), Q_ARG(QByteArray, name));
 
586
                                return i->id;
 
587
                        }*/
 
588
 
 
589
                        // we don't support long-lived internet queries
 
590
                        if(longLived)
 
591
                        {
 
592
                                // but we do support long-lived local queries
 
593
                                if(isLocalName)
 
594
                                {
 
595
                                        Item *i = new Item(this);
 
596
                                        i->id = idman.reserveId();
 
597
                                        i->longLived = longLived;
 
598
                                        i->useLocal = true;
 
599
                                        items += i;
 
600
                                        i->sess.defer(this, "do_local", Q_ARG(int, i->id), Q_ARG(QByteArray, name));
 
601
                                        return i->id;
 
602
                                }
 
603
 
 
604
                                Item *i = new Item(this);
 
605
                                i->id = idman.reserveId();
 
606
                                items += i;
 
607
                                i->sess.defer(this, "do_error", Q_ARG(int, i->id),
 
608
                                        Q_ARG(XMPP::NameResolver::Error, NameResolver::ErrorNoLongLived));
 
609
                                return i->id;
 
610
                        }
 
611
 
 
612
                        // perform the query
 
613
                        Item *i = new Item(this);
 
614
                        i->id = idman.reserveId();
 
615
                        i->req = new JDnsSharedRequest(global->uni_net);
 
616
                        connect(i->req, SIGNAL(resultsReady()), SLOT(req_resultsReady()));
 
617
                        i->type = qType;
 
618
                        i->longLived = false;
 
619
                        if(isLocalName)
 
620
                                i->useLocal = true;
 
621
                        items += i;
 
622
                        i->req->query(name, qType);
 
623
                        // if query ends in .local, simultaneously do local resolve
 
624
                        if(isLocalName)
 
625
                                i->sess.defer(this, "do_local", Q_ARG(int, i->id), Q_ARG(QByteArray, name));
 
626
                        return i->id;
 
627
                }
 
628
                else
 
629
                {
 
630
                        Item *i = new Item(this);
 
631
                        i->id = idman.reserveId();
 
632
                        i->type = qType;
 
633
                        if(longLived)
 
634
                        {
 
635
                                if(!global->ensure_mul())
 
636
                                {
 
637
                                        items += i;
 
638
                                        i->sess.defer(this, "do_error", Q_ARG(int, i->id),
 
639
                                                Q_ARG(XMPP::NameResolver::Error, NameResolver::ErrorNoLocal));
 
640
                                        return i->id;
 
641
                                }
 
642
 
 
643
                                i->req = new JDnsSharedRequest(global->mul);
 
644
                                i->longLived = true;
 
645
                        }
 
646
                        else
 
647
                        {
 
648
                                i->req = new JDnsSharedRequest(global->uni_local);
 
649
                                i->longLived = false;
 
650
                        }
 
651
                        connect(i->req, SIGNAL(resultsReady()), SLOT(req_resultsReady()));
 
652
                        items += i;
 
653
                        i->req->query(name, qType);
 
654
                        return i->id;
 
655
                }
 
656
        }
 
657
 
 
658
        virtual void resolve_stop(int id)
 
659
        {
 
660
                Item *i = getItemById(id);
 
661
                Q_ASSERT(i);
 
662
 
 
663
                if(i->req)
 
664
                        i->req->cancel();
 
665
                releaseItem(i);
 
666
        }
 
667
 
 
668
        virtual void resolve_localResultsReady(int id, const QList<XMPP::NameRecord> &results)
 
669
        {
 
670
                Item *i = getItemById(id);
 
671
                Q_ASSERT(i);
 
672
                Q_ASSERT(!i->localResult);
 
673
 
 
674
                i->localResult = true;
 
675
                i->sess.defer(this, "do_local_ready", Q_ARG(int, id),
 
676
                        Q_ARG(QList<XMPP::NameRecord>, results));
 
677
        }
 
678
 
 
679
        virtual void resolve_localError(int id, XMPP::NameResolver::Error e)
 
680
        {
 
681
                Item *i = getItemById(id);
 
682
                Q_ASSERT(i);
 
683
                Q_ASSERT(!i->localResult);
 
684
 
 
685
                i->localResult = true;
 
686
                i->sess.defer(this, "do_local_error", Q_ARG(int, id),
 
687
                        Q_ARG(XMPP::NameResolver::Error, e));
 
688
        }
 
689
 
 
690
private slots:
 
691
        void req_resultsReady()
 
692
        {
 
693
                JDnsSharedRequest *req = (JDnsSharedRequest *)sender();
 
694
                Item *i = getItemByReq(req);
 
695
                Q_ASSERT(i);
 
696
 
 
697
                int id = i->id;
 
698
 
 
699
                NameResolver::Error error;
 
700
 
 
701
                if(req->success())
 
702
                {
 
703
                        QList<NameRecord> out;
 
704
                        foreach(const QJDns::Record &r, req->results())
 
705
                        {
 
706
                                // unless we are asking for all types, only
 
707
                                //   accept the type we asked for
 
708
                                if(i->type == QJDns::Any || r.type == i->type)
 
709
                                {
 
710
                                        NameRecord rec = importJDNSRecord(r);
 
711
                                        if(!rec.isNull())
 
712
                                                out += rec;
 
713
                                }
 
714
                        }
 
715
 
 
716
                        // don't report anything if long-lived gives no results
 
717
                        if(i->longLived && out.isEmpty())
 
718
                                return;
 
719
 
 
720
                        // only emit success if we have at least 1 result
 
721
                        if(!out.isEmpty())
 
722
                        {
 
723
                                // FIXME: need a way to cancel related local
 
724
                                //   query if still active
 
725
 
 
726
                                if(!i->longLived)
 
727
                                        releaseItem(i);
 
728
                                emit resolve_resultsReady(id, out);
 
729
                                return;
 
730
                        }
 
731
                        else
 
732
                        {
 
733
                                error = NameResolver::ErrorGeneric;
 
734
                        }
 
735
                }
 
736
                else
 
737
                {
 
738
                        JDnsSharedRequest::Error e = req->error();
 
739
 
 
740
                        error = NameResolver::ErrorGeneric;
 
741
                        if(e == JDnsSharedRequest::ErrorNXDomain)
 
742
                                error = NameResolver::ErrorNoName;
 
743
                        else if(e == JDnsSharedRequest::ErrorTimeout)
 
744
                                error = NameResolver::ErrorTimeout;
 
745
                        else // ErrorGeneric or ErrorNoNet
 
746
                                error = NameResolver::ErrorGeneric;
 
747
                }
 
748
 
 
749
                delete i->req;
 
750
                i->req = 0;
 
751
                i->error = error;
 
752
                tryError(i);
 
753
        }
 
754
 
 
755
        void do_error(int id, XMPP::NameResolver::Error e)
 
756
        {
 
757
                Item *i = getItemById(id);
 
758
                Q_ASSERT(i);
 
759
 
 
760
                // note: for an internet resolve, this slot is not called in
 
761
                //   any case where a local subquery is invoked as well (yet?)
 
762
 
 
763
                releaseItem(i);
 
764
                emit resolve_error(id, e);
 
765
        }
 
766
 
 
767
        void do_local(int id, const QByteArray &name)
 
768
        {
 
769
                //Item *i = getItemById(id);
 
770
                //Q_ASSERT(i);
 
771
 
 
772
                /*// resolve_useLocal has two behaviors:
 
773
                // - if longlived, then it indicates a hand-off
 
774
                // - if non-longlived, then it indicates we want a subquery
 
775
 
 
776
                if(i->longLived)
 
777
                        releaseItem(i);*/
 
778
                emit resolve_useLocal(id, name);
 
779
        }
 
780
 
 
781
        void do_local_ready(int id, const QList<XMPP::NameRecord> &results)
 
782
        {
 
783
                Item *i = getItemById(id);
 
784
                Q_ASSERT(i);
 
785
 
 
786
                if(!i->longLived)
 
787
                {
 
788
                        // stop any simultaneous internet resolve
 
789
                        if(i->req)
 
790
                                i->req->cancel();
 
791
 
 
792
                        // for non-longlived, we're done
 
793
                        releaseItem(i);
 
794
                }
 
795
 
 
796
                emit resolve_resultsReady(id, results);
 
797
        }
 
798
 
 
799
        void do_local_error(int id, XMPP::NameResolver::Error e)
 
800
        {
 
801
                Item *i = getItemById(id);
 
802
                Q_ASSERT(i);
 
803
 
 
804
                i->localError = e;
 
805
                tryError(i);
 
806
        }
 
807
};
 
808
 
 
809
//----------------------------------------------------------------------------
 
810
// JDnsBrowse
 
811
//----------------------------------------------------------------------------
 
812
class JDnsBrowse : public QObject
 
813
{
 
814
        Q_OBJECT
 
815
 
 
816
public:
 
817
        QByteArray type, typeAndDomain;
 
818
        JDnsSharedRequest req;
 
819
 
 
820
        JDnsBrowse(JDnsShared *_jdns, QObject *parent = 0) :
 
821
                QObject(parent),
 
822
                req(_jdns, this)
 
823
        {
 
824
                connect(&req, SIGNAL(resultsReady()), SLOT(jdns_resultsReady()));
 
825
        }
 
826
 
 
827
        void start(const QByteArray &_type)
 
828
        {
 
829
                type = _type;
 
830
                Q_ASSERT(validServiceType(type));
 
831
                typeAndDomain = type + ".local.";
 
832
                req.query(typeAndDomain, QJDns::Ptr);
 
833
        }
 
834
 
 
835
signals:
 
836
        void available(const QByteArray &instance);
 
837
        void unavailable(const QByteArray &instance);
 
838
 
 
839
private:
 
840
        QByteArray parseInstanceName(const QByteArray &name)
 
841
        {
 
842
                // needs to be at least X + '.' + typeAndDomain
 
843
                if(name.length() < typeAndDomain.length() + 2)
 
844
                        return QByteArray();
 
845
 
 
846
                // index of the '.' character
 
847
                int at = name.length() - typeAndDomain.length() - 1;
 
848
 
 
849
                if(name[at] != '.')
 
850
                        return QByteArray();
 
851
                if(name.mid(at + 1) != typeAndDomain)
 
852
                        return QByteArray();
 
853
 
 
854
                QByteArray friendlyName = unescapeDomainPart(name.mid(0, at));
 
855
                if(friendlyName.isEmpty())
 
856
                        return QByteArray();
 
857
 
 
858
                return friendlyName;
 
859
        }
 
860
 
 
861
private slots:
 
862
        void jdns_resultsReady()
 
863
        {
 
864
                // ignore errors
 
865
                if(!req.success())
 
866
                        return;
 
867
 
 
868
                QJDns::Record rec = req.results().first();
 
869
 
 
870
                Q_ASSERT(rec.type == QJDns::Ptr);
 
871
 
 
872
                QByteArray name = rec.name;
 
873
                QByteArray instance = parseInstanceName(name);
 
874
                if(instance.isEmpty())
 
875
                        return;
 
876
 
 
877
                if(rec.ttl == 0)
 
878
                {
 
879
                        emit unavailable(instance);
 
880
                        return;
 
881
                }
 
882
 
 
883
                emit available(instance);
 
884
        }
 
885
};
 
886
 
 
887
//----------------------------------------------------------------------------
 
888
// JDnsServiceResolve
 
889
//----------------------------------------------------------------------------
 
890
 
 
891
// 5 second timeout waiting for both A and AAAA
 
892
// 8 second timeout waiting for at least one record
 
893
class JDnsServiceResolve : public QObject
 
894
{
 
895
        Q_OBJECT
 
896
 
 
897
public:
 
898
        enum SrvState
 
899
        {
 
900
                Srv               = 0,
 
901
                AddressWait       = 1,
 
902
                AddressFirstCome  = 2
 
903
        };
 
904
 
 
905
        JDnsSharedRequest reqtxt; // for TXT
 
906
        JDnsSharedRequest req;    // for SRV/A
 
907
        JDnsSharedRequest req6;   // for AAAA
 
908
        bool haveTxt;
 
909
        SrvState srvState;
 
910
        QTimer *opTimer;
 
911
 
 
912
        // out
 
913
        QList<QByteArray> attribs;
 
914
        QByteArray host;
 
915
        int port;
 
916
        bool have4, have6;
 
917
        QHostAddress addr4, addr6;
 
918
 
 
919
        JDnsServiceResolve(JDnsShared *_jdns, QObject *parent = 0) :
 
920
                QObject(parent),
 
921
                reqtxt(_jdns, this),
 
922
                req(_jdns, this),
 
923
                req6(_jdns, this)
 
924
        {
 
925
                connect(&reqtxt, SIGNAL(resultsReady()), SLOT(reqtxt_ready()));
 
926
                connect(&req, SIGNAL(resultsReady()), SLOT(req_ready()));
 
927
                connect(&req6, SIGNAL(resultsReady()), SLOT(req6_ready()));
 
928
 
 
929
                opTimer = new QTimer(this);
 
930
                connect(opTimer, SIGNAL(timeout()), SLOT(op_timeout()));
 
931
                opTimer->setSingleShot(true);
 
932
        }
 
933
 
 
934
        ~JDnsServiceResolve()
 
935
        {
 
936
                opTimer->disconnect(this);
 
937
                opTimer->setParent(0);
 
938
                opTimer->deleteLater();
 
939
        }
 
940
 
 
941
        void start(const QByteArray name)
 
942
        {
 
943
                haveTxt = false;
 
944
                srvState = Srv;
 
945
                have4 = false;
 
946
                have6 = false;
 
947
 
 
948
                opTimer->start(8000);
 
949
 
 
950
                reqtxt.query(name, QJDns::Txt);
 
951
                req.query(name, QJDns::Srv);
 
952
        }
 
953
 
 
954
signals:
 
955
        void finished();
 
956
        void error(JDnsSharedRequest::Error e);
 
957
 
 
958
private:
 
959
        void cleanup()
 
960
        {
 
961
                if(opTimer->isActive())
 
962
                        opTimer->stop();
 
963
                if(!haveTxt)
 
964
                        reqtxt.cancel();
 
965
                if(srvState == Srv || !have4)
 
966
                        req.cancel();
 
967
                if(srvState >= AddressWait && !have6)
 
968
                        req6.cancel();
 
969
        }
 
970
 
 
971
        bool tryDone()
 
972
        {
 
973
                // we're done when we have txt and addresses
 
974
                if(haveTxt && ( (have4 && have6) || (srvState == AddressFirstCome && (have4 || have6)) ))
 
975
                {
 
976
                        cleanup();
 
977
                        emit finished();
 
978
                        return true;
 
979
                }
 
980
 
 
981
                return false;
 
982
        }
 
983
 
 
984
private slots:
 
985
        void reqtxt_ready()
 
986
        {
 
987
                if(!reqtxt.success())
 
988
                {
 
989
                        cleanup();
 
990
                        emit error(reqtxt.error());
 
991
                        return;
 
992
                }
 
993
 
 
994
                QJDns::Record rec = reqtxt.results().first();
 
995
                reqtxt.cancel();
 
996
 
 
997
                Q_ASSERT(rec.type == QJDns::Txt);
 
998
 
 
999
                attribs.clear();
 
1000
                if(!rec.texts.isEmpty())
 
1001
                {
 
1002
                        // if there is only 1 text, it needs to be
 
1003
                        //   non-empty for us to care
 
1004
                        if(rec.texts.count() != 1 || !rec.texts[0].isEmpty())
 
1005
                                attribs = rec.texts;
 
1006
                }
 
1007
 
 
1008
                haveTxt = true;
 
1009
 
 
1010
                tryDone();
 
1011
        }
 
1012
 
 
1013
        void req_ready()
 
1014
        {
 
1015
                if(!req.success())
 
1016
                {
 
1017
                        cleanup();
 
1018
                        emit error(req.error());
 
1019
                        return;
 
1020
                }
 
1021
 
 
1022
                QJDns::Record rec = req.results().first();
 
1023
                req.cancel();
 
1024
 
 
1025
                if(srvState == Srv)
 
1026
                {
 
1027
                        // in Srv state, req is used for SRV records
 
1028
 
 
1029
                        Q_ASSERT(rec.type == QJDns::Srv);
 
1030
 
 
1031
                        host = rec.name;
 
1032
                        port = rec.port;
 
1033
 
 
1034
                        srvState = AddressWait;
 
1035
                        opTimer->start(5000);
 
1036
 
 
1037
                        req.query(host, QJDns::A);
 
1038
                        req6.query(host, QJDns::Aaaa);
 
1039
                }
 
1040
                else
 
1041
                {
 
1042
                        // in the other states, req is used for A records
 
1043
 
 
1044
                        Q_ASSERT(rec.type == QJDns::A);
 
1045
 
 
1046
                        addr4 = rec.address;
 
1047
                        have4 = true;
 
1048
 
 
1049
                        tryDone();
 
1050
                }
 
1051
        }
 
1052
 
 
1053
        void req6_ready()
 
1054
        {
 
1055
                if(!req6.success())
 
1056
                {
 
1057
                        cleanup();
 
1058
                        emit error(req6.error());
 
1059
                        return;
 
1060
                }
 
1061
 
 
1062
                QJDns::Record rec = req6.results().first();
 
1063
                req6.cancel();
 
1064
 
 
1065
                Q_ASSERT(rec.type == QJDns::Aaaa);
 
1066
 
 
1067
                addr6 = rec.address;
 
1068
                have6 = true;
 
1069
 
 
1070
                tryDone();
 
1071
        }
 
1072
 
 
1073
        void op_timeout()
 
1074
        {
 
1075
                if(srvState == Srv)
 
1076
                {
 
1077
                        // timeout getting SRV.  it is possible that we could
 
1078
                        //   have obtained the TXT record, but if SRV times
 
1079
                        //   out then we consider the whole job to have
 
1080
                        //   failed.
 
1081
                        cleanup();
 
1082
                        emit error(JDnsSharedRequest::ErrorTimeout);
 
1083
                }
 
1084
                else if(srvState == AddressWait)
 
1085
                {
 
1086
                        // timeout while waiting for both A and AAAA.  we now
 
1087
                        //   switch to the AddressFirstCome state, where an
 
1088
                        //   answer for either will do
 
1089
 
 
1090
                        srvState = AddressFirstCome;
 
1091
 
 
1092
                        // if we have at least one of these, we're done
 
1093
                        if(have4 || have6)
 
1094
                        {
 
1095
                                // well, almost.  we might still be waiting
 
1096
                                //   for the TXT record
 
1097
                                if(tryDone())
 
1098
                                        return;
 
1099
                        }
 
1100
 
 
1101
                        // if we are here, then it means we are missing TXT
 
1102
                        //   still, or we have neither A nor AAAA.
 
1103
 
 
1104
                        // wait 3 more seconds
 
1105
                        opTimer->start(3000);
 
1106
                }
 
1107
                else // AddressFirstCome
 
1108
                {
 
1109
                        // last chance!
 
1110
                        if(!tryDone())
 
1111
                        {
 
1112
                                cleanup();
 
1113
                                emit error(JDnsSharedRequest::ErrorTimeout);
 
1114
                        }
 
1115
                }
 
1116
        }
 
1117
};
 
1118
 
 
1119
//----------------------------------------------------------------------------
 
1120
// JDnsPublishAddresses
 
1121
//----------------------------------------------------------------------------
 
1122
 
 
1123
// helper class for JDnsPublishAddresses.  publishes A+PTR or AAAA+PTR pair.
 
1124
class JDnsPublishAddress : public QObject
 
1125
{
 
1126
        Q_OBJECT
 
1127
 
 
1128
public:
 
1129
        enum Type
 
1130
        {
 
1131
                IPv4,
 
1132
                IPv6
 
1133
        };
 
1134
 
 
1135
        Type type;
 
1136
        QByteArray host;
 
1137
        JDnsSharedRequest pub_addr;
 
1138
        JDnsSharedRequest pub_ptr;
 
1139
        bool success_;
 
1140
 
 
1141
        JDnsPublishAddress(JDnsShared *_jdns, QObject *parent = 0) :
 
1142
                QObject(parent),
 
1143
                pub_addr(_jdns, this),
 
1144
                pub_ptr(_jdns, this)
 
1145
        {
 
1146
                connect(&pub_addr, SIGNAL(resultsReady()), SLOT(pub_addr_ready()));
 
1147
                connect(&pub_ptr, SIGNAL(resultsReady()), SLOT(pub_ptr_ready()));
 
1148
        }
 
1149
 
 
1150
        void start(Type _type, const QByteArray &_host)
 
1151
        {
 
1152
                type = _type;
 
1153
                host = _host;
 
1154
                success_ = false;
 
1155
 
 
1156
                QJDns::Record rec;
 
1157
                if(type == IPv6)
 
1158
                        rec.type = QJDns::Aaaa;
 
1159
                else
 
1160
                        rec.type = QJDns::A;
 
1161
                rec.owner = host;
 
1162
                rec.ttl = 120;
 
1163
                rec.haveKnown = true;
 
1164
                rec.address = QHostAddress(); // null address, will be filled in
 
1165
                pub_addr.publish(QJDns::Unique, rec);
 
1166
        }
 
1167
 
 
1168
        void cancel()
 
1169
        {
 
1170
                pub_addr.cancel();
 
1171
                pub_ptr.cancel();
 
1172
        }
 
1173
 
 
1174
        bool success() const
 
1175
        {
 
1176
                return success_;
 
1177
        }
 
1178
 
 
1179
signals:
 
1180
        void resultsReady();
 
1181
 
 
1182
private slots:
 
1183
        void pub_addr_ready()
 
1184
        {
 
1185
                if(pub_addr.success())
 
1186
                {
 
1187
                        QJDns::Record rec;
 
1188
                        rec.type = QJDns::Ptr;
 
1189
                        if(type == IPv6)
 
1190
                                rec.owner = ".ip6.arpa.";
 
1191
                        else
 
1192
                                rec.owner = ".in-addr.arpa.";
 
1193
                        rec.ttl = 120;
 
1194
                        rec.haveKnown = true;
 
1195
                        rec.name = host;
 
1196
                        pub_ptr.publish(QJDns::Shared, rec);
 
1197
                }
 
1198
                else
 
1199
                {
 
1200
                        pub_ptr.cancel(); // needed if addr fails during or after ptr
 
1201
                        success_ = false;
 
1202
                        emit resultsReady();
 
1203
                }
 
1204
        }
 
1205
 
 
1206
        void pub_ptr_ready()
 
1207
        {
 
1208
                if(pub_ptr.success())
 
1209
                {
 
1210
                        success_ = true;
 
1211
                }
 
1212
                else
 
1213
                {
 
1214
                        pub_addr.cancel();
 
1215
                        success_ = false;
 
1216
                }
 
1217
 
 
1218
                emit resultsReady();
 
1219
        }
 
1220
};
 
1221
 
 
1222
// This class publishes A/AAAA records for the machine, using a derived
 
1223
//   hostname (it will use QHostInfo::localHostName(), but append a unique
 
1224
//   suffix if necessary).  If there is ever a record conflict, it will
 
1225
//   republish under a unique name.
 
1226
//
 
1227
// The hostName() signal is emitted when a hostname is successfully
 
1228
//   published as.  When there is a conflict, hostName() is emitted with
 
1229
//   an empty value, and will again be emitted with a non-empty value
 
1230
//   once the conflict is resolved.  A missing hostname is considered a
 
1231
//   temporary problem, and so other publish operations that depend on a
 
1232
//   hostname (SRV, etc) should block until a hostname is available.
 
1233
class JDnsPublishAddresses : public QObject
 
1234
{
 
1235
        Q_OBJECT
 
1236
 
 
1237
public:
 
1238
        bool started;
 
1239
        bool use6, use4;
 
1240
        JDnsPublishAddress pub6;
 
1241
        JDnsPublishAddress pub4;
 
1242
        int counter;
 
1243
        QByteArray host;
 
1244
        bool success;
 
1245
        bool have6, have4;
 
1246
        ObjectSession sess;
 
1247
 
 
1248
        JDnsPublishAddresses(JDnsShared *_jdns, QObject *parent = 0) :
 
1249
                QObject(parent),
 
1250
                started(false),
 
1251
                use6(false),
 
1252
                use4(false),
 
1253
                pub6(_jdns, this),
 
1254
                pub4(_jdns, this),
 
1255
                sess(this)
 
1256
        {
 
1257
                connect(&pub6, SIGNAL(resultsReady()), SLOT(pub6_ready()));
 
1258
                connect(&pub4, SIGNAL(resultsReady()), SLOT(pub4_ready()));
 
1259
        }
 
1260
 
 
1261
        void start()
 
1262
        {
 
1263
                counter = 1;
 
1264
                success = false;
 
1265
                have6 = false;
 
1266
                have4 = false;
 
1267
                started = true;
 
1268
                tryPublish();
 
1269
        }
 
1270
 
 
1271
        bool isStarted() const
 
1272
        {
 
1273
                return started;
 
1274
        }
 
1275
 
 
1276
        // comments in this method apply to setUseIPv4 as well.
 
1277
        void setUseIPv6(bool b)
 
1278
        {
 
1279
                if(b == use6)
 
1280
                        return;
 
1281
 
 
1282
                use6 = b;
 
1283
                if(!started)
 
1284
                        return;
 
1285
 
 
1286
                // a "deferred call to doDisable" and "publish operations"
 
1287
                //   are mutually exclusive.  thus, a deferred call is only
 
1288
                //   invoked when both publishes are canceled, and the
 
1289
                //   deferred call is canceled if any of the publishes are
 
1290
                //   reinstantiated.
 
1291
 
 
1292
                if(use6)
 
1293
                {
 
1294
                        if(use4)
 
1295
                        {
 
1296
                                // if the other is already active, then
 
1297
                                //   just activate this one without
 
1298
                                //   recomputing the hostname
 
1299
                                tryPublish6();
 
1300
                        }
 
1301
                        else
 
1302
                        {
 
1303
                                sess.reset();
 
1304
 
 
1305
                                // otherwise, recompute the hostname
 
1306
                                tryPublish();
 
1307
                        }
 
1308
                }
 
1309
                else
 
1310
                {
 
1311
                        pub6.cancel();
 
1312
                        have6 = false;
 
1313
                        if(!use4)
 
1314
                                sess.defer(this, "doDisable");
 
1315
                }
 
1316
        }
 
1317
 
 
1318
        void setUseIPv4(bool b)
 
1319
        {
 
1320
                if(b == use4)
 
1321
                        return;
 
1322
 
 
1323
                use4 = b;
 
1324
                if(!started)
 
1325
                        return;
 
1326
 
 
1327
                if(use4)
 
1328
                {
 
1329
                        if(use6)
 
1330
                        {
 
1331
                                tryPublish4();
 
1332
                        }
 
1333
                        else
 
1334
                        {
 
1335
                                sess.reset();
 
1336
                                tryPublish();
 
1337
                        }
 
1338
                }
 
1339
                else
 
1340
                {
 
1341
                        pub4.cancel();
 
1342
                        have4 = false;
 
1343
                        if(!use6)
 
1344
                                sess.defer(this, "doDisable");
 
1345
                }
 
1346
        }
 
1347
 
 
1348
signals:
 
1349
        void hostName(const QByteArray &str);
 
1350
 
 
1351
private:
 
1352
        void tryPublish()
 
1353
        {
 
1354
                QString me = QHostInfo::localHostName();
 
1355
 
 
1356
                // some hosts may already have ".local" in their name
 
1357
                if(me.endsWith(".local"))
 
1358
                        me.truncate(me.length() - 6);
 
1359
 
 
1360
                // prefix our hostname so we don't conflict with a system
 
1361
                //   mdns daemon
 
1362
                me.prepend("jdns-");
 
1363
 
 
1364
                if(counter > 1)
 
1365
                        me += QString("-%1").arg(counter);
 
1366
 
 
1367
                host = escapeDomainPart(me.toUtf8()) + ".local.";
 
1368
 
 
1369
                if(use6)
 
1370
                        tryPublish6();
 
1371
                if(use4)
 
1372
                        tryPublish4();
 
1373
        }
 
1374
 
 
1375
        void tryPublish6()
 
1376
        {
 
1377
                pub6.start(JDnsPublishAddress::IPv6, host);
 
1378
        }
 
1379
 
 
1380
        void tryPublish4()
 
1381
        {
 
1382
                pub4.start(JDnsPublishAddress::IPv4, host);
 
1383
        }
 
1384
 
 
1385
        void tryDone()
 
1386
        {
 
1387
                bool done = true;
 
1388
                if(use6 && !have6)
 
1389
                        done = false;
 
1390
                if(use4 && !have4)
 
1391
                        done = false;
 
1392
 
 
1393
                if(done)
 
1394
                {
 
1395
                        success = true;
 
1396
                        emit hostName(host);
 
1397
                }
 
1398
        }
 
1399
 
 
1400
        void handleFail()
 
1401
        {
 
1402
                // we get here if we fail to publish at all, or if we
 
1403
                //   successfully publish but then fail later on.  in the
 
1404
                //   latter case it means we "lost" our host records.
 
1405
 
 
1406
                bool lostHost = success; // as in earlier publish success
 
1407
                success = false;
 
1408
 
 
1409
                // if we lost a hostname with a suffix, or counter is
 
1410
                //   at 99, then start counter over at 1 (no suffix).
 
1411
                if((lostHost && counter > 1) || counter >= 99)
 
1412
                        counter = 1;
 
1413
                else
 
1414
                        ++counter;
 
1415
 
 
1416
                tryPublish();
 
1417
 
 
1418
                // only emit lost host signal once
 
1419
                if(lostHost)
 
1420
                        emit hostName(QByteArray());
 
1421
        }
 
1422
 
 
1423
private slots:
 
1424
        void doDisable()
 
1425
        {
 
1426
                bool lostHost = success;
 
1427
                success = false;
 
1428
 
 
1429
                if(lostHost)
 
1430
                        emit hostName(QByteArray());
 
1431
        }
 
1432
 
 
1433
        void pub6_ready()
 
1434
        {
 
1435
                if(pub6.success())
 
1436
                {
 
1437
                        have6 = true;
 
1438
                        tryDone();
 
1439
                }
 
1440
                else
 
1441
                {
 
1442
                        have6 = false;
 
1443
                        have4 = false;
 
1444
                        pub4.cancel();
 
1445
                        handleFail();
 
1446
                }
 
1447
        }
 
1448
 
 
1449
        void pub4_ready()
 
1450
        {
 
1451
                if(pub4.success())
 
1452
                {
 
1453
                        have4 = true;
 
1454
                        tryDone();
 
1455
                }
 
1456
                else
 
1457
                {
 
1458
                        have4 = false;
 
1459
                        have6 = false;
 
1460
                        pub6.cancel();
 
1461
                        handleFail();
 
1462
                }
 
1463
        }
 
1464
};
 
1465
 
 
1466
//----------------------------------------------------------------------------
 
1467
// JDnsPublish
 
1468
//----------------------------------------------------------------------------
 
1469
class JDnsPublish;
 
1470
 
 
1471
class JDnsPublishExtra : public QObject
 
1472
{
 
1473
        Q_OBJECT
 
1474
 
 
1475
public:
 
1476
        JDnsPublishExtra(JDnsPublish *_jdnsPub);
 
1477
        ~JDnsPublishExtra();
 
1478
 
 
1479
        void start(const QJDns::Record &_rec);
 
1480
        void update(const QJDns::Record &_rec);
 
1481
 
 
1482
signals:
 
1483
        void published();
 
1484
        void error(JDnsSharedRequest::Error e);
 
1485
 
 
1486
private:
 
1487
        friend class JDnsPublish;
 
1488
 
 
1489
        JDnsPublish *jdnsPub;
 
1490
        bool started;
 
1491
        JDnsSharedRequest pub;
 
1492
        QJDns::Record rec;
 
1493
        bool have;
 
1494
        bool need_update;
 
1495
};
 
1496
 
 
1497
// This class publishes SRV/TXT/PTR for a service.  if a hostName is not
 
1498
//   is not available (see JDnsPublishAddresses) then the publish action
 
1499
//   will be deferred until one is available.  SRV and TXT are published
 
1500
//   as unique records, and once they both succeed then the PTR record
 
1501
//   is published.  once the PTR succeeds, then published() is emitted.
 
1502
//   if a conflict occurs with any action, then the whole thing fails and
 
1503
//   error() is emitted.  if, at any time, the hostName is lost, then
 
1504
//   then the SRV operation is canceled, but no error is emitted.  when the
 
1505
//   hostName is regained, then the SRV record is republished.
 
1506
//
 
1507
// It's important to note that published() is only emitted once ever, even
 
1508
//   if a hostName change causes a republishing.  this way, hostName changes
 
1509
//   are completely transparent.
 
1510
class JDnsPublish : public QObject
 
1511
{
 
1512
        Q_OBJECT
 
1513
 
 
1514
public:
 
1515
        JDnsShared *jdns;
 
1516
        JDnsSharedRequest pub_srv;
 
1517
        JDnsSharedRequest pub_txt;
 
1518
        JDnsSharedRequest pub_ptr;
 
1519
 
 
1520
        bool have_srv, have_txt, have_ptr;
 
1521
        bool need_update_txt;
 
1522
 
 
1523
        QByteArray fullname;
 
1524
        QByteArray instance;
 
1525
        QByteArray type;
 
1526
        QByteArray host;
 
1527
        int port;
 
1528
        QList<QByteArray> attribs;
 
1529
 
 
1530
        QSet<JDnsPublishExtra*> extraList;
 
1531
 
 
1532
        JDnsPublish(JDnsShared *_jdns, QObject *parent = 0) :
 
1533
                QObject(parent),
 
1534
                jdns(_jdns),
 
1535
                pub_srv(_jdns, this),
 
1536
                pub_txt(_jdns, this),
 
1537
                pub_ptr(_jdns, this)
 
1538
        {
 
1539
                connect(&pub_srv, SIGNAL(resultsReady()), SLOT(pub_srv_ready()));
 
1540
                connect(&pub_txt, SIGNAL(resultsReady()), SLOT(pub_txt_ready()));
 
1541
                connect(&pub_ptr, SIGNAL(resultsReady()), SLOT(pub_ptr_ready()));
 
1542
        }
 
1543
 
 
1544
        ~JDnsPublish()
 
1545
        {
 
1546
                qDeleteAll(extraList);
 
1547
        }
 
1548
 
 
1549
        void start(const QString &_instance, const QByteArray &_type, const QByteArray &localHost, int _port, const QMap<QString,QByteArray> &attributes)
 
1550
        {
 
1551
                type = _type;
 
1552
                Q_ASSERT(validServiceType(type));
 
1553
 
 
1554
                instance = escapeDomainPart(_instance.toUtf8());
 
1555
                fullname = instance + '.' + type + ".local.";
 
1556
                host = localHost;
 
1557
                port = _port;
 
1558
                attribs = makeTxtList(attributes);
 
1559
 
 
1560
                have_srv = false;
 
1561
                have_txt = false;
 
1562
                have_ptr = false;
 
1563
                need_update_txt = false;
 
1564
 
 
1565
                // no host?  defer publishing till we have one
 
1566
                if(host.isEmpty())
 
1567
                        return;
 
1568
 
 
1569
                doPublish();
 
1570
        }
 
1571
 
 
1572
        void update(const QMap<QString,QByteArray> &attributes)
 
1573
        {
 
1574
                attribs = makeTxtList(attributes);
 
1575
 
 
1576
                // still publishing the initial txt?
 
1577
                if(!have_txt)
 
1578
                {
 
1579
                        // flag that we want to update once the publish
 
1580
                        //   succeeds.
 
1581
                        need_update_txt = true;
 
1582
                        return;
 
1583
                }
 
1584
 
 
1585
                // no SRV, but have TXT?  this means we lost SRV due to
 
1586
                //   a hostname change.
 
1587
                if(!have_srv)
 
1588
                {
 
1589
                        // in that case, revoke the TXT.  it'll get
 
1590
                        //   republished after SRV then.
 
1591
                        have_txt = false;
 
1592
                        pub_txt.cancel();
 
1593
                        return;
 
1594
                }
 
1595
 
 
1596
                doPublishTxt();
 
1597
        }
 
1598
 
 
1599
public slots:
 
1600
        // pass empty host if host lost
 
1601
        void hostChanged(const QByteArray &_host)
 
1602
        {
 
1603
                bool changed = (host != _host);
 
1604
 
 
1605
                if(changed)
 
1606
                {
 
1607
                        host = _host;
 
1608
 
 
1609
                        if(host.isEmpty())
 
1610
                        {
 
1611
                                // cancel srv record momentarily
 
1612
                                have_srv = false;
 
1613
                                pub_srv.cancel();
 
1614
                        }
 
1615
                        else
 
1616
                        {
 
1617
                                // we now have a host, publish
 
1618
                                doPublish();
 
1619
                        }
 
1620
                }
 
1621
        }
 
1622
 
 
1623
signals:
 
1624
        void published();
 
1625
        void error(JDnsSharedRequest::Error e);
 
1626
 
 
1627
private:
 
1628
        friend class JDnsPublishExtra;
 
1629
 
 
1630
        static QList<QByteArray> makeTxtList(const QMap<QString,QByteArray> &attributes)
 
1631
        {
 
1632
                QList<QByteArray> out;
 
1633
 
 
1634
                QMapIterator<QString,QByteArray> it(attributes);
 
1635
                while(it.hasNext())
 
1636
                {
 
1637
                        it.next();
 
1638
                        out += it.key().toLatin1() + '=' + it.value();
 
1639
                }
 
1640
                if(out.isEmpty())
 
1641
                        out += QByteArray();
 
1642
 
 
1643
                return out;
 
1644
        }
 
1645
 
 
1646
        void doPublish()
 
1647
        {
 
1648
                // SRV
 
1649
                QJDns::Record rec;
 
1650
                rec.type = QJDns::Srv;
 
1651
                rec.owner = fullname;
 
1652
                rec.ttl = 120;
 
1653
                rec.haveKnown = true;
 
1654
                rec.name = host;
 
1655
                rec.port = port;
 
1656
                rec.priority = 0;
 
1657
                rec.weight = 0;
 
1658
                pub_srv.publish(QJDns::Unique, rec);
 
1659
 
 
1660
                // if we're just republishing SRV after losing/regaining
 
1661
                //   our hostname, then TXT is already published
 
1662
                if(!have_txt)
 
1663
                        doPublishTxt();
 
1664
 
 
1665
                // publish extra records as needed
 
1666
                foreach(JDnsPublishExtra *extra, extraList)
 
1667
                {
 
1668
                        if(!extra->have)
 
1669
                                doPublishExtra(extra);
 
1670
                }
 
1671
        }
 
1672
 
 
1673
        void doPublishTxt()
 
1674
        {
 
1675
                // TXT
 
1676
                QJDns::Record rec;
 
1677
                rec.type = QJDns::Txt;
 
1678
                rec.owner = fullname;
 
1679
                rec.ttl = 4500;
 
1680
                rec.haveKnown = true;
 
1681
                rec.texts = attribs;
 
1682
 
 
1683
                if(!have_txt)
 
1684
                        pub_txt.publish(QJDns::Unique, rec);
 
1685
                else
 
1686
                        pub_txt.publishUpdate(rec);
 
1687
        }
 
1688
 
 
1689
        void tryDone()
 
1690
        {
 
1691
                if(have_srv && have_txt)
 
1692
                {
 
1693
                        // PTR
 
1694
                        QJDns::Record rec;
 
1695
                        rec.type = QJDns::Ptr;
 
1696
                        rec.owner = type + ".local.";
 
1697
                        rec.ttl = 4500;
 
1698
                        rec.haveKnown = true;
 
1699
                        rec.name = fullname;
 
1700
                        pub_ptr.publish(QJDns::Shared, rec);
 
1701
                }
 
1702
        }
 
1703
 
 
1704
        void cleanup()
 
1705
        {
 
1706
                foreach(JDnsPublishExtra *extra, extraList)
 
1707
                        cleanupExtra(extra);
 
1708
                qDeleteAll(extraList);
 
1709
                extraList.clear();
 
1710
 
 
1711
                have_srv = false;
 
1712
                have_txt = false;
 
1713
                have_ptr = false;
 
1714
                pub_srv.cancel();
 
1715
                pub_txt.cancel();
 
1716
                pub_ptr.cancel();
 
1717
        }
 
1718
 
 
1719
        void publishExtra(JDnsPublishExtra *extra)
 
1720
        {
 
1721
                Q_ASSERT(!extraList.contains(extra));
 
1722
 
 
1723
                connect(&extra->pub, SIGNAL(resultsReady()), SLOT(pub_extra_ready()));
 
1724
                extraList += extra;
 
1725
 
 
1726
                // defer publishing until SRV is ready
 
1727
                if(!have_srv)
 
1728
                        return;
 
1729
 
 
1730
                doPublishExtra(extra);
 
1731
        }
 
1732
 
 
1733
        void publishExtraUpdate(JDnsPublishExtra *extra)
 
1734
        {
 
1735
                if(!extra->have)
 
1736
                {
 
1737
                        extra->need_update = true;
 
1738
                        return;
 
1739
                }
 
1740
 
 
1741
                if(!have_srv)
 
1742
                {
 
1743
                        extra->have = false;
 
1744
                        extra->pub.cancel();
 
1745
                        return;
 
1746
                }
 
1747
 
 
1748
                doPublishExtra(extra);
 
1749
        }
 
1750
 
 
1751
        void unpublishExtra(JDnsPublishExtra *extra)
 
1752
        {
 
1753
                extraList.remove(extra);
 
1754
        }
 
1755
 
 
1756
        void doPublishExtra(JDnsPublishExtra *extra)
 
1757
        {
 
1758
                if(!extra->have)
 
1759
                        extra->pub.publish(QJDns::Unique, extra->rec);
 
1760
                else
 
1761
                        extra->pub.publishUpdate(extra->rec);
 
1762
        }
 
1763
 
 
1764
        void cleanupExtra(JDnsPublishExtra *extra)
 
1765
        {
 
1766
                extra->pub.cancel();
 
1767
                extra->disconnect(this);
 
1768
                extra->started = false;
 
1769
                extra->have = false;
 
1770
        }
 
1771
 
 
1772
private slots:
 
1773
        void pub_srv_ready()
 
1774
        {
 
1775
                if(pub_srv.success())
 
1776
                {
 
1777
                        have_srv = true;
 
1778
                        tryDone();
 
1779
                }
 
1780
                else
 
1781
                {
 
1782
                        JDnsSharedRequest::Error e = pub_srv.error();
 
1783
                        cleanup();
 
1784
                        emit error(e);
 
1785
                }
 
1786
        }
 
1787
 
 
1788
        void pub_txt_ready()
 
1789
        {
 
1790
                if(pub_txt.success())
 
1791
                {
 
1792
                        have_txt = true;
 
1793
 
 
1794
                        if(need_update_txt)
 
1795
                        {
 
1796
                                need_update_txt = false;
 
1797
                                doPublishTxt();
 
1798
                        }
 
1799
 
 
1800
                        tryDone();
 
1801
                }
 
1802
                else
 
1803
                {
 
1804
                        JDnsSharedRequest::Error e = pub_txt.error();
 
1805
                        cleanup();
 
1806
                        emit error(e);
 
1807
                }
 
1808
        }
 
1809
 
 
1810
        void pub_ptr_ready()
 
1811
        {
 
1812
                if(pub_ptr.success())
 
1813
                {
 
1814
                        have_ptr = true;
 
1815
                        emit published();
 
1816
                }
 
1817
                else
 
1818
                {
 
1819
                        JDnsSharedRequest::Error e = pub_ptr.error();
 
1820
                        cleanup();
 
1821
                        emit error(e);
 
1822
                }
 
1823
        }
 
1824
 
 
1825
        void pub_extra_ready()
 
1826
        {
 
1827
                JDnsSharedRequest *req = (JDnsSharedRequest *)sender();
 
1828
                JDnsPublishExtra *extra = 0;
 
1829
                foreach(JDnsPublishExtra *e, extraList)
 
1830
                {
 
1831
                        if(&e->pub == req)
 
1832
                        {
 
1833
                                extra = e;
 
1834
                                break;
 
1835
                        }
 
1836
                }
 
1837
                Q_ASSERT(extra);
 
1838
 
 
1839
                if(extra->pub.success())
 
1840
                {
 
1841
                        extra->have = true;
 
1842
 
 
1843
                        if(extra->need_update)
 
1844
                        {
 
1845
                                extra->need_update = false;
 
1846
                                doPublishExtra(extra);
 
1847
                        }
 
1848
 
 
1849
                        emit extra->published();
 
1850
                }
 
1851
                else
 
1852
                {
 
1853
                        JDnsSharedRequest::Error e = extra->pub.error();
 
1854
                        cleanupExtra(extra);
 
1855
                        emit extra->error(e);
 
1856
                }
 
1857
        }
 
1858
};
 
1859
 
 
1860
JDnsPublishExtra::JDnsPublishExtra(JDnsPublish *_jdnsPub) :
 
1861
        QObject(_jdnsPub),
 
1862
        jdnsPub(_jdnsPub),
 
1863
        started(false),
 
1864
        pub(_jdnsPub->jdns, this)
 
1865
{
 
1866
}
 
1867
 
 
1868
JDnsPublishExtra::~JDnsPublishExtra()
 
1869
{
 
1870
        if(started)
 
1871
                jdnsPub->unpublishExtra(this);
 
1872
}
 
1873
 
 
1874
void JDnsPublishExtra::start(const QJDns::Record &_rec)
 
1875
{
 
1876
        rec = _rec;
 
1877
        started = true;
 
1878
        have = false;
 
1879
        need_update = false;
 
1880
        jdnsPub->publishExtra(this);
 
1881
}
 
1882
 
 
1883
void JDnsPublishExtra::update(const QJDns::Record &_rec)
 
1884
{
 
1885
        rec = _rec;
 
1886
        jdnsPub->publishExtraUpdate(this);
 
1887
}
 
1888
 
 
1889
//----------------------------------------------------------------------------
 
1890
// JDnsServiceProvider
 
1891
//----------------------------------------------------------------------------
 
1892
class BrowseItem
 
1893
{
 
1894
public:
 
1895
        const int id;
 
1896
        JDnsBrowse * const browse;
 
1897
        ObjectSession *sess;
 
1898
 
 
1899
        BrowseItem(int _id, JDnsBrowse *_browse) :
 
1900
                id(_id),
 
1901
                browse(_browse),
 
1902
                sess(0)
 
1903
        {
 
1904
        }
 
1905
 
 
1906
        ~BrowseItem()
 
1907
        {
 
1908
                delete browse;
 
1909
                delete sess;
 
1910
        }
 
1911
};
 
1912
 
 
1913
class BrowseItemList
 
1914
{
 
1915
private:
 
1916
        QSet<BrowseItem*> items;
 
1917
        QHash<int,BrowseItem*> indexById;
 
1918
        QHash<JDnsBrowse*,BrowseItem*> indexByBrowse;
 
1919
        IdManager idman;
 
1920
 
 
1921
public:
 
1922
        ~BrowseItemList()
 
1923
        {
 
1924
                qDeleteAll(items);
 
1925
        }
 
1926
 
 
1927
        int reserveId()
 
1928
        {
 
1929
                return idman.reserveId();
 
1930
        }
 
1931
 
 
1932
        void insert(BrowseItem *item)
 
1933
        {
 
1934
                items.insert(item);
 
1935
                indexById.insert(item->id, item);
 
1936
                indexByBrowse.insert(item->browse, item);
 
1937
        }
 
1938
 
 
1939
        void remove(BrowseItem *item)
 
1940
        {
 
1941
                indexById.remove(item->id);
 
1942
                indexByBrowse.remove(item->browse);
 
1943
                items.remove(item);
 
1944
                if(item->id != -1)
 
1945
                        idman.releaseId(item->id);
 
1946
                delete item;
 
1947
        }
 
1948
 
 
1949
        BrowseItem *itemById(int id) const
 
1950
        {
 
1951
                return indexById.value(id);
 
1952
        }
 
1953
 
 
1954
        BrowseItem *itemByBrowse(JDnsBrowse *browse) const
 
1955
        {
 
1956
                return indexByBrowse.value(browse);
 
1957
        }
 
1958
};
 
1959
 
 
1960
class ResolveItem
 
1961
{
 
1962
public:
 
1963
        const int id;
 
1964
        JDnsServiceResolve * const resolve;
 
1965
        ObjectSession *sess;
 
1966
 
 
1967
        ResolveItem(int _id, JDnsServiceResolve *_resolve) :
 
1968
                id(_id),
 
1969
                resolve(_resolve),
 
1970
                sess(0)
 
1971
        {
 
1972
        }
 
1973
 
 
1974
        ~ResolveItem()
 
1975
        {
 
1976
                delete resolve;
 
1977
                delete sess;
 
1978
        }
 
1979
};
 
1980
 
 
1981
class ResolveItemList
 
1982
{
 
1983
private:
 
1984
        QSet<ResolveItem*> items;
 
1985
        QHash<int,ResolveItem*> indexById;
 
1986
        QHash<JDnsServiceResolve*,ResolveItem*> indexByResolve;
 
1987
        IdManager idman;
 
1988
 
 
1989
public:
 
1990
        ~ResolveItemList()
 
1991
        {
 
1992
                qDeleteAll(items);
 
1993
        }
 
1994
 
 
1995
        int reserveId()
 
1996
        {
 
1997
                return idman.reserveId();
 
1998
        }
 
1999
 
 
2000
        void insert(ResolveItem *item)
 
2001
        {
 
2002
                items.insert(item);
 
2003
                indexById.insert(item->id, item);
 
2004
                indexByResolve.insert(item->resolve, item);
 
2005
        }
 
2006
 
 
2007
        void remove(ResolveItem *item)
 
2008
        {
 
2009
                indexById.remove(item->id);
 
2010
                indexByResolve.remove(item->resolve);
 
2011
                items.remove(item);
 
2012
                if(item->id != -1)
 
2013
                        idman.releaseId(item->id);
 
2014
                delete item;
 
2015
        }
 
2016
 
 
2017
        ResolveItem *itemById(int id) const
 
2018
        {
 
2019
                return indexById.value(id);
 
2020
        }
 
2021
 
 
2022
        ResolveItem *itemByResolve(JDnsServiceResolve *resolve) const
 
2023
        {
 
2024
                return indexByResolve.value(resolve);
 
2025
        }
 
2026
};
 
2027
 
 
2028
class PublishItem
 
2029
{
 
2030
public:
 
2031
        const int id;
 
2032
        JDnsPublish * const publish;
 
2033
        ObjectSession *sess;
 
2034
 
 
2035
        PublishItem(int _id, JDnsPublish *_publish) :
 
2036
                id(_id),
 
2037
                publish(_publish),
 
2038
                sess(0)
 
2039
        {
 
2040
        }
 
2041
 
 
2042
        ~PublishItem()
 
2043
        {
 
2044
                delete publish;
 
2045
                delete sess;
 
2046
        }
 
2047
};
 
2048
 
 
2049
class PublishItemList
 
2050
{
 
2051
public:
 
2052
        QSet<PublishItem*> items;
 
2053
 
 
2054
private:
 
2055
        QHash<int,PublishItem*> indexById;
 
2056
        QHash<JDnsPublish*,PublishItem*> indexByPublish;
 
2057
        IdManager idman;
 
2058
 
 
2059
public:
 
2060
        ~PublishItemList()
 
2061
        {
 
2062
                qDeleteAll(items);
 
2063
        }
 
2064
 
 
2065
        int reserveId()
 
2066
        {
 
2067
                return idman.reserveId();
 
2068
        }
 
2069
 
 
2070
        void insert(PublishItem *item)
 
2071
        {
 
2072
                items.insert(item);
 
2073
                indexById.insert(item->id, item);
 
2074
                indexByPublish.insert(item->publish, item);
 
2075
        }
 
2076
 
 
2077
        void remove(PublishItem *item)
 
2078
        {
 
2079
                indexById.remove(item->id);
 
2080
                indexByPublish.remove(item->publish);
 
2081
                items.remove(item);
 
2082
                if(item->id != -1)
 
2083
                        idman.releaseId(item->id);
 
2084
                delete item;
 
2085
        }
 
2086
 
 
2087
        PublishItem *itemById(int id) const
 
2088
        {
 
2089
                return indexById.value(id);
 
2090
        }
 
2091
 
 
2092
        PublishItem *itemByPublish(JDnsPublish *publish) const
 
2093
        {
 
2094
                return indexByPublish.value(publish);
 
2095
        }
 
2096
};
 
2097
 
 
2098
class PublishExtraItem
 
2099
{
 
2100
public:
 
2101
        const int id;
 
2102
        JDnsPublishExtra * const publish;
 
2103
        ObjectSession *sess;
 
2104
 
 
2105
        PublishExtraItem(int _id, JDnsPublishExtra *_publish) :
 
2106
                id(_id),
 
2107
                publish(_publish),
 
2108
                sess(0)
 
2109
        {
 
2110
        }
 
2111
 
 
2112
        ~PublishExtraItem()
 
2113
        {
 
2114
                delete publish;
 
2115
                delete sess;
 
2116
        }
 
2117
};
 
2118
 
 
2119
class PublishExtraItemList
 
2120
{
 
2121
public:
 
2122
        QSet<PublishExtraItem*> items;
 
2123
 
 
2124
private:
 
2125
        QHash<int,PublishExtraItem*> indexById;
 
2126
        QHash<JDnsPublishExtra*,PublishExtraItem*> indexByPublish;
 
2127
        IdManager idman;
 
2128
 
 
2129
public:
 
2130
        ~PublishExtraItemList()
 
2131
        {
 
2132
                qDeleteAll(items);
 
2133
        }
 
2134
 
 
2135
        void clear()
 
2136
        {
 
2137
                qDeleteAll(items);
 
2138
                items.clear();
 
2139
                indexById.clear();
 
2140
                indexByPublish.clear();
 
2141
                idman.clear();
 
2142
        }
 
2143
 
 
2144
        int reserveId()
 
2145
        {
 
2146
                return idman.reserveId();
 
2147
        }
 
2148
 
 
2149
        void insert(PublishExtraItem *item)
 
2150
        {
 
2151
                items.insert(item);
 
2152
                indexById.insert(item->id, item);
 
2153
                indexByPublish.insert(item->publish, item);
 
2154
        }
 
2155
 
 
2156
        void remove(PublishExtraItem *item)
 
2157
        {
 
2158
                indexById.remove(item->id);
 
2159
                indexByPublish.remove(item->publish);
 
2160
                items.remove(item);
 
2161
                if(item->id != -1)
 
2162
                        idman.releaseId(item->id);
 
2163
                delete item;
 
2164
        }
 
2165
 
 
2166
        PublishExtraItem *itemById(int id) const
 
2167
        {
 
2168
                return indexById.value(id);
 
2169
        }
 
2170
 
 
2171
        PublishExtraItem *itemByPublish(JDnsPublishExtra *publish) const
 
2172
        {
 
2173
                return indexByPublish.value(publish);
 
2174
        }
 
2175
};
 
2176
 
 
2177
class JDnsServiceProvider : public ServiceProvider
 
2178
{
 
2179
        Q_OBJECT
 
2180
 
 
2181
public:
 
2182
        JDnsGlobal *global;
 
2183
 
 
2184
        // browse
 
2185
        BrowseItemList browseItemList;
 
2186
        QHash<QByteArray,ServiceInstance> items;
 
2187
 
 
2188
        // resolve
 
2189
        ResolveItemList resolveItemList;
 
2190
 
 
2191
        // publish
 
2192
        JDnsPublishAddresses *pub_addresses;
 
2193
        QByteArray localHost;
 
2194
        PublishItemList publishItemList;
 
2195
        PublishExtraItemList publishExtraItemList;
 
2196
 
 
2197
        static JDnsServiceProvider *create(JDnsGlobal *global, QObject *parent = 0)
 
2198
        {
 
2199
                return new JDnsServiceProvider(global, parent);
 
2200
        }
 
2201
 
 
2202
        JDnsServiceProvider(JDnsGlobal *_global, QObject *parent = 0) :
 
2203
                ServiceProvider(parent),
 
2204
                pub_addresses(0)
 
2205
        {
 
2206
                global = _global;
 
2207
                connect(global, SIGNAL(interfacesChanged()), SLOT(interfacesChanged()));
 
2208
        }
 
2209
 
 
2210
        ~JDnsServiceProvider()
 
2211
        {
 
2212
                // make sure extra items are deleted before normal ones
 
2213
                publishExtraItemList.clear();
 
2214
        }
 
2215
 
 
2216
        virtual int browse_start(const QString &_type, const QString &_domain)
 
2217
        {
 
2218
                QString domain;
 
2219
                if(_domain.isEmpty() || _domain == ".")
 
2220
                        domain = "local.";
 
2221
                else
 
2222
                        domain = _domain;
 
2223
 
 
2224
                if(domain[domain.length() - 1] != '.')
 
2225
                        domain += '.';
 
2226
 
 
2227
                Q_ASSERT(domain.length() >= 2 && domain[domain.length() - 1] == '.');
 
2228
 
 
2229
                int id = browseItemList.reserveId();
 
2230
 
 
2231
                // no support for non-local domains
 
2232
                if(domain != "local.")
 
2233
                {
 
2234
                        BrowseItem *i = new BrowseItem(id, 0);
 
2235
                        i->sess = new ObjectSession(this);
 
2236
                        browseItemList.insert(i);
 
2237
                        i->sess->defer(this, "do_browse_error", Q_ARG(int, i->id),
 
2238
                                Q_ARG(XMPP::ServiceBrowser::Error, ServiceBrowser::ErrorNoWide));
 
2239
                        return i->id;
 
2240
                }
 
2241
 
 
2242
                if(!global->ensure_mul())
 
2243
                {
 
2244
                        BrowseItem *i = new BrowseItem(id, 0);
 
2245
                        i->sess = new ObjectSession(this);
 
2246
                        browseItemList.insert(i);
 
2247
                        i->sess->defer(this, "do_browse_error", Q_ARG(int, i->id),
 
2248
                                Q_ARG(XMPP::ServiceBrowser::Error, ServiceBrowser::ErrorNoLocal));
 
2249
                        return i->id;
 
2250
                }
 
2251
 
 
2252
                QByteArray type = _type.toUtf8();
 
2253
                if(!validServiceType(type))
 
2254
                {
 
2255
                        BrowseItem *i = new BrowseItem(id, 0);
 
2256
                        i->sess = new ObjectSession(this);
 
2257
                        browseItemList.insert(i);
 
2258
                        i->sess->defer(this, "do_browse_error", Q_ARG(int, i->id),
 
2259
                                Q_ARG(XMPP::ServiceBrowser::Error, ServiceBrowser::ErrorGeneric));
 
2260
                        return i->id;
 
2261
                }
 
2262
 
 
2263
                BrowseItem *i = new BrowseItem(id, new JDnsBrowse(global->mul, this));
 
2264
                connect(i->browse, SIGNAL(available(const QByteArray &)), SLOT(jb_available(const QByteArray &)));
 
2265
                connect(i->browse, SIGNAL(unavailable(const QByteArray &)), SLOT(jb_unavailable(const QByteArray &)));
 
2266
                browseItemList.insert(i);
 
2267
                i->browse->start(type);
 
2268
                return i->id;
 
2269
        }
 
2270
 
 
2271
        virtual void browse_stop(int id)
 
2272
        {
 
2273
                BrowseItem *i = browseItemList.itemById(id);
 
2274
                Q_ASSERT(i);
 
2275
 
 
2276
                browseItemList.remove(i);
 
2277
        }
 
2278
 
 
2279
        virtual int resolve_start(const QByteArray &name)
 
2280
        {
 
2281
                int id = resolveItemList.reserveId();
 
2282
 
 
2283
                if(!global->ensure_mul())
 
2284
                {
 
2285
                        ResolveItem *i = new ResolveItem(id, 0);
 
2286
                        i->sess = new ObjectSession(this);
 
2287
                        resolveItemList.insert(i);
 
2288
                        i->sess->defer(this, "do_resolve_error", Q_ARG(int, i->id),
 
2289
                                Q_ARG(XMPP::ServiceResolver::Error, ServiceResolver::ErrorNoLocal));
 
2290
                        return i->id;
 
2291
                }
 
2292
 
 
2293
                ResolveItem *i = new ResolveItem(id, new JDnsServiceResolve(global->mul, this));
 
2294
                connect(i->resolve, SIGNAL(finished()), SLOT(jr_finished()));
 
2295
                connect(i->resolve, SIGNAL(error(JDnsSharedRequest::Error)), SLOT(jr_error(JDnsSharedRequest::Error)));
 
2296
                resolveItemList.insert(i);
 
2297
                i->resolve->start(name);
 
2298
                return i->id;
 
2299
        }
 
2300
 
 
2301
        virtual void resolve_stop(int id)
 
2302
        {
 
2303
                ResolveItem *i = resolveItemList.itemById(id);
 
2304
                Q_ASSERT(i);
 
2305
 
 
2306
                resolveItemList.remove(i);
 
2307
        }
 
2308
 
 
2309
        virtual int publish_start(const QString &instance, const QString &_type, int port, const QMap<QString,QByteArray> &attributes)
 
2310
        {
 
2311
                int id = publishItemList.reserveId();
 
2312
 
 
2313
                if(!global->ensure_mul())
 
2314
                {
 
2315
                        PublishItem *i = new PublishItem(id, 0);
 
2316
                        i->sess = new ObjectSession(this);
 
2317
                        publishItemList.insert(i);
 
2318
                        i->sess->defer(this, "do_publish_error", Q_ARG(int, i->id),
 
2319
                                Q_ARG(XMPP::ServiceLocalPublisher::Error, ServiceLocalPublisher::ErrorNoLocal));
 
2320
                        return i->id;
 
2321
                }
 
2322
 
 
2323
                QByteArray type = _type.toUtf8();
 
2324
                if(!validServiceType(type))
 
2325
                {
 
2326
                        PublishItem *i = new PublishItem(id, 0);
 
2327
                        i->sess = new ObjectSession(this);
 
2328
                        publishItemList.insert(i);
 
2329
                        i->sess->defer(this, "do_publish_error", Q_ARG(int, i->id),
 
2330
                                Q_ARG(XMPP::ServiceLocalPublisher::Error, ServiceLocalPublisher::ErrorGeneric));
 
2331
                        return i->id;
 
2332
                }
 
2333
 
 
2334
                // make sure A/AAAA records are published
 
2335
                if(!pub_addresses)
 
2336
                {
 
2337
                        pub_addresses = new JDnsPublishAddresses(global->mul, this);
 
2338
                        connect(pub_addresses, SIGNAL(hostName(const QByteArray &)), SLOT(pub_addresses_hostName(const QByteArray &)));
 
2339
                        pub_addresses->setUseIPv6(global->haveMulticast6());
 
2340
                        pub_addresses->setUseIPv4(global->haveMulticast4());
 
2341
                        pub_addresses->start();
 
2342
                }
 
2343
 
 
2344
                // it's okay to attempt to publish even if pub_addresses
 
2345
                //   hasn't succeeded yet.  JDnsPublish is smart enough to
 
2346
                //   defer the operation until a host is acquired.
 
2347
                PublishItem *i = new PublishItem(id, new JDnsPublish(global->mul, this));
 
2348
                connect(i->publish, SIGNAL(published()), SLOT(jp_published()));
 
2349
                connect(i->publish, SIGNAL(error(JDnsSharedRequest::Error)), SLOT(jp_error(JDnsSharedRequest::Error)));
 
2350
                publishItemList.insert(i);
 
2351
                i->publish->start(instance, type, localHost, port, attributes);
 
2352
                return i->id;
 
2353
        }
 
2354
 
 
2355
        virtual void publish_update(int id, const QMap<QString,QByteArray> &attributes)
 
2356
        {
 
2357
                PublishItem *i = publishItemList.itemById(id);
 
2358
                Q_ASSERT(i);
 
2359
 
 
2360
                // if we already have an error queued, do nothing
 
2361
                if(i->sess->isDeferred(this, "do_publish_error"))
 
2362
                        return;
 
2363
 
 
2364
                i->publish->update(attributes);
 
2365
        }
 
2366
 
 
2367
        virtual void publish_stop(int id)
 
2368
        {
 
2369
                PublishItem *i = publishItemList.itemById(id);
 
2370
                Q_ASSERT(i);
 
2371
 
 
2372
                cleanupExtra(i);
 
2373
                publishItemList.remove(i);
 
2374
        }
 
2375
 
 
2376
        virtual int publish_extra_start(int pub_id, const NameRecord &name)
 
2377
        {
 
2378
                PublishItem *pi = publishItemList.itemById(pub_id);
 
2379
                Q_ASSERT(pi);
 
2380
 
 
2381
                int id = publishItemList.reserveId();
 
2382
 
 
2383
                QJDns::Record rec = exportJDNSRecord(name);
 
2384
                if(rec.type == -1)
 
2385
                {
 
2386
                        PublishExtraItem *i = new PublishExtraItem(id, 0);
 
2387
                        i->sess = new ObjectSession(this);
 
2388
                        publishExtraItemList.insert(i);
 
2389
                        i->sess->defer(this, "do_publish_extra_error", Q_ARG(int, i->id),
 
2390
                                Q_ARG(XMPP::ServiceLocalPublisher::Error, ServiceLocalPublisher::ErrorGeneric));
 
2391
                        return i->id;
 
2392
                }
 
2393
 
 
2394
                // fill in owner if necessary
 
2395
                if(rec.owner.isEmpty())
 
2396
                        rec.owner = pi->publish->fullname;
 
2397
 
 
2398
                // fill in the ttl if necessary
 
2399
                if(rec.ttl == 0)
 
2400
                        rec.ttl = 4500;
 
2401
 
 
2402
                PublishExtraItem *i = new PublishExtraItem(id, new JDnsPublishExtra(pi->publish));
 
2403
                connect(i->publish, SIGNAL(published()), SLOT(jpe_published()));
 
2404
                connect(i->publish, SIGNAL(error(JDnsSharedRequest::Error)), SLOT(jpe_error(JDnsSharedRequest::Error)));
 
2405
                publishExtraItemList.insert(i);
 
2406
                i->publish->start(rec);
 
2407
                return i->id;
 
2408
        }
 
2409
 
 
2410
        virtual void publish_extra_update(int id, const NameRecord &name)
 
2411
        {
 
2412
                PublishExtraItem *i = publishExtraItemList.itemById(id);
 
2413
                Q_ASSERT(i);
 
2414
 
 
2415
                // if we already have an error queued, do nothing
 
2416
                if(i->sess->isDeferred(this, "do_publish_extra_error"))
 
2417
                        return;
 
2418
 
 
2419
                QJDns::Record rec = exportJDNSRecord(name);
 
2420
                if(rec.type == -1)
 
2421
                {
 
2422
                        i->sess = new ObjectSession(this);
 
2423
                        i->sess->defer(this, "do_publish_extra_error", Q_ARG(int, i->id),
 
2424
                                Q_ARG(XMPP::ServiceLocalPublisher::Error, ServiceLocalPublisher::ErrorGeneric));
 
2425
                        return;
 
2426
                }
 
2427
 
 
2428
                // fill in owner if necessary
 
2429
                if(rec.owner.isEmpty())
 
2430
                        rec.owner = static_cast<JDnsPublish*>(i->publish->parent())->fullname;
 
2431
 
 
2432
                // fill in the ttl if necessary
 
2433
                if(rec.ttl == 0)
 
2434
                        rec.ttl = 4500;
 
2435
 
 
2436
                i->publish->update(rec);
 
2437
        }
 
2438
 
 
2439
        virtual void publish_extra_stop(int id)
 
2440
        {
 
2441
                PublishExtraItem *i = publishExtraItemList.itemById(id);
 
2442
                Q_ASSERT(i);
 
2443
 
 
2444
                publishExtraItemList.remove(i);
 
2445
        }
 
2446
 
 
2447
private:
 
2448
        void cleanupExtra(PublishItem *pi)
 
2449
        {
 
2450
                // remove all extra publishes associated with this publish.
 
2451
                //   the association can be checked via QObject parenting.
 
2452
                QSet<PublishExtraItem*> remove;
 
2453
                foreach(PublishExtraItem *i, publishExtraItemList.items)
 
2454
                {
 
2455
                        if(static_cast<JDnsPublish*>(i->publish->parent()) == pi->publish)
 
2456
                                remove += i;
 
2457
                }
 
2458
 
 
2459
                foreach(PublishExtraItem *i, remove)
 
2460
                        publishExtraItemList.remove(i);
 
2461
        }
 
2462
 
 
2463
private slots:
 
2464
        void interfacesChanged()
 
2465
        {
 
2466
                if(pub_addresses)
 
2467
                {
 
2468
                        pub_addresses->setUseIPv6(global->haveMulticast6());
 
2469
                        pub_addresses->setUseIPv4(global->haveMulticast4());
 
2470
                }
 
2471
        }
 
2472
 
 
2473
        void jb_available(const QByteArray &instance)
 
2474
        {
 
2475
                JDnsBrowse *jb = (JDnsBrowse *)sender();
 
2476
                BrowseItem *i = browseItemList.itemByBrowse(jb);
 
2477
                Q_ASSERT(i);
 
2478
 
 
2479
                QByteArray name = instance + '.' + jb->typeAndDomain;
 
2480
                ServiceInstance si(QString::fromLatin1(instance), QString::fromLatin1(jb->type), "local.", QMap<QString,QByteArray>());
 
2481
                items.insert(name, si);
 
2482
 
 
2483
                emit browse_instanceAvailable(i->id, si);
 
2484
        }
 
2485
 
 
2486
        void jb_unavailable(const QByteArray &instance)
 
2487
        {
 
2488
                JDnsBrowse *jb = (JDnsBrowse *)sender();
 
2489
                BrowseItem *i = browseItemList.itemByBrowse(jb);
 
2490
                Q_ASSERT(i);
 
2491
 
 
2492
                QByteArray name = instance + '.' + jb->typeAndDomain;
 
2493
                Q_ASSERT(items.contains(name));
 
2494
 
 
2495
                ServiceInstance si = items.value(name);
 
2496
                items.remove(name);
 
2497
 
 
2498
                emit browse_instanceUnavailable(i->id, si);
 
2499
        }
 
2500
 
 
2501
        void do_browse_error(int id, XMPP::ServiceBrowser::Error e)
 
2502
        {
 
2503
                BrowseItem *i = browseItemList.itemById(id);
 
2504
                Q_ASSERT(i);
 
2505
 
 
2506
                browseItemList.remove(i);
 
2507
                emit browse_error(id, e);
 
2508
        }
 
2509
 
 
2510
        void jr_finished()
 
2511
        {
 
2512
                JDnsServiceResolve *jr = (JDnsServiceResolve *)sender();
 
2513
                ResolveItem *i = resolveItemList.itemByResolve(jr);
 
2514
                Q_ASSERT(i);
 
2515
 
 
2516
                // parse TXT list into attribute map
 
2517
                QMap<QString,QByteArray> attribs;
 
2518
                for(int n = 0; n < jr->attribs.count(); ++n)
 
2519
                {
 
2520
                        const QByteArray &a = jr->attribs[n];
 
2521
                        QString key;
 
2522
                        QByteArray value;
 
2523
                        int x = a.indexOf('=');
 
2524
                        if(x != -1)
 
2525
                        {
 
2526
                                key = QString::fromLatin1(a.mid(0, x));
 
2527
                                value = a.mid(x + 1);
 
2528
                        }
 
2529
                        else
 
2530
                        {
 
2531
                                key = QString::fromLatin1(a);
 
2532
                        }
 
2533
 
 
2534
                        attribs.insert(key, value);
 
2535
                }
 
2536
 
 
2537
                // one of these must be true
 
2538
                Q_ASSERT(jr->have4 || jr->have6);
 
2539
 
 
2540
                QList<ResolveResult> results;
 
2541
                if(jr->have6)
 
2542
                {
 
2543
                        ResolveResult r;
 
2544
                        r.attributes = attribs;
 
2545
                        r.address = jr->addr6;
 
2546
                        r.port = jr->port;
 
2547
                        r.hostName = jr->host;
 
2548
                        results += r;
 
2549
                }
 
2550
                if(jr->have4)
 
2551
                {
 
2552
                        ResolveResult r;
 
2553
                        r.attributes = attribs;
 
2554
                        r.address = jr->addr4;
 
2555
                        r.port = jr->port;
 
2556
                        r.hostName = jr->host;
 
2557
                        results += r;
 
2558
                }
 
2559
 
 
2560
                int id = i->id;
 
2561
                resolveItemList.remove(i);
 
2562
                emit resolve_resultsReady(id, results);
 
2563
        }
 
2564
 
 
2565
        void jr_error(JDnsSharedRequest::Error e)
 
2566
        {
 
2567
                JDnsServiceResolve *jr = (JDnsServiceResolve *)sender();
 
2568
                ResolveItem *i = resolveItemList.itemByResolve(jr);
 
2569
                Q_ASSERT(i);
 
2570
 
 
2571
                ServiceResolver::Error err;
 
2572
                if(e == JDnsSharedRequest::ErrorTimeout)
 
2573
                        err = ServiceResolver::ErrorTimeout;
 
2574
                else
 
2575
                        err = ServiceResolver::ErrorGeneric;
 
2576
 
 
2577
                int id = i->id;
 
2578
                resolveItemList.remove(i);
 
2579
                emit resolve_error(id, err);
 
2580
        }
 
2581
 
 
2582
        void do_resolve_error(int id, XMPP::ServiceResolver::Error e)
 
2583
        {
 
2584
                ResolveItem *i = resolveItemList.itemById(id);
 
2585
                Q_ASSERT(i);
 
2586
 
 
2587
                resolveItemList.remove(i);
 
2588
                emit resolve_error(id, e);
 
2589
        }
 
2590
 
 
2591
        void pub_addresses_hostName(const QByteArray &name)
 
2592
        {
 
2593
                // tell all active publishes about the change
 
2594
                foreach(PublishItem *item, publishItemList.items)
 
2595
                        item->publish->hostChanged(name);
 
2596
        }
 
2597
 
 
2598
        void jp_published()
 
2599
        {
 
2600
                JDnsPublish *jp = (JDnsPublish *)sender();
 
2601
                PublishItem *i = publishItemList.itemByPublish(jp);
 
2602
                Q_ASSERT(i);
 
2603
 
 
2604
                emit publish_published(i->id);
 
2605
        }
 
2606
 
 
2607
        void jp_error(JDnsSharedRequest::Error e)
 
2608
        {
 
2609
                JDnsPublish *jp = (JDnsPublish *)sender();
 
2610
                PublishItem *i = publishItemList.itemByPublish(jp);
 
2611
                Q_ASSERT(i);
 
2612
 
 
2613
                ServiceLocalPublisher::Error err;
 
2614
                if(e == JDnsSharedRequest::ErrorConflict)
 
2615
                        err = ServiceLocalPublisher::ErrorConflict;
 
2616
                else
 
2617
                        err = ServiceLocalPublisher::ErrorGeneric;
 
2618
 
 
2619
                int id = i->id;
 
2620
                cleanupExtra(i);
 
2621
                publishItemList.remove(i);
 
2622
                emit publish_error(id, err);
 
2623
        }
 
2624
 
 
2625
        void do_publish_error(int id, XMPP::ServiceLocalPublisher::Error e)
 
2626
        {
 
2627
                PublishItem *i = publishItemList.itemById(id);
 
2628
                Q_ASSERT(i);
 
2629
 
 
2630
                cleanupExtra(i);
 
2631
                publishItemList.remove(i);
 
2632
                emit publish_error(id, e);
 
2633
        }
 
2634
 
 
2635
        void jpe_published()
 
2636
        {
 
2637
                JDnsPublishExtra *jp = (JDnsPublishExtra *)sender();
 
2638
                PublishExtraItem *i = publishExtraItemList.itemByPublish(jp);
 
2639
                Q_ASSERT(i);
 
2640
 
 
2641
                emit publish_extra_published(i->id);
 
2642
        }
 
2643
 
 
2644
        void jpe_error(JDnsSharedRequest::Error e)
 
2645
        {
 
2646
                JDnsPublishExtra *jp = (JDnsPublishExtra *)sender();
 
2647
                PublishExtraItem *i = publishExtraItemList.itemByPublish(jp);
 
2648
                Q_ASSERT(i);
 
2649
 
 
2650
                ServiceLocalPublisher::Error err;
 
2651
                if(e == JDnsSharedRequest::ErrorConflict)
 
2652
                        err = ServiceLocalPublisher::ErrorConflict;
 
2653
                else
 
2654
                        err = ServiceLocalPublisher::ErrorGeneric;
 
2655
 
 
2656
                int id = i->id;
 
2657
                publishExtraItemList.remove(i);
 
2658
                emit publish_extra_error(id, err);
 
2659
        }
 
2660
 
 
2661
        void do_publish_extra_error(int id, XMPP::ServiceLocalPublisher::Error e)
 
2662
        {
 
2663
                PublishExtraItem *i = publishExtraItemList.itemById(id);
 
2664
                Q_ASSERT(i);
 
2665
 
 
2666
                publishExtraItemList.remove(i);
 
2667
                emit publish_extra_error(id, e);
 
2668
        }
 
2669
};
 
2670
 
 
2671
//----------------------------------------------------------------------------
 
2672
// JDnsProvider
 
2673
//----------------------------------------------------------------------------
 
2674
class JDnsProvider : public IrisNetProvider
 
2675
{
 
2676
        Q_OBJECT
 
2677
        Q_INTERFACES(XMPP::IrisNetProvider)
 
2678
 
 
2679
public:
 
2680
        JDnsGlobal *global;
 
2681
 
 
2682
        JDnsProvider()
 
2683
        {
 
2684
                global = 0;
 
2685
        }
 
2686
 
 
2687
        ~JDnsProvider()
 
2688
        {
 
2689
                delete global;
 
2690
        }
 
2691
 
 
2692
        void ensure_global()
 
2693
        {
 
2694
                if(!global)
 
2695
                        global = new JDnsGlobal;
 
2696
        }
 
2697
 
 
2698
        virtual NameProvider *createNameProviderInternet()
 
2699
        {
 
2700
                ensure_global();
 
2701
                return JDnsNameProvider::create(global, JDnsNameProvider::Internet);
 
2702
        }
 
2703
 
 
2704
        virtual NameProvider *createNameProviderLocal()
 
2705
        {
 
2706
                ensure_global();
 
2707
                return JDnsNameProvider::create(global, JDnsNameProvider::Local);
 
2708
        }
 
2709
 
 
2710
        virtual ServiceProvider *createServiceProvider()
 
2711
        {
 
2712
                ensure_global();
 
2713
                return JDnsServiceProvider::create(global);
 
2714
        }
 
2715
};
 
2716
 
 
2717
IrisNetProvider *irisnet_createJDnsProvider()
 
2718
{
 
2719
        return new JDnsProvider;
 
2720
}
 
2721
 
 
2722
}
 
2723
 
 
2724
#include "netnames_jdns.moc"