1 | /******************************************************************** |
2 | KWin - the KDE window manager |
3 | This file is part of the KDE project. |
4 | |
5 | Copyright (C) 2013 Martin Gräßlin <mgraesslin@kde.org> |
6 | |
7 | This program is free software; you can redistribute it and/or modify |
8 | it under the terms of the GNU General Public License as published by |
9 | the Free Software Foundation; either version 2 of the License, or |
10 | (at your option) any later version. |
11 | |
12 | This program is distributed in the hope that it will be useful, |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | GNU General Public License for more details. |
16 | |
17 | You should have received a copy of the GNU General Public License |
18 | along 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 | |
35 | namespace KWin { |
36 | |
37 | static 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 | |
51 | GetAddrInfo::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 | |
71 | GetAddrInfo::~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 | |
88 | void 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 | |
105 | void GetAddrInfo::slotResolved() |
106 | { |
107 | if (resolved(m_watcher)) { |
108 | m_resolved = true; |
109 | compare(); |
110 | } |
111 | } |
112 | |
113 | void GetAddrInfo::slotOwnAddressResolved() |
114 | { |
115 | if (resolved(m_ownAddressWatcher)) { |
116 | m_ownResolved = true; |
117 | compare(); |
118 | } |
119 | } |
120 | |
121 | bool 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 | |
135 | void 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 | |
163 | ClientMachine::ClientMachine(QObject *parent) |
164 | : QObject(parent) |
165 | , m_localhost(false) |
166 | , m_resolved(false) |
167 | , m_resolving(false) |
168 | { |
169 | } |
170 | |
171 | ClientMachine::~ClientMachine() |
172 | { |
173 | } |
174 | |
175 | void 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 | |
195 | void 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 | |
228 | void ClientMachine::setLocal() |
229 | { |
230 | m_localhost = true; |
231 | emit localhostChanged(); |
232 | } |
233 | |
234 | void ClientMachine::resolveFinished() |
235 | { |
236 | m_resolving = false; |
237 | } |
238 | |
239 | } // namespace |
240 | |