blob: 4a5f32da7e7412764d876ac53c9c4eb20b146cbc [file] [log] [blame]
Constantin Kaplinsky729598c2006-05-25 05:12:25 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
2 *
3 * This is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This software is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this software; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
16 * USA.
17 */
18
19#include <windows.h>
20#include <winsock2.h>
21#include <vncviewer/UserPasswdDialog.h>
22#include <vncviewer/CConn.h>
23#include <vncviewer/CConnThread.h>
24#include <vncviewer/resource.h>
25#include <rfb/encodings.h>
26#include <rfb/secTypes.h>
27#include <rfb/CSecurityNone.h>
28#include <rfb/CSecurityVncAuth.h>
29#include <rfb/CMsgWriter.h>
30#include <rfb/Configuration.h>
31#include <rfb/LogWriter.h>
32#include <rfb_win32/AboutDialog.h>
33
34using namespace rfb;
35using namespace rfb::win32;
36using namespace rdr;
37
38// - Statics & consts
39
40static LogWriter vlog("CConn");
41
42
43const int IDM_FULLSCREEN = ID_FULLSCREEN;
44const int IDM_SEND_MENU_KEY = ID_SEND_MENU_KEY;
45const int IDM_SEND_CAD = ID_SEND_CAD;
46const int IDM_SEND_CTLESC = ID_SEND_CTLESC;
47const int IDM_ABOUT = ID_ABOUT;
48const int IDM_OPTIONS = ID_OPTIONS;
49const int IDM_INFO = ID_INFO;
50const int IDM_NEWCONN = ID_NEW_CONNECTION;
51const int IDM_REQUEST_REFRESH = ID_REQUEST_REFRESH;
52const int IDM_CTRL_KEY = ID_CTRL_KEY;
53const int IDM_ALT_KEY = ID_ALT_KEY;
54const int IDM_FILE_TRANSFER = ID_FILE_TRANSFER;
55const int IDM_CONN_SAVE_AS = ID_CONN_SAVE_AS;
56
57
58static IntParameter debugDelay("DebugDelay","Milliseconds to display inverted "
59 "pixel data - a debugging feature", 0);
60
61
62//
63// -=- CConn implementation
64//
65
66RegKey CConn::userConfigKey;
67
68
69CConn::CConn()
70 : window(0), sock(0), sockEvent(CreateEvent(0, TRUE, FALSE, 0)), requestUpdate(false),
71 sameMachine(false), encodingChange(false), formatChange(false),
72 reverseConnection(false), lastUsedEncoding_(encodingRaw), isClosed_(false) {
73}
74
75CConn::~CConn() {
76 delete window;
77}
78
79bool CConn::initialise(network::Socket* s, bool reverse) {
80 // Set the server's name for MRU purposes
81 CharArray endpoint(s->getPeerEndpoint());
82 setServerName(endpoint.buf);
83 if (!options.host.buf)
84 options.setHost(endpoint.buf);
85
86 // Initialise the underlying CConnection
87 setStreams(&s->inStream(), &s->outStream());
88
89 // Enable processing of window messages while blocked on I/O
90 s->inStream().setBlockCallback(this);
91
92 // Initialise the viewer options
93 applyOptions(options);
94
95 // - Set which auth schemes we support, in order of preference
96 addSecType(secTypeVncAuth);
97 addSecType(secTypeNone);
98
99 // Start the RFB protocol
100 sock = s;
101 reverseConnection = reverse;
102 initialiseProtocol();
103
104 m_fileTransfer.initialize(&s->inStream(), &s->outStream());
105
106 return true;
107}
108
109
110void
111CConn::applyOptions(CConnOptions& opt) {
112 // - If any encoding-related settings have changed then we must
113 // notify the server of the new settings
114 encodingChange |= ((options.useLocalCursor != opt.useLocalCursor) ||
115 (options.useDesktopResize != opt.useDesktopResize) ||
116 (options.customCompressLevel != opt.customCompressLevel) ||
117 (options.compressLevel != opt.compressLevel) ||
118 (options.noJpeg != opt.noJpeg) ||
119 (options.qualityLevel != opt.qualityLevel) ||
120 (options.preferredEncoding != opt.preferredEncoding));
121
122 // - If the preferred pixel format has changed then notify the server
123 formatChange |= (options.fullColour != opt.fullColour);
124 if (!opt.fullColour)
125 formatChange |= (options.lowColourLevel != opt.lowColourLevel);
126
127 // - Save the new set of options
128 options = opt;
129
130 // - Set optional features in ConnParams
131 cp.supportsLocalCursor = options.useLocalCursor;
132 cp.supportsDesktopResize = options.useDesktopResize;
133 cp.customCompressLevel = options.customCompressLevel;
134 cp.compressLevel = options.compressLevel;
135 cp.noJpeg = options.noJpeg;
136 cp.qualityLevel = options.qualityLevel;
137
138 // - Configure connection sharing on/off
139 setShared(options.shared);
140
141 // - Whether to use protocol 3.3 for legacy compatibility
142 setProtocol3_3(options.protocol3_3);
143
144 // - Apply settings that affect the window, if it is visible
145 if (window) {
146 window->setMonitor(options.monitor.buf);
147 window->setFullscreen(options.fullScreen);
148 window->setEmulate3(options.emulate3);
149 window->setPointerEventInterval(options.pointerEventInterval);
150 window->setMenuKey(options.menuKey);
151 window->setDisableWinKeys(options.disableWinKeys);
152 window->setShowToolbar(options.showToolbar);
george82ffc14a62006-09-05 06:51:41 +0000153 if (options.autoScaling) {
154 window->setAutoScaling(true);
155 } else {
156 window->setAutoScaling(false);
157 window->setDesktopScale(options.scale);
158 }
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000159 if (!options.useLocalCursor)
160 window->setCursor(0, 0, Point(), 0, 0);
161 }
162}
163
164
165void
166CConn::displayChanged() {
167 // Display format has changed - recalculate the full-colour pixel format
168 calculateFullColourPF();
169}
170
171void
172CConn::paintCompleted() {
173 // A repaint message has just completed - request next update if necessary
174 requestNewUpdate();
175}
176
177bool
178CConn::sysCommand(WPARAM wParam, LPARAM lParam) {
179 // - If it's one of our (F8 Menu) messages
180 switch (wParam) {
181 case IDM_FULLSCREEN:
182 options.fullScreen = !window->isFullscreen();
183 window->setFullscreen(options.fullScreen);
184 return true;
185 case IDM_SHOW_TOOLBAR:
186 options.showToolbar = !window->isToolbarEnabled();
187 window->setShowToolbar(options.showToolbar);
188 return true;
189 case IDM_CTRL_KEY:
190 window->kbd.keyEvent(this, VK_CONTROL, 0, !window->kbd.keyPressed(VK_CONTROL));
191 return true;
192 case IDM_ALT_KEY:
193 window->kbd.keyEvent(this, VK_MENU, 0, !window->kbd.keyPressed(VK_MENU));
194 return true;
195 case IDM_SEND_MENU_KEY:
196 window->kbd.keyEvent(this, options.menuKey, 0, true);
197 window->kbd.keyEvent(this, options.menuKey, 0, false);
198 return true;
199 case IDM_SEND_CAD:
200 window->kbd.keyEvent(this, VK_CONTROL, 0, true);
201 window->kbd.keyEvent(this, VK_MENU, 0, true);
202 window->kbd.keyEvent(this, VK_DELETE, 0x1000000, true);
203 window->kbd.keyEvent(this, VK_DELETE, 0x1000000, false);
204 window->kbd.keyEvent(this, VK_MENU, 0, false);
205 window->kbd.keyEvent(this, VK_CONTROL, 0, false);
206 return true;
207 case IDM_SEND_CTLESC:
208 window->kbd.keyEvent(this, VK_CONTROL, 0, true);
209 window->kbd.keyEvent(this, VK_ESCAPE, 0, true);
210 window->kbd.keyEvent(this, VK_ESCAPE, 0, false);
211 window->kbd.keyEvent(this, VK_CONTROL, 0, false);
212 return true;
213 case IDM_REQUEST_REFRESH:
214 try {
215 writer()->writeFramebufferUpdateRequest(Rect(0,0,cp.width,cp.height), false);
216 requestUpdate = false;
217 } catch (rdr::Exception& e) {
218 close(e.str());
219 }
220 return true;
221 case IDM_NEWCONN:
222 {
223 Thread* newThread = new CConnThread;
224 }
225 return true;
226 case IDM_OPTIONS:
227 // Update the monitor device name in the CConnOptions instance
228 options.monitor.replaceBuf(window->getMonitor());
229 showOptionsDialog();
230 return true;
231 case IDM_INFO:
232 infoDialog.showDialog(this);
233 return true;
234 case IDM_ABOUT:
235 AboutDialog::instance.showDialog();
236 return true;
237 case IDM_FILE_TRANSFER:
238 m_fileTransfer.show(window->getHandle());
239 return true;
240 case IDM_CONN_SAVE_AS:
241 return true;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000242 };
243 return false;
244}
245
246
247void
248CConn::closeWindow() {
249 vlog.info("window closed");
250 close();
251}
252
253
254void
255CConn::refreshMenu(bool enableSysItems) {
256 HMENU menu = GetSystemMenu(window->getHandle(), FALSE);
257
258 if (!enableSysItems) {
259 // Gray out menu items that might cause a World Of Pain
260 EnableMenuItem(menu, SC_SIZE, MF_BYCOMMAND | MF_GRAYED);
261 EnableMenuItem(menu, SC_MOVE, MF_BYCOMMAND | MF_GRAYED);
262 EnableMenuItem(menu, SC_RESTORE, MF_BYCOMMAND | MF_ENABLED);
263 EnableMenuItem(menu, SC_MINIMIZE, MF_BYCOMMAND | MF_ENABLED);
264 EnableMenuItem(menu, SC_MAXIMIZE, MF_BYCOMMAND | MF_ENABLED);
265 }
266
267 // Update the modifier key menu items
268 UINT ctrlCheckFlags = window->kbd.keyPressed(VK_CONTROL) ? MF_CHECKED : MF_UNCHECKED;
269 UINT altCheckFlags = window->kbd.keyPressed(VK_MENU) ? MF_CHECKED : MF_UNCHECKED;
270 CheckMenuItem(menu, IDM_CTRL_KEY, MF_BYCOMMAND | ctrlCheckFlags);
271 CheckMenuItem(menu, IDM_ALT_KEY, MF_BYCOMMAND | altCheckFlags);
272
273 // Ensure that the Send <MenuKey> menu item has the correct text
274 if (options.menuKey) {
275 TCharArray menuKeyStr(options.menuKeyName());
276 TCharArray tmp(_tcslen(menuKeyStr.buf) + 6);
277 _stprintf(tmp.buf, _T("Send %s"), menuKeyStr.buf);
278 if (!ModifyMenu(menu, IDM_SEND_MENU_KEY, MF_BYCOMMAND | MF_STRING, IDM_SEND_MENU_KEY, tmp.buf))
279 InsertMenu(menu, IDM_SEND_CAD, MF_BYCOMMAND | MF_STRING, IDM_SEND_MENU_KEY, tmp.buf);
280 } else {
281 RemoveMenu(menu, IDM_SEND_MENU_KEY, MF_BYCOMMAND);
282 }
283
284 // Set the menu fullscreen option tick
285 CheckMenuItem(menu, IDM_FULLSCREEN, (window->isFullscreen() ? MF_CHECKED : 0) | MF_BYCOMMAND);
286
287 // Set the menu toolbar option tick
288 int toolbarFlags = window->isToolbarEnabled() ? MF_CHECKED : 0;
289 CheckMenuItem(menu, IDM_SHOW_TOOLBAR, MF_BYCOMMAND | toolbarFlags);
290
291 // In the full-screen mode, "Show toolbar" should be grayed.
292 toolbarFlags = window->isFullscreen() ? MF_GRAYED : MF_ENABLED;
293 EnableMenuItem(menu, IDM_SHOW_TOOLBAR, MF_BYCOMMAND | toolbarFlags);
294}
295
296
297void
298CConn::blockCallback() {
299 // - An InStream has blocked on I/O while processing an RFB message
300 // We re-enable socket event notifications, so we'll know when more
301 // data is available, then we sit and dispatch window events until
302 // the notification arrives.
303 if (!isClosed()) {
304 if (WSAEventSelect(sock->getFd(), sockEvent, FD_READ | FD_CLOSE) == SOCKET_ERROR)
305 throw rdr::SystemException("Unable to wait for sokcet data", WSAGetLastError());
306 }
307 while (true) {
308 // If we have closed then we can't block waiting for data
309 if (isClosed())
310 throw rdr::EndOfStream();
311
312 // Wait for socket data, or a message to process
313 DWORD result = MsgWaitForMultipleObjects(1, &sockEvent.h, FALSE, INFINITE, QS_ALLINPUT);
314 if (result == WAIT_OBJECT_0) {
315 // - Network event notification. Return control to I/O routine.
316 break;
317 } else if (result == WAIT_FAILED) {
318 // - The wait operation failed - raise an exception
319 throw rdr::SystemException("blockCallback wait error", GetLastError());
320 }
321
322 // - There should be a message in the message queue
323 MSG msg;
324 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
325 // IMPORTANT: We mustn't call TranslateMessage() here, because instead we
326 // call ToAscii() in CKeyboard::keyEvent(). ToAscii() stores dead key
327 // state from one call to the next, which would be messed up by calls to
328 // TranslateMessage() (actually it looks like TranslateMessage() calls
329 // ToAscii() internally).
330 DispatchMessage(&msg);
331 }
332 }
333
334 // Before we return control to the InStream, reset the network event
335 WSAEventSelect(sock->getFd(), sockEvent, 0);
336 ResetEvent(sockEvent);
337}
338
339
340void CConn::keyEvent(rdr::U32 key, bool down) {
341 if (!options.sendKeyEvents) return;
342 try {
343 writer()->keyEvent(key, down);
344 } catch (rdr::Exception& e) {
345 close(e.str());
346 }
347}
348void CConn::pointerEvent(const Point& pos, int buttonMask) {
349 if (!options.sendPtrEvents) return;
350 try {
351 writer()->pointerEvent(pos, buttonMask);
352 } catch (rdr::Exception& e) {
353 close(e.str());
354 }
355}
356void CConn::clientCutText(const char* str, int len) {
357 if (!options.clientCutText) return;
358 if (state() != RFBSTATE_NORMAL) return;
359 try {
360 writer()->clientCutText(str, len);
361 } catch (rdr::Exception& e) {
362 close(e.str());
363 }
364}
365
366
367CSecurity* CConn::getCSecurity(int secType)
368{
369 switch (secType) {
370 case secTypeNone:
371 return new CSecurityNone();
372 case secTypeVncAuth:
373 return new CSecurityVncAuth(this);
374 default:
375 throw Exception("Unsupported secType?");
376 }
377}
378
379
380void
381CConn::setColourMapEntries(int first, int count, U16* rgbs) {
382 vlog.debug("setColourMapEntries: first=%d, count=%d", first, count);
383 int i;
384 for (i=0;i<count;i++)
385 window->setColour(i+first, rgbs[i*3], rgbs[i*3+1], rgbs[i*3+2]);
386 // *** change to 0, 256?
387 window->refreshWindowPalette(first, count);
388}
389
390void
391CConn::bell() {
392 if (options.acceptBell)
393 MessageBeep(-1);
394}
395
396
397void
398CConn::setDesktopSize(int w, int h) {
399 vlog.debug("setDesktopSize %dx%d", w, h);
400
401 // Resize the window's buffer
402 if (window)
403 window->setSize(w, h);
404
405 // Tell the underlying CConnection
406 CConnection::setDesktopSize(w, h);
407}
408
409void
410CConn::setCursor(int w, int h, const Point& hotspot, void* data, void* mask) {
411 if (!options.useLocalCursor) return;
412
413 // Set the window to use the new cursor
414 window->setCursor(w, h, hotspot, data, mask);
415}
416
417
418void
419CConn::close(const char* reason) {
420 // If already closed then ignore this
421 if (isClosed())
422 return;
423
424 // Hide the window, if it exists
425 if (window)
426 ShowWindow(window->getHandle(), SW_HIDE);
427
428 // Save the reason & flag that we're closed & shutdown the socket
429 isClosed_ = true;
430 closeReason_.replaceBuf(strDup(reason));
431 sock->shutdown();
432}
433
434
435void
436CConn::showOptionsDialog() {
437 optionsDialog.showDialog(this);
438}
439
440
441void
442CConn::framebufferUpdateEnd() {
443 if (debugDelay != 0) {
444 vlog.debug("debug delay %d",(int)debugDelay);
445 UpdateWindow(window->getHandle());
446 Sleep(debugDelay);
447 std::list<rfb::Rect>::iterator i;
448 for (i = debugRects.begin(); i != debugRects.end(); i++) {
449 window->invertRect(*i);
450 }
451 debugRects.clear();
452 }
453 if (options.autoSelect)
454 autoSelectFormatAndEncoding();
455
456 // Always request the next update
457 requestUpdate = true;
458
459 // Check that at least part of the window has changed
460 if (!GetUpdateRect(window->getHandle(), 0, FALSE)) {
461 if (!(GetWindowLong(window->getHandle(), GWL_STYLE) & WS_MINIMIZE))
462 requestNewUpdate();
463 }
464
465 // Make sure the local cursor is shown
466 window->showCursor();
467}
468
469
470// Note: The method below is duplicated in vncviewer_unix/CConn.cxx!
471
472// autoSelectFormatAndEncoding() chooses the format and encoding appropriate
473// to the connection speed:
474//
475// Above 16Mbps (timing for at least a second), switch to hextile
476// Otherwise, switch to ZRLE
477//
478// Above 256Kbps, use full colour mode
479//
480void
481CConn::autoSelectFormatAndEncoding() {
482 int kbitsPerSecond = sock->inStream().kbitsPerSecond();
483 unsigned int newEncoding = options.preferredEncoding;
484
485 bool newFullColour = options.fullColour;
486 unsigned int timeWaited = sock->inStream().timeWaited();
487
488 // Select best encoding
489 if (kbitsPerSecond > 16000 && timeWaited >= 10000) {
490 newEncoding = encodingHextile;
491 } else {
492 newEncoding = encodingZRLE;
493 }
494
495 if (newEncoding != options.preferredEncoding) {
496 vlog.info("Throughput %d kbit/s - changing to %s encoding",
497 kbitsPerSecond, encodingName(newEncoding));
498 options.preferredEncoding = newEncoding;
499 encodingChange = true;
500 }
501
502 if (kbitsPerSecond == 0) {
503 return;
504 }
505
506 if (cp.beforeVersion(3, 8)) {
507 // Xvnc from TightVNC 1.2.9 sends out FramebufferUpdates with
508 // cursors "asynchronously". If this happens in the middle of a
509 // pixel format change, the server will encode the cursor with
510 // the old format, but the client will try to decode it
511 // according to the new format. This will lead to a
512 // crash. Therefore, we do not allow automatic format change for
513 // old servers.
514 return;
515 }
516
517 // Select best color level
518 newFullColour = (kbitsPerSecond > 256);
519 if (newFullColour != options.fullColour) {
520 vlog.info("Throughput %d kbit/s - full color is now %s",
521 kbitsPerSecond,
522 newFullColour ? "enabled" : "disabled");
523 options.fullColour = newFullColour;
524 formatChange = true;
525 }
526}
527
528void
529CConn::requestNewUpdate() {
530 if (!requestUpdate) return;
531
532 if (formatChange) {
533 // Select the required pixel format
534 if (options.fullColour) {
535 window->setPF(fullColourPF);
536 } else {
537 switch (options.lowColourLevel) {
538 case 0:
539 window->setPF(PixelFormat(8,3,0,1,1,1,1,2,1,0));
540 break;
541 case 1:
542 window->setPF(PixelFormat(8,6,0,1,3,3,3,4,2,0));
543 break;
544 case 2:
545 window->setPF(PixelFormat(8,8,0,0,0,0,0,0,0,0));
546 break;
547 }
548 }
549
550 // Print the current pixel format
551 char str[256];
552 window->getPF().print(str, 256);
553 vlog.info("Using pixel format %s",str);
554
555 // Save the connection pixel format and tell server to use it
556 cp.setPF(window->getPF());
557 writer()->writeSetPixelFormat(cp.pf());
558
559 // Correct the local window's palette
560 if (!window->getNativePF().trueColour)
561 window->refreshWindowPalette(0, 1 << cp.pf().depth);
562 }
563
564 if (encodingChange) {
565 vlog.info("Using %s encoding",encodingName(options.preferredEncoding));
566 writer()->writeSetEncodings(options.preferredEncoding, true);
567 }
568
569 writer()->writeFramebufferUpdateRequest(Rect(0, 0, cp.width, cp.height),
570 !formatChange);
571
572 encodingChange = formatChange = requestUpdate = false;
573}
574
575
576void
577CConn::calculateFullColourPF() {
578 // If the server is palette based then use palette locally
579 // Also, don't bother doing bgr222
580 if (!serverDefaultPF.trueColour || (serverDefaultPF.depth < 6)) {
581 fullColourPF = serverDefaultPF;
582 options.fullColour = true;
583 } else {
584 // If server is trueColour, use lowest depth PF
585 PixelFormat native = window->getNativePF();
586 if ((serverDefaultPF.bpp < native.bpp) ||
587 ((serverDefaultPF.bpp == native.bpp) &&
588 (serverDefaultPF.depth < native.depth)))
589 fullColourPF = serverDefaultPF;
590 else
591 fullColourPF = window->getNativePF();
592 }
593 formatChange = true;
594}
595
596
597void
598CConn::setName(const char* name) {
599 if (window)
600 window->setName(name);
601 CConnection::setName(name);
602}
603
604
605void CConn::serverInit() {
606 CConnection::serverInit();
607
608 // If using AutoSelect with old servers, start in FullColor
609 // mode. See comment in autoSelectFormatAndEncoding.
610 if (cp.beforeVersion(3, 8) && options.autoSelect) {
611 options.fullColour = true;
612 }
613
614 // Show the window
615 window = new DesktopWindow(this);
george82cb5a9a22006-09-05 15:22:35 +0000616 applyOptions(options);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000617 window->setName(cp.name());
618 window->setSize(cp.width, cp.height);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000619
620 // Save the server's current format
621 serverDefaultPF = cp.pf();
622
623 // Calculate the full-colour format to use
624 calculateFullColourPF();
625
626 // Request the initial update
627 vlog.info("requesting initial update");
628 formatChange = encodingChange = requestUpdate = true;
629 requestNewUpdate();
630
631 // Update the window menu
632 HMENU wndmenu = GetSystemMenu(window->getHandle(), FALSE);
633 int toolbarChecked = options.showToolbar ? MF_CHECKED : 0;
634
635 AppendMenu(wndmenu, MF_SEPARATOR, 0, 0);
636 AppendMenu(wndmenu, MF_STRING, IDM_FULLSCREEN, _T("&Full screen"));
637 AppendMenu(wndmenu, MF_STRING | toolbarChecked, IDM_SHOW_TOOLBAR,
638 _T("Show tool&bar"));
639 AppendMenu(wndmenu, MF_SEPARATOR, 0, 0);
640 AppendMenu(wndmenu, MF_STRING, IDM_CTRL_KEY, _T("Ctr&l"));
641 AppendMenu(wndmenu, MF_STRING, IDM_ALT_KEY, _T("Al&t"));
642 AppendMenu(wndmenu, MF_STRING, IDM_SEND_CAD, _T("Send Ctrl-Alt-&Del"));
643 AppendMenu(wndmenu, MF_STRING, IDM_SEND_CTLESC, _T("Send Ctrl-&Esc"));
644 AppendMenu(wndmenu, MF_STRING, IDM_REQUEST_REFRESH, _T("Refres&h Screen"));
645 AppendMenu(wndmenu, MF_SEPARATOR, 0, 0);
646 AppendMenu(wndmenu, MF_STRING, IDM_NEWCONN, _T("Ne&w Connection..."));
647 AppendMenu(wndmenu, MF_STRING, IDM_OPTIONS, _T("&Options..."));
648 AppendMenu(wndmenu, MF_STRING, IDM_INFO, _T("Connection &Info..."));
649 AppendMenu(wndmenu, MF_STRING, IDM_ABOUT, _T("&About..."));
650}
651
652void
653CConn::serverCutText(const char* str, int len) {
654 if (!options.serverCutText) return;
655 window->serverCutText(str, len);
656}
657
658
659void CConn::beginRect(const Rect& r, unsigned int encoding) {
660 sock->inStream().startTiming();
661}
662
663void CConn::endRect(const Rect& r, unsigned int encoding) {
664 sock->inStream().stopTiming();
665 lastUsedEncoding_ = encoding;
666 if (debugDelay != 0) {
667 window->invertRect(r);
668 debugRects.push_back(r);
669 }
670}
671
672void CConn::fillRect(const Rect& r, Pixel pix) {
673 window->fillRect(r, pix);
674}
675void CConn::imageRect(const Rect& r, void* pixels) {
676 window->imageRect(r, pixels);
677}
678void CConn::copyRect(const Rect& r, int srcX, int srcY) {
679 window->copyRect(r, srcX, srcY);
680}
681
682void CConn::getUserPasswd(char** user, char** password) {
683 if (!user && options.passwordFile.buf[0]) {
684 FILE* fp = fopen(options.passwordFile.buf, "rb");
685 if (fp) {
686 char data[256];
687 int datalen = fread(data, 1, 256, fp);
688 fclose(fp);
689 if (datalen == 8) {
690 ObfuscatedPasswd obfPwd;
691 obfPwd.buf = data;
692 obfPwd.length = datalen;
693 PlainPasswd passwd(obfPwd);
694 *password = strDup(passwd.buf);
695 memset(data, 0, strlen(data));
696 }
697 }
698 }
699 if (user && options.userName.buf)
700 *user = strDup(options.userName.buf);
701 if (password && options.password.buf)
702 *password = strDup(options.password.buf);
703 if ((user && !*user) || (password && !*password)) {
704 // Missing username or password - prompt the user
705 UserPasswdDialog userPasswdDialog;
706 userPasswdDialog.setCSecurity(getCurrentCSecurity());
707 userPasswdDialog.getUserPasswd(user, password);
708 }
709 if (user) options.setUserName(*user);
710 if (password) options.setPassword(*password);
711}
712
713bool CConn::processFTMsg(int type) {
714 return m_fileTransfer.processFTMsg(type);
715}