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