blob: 4a8370b9520d1f1bc7b4faadedc366956c4add14 [file] [log] [blame]
Pierre Ossman5156d5e2011-03-09 09:42:34 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
2 * Copyright 2011 Pierre Ossman <ossman@cendio.se> for Cendio AB
DRCb65bb932011-06-24 03:17:00 +00003 * Copyright (C) 2011 D. R. Commander. All Rights Reserved.
Pierre Ossman5156d5e2011-03-09 09:42:34 +00004 *
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
Peter Åstrandc359f362011-08-23 12:04:46 +000021#ifdef HAVE_CONFIG_H
22#include <config.h>
23#endif
24
Pierre Ossman5156d5e2011-03-09 09:42:34 +000025#include <string.h>
26#include <stdio.h>
Pierre Ossman2a7a8d62013-02-15 08:33:39 +000027#include <ctype.h>
Pierre Ossman5156d5e2011-03-09 09:42:34 +000028#include <stdlib.h>
29#include <errno.h>
30#include <signal.h>
31#include <locale.h>
Pierre Ossman5bf17ad2015-02-17 13:23:00 +010032#include <fcntl.h>
Pierre Ossmana71a2762015-03-04 09:57:06 +010033#include <unistd.h>
Pierre Ossman5156d5e2011-03-09 09:42:34 +000034#include <sys/stat.h>
35
36#ifdef WIN32
Peter Åstrand (astrand)11167e12015-02-05 11:10:32 +010037#include <os/winerrno.h>
Pierre Ossman5156d5e2011-03-09 09:42:34 +000038#include <direct.h>
39#define mkdir(path, mode) _mkdir(path)
40#endif
41
Pierre Ossman6b9622d2014-07-21 16:42:12 +020042#if !defined(WIN32) && !defined(__APPLE__)
43#include <X11/Xlib.h>
44#include <X11/XKBlib.h>
45#endif
46
Pierre Ossman5156d5e2011-03-09 09:42:34 +000047#include <rfb/Logger_stdio.h>
48#include <rfb/SecurityClient.h>
49#include <rfb/Security.h>
50#ifdef HAVE_GNUTLS
51#include <rfb/CSecurityTLS.h>
52#endif
53#include <rfb/LogWriter.h>
54#include <rfb/Timer.h>
Peter Åstrand8a2b0812012-08-08 11:49:01 +000055#include <rfb/Exception.h>
Pierre Ossman5156d5e2011-03-09 09:42:34 +000056#include <network/TcpSocket.h>
DRC4426f002011-10-12 20:02:55 +000057#include <os/os.h>
Pierre Ossman5156d5e2011-03-09 09:42:34 +000058
59#include <FL/Fl.H>
60#include <FL/Fl_Widget.H>
Pierre Ossman8eb35082012-03-27 12:50:54 +000061#include <FL/Fl_PNG_Image.H>
Pierre Ossmana71a2762015-03-04 09:57:06 +010062#include <FL/Fl_Sys_Menu_Bar.H>
Pierre Ossman5156d5e2011-03-09 09:42:34 +000063#include <FL/fl_ask.H>
Pierre Ossmanb8858222011-04-29 11:51:38 +000064#include <FL/x.H>
Pierre Ossman5156d5e2011-03-09 09:42:34 +000065
66#include "i18n.h"
67#include "parameters.h"
68#include "CConn.h"
Pierre Ossman561ff0c2011-05-13 14:04:59 +000069#include "ServerDialog.h"
Pierre Ossman5156d5e2011-03-09 09:42:34 +000070#include "UserDialog.h"
Adam Tkac8ac4b302013-01-23 13:55:46 +000071#include "vncviewer.h"
Pierre Ossmana71a2762015-03-04 09:57:06 +010072#include "fltk_layout.h"
Pierre Ossman5156d5e2011-03-09 09:42:34 +000073
DRCb65bb932011-06-24 03:17:00 +000074#ifdef WIN32
Pierre Ossman8eb35082012-03-27 12:50:54 +000075#include "resource.h"
DRCb65bb932011-06-24 03:17:00 +000076#include "win32.h"
77#endif
78
Pierre Ossman5156d5e2011-03-09 09:42:34 +000079rfb::LogWriter vlog("main");
80
81using namespace network;
82using namespace rfb;
83using namespace std;
84
Adam Tkac8ac4b302013-01-23 13:55:46 +000085char vncServerName[VNCSERVERNAMELEN] = { '\0' };
86
Pierre Ossmana71a2762015-03-04 09:57:06 +010087static const char *argv0 = NULL;
88
Pierre Ossman5156d5e2011-03-09 09:42:34 +000089static bool exitMainloop = false;
Pierre Ossmane2ef5c12011-07-12 16:56:34 +000090static const char *exitError = NULL;
Pierre Ossman5156d5e2011-03-09 09:42:34 +000091
Pierre Ossmanccd09ba2015-04-07 14:58:45 +020092static const char *about_text()
93{
94 static char buffer[1024];
95
96 // This is used in multiple places with potentially different
97 // encodings, so we need to make sure we get a fresh string every
98 // time.
99 snprintf(buffer, sizeof(buffer),
100 _("TigerVNC Viewer %d-bit v%s\n"
101 "Built on: %s\n"
Peter Åstrand (astrand)6011f962017-11-29 09:11:29 +0100102 "Copyright (C) 1999-%d TigerVNC Team and many others (see README.rst)\n"
Pierre Ossman32b96ff2018-10-19 14:56:53 +0200103 "See https://www.tigervnc.org for information on TigerVNC."),
Pierre Ossmanccd09ba2015-04-07 14:58:45 +0200104 (int)sizeof(size_t)*8, PACKAGE_VERSION,
Samuel Mannehed71984912019-02-27 15:29:33 +0100105 BUILD_TIMESTAMP, 2019);
Pierre Ossmanccd09ba2015-04-07 14:58:45 +0200106
107 return buffer;
108}
109
Pierre Ossmane2ef5c12011-07-12 16:56:34 +0000110void exit_vncviewer(const char *error)
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000111{
Pierre Ossmane2ef5c12011-07-12 16:56:34 +0000112 // Prioritise the first error we get as that is probably the most
113 // relevant one.
114 if ((error != NULL) && (exitError == NULL))
115 exitError = strdup(error);
116
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000117 exitMainloop = true;
118}
119
Pierre Ossman1db73242015-09-23 12:20:32 +0200120bool should_exit()
121{
122 return exitMainloop;
123}
124
Pierre Ossmanb8858222011-04-29 11:51:38 +0000125void about_vncviewer()
126{
127 fl_message_title(_("About TigerVNC Viewer"));
Pierre Ossmanccd09ba2015-04-07 14:58:45 +0200128 fl_message("%s", about_text());
Pierre Ossmanb8858222011-04-29 11:51:38 +0000129}
130
Pierre Ossman5102fa92015-11-18 16:23:21 +0100131void run_mainloop()
132{
133 int next_timer;
134
135 next_timer = Timer::checkTimeouts();
136 if (next_timer == 0)
137 next_timer = INT_MAX;
138
139 if (Fl::wait((double)next_timer / 1000.0) < 0.0) {
140 vlog.error(_("Internal FLTK error. Exiting."));
141 exit(-1);
142 }
143}
144
Pierre Ossman8d713a92015-03-04 09:54:27 +0100145#ifdef __APPLE__
Pierre Ossmanb8858222011-04-29 11:51:38 +0000146static void about_callback(Fl_Widget *widget, void *data)
147{
148 about_vncviewer();
149}
Pierre Ossmana71a2762015-03-04 09:57:06 +0100150
151static void new_connection_cb(Fl_Widget *widget, void *data)
152{
153 const char *argv[2];
154 pid_t pid;
155
156 pid = fork();
157 if (pid == -1) {
158 vlog.error(_("Error starting new TigerVNC Viewer: %s"), strerror(errno));
159 return;
160 }
161
162 if (pid != 0)
163 return;
164
165 argv[0] = argv0;
166 argv[1] = NULL;
167
168 execvp(argv[0], (char * const *)argv);
169
170 vlog.error(_("Error starting new TigerVNC Viewer: %s"), strerror(errno));
171 _exit(1);
172}
Pierre Ossman8d713a92015-03-04 09:54:27 +0100173#endif
Pierre Ossmanb8858222011-04-29 11:51:38 +0000174
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000175static void CleanupSignalHandler(int sig)
176{
177 // CleanupSignalHandler allows C++ object cleanup to happen because it calls
178 // exit() rather than the default which is to abort.
Pierre Ossmanc76bd2b2014-12-03 14:01:31 +0100179 vlog.info(_("Termination signal %d has been received. TigerVNC Viewer will now exit."), sig);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000180 exit(1);
181}
182
183static void init_fltk()
184{
185 // Basic text size (10pt @ 96 dpi => 13px)
186 FL_NORMAL_SIZE = 13;
187
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000188 // Select a FLTK scheme and background color that looks somewhat
Pierre Ossman490b3192018-01-22 15:12:41 +0100189 // close to modern systems
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000190 Fl::scheme("gtk+");
191 Fl::background(220, 220, 220);
Pierre Ossman490b3192018-01-22 15:12:41 +0100192
193 // macOS has a slightly brighter default background though
194#ifdef __APPLE__
195 Fl::background(240, 240, 240);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000196#endif
197
Henrik Andersson3b837032011-09-19 13:46:55 +0000198 // Proper Gnome Shell integration requires that we set a sensible
199 // WM_CLASS for the window.
200 Fl_Window::default_xclass("vncviewer");
201
Pierre Ossman8eb35082012-03-27 12:50:54 +0000202 // Set the default icon for all windows.
Pierre Ossman8eb35082012-03-27 12:50:54 +0000203#ifdef WIN32
204 HICON lg, sm;
205
206 lg = (HICON)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON),
207 IMAGE_ICON, GetSystemMetrics(SM_CXICON),
208 GetSystemMetrics(SM_CYICON),
209 LR_DEFAULTCOLOR | LR_SHARED);
210 sm = (HICON)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON),
211 IMAGE_ICON, GetSystemMetrics(SM_CXSMICON),
212 GetSystemMetrics(SM_CYSMICON),
213 LR_DEFAULTCOLOR | LR_SHARED);
214
215 Fl_Window::default_icons(lg, sm);
216#elif ! defined(__APPLE__)
217 const int icon_sizes[] = {48, 32, 24, 16};
218
219 Fl_PNG_Image *icons[4];
220 int count;
221
222 count = 0;
223
224 // FIXME: Follow icon theme specification
225 for (size_t i = 0;i < sizeof(icon_sizes)/sizeof(icon_sizes[0]);i++) {
226 char icon_path[PATH_MAX];
227 bool exists;
228
Pierre Ossman6a007bd2012-09-11 10:56:21 +0000229 sprintf(icon_path, "%s/icons/hicolor/%dx%d/apps/tigervnc.png",
Pierre Ossman8eb35082012-03-27 12:50:54 +0000230 DATA_DIR, icon_sizes[i], icon_sizes[i]);
231
232#ifndef WIN32
233 struct stat st;
234 if (stat(icon_path, &st) != 0)
235#else
236 struct _stat st;
237 if (_stat(icon_path, &st) != 0)
238 return(false);
239#endif
240 exists = false;
241 else
242 exists = true;
243
244 if (exists) {
245 icons[count] = new Fl_PNG_Image(icon_path);
246 if (icons[count]->w() == 0 ||
247 icons[count]->h() == 0 ||
248 icons[count]->d() != 4) {
249 delete icons[count];
250 continue;
251 }
252
253 count++;
254 }
255 }
256
257 Fl_Window::default_icons((const Fl_RGB_Image**)icons, count);
258
259 for (int i = 0;i < count;i++)
260 delete icons[i];
261#endif
Pierre Ossman8eb35082012-03-27 12:50:54 +0000262
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000263 // This makes the "icon" in dialogs rounded, which fits better
264 // with the above schemes.
265 fl_message_icon()->box(FL_UP_BOX);
266
267 // Turn off the annoying behaviour where popups track the mouse.
268 fl_message_hotspot(false);
269
270 // Avoid empty titles for popups
271 fl_message_title_default(_("TigerVNC Viewer"));
272
273#ifdef WIN32
274 // Most "normal" Windows apps use this font for UI elements.
275 Fl::set_font(FL_HELVETICA, "Tahoma");
276#endif
277
278 // FLTK exposes these so that we can translate them.
279 fl_no = _("No");
280 fl_yes = _("Yes");
281 fl_ok = _("OK");
282 fl_cancel = _("Cancel");
283 fl_close = _("Close");
Pierre Ossmanb8858222011-04-29 11:51:38 +0000284
285#ifdef __APPLE__
Peter Åstrandb7c55242011-08-29 13:14:51 +0000286 /* Needs trailing space */
287 static char fltk_about[16];
288 snprintf(fltk_about, sizeof(fltk_about), "%s ", _("About"));
289 Fl_Mac_App_Menu::about = fltk_about;
290 static char fltk_hide[16];
291 snprintf(fltk_hide, sizeof(fltk_hide), "%s ", _("Hide"));
292 Fl_Mac_App_Menu::hide = fltk_hide;
293 static char fltk_quit[16];
294 snprintf(fltk_quit, sizeof(fltk_quit), "%s ", _("Quit"));
295 Fl_Mac_App_Menu::quit = fltk_quit;
296
Pierre Ossman41ba6032011-06-16 11:09:31 +0000297 Fl_Mac_App_Menu::print = ""; // Don't want the print item
298 Fl_Mac_App_Menu::services = _("Services");
Pierre Ossman41ba6032011-06-16 11:09:31 +0000299 Fl_Mac_App_Menu::hide_others = _("Hide Others");
300 Fl_Mac_App_Menu::show = _("Show All");
Pierre Ossman41ba6032011-06-16 11:09:31 +0000301
Pierre Ossmanb8858222011-04-29 11:51:38 +0000302 fl_mac_set_about(about_callback, NULL);
Pierre Ossmana71a2762015-03-04 09:57:06 +0100303
304 Fl_Sys_Menu_Bar *menubar;
305 char buffer[1024];
306 menubar = new Fl_Sys_Menu_Bar(0, 0, 500, 25);
307 // Fl_Sys_Menu_Bar overrides methods without them being virtual,
308 // which means we cannot use our generic Fl_Menu_ helpers.
Pierre Ossmanff626f82015-09-23 16:39:54 +0200309 if (fltk_menu_escape(p_("SysMenu|", "&File"),
310 buffer, sizeof(buffer)) < sizeof(buffer))
Pierre Ossmana71a2762015-03-04 09:57:06 +0100311 menubar->add(buffer, 0, 0, 0, FL_SUBMENU);
Pierre Ossmanff626f82015-09-23 16:39:54 +0200312 if (fltk_menu_escape(p_("SysMenu|File|", "&New Connection"),
313 buffer, sizeof(buffer)) < sizeof(buffer))
Pierre Ossmana71a2762015-03-04 09:57:06 +0100314 menubar->insert(1, buffer, FL_COMMAND | 'n', new_connection_cb);
Pierre Ossmanb8858222011-04-29 11:51:38 +0000315#endif
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000316}
317
318static void mkvnchomedir()
319{
320 // Create .vnc in the user's home directory if it doesn't already exist
321 char* homeDir = NULL;
322
323 if (getvnchomedir(&homeDir) == -1) {
324 vlog.error(_("Could not create VNC home directory: can't obtain home "
325 "directory path."));
326 } else {
327 int result = mkdir(homeDir, 0755);
328 if (result == -1 && errno != EEXIST)
329 vlog.error(_("Could not create VNC home directory: %s."), strerror(errno));
330 delete [] homeDir;
331 }
332}
333
334static void usage(const char *programName)
335{
Pierre Ossman5bf17ad2015-02-17 13:23:00 +0100336#ifdef WIN32
337 // If we don't have a console then we need to create one for output
338 if (GetConsoleWindow() == NULL) {
339 HANDLE handle;
340 int fd;
341
342 AllocConsole();
343
344 handle = GetStdHandle(STD_ERROR_HANDLE);
345 fd = _open_osfhandle((intptr_t)handle, O_TEXT);
346 *stderr = *fdopen(fd, "w");
347 }
348#endif
349
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000350 fprintf(stderr,
Pierre Ossman2b69d382018-09-28 15:31:14 +0200351 "\n"
352 "usage: %s [parameters] [host][:displayNum]\n"
353 " %s [parameters] [host][::port]\n"
354#ifndef WIN32
355 " %s [parameters] [unix socket]\n"
356#endif
357 " %s [parameters] -listen [port]\n"
Mathias Jonsson50439d72018-04-22 19:10:30 +0200358 " %s [parameters] [.tigervnc file]\n",
Pierre Ossman2b69d382018-09-28 15:31:14 +0200359 programName, programName,
360#ifndef WIN32
361 programName,
362#endif
363 programName, programName);
Pierre Ossmaneea92e22019-03-26 10:17:41 +0100364
365#if !defined(WIN32) && !defined(__APPLE__)
366 fprintf(stderr,"\n"
367 "Options:\n\n"
368 " -display Xdisplay - Specifies the X display for the viewer window\n"
Pierre Ossman26cd3322019-03-26 10:29:29 +0100369 " -geometry geometry - Initial position of the main VNC viewer window. See the\n"
370 " man page for details.\n");
Pierre Ossmaneea92e22019-03-26 10:17:41 +0100371#endif
372
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000373 fprintf(stderr,"\n"
374 "Parameters can be turned on with -<param> or off with -<param>=0\n"
375 "Parameters which take a value can be specified as "
376 "-<param> <value>\n"
377 "Other valid forms are <param>=<value> -<param>=<value> "
378 "--<param>=<value>\n"
379 "Parameter names are case-insensitive. The parameters are:\n\n");
380 Configuration::listParams(79, 14);
Pierre Ossman5bf17ad2015-02-17 13:23:00 +0100381
382#ifdef WIN32
383 // Just wait for the user to kill the console window
384 Sleep(INFINITE);
385#endif
386
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000387 exit(1);
388}
389
Mathias Jonsson50439d72018-04-22 19:10:30 +0200390static void
391potentiallyLoadConfigurationFile(char *vncServerName)
392{
393 const bool hasPathSeparator = (strchr(vncServerName, '/') != NULL ||
394 (strchr(vncServerName, '\\')) != NULL);
395
396 if (hasPathSeparator) {
Pierre Ossmanfe2e5fc2018-05-29 16:46:19 +0200397#ifndef WIN32
398 struct stat sb;
399
400 // This might be a UNIX socket, we need to check
401 if (stat(vncServerName, &sb) == -1) {
402 // Some access problem; let loadViewerParameters() deal with it...
403 } else {
404 if ((sb.st_mode & S_IFMT) == S_IFSOCK)
405 return;
406 }
407#endif
408
Mathias Jonsson50439d72018-04-22 19:10:30 +0200409 try {
Pierre Ossmancb99be52018-05-29 16:37:54 +0200410 const char* newServerName;
411 newServerName = loadViewerParameters(vncServerName);
412 // This might be empty, but we still need to clear it so we
413 // don't try to connect to the filename
Pierre Ossman7240f622019-03-26 11:11:20 +0100414 strncpy(vncServerName, newServerName, VNCSERVERNAMELEN-1);
415 vncServerName[VNCSERVERNAMELEN-1] = '\0';
Mathias Jonsson50439d72018-04-22 19:10:30 +0200416 } catch (rfb::Exception& e) {
417 vlog.error("%s", e.str());
418 if (alertOnFatalError)
419 fl_alert("%s", e.str());
420 exit(EXIT_FAILURE);
421 }
422 }
423}
424
Adam Tkac8ac4b302013-01-23 13:55:46 +0000425#ifndef WIN32
426static int
427interpretViaParam(char *remoteHost, int *remotePort, int localPort)
428{
429 const int SERVER_PORT_OFFSET = 5900;
430 char *pos = strchr(vncServerName, ':');
431 if (pos == NULL)
432 *remotePort = SERVER_PORT_OFFSET;
433 else {
434 int portOffset = SERVER_PORT_OFFSET;
435 size_t len;
436 *pos++ = '\0';
437 len = strlen(pos);
438 if (*pos == ':') {
439 /* Two colons is an absolute port number, not an offset. */
440 pos++;
441 len--;
442 portOffset = 0;
443 }
444 if (!len || strspn (pos, "-0123456789") != len )
445 return 1;
446 *remotePort = atoi(pos) + portOffset;
447 }
448
449 if (*vncServerName != '\0')
450 strncpy(remoteHost, vncServerName, VNCSERVERNAMELEN);
451 else
452 strncpy(remoteHost, "localhost", VNCSERVERNAMELEN);
453
454 remoteHost[VNCSERVERNAMELEN - 1] = '\0';
455
456 snprintf(vncServerName, VNCSERVERNAMELEN, "localhost::%d", localPort);
457 vncServerName[VNCSERVERNAMELEN - 1] = '\0';
Adam Tkac8ac4b302013-01-23 13:55:46 +0000458
459 return 0;
460}
461
462static void
463createTunnel(const char *gatewayHost, const char *remoteHost,
464 int remotePort, int localPort)
465{
Pierre Ossmanf8d525b2014-07-09 17:02:27 +0200466 const char *cmd = getenv("VNC_VIA_CMD");
467 char *cmd2, *percent;
Adam Tkac8ac4b302013-01-23 13:55:46 +0000468 char lport[10], rport[10];
469 sprintf(lport, "%d", localPort);
470 sprintf(rport, "%d", remotePort);
471 setenv("G", gatewayHost, 1);
472 setenv("H", remoteHost, 1);
473 setenv("R", rport, 1);
474 setenv("L", lport, 1);
Adam Tkac8ac4b302013-01-23 13:55:46 +0000475 if (!cmd)
476 cmd = "/usr/bin/ssh -f -L \"$L\":\"$H\":\"$R\" \"$G\" sleep 20";
477 /* Compatibility with TigerVNC's method. */
Pierre Ossmanf8d525b2014-07-09 17:02:27 +0200478 cmd2 = strdup(cmd);
479 while ((percent = strchr(cmd2, '%')) != NULL)
Adam Tkac8ac4b302013-01-23 13:55:46 +0000480 *percent = '$';
Pierre Ossmanf8d525b2014-07-09 17:02:27 +0200481 system(cmd2);
482 free(cmd2);
Adam Tkac8ac4b302013-01-23 13:55:46 +0000483}
484
485static int mktunnel()
486{
487 const char *gatewayHost;
488 char remoteHost[VNCSERVERNAMELEN];
489 int localPort = findFreeTcpPort();
490 int remotePort;
491
Adam Tkac8ac4b302013-01-23 13:55:46 +0000492 if (interpretViaParam(remoteHost, &remotePort, localPort) != 0)
493 return 1;
Jan Grulicha5720e52018-11-20 10:44:39 +0100494 gatewayHost = (const char*)via;
Adam Tkac8ac4b302013-01-23 13:55:46 +0000495 createTunnel(gatewayHost, remoteHost, remotePort, localPort);
496
497 return 0;
498}
499#endif /* !WIN32 */
500
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000501int main(int argc, char** argv)
502{
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000503 UserDialog dlg;
504
Pierre Ossmana71a2762015-03-04 09:57:06 +0100505 argv0 = argv[0];
506
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000507 setlocale(LC_ALL, "");
Pierre Ossman0878eca2012-03-27 13:03:22 +0000508 bindtextdomain(PACKAGE_NAME, LOCALE_DIR);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000509 textdomain(PACKAGE_NAME);
510
511 rfb::SecurityClient::setDefaults();
512
513 // Write about text to console, still using normal locale codeset
Pierre Ossmanccd09ba2015-04-07 14:58:45 +0200514 fprintf(stderr,"\n%s\n", about_text());
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000515
516 // Set gettext codeset to what our GUI toolkit uses. Since we are
517 // passing strings from strerror/gai_strerror to the GUI, these must
518 // be in GUI codeset as well.
519 bind_textdomain_codeset(PACKAGE_NAME, "UTF-8");
520 bind_textdomain_codeset("libc", "UTF-8");
521
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000522 rfb::initStdIOLoggers();
Pierre Ossmanc8719ad2012-04-26 14:27:52 +0000523#ifdef WIN32
524 rfb::initFileLogger("C:\\temp\\vncviewer.log");
525#else
526 rfb::initFileLogger("/tmp/vncviewer.log");
527#endif
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000528 rfb::LogWriter::setLogParams("*:stderr:30");
529
530#ifdef SIGHUP
531 signal(SIGHUP, CleanupSignalHandler);
532#endif
533 signal(SIGINT, CleanupSignalHandler);
534 signal(SIGTERM, CleanupSignalHandler);
535
536 init_fltk();
537
538 Configuration::enableViewerParams();
539
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000540 /* Load the default parameter settings */
Pierre Ossmanb08a6482018-06-13 12:58:59 +0200541 char defaultServerName[VNCSERVERNAMELEN] = "";
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000542 try {
Pierre Ossmanb08a6482018-06-13 12:58:59 +0200543 const char* configServerName;
544 configServerName = loadViewerParameters(NULL);
Pierre Ossman7240f622019-03-26 11:11:20 +0100545 if (configServerName != NULL) {
546 strncpy(defaultServerName, configServerName, VNCSERVERNAMELEN-1);
547 defaultServerName[VNCSERVERNAMELEN-1] = '\0';
548 }
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000549 } catch (rfb::Exception& e) {
Dr. David Alan Gilbertf4d1d892017-07-11 12:11:50 +0100550 vlog.error("%s", e.str());
551 if (alertOnFatalError)
552 fl_alert("%s", e.str());
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000553 }
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000554
Pierre Ossman8c2a7442017-11-14 16:04:32 +0100555 for (int i = 1; i < argc;) {
Pierre Ossman8c2a7442017-11-14 16:04:32 +0100556 if (Configuration::setParam(argv[i])) {
557 i++;
558 continue;
559 }
560
561 if (argv[i][0] == '-') {
562 if (i+1 < argc) {
563 if (Configuration::setParam(&argv[i][1], argv[i+1])) {
564 i += 2;
565 continue;
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000566 }
567 }
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000568
Pierre Ossman8c2a7442017-11-14 16:04:32 +0100569 usage(argv[0]);
DRC5aa06502011-06-23 22:04:46 +0000570 }
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000571
Pierre Ossman8c2a7442017-11-14 16:04:32 +0100572 strncpy(vncServerName, argv[i], VNCSERVERNAMELEN);
573 vncServerName[VNCSERVERNAMELEN - 1] = '\0';
574 i++;
575 }
576
Mathias Jonsson50439d72018-04-22 19:10:30 +0200577 // Check if the server name in reality is a configuration file
578 potentiallyLoadConfigurationFile(vncServerName);
579
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000580 mkvnchomedir();
581
Pierre Ossman727a70f2017-11-14 15:51:50 +0100582#if !defined(WIN32) && !defined(__APPLE__)
Peter Åstrand (astrand)3ec76bd2018-01-29 10:42:44 +0100583 if (strcmp(display, "") != 0) {
584 Fl::display(display);
585 }
Pierre Ossman727a70f2017-11-14 15:51:50 +0100586 fl_open_display();
587 XkbSetDetectableAutoRepeat(fl_display, True, NULL);
588#endif
589
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000590 CSecurity::upg = &dlg;
591#ifdef HAVE_GNUTLS
592 CSecurityTLS::msg = &dlg;
593#endif
594
Pierre Ossman2a7a8d62013-02-15 08:33:39 +0000595 Socket *sock = NULL;
596
DRC3e7ed812013-02-26 10:34:22 +0000597#ifndef WIN32
Adam Tkac571089b2013-02-19 14:30:32 +0000598 /* Specifying -via and -listen together is nonsense */
599 if (listenMode && strlen(via.getValueStr()) > 0) {
Pierre Ossman744e55c2014-12-03 14:00:54 +0100600 // TRANSLATORS: "Parameters" are command line arguments, or settings
601 // from a file or the Windows registry.
Pierre Ossman8ca4c1d2014-09-22 12:54:26 +0200602 vlog.error(_("Parameters -listen and -via are incompatible"));
Dr. David Alan Gilbertf4d1d892017-07-11 12:11:50 +0100603 if (alertOnFatalError)
604 fl_alert(_("Parameters -listen and -via are incompatible"));
Adam Tkac571089b2013-02-19 14:30:32 +0000605 exit_vncviewer();
606 return 1;
607 }
DRC3e7ed812013-02-26 10:34:22 +0000608#endif
Adam Tkac571089b2013-02-19 14:30:32 +0000609
610 if (listenMode) {
Pierre Ossmane3a2be62018-05-03 14:03:55 +0200611 std::list<SocketListener*> listeners;
Pierre Ossman2a7a8d62013-02-15 08:33:39 +0000612 try {
613 int port = 5500;
614 if (isdigit(vncServerName[0]))
615 port = atoi(vncServerName);
616
Tim Waugh892d10a2015-03-11 13:12:07 +0000617 createTcpListeners(&listeners, 0, port);
Pierre Ossman2a7a8d62013-02-15 08:33:39 +0000618
Peter Åstrand (astrand)4ab2f8a2015-03-18 10:31:01 +0100619 vlog.info(_("Listening on port %d"), port);
Tim Waugh892d10a2015-03-11 13:12:07 +0000620
621 /* Wait for a connection */
622 while (sock == NULL) {
623 fd_set rfds;
624 FD_ZERO(&rfds);
Pierre Ossmane3a2be62018-05-03 14:03:55 +0200625 for (std::list<SocketListener*>::iterator i = listeners.begin();
Tim Waugh892d10a2015-03-11 13:12:07 +0000626 i != listeners.end();
627 i++)
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200628 FD_SET((*i)->getFd(), &rfds);
Tim Waugh892d10a2015-03-11 13:12:07 +0000629
630 int n = select(FD_SETSIZE, &rfds, 0, 0, 0);
631 if (n < 0) {
632 if (errno == EINTR) {
633 vlog.debug("Interrupted select() system call");
634 continue;
635 } else {
636 throw rdr::SystemException("select", errno);
637 }
638 }
639
Pierre Ossmane3a2be62018-05-03 14:03:55 +0200640 for (std::list<SocketListener*>::iterator i = listeners.begin ();
Tim Waugh892d10a2015-03-11 13:12:07 +0000641 i != listeners.end();
642 i++)
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200643 if (FD_ISSET((*i)->getFd(), &rfds)) {
644 sock = (*i)->accept();
Tim Waugh892d10a2015-03-11 13:12:07 +0000645 if (sock)
646 /* Got a connection */
647 break;
648 }
649 }
Pierre Ossman2a7a8d62013-02-15 08:33:39 +0000650 } catch (rdr::Exception& e) {
651 vlog.error("%s", e.str());
Dr. David Alan Gilbertf4d1d892017-07-11 12:11:50 +0100652 if (alertOnFatalError)
653 fl_alert("%s", e.str());
Pierre Ossman2a7a8d62013-02-15 08:33:39 +0000654 exit_vncviewer();
655 return 1;
656 }
657
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200658 while (!listeners.empty()) {
659 delete listeners.back();
660 listeners.pop_back();
661 }
Pierre Ossman2a7a8d62013-02-15 08:33:39 +0000662 } else {
663 if (vncServerName[0] == '\0') {
664 ServerDialog::run(defaultServerName, vncServerName);
665 if (vncServerName[0] == '\0')
666 return 1;
667 }
Pierre Ossman561ff0c2011-05-13 14:04:59 +0000668
Adam Tkac8ac4b302013-01-23 13:55:46 +0000669#ifndef WIN32
Pierre Ossman2a7a8d62013-02-15 08:33:39 +0000670 if (strlen (via.getValueStr()) > 0 && mktunnel() != 0)
671 usage(argv[0]);
Adam Tkac8ac4b302013-01-23 13:55:46 +0000672#endif
Pierre Ossman2a7a8d62013-02-15 08:33:39 +0000673 }
Adam Tkac8ac4b302013-01-23 13:55:46 +0000674
Pierre Ossman2a7a8d62013-02-15 08:33:39 +0000675 CConn *cc = new CConn(vncServerName, sock);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000676
Pierre Ossman5102fa92015-11-18 16:23:21 +0100677 while (!exitMainloop)
678 run_mainloop();
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000679
Pierre Ossmane2ef5c12011-07-12 16:56:34 +0000680 delete cc;
681
Dr. David Alan Gilbertf4d1d892017-07-11 12:11:50 +0100682 if (exitError != NULL && alertOnFatalError)
Pierre Ossmanf52740e2012-04-25 15:43:56 +0000683 fl_alert("%s", exitError);
Pierre Ossmane2ef5c12011-07-12 16:56:34 +0000684
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000685 return 0;
686}