1 /****************************************************************************
2 **
3 ** Copyright (C) 2007-2008 Urs Wolfer <uwolfer @ kde.org>
4 **
5 ** This file is part of KDE.
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; see the file COPYING. If not, write to
19 ** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 ** Boston, MA 02110-1301, USA.
21 **
22 ****************************************************************************/
23 
24 #include "vncclientthread.h"
25 
26 #include <QMutexLocker>
27 #include <QTimer>
28 
29 #include "Utils.h"
30 
31 static QString outputErrorMessageString; // FIXME test it (static?)
32 
newclient(rfbClient * cl)33 rfbBool VncClientThread::newclient(rfbClient *cl)
34 {
35     VncClientThread *t = (VncClientThread*)rfbClientGetClientData(cl, 0);
36     Q_ASSERT(t);
37 
38     const int width = cl->width, height = cl->height, depth = cl->format.bitsPerPixel;
39     const int size = width * height * (depth / 8);
40     if (t->frameBuffer)
41         delete [] t->frameBuffer; // do not leak if we get a new framebuffer size
42     t->frameBuffer = new uint8_t[size];
43     cl->frameBuffer = t->frameBuffer;
44     memset(cl->frameBuffer, '\0', size);
45     cl->format.bitsPerPixel = 32;
46     cl->format.redShift = 16;
47     cl->format.greenShift = 8;
48     cl->format.blueShift = 0;
49     cl->format.redMax = 0xff;
50     cl->format.greenMax = 0xff;
51     cl->format.blueMax = 0xff;
52 
53     switch (t->quality()) {
54     case RemoteView::High:
55         cl->appData.useBGR233 = 0;
56         cl->appData.encodingsString = "copyrect hextile raw";
57         cl->appData.compressLevel = 0;
58         cl->appData.qualityLevel = 9;
59         break;
60     case RemoteView::Medium:
61         cl->appData.useBGR233 = 0;
62         cl->appData.encodingsString = "tight zrle ultra copyrect hextile zlib corre rre raw";
63         cl->appData.compressLevel = 5;
64         cl->appData.qualityLevel = 7;
65         break;
66     case RemoteView::Low:
67     case RemoteView::Unknown:
68     default:
69         cl->appData.useBGR233 = 1;
70         cl->appData.encodingsString = "tight zrle ultra copyrect hextile zlib corre rre raw";
71         cl->appData.compressLevel = 9;
72         cl->appData.qualityLevel = 1;
73     }
74 
75     SetFormatAndEncodings(cl);
76 
77     return true;
78 }
79 
updatefb(rfbClient * cl,int x,int y,int w,int h)80 void VncClientThread::updatefb(rfbClient* cl, int x, int y, int w, int h)
81 {
82 //     kDebug(5011) << "updated client: x: " << x << ", y: " << y << ", w: " << w << ", h: " << h;
83 
84     const int width = cl->width, height = cl->height;
85 
86     const QImage img(cl->frameBuffer, width, height, QImage::Format_RGB32);
87 
88     if (img.isNull()) {
89         kDebug(5011) << "image not loaded";
90     }
91 
92     VncClientThread *t = (VncClientThread*)rfbClientGetClientData(cl, 0);
93     Q_ASSERT(t);
94 
95     t->setImage(img);
96 
97     t->emitUpdated(x, y, w, h);
98 }
99 
cuttext(rfbClient * cl,const char * text,int textlen)100 void VncClientThread::cuttext(rfbClient* cl, const char *text, int textlen)
101 {
102     const QString cutText = QString::fromUtf8(text, textlen);
103     kDebug(5011) << cutText;
104 
105     if (!cutText.isEmpty()) {
106         VncClientThread *t = (VncClientThread*)rfbClientGetClientData(cl, 0);
107         Q_ASSERT(t);
108 
109         t->emitGotCut(cutText);
110     }
111 }
112 
passwdHandler(rfbClient * cl)113 char *VncClientThread::passwdHandler(rfbClient *cl)
114 {
115     kDebug(5011) << "password request" << kBacktrace();
116 
117     VncClientThread *t = (VncClientThread*)rfbClientGetClientData(cl, 0);
118     Q_ASSERT(t);
119 
120     t->passwordRequest();
121     t->m_passwordError = true;
122 
123     return strdup(t->password().toLocal8Bit());
124 }
125 
outputHandler(const char * format,...)126 void VncClientThread::outputHandler(const char *format, ...)
127 {
128     va_list args;
129     va_start(args, format);
130 
131     QString message;
132     message.vsprintf(format, args);
133 
134     va_end(args);
135 
136     message = message.trimmed();
137 
138     kDebug(5011) << message;
139 
140     if ((message.contains("Couldn't convert ")) ||
141             (message.contains("Unable to connect to VNC server")))
142         outputErrorMessageString = i18n("Server not found.");
143 
144     if ((message.contains("VNC connection failed: Authentication failed, too many tries")) ||
145             (message.contains("VNC connection failed: Too many authentication failures")))
146         outputErrorMessageString = i18n("VNC authentication failed because of too many authentication tries.");
147 
148     if (message.contains("VNC connection failed: Authentication failed"))
149         outputErrorMessageString = i18n("VNC authentication failed.");
150 
151     if (message.contains("VNC server closed connection"))
152         outputErrorMessageString = i18n("VNC server closed connection.");
153 
154     // internal messages, not displayed to user
155     if (message.contains("VNC server supports protocol version 3.889")) // see http://bugs.kde.org/162640
156         outputErrorMessageString = "INTERNAL:APPLE_VNC_COMPATIBILTY";
157 }
158 
VncClientThread(QObject * parent)159 VncClientThread::VncClientThread(QObject *parent)
160         : QThread(parent)
161         , frameBuffer(0)
162 {
163     QMutexLocker locker(&mutex);
164     m_stopped = false;
165 
166     QTimer *outputErrorMessagesCheckTimer = new QTimer(this);
167     outputErrorMessagesCheckTimer->setInterval(500);
168     connect(outputErrorMessagesCheckTimer, SIGNAL(timeout()), this, SLOT(checkOutputErrorMessage()));
169     outputErrorMessagesCheckTimer->start();
170 }
171 
~VncClientThread()172 VncClientThread::~VncClientThread()
173 {
174     stop();
175 
176     const bool quitSuccess = wait(500);
177 
178     kDebug(5011) << "Quit VNC thread success:" << quitSuccess;
179 
180     delete [] frameBuffer;
181 }
182 
checkOutputErrorMessage()183 void VncClientThread::checkOutputErrorMessage()
184 {
185     if (!outputErrorMessageString.isEmpty()) {
186         kDebug(5011) << outputErrorMessageString;
187         QString errorMessage = outputErrorMessageString;
188         outputErrorMessageString.clear();
189         // show authentication failure error only after the 3rd unsuccessful try
190         if ((errorMessage != i18n("VNC authentication failed.")) || m_passwordError)
191             outputErrorMessage(errorMessage);
192     }
193 }
194 
setHost(const QString & host)195 void VncClientThread::setHost(const QString &host)
196 {
197     QMutexLocker locker(&mutex);
198     m_host = host;
199 }
200 
setPort(int port)201 void VncClientThread::setPort(int port)
202 {
203     QMutexLocker locker(&mutex);
204     m_port = port;
205 }
206 
setQuality(RemoteView::Quality quality)207 void VncClientThread::setQuality(RemoteView::Quality quality)
208 {
209     m_quality = quality;
210 }
211 
quality() const212 RemoteView::Quality VncClientThread::quality() const
213 {
214     return m_quality;
215 }
216 
setImage(const QImage & img)217 void VncClientThread::setImage(const QImage &img)
218 {
219     QMutexLocker locker(&mutex);
220     m_image = img;
221 }
222 
image(int x,int y,int w,int h)223 const QImage VncClientThread::image(int x, int y, int w, int h)
224 {
225     QMutexLocker locker(&mutex);
226 
227     if (w == 0) // full image requested
228         return m_image;
229     else
230         return m_image.copy(x, y, w, h);
231 }
232 
emitUpdated(int x,int y,int w,int h)233 void VncClientThread::emitUpdated(int x, int y, int w, int h)
234 {
235     emit imageUpdated(x, y, w, h);
236 }
237 
emitGotCut(const QString & text)238 void VncClientThread::emitGotCut(const QString &text)
239 {
240     emit gotCut(text);
241 }
242 
stop()243 void VncClientThread::stop()
244 {
245     QMutexLocker locker(&mutex);
246     m_stopped = true;
247 }
248 
run()249 void VncClientThread::run()
250 {
251     QMutexLocker locker(&mutex);
252 
253     while (!m_stopped) { // try to connect as long as the server allows
254 
255         m_passwordError = false;
256 
257         rfbClientLog = outputHandler;
258         rfbClientErr = outputHandler;
259         cl = rfbGetClient(8, 3, 4);
260         cl->MallocFrameBuffer = newclient;
261         cl->canHandleNewFBSize = true;
262         cl->GetPassword = passwdHandler;
263         cl->GotFrameBufferUpdate = updatefb;
264         cl->GotXCutText = cuttext;
265         rfbClientSetClientData(cl, 0, this);
266 
267         cl->serverHost = strdup(m_host.toUtf8().constData());
268 
269         if (m_port < 0 || !m_port) // port is invalid or empty...
270             m_port = 5900; // fallback: try an often used VNC port
271 
272         if (m_port >= 0 && m_port < 100) // the user most likely used the short form (e.g. :1)
273             m_port += 5900;
274         cl->serverPort = m_port;
275 
276         kDebug(5011) << "--------------------- trying init ---------------------";
277 
278         if (rfbInitClient(cl, 0, 0))
279             break;
280 
281         if (m_passwordError)
282             continue;
283 
284         return;
285     }
286 
287     locker.unlock();
288 
289     // Main VNC event loop
290     while (!m_stopped) {
291 
292         const int i = WaitForMessage(cl, 500);
293         if (i < 0)
294             break;
295         if (i)
296             if (!HandleRFBServerMessage(cl))
297                 break;
298 
299         locker.relock();
300 
301         while (!m_eventQueue.isEmpty()) {
302             ClientEvent* clientEvent = m_eventQueue.dequeue();
303             clientEvent->fire(cl);
304             delete clientEvent;
305         }
306 
307         locker.unlock();
308     }
309 
310     // Cleanup allocated resources
311     locker.relock();
312     rfbClientCleanup(cl);
313     m_stopped = true;
314 }
315 
~ClientEvent()316 ClientEvent::~ClientEvent()
317 {
318 }
319 
fire(rfbClient * cl)320 void PointerClientEvent::fire(rfbClient* cl)
321 {
322     SendPointerEvent(cl, m_x, m_y, m_buttonMask);
323 }
324 
fire(rfbClient * cl)325 void KeyClientEvent::fire(rfbClient* cl)
326 {
327     SendKeyEvent(cl, m_key, m_pressed);
328 }
329 
fire(rfbClient * cl)330 void ClientCutEvent::fire(rfbClient* cl)
331 {
332     SendClientCutText(cl, text.toUtf8().data(), text.size());
333 }
334 
mouseEvent(int x,int y,int buttonMask)335 void VncClientThread::mouseEvent(int x, int y, int buttonMask)
336 {
337     QMutexLocker lock(&mutex);
338     if (m_stopped)
339         return;
340 
341     m_eventQueue.enqueue(new PointerClientEvent(x, y, buttonMask));
342 }
343 
keyEvent(int key,bool pressed)344 void VncClientThread::keyEvent(int key, bool pressed)
345 {
346     QMutexLocker lock(&mutex);
347     if (m_stopped)
348         return;
349 
350     m_eventQueue.enqueue(new KeyClientEvent(key, pressed));
351 }
352 
clientCut(const QString & text)353 void VncClientThread::clientCut(const QString &text)
354 {
355     QMutexLocker lock(&mutex);
356     if (m_stopped)
357         return;
358 
359     m_eventQueue.enqueue(new ClientCutEvent(text));
360 }
361 
362 //#include "moc_vncclientthread.cpp"
363