blob: bda7818d640e8174832be3aeddd51d8ce61cc5fc [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"
57 " successive pointer events", 0);
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
62StringParameter passwordFile("PasswordFile",
63 "Password file for VNC authentication", "");
64AliasParameter passwd("passwd", "Alias for PasswordFile", &passwordFile);
65
66BoolParameter autoSelect("AutoSelect",
67 "Auto select pixel format and encoding. "
68 "Default if PreferredEncoding and FullColor are not specified.",
69 true);
70BoolParameter fullColour("FullColor",
71 "Use full color", true);
72AliasParameter fullColourAlias("FullColour", "Alias for FullColor", &fullColour);
73IntParameter lowColourLevel("LowColorLevel",
74 "Color level to use on slow connections. "
75 "0 = Very Low (8 colors), 1 = Low (64 colors), "
76 "2 = Medium (256 colors)", 2);
77AliasParameter lowColourLevelAlias("LowColourLevel", "Alias for LowColorLevel", &lowColourLevel);
78StringParameter preferredEncoding("PreferredEncoding",
79 "Preferred encoding to use (Tight, ZRLE, Hextile or"
80 " Raw)", "Tight");
81BoolParameter customCompressLevel("CustomCompressLevel",
82 "Use custom compression level. "
83 "Default if CompressLevel is specified.", false);
84IntParameter compressLevel("CompressLevel",
DRCba7bc512011-08-17 02:30:34 +000085 "Use specified compression level 0 = Low, 6 = High",
Pierre Ossman701ad682011-11-20 15:39:17 +000086 2);
Pierre Ossman5156d5e2011-03-09 09:42:34 +000087BoolParameter noJpeg("NoJPEG",
88 "Disable lossy JPEG compression in Tight encoding.",
89 false);
90IntParameter qualityLevel("QualityLevel",
91 "JPEG quality level. 0 = Low, 9 = High",
92 8);
93
Peter Åstrand49b11572012-08-01 08:09:09 +000094BoolParameter maximize("Maximize", "Maximize viewer window", false);
Pierre Ossman2441e822012-07-10 11:11:23 +000095#ifdef HAVE_FLTK_FULLSCREEN
Pierre Ossman5156d5e2011-03-09 09:42:34 +000096BoolParameter fullScreen("FullScreen", "Full screen mode", false);
Pierre Ossmanaae38912012-07-13 11:22:55 +000097#ifdef HAVE_FLTK_FULLSCREEN_SCREENS
98BoolParameter fullScreenAllMonitors("FullScreenAllMonitors",
99 "Enable full screen over all monitors",
100 true);
101#endif // HAVE_FLTK_FULLSCREEN_SCREENS
Pierre Ossman2441e822012-07-10 11:11:23 +0000102#endif // HAVE_FLTK_FULLSCREEN
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);
127BoolParameter sendClipboard("SendClipboard",
128 "Send clipboard changes to the server", true);
129BoolParameter sendPrimary("SendPrimary",
130 "Send the primary selection and cut buffer to the "
131 "server as well as the clipboard selection",
132 true);
133
134StringParameter menuKey("MenuKey", "The key which brings up the popup menu",
135 "F8");
136
Pierre Ossman407a5c32011-05-26 14:48:29 +0000137BoolParameter fullscreenSystemKeys("FullscreenSystemKeys",
138 "Pass special keys (like Alt+Tab) directly "
139 "to the server when in full screen mode.",
140 true);
141
Adam Tkac8ac4b302013-01-23 13:55:46 +0000142#ifndef WIN32
143StringParameter via("via", "Gateway to tunnel via", "");
144#endif
145
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000146const char* IDENTIFIER_STRING = "TigerVNC Configuration file Version 1.0";
147
148VoidParameter* parameterArray[] = {
149#ifdef HAVE_GNUTLS
Pierre Ossman3d2a84b2014-09-17 16:45:35 +0200150 &CSecurityTLS::X509CA,
151 &CSecurityTLS::X509CRL,
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000152#endif // HAVE_GNUTLS
153 &SecurityClient::secTypes,
154 &dotWhenNoCursor,
155 &autoSelect,
156 &fullColour,
157 &lowColourLevel,
158 &preferredEncoding,
159 &customCompressLevel,
160 &compressLevel,
161 &noJpeg,
162 &qualityLevel,
163#ifdef HAVE_FLTK_FULLSCREEN
164 &fullScreen,
165#ifdef HAVE_FLTK_FULLSCREEN_SCREENS
166 &fullScreenAllMonitors,
167#endif // HAVE_FLTK_FULLSCREEN_SCREENS
168#endif // HAVE_FLTK_FULLSCREEN
169 &desktopSize,
Peter Åstrandb182a9e2012-08-27 07:28:08 +0000170 &geometry,
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000171 &remoteResize,
172 &viewOnly,
173 &shared,
174 &acceptClipboard,
175 &sendClipboard,
176 &sendPrimary,
177 &menuKey,
178 &fullscreenSystemKeys
179};
180
181// Encoding Table
182static struct {
183 const char first;
184 const char second;
185} replaceMap[] = {'\n', 'n',
186 '\r', 'r'};
187
188bool encodeValue(const char* val, char* dest, size_t destSize) {
189
190 bool normalCharacter = true;
191 size_t pos = 0;
192
193 for (int i = 0; (val[i] != '\0') && (i < (destSize - 1)); i++) {
194
195 // Check for sequences which will need encoding
196 if (val[i] == '\\') {
197
198 strncpy(dest+pos, "\\\\", 2);
199 pos++;
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100200 if (pos >= destSize)
Pierre Ossmanffcffd32014-09-22 12:58:48 +0200201 return false;
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000202
203 } else {
204
Pierre Ossmanffcffd32014-09-22 12:58:48 +0200205 for (int j = 0; j < sizeof(replaceMap)/sizeof(replaceMap[0]); j++) {
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000206
Pierre Ossmanffcffd32014-09-22 12:58:48 +0200207 if (val[i] == replaceMap[j].first) {
208 dest[pos] = '\\';
209 pos++;
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100210 if (pos >= destSize)
Pierre Ossmanffcffd32014-09-22 12:58:48 +0200211 return false;
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000212
Pierre Ossmanffcffd32014-09-22 12:58:48 +0200213 dest[pos] = replaceMap[j].second;
214 normalCharacter = false;
215 break;
216 }
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000217
Pierre Ossmanffcffd32014-09-22 12:58:48 +0200218 if (normalCharacter) {
219 dest[pos] = val[i];
220 }
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000221 }
222 }
223 normalCharacter = true; // Reset for next loop
224
225 pos++;
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100226 if (pos >= destSize)
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000227 return false;
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000228 }
229
230 dest[pos] = '\0';
231 return true;
232}
233
234
235bool decodeValue(const char* val, char* dest, size_t destSize) {
236
237 size_t pos = 0;
238 bool escapedCharacter = false;
239
240 for (int i = 0; (val[i] != '\0') && (i < (destSize - 1)); i++) {
241
242 // Check for escape sequences
243 if (val[i] == '\\') {
244
245 for (int j = 0; j < sizeof(replaceMap)/sizeof(replaceMap[0]); j++) {
Pierre Ossmanffcffd32014-09-22 12:58:48 +0200246 if (val[i+1] == replaceMap[j].second) {
247 dest[pos] = replaceMap[j].first;
248 escapedCharacter = true;
249 pos--;
250 break;
251 }
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000252 }
253
254 if (!escapedCharacter) {
Pierre Ossmanffcffd32014-09-22 12:58:48 +0200255 if (val[i+1] == '\\') {
256 dest[pos] = val[i];
257 i++;
258 } else {
Pierre Ossmanffcffd32014-09-22 12:58:48 +0200259 return false;
260 }
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000261 }
262
263 } else {
264 dest[pos] = val[i];
265 }
266
267 escapedCharacter = false; // Reset for next loop
268 pos++;
269 if (pos >= destSize) {
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000270 return false;
271 }
272 }
273
274 dest[pos] = '\0';
275 return true;
276}
277
278
279#ifdef _WIN32
280void setKeyString(const char *_name, const char *_value, HKEY* hKey) {
281
282 const DWORD buffersize = 256;
283
284 wchar_t name[buffersize];
285 unsigned size = fl_utf8towc(_name, strlen(_name)+1, name, buffersize);
286 if (size >= buffersize) {
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100287 vlog.error(_("The name of the parameter %s was too large to write to the registry"), _name);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000288 return;
289 }
290
291 char encodingBuffer[buffersize];
292 if (!encodeValue(_value, encodingBuffer, buffersize)) {
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100293 vlog.error(_("The parameter %s was too large to write to the registry"), _name);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000294 return;
295 }
296
297 wchar_t value[buffersize];
298 size = fl_utf8towc(encodingBuffer, strlen(encodingBuffer)+1, value, buffersize);
299 if (size >= buffersize) {
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100300 vlog.error(_("The parameter %s was too large to write to the registry"), _name);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000301 return;
302 }
303
304 LONG res = RegSetValueExW(*hKey, name, 0, REG_SZ, (BYTE*)&value, (wcslen(value)+1)*2);
305 if (res != ERROR_SUCCESS) {
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100306 vlog.error(_("Failed to write parameter %s of type %s to the registry: %d"),
307 _name, "REG_SZ", res);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000308 return;
309 }
310}
311
312
313void setKeyInt(const char *_name, const int _value, HKEY* hKey) {
314
315 const DWORD buffersize = 256;
316 wchar_t name[buffersize];
317 DWORD value = _value;
318
319 unsigned size = fl_utf8towc(_name, strlen(_name)+1, name, buffersize);
320 if (size >= buffersize) {
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100321 vlog.error(_("The name of the parameter %s was too large to write to the registry"), _name);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000322 return;
323 }
324
325 LONG res = RegSetValueExW(*hKey, name, 0, REG_DWORD, (BYTE*)&value, sizeof(DWORD));
326 if (res != ERROR_SUCCESS) {
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100327 vlog.error(_("Failed to write parameter %s of type %s to the registry: %d"),
328 _name, "REG_DWORD", res);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000329 return;
330 }
331}
332
333
334bool getKeyString(const char* _name, char* dest, size_t destSize, HKEY* hKey) {
335
336 DWORD buffersize = 256;
337 WCHAR value[destSize];
338 wchar_t name[buffersize];
339
340 unsigned size = fl_utf8towc(_name, strlen(_name)+1, name, buffersize);
341 if (size >= buffersize) {
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100342 vlog.error(_("The name of the parameter %s was too large to read from the registry"), _name);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000343 return false;
344 }
345
346 LONG res = RegQueryValueExW(*hKey, name, 0, NULL, (LPBYTE)value, &buffersize);
347 if (res != ERROR_SUCCESS){
348 if (res == ERROR_FILE_NOT_FOUND) {
349 // The value does not exist, defaults will be used.
350 } else {
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100351 vlog.error(_("Failed to read parameter %s from the registry: %d"),
352 _name, res);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000353 }
354 return false;
355 }
356
357 char utf8val[destSize];
358 size = fl_utf8fromwc(utf8val, sizeof(utf8val), value, wcslen(value)+1);
359 if (size >= sizeof(utf8val)) {
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100360 vlog.error(_("The parameter %s was too large to read from the registry"), _name);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000361 return false;
362 }
363 const char *ret = utf8val;
364
365 if(decodeValue(ret, dest, destSize))
366 return true;
367 else
368 return false;
369}
370
371
372bool getKeyInt(const char* _name, int* dest, HKEY* hKey) {
373
374 const DWORD buffersize = 256;
375 DWORD dwordsize = sizeof(DWORD);
376 DWORD value = 0;
377 wchar_t name[buffersize];
378
379 unsigned size = fl_utf8towc(_name, strlen(_name)+1, name, buffersize);
380 if (size >= buffersize) {
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100381 vlog.error(_("The name of the parameter %s was too large to read from the registry"), _name);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000382 return false;
383 }
384
385 LONG res = RegQueryValueExW(*hKey, name, 0, NULL, (LPBYTE)&value, &dwordsize);
386 if (res != ERROR_SUCCESS){
387 if (res == ERROR_FILE_NOT_FOUND) {
388 // The value does not exist, defaults will be used.
389 } else {
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100390 vlog.error(_("Failed to read parameter %s from the registry: %d"),
391 _name, res);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000392 }
393 return false;
394 }
395
396 *dest = (int)value;
397 return true;
398}
399
400
401void saveToReg(const char* servername) {
402
403 HKEY hKey;
404
Pierre Ossmanffcffd32014-09-22 12:58:48 +0200405 LONG res = RegCreateKeyExW(HKEY_CURRENT_USER,
406 L"Software\\TigerVNC\\vncviewer", 0, NULL,
407 REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
408 &hKey, NULL);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000409 if (res != ERROR_SUCCESS) {
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100410 vlog.error(_("Failed to create registry key: %d"), res);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000411 return;
412 }
413
414 setKeyString("ServerName", servername, &hKey);
415
416 for (int i = 0; i < sizeof(parameterArray)/sizeof(VoidParameter*); i++) {
417 if (dynamic_cast<StringParameter*>(parameterArray[i]) != NULL) {
418 setKeyString(parameterArray[i]->getName(), *(StringParameter*)parameterArray[i], &hKey);
419 } else if (dynamic_cast<IntParameter*>(parameterArray[i]) != NULL) {
420 setKeyInt(parameterArray[i]->getName(), (int)*(IntParameter*)parameterArray[i], &hKey);
421 } else if (dynamic_cast<BoolParameter*>(parameterArray[i]) != NULL) {
422 setKeyInt(parameterArray[i]->getName(), (int)*(BoolParameter*)parameterArray[i], &hKey);
423 } else {
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100424 vlog.error(_("Unknown parameter type for parameter %s"),
425 parameterArray[i]->getName());
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000426 }
427 }
428
429 res = RegCloseKey(hKey);
430 if (res != ERROR_SUCCESS) {
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100431 vlog.error(_("Failed to close registry key: %d"), res);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000432 }
433}
434
435
436char* loadFromReg() {
437
438 HKEY hKey;
439
Pierre Ossmanffcffd32014-09-22 12:58:48 +0200440 LONG res = RegOpenKeyExW(HKEY_CURRENT_USER,
441 L"Software\\TigerVNC\\vncviewer", 0,
442 KEY_READ, &hKey);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000443 if (res != ERROR_SUCCESS) {
444 if (res == ERROR_FILE_NOT_FOUND) {
445 // The key does not exist, defaults will be used.
446 } else {
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100447 vlog.error(_("Failed to open registry key: %d"), res);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000448 }
449 return NULL;
450 }
451
452 const size_t buffersize = 256;
453 static char servername[buffersize];
454
455 char servernameBuffer[buffersize];
456 if (getKeyString("ServerName", servernameBuffer, buffersize, &hKey))
457 snprintf(servername, buffersize, "%s", servernameBuffer);
458
459 int intValue = 0;
460 char stringValue[buffersize];
461
462 for (int i = 0; i < sizeof(parameterArray)/sizeof(VoidParameter*); i++) {
463 if (dynamic_cast<StringParameter*>(parameterArray[i]) != NULL) {
464 if (getKeyString(parameterArray[i]->getName(), stringValue, buffersize, &hKey))
Pierre Ossmanffcffd32014-09-22 12:58:48 +0200465 parameterArray[i]->setParam(stringValue);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000466 } else if (dynamic_cast<IntParameter*>(parameterArray[i]) != NULL) {
467 if (getKeyInt(parameterArray[i]->getName(), &intValue, &hKey))
Pierre Ossmanffcffd32014-09-22 12:58:48 +0200468 ((IntParameter*)parameterArray[i])->setParam(intValue);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000469 } else if (dynamic_cast<BoolParameter*>(parameterArray[i]) != NULL) {
470 if (getKeyInt(parameterArray[i]->getName(), &intValue, &hKey))
Pierre Ossmanffcffd32014-09-22 12:58:48 +0200471 ((BoolParameter*)parameterArray[i])->setParam(intValue);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000472 } else {
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100473 vlog.error(_("Unknown parameter type for parameter %s"),
474 parameterArray[i]->getName());
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000475 }
476 }
477
478 res = RegCloseKey(hKey);
479 if (res != ERROR_SUCCESS){
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100480 vlog.error(_("Failed to close registry key: %d"), res);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000481 }
482
483 return servername;
484}
485#endif // _WIN32
486
487
488void saveViewerParameters(const char *filename, const char *servername) {
489
490 const size_t buffersize = 256;
491 char filepath[PATH_MAX];
492 char write_error[buffersize*2];
493 char encodingBuffer[buffersize];
494
495 // Write to the registry or a predefined file if no filename was specified.
496 if(filename == NULL) {
497
498#ifdef _WIN32
499 saveToReg(servername);
500 return;
501#endif
502
503 char* homeDir = NULL;
504 if (getvnchomedir(&homeDir) == -1) {
Pierre Ossman8ca4c1d2014-09-22 12:54:26 +0200505 vlog.error(_("Failed to write configuration file, can't obtain home "
506 "directory path."));
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000507 return;
508 }
509
510 snprintf(filepath, sizeof(filepath), "%sdefault.tigervnc", homeDir);
511 } else {
512 snprintf(filepath, sizeof(filepath), "%s", filename);
513 }
514
515 /* Write parameters to file */
516 FILE* f = fopen(filepath, "w+");
517 if (!f) {
Pierre Ossman74d412c2013-07-01 13:59:35 +0000518 snprintf(write_error, sizeof(write_error),
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100519 _("Failed to write configuration file, can't open %s: %s"),
520 filepath, strerror(errno));
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000521 throw Exception(write_error);
522 }
523
524 fprintf(f, "%s\r\n", IDENTIFIER_STRING);
525 fprintf(f, "\r\n");
526
527 if (encodeValue(servername, encodingBuffer, buffersize))
528 fprintf(f, "ServerName=%s\n", encodingBuffer);
529
530 for (int i = 0; i < sizeof(parameterArray)/sizeof(VoidParameter*); i++) {
531 if (dynamic_cast<StringParameter*>(parameterArray[i]) != NULL) {
532 if (encodeValue(*(StringParameter*)parameterArray[i], encodingBuffer, buffersize))
Pierre Ossmanffcffd32014-09-22 12:58:48 +0200533 fprintf(f, "%s=%s\n", ((StringParameter*)parameterArray[i])->getName(), encodingBuffer);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000534 } else if (dynamic_cast<IntParameter*>(parameterArray[i]) != NULL) {
535 fprintf(f, "%s=%d\n", ((IntParameter*)parameterArray[i])->getName(), (int)*(IntParameter*)parameterArray[i]);
536 } else if (dynamic_cast<BoolParameter*>(parameterArray[i]) != NULL) {
537 fprintf(f, "%s=%d\n", ((BoolParameter*)parameterArray[i])->getName(), (int)*(BoolParameter*)parameterArray[i]);
538 } else {
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100539 vlog.error(_("Unknown parameter type for parameter %s"),
540 parameterArray[i]->getName());
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000541 }
542 }
543 fclose(f);
544}
545
546
547char* loadViewerParameters(const char *filename) {
548
549 const size_t buffersize = 256;
550 char filepath[PATH_MAX];
551 char readError[buffersize*2];
552 char line[buffersize];
553 char decodingBuffer[buffersize];
554 char decodedValue[buffersize];
555 static char servername[sizeof(line)];
556
557 // Load from the registry or a predefined file if no filename was specified.
558 if(filename == NULL) {
559
560#ifdef _WIN32
561 return loadFromReg();
562#endif
563
564 char* homeDir = NULL;
565 if (getvnchomedir(&homeDir) == -1)
Pierre Ossman8ca4c1d2014-09-22 12:54:26 +0200566 throw Exception(_("Failed to read configuration file, "
567 "can't obtain home directory path."));
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000568
569 snprintf(filepath, sizeof(filepath), "%sdefault.tigervnc", homeDir);
570 } else {
571 snprintf(filepath, sizeof(filepath), "%s", filename);
572 }
573
574 /* Read parameters from file */
575 FILE* f = fopen(filepath, "r");
576 if (!f) {
577 if (!filename)
578 return NULL; // Use defaults.
Pierre Ossman8ca4c1d2014-09-22 12:54:26 +0200579 snprintf(readError, sizeof(readError),
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100580 _("Failed to read configuration file, can't open %s: %s"),
581 filepath, strerror(errno));
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000582 throw Exception(readError);
583 }
584
585 int lineNr = 0;
586 while (!feof(f)) {
587
588 // Read the next line
589 lineNr++;
590 if (!fgets(line, sizeof(line), f)) {
591 if (line[sizeof(line) -1] != '\0') {
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100592 snprintf(readError, sizeof(readError),
593 _("Failed to read line %d in file %s: %s"),
594 lineNr, filepath, _("Line too long"));
595 throw Exception(readError);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000596 }
597 if (feof(f))
Pierre Ossmanffcffd32014-09-22 12:58:48 +0200598 break;
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000599
Pierre Ossman8ca4c1d2014-09-22 12:54:26 +0200600 snprintf(readError, sizeof(readError),
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100601 _("Failed to read line %d in file %s: %s"),
602 lineNr, filepath, strerror(errno));
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000603 throw Exception(readError);
604 }
605
606 // Make sure that the first line of the file has the file identifier string
607 if(lineNr == 1) {
608 if(strncmp(line, IDENTIFIER_STRING, strlen(IDENTIFIER_STRING)) == 0) {
Pierre Ossmanffcffd32014-09-22 12:58:48 +0200609 continue;
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000610 } else {
Pierre Ossman8ca4c1d2014-09-22 12:54:26 +0200611 snprintf(readError, sizeof(readError),
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100612 _("Configuration file %s is in an invalid format"),
613 filepath);
Pierre Ossmanffcffd32014-09-22 12:58:48 +0200614 throw Exception(readError);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000615 }
616 }
617
618 // Skip empty lines and comments
619 if ((line[0] == '\n') || (line[0] == '#') || (line[0] == '\r'))
620 continue;
621
622 int len = strlen(line);
623 if (line[len-1] == '\n') {
624 line[len-1] = '\0';
625 len--;
626 }
627
628 // Find the parameter value
629 char *value = strchr(line, '=');
630 if (value == NULL) {
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100631 vlog.error(_("Failed to read line %d in file %s: %s"),
632 lineNr, filepath, _("Invalid format"));
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000633 continue;
634 }
635 *value = '\0'; // line only contains the parameter name below.
636 value++;
637
638 bool invalidParameterName = true; // Will be set to false below if
639 // the line contains a valid name.
640
641 if (strcasecmp(line, "ServerName") == 0) {
642
643 if(!decodeValue(value, decodingBuffer, sizeof(decodingBuffer))) {
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100644 vlog.error(_("Failed to read line %d in file %s: %s"),
645 lineNr, filepath, _("Invalid format or too large value"));
Pierre Ossmanffcffd32014-09-22 12:58:48 +0200646 continue;
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000647 }
648 snprintf(servername, sizeof(decodingBuffer), "%s", decodingBuffer);
649 invalidParameterName = false;
650
651 } else {
652
653 // Find and set the correct parameter
654 for (int i = 0; i < sizeof(parameterArray)/sizeof(VoidParameter*); i++) {
655
Pierre Ossmanffcffd32014-09-22 12:58:48 +0200656 if (dynamic_cast<StringParameter*>(parameterArray[i]) != NULL) {
657 if (strcasecmp(line, ((StringParameter*)parameterArray[i])->getName()) == 0) {
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000658
Pierre Ossmanffcffd32014-09-22 12:58:48 +0200659 if(!decodeValue(value, decodingBuffer, sizeof(decodingBuffer))) {
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100660 vlog.error(_("Failed to read line %d in file %s: %s"),
661 lineNr, filepath, _("Invalid format or too large value"));
Pierre Ossmanffcffd32014-09-22 12:58:48 +0200662 continue;
663 }
664 ((StringParameter*)parameterArray[i])->setParam(decodingBuffer);
665 invalidParameterName = false;
666 }
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000667
Pierre Ossmanffcffd32014-09-22 12:58:48 +0200668 } else if (dynamic_cast<IntParameter*>(parameterArray[i]) != NULL) {
669 if (strcasecmp(line, ((IntParameter*)parameterArray[i])->getName()) == 0) {
670 ((IntParameter*)parameterArray[i])->setParam(atoi(value));
671 invalidParameterName = false;
672 }
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000673
Pierre Ossmanffcffd32014-09-22 12:58:48 +0200674 } else if (dynamic_cast<BoolParameter*>(parameterArray[i]) != NULL) {
675 if (strcasecmp(line, ((BoolParameter*)parameterArray[i])->getName()) == 0) {
676 ((BoolParameter*)parameterArray[i])->setParam(atoi(value));
677 invalidParameterName = false;
678 }
679
680 } else {
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100681 vlog.error(_("Unknown parameter type for parameter %s"),
682 parameterArray[i]->getName());
Pierre Ossmanffcffd32014-09-22 12:58:48 +0200683 }
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000684 }
685 }
686
687 if (invalidParameterName)
Pierre Ossman1bfbcb52014-12-03 14:03:29 +0100688 vlog.info(_("Unknown parameter %s on line %d in file %s"),
689 line, lineNr, filepath);
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000690 }
691 fclose(f); f=0;
692
693 return servername;
694}