Files

702 lines
19 KiB
C++

///////////////////////////////////////////////////////////////////////////////
//Telnet Win32 : an ANSI telnet client.
//Copyright (C) 1998-2000 Paul Brannan
//Copyright (C) 1998 I.Ioannou
//Copyright (C) 1997 Brad Johnson
//
//This program 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 program 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 program; if not, write to the Free Software
//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
//I.Ioannou
//[email protected]
//
///////////////////////////////////////////////////////////////////////////
// tnconfig.cpp
// Written by Paul Brannan <[email protected]>
// Last modified August 30, 1998
//
// This is a class designed for use with Brad Johnson's Console Telnet
// see the file tnconfig.h for more information
#include "precomp.h"
#include <locale.h>
#include <io.h>
#include <sys/stat.h>
// Turn off the "forcing value to bool 'true' or 'false'" warning
#ifdef _MSC_VER
#pragma warning(disable: 4800)
#endif
// This is the ini variable that is used for everybody
TConfig ini;
TConfig::TConfig() {
// set all default values
startdir[0] = '\0';
keyfile[0] = '\0';
inifile[0] = '\0';
dumpfile[0] = '\0';
term[0] = '\0';
default_config[0] = '\0';
strcpy(printer_name, "LPT1");
input_redir = 0;
output_redir = 0;
strip_redir = FALSE;
dstrbksp = FALSE;
eightbit_ansi = FALSE;
vt100_mode = FALSE;
disable_break = FALSE;
speaker_beep = TRUE;
do_beep = TRUE;
preserve_colors = FALSE;
wrapline = TRUE;
lock_linewrap = FALSE;
fast_write = TRUE;
enable_mouse = TRUE;
alt_erase = FALSE;
wide_enable = FALSE;
keyboard_paste = FALSE;
set_title = TRUE;
blink_bg = -1;
blink_fg = 2;
underline_bg = -1;
underline_fg = 3;
ulblink_bg = -1;
ulblink_fg = 1;
normal_bg = -1;
normal_fg = -1;
scroll_bg = 0;
scroll_fg = 7;
status_bg = 1;
status_fg = 15;
buffer_size = 2048;
term_width = -1;
term_height = -1;
window_width = -1;
window_height = -1;
strcpy(escape_key, "]");
strcpy(scrollback_key, "[");
strcpy(dial_key, "\\");
strcpy(default_config, "ANSI");
strcpy(term, "ansi");
strcpy(scroll_mode, "DUMP");
scroll_size=32000;
scroll_enable=TRUE;
host[0] = '\0';
port = "23";
init_varlist();
aliases = NULL;
}
TConfig::~TConfig() {
if(aliases) {
for(int j = 0; j < alias_total; j++) delete[] aliases[j];
delete[] aliases;
}
}
enum ini_data_type {
INI_STRING,
INI_INT,
INI_BOOL
};
enum {
INIFILE,
KEYFILE,
DUMPFILE,
DEFAULT_CONFIG,
TERM,
INPUT_REDIR,
OUTPUT_REDIR,
STRIP_REDIR,
DSTRBKSP,
EIGHTBIT_ANSI,
VT100_MODE,
DISABLE_BREAK,
SPEAKER_BEEP,
DO_BEEP,
PRESERVE_COLORS,
WRAP_LINE,
LOCK_LINEWRAP,
FAST_WRITE,
TERM_WIDTH,
TERM_HEIGHT,
WINDOW_WIDTH,
WINDOW_HEIGHT,
WIDE_ENABLE,
CTRLBREAK_AS_CTRLC,
BUFFER_SIZE,
SET_TITLE,
BLINK_BG,
BLINK_FG,
UNDERLINE_BG,
UNDERLINE_FG,
ULBLINK_BG,
ULBLINK_FG,
NORMAL_BG,
NORMAL_FG,
SCROLL_BG,
SCROLL_FG,
STATUS_BG,
STATUS_FG,
PRINTER_NAME,
ENABLE_MOUSE,
ESCAPE_KEY,
SCROLLBACK_KEY,
DIAL_KEY,
ALT_ERASE,
KEYBOARD_PASTE,
SCROLL_MODE,
SCROLL_SIZE,
SCROLL_ENABLE,
SCRIPTNAME,
SCRIPT_ENABLE,
NETPIPE,
IOPIPE,
MAX_INI_VARS // must be last
};
struct ini_variable {
const char *name; // variable name
const char *section; // name of ini file section the variable is in
enum ini_data_type data_type; // type of data
void *ini_data; // pointer to data
int max_size; // max size if string
};
// Note: default values are set in the constructor, TConfig()
ini_variable ini_varlist[MAX_INI_VARS];
enum {
KEYBOARD,
TERMINAL,
COLORS,
MOUSE,
PRINTER,
SCROLLBACK,
SCRIPTING,
PIPES,
MAX_INI_GROUPS // Must be last
};
char *ini_groups[MAX_INI_GROUPS];
void TConfig::init_varlist() {
static const ini_variable static_ini_varlist[MAX_INI_VARS] = {
{"Inifile", NULL, INI_STRING, &inifile, sizeof(inifile)},
{"Keyfile", "Keyboard", INI_STRING, &keyfile, sizeof(keyfile)},
{"Dumpfile", "Terminal", INI_STRING, &dumpfile, sizeof(dumpfile)},
{"Default_Config","Keyboard", INI_STRING, &default_config, sizeof(default_config)},
{"Term", "Terminal", INI_STRING, &term, sizeof(term)},
{"Input_Redir", "Terminal", INI_INT, &input_redir, 0},
{"Output_Redir","Terminal", INI_INT, &output_redir, 0},
{"Strip_Redir", "Terminal", INI_BOOL, &strip_redir, 0},
{"Destructive_Backspace","Terminal",INI_BOOL, &dstrbksp, 0},
{"EightBit_Ansi","Terminal", INI_BOOL, &eightbit_ansi, 0},
{"VT100_Mode", "Terminal", INI_BOOL, &vt100_mode, 0},
{"Disable_Break","Terminal", INI_BOOL, &disable_break, 0},
{"Speaker_Beep","Terminal", INI_BOOL, &speaker_beep, 0},
{"Beep", "Terminal", INI_BOOL, &do_beep, 0},
{"Preserve_Colors","Terminal", INI_BOOL, &preserve_colors, 0},
{"Wrap_Line", "Terminal", INI_BOOL, &wrapline, 0},
{"Lock_linewrap","Terminal", INI_BOOL, &lock_linewrap, 0},
{"Fast_Write", "Terminal", INI_BOOL, &fast_write, 0},
{"Term_Width", "Terminal", INI_INT, &term_width, 0},
{"Term_Height", "Terminal", INI_INT, &term_height, 0},
{"Window_Width","Terminal", INI_INT, &window_width, 0},
{"Window_Height","Terminal", INI_INT, &window_height, 0},
{"Wide_Enable", "Terminal", INI_BOOL, &wide_enable, 0},
{"Ctrlbreak_as_Ctrlc","Keyboard", INI_BOOL, &ctrlbreak_as_ctrlc, 0},
{"Buffer_Size", "Terminal", INI_INT, &buffer_size, 0},
{"Set_Title", "Terminal", INI_BOOL, &set_title, 0},
{"Blink_bg", "Colors", INI_INT, &blink_bg, 0},
{"Blink_fg", "Colors", INI_INT, &blink_fg, 0},
{"Underline_bg","Colors", INI_INT, &underline_bg, 0},
{"Underline_fg","Colors", INI_INT, &underline_fg, 0},
{"UlBlink_bg", "Colors", INI_INT, &ulblink_bg, 0},
{"UlBlink_fg", "Colors", INI_INT, &ulblink_fg, 0},
{"Normal_bg", "Colors", INI_INT, &normal_bg, 0},
{"Normal_fg", "Colors", INI_INT, &normal_fg, 0},
{"Scroll_bg", "Colors", INI_INT, &scroll_bg, 0},
{"Scroll_fg", "Colors", INI_INT, &scroll_fg, 0},
{"Status_bg", "Colors", INI_INT, &status_bg, 0},
{"Status_fg", "Colors", INI_INT, &status_fg, 0},
{"Enable_Mouse","Mouse", INI_BOOL, &enable_mouse, 0},
{"Printer_Name","Printer", INI_STRING, &printer_name, sizeof(printer_name)},
{"Escape_Key", "Keyboard", INI_STRING, &escape_key, 1},
{"Scrollback_Key","Keyboard", INI_STRING, &scrollback_key, 1},
{"Dial_Key", "Keyboard", INI_STRING, &dial_key, 1},
{"Alt_Erase", "Keyboard", INI_BOOL, &alt_erase, 0},
{"Keyboard_Paste","Keyboard", INI_BOOL, &keyboard_paste, 0},
{"Scroll_Mode", "Scrollback", INI_STRING, &scroll_mode, sizeof(scroll_mode)},
{"Scroll_Size", "Scrollback", INI_INT, &scroll_size, 0},
{"Scroll_Enable","Scrollback", INI_BOOL, &scroll_enable, 0},
{"Scriptname", "Scripting", INI_STRING, &scriptname, sizeof(scriptname)},
{"Script_enable","Scripting", INI_BOOL, &script_enable, 0},
{"Netpipe", "Pipes", INI_STRING, &netpipe, sizeof(netpipe)},
{"Iopipe", "Pipes", INI_STRING, &iopipe, sizeof(iopipe)}
};
static const char *static_ini_groups[MAX_INI_GROUPS] = {
"Keyboard",
"Terminal",
"Colors",
"Mouse",
"Printer",
"Scrollback",
"Scripting",
"Pipes"
};
memcpy(ini_varlist, static_ini_varlist, sizeof(ini_varlist));
memcpy(ini_groups, static_ini_groups, sizeof(ini_groups));
}
void TConfig::init(char *dirname, char *execname) {
// Copy temporary dirname to permanent startdir
strncpy(startdir, dirname, sizeof(startdir));
startdir[sizeof(startdir) - 1] = 0;
// Copy temp execname to permanent exename (Thomas Briggs 12/7/98)
strncpy(exename, execname, sizeof(exename));
exename[sizeof(exename) - 1] = 0;
// Initialize INI file
inifile_init();
// Initialize redir
// Note that this must be done early, so error messages will be printed
// properly
redir_init();
// Initialize aliases (Paul Brannan 1/1/99)
init_aliases();
// Make sure the file that we're trying to work with exists
int iResult = access(inifile, 04);
// Thomas Briggs 9/14/98
if( iResult == 0 )
// Tell the user what file we are reading
// We cannot print any messages before initializing telnet_redir
printm(0, FALSE, MSG_CONFIG, inifile);
else
// Tell the user that the file doesn't exist, but later read the
// file anyway simply to populate the defaults
printm(0, FALSE, MSG_NOINI, inifile);
init_vars(); // Initialize misc. vars
keyfile_init(); // Initialize keyfile
}
// Alias support (Paul Brannan 1/1/99)
void TConfig::init_aliases() {
char *buffer;
alias_total = 0;
// Find the correct buffer size
// FIX ME!! some implementations of Mingw32 don't have a
// GetPrivateProfileSecionNames function. What do we do about this?
#ifndef __MINGW32__
{
int size=1024, Result = 0;
for(;;) {
buffer = new char[size];
Result = GetPrivateProfileSectionNames(buffer, size, inifile);
if(Result < size - 2) break;
size *= 2;
delete[] buffer;
}
}
#else
return;
#endif
// Find the maximum number of aliases
int max = 0;
char *tmp;
for(tmp = buffer; *tmp != 0; tmp += strlen(tmp) + 1)
max++;
aliases = new char*[max];
// Load the aliases into an array
for(tmp = buffer; *tmp != 0; tmp += strlen(tmp) + 1) {
int flag = 0;
for(int j = 0; j < MAX_INI_GROUPS; j++) {
if(!stricmp(ini_groups[j], tmp)) flag = 1;
}
if(!flag) {
aliases[alias_total] = new char[strlen(tmp)+1];
strcpy(aliases[alias_total], tmp);
alias_total++;
}
}
delete[] buffer;
}
void TConfig::print_aliases() {
for(int j = 0; j < alias_total; j++) {
char alias_name[20];
set_string(alias_name, aliases[j], sizeof(alias_name));
for(unsigned int i = strlen(alias_name); i < sizeof(alias_name) - 1; i++)
alias_name[i] = ' ';
alias_name[sizeof(alias_name) - 1] = 0;
printit(alias_name);
if((j % 4) == 3) printit("\n");
}
printit("\n");
}
bool find_alias(const char *alias_name) {
return false;
}
void TConfig::print_vars() {
int j;
for(j = 0; j < MAX_INI_VARS; j++) {
if(print_value(ini_varlist[j].name) > 40) printit("\n");
else if(j % 2) printit("\n");
else printit("\t");
}
if(j % 2) printit("\n");
}
// Paul Brannan 9/3/98
void TConfig::print_vars(char *s) {
if(!strnicmp(s, "all", 3)) { // Print out all vars
print_vars();
return;
}
// See if the group exists
int j;
for(j = 0; j < MAX_INI_GROUPS; j++)
if(!stricmp(ini_groups[j], s)) break;
// If not, print out the value of the variable by that name
if(j == MAX_INI_GROUPS) {
print_value(s);
printit("\n");
return;
}
// Print out the vars in the given group
int count = 0;
for(j = 0; j < MAX_INI_VARS; j++) {
if(ini_varlist[j].section == NULL) continue;
if(!stricmp(ini_varlist[j].section, s)) {
if(print_value(ini_varlist[j].name) > 40) printit("\n");
else if(count % 2) printit("\n");
else printit("\t");
count++;
}
}
if(count % 2) printit("\n");
}
// Paul Brannan 9/3/98
void TConfig::print_groups() {
for(int j = 0; j < MAX_INI_GROUPS; j++) {
char group_name[20];
set_string(group_name, ini_groups[j], sizeof(group_name));
for(unsigned int i = strlen(group_name); i < sizeof(group_name) - 1; i++)
group_name[i] = ' ';
group_name[sizeof(group_name) - 1] = 0;
printit(group_name);
if((j % 4) == 3) printit("\n");
}
printit("\n");
}
// Ioannou : The index in the while causes segfaults if there is no match
// changes to for(), and strcmp to stricmp (prompt gives rong names)
bool TConfig::set_value(const char *var, const char *value) {
//int j = 0;
//while(strcmp(var, ini_varlist[j].name) && j < MAX_INI_VARS) j++;
for (int j = 0; j < MAX_INI_VARS; j++)
{
if (stricmp(var, ini_varlist[j].name) == 0)
{
switch(ini_varlist[j].data_type) {
case INI_STRING:
set_string((char *)ini_varlist[j].ini_data, value,
ini_varlist[j].max_size);
break;
case INI_INT:
*(int *)ini_varlist[j].ini_data = atoi(value);
break;
case INI_BOOL:
set_bool((bool *)ini_varlist[j].ini_data, value);
break;
}
// j = MAX_INI_VARS;
return TRUE;
}
}
return FALSE;
}
int TConfig::print_value(const char *var) {
//int j = 0;
//while(strcmp(var, ini_varlist[j].name) && j < MAX_INI_VARS) j++;
int Result = 0;
for (int j = 0; j < MAX_INI_VARS; j++)
{
if (stricmp(var, ini_varlist[j].name) == 0)
{
char var_name[25];
set_string(var_name, var, sizeof(var_name));
for(unsigned int i = strlen(var_name); i < sizeof(var_name) - 1; i++)
var_name[i] = ' ';
var_name[sizeof(var_name) - 1] = 0;
Result = sizeof(var_name);
printit(var_name);
printit("\t");
Result = Result / 8 + 8;
switch(ini_varlist[j].data_type) {
case INI_STRING:
printit((char *)ini_varlist[j].ini_data);
Result += strlen((char *)ini_varlist[j].ini_data);
break;
case INI_INT:
char buffer[20]; // this may not be safe
// Ioannou : Paul this was _itoa, but Borland needs itoa !!
itoa(*(int *)ini_varlist[j].ini_data, buffer, 10);
printit(buffer);
Result += strlen(buffer);
break;
case INI_BOOL:
if(*(bool *)ini_varlist[j].ini_data == true) {
printit("on");
Result += 2;
} else {
printit("off");
Result += 3;
}
}
// printit("\n");
j = MAX_INI_VARS;
}
}
return Result;
}
void TConfig::init_vars() {
char buffer[4096];
for(int j = 0; j < MAX_INI_VARS; j++) {
if(ini_varlist[j].section != NULL) {
GetPrivateProfileString(ini_varlist[j].section, ini_varlist[j].name, "",
buffer, sizeof(buffer), inifile);
if(*buffer != 0) set_value(ini_varlist[j].name, buffer);
}
}
}
void TConfig::inifile_init() {
// B. K. Oxley 9/16/98
char* env_telnet_ini = getenv (ENV_TELNET_INI);
if (env_telnet_ini && *env_telnet_ini) {
strncpy (inifile, env_telnet_ini, sizeof(inifile));
return;
}
strcpy(inifile, startdir);
if (sizeof(inifile) >= strlen(inifile)+strlen("telnet.ini")) {
strcat(inifile,"telnet.ini"); // add the default filename to the path
} else {
// if there is not enough room set the path to nothing
strcpy(inifile,"");
}
}
void TConfig::keyfile_init() {
// check to see if there is a key config file environment variable.
char *k;
if ((k = getenv(ENV_TELNET_CFG)) == NULL){
// if there is no environment variable
GetPrivateProfileString("Keyboard", "Keyfile", "", keyfile,
sizeof(keyfile), inifile);
if (!*keyfile) {
// and there is no profile string
strcpy(keyfile, startdir);
if (sizeof(keyfile) >= strlen(keyfile)+strlen("telnet.cfg")) {
struct stat buf;
strcat(keyfile,"telnet.cfg"); // add the default filename to the path
if(stat(keyfile, &buf) != 0) {
char *s = keyfile + strlen(keyfile) - strlen("telnet.cfg");
strcpy(s, "keys.cfg");
}
} else {
// if there is not enough room set the path to nothing
strcpy(keyfile,"");
}
// Vassili Bourdo ([email protected])
} else {
// check that keyfile really exists
if( access(keyfile,04) == -1 ) {
//it does not...
char pathbuf[MAX_PATH], *fn;
//substitute keyfile path with startdir path
if((fn = strrchr(keyfile,'\\'))) strcpy(keyfile,fn);
strcat(strcpy(pathbuf,startdir),keyfile);
//check that startdir\keyfile does exist
if( access(pathbuf,04) == -1 ) {
//it does not...
//so, look for it in all paths
_searchenv(keyfile, "PATH", pathbuf);
if( *pathbuf == 0 ) //no luck - revert it to INI file value
GetPrivateProfileString("Keyboard", "Keyfile", "",
keyfile, sizeof(keyfile), inifile);
} else {
strcpy(keyfile, pathbuf);
}
}
}
////
} else {
// set the keyfile to the value of the environment variable
strncpy(keyfile, k, sizeof(keyfile));
}
}
void TConfig::redir_init() {
// check to see if the environment variable 'TELNET_REDIR' is not 0;
char* p = getenv(ENV_TELNET_REDIR);
if (p) {
input_redir = output_redir = atoi(p);
if((p = getenv(ENV_INPUT_REDIR))) input_redir = atoi(p);
if((p = getenv(ENV_OUTPUT_REDIR))) output_redir = atoi(p);
} else {
input_redir = output_redir = GetPrivateProfileInt("Terminal",
"Telnet_Redir", 0, inifile);
input_redir = GetPrivateProfileInt("Terminal",
"Input_Redir", input_redir, inifile);
output_redir = GetPrivateProfileInt("Terminal",
"Output_Redir", output_redir, inifile);
}
if ((input_redir > 1) || (output_redir > 1))
setlocale(LC_CTYPE,"");
// tell isprint() to not ignore local characters, if the environment
// variable "LANG" has a valid value (e.g. LANG=de for german characters)
// and the file LOCALE.BLL is installed somewhere along the PATH.
}
// Modified not to use getopt() by Paul Brannan 12/17/98
bool TConfig::Process_Params(int argc, char *argv[]) {
int optind = 1;
char *optarg = argv[optind];
char c;
while(optind < argc) {
if(argv[optind][0] != '-') break;
// getopt
c = argv[optind][1];
if(argv[optind][2] == 0)
optarg = argv[++optind];
else
optarg = &argv[optind][2];
optind++;
switch(c) {
case 'd':
set_string(dumpfile, optarg, sizeof(dumpfile));
printm(0, FALSE, MSG_DUMPFILE, dumpfile);
break;
// added support for setting options on the command-line
// (Paul Brannan 7/31/98)
case '-':
{
int j;
for(j = 0; optarg[j] != ' ' && optarg[j] != '=' && optarg[j] != 0; j++);
if(optarg == 0) {
printm(0, FALSE, MSG_USAGE); // print a usage message
printm(0, FALSE, MSG_USAGE_1);
return FALSE;
}
optarg[j] = 0;
if(!set_value(optarg, &optarg[j+1]))
printm(0, FALSE, MSG_BADVAL, optarg);
}
break;
default:
printm(0, FALSE, MSG_USAGE); // print a usage message
printm(0, FALSE, MSG_USAGE_1);
return FALSE;
}
}
if(optind < argc)
set_string(host, argv[optind++], sizeof(host)-1);
if(!strnicmp(host, "telnet://", 9)) {
// we have a URL to parse
char *s, *t;
for(s = host+9, t = host; *s != 0; *(t++) = *(s++));
*t = 0;
for(s = host; *s != ':' && *s != 0; s++);
if(*s != 0) {
*(s++) = 0;
port = s;
}
}
if(optind < argc)
port = argv[optind++];
return TRUE;
}
void TConfig::set_string(char *dest, const char *src, const int length) {
int l = length;
strncpy(dest, src, l);
// dest[length-1] = '\0';
// Ioannou : this messes strings - is this really needed ?
// The target string, dest, might not be null-terminated
// if the length of src is length or more.
// it should be dest[length] = '\0' for strings with length 1
// (Escape_string etc), but doesn't work with others (like host).
// dest is long enough to avoid this in all the tested cases
}
// Ioannou : ignore case for true or on
void TConfig::set_bool(bool *boolval, const char *str) {
if(!stricmp(str, "true")) *boolval = true;
else if(!stricmp(str, "on")) *boolval = true;
else *boolval = (bool)atoi(str);
}