1 /*
2  * Copyright (c) 2006-2009, 2013-2015, 2019-2020 Paul Mattes.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *     * Redistributions of source code must retain the above copyright
9  *       notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above copyright
11  *       notice, this list of conditions and the following disclaimer in the
12  *       documentation and/or other materials provided with the distribution.
13  *     * Neither the name of Paul Mattes nor his contributors may be used
14  *       to endorse or promote products derived from this software without
15  *       specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY PAUL MATTES "AS IS" AND ANY EXPRESS
18  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL PAUL MATTES BE LIABLE FOR ANY DIRECT,
21  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
25  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
26  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 /*
31  *	relink.c
32  *		A Windows console-based 3270 Terminal Emulator
33  *		Utility functions to read a session file and create a
34  *		compatible shortcut.
35  */
36 
37 #include "globals.h"
38 
39 #include <signal.h>
40 #include "appres.h"
41 #include "3270ds.h"
42 #include "resources.h"
43 #include "ctlr.h"
44 
45 #include "ctlrc.h"
46 #include "host.h"
47 #include "screen.h"
48 #include "task.h"
49 #include "trace.h"
50 #include "utils.h"
51 
52 #include <wincon.h>
53 
54 #include "winvers.h"
55 #include "shortcutc.h"
56 #include "windirs.h"
57 
58 #include "relinkc.h"
59 
60 codepages_t codepages[] = {
61     { "belgian",	"500",	0, L"1252"	},
62     { "belgian-euro",	"1148",	0, L"1252"	},
63     { "bracket",	"37*",	0, L"1252"	},
64     { "brazilian",	"275",	0, L"1252"	},
65     { "cp1047",		"1047",	0, L"1252"	},
66     { "cp870",		"870",	0, L"1250"	},
67     { "chinese-gb18030","1388",	1, L"936"	},
68     { "finnish",	"278",	0, L"1252"	},
69     { "finnish-euro",	"1143",	0, L"1252"	},
70     { "french",		"297",	0, L"1252"	},
71     { "french-euro",	"1147",	0, L"1252"	},
72     { "german",		"273",	0, L"1252"	},
73     { "german-euro",	"1141",	0, L"1252"	},
74     { "greek",		"875",	0, L"1253"	},
75     { "hebrew",		"424",	0, L"1255"	},
76     { "icelandic",	"871",	0, L"1252"	},
77     { "icelandic-euro",	"1149",	0, L"1252"	},
78     { "italian",	"280",	0, L"1252"	},
79     { "italian-euro",	"1144",	0, L"1252"	},
80     { "japanese-kana",	"930",  1, L"932"	},
81     { "japanese-latin",	"939",  1, L"932"	},
82     { "norwegian",	"277",	0, L"1252"	},
83     { "norwegian-euro",	"1142",	0, L"1252"	},
84     { "russian",	"880",	0, L"1251"	},
85     { "simplified-chinese","935",1,L"936"	},
86     { "spanish",	"284",	0, L"1252"	},
87     { "spanish-euro",	"1145",	0, L"1252"	},
88     { "thai",		"1160",	0, L"874"	},
89     { "traditional-chinese","937",1,L"950"	},
90     { "turkish",	"1026",	0, L"1254"	},
91     { "uk",		"285",	0, L"1252"	},
92     { "uk-euro",	"1146",	0, L"1252"	},
93     { "us-euro",	"1140",	0, L"1252"	},
94     { "us-intl",	"037",	0, L"1252"	},
95     { NULL,		NULL,	0, NULL	}
96 };
97 
98 size_t num_codepages = (sizeof(codepages) / sizeof(codepages[0])) - 1;
99 
100 /*  2             3             4             5                 */
101 int wrows[6] = { 0, 0,
102     MODEL_2_ROWS, MODEL_3_ROWS, MODEL_4_ROWS, MODEL_5_ROWS };
103 int wcols[6] = { 0, 0,
104     MODEL_2_COLS, MODEL_3_COLS, MODEL_4_COLS, MODEL_5_COLS };
105 
106 static wchar_t *
reg_font_from_cset(char * cset,int * codepage)107 reg_font_from_cset(char *cset, int *codepage)
108 {
109     unsigned i, j;
110     wchar_t *cpname = NULL;
111     wchar_t data[1024];
112     DWORD dlen;
113     HKEY key;
114     static wchar_t font[1024];
115     DWORD type;
116 
117     *codepage = 0;
118 
119     /* Search the table for a match. */
120     for (i = 0; codepages[i].name != NULL; i++) {
121 	if (!strcmp(cset, codepages[i].name)) {
122 	    cpname = codepages[i].codepage;
123 	    break;
124 	}
125     }
126 
127     /* If no match, use Lucida Console. */
128     if (cpname == NULL) {
129 	return L"Lucida Console";
130     }
131 
132     /*
133      * Look in the registry for the console font associated with the
134      * Windows code page.
135      */
136     if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
137 		"Software\\Microsoft\\Windows NT\\CurrentVersion\\"
138 		"Console\\TrueTypeFont",
139 		0,
140 		KEY_READ,
141 		&key) != ERROR_SUCCESS) {
142 	printf("RegOpenKey failed -- cannot find font\n");
143 	return L"Lucida Console";
144     }
145     dlen = sizeof(data);
146     if (RegQueryValueExW(key, cpname, NULL, &type, (LPVOID)data,
147 		&dlen) != ERROR_SUCCESS) {
148 	/* No codepage-specific match, try the default. */
149 	dlen = sizeof(data);
150 	if (RegQueryValueExW(key, L"0", NULL, &type, (LPVOID)data,
151 		    &dlen) != ERROR_SUCCESS) {
152 	    RegCloseKey(key);
153 	    printf("RegQueryValueEx failed -- cannot find font\n");
154 	    return L"Lucida Console";
155 	}
156     }
157     RegCloseKey(key);
158     if (type == REG_MULTI_SZ) {
159 	for (i = 0; i < dlen/sizeof(wchar_t); i++) {
160 	    if (data[i] == 0x0000) {
161 		break;
162 	    }
163 	}
164 	if (i + 1 >= dlen / sizeof(wchar_t) || data[i + 1] == 0x0000) {
165 	    printf("Bad registry value -- cannot find font\n");
166 	    return L"Lucida Console";
167 	}
168 	i++;
169     } else {
170 	i = 0;
171     }
172     for (j = 0; i < dlen; i++, j++) {
173 	if (j == 0 && data[i] == L'*') {
174 	    i++;
175 	}
176 	if ((font[j] = data[i]) == 0x0000) {
177 	    break;
178 	}
179     }
180     *codepage = _wtoi(cpname);
181     return font;
182 }
183 
184 /* Convert a hexadecimal digit to a nybble. */
185 static unsigned
hex(char c)186 hex(char c)
187 {
188     static char *digits = "0123456789abcdef";
189     char *pos;
190 
191     pos = strchr(digits, c);
192     if (pos == NULL) {
193 	return 0; /* XXX */
194     }
195     return (unsigned)(pos - digits);
196 }
197 
198 //#define DEBUG_EDIT 1
199 
200 int
read_user_settings(FILE * f,char ** usp)201 read_user_settings(FILE *f, char **usp)
202 {
203     int saw_star;
204     char buf[1024];
205 
206     if (usp == NULL) {
207 	return 1; /* success */
208     }
209     *usp = NULL;
210 
211     /*
212      * Read the balance of the file into a temporary buffer, ignoring
213      * the '!*' line.
214      */
215     saw_star = 0;
216     while (fgets(buf, sizeof(buf), f) != NULL) {
217 	if (!saw_star) {
218 	    if (buf[0] == '!' && buf[1] == '*') {
219 		saw_star = 1;
220 	    }
221 	    continue;
222 	}
223 	if (*usp == NULL) {
224 	    *usp = malloc(strlen(buf) + 1);
225 	    (*usp)[0] = '\0';
226 	} else {
227 	    *usp = realloc(*usp, strlen(*usp) + strlen(buf) + 1);
228 	}
229 	if (*usp == NULL) {
230 #if defined(DEBUG_EDIT) /*[*/
231 	    printf("out of memory]\n");
232 #endif /*]*/
233 	    return 0;
234 	}
235 	strcat(*usp, buf);
236     }
237     return 1;
238 }
239 
240 /*
241  * Read an existing session file.
242  * Returns 1 for success (file read and editable), 0 for failure.
243  */
244 int
read_session(FILE * f,session_t * s,char ** usp)245 read_session(FILE *f, session_t *s, char **usp)
246 {
247     char buf[1024];
248     int saw_hex = 0;
249     int saw_star = 0;
250     unsigned long csum;
251     unsigned long fcsum = 0;
252     int ver;
253     int s_off = 0;
254 
255     /*
256      * Look for the checksum and version.  Verify the version.
257      *
258      * XXX: It might be a good idea to validate each '!x' line and
259      * make sure that the length is right.
260      */
261     while (fgets(buf, sizeof(buf), f) != NULL) {
262 	if (buf[0] == '!' && buf[1] == 'x')
263 	    saw_hex = 1;
264 	else if (buf[0] == '!' && buf[1] == '*')
265 	    saw_star = 1;
266 	else if (buf[0] == '!' && buf[1] == 'c') {
267 	    if (sscanf(buf + 2, "%lx %d", &csum, &ver) != 2) {
268 #if defined(DEBUG_EDIT) /*[*/
269 		printf("[bad !c line '%s']\n", buf);
270 #endif /*]*/
271 		return 0;
272 	    }
273 	    if (ver > WIZARD_VER) {
274 #if defined(DEBUG_EDIT) /*[*/
275 		printf("[bad ver %d > %d]\n", ver, WIZARD_VER);
276 #endif /*]*/
277 		return 0;
278 	    }
279 	}
280     }
281     if (!saw_hex || !saw_star) {
282 #if defined(DEBUG_EDIT) /*[*/
283 	printf("[missing%s%s]\n", saw_hex? "": "hex", saw_star? "": "star");
284 #endif /*]*/
285 	return 0;
286     }
287 
288     /* Checksum from the top up to the '!c' line. */
289     fflush(f);
290     fseek(f, 0, SEEK_SET);
291     fcsum = 0;
292     while (fgets(buf, sizeof(buf), f) != NULL) {
293 	char *t;
294 
295 	if (buf[0] == '!' && buf[1] == 'c') {
296 	    break;
297 	}
298 
299 	for (t = buf; *t; t++) {
300 	    fcsum += *t & 0xff;
301 	}
302     }
303     if (fcsum != csum) {
304 #if defined(DEBUG_EDIT) /*[*/
305 	printf("[checksum mismatch, want 0x%08lx got 0x%08lx]\n", csum, fcsum);
306 #endif /*]*/
307 	return 0;
308     }
309 
310     /* Once more, with feeling.  Scribble onto the session structure. */
311     fflush(f);
312     fseek(f, 0, SEEK_SET);
313     s_off = 0;
314     while (fgets(buf, sizeof(buf), f) != NULL) {
315 
316 	if (buf[0] == '!' && buf[1] == 'x') {
317 	    char *t;
318 
319 	    for (t = buf + 2; *t; t += 2) {
320 		if (*t == '\n') {
321 		    break;
322 		}
323 		if (s_off > sizeof(*s)) {
324 #if defined(DEBUG_EDIT) /*[*/
325 		    printf("[s overflow: %d > %d]\n", s_off, (int)sizeof(*s));
326 #endif /*]*/
327 		    return 0;
328 		}
329 		((char *)s)[s_off++] = (hex(*t) << 4) | hex(*(t + 1));
330 	    }
331 	} else if (buf[0] == '!' && buf[1] == 'c') {
332 	    break;
333 	}
334     }
335 
336     /*
337      * Read the balance of the file into a temporary buffer, ignoring
338      * the '!*' line.
339      */
340     if (usp != NULL && read_user_settings(f, usp) == 0) {
341 	return 0;
342     }
343 
344     /* Success */
345     return 1;
346 }
347 
348 HRESULT
create_shortcut(session_t * session,char * exepath,char * linkpath,char * args,char * workingdir)349 create_shortcut(session_t *session, char *exepath, char *linkpath, char *args,
350 	char *workingdir)
351 {
352     wchar_t *font;
353     int codepage = 0;
354     int extra_height = 1;
355 
356     font = reg_font_from_cset(session->codepage, &codepage);
357 
358     if (!(session->flags & WF_NO_MENUBAR)) {
359 	extra_height += 2;
360     }
361 
362     return create_link(
363 	    exepath,		/* path to executable */
364 	    linkpath,		/* where to put the link */
365 	    "wc3270 session",	/* description */
366 	    args,		/* arguments */
367 	    workingdir,		/* working directory */
368 	    (session->ov_rows? session->ov_rows: wrows[session->model])
369 		+ extra_height,	/* console rows */
370 	    session->ov_cols? session->ov_cols: wcols[session->model],
371 				/* console columns */
372 	    font,		/* font */
373 	    session->point_size,/* point size */
374 	    codepage);		/* code page */
375 }
376