1 /***************************************************************************
2                           tqsllib.c  -  description
3                              -------------------
4     begin                : Mon May 20 2002
5     copyright            : (C) 2002 by ARRL
6     author               : Jon Bloom
7     email                : jbloom@arrl.org
8     revision             : $Id$
9  ***************************************************************************/
10 
11 #define TQSLLIB_DEF
12 
13 #include "tqsllib.h"
14 #include <string.h>
15 #include <errno.h>
16 #include <ctype.h>
17 #include <stdlib.h>
18 #include <sys/stat.h>
19 #ifdef _WIN32
20     #include <io.h>
21     #include <windows.h>
22     #include <direct.h>
23     #include <Shlobj.h>
24 #endif
25 #include <openssl/err.h>
26 #include <openssl/objects.h>
27 #include <openssl/evp.h>
28 
29 #include "tqslerrno.h"
30 #include "adif.h"
31 #include "winstrdefs.h"
32 
33 #ifdef _WIN32
34 #define MKDIR(x, y) _wmkdir((x))
35 #else
36 #define MKDIR(x, y) mkdir((x), (y))
37 #endif
38 
39 DLLEXPORTDATA int tQSL_Error = 0;
40 DLLEXPORTDATA int tQSL_Errno = 0;
41 DLLEXPORTDATA TQSL_ADIF_GET_FIELD_ERROR tQSL_ADIF_Error;
42 DLLEXPORTDATA const char *tQSL_BaseDir = 0;
43 DLLEXPORTDATA char tQSL_ErrorFile[256];
44 DLLEXPORTDATA char tQSL_CustomError[256];
45 DLLEXPORTDATA char tQSL_ImportCall[256];
46 DLLEXPORTDATA long tQSL_ImportSerial = 0;
47 DLLEXPORTDATA FILE* tQSL_DiagFile = 0;
48 
49 #define TQSL_OID_BASE "1.3.6.1.4.1.12348.1."
50 #define TQSL_OID_CALLSIGN TQSL_OID_BASE "1"
51 #define TQSL_OID_QSO_NOT_BEFORE TQSL_OID_BASE "2"
52 #define TQSL_OID_QSO_NOT_AFTER TQSL_OID_BASE "3"
53 #define TQSL_OID_DXCC_ENTITY TQSL_OID_BASE "4"
54 #define TQSL_OID_SUPERCEDED_CERT TQSL_OID_BASE "5"
55 #define TQSL_OID_CRQ_ISSUER_ORGANIZATION TQSL_OID_BASE "6"
56 #define TQSL_OID_CRQ_ISSUER_ORGANIZATIONAL_UNIT TQSL_OID_BASE "7"
57 #define TQSL_OID_CRQ_EMAIL TQSL_OID_BASE "8"
58 #define TQSL_OID_CRQ_ADDRESS1 TQSL_OID_BASE "9"
59 #define TQSL_OID_CRQ_ADDRESS2 TQSL_OID_BASE "10"
60 #define TQSL_OID_CRQ_CITY TQSL_OID_BASE "11"
61 #define TQSL_OID_CRQ_STATE TQSL_OID_BASE "12"
62 #define TQSL_OID_CRQ_POSTAL TQSL_OID_BASE "13"
63 #define TQSL_OID_CRQ_COUNTRY TQSL_OID_BASE "14"
64 
65 static const char *custom_objects[][3] = {
66 	{ TQSL_OID_CALLSIGN, "AROcallsign", "AROcallsign" },
67 	{ TQSL_OID_QSO_NOT_BEFORE, "QSONotBeforeDate", "QSONotBeforeDate" },
68 	{ TQSL_OID_QSO_NOT_AFTER, "QSONotAfterDate", "QSONotAfterDate" },
69 	{ TQSL_OID_DXCC_ENTITY, "dxccEntity", "dxccEntity" },
70 	{ TQSL_OID_SUPERCEDED_CERT, "supercededCertificate", "supercededCertificate" },
71 	{ TQSL_OID_CRQ_ISSUER_ORGANIZATION, "tqslCRQIssuerOrganization", "tqslCRQIssuerOrganization" },
72 	{ TQSL_OID_CRQ_ISSUER_ORGANIZATIONAL_UNIT,
73 			"tqslCRQIssuerOrganizationalUnit", "tqslCRQIssuerOrganizationalUnit" },
74 	{ TQSL_OID_CRQ_EMAIL, "tqslCRQEmail", "tqslCRQEmail" },
75 	{ TQSL_OID_CRQ_ADDRESS1, "tqslCRQAddress1", "tqslCRQAddress1" },
76 	{ TQSL_OID_CRQ_ADDRESS2, "tqslCRQAddress2", "tqslCRQAddress2" },
77 	{ TQSL_OID_CRQ_CITY, "tqslCRQCity", "tqslCRQCity" },
78 	{ TQSL_OID_CRQ_STATE, "tqslCRQState", "tqslCRQState" },
79 	{ TQSL_OID_CRQ_POSTAL, "tqslCRQPostal", "tqslCRQPostal" },
80 	{ TQSL_OID_CRQ_COUNTRY, "tqslCRQCountry", "tqslCRQCountry" },
81 };
82 
83 static const char *error_strings[] = {
84 	"Memory allocation failure",				/* TQSL_ALLOC_ERROR */
85 	"Unable to initialize random number generator",		/* TQSL_RANDOM_ERROR */
86 	"Invalid argument",					/* TQSL_ARGUMENT_ERROR */
87 	"Operator aborted operation",				/* TQSL_OPERATOR_ABORT */
88 	"No Certificate Request matches the selected Callsign Certificate",/* TQSL_NOKEY_ERROR */
89 	"Buffer too small",					/* TQSL_BUFFER_ERROR */
90 	"Invalid date format",					/* TQSL_INVALID_DATE */
91 	"Certificate not initialized for signing",		/* TQSL_SIGNINIT_ERROR */
92 	"Password not correct",					/* TQSL_PASSWORD_ERROR */
93 	"Expected name",					/* TQSL_EXPECTED_NAME */
94 	"Name exists",						/* TQSL_NAME_EXISTS */
95 	"Data for this DXCC entity could not be found",		/* TQSL_NAME_NOT_FOUND */
96 	"Invalid time format",					/* TQSL_INVALID_TIME */
97 	"QSO date is not within the date range specified on your Callsign Certificate",	/* TQSL_CERT_DATE_MISMATCH */
98 	"Certificate provider not found",			/* TQSL_PROVIDER_NOT_FOUND */
99 	"No callsign certificate for key",			/* TQSL_CERT_KEY_ONLY */
100 	"Configuration file cannot be opened",			/* TQSL_CONFIG_ERROR */
101 	"Callsign Certificate or Certificate Request not found",/* TQSL_CERT_NOT_FOUND */
102 	"PKCS#12 file not TQSL compatible",			/* TQSL_PKCS12_ERROR */
103 	"Callsign Certificate not TQSL compatible",		/* TQSL_CERT_TYPE_ERROR */
104 	"Date out of range",					/* TQSL_DATE_OUT_OF_RANGE */
105 	"Duplicate QSO suppressed",				/* TQSL_DUPLICATE_QSO */
106 	"Database error",					/* TQSL_DB_ERROR */
107 	"The selected station location could not be found",	/* TQSL_LOCATION_NOT_FOUND */
108 	"The selected callsign could not be found",		/* TQSL_CALL_NOT_FOUND */
109 	"The TQSL configuration file cannot be parsed",		/* TQSL_CONFIG_SYNTAX_ERROR */
110 	"This file can not be processed due to a system error",	/* TQSL_FILE_SYSTEM_ERROR */
111 	"The format of this file is incorrect.",		/* TQSL_FILE_SYNTAX_ERROR */
112 	"This Callsign Certificate could not be installed", 	/* TQSL_CERT_ERROR */
113 };
114 
115 const char* tqsl_openssl_error(void);
116 
117 #if defined(_WIN32)
pmkdir(const wchar_t * path,int perm)118 static int pmkdir(const wchar_t *path, int perm) {
119 	wchar_t dpath[TQSL_MAX_PATH_LEN];
120 	wchar_t npath[TQSL_MAX_PATH_LEN];
121 	wchar_t *cp;
122 	char *p = wchar_to_utf8(path, true);
123 	tqslTrace("pmkdir", "path=%s", p);
124 	free(p);
125 	int nleft = (sizeof npath / 2) - 1;
126 	wcsncpy(dpath, path, (sizeof dpath / 2));
127 	cp = wcstok(dpath, L"/\\");
128 	npath[0] = 0;
129 	while (cp) {
130 		if (wcslen(cp) > 0 && cp[wcslen(cp)-1] != ':') {
131 			wcsncat(npath, L"\\", nleft);
132 			nleft--;
133 			wcsncat(npath, cp, nleft);
134 			nleft -= wcslen(cp);
135 			if (MKDIR(npath, perm) != 0 && errno != EEXIST) {
136 				tqslTrace("pmkdir", "Error creating %s: %s", npath, strerror(errno));
137 				return 1;
138 			}
139 		} else {
140 			wcsncat(npath, cp, nleft);
141 			nleft -= wcslen(cp);
142 		}
143 		cp = wcstok(NULL, L"/\\");
144 	}
145 	return 0;
146 }
147 
148 #else // defined(_WIN32)
pmkdir(const char * path,int perm)149 static int pmkdir(const char *path, int perm) {
150 	char dpath[TQSL_MAX_PATH_LEN];
151 	char npath[TQSL_MAX_PATH_LEN];
152 	char *cp;
153 	tqslTrace("pmkdir", "path=%s", path);
154 	int nleft = sizeof npath - 1;
155 	strncpy(dpath, path, sizeof dpath);
156 	cp = strtok(dpath, "/\\");
157 	npath[0] = 0;
158 	while (cp) {
159 		if (strlen(cp) > 0 && cp[strlen(cp)-1] != ':') {
160 			strncat(npath, "/", nleft);
161 			nleft--;
162 			strncat(npath, cp, nleft);
163 			nleft -= strlen(cp);
164 			if (MKDIR(npath, perm) != 0 && errno != EEXIST) {
165 				tqslTrace("pmkdir", "Error creating %s: %s", npath, strerror(errno));
166 				return 1;
167 			}
168 		} else {
169 			strncat(npath, cp, nleft);
170 			nleft -= strlen(cp);
171 		}
172 		cp = strtok(NULL, "/\\");
173 	}
174 	return 0;
175 }
176 #endif // defined(_WIN32)
177 
178 DLLEXPORT int CALLCONVENTION
tqsl_init()179 tqsl_init() {
180 	static char semaphore = 0;
181 	unsigned int i;
182 #ifdef _WIN32
183 	static wchar_t path[TQSL_MAX_PATH_LEN * 2];
184 	// lets cin/out/err work in windows
185 	// AllocConsole();
186 	// freopen("CONIN$", "r", stdin);
187 // freopen("CONOUT$", "w", stdout);
188 // freopen("CONOUT$", "w", stderr);
189 	DWORD bsize = sizeof path;
190 	int wval;
191 #else
192 	static char path[TQSL_MAX_PATH_LEN];
193 #endif
194 
195 #if !defined(_WIN32) && !defined(__APPLE__)
196 // Work around ill-considered decision by Fedora to stop allowing
197 // certificates with MD5 signatures
198 	setenv("OPENSSL_ENABLE_MD5_VERIFY", "1", 0);
199 #endif
200 
201 	/* OpenSSL API tends to change between minor version numbers, so make sure
202 	 * we're using the right version */
203 #if OPENSSL_VERSION_NUMBER >= 0x10100000L
204 	unsigned long SSLver = OpenSSL_version_num();
205 #else
206 	long SSLver = SSLeay();
207 #endif
208 	int SSLmajor = (SSLver >> 28) & 0xff;
209 	int SSLminor = (SSLver >> 20) & 0xff;
210 	int TQSLmajor = (OPENSSL_VERSION_NUMBER >> 28) & 0xff;
211 	int TQSLminor =  (OPENSSL_VERSION_NUMBER >> 20) & 0xff;
212 
213 	if (SSLmajor != TQSLmajor ||
214 		(SSLminor != TQSLminor &&
215 		(SSLmajor != 9 && SSLminor != 7 && TQSLminor == 6))) {
216 		tqslTrace("tqsl_init", "version error - ssl %d.%d", SSLmajor, SSLminor);
217 		tQSL_Error = TQSL_OPENSSL_VERSION_ERROR;
218 		return 1;
219 	}
220 	ERR_clear_error();
221 	tqsl_getErrorString();	/* Clear the error status */
222 	if (semaphore)
223 		return 0;
224 #if OPENSSL_VERSION_NUMBER < 0x10100000L
225 	ERR_load_crypto_strings();
226 	OpenSSL_add_all_algorithms();
227 #endif
228 	for (i = 0; i < (sizeof custom_objects / sizeof custom_objects[0]); i++) {
229 		if (OBJ_create(custom_objects[i][0], custom_objects[i][1], custom_objects[i][2]) == 0) {
230 			tqslTrace("tqsl_init", "Error making custom objects: %s", tqsl_openssl_error());
231 			tQSL_Error = TQSL_OPENSSL_ERROR;
232 			return 1;
233 		}
234 	}
235 	if (tQSL_BaseDir == NULL) {
236 #if defined(_WIN32)
237 		wchar_t *wcp;
238 		if ((wcp = _wgetenv(L"TQSLDIR")) != NULL && *wcp != '\0') {
239 			wcsncpy(path, wcp, sizeof path);
240 #else
241 		char *cp;
242 		if ((cp = getenv("TQSLDIR")) != NULL && *cp != '\0') {
243 			strncpy(path, cp, sizeof path);
244 #endif
245 		} else {
246 #if defined(_WIN32)
247 			wval = SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, 0, path);
248 			if (wval != ERROR_SUCCESS)
249 				wcsncpy(path, L"C:", sizeof path);
250 			wcsncat(path, L"\\TrustedQSL", sizeof path - wcslen(path) - 1);
251 #elif defined(LOTW_SERVER)
252 			strncpy(path, "/var/lotw/tqsl", sizeof path);
253 #else  // some unix flavor
254 			if (getenv("HOME") != NULL) {
255 				strncpy(path, getenv("HOME"), sizeof path);
256 				strncat(path, "/", sizeof path - strlen(path)-1);
257 				strncat(path, ".tqsl", sizeof path - strlen(path)-1);
258 			} else {
259 				strncpy(path, ".tqsl", sizeof path);
260 			}
261 #endif
262 		}
263 		if (pmkdir(path, 0700)) {
264 #if defined(_WIN32)
265 			char *p = wchar_to_utf8(path, false);
266 			strncpy(tQSL_ErrorFile, p, sizeof tQSL_ErrorFile);
267 #else
268 			strncpy(tQSL_ErrorFile, path, sizeof tQSL_ErrorFile);
269 #endif
270 			tQSL_Error = TQSL_SYSTEM_ERROR;
271 			tQSL_Errno = errno;
272 #if defined(_WIN32)
273 			tqslTrace("tqsl_init", "Error creating working path %s: %s", p, strerror(errno));
274 			free(p);
275 #else
276 			tqslTrace("tqsl_init", "Error creating working path %s: %s", path, strerror(errno));
277 #endif
278 			return 1;
279 		}
280 #if defined(_WIN32)
281 		tQSL_BaseDir = wchar_to_utf8(path, true);
282 #else
283 		tQSL_BaseDir = path;
284 #endif
285 	}
286 	semaphore = 1;
287 	return 0;
288 }
289 
290 DLLEXPORT int CALLCONVENTION
291 tqsl_setDirectory(const char *dir) {
292 	static char path[TQSL_MAX_PATH_LEN];
293 	if (strlen(dir) >= TQSL_MAX_PATH_LEN) {
294 		tQSL_Error = TQSL_BUFFER_ERROR;
295 		return 1;
296 	}
297 	strncpy(path, dir, sizeof path);
298 	tQSL_BaseDir = path;
299 	return 0;
300 }
301 
302 DLLEXPORT const char* CALLCONVENTION
303 tqsl_getErrorString_v(int err) {
304 	static char buf[256];
305 	unsigned long openssl_err;
306 	int adjusted_err;
307 
308 	if (err == 0)
309 		return "NO ERROR";
310 	if (err == TQSL_CUSTOM_ERROR) {
311 		if (tQSL_CustomError[0] == 0) {
312 			return "Unknown custom error";
313 		} else {
314 			strncpy(buf, tQSL_CustomError, sizeof buf);
315 			return buf;
316 		}
317 	}
318 	if (err == TQSL_DB_ERROR && tQSL_CustomError[0] != 0) {
319 		snprintf(buf, sizeof buf, "Database Error: %s", tQSL_CustomError);
320 		return buf;
321 	}
322 
323 	if (err == TQSL_SYSTEM_ERROR || err == TQSL_FILE_SYSTEM_ERROR) {
324 		if (strlen(tQSL_ErrorFile) > 0) {
325 			snprintf(buf, sizeof buf, "System error: %s : %s",
326 				tQSL_ErrorFile, strerror(tQSL_Errno));
327 			tQSL_ErrorFile[0] = '\0';
328 		} else {
329 			snprintf(buf, sizeof buf, "System error: %s",
330 				strerror(tQSL_Errno));
331 		}
332 		return buf;
333 	}
334 	if (err == TQSL_FILE_SYNTAX_ERROR) {
335 		if (strlen(tQSL_ErrorFile) > 0) {
336 			snprintf(buf, sizeof buf, "File syntax error: %s",
337 				tQSL_ErrorFile);
338 			tQSL_ErrorFile[0] = '\0';
339 		} else {
340 			strncpy(buf, "File syntax error", sizeof buf);
341 		}
342 		return buf;
343 	}
344 	if (err == TQSL_OPENSSL_ERROR) {
345 		openssl_err = ERR_get_error();
346 		strncpy(buf, "OpenSSL error: ", sizeof buf);
347 		if (openssl_err)
348 			ERR_error_string_n(openssl_err, buf + strlen(buf), sizeof buf - strlen(buf)-1);
349 		else
350 			strncat(buf, "[error code not available]", sizeof buf - strlen(buf)-1);
351 		return buf;
352 	}
353 	if (err == TQSL_ADIF_ERROR) {
354 		buf[0] = 0;
355 		if (strlen(tQSL_ErrorFile) > 0) {
356 			snprintf(buf, sizeof buf, "%s: %s",
357 				tQSL_ErrorFile, tqsl_adifGetError(tQSL_ADIF_Error));
358 			tQSL_ErrorFile[0] = '\0';
359 		} else {
360 			snprintf(buf, sizeof buf, "%s",
361 				tqsl_adifGetError(tQSL_ADIF_Error));
362 		}
363 		return buf;
364 	}
365 	if (err == TQSL_CABRILLO_ERROR) {
366 		buf[0] = 0;
367 		if (strlen(tQSL_ErrorFile) > 0) {
368 			snprintf(buf, sizeof buf, "%s: %s",
369 				tQSL_ErrorFile, tqsl_cabrilloGetError(tQSL_Cabrillo_Error));
370 			tQSL_ErrorFile[0] = '\0';
371 		} else {
372 			snprintf(buf, sizeof buf, "%s",
373 				tqsl_cabrilloGetError(tQSL_Cabrillo_Error));
374 		}
375 		return buf;
376 	}
377 	if (err == TQSL_OPENSSL_VERSION_ERROR) {
378 #if OPENSSL_VERSION_NUMBER >= 0x10100000L
379 		unsigned long SSLver = OpenSSL_version_num();
380 #else
381 		long SSLver = SSLeay();
382 #endif
383 		snprintf(buf, sizeof buf,
384 			"Incompatible OpenSSL Library version %d.%d.%d; expected %d.%d.%d",
385 			static_cast<int>(SSLver >> 28) & 0xff,
386 			static_cast<int>(SSLver >> 20) & 0xff,
387 			static_cast<int>(SSLver >> 12) & 0xff,
388 			static_cast<int>(OPENSSL_VERSION_NUMBER >> 28) & 0xff,
389 			static_cast<int>(OPENSSL_VERSION_NUMBER >> 20) & 0xff,
390 			static_cast<int>(OPENSSL_VERSION_NUMBER >> 12) & 0xff);
391 		return buf;
392 	}
393 	if (err == TQSL_CERT_NOT_FOUND && tQSL_ImportCall[0] != '\0') {
394 		snprintf(buf, sizeof buf,
395 			"Callsign Certificate or Certificate Request not found for callsign %s serial %ld",
396 			tQSL_ImportCall, tQSL_ImportSerial);
397 		tQSL_ImportCall[0] = '\0';
398 		return buf;
399 	}
400 	adjusted_err = err - TQSL_ERROR_ENUM_BASE;
401 	if (adjusted_err < 0 ||
402 	    adjusted_err >=
403 		static_cast<int>(sizeof error_strings / sizeof error_strings[0])) {
404 		snprintf(buf, sizeof buf, "Invalid error code: %d", err);
405 		return buf;
406 	}
407 	return error_strings[adjusted_err];
408 }
409 
410 DLLEXPORT const char* CALLCONVENTION
411 tqsl_getErrorString() {
412 	const char *cp;
413 	cp = tqsl_getErrorString_v(tQSL_Error);
414 	tQSL_Error = TQSL_NO_ERROR;
415 	tQSL_Errno = 0;
416 	tQSL_ErrorFile[0] = 0;
417 	tQSL_CustomError[0] = 0;
418 	return cp;
419 }
420 
421 DLLEXPORT int CALLCONVENTION
422 tqsl_encodeBase64(const unsigned char *data, int datalen, char *output, int outputlen) {
423 	BIO *bio = NULL, *bio64 = NULL;
424 	int n;
425 	char *memp;
426 	int rval = 1;
427 
428 	if (data == NULL || output == NULL) {
429 		tQSL_Error = TQSL_ARGUMENT_ERROR;
430 		tqslTrace("tqsl_encodeBase64", "arg err data=0x%lx, output=0x%lx", data, output);
431 		return rval;
432 	}
433 	if ((bio = BIO_new(BIO_s_mem())) == NULL) {
434 		tqslTrace("tqsl_encodeBase64", "BIO_new err %s", tqsl_openssl_error());
435 		goto err;
436 	}
437 	if ((bio64 = BIO_new(BIO_f_base64())) == NULL) {
438 		tqslTrace("tqsl_encodeBase64", "BIO_new64 err %s", tqsl_openssl_error());
439 		goto err;
440 	}
441 	bio = BIO_push(bio64, bio);
442 	if (BIO_write(bio, data, datalen) < 1) {
443 		tqslTrace("tqsl_encodeBase64", "BIO_write err %s", tqsl_openssl_error());
444 		goto err;
445 	}
446 	if (BIO_flush(bio) != 1) {
447 		tqslTrace("tqsl_encodeBase64", "BIO_flush err %s", tqsl_openssl_error());
448 		goto err;
449 	}
450 	n = BIO_get_mem_data(bio, &memp);
451 	if (n > outputlen-1) {
452 		tqslTrace("tqsl_encodeBase64", "buffer has %d, avail %d", n, outputlen);
453 		tQSL_Error = TQSL_BUFFER_ERROR;
454 		goto end;
455 	}
456 	memcpy(output, memp, n);
457 	output[n] = 0;
458 	BIO_free_all(bio);
459 	bio = NULL;
460 	rval = 0;
461 	goto end;
462 
463  err:
464 	tQSL_Error = TQSL_OPENSSL_ERROR;
465  end:
466 	if (bio != NULL)
467 		BIO_free_all(bio);
468 	return rval;
469 }
470 
471 DLLEXPORT int CALLCONVENTION
472 tqsl_decodeBase64(const char *input, unsigned char *data, int *datalen) {
473 	BIO *bio = NULL, *bio64 = NULL;
474 	int n;
475 	int rval = 1;
476 
477 	if (input == NULL || data == NULL || datalen == NULL) {
478 		tqslTrace("tqsl_decodeBase64", "arg error input=0x%lx, data=0x%lx, datalen=0x%lx", input, data, datalen);
479 		tQSL_Error = TQSL_ARGUMENT_ERROR;
480 		return rval;
481 	}
482 	if ((bio = BIO_new_mem_buf(const_cast<char *>(input), strlen(input))) == NULL) {
483 		tqslTrace("tqsl_decodeBase64", "BIO_new_mem_buf err %s", tqsl_openssl_error());
484 		goto err;
485 	}
486 	BIO_set_mem_eof_return(bio, 0);
487 	if ((bio64 = BIO_new(BIO_f_base64())) == NULL) {
488 		tqslTrace("tqsl_decodeBase64", "BIO_new err %s", tqsl_openssl_error());
489 		goto err;
490 	}
491 	bio = BIO_push(bio64, bio);
492 	n = BIO_read(bio, data, *datalen);
493 	if (n < 0) {
494 		tqslTrace("tqsl_decodeBase64", "BIO_read error %s", tqsl_openssl_error());
495 		goto err;
496 	}
497 	if (BIO_ctrl_pending(bio) != 0) {
498 		tqslTrace("tqsl_decodeBase64", "ctrl_pending err %s", tqsl_openssl_error());
499 		tQSL_Error = TQSL_BUFFER_ERROR;
500 		goto end;
501 	}
502 	*datalen = n;
503 	rval = 0;
504 	goto end;
505 
506  err:
507 	tQSL_Error = TQSL_OPENSSL_ERROR;
508  end:
509 	if (bio != NULL)
510 		BIO_free_all(bio);
511 	return rval;
512 }
513 
514 /* Convert a tQSL_Date field to an ISO-format date string
515  */
516 DLLEXPORT char* CALLCONVENTION
517 tqsl_convertDateToText(const tQSL_Date *date, char *buf, int bufsiz) {
518 	char lbuf[10];
519 	int len;
520 	char *cp = buf;
521 	int bufleft = bufsiz-1;
522 
523 	if (date == NULL || buf == NULL) {
524 		tQSL_Error = TQSL_ARGUMENT_ERROR;
525 		if (buf) buf[0] = '\0';
526 		return NULL;
527 	}
528 	if (date->year < 1 || date->year > 9999 || date->month < 1
529 		|| date->month > 12 || date->day < 1 || date->day > 31) {
530 		buf[0] = '\0';
531 		return NULL;
532 	}
533 	len = snprintf(lbuf, sizeof lbuf, "%04d-", date->year);
534 	strncpy(cp, lbuf, bufleft);
535 	cp += len;
536 	bufleft -= len;
537 	len = snprintf(lbuf, sizeof lbuf, "%02d-", date->month);
538 	if (bufleft > 0)
539 		strncpy(cp, lbuf, bufleft);
540 	cp += len;
541 	bufleft -= len;
542 	len = snprintf(lbuf, sizeof lbuf, "%02d", date->day);
543 	if (bufleft > 0)
544 		strncpy(cp, lbuf, bufleft);
545 	bufleft -= len;
546 	if (bufleft < 0)
547 		return NULL;
548 	buf[bufsiz-1] = '\0';
549 	return buf;
550 }
551 
552 DLLEXPORT int CALLCONVENTION
553 tqsl_isDateValid(const tQSL_Date *d) {
554 	static int mon_days[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
555 
556 	if (d == NULL) {
557 		tQSL_Error = TQSL_ARGUMENT_ERROR;
558 		return 0;
559 	}
560 	if (d->year < 1 || d->year > 9999)
561 		return 0;
562 	if (d->month < 1 || d->month > 12)
563 		return 0;
564 	if (d->day < 1 || d->day > 31)
565 		return 0;
566 	mon_days[2] = ((d->year % 4) == 0 &&
567 		      ((d->year % 100) != 0 || (d->year % 400) == 0))
568 		? 29 : 28;
569 	if (d->day > mon_days[d->month])
570 		return 0;
571 	return 1;
572 }
573 
574 DLLEXPORT int CALLCONVENTION
575 tqsl_isDateNull(const tQSL_Date *d) {
576 	if (d == NULL) {
577 		tQSL_Error = TQSL_ARGUMENT_ERROR;
578 		return 1;
579 	}
580 	return (d->year == 0 && d->month == 0 && d->day == 0) ? 1 : 0;
581 }
582 
583 /* Convert a tQSL_Time field to an ISO-format date string
584  */
585 DLLEXPORT char* CALLCONVENTION
586 tqsl_convertTimeToText(const tQSL_Time *time, char *buf, int bufsiz) {
587 	char lbuf[10];
588 	int len;
589 	char *cp = buf;
590 	int bufleft = bufsiz-1;
591 
592 	if (time == NULL || buf == NULL) {
593 		tQSL_Error = TQSL_ARGUMENT_ERROR;
594 		return NULL;
595 	}
596 	if (!tqsl_isTimeValid(time))
597 		return NULL;
598 	len = snprintf(lbuf, sizeof lbuf, "%02d:", time->hour);
599 	strncpy(cp, lbuf, bufleft);
600 	cp += len;
601 	bufleft -= len;
602 	len = snprintf(lbuf, sizeof lbuf, "%02d:", time->minute);
603 	if (bufleft > 0)
604 		strncpy(cp, lbuf, bufleft);
605 	cp += len;
606 	bufleft -= len;
607 	len = snprintf(lbuf, sizeof lbuf, "%02d", time->second);
608 	if (bufleft > 0)
609 		strncpy(cp, lbuf, bufleft);
610 	cp += len;
611 	bufleft -= len;
612 	if (bufleft > 0)
613 		strncpy(cp, "Z", bufleft);
614 	bufleft -= 1;
615 	if (bufleft < 0)
616 		return NULL;
617 	buf[bufsiz-1] = '\0';
618 	return buf;
619 }
620 
621 DLLEXPORT int CALLCONVENTION
622 tqsl_isTimeValid(const tQSL_Time *t) {
623 	if (t == NULL) {
624 		tQSL_Error = TQSL_ARGUMENT_ERROR;
625 		return 0;
626 	}
627 	if (t->hour < 0 || t->hour > 23)
628 		return 0;
629 	if (t->minute < 0 || t->minute > 59)
630 		return 0;
631 	if (t->second < 0 || t->second > 59)
632 		return 0;
633 	return 1;
634 }
635 
636 /* Compare two tQSL_Date values, returning -1, 0, 1
637  */
638 DLLEXPORT int CALLCONVENTION
639 tqsl_compareDates(const tQSL_Date *a, const tQSL_Date *b) {
640 	if (a == NULL || b == NULL) {
641 		tQSL_Error = TQSL_ARGUMENT_ERROR;
642 		return 1;
643 	}
644 	if (a->year < b->year)
645 		return -1;
646 	if (a->year > b->year)
647 		return 1;
648 	if (a->month < b->month)
649 		return -1;
650 	if (a->month > b->month)
651 		return 1;
652 	if (a->day < b->day)
653 		return -1;
654 	if (a->day > b->day)
655 		return 1;
656 	return 0;
657 }
658 
659 // Return the number of days for a given year/month (January=1)
660 static int
661 days_per_month(int year, int month) {
662 	switch (month) {
663 		case 2:
664 			if ((((year % 4) == 0) && ((year % 100) != 0)) || ((year % 400) == 0)) {
665 				return 29;
666 			} else {
667 				return 28;
668 			}
669 		case 4:
670 		case 6:
671 		case 9:
672 		case 11:
673 			return 30;
674 		default:
675 			return 31;
676 	}
677 	return 0;
678 }
679 
680 // Return the julian day number for a given date.
681 // One-based year/month/day
682 static int
683 julian_day(int year, int month, int day) {
684 	int jday = 0;
685 	for (int mon = 1; mon < month; mon ++) {
686 		jday += days_per_month(year, mon);
687 	}
688 	jday += day;
689 	return jday;
690 }
691 
692 /* Calculate the difference between two tQSL_Date values
693  */
694 DLLEXPORT int CALLCONVENTION
695 tqsl_subtractDates(const tQSL_Date *a, const tQSL_Date *b, int *diff) {
696 	if (a == NULL || b == NULL || diff == NULL) {
697 		tQSL_Error = TQSL_ARGUMENT_ERROR;
698 		return 1;
699 	}
700 	tQSL_Date first = *a;
701 	tQSL_Date last = *b;
702 	int mult = 1;
703 	// Ensure that the first is earliest
704 	if (tqsl_compareDates(&last, &first) < 0) {
705 		first = *b;
706 		last = *a;
707 		mult = -1;
708 	}
709 	int delta = 0;
710 	for (; first.year < last.year; first.year++) {
711 		int fday = julian_day(first.year, first.month, first.day);
712 		int fend = julian_day(first.year, 12, 31);
713 		delta += (fend - fday + 1);  // days until next 1 Jan
714 		first.month = 1;
715 		first.day = 1;
716 	}
717 	// Now the years are the same - calculate delta
718 	int fjulian = julian_day(first.year, first.month, first.day);
719 	int ljulian = julian_day(last.year, last.month, last.day);
720 
721 	delta += (ljulian - fjulian);
722 	*diff = (delta * mult);			// Swap sign if necessary
723 	return 0;
724 }
725 
726 /* Fill a tQSL_Date struct with the date from a text string
727  */
728 DLLEXPORT int CALLCONVENTION
729 tqsl_initDate(tQSL_Date *date, const char *str) {
730 	const char *cp;
731 
732 	if (date == NULL) {
733 		tQSL_Error = TQSL_ARGUMENT_ERROR;
734 		return 1;
735 	}
736 	if (str == NULL) {
737 		date->year = date->month = date->day = 0;
738 		return 0;
739 	}
740 	if ((cp = strchr(str, '-')) != NULL) {
741 		/* Parse YYYY-MM-DD */
742 		date->year = strtol(str, NULL, 10);
743 		cp++;
744 		date->month = strtol(cp, NULL, 10);
745 		cp = strchr(cp, '-');
746 		if (cp == NULL)
747 			goto err;
748 		cp++;
749 		date->day = strtol(cp, NULL, 10);
750 	} else if (strlen(str) == 8) {
751 		/* Parse YYYYMMDD */
752 		char frag[10];
753 		strncpy(frag, str, 4);
754 		frag[4] = 0;
755 		date->year = strtol(frag, NULL, 10);
756 		strncpy(frag, str+4, 2);
757 		frag[2] = 0;
758 		date->month = strtol(frag, NULL, 10);
759 		date->day = strtol(str+6, NULL, 10);
760 	} else {	/* Invalid ISO date string */
761 		goto err;
762 	}
763 	if (date->year < 1 || date->year > 9999)
764 		goto err;
765 	if (date->month < 1 || date->month > 12)
766 		goto err;
767 	if (date->day < 1 || date->day > 31)
768 		goto err;
769 	return 0;
770  err:
771 	tQSL_Error = TQSL_INVALID_DATE;
772 		return 1;
773 }
774 
775 /* Fill a tQSL_Time struct with the time from a text string
776  */
777 DLLEXPORT int CALLCONVENTION
778 tqsl_initTime(tQSL_Time *time, const char *str) {
779 	const char *cp;
780 	int parts[3];
781 	int i;
782 
783 	if (time == NULL) {
784 		tQSL_Error = TQSL_ARGUMENT_ERROR;
785 		return 1;
786 	}
787 	time->hour = time->minute = time->second = 0;
788 	if (str == NULL)
789 		return 0;
790 	if (strlen(str) < 3) {
791 		tQSL_Error = TQSL_INVALID_TIME;
792 		return 1;
793 	}
794 	parts[0] = parts[1] = parts[2] = 0;
795 	for (i = 0, cp = str; i < static_cast<int>(sizeof parts / sizeof parts[0]); i++) {
796 		if (strlen(cp) < 2)
797 			break;
798 		if (!isdigit(*cp) || !isdigit(*(cp+1)))
799 			goto err;
800 		if (i == 0 && strlen(str) == 3) {
801 			// Special case: HMM -- no colons, one-digit hour
802 			parts[i] = *cp - '0';
803 			++cp;
804 		} else {
805 			parts[i] = (*cp - '0') * 10 + *(cp+1) - '0';
806 			cp += 2;
807 		}
808 		if (*cp == ':')
809 			cp++;
810 	}
811 
812 	if (parts[0] < 0 || parts[0] > 23)
813 		goto err;
814 	if (parts[1] < 0 || parts[1] > 59)
815 		goto err;
816 	if (parts[2] < 0 || parts[2] > 59)
817 		goto err;
818 	time->hour = parts[0];
819 	time->minute = parts[1];
820 	time->second = parts[2];
821 	return 0;
822  err:
823 	tQSL_Error = TQSL_INVALID_TIME;
824 		return 1;
825 }
826 
827 DLLEXPORT int CALLCONVENTION
828 tqsl_getVersion(int *major, int *minor) {
829 	if (major)
830 		*major = TQSLLIB_VERSION_MAJOR;
831 	if (minor)
832 		*minor = TQSLLIB_VERSION_MINOR;
833 	return 0;
834 }
835 #ifdef _WIN32
836 DLLEXPORT wchar_t* CALLCONVENTION
837 utf8_to_wchar(const char* str) {
838 	wchar_t* buffer;
839 	int needed = MultiByteToWideChar(CP_UTF8, 0, str, -1, 0, 0);
840 	buffer = static_cast<wchar_t *>(malloc(needed*sizeof(wchar_t) + 4));
841 	if (!buffer)
842 		return NULL;
843 	MultiByteToWideChar(CP_UTF8, 0, str, -1, &buffer[0], needed);
844 	return buffer;
845 }
846 
847 DLLEXPORT char* CALLCONVENTION
848 wchar_to_utf8(const wchar_t* str, bool forceUTF8) {
849 	char* buffer;
850 	int needed = WideCharToMultiByte(forceUTF8 ? CP_UTF8 : CP_ACP, 0, str, -1, 0, 0, NULL, NULL);
851 	buffer = static_cast<char *>(malloc(needed + 2));
852 	if (!buffer)
853 		return NULL;
854 	WideCharToMultiByte(forceUTF8 ? CP_UTF8 : CP_ACP, 0, str, -1, &buffer[0], needed, NULL, NULL);
855 	return buffer;
856 }
857 
858 DLLEXPORT void CALLCONVENTION
859 free_wchar(wchar_t* ptr) {
860 	free(ptr);
861 }
862 #endif
863 
864 DLLEXPORT void CALLCONVENTION
865 tqslTrace(const char *name, const char *format, ...) {
866 	va_list ap;
867 	if (!tQSL_DiagFile) return;
868 
869 	time_t t = time(0);
870 	char timebuf[50];
871 	strncpy(timebuf, ctime(&t), sizeof timebuf);
872 	timebuf[strlen(timebuf) - 1] = '\0';		// Strip the newline
873 	if (!format) {
874 		fprintf(tQSL_DiagFile, "%s %s\r\n", timebuf, name);
875 		fflush(tQSL_DiagFile);
876 		return;
877 	} else {
878 		if (name) {
879 			fprintf(tQSL_DiagFile, "%s %s: ", timebuf, name);
880 		}
881 	}
882 	va_start(ap, format);
883 	vfprintf(tQSL_DiagFile, format, ap);
884 	va_end(ap);
885 	fprintf(tQSL_DiagFile, "\r\n");
886 	fflush(tQSL_DiagFile);
887 }
888 
889 DLLEXPORT void CALLCONVENTION
890 tqsl_closeDiagFile(void) {
891 	if (tQSL_DiagFile)
892 		fclose(tQSL_DiagFile);
893 	tQSL_DiagFile = NULL;
894 }
895 
896 DLLEXPORT int CALLCONVENTION
897 tqsl_diagFileOpen(void) {
898 	return tQSL_DiagFile != NULL;
899 }
900 
901 DLLEXPORT int CALLCONVENTION
902 tqsl_openDiagFile(const char *fname) {
903 #ifdef _WIN32
904 	wchar_t* lfn = utf8_to_wchar(fname);
905 	tQSL_DiagFile = _wfopen(lfn, L"wb");
906 	free_wchar(lfn);
907 #else
908 	tQSL_DiagFile = fopen(fname, "wb");
909 #endif
910 	return (tQSL_DiagFile == NULL);
911 }
912 
913 const char*
914 tqsl_openssl_error(void) {
915 	static char buf[256];
916 	int openssl_err;
917 
918 	openssl_err = ERR_peek_error();
919 	if (openssl_err)
920 		ERR_error_string_n(openssl_err, buf, sizeof buf);
921 	else
922 		strncpy(buf, "[error code not available]", sizeof buf);
923 	return buf;
924 }
925 
926