1 /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
2 * Copyright 2011 Pierre Ossman <ossman@cendio.se> for Cendio AB
3 * Copyright 2012 Samuel Mannehed <samuel@cendio.se> for Cendio AB
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 #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
34 #include "parameters.h"
35
36 #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>
46 #include <errno.h>
47 #include <assert.h>
48
49 #include "i18n.h"
50
51 using namespace rfb;
52 using namespace std;
53
54 static LogWriter vlog("Parameters");
55
56
57 IntParameter pointerEventInterval("PointerEventInterval",
58 "Time in milliseconds to rate-limit"
59 " successive pointer events", 17);
60 BoolParameter emulateMiddleButton("EmulateMiddleButton",
61 "Emulate middle mouse button by pressing "
62 "left and right mouse buttons simultaneously",
63 false);
64 BoolParameter dotWhenNoCursor("DotWhenNoCursor",
65 "Show the dot cursor when the server sends an "
66 "invisible cursor", false);
67
68 BoolParameter alertOnFatalError("AlertOnFatalError",
69 "Give a dialog on connection problems rather "
70 "than exiting immediately", true);
71
72 BoolParameter reconnectOnError("ReconnectOnError",
73 "Give a dialog on connection problems rather "
74 "than exiting immediately and ask for a reconnect.", true);
75
76 StringParameter passwordFile("PasswordFile",
77 "Password file for VNC authentication", "");
78 AliasParameter passwd("passwd", "Alias for PasswordFile", &passwordFile);
79
80 BoolParameter autoSelect("AutoSelect",
81 "Auto select pixel format and encoding. "
82 "Default if PreferredEncoding and FullColor are not specified.",
83 true);
84 BoolParameter fullColour("FullColor",
85 "Use full color", true);
86 AliasParameter fullColourAlias("FullColour", "Alias for FullColor", &fullColour);
87 IntParameter lowColourLevel("LowColorLevel",
88 "Color level to use on slow connections. "
89 "0 = Very Low, 1 = Low, 2 = Medium", 2);
90 AliasParameter lowColourLevelAlias("LowColourLevel", "Alias for LowColorLevel", &lowColourLevel);
91 StringParameter preferredEncoding("PreferredEncoding",
92 "Preferred encoding to use (Tight, ZRLE, Hextile or"
93 " Raw)", "Tight");
94 BoolParameter customCompressLevel("CustomCompressLevel",
95 "Use custom compression level. "
96 "Default if CompressLevel is specified.", false);
97 IntParameter compressLevel("CompressLevel",
98 "Use specified compression level 0 = Low, 9 = High",
99 2);
100 BoolParameter noJpeg("NoJPEG",
101 "Disable lossy JPEG compression in Tight encoding.",
102 false);
103 IntParameter qualityLevel("QualityLevel",
104 "JPEG quality level. 0 = Low, 9 = High",
105 8);
106
107 BoolParameter maximize("Maximize", "Maximize viewer window", false);
108 BoolParameter fullScreen("FullScreen", "Enable full screen", false);
109 StringParameter fullScreenMode("FullScreenMode", "Specify which monitors to use when in full screen. "
110 "Should be either Current, Selected or All",
111 "Current");
112 BoolParameter fullScreenAllMonitors("FullScreenAllMonitors",
113 "[DEPRECATED] Enable full screen over all monitors",
114 false);
115 MonitorIndicesParameter fullScreenSelectedMonitors("FullScreenSelectedMonitors",
116 "Use the given list of monitors in full screen"
117 " when -FullScreenMode=Selected.",
118 "1");
119 StringParameter desktopSize("DesktopSize",
120 "Reconfigure desktop size on the server on "
121 "connect (if possible)", "");
122 StringParameter geometry("geometry",
123 "Specify size and position of viewer window", "");
124
125 BoolParameter listenMode("listen", "Listen for connections from VNC servers", false);
126
127 BoolParameter remoteResize("RemoteResize",
128 "Dynamically resize the remote desktop size as "
129 "the size of the local client window changes. "
130 "(Does not work with all servers)", true);
131
132 BoolParameter viewOnly("ViewOnly",
133 "Don't send any mouse or keyboard events to the server",
134 false);
135 BoolParameter shared("Shared",
136 "Don't disconnect other viewers upon connection - "
137 "share the desktop instead",
138 false);
139
140 BoolParameter acceptClipboard("AcceptClipboard",
141 "Accept clipboard changes from the server",
142 true);
143 BoolParameter sendClipboard("SendClipboard",
144 "Send clipboard changes to the server", true);
145 #if !defined(WIN32) && !defined(__APPLE__)
146 BoolParameter setPrimary("SetPrimary",
147 "Set the primary selection as well as the "
148 "clipboard selection", true);
149 BoolParameter sendPrimary("SendPrimary",
150 "Send the primary selection to the "
151 "server as well as the clipboard selection",
152 true);
153 StringParameter display("display",
154 "Specifies the X display on which the VNC viewer window should appear.",
155 "");
156 #endif
157
158 StringParameter menuKey("MenuKey", "The key which brings up the popup menu",
159 "F8");
160
161 BoolParameter fullscreenSystemKeys("FullscreenSystemKeys",
162 "Pass special keys (like Alt+Tab) directly "
163 "to the server when in full screen mode.",
164 true);
165
166 #ifndef WIN32
167 StringParameter via("via", "Gateway to tunnel via", "");
168 #endif
169
170 static const char* IDENTIFIER_STRING = "TigerVNC Configuration file Version 1.0";
171
172 static VoidParameter* parameterArray[] = {
173 #ifdef HAVE_GNUTLS
174 &CSecurityTLS::X509CA,
175 &CSecurityTLS::X509CRL,
176 #endif // HAVE_GNUTLS
177 &SecurityClient::secTypes,
178 &emulateMiddleButton,
179 &dotWhenNoCursor,
180 &reconnectOnError,
181 &autoSelect,
182 &fullColour,
183 &lowColourLevel,
184 &preferredEncoding,
185 &customCompressLevel,
186 &compressLevel,
187 &noJpeg,
188 &qualityLevel,
189 &fullScreen,
190 &fullScreenMode,
191 &fullScreenSelectedMonitors,
192 &desktopSize,
193 &remoteResize,
194 &viewOnly,
195 &shared,
196 &acceptClipboard,
197 &sendClipboard,
198 #if !defined(WIN32) && !defined(__APPLE__)
199 &sendPrimary,
200 &setPrimary,
201 #endif
202 &menuKey,
203 &fullscreenSystemKeys
204 };
205
206 static VoidParameter* readOnlyParameterArray[] = {
207 &fullScreenAllMonitors
208 };
209
210 // Encoding Table
211 static struct {
212 const char first;
213 const char second;
214 } replaceMap[] = { { '\n', 'n' },
215 { '\r', 'r' },
216 { '\\', '\\' } };
217
encodeValue(const char * val,char * dest,size_t destSize)218 static bool encodeValue(const char* val, char* dest, size_t destSize) {
219
220 size_t pos = 0;
221
222 for (size_t i = 0; (val[i] != '\0') && (i < (destSize - 1)); i++) {
223 bool normalCharacter;
224
225 // Check for sequences which will need encoding
226 normalCharacter = true;
227 for (size_t j = 0; j < sizeof(replaceMap)/sizeof(replaceMap[0]); j++) {
228
229 if (val[i] == replaceMap[j].first) {
230 dest[pos] = '\\';
231 pos++;
232 if (pos >= destSize)
233 return false;
234
235 dest[pos] = replaceMap[j].second;
236 normalCharacter = false;
237 break;
238 }
239
240 if (normalCharacter) {
241 dest[pos] = val[i];
242 }
243 }
244
245 pos++;
246 if (pos >= destSize)
247 return false;
248 }
249
250 dest[pos] = '\0';
251 return true;
252 }
253
254
decodeValue(const char * val,char * dest,size_t destSize)255 static bool decodeValue(const char* val, char* dest, size_t destSize) {
256
257 size_t pos = 0;
258
259 for (size_t i = 0; (val[i] != '\0') && (i < (destSize - 1)); i++) {
260
261 // Check for escape sequences
262 if (val[i] == '\\') {
263 bool escapedCharacter;
264
265 escapedCharacter = false;
266 for (size_t j = 0; j < sizeof(replaceMap)/sizeof(replaceMap[0]); j++) {
267 if (val[i+1] == replaceMap[j].second) {
268 dest[pos] = replaceMap[j].first;
269 escapedCharacter = true;
270 i++;
271 break;
272 }
273 }
274
275 if (!escapedCharacter)
276 return false;
277
278 } else {
279 dest[pos] = val[i];
280 }
281
282 pos++;
283 if (pos >= destSize) {
284 return false;
285 }
286 }
287
288 dest[pos] = '\0';
289 return true;
290 }
291
292
293 #ifdef _WIN32
setKeyString(const char * _name,const char * _value,HKEY * hKey)294 static void setKeyString(const char *_name, const char *_value, HKEY* hKey) {
295
296 const DWORD buffersize = 256;
297
298 wchar_t name[buffersize];
299 unsigned size = fl_utf8towc(_name, strlen(_name)+1, name, buffersize);
300 if (size >= buffersize)
301 throw Exception(_("The name of the parameter is too large"));
302
303 char encodingBuffer[buffersize];
304 if (!encodeValue(_value, encodingBuffer, buffersize))
305 throw Exception(_("The parameter is too large"));
306
307 wchar_t value[buffersize];
308 size = fl_utf8towc(encodingBuffer, strlen(encodingBuffer)+1, value, buffersize);
309 if (size >= buffersize)
310 throw Exception(_("The parameter is too large"));
311
312 LONG res = RegSetValueExW(*hKey, name, 0, REG_SZ, (BYTE*)&value, (wcslen(value)+1)*2);
313 if (res != ERROR_SUCCESS)
314 throw rdr::SystemException("RegSetValueExW", res);
315 }
316
317
setKeyInt(const char * _name,const int _value,HKEY * hKey)318 static void setKeyInt(const char *_name, const int _value, HKEY* hKey) {
319
320 const DWORD buffersize = 256;
321 wchar_t name[buffersize];
322 DWORD value = _value;
323
324 unsigned size = fl_utf8towc(_name, strlen(_name)+1, name, buffersize);
325 if (size >= buffersize)
326 throw Exception(_("The name of the parameter is too large"));
327
328 LONG res = RegSetValueExW(*hKey, name, 0, REG_DWORD, (BYTE*)&value, sizeof(DWORD));
329 if (res != ERROR_SUCCESS)
330 throw rdr::SystemException("RegSetValueExW", res);
331 }
332
333
getKeyString(const char * _name,char * dest,size_t destSize,HKEY * hKey)334 static bool getKeyString(const char* _name, char* dest, size_t destSize, HKEY* hKey) {
335
336 const DWORD buffersize = 256;
337 wchar_t name[buffersize];
338 WCHAR* value;
339 DWORD valuesize;
340
341 unsigned size = fl_utf8towc(_name, strlen(_name)+1, name, buffersize);
342 if (size >= buffersize)
343 throw Exception(_("The name of the parameter is too large"));
344
345 value = new WCHAR[destSize];
346 valuesize = destSize;
347 LONG res = RegQueryValueExW(*hKey, name, 0, NULL, (LPBYTE)value, &valuesize);
348 if (res != ERROR_SUCCESS){
349 delete [] value;
350 if (res != ERROR_FILE_NOT_FOUND)
351 throw rdr::SystemException("RegQueryValueExW", res);
352 // The value does not exist, defaults will be used.
353 return false;
354 }
355
356 char* utf8val = new char[destSize];
357 size = fl_utf8fromwc(utf8val, destSize, value, wcslen(value)+1);
358 delete [] value;
359 if (size >= destSize) {
360 delete [] utf8val;
361 throw Exception(_("The parameter is too large"));
362 }
363
364 bool ret = decodeValue(utf8val, dest, destSize);
365 delete [] utf8val;
366
367 if (!ret)
368 throw Exception(_("Invalid format or too large value"));
369
370 return true;
371 }
372
373
getKeyInt(const char * _name,int * dest,HKEY * hKey)374 static bool getKeyInt(const char* _name, int* dest, HKEY* hKey) {
375
376 const DWORD buffersize = 256;
377 DWORD dwordsize = sizeof(DWORD);
378 DWORD value = 0;
379 wchar_t name[buffersize];
380
381 unsigned size = fl_utf8towc(_name, strlen(_name)+1, name, buffersize);
382 if (size >= buffersize)
383 throw Exception(_("The name of the parameter is too large"));
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 throw rdr::SystemException("RegQueryValueExW", res);
389 // The value does not exist, defaults will be used.
390 return false;
391 }
392
393 *dest = (int)value;
394 return true;
395 }
396
removeValue(const char * _name,HKEY * hKey)397 static void removeValue(const char* _name, HKEY* hKey) {
398 const DWORD buffersize = 256;
399 wchar_t name[buffersize];
400
401 unsigned size = fl_utf8towc(_name, strlen(_name)+1, name, buffersize);
402 if (size >= buffersize)
403 throw Exception(_("The name of the parameter is too large"));
404
405 LONG res = RegDeleteValueW(*hKey, name);
406 if (res != ERROR_SUCCESS) {
407 if (res != ERROR_FILE_NOT_FOUND)
408 throw rdr::SystemException("RegDeleteValueW", res);
409 // The value does not exist, no need to remove it.
410 return;
411 }
412 }
413
saveHistoryToRegKey(const vector<string> & serverHistory)414 void saveHistoryToRegKey(const vector<string>& serverHistory) {
415 HKEY hKey;
416 LONG res = RegCreateKeyExW(HKEY_CURRENT_USER,
417 L"Software\\TigerVNC\\vncviewer\\history", 0, NULL,
418 REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
419 &hKey, NULL);
420
421 if (res != ERROR_SUCCESS)
422 throw rdr::SystemException(_("Failed to create registry key"), res);
423
424 size_t index = 0;
425 assert(SERVER_HISTORY_SIZE < 100);
426 char indexString[3];
427
428 try {
429 while(index < serverHistory.size() && index <= SERVER_HISTORY_SIZE) {
430 snprintf(indexString, 3, "%d", index);
431 setKeyString(indexString, serverHistory[index].c_str(), &hKey);
432 index++;
433 }
434 } catch (Exception& e) {
435 RegCloseKey(hKey);
436 throw;
437 }
438
439 res = RegCloseKey(hKey);
440 if (res != ERROR_SUCCESS)
441 throw rdr::SystemException(_("Failed to close registry key"), res);
442 }
443
saveToReg(const char * servername)444 static void saveToReg(const char* servername) {
445
446 HKEY hKey;
447
448 LONG res = RegCreateKeyExW(HKEY_CURRENT_USER,
449 L"Software\\TigerVNC\\vncviewer", 0, NULL,
450 REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
451 &hKey, NULL);
452 if (res != ERROR_SUCCESS)
453 throw rdr::SystemException(_("Failed to create registry key"), res);
454
455 try {
456 setKeyString("ServerName", servername, &hKey);
457 } catch (Exception& e) {
458 RegCloseKey(hKey);
459 throw Exception(_("Failed to save \"%s\": %s"),
460 "ServerName", e.str());
461 }
462
463 for (size_t i = 0; i < sizeof(parameterArray)/sizeof(VoidParameter*); i++) {
464 try {
465 if (dynamic_cast<StringParameter*>(parameterArray[i]) != NULL) {
466 setKeyString(parameterArray[i]->getName(), *(StringParameter*)parameterArray[i], &hKey);
467 } else if (dynamic_cast<IntParameter*>(parameterArray[i]) != NULL) {
468 setKeyInt(parameterArray[i]->getName(), (int)*(IntParameter*)parameterArray[i], &hKey);
469 } else if (dynamic_cast<BoolParameter*>(parameterArray[i]) != NULL) {
470 setKeyInt(parameterArray[i]->getName(), (int)*(BoolParameter*)parameterArray[i], &hKey);
471 } else {
472 throw Exception(_("Unknown parameter type"));
473 }
474 } catch (Exception& e) {
475 RegCloseKey(hKey);
476 throw Exception(_("Failed to save \"%s\": %s"),
477 parameterArray[i]->getName(), e.str());
478 }
479 }
480
481 // Remove read-only parameters to replicate the behaviour of Linux/macOS when they
482 // store a config to disk. If the parameter hasn't been migrated at this point it
483 // will be lost.
484 for (size_t i = 0; i < sizeof(readOnlyParameterArray)/sizeof(VoidParameter*); i++) {
485 try {
486 removeValue(readOnlyParameterArray[i]->getName(), &hKey);
487 } catch (Exception& e) {
488 RegCloseKey(hKey);
489 throw Exception(_("Failed to remove \"%s\": %s"),
490 readOnlyParameterArray[i]->getName(), e.str());
491 }
492 }
493
494 res = RegCloseKey(hKey);
495 if (res != ERROR_SUCCESS)
496 throw rdr::SystemException(_("Failed to close registry key"), res);
497 }
498
loadHistoryFromRegKey(vector<string> & serverHistory)499 void loadHistoryFromRegKey(vector<string>& serverHistory) {
500 HKEY hKey;
501
502 LONG res = RegOpenKeyExW(HKEY_CURRENT_USER,
503 L"Software\\TigerVNC\\vncviewer\\history", 0,
504 KEY_READ, &hKey);
505 if (res != ERROR_SUCCESS) {
506 if (res == ERROR_FILE_NOT_FOUND) {
507 // The key does not exist, defaults will be used.
508 return;
509 }
510
511 throw rdr::SystemException(_("Failed to open registry key"), res);
512 }
513
514 size_t index;
515 const DWORD buffersize = 256;
516 char indexString[3];
517
518 for (index = 0;;index++) {
519 snprintf(indexString, 3, "%d", index);
520 char servernameBuffer[buffersize];
521
522 try {
523 if (!getKeyString(indexString, servernameBuffer,
524 buffersize, &hKey))
525 break;
526 } catch (Exception& e) {
527 // Just ignore this entry and try the next one
528 vlog.error(_("Failed to read server history entry %d: %s"),
529 (int)index, e.str());
530 continue;
531 }
532
533 serverHistory.push_back(servernameBuffer);
534 }
535
536 res = RegCloseKey(hKey);
537 if (res != ERROR_SUCCESS)
538 throw rdr::SystemException(_("Failed to close registry key"), res);
539 }
540
getParametersFromReg(VoidParameter * parameters[],size_t parameters_len,HKEY * hKey)541 static void getParametersFromReg(VoidParameter* parameters[],
542 size_t parameters_len, HKEY* hKey)
543 {
544 const size_t buffersize = 256;
545 int intValue = 0;
546 char stringValue[buffersize];
547
548 for (size_t i = 0; i < parameters_len/sizeof(VoidParameter*); i++) {
549 try {
550 if (dynamic_cast<StringParameter*>(parameters[i]) != NULL) {
551 if (getKeyString(parameters[i]->getName(), stringValue, buffersize, hKey))
552 parameters[i]->setParam(stringValue);
553 } else if (dynamic_cast<IntParameter*>(parameters[i]) != NULL) {
554 if (getKeyInt(parameters[i]->getName(), &intValue, hKey))
555 ((IntParameter*)parameters[i])->setParam(intValue);
556 } else if (dynamic_cast<BoolParameter*>(parameters[i]) != NULL) {
557 if (getKeyInt(parameters[i]->getName(), &intValue, hKey))
558 ((BoolParameter*)parameters[i])->setParam(intValue);
559 } else {
560 throw Exception(_("Unknown parameter type"));
561 }
562 } catch(Exception& e) {
563 // Just ignore this entry and continue with the rest
564 vlog.error(_("Failed to read parameter \"%s\": %s"),
565 parameters[i]->getName(), e.str());
566 }
567 }
568 }
569
loadFromReg()570 static char* loadFromReg() {
571
572 HKEY hKey;
573
574 LONG res = RegOpenKeyExW(HKEY_CURRENT_USER,
575 L"Software\\TigerVNC\\vncviewer", 0,
576 KEY_READ, &hKey);
577 if (res != ERROR_SUCCESS) {
578 if (res == ERROR_FILE_NOT_FOUND) {
579 // The key does not exist, defaults will be used.
580 return NULL;
581 }
582
583 throw rdr::SystemException(_("Failed to open registry key"), res);
584 }
585
586 const size_t buffersize = 256;
587 static char servername[buffersize];
588
589 char servernameBuffer[buffersize];
590 try {
591 if (getKeyString("ServerName", servernameBuffer, buffersize, &hKey))
592 snprintf(servername, buffersize, "%s", servernameBuffer);
593 } catch(Exception& e) {
594 vlog.error(_("Failed to read parameter \"%s\": %s"),
595 "ServerName", e.str());
596 strcpy(servername, "");
597 }
598
599 getParametersFromReg(parameterArray, sizeof(parameterArray), &hKey);
600 getParametersFromReg(readOnlyParameterArray,
601 sizeof(readOnlyParameterArray), &hKey);
602
603 res = RegCloseKey(hKey);
604 if (res != ERROR_SUCCESS)
605 throw rdr::SystemException(_("Failed to close registry key"), res);
606
607 return servername;
608 }
609 #endif // _WIN32
610
611
saveViewerParameters(const char * filename,const char * servername)612 void saveViewerParameters(const char *filename, const char *servername) {
613
614 const size_t buffersize = 256;
615 char filepath[PATH_MAX];
616 char encodingBuffer[buffersize];
617
618 // Write to the registry or a predefined file if no filename was specified.
619 if(filename == NULL) {
620
621 #ifdef _WIN32
622 saveToReg(servername);
623 return;
624 #endif
625
626 char* homeDir = NULL;
627 if (getvnchomedir(&homeDir) == -1)
628 throw Exception(_("Could not obtain the home directory path"));
629
630 snprintf(filepath, sizeof(filepath), "%sdefault.tigervnc", homeDir);
631 delete[] homeDir;
632 } else {
633 snprintf(filepath, sizeof(filepath), "%s", filename);
634 }
635
636 /* Write parameters to file */
637 FILE* f = fopen(filepath, "w+");
638 if (!f)
639 throw Exception(_("Could not open \"%s\": %s"),
640 filepath, strerror(errno));
641
642 fprintf(f, "%s\n", IDENTIFIER_STRING);
643 fprintf(f, "\n");
644
645 if (!encodeValue(servername, encodingBuffer, buffersize)) {
646 fclose(f);
647 throw Exception(_("Failed to save \"%s\": %s"),
648 "ServerName", _("Could not encode parameter"));
649 }
650 fprintf(f, "ServerName=%s\n", encodingBuffer);
651
652 for (size_t i = 0; i < sizeof(parameterArray)/sizeof(VoidParameter*); i++) {
653 if (dynamic_cast<StringParameter*>(parameterArray[i]) != NULL) {
654 if (!encodeValue(*(StringParameter*)parameterArray[i],
655 encodingBuffer, buffersize)) {
656 fclose(f);
657 throw Exception(_("Failed to save \"%s\": %s"),
658 parameterArray[i]->getName(),
659 _("Could not encode parameter"));
660 }
661 fprintf(f, "%s=%s\n", ((StringParameter*)parameterArray[i])->getName(), encodingBuffer);
662 } else if (dynamic_cast<IntParameter*>(parameterArray[i]) != NULL) {
663 fprintf(f, "%s=%d\n", ((IntParameter*)parameterArray[i])->getName(), (int)*(IntParameter*)parameterArray[i]);
664 } else if (dynamic_cast<BoolParameter*>(parameterArray[i]) != NULL) {
665 fprintf(f, "%s=%d\n", ((BoolParameter*)parameterArray[i])->getName(), (int)*(BoolParameter*)parameterArray[i]);
666 } else {
667 fclose(f);
668 throw Exception(_("Failed to save \"%s\": %s"),
669 parameterArray[i]->getName(),
670 _("Unknown parameter type"));
671 }
672 }
673 fclose(f);
674 }
675
findAndSetViewerParameterFromValue(VoidParameter * parameters[],size_t parameters_len,char * value,char * line,char * filepath)676 static bool findAndSetViewerParameterFromValue(
677 VoidParameter* parameters[], size_t parameters_len,
678 char* value, char* line, char* filepath)
679 {
680 const size_t buffersize = 256;
681 char decodingBuffer[buffersize];
682
683 // Find and set the correct parameter
684 for (size_t i = 0; i < parameters_len/sizeof(VoidParameter*); i++) {
685
686 if (dynamic_cast<StringParameter*>(parameters[i]) != NULL) {
687 if (strcasecmp(line, ((StringParameter*)parameters[i])->getName()) == 0) {
688 if(!decodeValue(value, decodingBuffer, sizeof(decodingBuffer)))
689 throw Exception(_("Invalid format or too large value"));
690 ((StringParameter*)parameters[i])->setParam(decodingBuffer);
691 return false;
692 }
693
694 } else if (dynamic_cast<IntParameter*>(parameters[i]) != NULL) {
695 if (strcasecmp(line, ((IntParameter*)parameters[i])->getName()) == 0) {
696 ((IntParameter*)parameters[i])->setParam(atoi(value));
697 return false;
698 }
699
700 } else if (dynamic_cast<BoolParameter*>(parameters[i]) != NULL) {
701 if (strcasecmp(line, ((BoolParameter*)parameters[i])->getName()) == 0) {
702 ((BoolParameter*)parameters[i])->setParam(atoi(value));
703 return false;
704 }
705
706 } else {
707 throw Exception(_("Unknown parameter type"));
708 }
709 }
710
711 return true;
712 }
713
loadViewerParameters(const char * filename)714 char* loadViewerParameters(const char *filename) {
715
716 const size_t buffersize = 256;
717 char filepath[PATH_MAX];
718 char line[buffersize];
719 char decodingBuffer[buffersize];
720 static char servername[sizeof(line)];
721
722 memset(servername, '\0', sizeof(servername));
723
724 // Load from the registry or a predefined file if no filename was specified.
725 if(filename == NULL) {
726
727 #ifdef _WIN32
728 return loadFromReg();
729 #endif
730
731 char* homeDir = NULL;
732 if (getvnchomedir(&homeDir) == -1)
733 throw Exception(_("Could not obtain the home directory path"));
734
735 snprintf(filepath, sizeof(filepath), "%sdefault.tigervnc", homeDir);
736 delete[] homeDir;
737 } else {
738 snprintf(filepath, sizeof(filepath), "%s", filename);
739 }
740
741 /* Read parameters from file */
742 FILE* f = fopen(filepath, "r");
743 if (!f) {
744 if (!filename)
745 return NULL; // Use defaults.
746 throw Exception(_("Could not open \"%s\": %s"),
747 filepath, strerror(errno));
748 }
749
750 int lineNr = 0;
751 while (!feof(f)) {
752
753 // Read the next line
754 lineNr++;
755 if (!fgets(line, sizeof(line), f)) {
756 if (feof(f))
757 break;
758
759 fclose(f);
760 throw Exception(_("Failed to read line %d in file %s: %s"),
761 lineNr, filepath, strerror(errno));
762 }
763
764 if (strlen(line) == (sizeof(line) - 1)) {
765 fclose(f);
766 throw Exception(_("Failed to read line %d in file %s: %s"),
767 lineNr, filepath, _("Line too long"));
768 }
769
770 // Make sure that the first line of the file has the file identifier string
771 if(lineNr == 1) {
772 if(strncmp(line, IDENTIFIER_STRING, strlen(IDENTIFIER_STRING)) == 0)
773 continue;
774
775 fclose(f);
776 throw Exception(_("Configuration file %s is in an invalid format"),
777 filepath);
778 }
779
780 // Skip empty lines and comments
781 if ((line[0] == '\n') || (line[0] == '#') || (line[0] == '\r'))
782 continue;
783
784 int len = strlen(line);
785 if (line[len-1] == '\n') {
786 line[len-1] = '\0';
787 len--;
788 }
789 if (line[len-1] == '\r') {
790 line[len-1] = '\0';
791 len--;
792 }
793
794 // Find the parameter value
795 char *value = strchr(line, '=');
796 if (value == NULL) {
797 vlog.error(_("Failed to read line %d in file %s: %s"),
798 lineNr, filepath, _("Invalid format"));
799 continue;
800 }
801 *value = '\0'; // line only contains the parameter name below.
802 value++;
803
804 bool invalidParameterName = true; // Will be set to false below if
805 // the line contains a valid name.
806
807 try {
808 if (strcasecmp(line, "ServerName") == 0) {
809
810 if(!decodeValue(value, decodingBuffer, sizeof(decodingBuffer)))
811 throw Exception(_("Invalid format or too large value"));
812 snprintf(servername, sizeof(decodingBuffer), "%s", decodingBuffer);
813 invalidParameterName = false;
814
815 } else {
816 invalidParameterName = findAndSetViewerParameterFromValue(parameterArray, sizeof(parameterArray),
817 value, line, filepath);
818
819 if (invalidParameterName) {
820 invalidParameterName = findAndSetViewerParameterFromValue(readOnlyParameterArray, sizeof(readOnlyParameterArray),
821 value, line, filepath);
822 }
823 }
824 } catch(Exception& e) {
825 // Just ignore this entry and continue with the rest
826 vlog.error(_("Failed to read line %d in file %s: %s"),
827 lineNr, filepath, e.str());
828 continue;
829 }
830
831 if (invalidParameterName)
832 vlog.error(_("Failed to read line %d in file %s: %s"),
833 lineNr, filepath, _("Unknown parameter"));
834 }
835 fclose(f); f=0;
836
837 return servername;
838 }
839