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