1 /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
2 * Copyright 2011 Pierre Ossman <ossman@cendio.se> for Cendio AB
3 * Copyright (C) 2011 D. R. Commander. All Rights Reserved.
4 *
5 * This is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This software is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this software; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
18 * USA.
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <assert.h>
26 #include <string.h>
27 #include <stdio.h>
28 #include <ctype.h>
29 #include <stdlib.h>
30 #include <errno.h>
31 #include <signal.h>
32 #include <locale.h>
33 #include <fcntl.h>
34 #include <unistd.h>
35 #include <sys/stat.h>
36
37 #ifdef WIN32
38 #include <os/winerrno.h>
39 #include <direct.h>
40 #define mkdir(path, mode) _mkdir(path)
41 #endif
42
43 #ifdef __APPLE__
44 #include <Carbon/Carbon.h>
45 #endif
46
47 #if !defined(WIN32) && !defined(__APPLE__)
48 #include <X11/Xlib.h>
49 #include <X11/XKBlib.h>
50 #endif
51
52 #include <rfb/Logger_stdio.h>
53 #ifdef HAVE_GNUTLS
54 #include <rfb/CSecurityTLS.h>
55 #endif
56 #include <rfb/LogWriter.h>
57 #include <rfb/Timer.h>
58 #include <rfb/Exception.h>
59 #include <network/TcpSocket.h>
60 #include <os/os.h>
61
62 #include <FL/Fl.H>
63 #include <FL/Fl_Widget.H>
64 #include <FL/Fl_PNG_Image.H>
65 #include <FL/Fl_Sys_Menu_Bar.H>
66 #include <FL/fl_ask.H>
67 #include <FL/x.H>
68
69 #include "i18n.h"
70 #include "parameters.h"
71 #include "CConn.h"
72 #include "ServerDialog.h"
73 #include "UserDialog.h"
74 #include "touch.h"
75 #include "vncviewer.h"
76 #include "fltk_layout.h"
77
78 #ifdef WIN32
79 #include "resource.h"
80 #include "win32.h"
81 #endif
82
83 rfb::LogWriter vlog("main");
84
85 using namespace network;
86 using namespace rfb;
87 using namespace std;
88
89 char vncServerName[VNCSERVERNAMELEN] = { '\0' };
90
91 static const char *argv0 = NULL;
92
93 static bool inMainloop = false;
94 static bool exitMainloop = false;
95 static char *exitError = NULL;
96 static bool fatalError = false;
97
about_text()98 static const char *about_text()
99 {
100 static char buffer[1024];
101
102 // This is used in multiple places with potentially different
103 // encodings, so we need to make sure we get a fresh string every
104 // time.
105 snprintf(buffer, sizeof(buffer),
106 _("TigerVNC Viewer %d-bit v%s\n"
107 "Built on: %s\n"
108 "Copyright (C) 1999-%d TigerVNC Team and many others (see README.rst)\n"
109 "See https://www.tigervnc.org for information on TigerVNC."),
110 (int)sizeof(size_t)*8, PACKAGE_VERSION,
111 BUILD_TIMESTAMP, 2021);
112
113 return buffer;
114 }
115
116
abort_vncviewer(const char * error,...)117 void abort_vncviewer(const char *error, ...)
118 {
119 fatalError = true;
120
121 // Prioritise the first error we get as that is probably the most
122 // relevant one.
123 if (exitError == NULL) {
124 va_list ap;
125
126 va_start(ap, error);
127 exitError = (char*)malloc(1024);
128 vsnprintf(exitError, 1024, error, ap);
129 va_end(ap);
130 }
131
132 if (inMainloop)
133 exitMainloop = true;
134 else {
135 // We're early in the startup. Assume we can just exit().
136 if (alertOnFatalError && (exitError != NULL))
137 fl_alert("%s", exitError);
138 exit(EXIT_FAILURE);
139 }
140 }
141
abort_connection(const char * error,...)142 void abort_connection(const char *error, ...)
143 {
144 assert(inMainloop);
145
146 // Prioritise the first error we get as that is probably the most
147 // relevant one.
148 if (exitError == NULL) {
149 va_list ap;
150
151 va_start(ap, error);
152 exitError = (char*)malloc(1024);
153 vsnprintf(exitError, 1024, error, ap);
154 va_end(ap);
155 }
156
157 exitMainloop = true;
158 }
159
disconnect()160 void disconnect()
161 {
162 exitMainloop = true;
163 }
164
should_disconnect()165 bool should_disconnect()
166 {
167 return exitMainloop;
168 }
169
about_vncviewer()170 void about_vncviewer()
171 {
172 fl_message_title(_("About TigerVNC Viewer"));
173 fl_message("%s", about_text());
174 }
175
mainloop(const char * vncserver,network::Socket * sock)176 static void mainloop(const char* vncserver, network::Socket* sock)
177 {
178 while (true) {
179 CConn *cc;
180
181 exitMainloop = false;
182
183 cc = new CConn(vncServerName, sock);
184
185 while (!exitMainloop) {
186 int next_timer;
187
188 next_timer = Timer::checkTimeouts();
189 if (next_timer == 0)
190 next_timer = INT_MAX;
191
192 if (Fl::wait((double)next_timer / 1000.0) < 0.0) {
193 vlog.error(_("Internal FLTK error. Exiting."));
194 exit(-1);
195 }
196 }
197
198 delete cc;
199
200 if (fatalError) {
201 assert(exitError != NULL);
202 if (alertOnFatalError)
203 fl_alert("%s", exitError);
204 break;
205 }
206
207 if (exitError == NULL)
208 break;
209
210 if(reconnectOnError && (sock == NULL)) {
211 int ret;
212 ret = fl_choice(_("%s\n\n"
213 "Attempt to reconnect?"),
214 NULL, fl_yes, fl_no, exitError);
215 free(exitError);
216 exitError = NULL;
217 if (ret == 1)
218 continue;
219 else
220 break;
221 }
222
223 if (alertOnFatalError)
224 fl_alert("%s", exitError);
225
226 break;
227 }
228 }
229
230 #ifdef __APPLE__
about_callback(Fl_Widget * widget,void * data)231 static void about_callback(Fl_Widget *widget, void *data)
232 {
233 about_vncviewer();
234 }
235
new_connection_cb(Fl_Widget * widget,void * data)236 static void new_connection_cb(Fl_Widget *widget, void *data)
237 {
238 const char *argv[2];
239 pid_t pid;
240
241 pid = fork();
242 if (pid == -1) {
243 vlog.error(_("Error starting new TigerVNC Viewer: %s"), strerror(errno));
244 return;
245 }
246
247 if (pid != 0)
248 return;
249
250 argv[0] = argv0;
251 argv[1] = NULL;
252
253 execvp(argv[0], (char * const *)argv);
254
255 vlog.error(_("Error starting new TigerVNC Viewer: %s"), strerror(errno));
256 _exit(1);
257 }
258 #endif
259
CleanupSignalHandler(int sig)260 static void CleanupSignalHandler(int sig)
261 {
262 // CleanupSignalHandler allows C++ object cleanup to happen because it calls
263 // exit() rather than the default which is to abort.
264 vlog.info(_("Termination signal %d has been received. TigerVNC Viewer will now exit."), sig);
265 exit(1);
266 }
267
getlocaledir()268 static const char* getlocaledir()
269 {
270 #if defined(WIN32)
271 static char localebuf[PATH_MAX];
272 char *slash;
273
274 GetModuleFileName(NULL, localebuf, sizeof(localebuf));
275
276 slash = strrchr(localebuf, '\\');
277 if (slash == NULL)
278 return NULL;
279
280 *slash = '\0';
281
282 if ((strlen(localebuf) + strlen("\\locale")) >= sizeof(localebuf))
283 return NULL;
284
285 strcat(localebuf, "\\locale");
286
287 return localebuf;
288 #elif defined(__APPLE__)
289 CFBundleRef bundle;
290 CFURLRef localeurl;
291 CFStringRef localestr;
292 Boolean ret;
293
294 static char localebuf[PATH_MAX];
295
296 bundle = CFBundleGetMainBundle();
297 if (bundle == NULL)
298 return NULL;
299
300 localeurl = CFBundleCopyResourceURL(bundle, CFSTR("locale"),
301 NULL, NULL);
302 if (localeurl == NULL)
303 return NULL;
304
305 localestr = CFURLCopyFileSystemPath(localeurl, kCFURLPOSIXPathStyle);
306
307 CFRelease(localeurl);
308
309 ret = CFStringGetCString(localestr, localebuf, sizeof(localebuf),
310 kCFStringEncodingUTF8);
311 if (!ret)
312 return NULL;
313
314 return localebuf;
315 #else
316 return CMAKE_INSTALL_FULL_LOCALEDIR;
317 #endif
318 }
init_fltk()319 static void init_fltk()
320 {
321 // Basic text size (10pt @ 96 dpi => 13px)
322 FL_NORMAL_SIZE = 13;
323
324 // Select a FLTK scheme and background color that looks somewhat
325 // close to modern systems
326 Fl::scheme("gtk+");
327 Fl::background(220, 220, 220);
328
329 // macOS has a slightly brighter default background though
330 #ifdef __APPLE__
331 Fl::background(240, 240, 240);
332 #endif
333
334 // Proper Gnome Shell integration requires that we set a sensible
335 // WM_CLASS for the window.
336 Fl_Window::default_xclass("vncviewer");
337
338 // Set the default icon for all windows.
339 #ifdef WIN32
340 HICON lg, sm;
341
342 lg = (HICON)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON),
343 IMAGE_ICON, GetSystemMetrics(SM_CXICON),
344 GetSystemMetrics(SM_CYICON),
345 LR_DEFAULTCOLOR | LR_SHARED);
346 sm = (HICON)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON),
347 IMAGE_ICON, GetSystemMetrics(SM_CXSMICON),
348 GetSystemMetrics(SM_CYSMICON),
349 LR_DEFAULTCOLOR | LR_SHARED);
350
351 Fl_Window::default_icons(lg, sm);
352 #elif ! defined(__APPLE__)
353 const int icon_sizes[] = {48, 32, 24, 16};
354
355 Fl_PNG_Image *icons[4];
356 int count;
357
358 count = 0;
359
360 // FIXME: Follow icon theme specification
361 for (size_t i = 0;i < sizeof(icon_sizes)/sizeof(icon_sizes[0]);i++) {
362 char icon_path[PATH_MAX];
363 bool exists;
364
365 sprintf(icon_path, "%s/icons/hicolor/%dx%d/apps/tigervnc.png",
366 CMAKE_INSTALL_FULL_DATADIR, icon_sizes[i], icon_sizes[i]);
367
368 #ifndef WIN32
369 struct stat st;
370 if (stat(icon_path, &st) != 0)
371 #else
372 struct _stat st;
373 if (_stat(icon_path, &st) != 0)
374 return(false);
375 #endif
376 exists = false;
377 else
378 exists = true;
379
380 if (exists) {
381 icons[count] = new Fl_PNG_Image(icon_path);
382 if (icons[count]->w() == 0 ||
383 icons[count]->h() == 0 ||
384 icons[count]->d() != 4) {
385 delete icons[count];
386 continue;
387 }
388
389 count++;
390 }
391 }
392
393 Fl_Window::default_icons((const Fl_RGB_Image**)icons, count);
394
395 for (int i = 0;i < count;i++)
396 delete icons[i];
397 #endif
398
399 // This makes the "icon" in dialogs rounded, which fits better
400 // with the above schemes.
401 fl_message_icon()->box(FL_UP_BOX);
402
403 // Turn off the annoying behaviour where popups track the mouse.
404 fl_message_hotspot(false);
405
406 // Avoid empty titles for popups
407 fl_message_title_default(_("TigerVNC Viewer"));
408
409 #ifdef WIN32
410 // Most "normal" Windows apps use this font for UI elements.
411 Fl::set_font(FL_HELVETICA, "Tahoma");
412 #endif
413
414 // FLTK exposes these so that we can translate them.
415 fl_no = _("No");
416 fl_yes = _("Yes");
417 fl_ok = _("OK");
418 fl_cancel = _("Cancel");
419 fl_close = _("Close");
420
421 #ifdef __APPLE__
422 /* Needs trailing space */
423 static char fltk_about[16];
424 snprintf(fltk_about, sizeof(fltk_about), "%s ", _("About"));
425 Fl_Mac_App_Menu::about = fltk_about;
426 static char fltk_hide[16];
427 snprintf(fltk_hide, sizeof(fltk_hide), "%s ", _("Hide"));
428 Fl_Mac_App_Menu::hide = fltk_hide;
429 static char fltk_quit[16];
430 snprintf(fltk_quit, sizeof(fltk_quit), "%s ", _("Quit"));
431 Fl_Mac_App_Menu::quit = fltk_quit;
432
433 Fl_Mac_App_Menu::print = ""; // Don't want the print item
434 Fl_Mac_App_Menu::services = _("Services");
435 Fl_Mac_App_Menu::hide_others = _("Hide Others");
436 Fl_Mac_App_Menu::show = _("Show All");
437
438 fl_mac_set_about(about_callback, NULL);
439
440 Fl_Sys_Menu_Bar *menubar;
441 char buffer[1024];
442 menubar = new Fl_Sys_Menu_Bar(0, 0, 500, 25);
443 // Fl_Sys_Menu_Bar overrides methods without them being virtual,
444 // which means we cannot use our generic Fl_Menu_ helpers.
445 if (fltk_menu_escape(p_("SysMenu|", "&File"),
446 buffer, sizeof(buffer)) < sizeof(buffer))
447 menubar->add(buffer, 0, 0, 0, FL_SUBMENU);
448 if (fltk_menu_escape(p_("SysMenu|File|", "&New Connection"),
449 buffer, sizeof(buffer)) < sizeof(buffer))
450 menubar->insert(1, buffer, FL_COMMAND | 'n', new_connection_cb);
451 #endif
452 }
453
mkvnchomedir()454 static void mkvnchomedir()
455 {
456 // Create .vnc in the user's home directory if it doesn't already exist
457 char* homeDir = NULL;
458
459 if (getvnchomedir(&homeDir) == -1) {
460 vlog.error(_("Could not obtain the home directory path"));
461 } else {
462 int result = mkdir(homeDir, 0755);
463 if (result == -1 && errno != EEXIST)
464 vlog.error(_("Could not create VNC home directory: %s."), strerror(errno));
465 delete [] homeDir;
466 }
467 }
468
usage(const char * programName)469 static void usage(const char *programName)
470 {
471 #ifdef WIN32
472 // If we don't have a console then we need to create one for output
473 if (GetConsoleWindow() == NULL) {
474 HANDLE handle;
475 int fd;
476
477 AllocConsole();
478
479 handle = GetStdHandle(STD_ERROR_HANDLE);
480 fd = _open_osfhandle((intptr_t)handle, O_TEXT);
481 *stderr = *fdopen(fd, "w");
482 }
483 #endif
484
485 fprintf(stderr,
486 "\n"
487 "usage: %s [parameters] [host][:displayNum]\n"
488 " %s [parameters] [host][::port]\n"
489 #ifndef WIN32
490 " %s [parameters] [unix socket]\n"
491 #endif
492 " %s [parameters] -listen [port]\n"
493 " %s [parameters] [.tigervnc file]\n",
494 programName, programName,
495 #ifndef WIN32
496 programName,
497 #endif
498 programName, programName);
499
500 #if !defined(WIN32) && !defined(__APPLE__)
501 fprintf(stderr,"\n"
502 "Options:\n\n"
503 " -display Xdisplay - Specifies the X display for the viewer window\n"
504 " -geometry geometry - Initial position of the main VNC viewer window. See the\n"
505 " man page for details.\n");
506 #endif
507
508 fprintf(stderr,"\n"
509 "Parameters can be turned on with -<param> or off with -<param>=0\n"
510 "Parameters which take a value can be specified as "
511 "-<param> <value>\n"
512 "Other valid forms are <param>=<value> -<param>=<value> "
513 "--<param>=<value>\n"
514 "Parameter names are case-insensitive. The parameters are:\n\n");
515 Configuration::listParams(79, 14);
516
517 #ifdef WIN32
518 // Just wait for the user to kill the console window
519 Sleep(INFINITE);
520 #endif
521
522 exit(1);
523 }
524
525 static void
potentiallyLoadConfigurationFile(char * vncServerName)526 potentiallyLoadConfigurationFile(char *vncServerName)
527 {
528 const bool hasPathSeparator = (strchr(vncServerName, '/') != NULL ||
529 (strchr(vncServerName, '\\')) != NULL);
530
531 if (hasPathSeparator) {
532 #ifndef WIN32
533 struct stat sb;
534
535 // This might be a UNIX socket, we need to check
536 if (stat(vncServerName, &sb) == -1) {
537 // Some access problem; let loadViewerParameters() deal with it...
538 } else {
539 if ((sb.st_mode & S_IFMT) == S_IFSOCK)
540 return;
541 }
542 #endif
543
544 try {
545 const char* newServerName;
546 newServerName = loadViewerParameters(vncServerName);
547 // This might be empty, but we still need to clear it so we
548 // don't try to connect to the filename
549 strncpy(vncServerName, newServerName, VNCSERVERNAMELEN-1);
550 vncServerName[VNCSERVERNAMELEN-1] = '\0';
551 } catch (rfb::Exception& e) {
552 vlog.error("%s", e.str());
553 abort_vncviewer(_("Unable to load the specified configuration "
554 "file:\n\n%s"), e.str());
555 }
556 }
557 }
558
559 static void
migrateDeprecatedOptions()560 migrateDeprecatedOptions()
561 {
562 if (fullScreenAllMonitors) {
563 vlog.info(_("FullScreenAllMonitors is deprecated, set FullScreenMode to 'all' instead"));
564
565 fullScreenMode.setParam("all");
566 }
567 }
568
569 #ifndef WIN32
570 static int
interpretViaParam(char * remoteHost,int * remotePort,int localPort)571 interpretViaParam(char *remoteHost, int *remotePort, int localPort)
572 {
573 const int SERVER_PORT_OFFSET = 5900;
574 char *pos = strchr(vncServerName, ':');
575 if (pos == NULL)
576 *remotePort = SERVER_PORT_OFFSET;
577 else {
578 int portOffset = SERVER_PORT_OFFSET;
579 size_t len;
580 *pos++ = '\0';
581 len = strlen(pos);
582 if (*pos == ':') {
583 /* Two colons is an absolute port number, not an offset. */
584 pos++;
585 len--;
586 portOffset = 0;
587 }
588 if (!len || strspn (pos, "-0123456789") != len )
589 return 1;
590 *remotePort = atoi(pos) + portOffset;
591 }
592
593 if (*vncServerName != '\0')
594 strcpy(remoteHost, vncServerName);
595 else
596 strcpy(remoteHost, "localhost");
597
598 snprintf(vncServerName, VNCSERVERNAMELEN, "localhost::%d", localPort);
599 vncServerName[VNCSERVERNAMELEN - 1] = '\0';
600
601 return 0;
602 }
603
604 static void
createTunnel(const char * gatewayHost,const char * remoteHost,int remotePort,int localPort)605 createTunnel(const char *gatewayHost, const char *remoteHost,
606 int remotePort, int localPort)
607 {
608 const char *cmd = getenv("VNC_VIA_CMD");
609 char *cmd2, *percent;
610 char lport[10], rport[10];
611 sprintf(lport, "%d", localPort);
612 sprintf(rport, "%d", remotePort);
613 setenv("G", gatewayHost, 1);
614 setenv("H", remoteHost, 1);
615 setenv("R", rport, 1);
616 setenv("L", lport, 1);
617 if (!cmd)
618 cmd = "/usr/bin/ssh -f -L \"$L\":\"$H\":\"$R\" \"$G\" sleep 20";
619 /* Compatibility with TigerVNC's method. */
620 cmd2 = strdup(cmd);
621 while ((percent = strchr(cmd2, '%')) != NULL)
622 *percent = '$';
623 system(cmd2);
624 free(cmd2);
625 }
626
mktunnel()627 static int mktunnel()
628 {
629 const char *gatewayHost;
630 char remoteHost[VNCSERVERNAMELEN];
631 int localPort = findFreeTcpPort();
632 int remotePort;
633
634 if (interpretViaParam(remoteHost, &remotePort, localPort) != 0)
635 return 1;
636 gatewayHost = (const char*)via;
637 createTunnel(gatewayHost, remoteHost, remotePort, localPort);
638
639 return 0;
640 }
641 #endif /* !WIN32 */
642
main(int argc,char ** argv)643 int main(int argc, char** argv)
644 {
645 const char *localedir;
646 UserDialog dlg;
647
648 argv0 = argv[0];
649
650 setlocale(LC_ALL, "");
651
652 localedir = getlocaledir();
653 if (localedir == NULL)
654 fprintf(stderr, "Failed to determine locale directory\n");
655 else
656 bindtextdomain(PACKAGE_NAME, localedir);
657 textdomain(PACKAGE_NAME);
658
659 // Write about text to console, still using normal locale codeset
660 fprintf(stderr,"\n%s\n", about_text());
661
662 // Set gettext codeset to what our GUI toolkit uses. Since we are
663 // passing strings from strerror/gai_strerror to the GUI, these must
664 // be in GUI codeset as well.
665 bind_textdomain_codeset(PACKAGE_NAME, "UTF-8");
666 bind_textdomain_codeset("libc", "UTF-8");
667
668 rfb::initStdIOLoggers();
669 #ifdef WIN32
670 rfb::initFileLogger("C:\\temp\\vncviewer.log");
671 #else
672 rfb::initFileLogger("/tmp/vncviewer.log");
673 #endif
674 rfb::LogWriter::setLogParams("*:stderr:30");
675
676 #ifdef SIGHUP
677 signal(SIGHUP, CleanupSignalHandler);
678 #endif
679 signal(SIGINT, CleanupSignalHandler);
680 signal(SIGTERM, CleanupSignalHandler);
681
682 Configuration::enableViewerParams();
683
684 /* Load the default parameter settings */
685 char defaultServerName[VNCSERVERNAMELEN] = "";
686 try {
687 const char* configServerName;
688 configServerName = loadViewerParameters(NULL);
689 if (configServerName != NULL) {
690 strncpy(defaultServerName, configServerName, VNCSERVERNAMELEN-1);
691 defaultServerName[VNCSERVERNAMELEN-1] = '\0';
692 }
693 } catch (rfb::Exception& e) {
694 vlog.error("%s", e.str());
695 }
696
697 for (int i = 1; i < argc;) {
698 /* We need to resolve an ambiguity for booleans */
699 if (argv[i][0] == '-' && i+1 < argc) {
700 VoidParameter *param;
701
702 param = Configuration::getParam(&argv[i][1]);
703 if ((param != NULL) &&
704 (dynamic_cast<BoolParameter*>(param) != NULL)) {
705 if ((strcasecmp(argv[i+1], "0") == 0) ||
706 (strcasecmp(argv[i+1], "1") == 0) ||
707 (strcasecmp(argv[i+1], "true") == 0) ||
708 (strcasecmp(argv[i+1], "false") == 0) ||
709 (strcasecmp(argv[i+1], "yes") == 0) ||
710 (strcasecmp(argv[i+1], "no") == 0)) {
711 param->setParam(argv[i+1]);
712 i += 2;
713 continue;
714 }
715 }
716 }
717
718 if (Configuration::setParam(argv[i])) {
719 i++;
720 continue;
721 }
722
723 if (argv[i][0] == '-') {
724 if (i+1 < argc) {
725 if (Configuration::setParam(&argv[i][1], argv[i+1])) {
726 i += 2;
727 continue;
728 }
729 }
730
731 usage(argv[0]);
732 }
733
734 strncpy(vncServerName, argv[i], VNCSERVERNAMELEN);
735 vncServerName[VNCSERVERNAMELEN - 1] = '\0';
736 i++;
737 }
738
739 #if !defined(WIN32) && !defined(__APPLE__)
740 if (strcmp(display, "") != 0) {
741 Fl::display(display);
742 }
743 fl_open_display();
744 XkbSetDetectableAutoRepeat(fl_display, True, NULL);
745 #endif
746
747 init_fltk();
748 enable_touch();
749
750 // Check if the server name in reality is a configuration file
751 potentiallyLoadConfigurationFile(vncServerName);
752
753 migrateDeprecatedOptions();
754
755 mkvnchomedir();
756
757 CSecurity::upg = &dlg;
758 #ifdef HAVE_GNUTLS
759 CSecurityTLS::msg = &dlg;
760 #endif
761
762 Socket *sock = NULL;
763
764 #ifndef WIN32
765 /* Specifying -via and -listen together is nonsense */
766 if (listenMode && strlen(via.getValueStr()) > 0) {
767 // TRANSLATORS: "Parameters" are command line arguments, or settings
768 // from a file or the Windows registry.
769 vlog.error(_("Parameters -listen and -via are incompatible"));
770 abort_vncviewer(_("Parameters -listen and -via are incompatible"));
771 return 1; /* Not reached */
772 }
773 #endif
774
775 if (listenMode) {
776 std::list<SocketListener*> listeners;
777 try {
778 int port = 5500;
779 if (isdigit(vncServerName[0]))
780 port = atoi(vncServerName);
781
782 createTcpListeners(&listeners, 0, port);
783
784 vlog.info(_("Listening on port %d"), port);
785
786 /* Wait for a connection */
787 while (sock == NULL) {
788 fd_set rfds;
789 FD_ZERO(&rfds);
790 for (std::list<SocketListener*>::iterator i = listeners.begin();
791 i != listeners.end();
792 i++)
793 FD_SET((*i)->getFd(), &rfds);
794
795 int n = select(FD_SETSIZE, &rfds, 0, 0, 0);
796 if (n < 0) {
797 if (errno == EINTR) {
798 vlog.debug("Interrupted select() system call");
799 continue;
800 } else {
801 throw rdr::SystemException("select", errno);
802 }
803 }
804
805 for (std::list<SocketListener*>::iterator i = listeners.begin ();
806 i != listeners.end();
807 i++)
808 if (FD_ISSET((*i)->getFd(), &rfds)) {
809 sock = (*i)->accept();
810 if (sock)
811 /* Got a connection */
812 break;
813 }
814 }
815 } catch (rdr::Exception& e) {
816 vlog.error("%s", e.str());
817 abort_vncviewer(_("Failure waiting for incoming VNC connection:\n\n%s"), e.str());
818 return 1; /* Not reached */
819 }
820
821 while (!listeners.empty()) {
822 delete listeners.back();
823 listeners.pop_back();
824 }
825 } else {
826 if (vncServerName[0] == '\0') {
827 ServerDialog::run(defaultServerName, vncServerName);
828 if (vncServerName[0] == '\0')
829 return 1;
830 }
831
832 #ifndef WIN32
833 if (strlen (via.getValueStr()) > 0 && mktunnel() != 0)
834 usage(argv[0]);
835 #endif
836 }
837
838 inMainloop = true;
839 mainloop(vncServerName, sock);
840 inMainloop = false;
841
842 return 0;
843 }
844