1
/****************************************************************************
2
**
3
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4
** All rights reserved.
5
** Contact: Nokia Corporation (qt-info@nokia.com)
6
**
7
** This file is part of the QtNetwork module of the Qt Toolkit.
8
**
9
** $QT_BEGIN_LICENSE:LGPL$
10
** GNU Lesser General Public License Usage
11
** This file may be used under the terms of the GNU Lesser General Public
12
** License version 2.1 as published by the Free Software Foundation and
13
** appearing in the file LICENSE.LGPL included in the packaging of this
14
** file. Please review the following information to ensure the GNU Lesser
15
** General Public License version 2.1 requirements will be met:
16
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17
**
18
** In addition, as a special exception, Nokia gives you certain additional
19
** rights. These rights are described in the Nokia Qt LGPL Exception
20
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21
**
22
** GNU General Public License Usage
23
** Alternatively, this file may be used under the terms of the GNU General
24
** Public License version 3.0 as published by the Free Software Foundation
25
** and appearing in the file LICENSE.GPL included in the packaging of this
26
** file. Please review the following information to ensure the GNU General
27
** Public License version 3.0 requirements will be met:
28
** http://www.gnu.org/copyleft/gpl.html.
29
**
30
** Other Usage
31
** Alternatively, this file may be used in accordance with the terms and
32
** conditions contained in a signed written agreement between you and Nokia.
33
**
34
**
35
**
36
**
37
**
38
** $QT_END_LICENSE$
39
**
40
****************************************************************************/
41
42
#include "qhostinfo.h"
43
#include "qhostinfo_p.h"
44
45
#include "QtCore/qscopedpointer.h"
46
#include <qabstracteventdispatcher.h>
47
#include <qcoreapplication.h>
48
#include <qmetaobject.h>
49
#include <qstringlist.h>
50
#include <qthread.h>
51
#include <qurl.h>
52
#include <private/qnetworksession_p.h>
53
54
#ifdef Q_OS_UNIX
55
#  include <unistd.h>
56
#endif
57
58
QT_BEGIN_NAMESPACE
59
60
//#define QHOSTINFO_DEBUG
61
62
#ifndef Q_OS_SYMBIAN
63
Q_GLOBAL_STATIC(QHostInfoLookupManager, theHostInfoLookupManager)
64
#else
65
Q_GLOBAL_STATIC(QSymbianHostInfoLookupManager, theHostInfoLookupManager)
66
#endif
67
68
/*!
69
    \class QHostInfo
70
    \brief The QHostInfo class provides static functions for host name lookups.
71
72
    \reentrant
73
    \inmodule QtNetwork
74
    \ingroup network
75
76
    QHostInfo uses the lookup mechanisms provided by the operating
77
    system to find the IP address(es) associated with a host name,
78
    or the host name associated with an IP address.
79
    The class provides two static convenience functions: one that
80
    works asynchronously and emits a signal once the host is found,
81
    and one that blocks and returns a QHostInfo object.
82
83
    To look up a host's IP addresses asynchronously, call lookupHost(),
84
    which takes the host name or IP address, a receiver object, and a slot
85
    signature as arguments and returns an ID. You can abort the
86
    lookup by calling abortHostLookup() with the lookup ID.
87
88
    Example:
89
90
    \snippet doc/src/snippets/code/src_network_kernel_qhostinfo.cpp 0
91
92
93
    The slot is invoked when the results are ready. The results are
94
    stored in a QHostInfo object. Call
95
    addresses() to get the list of IP addresses for the host, and
96
    hostName() to get the host name that was looked up.
97
98
    If the lookup failed, error() returns the type of error that
99
    occurred. errorString() gives a human-readable description of the
100
    lookup error.
101
102
    If you want a blocking lookup, use the QHostInfo::fromName() function:
103
104
    \snippet doc/src/snippets/code/src_network_kernel_qhostinfo.cpp 1
105
106
    QHostInfo supports Internationalized Domain Names (IDNs) through the
107
    IDNA and Punycode standards.
108
109
    To retrieve the name of the local host, use the static
110
    QHostInfo::localHostName() function.
111
112
    \note Since Qt 4.6.1 QHostInfo is using multiple threads for DNS lookup
113
    instead of one dedicated DNS thread. This improves performance,
114
    but also changes the order of signal emissions when using lookupHost()
115
    compared to previous versions of Qt.
116
    \note Since Qt 4.6.3 QHostInfo is using a small internal 60 second DNS cache
117
    for performance improvements.
118
119
    \sa QAbstractSocket, {http://www.rfc-editor.org/rfc/rfc3492.txt}{RFC 3492}
120
*/
121
122
static QBasicAtomicInt theIdCounter = Q_BASIC_ATOMIC_INITIALIZER(1);
123
124
/*!
125
    Looks up the IP address(es) associated with host name \a name, and
126
    returns an ID for the lookup. When the result of the lookup is
127
    ready, the slot or signal \a member in \a receiver is called with
128
    a QHostInfo argument. The QHostInfo object can then be inspected
129
    to get the results of the lookup.
130
131
    The lookup is performed by a single function call, for example:
132
133
    \snippet doc/src/snippets/code/src_network_kernel_qhostinfo.cpp 2
134
135
    The implementation of the slot prints basic information about the
136
    addresses returned by the lookup, or reports an error if it failed:
137
138
    \snippet doc/src/snippets/code/src_network_kernel_qhostinfo.cpp 3
139
140
    If you pass a literal IP address to \a name instead of a host name,
141
    QHostInfo will search for the domain name for the IP (i.e., QHostInfo will
142
    perform a \e reverse lookup). On success, the resulting QHostInfo will
143
    contain both the resolved domain name and IP addresses for the host
144
    name. Example:
145
146
    \snippet doc/src/snippets/code/src_network_kernel_qhostinfo.cpp 4
147
148
    \note There is no guarantee on the order the signals will be emitted
149
    if you start multiple requests with lookupHost().
150
151
    \sa abortHostLookup(), addresses(), error(), fromName()
152
*/
153
int QHostInfo::lookupHost(const QString &name, QObject *receiver,
154
                          const char *member)
155
{
156
#if defined QHOSTINFO_DEBUG
157
    qDebug("QHostInfo::lookupHost(\"%s\", %p, %s)",
158
           name.toLatin1().constData(), receiver, member ? member + 1 : 0);
159
#endif
160
161
    if (!QAbstractEventDispatcher::instance(QThread::currentThread())) {
162
        qWarning("QHostInfo::lookupHost() called with no event dispatcher");
163
        return -1;
164
    }
165
166
    qRegisterMetaType<QHostInfo>("QHostInfo");
167
168
    int id = theIdCounter.fetchAndAddRelaxed(1); // generate unique ID
169
170
    if (name.isEmpty()) {
171
        QHostInfo hostInfo(id);
172
        hostInfo.setError(QHostInfo::HostNotFound);
173
        hostInfo.setErrorString(QCoreApplication::translate("QHostInfo", "No host name given"));
174
        QScopedPointer<QHostInfoResult> result(new QHostInfoResult);
175
        QObject::connect(result.data(), SIGNAL(resultsReady(QHostInfo)),
176
                         receiver, member, Qt::QueuedConnection);
177
        result.data()->emitResultsReady(hostInfo);
178
        return id;
179
    }
180
181
#ifndef Q_OS_SYMBIAN
182
    QHostInfoLookupManager *manager = theHostInfoLookupManager();
183
184
    if (manager) {
185
        // the application is still alive
186
        if (manager->cache.isEnabled()) {
187
            // check cache first
188
            bool valid = false;
189
            QHostInfo info = manager->cache.get(name, &valid);
190
            if (valid) {
191
                info.setLookupId(id);
192
                QHostInfoResult result;
193
                QObject::connect(&result, SIGNAL(resultsReady(QHostInfo)), receiver, member, Qt::QueuedConnection);
194
                result.emitResultsReady(info);
195
                return id;
196
            }
197
        }
198
199
        // cache is not enabled or it was not in the cache, do normal lookup
200
        QHostInfoRunnable* runnable = new QHostInfoRunnable(name, id);
201
        QObject::connect(&runnable->resultEmitter, SIGNAL(resultsReady(QHostInfo)), receiver, member, Qt::QueuedConnection);
202
        manager->scheduleLookup(runnable);
203
    }
204
#else
205
    QSymbianHostInfoLookupManager *manager = theHostInfoLookupManager();
206
207
    if (manager) {
208
        // the application is still alive
209
        if (manager->cache.isEnabled()) {
210
            // check cache first
211
            bool valid = false;
212
            QHostInfo info = manager->cache.get(name, &valid);
213
            if (valid) {
214
                info.setLookupId(id);
215
                QHostInfoResult result;
216
                QObject::connect(&result, SIGNAL(resultsReady(QHostInfo)), receiver, member, Qt::QueuedConnection);
217
                result.emitResultsReady(info);
218
                return id;
219
            }
220
        }
221
222
        // cache is not enabled or it was not in the cache, do normal lookup
223
#ifndef QT_NO_BEARERMANAGEMENT
224
        QSharedPointer<QNetworkSession> networkSession;
225
        QVariant v(receiver->property("_q_networksession"));
226
        if (v.isValid())
227
            networkSession = qvariant_cast< QSharedPointer<QNetworkSession> >(v);
228
#endif
229
230
        QSymbianHostResolver *symbianResolver = 0;
231
        QT_TRAP_THROWING(symbianResolver = new QSymbianHostResolver(name, id, networkSession));
232
        QObject::connect(&symbianResolver->resultEmitter, SIGNAL(resultsReady(QHostInfo)), receiver, member, Qt::QueuedConnection);
233
        manager->scheduleLookup(symbianResolver);
234
    }
235
#endif
236
237
    return id;
238
}
239
240
/*!
241
    Aborts the host lookup with the ID \a id, as returned by lookupHost().
242
243
    \sa lookupHost(), lookupId()
244
*/
245
void QHostInfo::abortHostLookup(int id)
246
{
247
    theHostInfoLookupManager()->abortLookup(id);
248
}
249
250
/*!
251
    Looks up the IP address(es) for the given host \a name. The
252
    function blocks during the lookup which means that execution of
253
    the program is suspended until the results of the lookup are
254
    ready. Returns the result of the lookup in a QHostInfo object.
255
256
    If you pass a literal IP address to \a name instead of a host name,
257
    QHostInfo will search for the domain name for the IP (i.e., QHostInfo will
258
    perform a \e reverse lookup). On success, the returned QHostInfo will
259
    contain both the resolved domain name and IP addresses for the host name.
260
261
    \sa lookupHost()
262
*/
263
QHostInfo QHostInfo::fromName(const QString &name)
264
{
265
#if defined QHOSTINFO_DEBUG
266
    qDebug("QHostInfo::fromName(\"%s\")",name.toLatin1().constData());
267
#endif
268
269
    QHostInfo hostInfo = QHostInfoAgent::fromName(name);
270
    QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager();
271
    manager->cache.put(name, hostInfo);
272
    return hostInfo;
273
}
274
275
#ifndef QT_NO_BEARERMANAGEMENT
276
QHostInfo QHostInfoPrivate::fromName(const QString &name, QSharedPointer<QNetworkSession> session)
277
{
278
#if defined QHOSTINFO_DEBUG
279
    qDebug("QHostInfoPrivate::fromName(\"%s\") with session %p",name.toLatin1().constData(), session.data());
280
#endif
281
282
    QHostInfo hostInfo = QHostInfoAgent::fromName(name, session);
283
    QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager();
284
    manager->cache.put(name, hostInfo);
285
    return hostInfo;
286
}
287
#endif
288
289
#ifndef Q_OS_SYMBIAN
290
#ifndef QT_NO_BEARERMANAGEMENT
291
// This function has a special implementation for symbian right now in qhostinfo_symbian.cpp but not on other OS.
292
QHostInfo QHostInfoAgent::fromName(const QString &hostName, QSharedPointer<QNetworkSession>)
293
{
294
    return QHostInfoAgent::fromName(hostName);
295
}
296
#endif
297
#endif
298
299
300
/*!
301
    \enum QHostInfo::HostInfoError
302
303
    This enum describes the various errors that can occur when trying
304
    to resolve a host name.
305
306
    \value NoError The lookup was successful.
307
    \value HostNotFound No IP addresses were found for the host.
308
    \value UnknownError An unknown error occurred.
309
310
    \sa error(), setError()
311
*/
312
313
/*!
314
    Constructs an empty host info object with lookup ID \a id.
315
316
    \sa lookupId()
317
*/
318
QHostInfo::QHostInfo(int id)
319
    : d(new QHostInfoPrivate)
320
{
321
    d->lookupId = id;
322
}
323
324
/*!
325
    Constructs a copy of \a other.
326
*/
327
QHostInfo::QHostInfo(const QHostInfo &other)
328
    : d(new QHostInfoPrivate(*other.d.data()))
329
{
330
}
331
332
/*!
333
    Assigns the data of the \a other object to this host info object,
334
    and returns a reference to it.
335
*/
336
QHostInfo &QHostInfo::operator=(const QHostInfo &other)
337
{
338
    *d.data() = *other.d.data();
339
    return *this;
340
}
341
342
/*!
343
    Destroys the host info object.
344
*/
345
QHostInfo::~QHostInfo()
346
{
347
}
348
349
/*!
350
    Returns the list of IP addresses associated with hostName(). This
351
    list may be empty.
352
353
    Example:
354
355
    \snippet doc/src/snippets/code/src_network_kernel_qhostinfo.cpp 5
356
357
    \sa hostName(), error()
358
*/
359
QList<QHostAddress> QHostInfo::addresses() const
360
{
361
    return d->addrs;
362
}
363
364
/*!
365
    Sets the list of addresses in this QHostInfo to \a addresses.
366
367
    \sa addresses()
368
*/
369
void QHostInfo::setAddresses(const QList<QHostAddress> &addresses)
370
{
371
    d->addrs = addresses;
372
}
373
374
/*!
375
    Returns the name of the host whose IP addresses were looked up.
376
377
    \sa localHostName()
378
*/
379
QString QHostInfo::hostName() const
380
{
381
    return d->hostName;
382
}
383
384
/*!
385
    Sets the host name of this QHostInfo to \a hostName.
386
387
    \sa hostName()
388
*/
389
void QHostInfo::setHostName(const QString &hostName)
390
{
391
    d->hostName = hostName;
392
}
393
394
/*!
395
    Returns the type of error that occurred if the host name lookup
396
    failed; otherwise returns NoError.
397
398
    \sa setError(), errorString()
399
*/
400
QHostInfo::HostInfoError QHostInfo::error() const
401
{
402
    return d->err;
403
}
404
405
/*!
406
    Sets the error type of this QHostInfo to \a error.
407
408
    \sa error(), errorString()
409
*/
410
void QHostInfo::setError(HostInfoError error)
411
{
412
    d->err = error;
413
}
414
415
/*!
416
    Returns the ID of this lookup.
417
418
    \sa setLookupId(), abortHostLookup(), hostName()
419
*/
420
int QHostInfo::lookupId() const
421
{
422
    return d->lookupId;
423
}
424
425
/*!
426
    Sets the ID of this lookup to \a id.
427
428
    \sa lookupId(), lookupHost()
429
*/
430
void QHostInfo::setLookupId(int id)
431
{
432
    d->lookupId = id;
433
}
434
435
/*!
436
    If the lookup failed, this function returns a human readable
437
    description of the error; otherwise "Unknown error" is returned.
438
439
    \sa setErrorString(), error()
440
*/
441
QString QHostInfo::errorString() const
442
{
443
    return d->errorStr;
444
}
445
446
/*!
447
    Sets the human readable description of the error that occurred to \a str
448
    if the lookup failed.
449
450
    \sa errorString(), setError()
451
*/
452
void QHostInfo::setErrorString(const QString &str)
453
{
454
    d->errorStr = str;
455
}
456
457
/*!
458
    \fn QString QHostInfo::localHostName()
459
460
    Returns the host name of this machine.
461
462
    \sa hostName()
463
*/
464
465
/*!
466
    \fn QString QHostInfo::localDomainName()
467
468
    Returns the DNS domain of this machine.
469
470
    Note: DNS domains are not related to domain names found in
471
    Windows networks.
472
473
    \sa hostName()
474
*/
475
476
#ifndef Q_OS_SYMBIAN
477
QHostInfoRunnable::QHostInfoRunnable(QString hn, int i) : toBeLookedUp(hn), id(i)
478
{
479
    setAutoDelete(true);
480
}
481
482
// the QHostInfoLookupManager will at some point call this via a QThreadPool
483
void QHostInfoRunnable::run()
484
{
485
    QHostInfoLookupManager *manager = theHostInfoLookupManager();
486
    // check aborted
487
    if (manager->wasAborted(id)) {
488
        manager->lookupFinished(this);
489
        return;
490
    }
491
492
    QHostInfo hostInfo;
493
494
    // QHostInfo::lookupHost already checks the cache. However we need to check
495
    // it here too because it might have been cache saved by another QHostInfoRunnable
496
    // in the meanwhile while this QHostInfoRunnable was scheduled but not running
497
    if (manager->cache.isEnabled()) {
498
        // check the cache first
499
        bool valid = false;
500
        hostInfo = manager->cache.get(toBeLookedUp, &valid);
501
        if (!valid) {
502
            // not in cache, we need to do the lookup and store the result in the cache
503
            hostInfo = QHostInfoAgent::fromName(toBeLookedUp);
504
            manager->cache.put(toBeLookedUp, hostInfo);
505
        }
506
    } else {
507
        // cache is not enabled, just do the lookup and continue
508
        hostInfo = QHostInfoAgent::fromName(toBeLookedUp);
509
    }
510
511
    // check aborted again
512
    if (manager->wasAborted(id)) {
513
        manager->lookupFinished(this);
514
        return;
515
    }
516
517
    // signal emission
518
    hostInfo.setLookupId(id);
519
    resultEmitter.emitResultsReady(hostInfo);
520
521
    // now also iterate through the postponed ones
522
    {
523
        QMutexLocker locker(&manager->mutex);
524
        QMutableListIterator<QHostInfoRunnable*> iterator(manager->postponedLookups);
525
        while (iterator.hasNext()) {
526
            QHostInfoRunnable* postponed = iterator.next();
527
            if (toBeLookedUp == postponed->toBeLookedUp) {
528
                // we can now emit
529
                iterator.remove();
530
                hostInfo.setLookupId(postponed->id);
531
                postponed->resultEmitter.emitResultsReady(hostInfo);
532
                delete postponed;
533
            }
534
        }
535
    }
536
537
    manager->lookupFinished(this);
538
539
    // thread goes back to QThreadPool
540
}
541
542
QHostInfoLookupManager::QHostInfoLookupManager() : mutex(QMutex::Recursive), wasDeleted(false)
543
{
544
    moveToThread(QCoreApplicationPrivate::mainThread());
545
    connect(QCoreApplication::instance(), SIGNAL(destroyed()), SLOT(waitForThreadPoolDone()), Qt::DirectConnection);
546
    threadPool.setMaxThreadCount(5); // do 5 DNS lookups in parallel
547
}
548
549
QHostInfoLookupManager::~QHostInfoLookupManager()
550
{
551
    wasDeleted = true;
552
553
    // don't qDeleteAll currentLookups, the QThreadPool has ownership
554
    clear();
555
}
556
557
void QHostInfoLookupManager::clear()
558
{
559
    {
560
        QMutexLocker locker(&mutex);
561
        qDeleteAll(postponedLookups);
562
        qDeleteAll(scheduledLookups);
563
        qDeleteAll(finishedLookups);
564
        postponedLookups.clear();
565
        scheduledLookups.clear();
566
        finishedLookups.clear();
567
    }
568
569
    threadPool.waitForDone();
570
    cache.clear();
571
}
572
573
void QHostInfoLookupManager::work()
574
{
575
    if (wasDeleted)
576
        return;
577
578
    // goals of this function:
579
    //  - launch new lookups via the thread pool
580
    //  - make sure only one lookup per host/IP is in progress
581
582
    QMutexLocker locker(&mutex);
583
584
    if (!finishedLookups.isEmpty()) {
585
        // remove ID from aborted if it is in there
586
        for (int i = 0; i < finishedLookups.length(); i++) {
587
           abortedLookups.removeAll(finishedLookups.at(i)->id);
588
        }
589
590
        finishedLookups.clear();
591
    }
592
593
    if (!postponedLookups.isEmpty()) {
594
        // try to start the postponed ones
595
596
        QMutableListIterator<QHostInfoRunnable*> iterator(postponedLookups);
597
        while (iterator.hasNext()) {
598
            QHostInfoRunnable* postponed = iterator.next();
599
600
            // check if none of the postponed hostnames is currently running
601
            bool alreadyRunning = false;
602
            for (int i = 0; i < currentLookups.length(); i++) {
603
                if (currentLookups.at(i)->toBeLookedUp == postponed->toBeLookedUp) {
604
                    alreadyRunning = true;
605
                    break;
606
                }
607
            }
608
            if (!alreadyRunning) {
609
                iterator.remove();
610
                scheduledLookups.prepend(postponed); // prepend! we want to finish it ASAP
611
            }
612
        }
613
    }
614
615
    if (!scheduledLookups.isEmpty()) {
616
        // try to start the new ones
617
        QMutableListIterator<QHostInfoRunnable*> iterator(scheduledLookups);
618
        while (iterator.hasNext()) {
619
            QHostInfoRunnable *scheduled = iterator.next();
620
621
            // check if a lookup for this host is already running, then postpone
622
            for (int i = 0; i < currentLookups.size(); i++) {
623
                if (currentLookups.at(i)->toBeLookedUp == scheduled->toBeLookedUp) {
624
                    iterator.remove();
625
                    postponedLookups.append(scheduled);
626
                    scheduled = 0;
627
                    break;
628
                }
629
            }
630
631
            if (scheduled && currentLookups.size() < threadPool.maxThreadCount()) {
632
                // runnable now running in new thread, track this in currentLookups
633
                threadPool.start(scheduled);
634
                iterator.remove();
635
                currentLookups.append(scheduled);
636
            } else {
637
                // was postponed, continue iterating
638
                continue;
639
            }
640
        };
641
    }
642
}
643
644
// called by QHostInfo
645
void QHostInfoLookupManager::scheduleLookup(QHostInfoRunnable *r)
646
{
647
    if (wasDeleted)
648
        return;
649
650
    QMutexLocker locker(&this->mutex);
651
    scheduledLookups.enqueue(r);
652
    work();
653
}
654
655
// called by QHostInfo
656
void QHostInfoLookupManager::abortLookup(int id)
657
{
658
    if (wasDeleted)
659
        return;
660
661
    QMutexLocker locker(&this->mutex);
662
663
    // is postponed? delete and return
664
    for (int i = 0; i < postponedLookups.length(); i++) {
665
        if (postponedLookups.at(i)->id == id) {
666
            delete postponedLookups.takeAt(i);
667
            return;
668
        }
669
    }
670
671
    // is scheduled? delete and return
672
    for (int i = 0; i < scheduledLookups.length(); i++) {
673
        if (scheduledLookups.at(i)->id == id) {
674
            delete scheduledLookups.takeAt(i);
675
            return;
676
        }
677
    }
678
679
    if (!abortedLookups.contains(id))
680
        abortedLookups.append(id);
681
}
682
683
// called from QHostInfoRunnable
684
bool QHostInfoLookupManager::wasAborted(int id)
685
{
686
    if (wasDeleted)
687
        return true;
688
689
    QMutexLocker locker(&this->mutex);
690
    return abortedLookups.contains(id);
691
}
692
693
// called from QHostInfoRunnable
694
void QHostInfoLookupManager::lookupFinished(QHostInfoRunnable *r)
695
{
696
    if (wasDeleted)
697
        return;
698
699
    QMutexLocker locker(&this->mutex);
700
    currentLookups.removeOne(r);
701
    finishedLookups.append(r);
702
    work();
703
}
704
#endif
705
706
// This function returns immediately when we had a result in the cache, else it will later emit a signal
707
QHostInfo qt_qhostinfo_lookup(const QString &name, QObject *receiver, const char *member, bool *valid, int *id)
708
{
709
    *valid = false;
710
    *id = -1;
711
712
    // check cache
713
    QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager();
714
    if (manager && manager->cache.isEnabled()) {
715
        QHostInfo info = manager->cache.get(name, valid);
716
        if (*valid) {
717
            return info;
718
        }
719
    }
720
721
    // was not in cache, trigger lookup
722
    *id = QHostInfo::lookupHost(name, receiver, member);
723
724
    // return empty response, valid==false
725
    return QHostInfo();
726
}
727
728
void qt_qhostinfo_clear_cache()
729
{
730
    QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager();
731
    if (manager) {
732
        manager->clear();
733
    }
734
}
735
736
void Q_AUTOTEST_EXPORT qt_qhostinfo_enable_cache(bool e)
737
{
738
    QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager();
739
    if (manager) {
740
        manager->cache.setEnabled(e);
741
    }
742
}
743
744
// cache for 60 seconds
745
// cache 64 items
746
QHostInfoCache::QHostInfoCache() : max_age(60), enabled(true), cache(64)
747
{
748
#ifdef QT_QHOSTINFO_CACHE_DISABLED_BY_DEFAULT
749
    enabled = false;
750
#endif
751
}
752
753
bool QHostInfoCache::isEnabled()
754
{
755
    return enabled;
756
}
757
758
// this function is currently only used for the auto tests
759
// and not usable by public API
760
void QHostInfoCache::setEnabled(bool e)
761
{
762
    enabled = e;
763
}
764
765
766
QHostInfo QHostInfoCache::get(const QString &name, bool *valid)
767
{
768
    QMutexLocker locker(&this->mutex);
769
770
    *valid = false;
771
    if (cache.contains(name)) {
772
        QHostInfoCacheElement *element = cache.object(name);
773
        if (element->age.elapsed() < max_age*1000)
774
            *valid = true;
775
        return element->info;
776
777
        // FIXME idea:
778
        // if too old but not expired, trigger a new lookup
779
        // to freshen our cache
780
    }
781
782
    return QHostInfo();
783
}
784
785
void QHostInfoCache::put(const QString &name, const QHostInfo &info)
786
{
787
    // if the lookup failed, don't cache
788
    if (info.error() != QHostInfo::NoError)
789
        return;
790
791
    QHostInfoCacheElement* element = new QHostInfoCacheElement();
792
    element->info = info;
793
    element->age = QElapsedTimer();
794
    element->age.start();
795
796
    QMutexLocker locker(&this->mutex);
797
    cache.insert(name, element); // cache will take ownership
798
}
799
800
void QHostInfoCache::clear()
801
{
802
    QMutexLocker locker(&this->mutex);
803
    cache.clear();
804
}
805
806
QAbstractHostInfoLookupManager* QAbstractHostInfoLookupManager::globalInstance()
807
{
808
    return theHostInfoLookupManager();
809
}
810
811
QT_END_NAMESPACE