blob: 8427818689c940348970809f2ab1a2e800f09a52 [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
Peter Åstrand8a2b0812012-08-08 11:49:01 +00003 * Copyright 2012 Samuel Mannehed <samuel@cendio.se> for Cendio AB
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
Peter Åstrand8a2b0812012-08-08 11:49:01 +000025#ifdef HAVE_GNUTLS
26#include <rfb/CSecurityTLS.h>
27#endif
28
29#ifdef _WIN32
30#include <windows.h>
31#include <tchar.h>
32#endif
33
Pierre Ossman5156d5e2011-03-09 09:42:34 +000034#include "parameters.h"
35
Peter Åstrand8a2b0812012-08-08 11:49:01 +000036#include <os/os.h>
37#include <rfb/Exception.h>
38#include <rfb/LogWriter.h>
39#include <rfb/SecurityClient.h>
40
41#include <FL/fl_utf8.h>
42
43#include <stdio.h>
44#include <string.h>
45#include <limits.h>
Pierre Ossman1bfbcb52014-12-03 14:03:29 +010046#include <errno.h>
Peter Åstrand8a2b0812012-08-08 11:49:01 +000047
Pierre Ossman8ca4c1d2014-09-22 12:54:26 +020048#include "i18n.h"
49
Pierre Ossman5156d5e2011-03-09 09:42:34 +000050using namespace rfb;
51
Peter Åstrand8a2b0812012-08-08 11:49:01 +000052static LogWriter vlog("Parameters");
53
54
Pierre Ossman4c8e3112011-06-08 17:21:48 +000055IntParameter pointerEventInterval("PointerEventInterval",
56 "Time in milliseconds to rate-limit"
Pierre Ossman157f5422017-07-19 11:20:53 +020057 " successive pointer events", 17);
Pierre Ossman5156d5e2011-03-09 09:42:34 +000058BoolParameter dotWhenNoCursor("DotWhenNoCursor",
59 "Show the dot cursor when the server sends an "
Pierre Ossman93f37742011-06-09 08:35:34 +000060 "invisible cursor", false);
Pierre Ossman5156d5e2011-03-09 09:42:34 +000061
Dr. David Alan Gilbertf4d1d892017-07-11 12:11:50 +010062BoolParameter alertOnFatalError("AlertOnFatalError",
63 "Give a dialog on connection problems rather "
64 "than exiting immediately", true);
65
Pierre Ossman5156d5e2011-03-09 09:42:34 +000066StringParameter passwordFile("PasswordFile",
67 "Password file for VNC authentication", "");
68AliasParameter passwd("passwd", "Alias for PasswordFile", &passwordFile);
69
70BoolParameter autoSelect("AutoSelect",
71 "Auto select pixel format and encoding. "
72 "Default if PreferredEncoding and FullColor are not specified.",
73 true);
74BoolParameter fullColour("FullColor",
75 "Use full color", true);
76AliasParameter fullColourAlias("FullColour", "Alias for FullColor", &fullColour);
77IntParameter lowColourLevel("LowColorLevel",
78 "Color level to use on slow connections. "
79 "0 = Very Low (8 colors), 1 = Low (64 colors), "
80 "2 = Medium (256 colors)", 2);
81AliasParameter lowColourLevelAlias("LowColourLevel", "Alias for LowColorLevel", &lowColourLevel);
82StringParameter preferredEncoding("PreferredEncoding",
83 "Preferred encoding to use (Tight, ZRLE, Hextile or"
84 " Raw)", "Tight");
85BoolParameter customCompressLevel("CustomCompressLevel",
86 "Use custom compression level. "
87 "Default if CompressLevel is specified.", false);
88IntParameter compressLevel("CompressLevel",
DRCba7bc512011-08-17 02:30:34 +000089 "Use specified compression level 0 = Low, 6 = High",
Pierre Ossman701ad682011-11-20 15:39:17 +000090 2);
Pierre Ossman5156d5e2011-03-09 09:42:34 +000091BoolParameter noJpeg("NoJPEG",
92 "Disable lossy JPEG compression in Tight encoding.",
93 false);
94IntParameter qualityLevel("QualityLevel",
95 "JPEG quality level. 0 = Low, 9 = High",
96 8);
97
Peter Åstrand49b11572012-08-01 08:09:09 +000098BoolParameter maximize("Maximize", "Maximize viewer window", false);
Pierre Ossman5156d5e2011-03-09 09:42:34 +000099BoolParameter fullScreen("FullScreen", "Full screen mode", false);
Pierre Ossmanaae38912012-07-13 11:22:55 +0000100BoolParameter fullScreenAllMonitors("FullScreenAllMonitors",
101 "Enable full screen over all monitors",
102 true);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000103StringParameter desktopSize("DesktopSize",
104 "Reconfigure desktop size on the server on "
105 "connect (if possible)", "");
Peter Åstrandb182a9e2012-08-27 07:28:08 +0000106StringParameter geometry("geometry",
Pierre Ossmanffcffd32014-09-22 12:58:48 +0200107 "Specify size and position of viewer window", "");
Pierre Ossman2a7a8d62013-02-15 08:33:39 +0000108
109BoolParameter listenMode("listen", "Listen for connections from VNC servers", false);
110
Pierre Ossmanff473402012-07-04 11:27:47 +0000111BoolParameter remoteResize("RemoteResize",
112 "Dynamically resize the remote desktop size as "
113 "the size of the local client window changes. "
114 "(Does not work with all servers)", true);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000115
116BoolParameter viewOnly("ViewOnly",
117 "Don't send any mouse or keyboard events to the server",
118 false);
119BoolParameter shared("Shared",
120 "Don't disconnect other viewers upon connection - "
121 "share the desktop instead",
122 false);
123
124BoolParameter acceptClipboard("AcceptClipboard",
125 "Accept clipboard changes from the server",
126 true);
Pierre Ossmanf862c2e2016-03-29 14:15:38 +0200127BoolParameter setPrimary("SetPrimary",
128 "Set the primary selection as well as the "
129 "clipboard selection", true);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000130BoolParameter sendClipboard("SendClipboard",
131 "Send clipboard changes to the server", true);
Pierre Ossmanf7fef922015-12-10 21:24:08 +0100132#if !defined(WIN32) && !defined(__APPLE__)
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000133BoolParameter sendPrimary("SendPrimary",
Pierre Ossman57cadc12016-03-29 14:18:36 +0200134 "Send the primary selection to the "
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000135 "server as well as the clipboard selection",
136 true);
Peter Åstrand (astrand)3ec76bd2018-01-29 10:42:44 +0100137StringParameter display("display",
138 "Specifies the X display on which the VNC viewer window should appear.",
139 "");
Pierre Ossmanf7fef922015-12-10 21:24:08 +0100140#endif
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000141
142StringParameter menuKey("MenuKey", "The key which brings up the popup menu",
143 "F8");
144
Pierre Ossman407a5c32011-05-26 14:48:29 +0000145BoolParameter fullscreenSystemKeys("FullscreenSystemKeys",
146 "Pass special keys (like Alt+Tab) directly "
147 "to the server when in full screen mode.",
148 true);
149
Adam Tkac8ac4b302013-01-23 13:55:46 +0000150#ifndef WIN32
151StringParameter via("via", "Gateway to tunnel via", "");
152#endif
153
Pierre Ossmanc0ea6422014-12-03 14:04:11 +0100154static const char* IDENTIFIER_STRING = "TigerVNC Configuration file Version 1.0";
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000155
Pierre Ossmanc0ea6422014-12-03 14:04:11 +0100156static VoidParameter* parameterArray[] = {
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000157#ifdef HAVE_GNUTLS
Pierre Ossman3d2a84b2014-09-17 16:45:35 +0200158 &CSecurityTLS::X509CA,
159 &CSecurityTLS::X509CRL,
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000160#endif // HAVE_GNUTLS
161 &SecurityClient::secTypes,
162 &dotWhenNoCursor,
163 &autoSelect,
164 &fullColour,
165 &lowColourLevel,
166 &preferredEncoding,
167 &customCompressLevel,
168 &compressLevel,
169 &noJpeg,
170 &qualityLevel,
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000171 &fullScreen,
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000172 &fullScreenAllMonitors,
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000173 &desktopSize,
Peter Åstrandb182a9e2012-08-27 07:28:08 +0000174 &geometry,
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000175 &remoteResize,
176 &viewOnly,
177 &shared,
178 &acceptClipboard,
179 &sendClipboard,
Pierre Ossmanf7fef922015-12-10 21:24:08 +0100180#if !defined(WIN32) && !defined(__APPLE__)
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000181 &sendPrimary,
Pierre Ossmanf7fef922015-12-10 21:24:08 +0100182#endif
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000183 &menuKey,
Dr. David Alan Gilbertf4d1d892017-07-11 12:11:50 +0100184 &fullscreenSystemKeys,
185 &alertOnFatalError
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000186};
187
188// Encoding Table
189static struct {
190 const char first;
191 const char second;
Pierre Ossman6e538b42015-03-03 16:48:01 +0100192} replaceMap[] = { { '\n', 'n' },
Pierre Ossmanb8b3cbf2018-05-29 15:51:42 +0200193 { '\r', 'r' },
194 { '\\', '\\' } };
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000195
Pierre Ossmanc0ea6422014-12-03 14:04:11 +0100196static bool encodeValue(const char* val, char* dest, size_t destSize) {
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000197
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000198 size_t pos = 0;
199
Pierre Ossmana9af1f12015-06-17 10:47:28 +0200200 for (size_t i = 0; (val[i] != '\0') && (i < (destSize - 1)); i++) {
Pierre Ossmanb8b3cbf2018-05-29 15:51:42 +0200201 bool normalCharacter;
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000202
203 // Check for sequences which will need encoding
Pierre Ossmanb8b3cbf2018-05-29 15:51:42 +0200204 normalCharacter = true;
205 for (size_t j = 0; j < sizeof(replaceMap)/sizeof(replaceMap[0]); j++) {
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000206
Pierre Ossmanb8b3cbf2018-05-29 15:51:42 +0200207 if (val[i] == replaceMap[j].first) {
208 dest[pos] = '\\';
209 pos++;
210 if (pos >= destSize)
211 return false;
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000212
Pierre Ossmanb8b3cbf2018-05-29 15:51:42 +0200213 dest[pos] = replaceMap[j].second;
214 normalCharacter = false;
215 break;
216 }
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000217
Pierre Ossmanb8b3cbf2018-05-29 15:51:42 +0200218 if (normalCharacter) {
219 dest[pos] = val[i];
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000220 }
221 }
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000222
223 pos++;
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100224 if (pos >= destSize)
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000225 return false;
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000226 }
227
228 dest[pos] = '\0';
229 return true;
230}
231
232
Pierre Ossmanc0ea6422014-12-03 14:04:11 +0100233static bool decodeValue(const char* val, char* dest, size_t destSize) {
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000234
235 size_t pos = 0;
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000236
Pierre Ossmana9af1f12015-06-17 10:47:28 +0200237 for (size_t i = 0; (val[i] != '\0') && (i < (destSize - 1)); i++) {
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000238
239 // Check for escape sequences
240 if (val[i] == '\\') {
Pierre Ossmanb8b3cbf2018-05-29 15:51:42 +0200241 bool escapedCharacter;
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000242
Pierre Ossmanb8b3cbf2018-05-29 15:51:42 +0200243 escapedCharacter = false;
Pierre Ossman5c23b9e2015-03-03 16:26:03 +0100244 for (size_t j = 0; j < sizeof(replaceMap)/sizeof(replaceMap[0]); j++) {
Pierre Ossmanffcffd32014-09-22 12:58:48 +0200245 if (val[i+1] == replaceMap[j].second) {
246 dest[pos] = replaceMap[j].first;
247 escapedCharacter = true;
Pierre Ossman2a854892018-05-29 15:51:03 +0200248 i++;
Pierre Ossmanffcffd32014-09-22 12:58:48 +0200249 break;
250 }
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000251 }
252
Pierre Ossmanb8b3cbf2018-05-29 15:51:42 +0200253 if (!escapedCharacter)
254 return false;
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000255
256 } else {
257 dest[pos] = val[i];
258 }
259
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000260 pos++;
261 if (pos >= destSize) {
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000262 return false;
263 }
264 }
265
266 dest[pos] = '\0';
267 return true;
268}
269
270
271#ifdef _WIN32
Pierre Ossmanc0ea6422014-12-03 14:04:11 +0100272static void setKeyString(const char *_name, const char *_value, HKEY* hKey) {
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000273
274 const DWORD buffersize = 256;
275
276 wchar_t name[buffersize];
277 unsigned size = fl_utf8towc(_name, strlen(_name)+1, name, buffersize);
278 if (size >= buffersize) {
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100279 vlog.error(_("The name of the parameter %s was too large to write to the registry"), _name);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000280 return;
281 }
282
283 char encodingBuffer[buffersize];
284 if (!encodeValue(_value, encodingBuffer, buffersize)) {
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100285 vlog.error(_("The parameter %s was too large to write to the registry"), _name);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000286 return;
287 }
288
289 wchar_t value[buffersize];
290 size = fl_utf8towc(encodingBuffer, strlen(encodingBuffer)+1, value, buffersize);
291 if (size >= buffersize) {
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100292 vlog.error(_("The parameter %s was too large to write to the registry"), _name);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000293 return;
294 }
295
296 LONG res = RegSetValueExW(*hKey, name, 0, REG_SZ, (BYTE*)&value, (wcslen(value)+1)*2);
297 if (res != ERROR_SUCCESS) {
Pierre Ossmanfb450fb2015-03-03 16:34:56 +0100298 vlog.error(_("Failed to write parameter %s of type %s to the registry: %ld"),
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100299 _name, "REG_SZ", res);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000300 return;
301 }
302}
303
304
Pierre Ossmanc0ea6422014-12-03 14:04:11 +0100305static void setKeyInt(const char *_name, const int _value, HKEY* hKey) {
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000306
307 const DWORD buffersize = 256;
308 wchar_t name[buffersize];
309 DWORD value = _value;
310
311 unsigned size = fl_utf8towc(_name, strlen(_name)+1, name, buffersize);
312 if (size >= buffersize) {
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100313 vlog.error(_("The name of the parameter %s was too large to write to the registry"), _name);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000314 return;
315 }
316
317 LONG res = RegSetValueExW(*hKey, name, 0, REG_DWORD, (BYTE*)&value, sizeof(DWORD));
318 if (res != ERROR_SUCCESS) {
Pierre Ossmanfb450fb2015-03-03 16:34:56 +0100319 vlog.error(_("Failed to write parameter %s of type %s to the registry: %ld"),
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100320 _name, "REG_DWORD", res);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000321 return;
322 }
323}
324
325
Pierre Ossmanc0ea6422014-12-03 14:04:11 +0100326static bool getKeyString(const char* _name, char* dest, size_t destSize, HKEY* hKey) {
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000327
Pierre Ossmanbeb59a42018-11-07 21:36:05 +0100328 const DWORD buffersize = 256;
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000329 wchar_t name[buffersize];
Pierre Ossmanbeb59a42018-11-07 21:36:05 +0100330 WCHAR* value;
331 DWORD valuesize;
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000332
333 unsigned size = fl_utf8towc(_name, strlen(_name)+1, name, buffersize);
334 if (size >= buffersize) {
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100335 vlog.error(_("The name of the parameter %s was too large to read from the registry"), _name);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000336 return false;
337 }
338
Pierre Ossmanbeb59a42018-11-07 21:36:05 +0100339 value = new WCHAR[destSize];
340 valuesize = destSize;
341 LONG res = RegQueryValueExW(*hKey, name, 0, NULL, (LPBYTE)value, &valuesize);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000342 if (res != ERROR_SUCCESS){
Pierre Ossmanbeb59a42018-11-07 21:36:05 +0100343 delete [] value;
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000344 if (res == ERROR_FILE_NOT_FOUND) {
345 // The value does not exist, defaults will be used.
346 } else {
Pierre Ossmanfb450fb2015-03-03 16:34:56 +0100347 vlog.error(_("Failed to read parameter %s from the registry: %ld"),
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100348 _name, res);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000349 }
350 return false;
351 }
352
Pierre Ossmanbeb59a42018-11-07 21:36:05 +0100353 char* utf8val = new char[destSize];
354 size = fl_utf8fromwc(utf8val, destSize, value, wcslen(value)+1);
355 delete [] value;
356 if (size >= destSize) {
357 delete [] utf8val;
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100358 vlog.error(_("The parameter %s was too large to read from the registry"), _name);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000359 return false;
360 }
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000361
Pierre Ossmanbeb59a42018-11-07 21:36:05 +0100362 bool ret = decodeValue(utf8val, dest, destSize);
363 delete [] utf8val;
364
365 return ret;
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000366}
367
368
Pierre Ossmanc0ea6422014-12-03 14:04:11 +0100369static bool getKeyInt(const char* _name, int* dest, HKEY* hKey) {
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000370
371 const DWORD buffersize = 256;
372 DWORD dwordsize = sizeof(DWORD);
373 DWORD value = 0;
374 wchar_t name[buffersize];
375
376 unsigned size = fl_utf8towc(_name, strlen(_name)+1, name, buffersize);
377 if (size >= buffersize) {
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100378 vlog.error(_("The name of the parameter %s was too large to read from the registry"), _name);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000379 return false;
380 }
381
382 LONG res = RegQueryValueExW(*hKey, name, 0, NULL, (LPBYTE)&value, &dwordsize);
383 if (res != ERROR_SUCCESS){
384 if (res == ERROR_FILE_NOT_FOUND) {
385 // The value does not exist, defaults will be used.
386 } else {
Pierre Ossmanfb450fb2015-03-03 16:34:56 +0100387 vlog.error(_("Failed to read parameter %s from the registry: %ld"),
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100388 _name, res);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000389 }
390 return false;
391 }
392
393 *dest = (int)value;
394 return true;
395}
396
397
Pierre Ossmanc0ea6422014-12-03 14:04:11 +0100398static void saveToReg(const char* servername) {
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000399
400 HKEY hKey;
401
Pierre Ossmanffcffd32014-09-22 12:58:48 +0200402 LONG res = RegCreateKeyExW(HKEY_CURRENT_USER,
403 L"Software\\TigerVNC\\vncviewer", 0, NULL,
404 REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
405 &hKey, NULL);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000406 if (res != ERROR_SUCCESS) {
Pierre Ossmanfb450fb2015-03-03 16:34:56 +0100407 vlog.error(_("Failed to create registry key: %ld"), res);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000408 return;
409 }
410
411 setKeyString("ServerName", servername, &hKey);
412
Pierre Ossman5c23b9e2015-03-03 16:26:03 +0100413 for (size_t i = 0; i < sizeof(parameterArray)/sizeof(VoidParameter*); i++) {
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000414 if (dynamic_cast<StringParameter*>(parameterArray[i]) != NULL) {
415 setKeyString(parameterArray[i]->getName(), *(StringParameter*)parameterArray[i], &hKey);
416 } else if (dynamic_cast<IntParameter*>(parameterArray[i]) != NULL) {
417 setKeyInt(parameterArray[i]->getName(), (int)*(IntParameter*)parameterArray[i], &hKey);
418 } else if (dynamic_cast<BoolParameter*>(parameterArray[i]) != NULL) {
419 setKeyInt(parameterArray[i]->getName(), (int)*(BoolParameter*)parameterArray[i], &hKey);
420 } else {
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100421 vlog.error(_("Unknown parameter type for parameter %s"),
422 parameterArray[i]->getName());
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000423 }
424 }
425
426 res = RegCloseKey(hKey);
427 if (res != ERROR_SUCCESS) {
Pierre Ossmanfb450fb2015-03-03 16:34:56 +0100428 vlog.error(_("Failed to close registry key: %ld"), res);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000429 }
430}
431
432
Pierre Ossmanc0ea6422014-12-03 14:04:11 +0100433static char* loadFromReg() {
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000434
435 HKEY hKey;
436
Pierre Ossmanffcffd32014-09-22 12:58:48 +0200437 LONG res = RegOpenKeyExW(HKEY_CURRENT_USER,
438 L"Software\\TigerVNC\\vncviewer", 0,
439 KEY_READ, &hKey);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000440 if (res != ERROR_SUCCESS) {
441 if (res == ERROR_FILE_NOT_FOUND) {
442 // The key does not exist, defaults will be used.
443 } else {
Pierre Ossmanfb450fb2015-03-03 16:34:56 +0100444 vlog.error(_("Failed to open registry key: %ld"), res);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000445 }
446 return NULL;
447 }
448
449 const size_t buffersize = 256;
450 static char servername[buffersize];
451
452 char servernameBuffer[buffersize];
453 if (getKeyString("ServerName", servernameBuffer, buffersize, &hKey))
454 snprintf(servername, buffersize, "%s", servernameBuffer);
455
456 int intValue = 0;
457 char stringValue[buffersize];
458
Pierre Ossman5c23b9e2015-03-03 16:26:03 +0100459 for (size_t i = 0; i < sizeof(parameterArray)/sizeof(VoidParameter*); i++) {
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000460 if (dynamic_cast<StringParameter*>(parameterArray[i]) != NULL) {
461 if (getKeyString(parameterArray[i]->getName(), stringValue, buffersize, &hKey))
Pierre Ossmanffcffd32014-09-22 12:58:48 +0200462 parameterArray[i]->setParam(stringValue);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000463 } else if (dynamic_cast<IntParameter*>(parameterArray[i]) != NULL) {
464 if (getKeyInt(parameterArray[i]->getName(), &intValue, &hKey))
Pierre Ossmanffcffd32014-09-22 12:58:48 +0200465 ((IntParameter*)parameterArray[i])->setParam(intValue);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000466 } else if (dynamic_cast<BoolParameter*>(parameterArray[i]) != NULL) {
467 if (getKeyInt(parameterArray[i]->getName(), &intValue, &hKey))
Pierre Ossmanffcffd32014-09-22 12:58:48 +0200468 ((BoolParameter*)parameterArray[i])->setParam(intValue);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000469 } else {
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100470 vlog.error(_("Unknown parameter type for parameter %s"),
471 parameterArray[i]->getName());
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000472 }
473 }
474
475 res = RegCloseKey(hKey);
476 if (res != ERROR_SUCCESS){
Pierre Ossmanfb450fb2015-03-03 16:34:56 +0100477 vlog.error(_("Failed to close registry key: %ld"), res);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000478 }
479
480 return servername;
481}
482#endif // _WIN32
483
484
485void saveViewerParameters(const char *filename, const char *servername) {
486
487 const size_t buffersize = 256;
488 char filepath[PATH_MAX];
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000489 char encodingBuffer[buffersize];
490
491 // Write to the registry or a predefined file if no filename was specified.
492 if(filename == NULL) {
493
494#ifdef _WIN32
495 saveToReg(servername);
496 return;
497#endif
498
499 char* homeDir = NULL;
500 if (getvnchomedir(&homeDir) == -1) {
Pierre Ossman8ca4c1d2014-09-22 12:54:26 +0200501 vlog.error(_("Failed to write configuration file, can't obtain home "
502 "directory path."));
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000503 return;
504 }
505
506 snprintf(filepath, sizeof(filepath), "%sdefault.tigervnc", homeDir);
Jan Grulicha5720e52018-11-20 10:44:39 +0100507 free(homeDir);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000508 } else {
509 snprintf(filepath, sizeof(filepath), "%s", filename);
510 }
511
512 /* Write parameters to file */
513 FILE* f = fopen(filepath, "w+");
Pierre Ossmana7bbe9c2015-03-03 16:17:51 +0100514 if (!f)
515 throw Exception(_("Failed to write configuration file, can't open %s: %s"),
516 filepath, strerror(errno));
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000517
518 fprintf(f, "%s\r\n", IDENTIFIER_STRING);
519 fprintf(f, "\r\n");
520
521 if (encodeValue(servername, encodingBuffer, buffersize))
522 fprintf(f, "ServerName=%s\n", encodingBuffer);
523
Pierre Ossman5c23b9e2015-03-03 16:26:03 +0100524 for (size_t i = 0; i < sizeof(parameterArray)/sizeof(VoidParameter*); i++) {
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000525 if (dynamic_cast<StringParameter*>(parameterArray[i]) != NULL) {
526 if (encodeValue(*(StringParameter*)parameterArray[i], encodingBuffer, buffersize))
Pierre Ossmanffcffd32014-09-22 12:58:48 +0200527 fprintf(f, "%s=%s\n", ((StringParameter*)parameterArray[i])->getName(), encodingBuffer);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000528 } else if (dynamic_cast<IntParameter*>(parameterArray[i]) != NULL) {
529 fprintf(f, "%s=%d\n", ((IntParameter*)parameterArray[i])->getName(), (int)*(IntParameter*)parameterArray[i]);
530 } else if (dynamic_cast<BoolParameter*>(parameterArray[i]) != NULL) {
531 fprintf(f, "%s=%d\n", ((BoolParameter*)parameterArray[i])->getName(), (int)*(BoolParameter*)parameterArray[i]);
532 } else {
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100533 vlog.error(_("Unknown parameter type for parameter %s"),
534 parameterArray[i]->getName());
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000535 }
536 }
537 fclose(f);
538}
539
540
541char* loadViewerParameters(const char *filename) {
542
543 const size_t buffersize = 256;
544 char filepath[PATH_MAX];
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000545 char line[buffersize];
546 char decodingBuffer[buffersize];
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000547 static char servername[sizeof(line)];
548
Pierre Ossmancb99be52018-05-29 16:37:54 +0200549 memset(servername, '\0', sizeof(servername));
550
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000551 // Load from the registry or a predefined file if no filename was specified.
552 if(filename == NULL) {
553
554#ifdef _WIN32
555 return loadFromReg();
556#endif
557
558 char* homeDir = NULL;
559 if (getvnchomedir(&homeDir) == -1)
Pierre Ossman8ca4c1d2014-09-22 12:54:26 +0200560 throw Exception(_("Failed to read configuration file, "
561 "can't obtain home directory path."));
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000562
563 snprintf(filepath, sizeof(filepath), "%sdefault.tigervnc", homeDir);
Jan Grulicha5720e52018-11-20 10:44:39 +0100564 free(homeDir);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000565 } else {
566 snprintf(filepath, sizeof(filepath), "%s", filename);
567 }
568
569 /* Read parameters from file */
570 FILE* f = fopen(filepath, "r");
571 if (!f) {
572 if (!filename)
573 return NULL; // Use defaults.
Pierre Ossmana7bbe9c2015-03-03 16:17:51 +0100574 throw Exception(_("Failed to read configuration file, can't open %s: %s"),
575 filepath, strerror(errno));
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000576 }
577
578 int lineNr = 0;
579 while (!feof(f)) {
580
581 // Read the next line
582 lineNr++;
583 if (!fgets(line, sizeof(line), f)) {
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000584 if (feof(f))
Pierre Ossmanffcffd32014-09-22 12:58:48 +0200585 break;
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000586
Pierre Ossmana7bbe9c2015-03-03 16:17:51 +0100587 throw Exception(_("Failed to read line %d in file %s: %s"),
588 lineNr, filepath, strerror(errno));
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000589 }
Pierre Ossman3b865f12015-03-04 09:53:16 +0100590
591 if (strlen(line) == (sizeof(line) - 1))
592 throw Exception(_("Failed to read line %d in file %s: %s"),
593 lineNr, filepath, _("Line too long"));
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000594
595 // Make sure that the first line of the file has the file identifier string
596 if(lineNr == 1) {
Pierre Ossmana7bbe9c2015-03-03 16:17:51 +0100597 if(strncmp(line, IDENTIFIER_STRING, strlen(IDENTIFIER_STRING)) == 0)
Pierre Ossmanffcffd32014-09-22 12:58:48 +0200598 continue;
Pierre Ossmana7bbe9c2015-03-03 16:17:51 +0100599 else
600 throw Exception(_("Configuration file %s is in an invalid format"),
601 filepath);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000602 }
603
604 // Skip empty lines and comments
605 if ((line[0] == '\n') || (line[0] == '#') || (line[0] == '\r'))
606 continue;
607
608 int len = strlen(line);
609 if (line[len-1] == '\n') {
610 line[len-1] = '\0';
611 len--;
612 }
613
614 // Find the parameter value
615 char *value = strchr(line, '=');
616 if (value == NULL) {
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100617 vlog.error(_("Failed to read line %d in file %s: %s"),
618 lineNr, filepath, _("Invalid format"));
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000619 continue;
620 }
621 *value = '\0'; // line only contains the parameter name below.
622 value++;
623
624 bool invalidParameterName = true; // Will be set to false below if
625 // the line contains a valid name.
626
627 if (strcasecmp(line, "ServerName") == 0) {
628
629 if(!decodeValue(value, decodingBuffer, sizeof(decodingBuffer))) {
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100630 vlog.error(_("Failed to read line %d in file %s: %s"),
631 lineNr, filepath, _("Invalid format or too large value"));
Pierre Ossmanffcffd32014-09-22 12:58:48 +0200632 continue;
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000633 }
634 snprintf(servername, sizeof(decodingBuffer), "%s", decodingBuffer);
635 invalidParameterName = false;
636
637 } else {
638
639 // Find and set the correct parameter
Pierre Ossman5c23b9e2015-03-03 16:26:03 +0100640 for (size_t i = 0; i < sizeof(parameterArray)/sizeof(VoidParameter*); i++) {
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000641
Pierre Ossmanffcffd32014-09-22 12:58:48 +0200642 if (dynamic_cast<StringParameter*>(parameterArray[i]) != NULL) {
643 if (strcasecmp(line, ((StringParameter*)parameterArray[i])->getName()) == 0) {
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000644
Pierre Ossmanffcffd32014-09-22 12:58:48 +0200645 if(!decodeValue(value, decodingBuffer, sizeof(decodingBuffer))) {
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100646 vlog.error(_("Failed to read line %d in file %s: %s"),
647 lineNr, filepath, _("Invalid format or too large value"));
Pierre Ossmanffcffd32014-09-22 12:58:48 +0200648 continue;
649 }
650 ((StringParameter*)parameterArray[i])->setParam(decodingBuffer);
651 invalidParameterName = false;
652 }
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000653
Pierre Ossmanffcffd32014-09-22 12:58:48 +0200654 } else if (dynamic_cast<IntParameter*>(parameterArray[i]) != NULL) {
655 if (strcasecmp(line, ((IntParameter*)parameterArray[i])->getName()) == 0) {
656 ((IntParameter*)parameterArray[i])->setParam(atoi(value));
657 invalidParameterName = false;
658 }
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000659
Pierre Ossmanffcffd32014-09-22 12:58:48 +0200660 } else if (dynamic_cast<BoolParameter*>(parameterArray[i]) != NULL) {
661 if (strcasecmp(line, ((BoolParameter*)parameterArray[i])->getName()) == 0) {
662 ((BoolParameter*)parameterArray[i])->setParam(atoi(value));
663 invalidParameterName = false;
664 }
665
666 } else {
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100667 vlog.error(_("Unknown parameter type for parameter %s"),
668 parameterArray[i]->getName());
Pierre Ossmanffcffd32014-09-22 12:58:48 +0200669 }
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000670 }
671 }
672
673 if (invalidParameterName)
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100674 vlog.info(_("Unknown parameter %s on line %d in file %s"),
675 line, lineNr, filepath);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000676 }
677 fclose(f); f=0;
678
679 return servername;
680}