/* Copyright (C) 2016 Brian P. Hinz
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*/
package com.tigervnc.vncviewer;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
import java.io.PrintWriter;
import java.util.StringTokenizer;
import com.tigervnc.rfb.*;
import com.tigervnc.rfb.Exception;
public class Parameters {
public static BoolParameter noLionFS
= new BoolParameter("NoLionFS",
"On Mac systems, setting this parameter will force the use of the old "+
"(pre-Lion) full-screen mode, even if the viewer is running on OS X 10.7 "+
"Lion or later.",
false);
public static BoolParameter embed
= new BoolParameter("Embed",
"If the viewer is being run as an applet, display its output to " +
"an embedded frame in the browser window rather than to a dedicated " +
"window. Embed=1 implies FullScreen=0 and Scale=100.",
false);
public static BoolParameter dotWhenNoCursor
= new BoolParameter("DotWhenNoCursor",
"Show the dot cursor when the server sends an invisible cursor",
false);
public static BoolParameter sendLocalUsername
= new BoolParameter("SendLocalUsername",
"Send the local username for SecurityTypes "+
"such as Plain rather than prompting",
true);
public static StringParameter passwordFile
= new StringParameter("PasswordFile",
"Password file for VNC authentication",
"");
public static AliasParameter passwd
= new AliasParameter("passwd",
"Alias for PasswordFile",
passwordFile);
public static BoolParameter autoSelect
= new BoolParameter("AutoSelect",
"Auto select pixel format and encoding",
true);
public static BoolParameter fullColor
= new BoolParameter("FullColor",
"Use full color - otherwise 6-bit colour is used "+
"until AutoSelect decides the link is fast enough",
true);
public static AliasParameter fullColorAlias
= new AliasParameter("FullColour",
"Alias for FullColor",
Parameters.fullColor);
public static IntParameter lowColorLevel
= new IntParameter("LowColorLevel",
"Color level to use on slow connections. "+
"0 = Very Low (8 colors), 1 = Low (64 colors), "+
"2 = Medium (256 colors)",
2);
public static AliasParameter lowColorLevelAlias
= new AliasParameter("LowColourLevel",
"Alias for LowColorLevel",
lowColorLevel);
public static StringParameter preferredEncoding
= new StringParameter("PreferredEncoding",
"Preferred encoding to use (Tight, ZRLE, "+
"hextile or raw) - implies AutoSelect=0",
"Tight");
public static BoolParameter remoteResize
= new BoolParameter("RemoteResize",
"Dynamically resize the remote desktop size as "+
"the size of the local client window changes. "+
"(Does not work with all servers)",
true);
public static BoolParameter viewOnly
= new BoolParameter("ViewOnly",
"Don't send any mouse or keyboard events to the server",
false);
public static BoolParameter shared
= new BoolParameter("Shared",
"Don't disconnect other viewers upon "+
"connection - share the desktop instead",
false);
public static BoolParameter maximize
= new BoolParameter("Maximize",
"Maximize viewer window",
false);
public static BoolParameter fullScreen
= new BoolParameter("FullScreen",
"Full Screen Mode",
false);
public static BoolParameter fullScreenAllMonitors
= new BoolParameter("FullScreenAllMonitors",
"Enable full screen over all monitors",
true);
public static BoolParameter acceptClipboard
= new BoolParameter("AcceptClipboard",
"Accept clipboard changes from the server",
true);
public static BoolParameter sendClipboard
= new BoolParameter("SendClipboard",
"Send clipboard changes to the server",
true);
public static IntParameter maxCutText
= new IntParameter("MaxCutText",
"Maximum permitted length of an outgoing clipboard update",
262144);
public static StringParameter menuKey
= new StringParameter("MenuKey",
"The key which brings up the popup menu",
"F8");
public static StringParameter desktopSize
= new StringParameter("DesktopSize",
"Reconfigure desktop size on the server on connect (if possible)",
"");
public static BoolParameter listenMode
= new BoolParameter("listen",
"Listen for connections from VNC servers",
false);
public static StringParameter scalingFactor
= new StringParameter("ScalingFactor",
"Reduce or enlarge the remote desktop image. "+
"The value is interpreted as a scaling factor "+
"in percent. If the parameter is set to "+
"\"Auto\", then automatic scaling is "+
"performed. Auto-scaling tries to choose a "+
"scaling factor in such a way that the whole "+
"remote desktop will fit on the local screen. "+
"If the parameter is set to \"FixedRatio\", "+
"then automatic scaling is performed, but the "+
"original aspect ratio is preserved.",
"100");
public static BoolParameter alwaysShowServerDialog
= new BoolParameter("AlwaysShowServerDialog",
"Always show the server dialog even if a server has been "+
"specified in an applet parameter or on the command line",
false);
public static StringParameter vncServerName
= new StringParameter("Server",
"The VNC server <host>[:<dpyNum>] or <host>::<port>",
"");
public static BoolParameter acceptBell
= new BoolParameter("AcceptBell",
"Produce a system beep when requested to by the server.",
true);
public static StringParameter via
= new StringParameter("Via",
"Automatically create an encrypted TCP tunnel to "+
"the gateway machine, then connect to the VNC host "+
"through that tunnel. By default, this option invokes "+
"SSH local port forwarding using the embedded JSch "+
"client, however an external SSH client may be specified "+
"using the \"-extSSH\" parameter. Note that when using "+
"the -via option, the VNC host machine name should be "+
"specified from the point of view of the gateway machine, "+
"e.g. \"localhost\" denotes the gateway, "+
"not the machine on which the viewer was launched. "+
"See the System Properties section below for "+
"information on configuring the -Via option.", "");
public static BoolParameter tunnel
= new BoolParameter("Tunnel",
"The -Tunnel command is basically a shorthand for the "+
"-via command when the VNC server and SSH gateway are "+
"one and the same. -Tunnel creates an SSH connection "+
"to the server and forwards the VNC through the tunnel "+
"without the need to specify anything else.", false);
public static BoolParameter extSSH
= new BoolParameter("extSSH",
"By default, SSH tunneling uses the embedded JSch client "+
"for tunnel creation. This option causes the client to "+
"invoke an external SSH client application for all tunneling "+
"operations. By default, \"/usr/bin/ssh\" is used, however "+
"the path to the external application may be specified using "+
"the -SSHClient option.", false);
public static StringParameter extSSHClient
= new StringParameter("extSSHClient",
"Specifies the path to an external SSH client application "+
"that is to be used for tunneling operations when the -extSSH "+
"option is in effect.", "/usr/bin/ssh");
public static StringParameter extSSHArgs
= new StringParameter("extSSHArgs",
"Specifies the arguments string or command template to be used "+
"by the external SSH client application when the -extSSH option "+
"is in effect. The string will be processed according to the same "+
"pattern substitution rules as the VNC_TUNNEL_CMD and VNC_VIA_CMD "+
"system properties, and can be used to override those in a more "+
"command-line friendly way. If not specified, then the appropriate "+
"VNC_TUNNEL_CMD or VNC_VIA_CMD command template will be used.", "");
public static StringParameter sshConfig
= new StringParameter("SSHConfig",
"Specifies the path to an OpenSSH configuration file that to "+
"be parsed by the embedded JSch SSH client during tunneling "+
"operations.", FileUtils.getHomeDir()+".ssh/config");
public static StringParameter sshKey
= new StringParameter("SSHKey",
"When using the Via or Tunnel options with the embedded SSH client, "+
"this parameter specifies the text of the SSH private key to use when "+
"authenticating with the SSH server. You can use \\n within the string "+
"to specify a new line.", "");
public static StringParameter sshKeyFile
= new StringParameter("SSHKeyFile",
"When using the Via or Tunnel options with the embedded SSH client, "+
"this parameter specifies a file that contains an SSH private key "+
"(or keys) to use when authenticating with the SSH server. If not "+
"specified, ~/.ssh/id_dsa or ~/.ssh/id_rsa will be used (if they exist). "+
"Otherwise, the client will fallback to prompting for an SSH password.",
"");
public static StringParameter sshKeyPass
= new StringParameter("SSHKeyPass",
"When using the Via or Tunnel options with the embedded SSH client, "+
"this parameter specifies the passphrase for the SSH key.", "");
public static BoolParameter customCompressLevel
= new BoolParameter("CustomCompressLevel",
"Use custom compression level. Default if CompressLevel is specified.",
false);
public static IntParameter compressLevel
= new IntParameter("CompressLevel",
"Use specified compression level. 0 = Low, 6 = High",
1);
public static BoolParameter noJpeg
= new BoolParameter("NoJPEG",
"Disable lossy JPEG compression in Tight encoding.",
false);
public static IntParameter qualityLevel
= new IntParameter("QualityLevel",
"JPEG quality level. 0 = Low, 9 = High",
8);
private static final String IDENTIFIER_STRING
= "TigerVNC Configuration file Version 1.0";
static VoidParameter[] parameterArray = {
CSecurityTLS.X509CA,
CSecurityTLS.X509CRL,
SecurityClient.secTypes,
autoSelect,
fullColor,
lowColorLevel,
preferredEncoding,
customCompressLevel,
compressLevel,
noJpeg,
qualityLevel,
maximize,
fullScreen,
fullScreenAllMonitors,
desktopSize,
remoteResize,
viewOnly,
shared,
acceptClipboard,
sendClipboard,
menuKey,
noLionFS,
sendLocalUsername,
maxCutText,
scalingFactor,
acceptBell,
via,
tunnel,
extSSH,
extSSHClient,
extSSHArgs,
sshConfig,
sshKeyFile,
};
static LogWriter vlog = new LogWriter("Parameters");
public static void saveViewerParameters(String filename, String servername) {
// Write to the registry or a predefined file if no filename was specified.
String filepath;
if (filename == null || filename.isEmpty()) {
saveToReg(servername);
return;
/*
String homeDir = FileUtils.getVncHomeDir();
if (homeDir == null)
throw new Exception("Failed to read configuration file, "+
"can't obtain home directory path.");
filepath = homeDir.concat("default.tigervnc");
*/
} else {
filepath = filename;
}
/* Write parameters to file */
File f = new File(filepath);
if (f.exists() && !f.canWrite())
throw new Exception(String.format("Failed to write configuration file,"+
"can't open %s", filepath));
PrintWriter pw = null;
try {
pw = new PrintWriter(f, "UTF-8");
} catch (java.lang.Exception e) {
throw new Exception(e.getMessage());
}
pw.println(IDENTIFIER_STRING);
pw.println("");
if (servername != null && !servername.isEmpty()) {
pw.println(String.format("ServerName=%s\n", servername));
updateConnHistory(servername);
}
for (int i = 0; i < parameterArray.length; i++) {
if (parameterArray[i] instanceof StringParameter) {
//if (line.substring(0,idx).trim().equalsIgnoreCase(parameterArray[i].getName()))
pw.println(String.format("%s=%s",parameterArray[i].getName(),
parameterArray[i].getValueStr()));
} else if (parameterArray[i] instanceof IntParameter) {
//if (line.substring(0,idx).trim().equalsIgnoreCase(parameterArray[i].getName()))
pw.println(String.format("%s=%s",parameterArray[i].getName(),
parameterArray[i].getValueStr()));
} else if (parameterArray[i] instanceof BoolParameter) {
//if (line.substring(0,idx).trim().equalsIgnoreCase(parameterArray[i].getName()))
pw.println(String.format("%s=%s",parameterArray[i].getName(),
parameterArray[i].getValueStr()));
} else {
vlog.error(String.format("Unknown parameter type for parameter %s",
parameterArray[i].getName()));
}
}
pw.flush();
pw.close();
}
public static String loadViewerParameters(String filename) throws Exception {
String servername = "";
String filepath;
if (filename == null) {
return loadFromReg();
/*
String homeDir = FileUtils.getVncHomeDir();
if (homeDir == null)
throw new Exception("Failed to read configuration file, "+
"can't obtain home directory path.");
filepath = homeDir.concat("default.tigervnc");
*/
} else {
filepath = filename;
}
/* Read parameters from file */
File f = new File(filepath);
if (!f.exists() || !f.canRead()) {
if (filename == null || filename.isEmpty())
return null;
throw new Exception(String.format("Failed to read configuration file, can't open %s",
filepath));
}
String line = "";
LineNumberReader reader;
try {
reader = new LineNumberReader(new FileReader(f));
} catch (FileNotFoundException e) {
throw new Exception(e.getMessage());
}
int lineNr = 0;
while (line != null) {
// Read the next line
try {
line = reader.readLine();
lineNr = reader.getLineNumber();
if (line == null)
break;
} catch (IOException e) {
throw new Exception(String.format("Failed to read line %d in file %s: %s",
lineNr, filepath, e.getMessage()));
}
// Make sure that the first line of the file has the file identifier string
if(lineNr == 1) {
if(line.equals(IDENTIFIER_STRING))
continue;
else
throw new Exception(String.format("Configuration file %s is in an invalid format", filename));
}
// Skip empty lines and comments
if (line.trim().isEmpty() || line.trim().startsWith("#"))
continue;
// Find the parameter value
int idx = line.indexOf("=");
if (idx == -1) {
vlog.error(String.format("Failed to read line %d in file %s: %s",
lineNr, filename, "Invalid format"));
continue;
}
String value = line.substring(idx+1).trim();
boolean invalidParameterName = true; // Will be set to false below if
// the line contains a valid name.
if (line.substring(0,idx).trim().equalsIgnoreCase("ServerName")) {
if (value.length() > 256) {
vlog.error(String.format("Failed to read line %d in file %s: %s",
lineNr, filepath, "Invalid format or too large value"));
continue;
}
servername = value;
invalidParameterName = false;
} else {
for (int i = 0; i < parameterArray.length; i++) {
if (parameterArray[i] instanceof StringParameter) {
if (line.substring(0,idx).trim().equalsIgnoreCase(parameterArray[i].getName())) {
if (value.length() > 256) {
vlog.error(String.format("Failed to read line %d in file %s: %s",
lineNr, filepath, "Invalid format or too large value"));
continue;
}
((StringParameter)parameterArray[i]).setParam(value);
invalidParameterName = false;
}
} else if (parameterArray[i] instanceof IntParameter) {
if (line.substring(0,idx).trim().equalsIgnoreCase(parameterArray[i].getName())) {
((IntParameter)parameterArray[i]).setParam(value);
invalidParameterName = false;
}
} else if (parameterArray[i] instanceof BoolParameter) {
if (line.substring(0,idx).trim().equalsIgnoreCase(parameterArray[i].getName())) {
((BoolParameter)parameterArray[i]).setParam(value);
invalidParameterName = false;
}
} else {
vlog.error(String.format("Unknown parameter type for parameter %s",
parameterArray[i].getName()));
}
}
}
if (invalidParameterName)
vlog.info(String.format("Unknown parameter %s on line %d in file %s",
line, lineNr, filepath));
}
try {
reader.close();
} catch (IOException e) {
vlog.info(e.getMessage());
} finally {
try {
if (reader != null)
reader.close();
} catch (IOException e) { }
}
return servername;
}
public static void saveToReg(String servername) {
String hKey = "global";
if (servername != null && !servername.isEmpty()) {
UserPreferences.set(hKey, "ServerName", servername);
updateConnHistory(servername);
}
for (int i = 0; i < parameterArray.length; i++) {
if (parameterArray[i] instanceof StringParameter) {
UserPreferences.set(hKey, parameterArray[i].getName(),
parameterArray[i].getValueStr());
} else if (parameterArray[i] instanceof IntParameter) {
UserPreferences.set(hKey, parameterArray[i].getName(),
((IntParameter)parameterArray[i]).getValue());
} else if (parameterArray[i] instanceof BoolParameter) {
UserPreferences.set(hKey, parameterArray[i].getName(),
((BoolParameter)parameterArray[i]).getValue());
} else {
vlog.error(String.format("Unknown parameter type for parameter %s",
parameterArray[i].getName()));
}
}
UserPreferences.save(hKey);
}
public static String loadFromReg() {
String hKey = "global";
String servername = UserPreferences.get(hKey, "ServerName");
if (servername == null)
servername = "";
for (int i = 0; i < parameterArray.length; i++) {
if (parameterArray[i] instanceof StringParameter) {
if (UserPreferences.get(hKey, parameterArray[i].getName()) != null) {
String stringValue =
UserPreferences.get(hKey, parameterArray[i].getName());
((StringParameter)parameterArray[i]).setParam(stringValue);
}
} else if (parameterArray[i] instanceof IntParameter) {
if (UserPreferences.get(hKey, parameterArray[i].getName()) != null) {
int intValue =
UserPreferences.getInt(hKey, parameterArray[i].getName());
((IntParameter)parameterArray[i]).setParam(intValue);
}
} else if (parameterArray[i] instanceof BoolParameter) {
if (UserPreferences.get(hKey, parameterArray[i].getName()) != null) {
boolean booleanValue =
UserPreferences.getBool(hKey, parameterArray[i].getName());
((BoolParameter)parameterArray[i]).setParam(booleanValue);
}
} else {
vlog.error(String.format("Unknown parameter type for parameter %s",
parameterArray[i].getName()));
}
}
return servername;
}
public static String loadAppletParameters(VncViewer applet) {
String servername = applet.getParameter("Server");
String serverport = applet.getParameter("Port");
String embedParam = applet.getParameter("Embed");
if (servername == null)
servername = applet.getCodeBase().getHost();
if (serverport != null)
servername = servername.concat("::"+serverport);
else
servername = servername.concat("::5900");
if (embedParam != null)
embed.setParam(embedParam);
for (int i = 0; i < parameterArray.length; i++) {
String value = applet.getParameter(parameterArray[i].getName());
if (value == null)
continue;
if (parameterArray[i] instanceof StringParameter) {
if (value.length() > 256) {
vlog.error(String.format("Failed to read applet parameter %s: %s",
parameterArray[i].getName(),
"Invalid format or too large value"));
continue;
}
((StringParameter)parameterArray[i]).setParam(value);
} else if (parameterArray[i] instanceof IntParameter) {
((IntParameter)parameterArray[i]).setParam(value);
} else if (parameterArray[i] instanceof BoolParameter) {
((BoolParameter)parameterArray[i]).setParam(value);
} else {
vlog.error(String.format("Unknown parameter type for parameter %s",
parameterArray[i].getName()));
}
}
return servername;
}
private static void updateConnHistory(String serverName) {
String hKey = "ServerDialog";
if (serverName != null && !serverName.isEmpty()) {
String valueStr = UserPreferences.get(hKey, "history");
String t = (valueStr == null) ? "" : valueStr;
StringTokenizer st = new StringTokenizer(t, ",");
StringBuffer sb = new StringBuffer().append(serverName);
while (st.hasMoreTokens()) {
String str = st.nextToken();
if (!str.equals(serverName) && !str.equals(""))
sb.append(',').append(str);
}
UserPreferences.set(hKey, "history", sb.toString());
UserPreferences.save(hKey);
}
}
}