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