1/********************************************************************
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5Copyright (C) 2013 Martin Gräßlin <mgraesslin@kde.org>
6
7This program is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 2 of the License, or
10(at your option) any later version.
11
12This program is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with this program. If not, see <http://www.gnu.org/licenses/>.
19*********************************************************************/
20// own
21#include "client_machine.h"
22// KWin
23#include "utils.h"
24// KDE
25#include <KDE/KDebug>
26// Qt
27#include <QtConcurrentRun>
28#include <QFutureWatcher>
29// system
30#include <unistd.h>
31#include <sys/types.h>
32#include <sys/socket.h>
33#include <netdb.h>
34
35namespace KWin {
36
37static QByteArray getHostName()
38{
39#ifdef HOST_NAME_MAX
40 char hostnamebuf[HOST_NAME_MAX];
41#else
42 char hostnamebuf[256];
43#endif
44 if (gethostname(hostnamebuf, sizeof hostnamebuf) >= 0) {
45 hostnamebuf[sizeof(hostnamebuf)-1] = 0;
46 return QByteArray(hostnamebuf);
47 }
48 return QByteArray();
49}
50
51GetAddrInfo::GetAddrInfo(const QByteArray &hostName, QObject *parent)
52 : QObject(parent)
53 , m_resolving(false)
54 , m_resolved(false)
55 , m_ownResolved(false)
56 , m_hostName(hostName)
57 , m_addressHints(new addrinfo)
58 , m_address(NULL)
59 , m_ownAddress(NULL)
60 , m_watcher(new QFutureWatcher<int>(this))
61 , m_ownAddressWatcher(new QFutureWatcher<int>(this))
62{
63 // watcher will be deleted together with the GetAddrInfo once the future
64 // got canceled or finished
65 connect(m_watcher, SIGNAL(canceled()), SLOT(deleteLater()));
66 connect(m_watcher, SIGNAL(finished()), SLOT(slotResolved()));
67 connect(m_ownAddressWatcher, SIGNAL(canceled()), SLOT(deleteLater()));
68 connect(m_ownAddressWatcher, SIGNAL(finished()), SLOT(slotOwnAddressResolved()));
69}
70
71GetAddrInfo::~GetAddrInfo()
72{
73 if (m_watcher && m_watcher->isRunning()) {
74 m_watcher->cancel();
75 }
76 if (m_ownAddressWatcher && m_ownAddressWatcher->isRunning()) {
77 m_ownAddressWatcher->cancel();
78 }
79 if (m_address) {
80 freeaddrinfo(m_address);
81 }
82 if (m_ownAddress) {
83 freeaddrinfo(m_ownAddress);
84 }
85 delete m_addressHints;
86}
87
88void GetAddrInfo::resolve()
89{
90 if (m_resolving) {
91 return;
92 }
93 m_resolving = true;
94 memset(m_addressHints, 0, sizeof(*m_addressHints));
95 m_addressHints->ai_family = PF_UNSPEC;
96 m_addressHints->ai_socktype = SOCK_STREAM;
97 m_addressHints->ai_flags |= AI_CANONNAME;
98
99 // TODO: C++11 nullptr
100 const char* nullPtr = NULL;
101 m_watcher->setFuture(QtConcurrent::run(getaddrinfo, m_hostName, nullPtr, m_addressHints, &m_address));
102 m_ownAddressWatcher->setFuture(QtConcurrent::run(getaddrinfo, getHostName(), nullPtr, m_addressHints, &m_ownAddress));
103}
104
105void GetAddrInfo::slotResolved()
106{
107 if (resolved(m_watcher)) {
108 m_resolved = true;
109 compare();
110 }
111}
112
113void GetAddrInfo::slotOwnAddressResolved()
114{
115 if (resolved(m_ownAddressWatcher)) {
116 m_ownResolved = true;
117 compare();
118 }
119}
120
121bool GetAddrInfo::resolved(QFutureWatcher< int >* watcher)
122{
123 if (!watcher->isFinished()) {
124 return false;
125 }
126 if (watcher->result() != 0) {
127 kDebug(1212) << "getaddrinfo failed with error:" << gai_strerror(watcher->result());
128 // call failed;
129 deleteLater();
130 return false;
131 }
132 return true;
133}
134
135void GetAddrInfo::compare()
136{
137 if (!m_resolved || !m_ownResolved) {
138 return;
139 }
140 addrinfo *address = m_address;
141 while (address) {
142 if (address->ai_canonname && m_hostName == QByteArray(address->ai_canonname).toLower()) {
143 addrinfo *ownAddress = m_ownAddress;
144 bool localFound = false;
145 while (ownAddress) {
146 if (ownAddress->ai_canonname && QByteArray(ownAddress->ai_canonname).toLower() == m_hostName) {
147 localFound = true;
148 break;
149 }
150 ownAddress = ownAddress->ai_next;
151 }
152 if (localFound) {
153 emit local();
154 break;
155 }
156 }
157 address = address->ai_next;
158 }
159 deleteLater();
160}
161
162
163ClientMachine::ClientMachine(QObject *parent)
164 : QObject(parent)
165 , m_localhost(false)
166 , m_resolved(false)
167 , m_resolving(false)
168{
169}
170
171ClientMachine::~ClientMachine()
172{
173}
174
175void ClientMachine::resolve(xcb_window_t window, xcb_window_t clientLeader)
176{
177 if (m_resolved) {
178 return;
179 }
180 QByteArray name = getStringProperty(window, XCB_ATOM_WM_CLIENT_MACHINE);
181 if (name.isEmpty() && clientLeader && clientLeader != window) {
182 name = getStringProperty(clientLeader, XCB_ATOM_WM_CLIENT_MACHINE);
183 }
184 if (name.isEmpty()) {
185 name = localhost();
186 }
187 if (name == localhost()) {
188 setLocal();
189 }
190 m_hostName = name;
191 checkForLocalhost();
192 m_resolved = true;
193}
194
195void ClientMachine::checkForLocalhost()
196{
197 if (isLocal()) {
198 // nothing to do
199 return;
200 }
201 QByteArray host = getHostName();
202
203 if (!host.isEmpty()) {
204 host = host.toLower();
205 const QByteArray lowerHostName(m_hostName.toLower());
206 if (host == lowerHostName) {
207 setLocal();
208 return;
209 }
210 if (char *dot = strchr(host.data(), '.')) {
211 *dot = '\0';
212 if (host == lowerHostName) {
213 setLocal();
214 return;
215 }
216 } else {
217 m_resolving = true;
218 // check using information from get addr info
219 // GetAddrInfo gets automatically destroyed once it finished or not
220 GetAddrInfo *info = new GetAddrInfo(lowerHostName, this);
221 connect(info, SIGNAL(local()), SLOT(setLocal()));
222 connect(info, SIGNAL(destroyed(QObject*)), SLOT(resolveFinished()));
223 info->resolve();
224 }
225 }
226}
227
228void ClientMachine::setLocal()
229{
230 m_localhost = true;
231 emit localhostChanged();
232}
233
234void ClientMachine::resolveFinished()
235{
236 m_resolving = false;
237}
238
239} // namespace
240