1 /***************************************************************************
2                           location.cpp  -  description
3                              -------------------
4     begin                : Wed Nov 6 2002
5     copyright            : (C) 2002 by ARRL
6     author               : Jon Bloom
7     email                : jbloom@arrl.org
8     revision             : $Id: location.cpp,v 1.14 2013/03/01 13:20:30 k1mu Exp $
9  ***************************************************************************/
10 
11 
12 #define DXCC_TEST
13 
14 #define TQSLLIB_DEF
15 
16 #include "location.h"
17 
18 #include <errno.h>
19 #include <stdlib.h>
20 #include <zlib.h>
21 #ifdef __APPLE__
22 #include <CoreFoundation/CFBundle.h>
23 #endif
24 #include <cstring>
25 #include <fstream>
26 #include <algorithm>
27 #include <vector>
28 #include <iostream>
29 #include <utility>
30 #include <map>
31 #include <string>
32 #include <cctype>
33 #include <functional>
34 #include "tqsllib.h"
35 #include "tqslerrno.h"
36 #include "xml.h"
37 #include "openssl_cert.h"
38 #ifdef _WIN32
39 	#include "windows.h"
40 	#include <fcntl.h>
41 #endif
42 
43 #include "winstrdefs.h"
44 
45 using std::string;
46 using std::vector;
47 using std::map;
48 using std::pair;
49 using std::make_pair;
50 using std::ofstream;
51 using std::ios;
52 using std::endl;
53 using std::exception;
54 
55 namespace tqsllib {
56 
57 class TQSL_LOCATION_ITEM {
58  public:
TQSL_LOCATION_ITEM()59 	TQSL_LOCATION_ITEM() : ivalue(0) {}
60 	string text;
61 	string label;
62 	string zonemap;
63 	int ivalue;
64 };
65 
66 class TQSL_LOCATION_FIELD {
67  public:
TQSL_LOCATION_FIELD()68 	TQSL_LOCATION_FIELD() {}
69 	TQSL_LOCATION_FIELD(string i_gabbi_name, const char *i_label, int i_data_type, int i_data_len,
70 		int i_input_type, int i_flags = 0);
71 	string label;
72 	string gabbi_name;
73 	int data_type;
74 	int data_len;
75 	string cdata;
76 	vector<TQSL_LOCATION_ITEM> items;
77 	int idx;
78 	int idata;
79 	int input_type;
80 	int flags;
81 	bool changed;
82 	string dependency;
83 };
84 
TQSL_LOCATION_FIELD(string i_gabbi_name,const char * i_label,int i_data_type,int i_data_len,int i_input_type,int i_flags)85 TQSL_LOCATION_FIELD::TQSL_LOCATION_FIELD(string i_gabbi_name, const char *i_label, int i_data_type,
86 	int i_data_len, int i_input_type, int i_flags) : data_type(i_data_type), data_len(i_data_len), cdata(""),
87 	input_type(i_input_type), flags(i_flags) {
88 		if (!i_gabbi_name.empty())
89 		gabbi_name = i_gabbi_name;
90 	if (i_label)
91 		label = i_label;
92 	idx = idata = 0;
93 }
94 
95 typedef vector<TQSL_LOCATION_FIELD> TQSL_LOCATION_FIELDLIST;
96 
97 class TQSL_LOCATION_PAGE {
98  public:
TQSL_LOCATION_PAGE()99 	TQSL_LOCATION_PAGE() : complete(false), prev(0), next(0) {}
100 	bool complete;
101 	int prev, next;
102 	string dependentOn, dependency;
103 	map<string, vector<string> > hash;
104 	TQSL_LOCATION_FIELDLIST fieldlist;
105 };
106 
107 typedef vector<TQSL_LOCATION_PAGE> TQSL_LOCATION_PAGELIST;
108 
109 class TQSL_NAME {
110  public:
TQSL_NAME(string n="",string c="")111 	explicit TQSL_NAME(string n = "", string c = "") : name(n), call(c) {}
112 	string name;
113 	string call;
114 };
115 
116 class TQSL_LOCATION {
117  public:
TQSL_LOCATION()118 	TQSL_LOCATION() : sentinel(0x5445), page(0), cansave(false), sign_clean(false), cert_flags(TQSL_SELECT_CERT_WITHKEYS | TQSL_SELECT_CERT_EXPIRED), newflags(false) {}
119 
~TQSL_LOCATION()120 	~TQSL_LOCATION() { sentinel = 0; }
121 	int sentinel;
122 	int page;
123 	bool cansave;
124 	string name;
125 	TQSL_LOCATION_PAGELIST pagelist;
126 	vector<TQSL_NAME> names;
127 	string signdata;
128 	string loc_details;
129 	string qso_details;
130 	bool sign_clean;
131 	string tSTATION;
132 	string tCONTACT;
133 	string sigspec;
134 	char data_errors[512];
135 	int cert_flags;
136 	bool newflags;
137 };
138 
139 class Band {
140  public:
141 	string name, spectrum;
142 	int low, high;
143 };
144 
145 class Mode {
146  public:
147 	string mode, group;
148 };
149 
150 class PropMode {
151  public:
152 	string descrip, name;
153 };
154 
155 class Satellite {
156  public:
Satellite()157 	Satellite() {
158 		start.year = start.month = start.day = 0;
159 		end.year = end.month = end.day = 0;
160 	}
161 	string descrip, name;
162 	tQSL_Date start, end;
163 };
164 
165 bool
operator <(const Band & o1,const Band & o2)166 operator< (const Band& o1, const Band& o2) {
167 	static const char *suffixes[] = { "M", "CM", "MM"};
168 	static const char *prefix_chars = "0123456789.";
169 	// get suffixes
170 	string b1_suf = o1.name.substr(o1.name.find_first_not_of(prefix_chars));
171 	string b2_suf = o2.name.substr(o2.name.find_first_not_of(prefix_chars));
172 	if (b1_suf != b2_suf) {
173 		// Suffixes differ -- compare suffixes
174 		int b1_idx = (sizeof suffixes / sizeof suffixes[0]);
175 		int b2_idx = b1_idx;
176 		for (int i = 0; i < static_cast<int>(sizeof suffixes / sizeof suffixes[0]); i++) {
177 			if (b1_suf == suffixes[i])
178 				b1_idx = i;
179 			if (b2_suf == suffixes[i])
180 				b2_idx = i;
181 		}
182 		return b1_idx < b2_idx;
183 	}
184 	return atof(o1.name.c_str()) > atof(o2.name.c_str());
185 }
186 
187 bool
operator <(const PropMode & o1,const PropMode & o2)188 operator< (const PropMode& o1, const PropMode& o2) {
189 	if (o1.descrip < o2.descrip)
190 		return true;
191 	if (o1.descrip == o2.descrip)
192 		return (o1.name < o2.name);
193 	return false;
194 }
195 
196 bool
operator <(const Satellite & o1,const Satellite & o2)197 operator< (const Satellite& o1, const Satellite& o2) {
198 	if (o1.descrip < o2.descrip)
199 		return true;
200 	if (o1.descrip == o2.descrip)
201 		return (o1.name < o2.name);
202 	return false;
203 }
204 
205 bool
operator <(const Mode & o1,const Mode & o2)206 operator< (const Mode& o1, const Mode& o2) {
207 	static const char *groups[] = { "CW", "PHONE", "IMAGE", "DATA" };
208 	// m1 < m2 if m1 is a modegroup and m2 is not
209 	if (o1.mode == o1.group) {
210 		if (o2.mode != o2.group)
211 			return true;
212 	} else if (o2.mode == o2.group) {
213 		return false;
214 	}
215 	// If groups are same, compare modes
216 	if (o1.group == o2.group)
217 		return o1.mode < o2.mode;
218 	int m1_g = (sizeof groups / sizeof groups[0]);
219 	int m2_g = m1_g;
220 	for (int i = 0; i < static_cast<int>(sizeof groups / sizeof groups[0]); i++) {
221 		if (o1.group == groups[i])
222 			m1_g = i;
223 		if (o2.group == groups[i])
224 			m2_g = i;
225 	}
226 	return m1_g < m2_g;
227 }
228 
229 }	// namespace tqsllib
230 
231 using tqsllib::XMLElement;
232 using tqsllib::XMLElementList;
233 using tqsllib::Band;
234 using tqsllib::Mode;
235 using tqsllib::PropMode;
236 using tqsllib::Satellite;
237 using tqsllib::TQSL_LOCATION;
238 using tqsllib::TQSL_LOCATION_PAGE;
239 using tqsllib::TQSL_LOCATION_PAGELIST;
240 using tqsllib::TQSL_LOCATION_FIELD;
241 using tqsllib::TQSL_LOCATION_FIELDLIST;
242 using tqsllib::TQSL_LOCATION_ITEM;
243 using tqsllib::TQSL_NAME;
244 using tqsllib::ROOTCERT;
245 using tqsllib::CACERT;
246 using tqsllib::USERCERT;
247 using tqsllib::tqsl_get_pem_serial;
248 
249 #define CAST_TQSL_LOCATION(x) (reinterpret_cast<TQSL_LOCATION *>((x)))
250 
251 typedef map<int, string> IntMap;
252 typedef map<int, tQSL_Date> DateMap;
253 
254 static int num_entities = 0;
255 static bool _ent_init = false;
256 
257 static struct _dxcc_entity {
258 	int number;
259 	const char* name;
260 	const char *zonemap;
261 	tQSL_Date start, end;
262 } *entity_list = 0;
263 
264 // config data
265 
266 static XMLElement tqsl_xml_config;
267 static int tqsl_xml_config_major = -1;
268 static int tqsl_xml_config_minor = 0;
269 static IntMap DXCCMap;
270 static IntMap DXCCZoneMap;
271 static DateMap DXCCStartMap;
272 static DateMap DXCCEndMap;
273 static vector< pair<int, string> > DXCCList;
274 static vector<Band> BandList;
275 static vector<Mode> ModeList;
276 static vector<PropMode> PropModeList;
277 static vector<Satellite> SatelliteList;
278 static map<int, XMLElement> tqsl_page_map;
279 static map<string, XMLElement> tqsl_field_map;
280 static map<string, string> tqsl_adif_map;
281 static map<string, pair<int, int> > tqsl_cabrillo_map;
282 static map<string, pair<int, int> > tqsl_cabrillo_user_map;
283 
284 
285 static char
char_toupper(char c)286 char_toupper(char c) {
287 	return toupper(c);
288 }
289 
290 static string
string_toupper(const string & in)291 string_toupper(const string& in) {
292 	string out = in;
293 	transform(out.begin(), out.end(), out.begin(), char_toupper);
294 	return out;
295 }
296 // isspace() called on extended chars in UTF-8 raises asserts in
297 // the windows C++ libs. Don't call isspace() if out of range.
298 //
isspc(int c)299 static inline int isspc(int c) {
300 	if (c < 0 || c > 255)
301 		return 0;
302 	return isspace(c);
303 }
304 
305 // trim from start
ltrim(std::string & s)306 static inline std::string &ltrim(std::string &s) {
307 	s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun<int, int>(isspc))));
308 	return s;
309 }
310 
311 // trim from end
rtrim(std::string & s)312 static inline std::string &rtrim(std::string &s) {
313 	s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun<int, int>(isspc))).base(), s.end());
314 	return s;
315 }
316 
317 // trim from both ends
trim(std::string & s)318 static inline std::string &trim(std::string &s) {
319 	return ltrim(rtrim(s));
320 }
321 
322 #define TQSL_NPAGES 4
323 
324 static TQSL_LOCATION *
check_loc(tQSL_Location loc,bool unclean=true)325 check_loc(tQSL_Location loc, bool unclean = true) {
326 	if (tqsl_init())
327 		return 0;
328 	if (loc == 0)
329 		return 0;
330 	if (unclean)
331 		CAST_TQSL_LOCATION(loc)->sign_clean = false;
332 	return CAST_TQSL_LOCATION(loc);
333 }
334 
335 static int
tqsl_load_xml_config()336 tqsl_load_xml_config() {
337 	if (tqsl_xml_config.getElementList().size() > 0)	// Already init'd
338 		return 0;
339 	XMLElement default_config;
340 	XMLElement user_config;
341 	string default_path;
342 	tqslTrace("tqsl_load_xml_config", NULL);
343 
344 #ifdef _WIN32
345 	HKEY hkey;
346 	DWORD dtype;
347 	char wpath[TQSL_MAX_PATH_LEN];
348 	DWORD bsize = sizeof wpath;
349 	int wval;
350 	if ((wval = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
351 		"Software\\TrustedQSL", 0, KEY_READ, &hkey)) == ERROR_SUCCESS) {
352 		wval = RegQueryValueEx(hkey, "InstallPath", 0, &dtype, (LPBYTE)wpath, &bsize);
353 		RegCloseKey(hkey);
354 		if (wval == ERROR_SUCCESS)
355 			default_path = string(wpath) + "\\config.xml";
356 		tqslTrace("tqsl_load_xml_config", "default_path=%s", default_path.c_str());
357 	}
358 #elif defined(__APPLE__)
359 	// Get path to config.xml resource from bundle
360 	CFBundleRef tqslBundle = CFBundleGetMainBundle();
361 	CFURLRef configXMLURL = CFBundleCopyResourceURL(tqslBundle, CFSTR("config"), CFSTR("xml"), NULL);
362 	if (configXMLURL) {
363 		CFStringRef pathString = CFURLCopyFileSystemPath(configXMLURL, kCFURLPOSIXPathStyle);
364 		CFRelease(configXMLURL);
365 
366 		// Convert CFString path to config.xml to string object
367 		CFIndex maxStringLengthInBytes = CFStringGetMaximumSizeForEncoding(CFStringGetLength(pathString), kCFStringEncodingUTF8);
368 		char *pathCString = static_cast<char *>(malloc(maxStringLengthInBytes));
369 		if (pathCString) {
370 			CFStringGetCString(pathString, pathCString, maxStringLengthInBytes, kCFStringEncodingASCII);
371 			CFRelease(pathString);
372 			default_path = string(pathCString);
373 			free(pathCString);
374 			tqslTrace("tqsl_load_xml_config", "default_path=%s", default_path.c_str());
375 		}
376 	}
377 #else
378 	default_path = CONFDIR "config.xml"; //KC2YWE: Removed temporarily. There's got to be a better way to do this
379 	tqslTrace("tqsl_load_xml_config", "default_path=%s", default_path.c_str());
380 #endif
381 
382 #ifdef _WIN32
383 	string user_path = string(tQSL_BaseDir) + "\\config.xml";
384 #else
385 	string user_path = string(tQSL_BaseDir) + "/config.xml";
386 #endif
387 
388 	tqslTrace("tqsl_load_xml_config", "user_path=%s", user_path.c_str());
389 	int default_status = default_config.parseFile(default_path.c_str());
390 	int user_status = user_config.parseFile(user_path.c_str());
391 	tqslTrace("tqsl_load_xml_config", "default_status=%d, user_status=%d", default_status, user_status);
392 	if (default_status != XML_PARSE_NO_ERROR && user_status != XML_PARSE_NO_ERROR) {
393 		if (user_status == XML_PARSE_SYSTEM_ERROR)
394 			tQSL_Error = TQSL_CONFIG_ERROR;
395 		else
396 			tQSL_Error = TQSL_CONFIG_SYNTAX_ERROR;
397 		return 1;
398 	}
399 
400 	int default_major = -1;
401 	int default_minor = 0;
402 	int user_major = -1;
403 	int user_minor = 0;
404 
405 	XMLElement top;
406 	if (default_config.getFirstElement("tqslconfig", top)) {
407 		default_major = strtol(top.getAttribute("majorversion").first.c_str(), NULL, 10);
408 		default_minor = strtol(top.getAttribute("minorversion").first.c_str(), NULL, 10);
409 	}
410 	if (user_config.getFirstElement("tqslconfig", top)) {
411 		user_major = strtol(top.getAttribute("majorversion").first.c_str(), NULL, 10);
412 		user_minor = strtol(top.getAttribute("minorversion").first.c_str(), NULL, 10);
413 	}
414 
415 	if (default_major > user_major
416 		|| (default_major == user_major && default_minor > user_minor)) {
417 			tqsl_xml_config = default_config;
418 			tqsl_xml_config_major = default_major;
419 			tqsl_xml_config_minor = default_minor;
420 			return 0;
421 	}
422 	if (user_major < 0) {
423 		tQSL_Error = TQSL_CONFIG_SYNTAX_ERROR;
424 		tqslTrace("tqsl_load_xml_config", "Syntax error");
425 		return 1;
426 	}
427 	tqsl_xml_config	= user_config;
428 	tqsl_xml_config_major = user_major;
429 	tqsl_xml_config_minor = user_minor;
430 	return 0;
431 }
432 
433 static int
tqsl_get_xml_config_section(const string & section,XMLElement & el)434 tqsl_get_xml_config_section(const string& section, XMLElement& el) {
435 	if (tqsl_load_xml_config())
436 		return 1;
437 	XMLElement top;
438 	if (!tqsl_xml_config.getFirstElement("tqslconfig", top)) {
439 		tqsl_xml_config.clear();
440 		tQSL_Error = TQSL_CONFIG_SYNTAX_ERROR;
441 		return 1;
442 	}
443 	if (!top.getFirstElement(section, el)) {
444 		tQSL_Error = TQSL_CONFIG_SYNTAX_ERROR;
445 		return 1;
446 	}
447 	return 0;
448 }
449 
450 static int
tqsl_load_provider_list(vector<TQSL_PROVIDER> & plist)451 tqsl_load_provider_list(vector<TQSL_PROVIDER> &plist) {
452 	plist.clear();
453 	XMLElement providers;
454 	if (tqsl_get_xml_config_section("providers", providers))
455 		return 1;
456 	tqslTrace("tqsl_load_provider_list", NULL);
457 	XMLElement provider;
458 	bool gotit = providers.getFirstElement("provider", provider);
459 	while (gotit) {
460 		TQSL_PROVIDER pdata;
461 		memset(&pdata, 0, sizeof pdata);
462 		pair<string, bool> rval = provider.getAttribute("organizationName");
463 		if (!rval.second) {
464 			tQSL_Error = TQSL_PROVIDER_NOT_FOUND;
465 			tqslTrace("tqsl_load_provider_list", "Providers not found");
466 			return 1;
467 		}
468 		strncpy(pdata.organizationName, rval.first.c_str(), sizeof pdata.organizationName);
469 		XMLElement item;
470 		if (provider.getFirstElement("organizationalUnitName", item))
471 			strncpy(pdata.organizationalUnitName, item.getText().c_str(),
472 				sizeof pdata.organizationalUnitName);
473 		if (provider.getFirstElement("emailAddress", item))
474 			strncpy(pdata.emailAddress, item.getText().c_str(),
475 				sizeof pdata.emailAddress);
476 		if (provider.getFirstElement("url", item))
477 			strncpy(pdata.url, item.getText().c_str(),
478 				sizeof pdata.url);
479 		plist.push_back(pdata);
480 		gotit = providers.getNextElement(provider);
481 		if (gotit && provider.getElementName() != "provider")
482 			break;
483 	}
484 	return 0;
485 }
486 
487 static XMLElement tCONTACT_sign;
488 
489 static int
make_sign_data(TQSL_LOCATION * loc)490 make_sign_data(TQSL_LOCATION *loc) {
491 	map<string, string> field_data;
492 
493 	// Loop through the location pages, getting field data
494 	//
495 	int old_page = loc->page;
496 	tqsl_setStationLocationCapturePage(loc, 1);
497 	do {
498 		TQSL_LOCATION_PAGE& p = loc->pagelist[loc->page-1];
499 		for (int i = 0; i < static_cast<int>(p.fieldlist.size()); i++) {
500 			TQSL_LOCATION_FIELD& f = p.fieldlist[i];
501 			string s;
502 			if (f.input_type == TQSL_LOCATION_FIELD_DDLIST || f.input_type == TQSL_LOCATION_FIELD_LIST) {
503 				if (f.idx < 0 || f.idx >= static_cast<int>(f.items.size()))
504 					s = "";
505 				else
506 					s = f.items[f.idx].text;
507 			} else if (f.data_type == TQSL_LOCATION_FIELD_INT) {
508 				char buf[20];
509 				snprintf(buf, sizeof buf, "%d", f.idata);
510 				s = buf;
511 			} else {
512 				s = f.cdata;
513 			}
514 			field_data[f.gabbi_name] = s;
515 		}
516 		int rval;
517 		if (tqsl_hasNextStationLocationCapture(loc, &rval) || !rval)
518 			break;
519 		tqsl_nextStationLocationCapture(loc);
520 	} while (1);
521 	tqsl_setStationLocationCapturePage(loc, old_page);
522 
523 	loc->signdata = "";
524 	loc->loc_details = "";
525 	loc->sign_clean = false;
526 	XMLElement sigspecs;
527 	if (tqsl_get_xml_config_section("sigspecs", sigspecs)) {
528 		tQSL_Error = TQSL_CUSTOM_ERROR;
529 		strncpy(tQSL_CustomError, "TQSL Configuration file invalid - it does not have a sigspecs section",
530 			sizeof tQSL_CustomError);
531 		tqslTrace("make_sign_data", "Error %s", tQSL_CustomError);
532 		return 1;
533 	}
534 	XMLElement sigspec;
535 	XMLElement ss;
536 	if (!sigspecs.getFirstElement("sigspec", sigspec)) {
537 		tQSL_Error = TQSL_CUSTOM_ERROR;
538 		strncpy(tQSL_CustomError, "TQSL Configuration file invalid - it does not have a sigspec section",
539 			sizeof tQSL_CustomError);
540 		tqslTrace("make_sign_data", "Error %s", tQSL_CustomError);
541 		return 1;
542 	}
543 	ss = sigspec;
544 	bool ok;
545 	do {
546 		if (sigspec.getAttribute("status").first == "deprecated") {
547 			ok = sigspecs.getNextElement(sigspec);
548 			continue;
549 		}
550 		double ssver = atof(ss.getAttribute("version").first.c_str());
551 		double newver = atof(sigspec.getAttribute("version").first.c_str());
552 		if (newver > ssver)
553 			ss = sigspec;
554 		ok = sigspecs.getNextElement(sigspec);
555 	} while (ok);
556 	sigspec = ss;
557 
558 	loc->sigspec = "SIGN_";
559 	loc->sigspec += sigspec.getAttribute("name").first;
560 	loc->sigspec += "_V";
561 	loc->sigspec += sigspec.getAttribute("version").first;
562 
563 	tCONTACT_sign.clear();
564 	if (!sigspec.getFirstElement("tCONTACT", tCONTACT_sign)) {
565 		tQSL_Error = TQSL_CUSTOM_ERROR;
566 		strncpy(tQSL_CustomError, "TQSL Configuration file invalid - missing sigspec.tCONTACT",
567 			sizeof tQSL_CustomError);
568 		tqslTrace("make_sign_data", "Error %s", tQSL_CustomError);
569 		return 1;
570 	}
571 	if (tCONTACT_sign.getElementList().size() == 0) {
572 		tQSL_Error = TQSL_CUSTOM_ERROR;
573 		strncpy(tQSL_CustomError, "TQSL Configuration file invalid - empty sigspec.tCONTACT",
574 			sizeof tQSL_CustomError);
575 		tqslTrace("make_sign_data", "Error %s", tQSL_CustomError);
576 		return 1;
577 	}
578 	XMLElement tSTATION;
579 	if (!sigspec.getFirstElement("tSTATION", tSTATION)) {
580 		tQSL_Error = TQSL_CUSTOM_ERROR;
581 		strncpy(tQSL_CustomError, "TQSL Configuration file invalid - missing sigspec.tSTATION",
582 			sizeof tQSL_CustomError);
583 		tqslTrace("make_sign_data", "Error %s", tQSL_CustomError);
584 		return 1;
585 	}
586 	XMLElement specfield;
587 
588 	if (!(ok = tSTATION.getFirstElement(specfield))) {
589 		tQSL_Error = TQSL_CUSTOM_ERROR;
590 		strncpy(tQSL_CustomError, "TQSL Configuration file invalid - missing tSTATION.specfield",
591 			sizeof tQSL_CustomError);
592 		tqslTrace("make_sign_data", "Error %s", tQSL_CustomError);
593 		return 1;
594 	}
595 	do {
596 		string value = field_data[specfield.getElementName()];
597 		value = trim(value);
598 		if (value == "") {
599 			pair<string, bool> attr = specfield.getAttribute("required");
600 			if (attr.second && strtol(attr.first.c_str(), NULL, 10)) {
601 				string err = specfield.getElementName() + " field required by ";
602 				attr = sigspec.getAttribute("name");
603 				if (attr.second)
604 					err += attr.first + " ";
605 				attr = sigspec.getAttribute("version");
606 				if (attr.second)
607 					err += "V" + attr.first + " ";
608 				err += "signature specification not found";
609 				tQSL_Error = TQSL_CUSTOM_ERROR;
610 				strncpy(tQSL_CustomError, err.c_str(), sizeof tQSL_CustomError);
611 				tqslTrace("make_sign_data", "Error %s", tQSL_CustomError);
612 				return 1;
613 			}
614 		} else {
615 			loc->signdata += value;
616 			if (loc->loc_details != "") {
617 				loc->loc_details += ", ";
618 			}
619 			loc->loc_details += specfield.getElementName() + ": " + value;
620 		}
621 		ok = tSTATION.getNextElement(specfield);
622 	} while (ok);
623 	loc->sign_clean = true;
624 	return 0;
625 }
626 
627 static int
init_dxcc()628 init_dxcc() {
629 	if (DXCCMap.size() > 0)
630 		return 0;
631 	tqslTrace("init_dxcc", NULL);
632 	XMLElement dxcc;
633 	if (tqsl_get_xml_config_section("dxcc", dxcc)) {
634 		tqslTrace("init_dxcc", "Error %d getting dxcc config section", tQSL_Error);
635 		return 1;
636 	}
637 	XMLElement dxcc_entity;
638 	bool ok = dxcc.getFirstElement("entity", dxcc_entity);
639 	while (ok) {
640 		pair<string, bool> rval = dxcc_entity.getAttribute("arrlId");
641 		pair<string, bool> zval = dxcc_entity.getAttribute("zonemap");
642 		pair<string, bool> strdate = dxcc_entity.getAttribute("valid");
643 		pair<string, bool> enddate = dxcc_entity.getAttribute("invalid");
644 		if (rval.second) {
645 			int num = strtol(rval.first.c_str(), NULL, 10);
646 			DXCCMap[num] = dxcc_entity.getText();
647 			if (zval.second)
648 				DXCCZoneMap[num] = zval.first;
649 			tQSL_Date d;
650 			d.year = 1945;
651 			d.month = 11;
652 			d.day = 15;
653 			DXCCStartMap[num] = d;
654 			if (strdate.second) {
655 				if (!tqsl_initDate(&d, strdate.first.c_str()))
656 					DXCCStartMap[num] = d;
657 			}
658 			d.year = 0;
659 			d.month = 0;
660 			d.day = 0;
661 			DXCCEndMap[num] = d;
662 			if (enddate.second) {
663 				if (!tqsl_initDate(&d, enddate.first.c_str()))
664 					DXCCEndMap[num] = d;
665 			}
666 			DXCCList.push_back(make_pair(num, dxcc_entity.getText()));
667 		}
668 		ok = dxcc.getNextElement(dxcc_entity);
669 	}
670 	return 0;
671 }
672 
673 static int
init_band()674 init_band() {
675 	if (BandList.size() > 0)
676 		return 0;
677 	tqslTrace("init_band", NULL);
678 	XMLElement bands;
679 	if (tqsl_get_xml_config_section("bands", bands)) {
680 		tqslTrace("init_band", "Error %d getting bands", tQSL_Error);
681 		return 1;
682 	}
683 	XMLElement config_band;
684 	bool ok = bands.getFirstElement("band", config_band);
685 	while (ok) {
686 		Band b;
687 		b.name = config_band.getText();
688 		b.spectrum = config_band.getAttribute("spectrum").first;
689 		b.low = strtol(config_band.getAttribute("low").first.c_str(), NULL, 10);
690 		b.high = strtol(config_band.getAttribute("high").first.c_str(), NULL, 10);
691 		BandList.push_back(b);
692 		ok = bands.getNextElement(config_band);
693 	}
694 	sort(BandList.begin(), BandList.end());
695 	return 0;
696 }
697 
698 DLLEXPORT int CALLCONVENTION
tqsl_getConfigVersion(int * major,int * minor)699 tqsl_getConfigVersion(int *major, int *minor) {
700 	if (tqsl_init())
701 		return 1;
702 	if (tqsl_load_xml_config()) {
703 		tqslTrace("tqsl_getConfigVersion", "Error %d from tqsl_load_xml_config", tQSL_Error);
704 		return 1;
705 	}
706 	tqslTrace("tqsl_getConfigVersion", "major=%d, minor=%d", tqsl_xml_config_major, tqsl_xml_config_minor);
707 	if (major)
708 		*major = tqsl_xml_config_major;
709 	if (minor)
710 		*minor = tqsl_xml_config_minor;
711 	return 0;
712 }
713 
714 DLLEXPORT int CALLCONVENTION
tqsl_getNumBand(int * number)715 tqsl_getNumBand(int *number) {
716 	if (number == 0) {
717 		tQSL_Error = TQSL_ARGUMENT_ERROR;
718 		return 1;
719 	}
720 	tqslTrace("tqsl_getNumBand", NULL);
721 	if (init_band()) {
722 		tqslTrace("tqsl_getNumBand", "init_band error=%d", tQSL_Error);
723 		return 1;
724 	}
725 	*number = BandList.size();
726 	return 0;
727 }
728 
729 DLLEXPORT int CALLCONVENTION
tqsl_getBand(int index,const char ** name,const char ** spectrum,int * low,int * high)730 tqsl_getBand(int index, const char **name, const char **spectrum, int *low, int *high) {
731 	if (index < 0 || name == 0) {
732 		tQSL_Error = TQSL_ARGUMENT_ERROR;
733 		return 1;
734 	}
735 	if (init_band()) {
736 		tqslTrace("tqsl_getBand", "init_band error=%d", tQSL_Error);
737 		return 1;
738 	}
739 	if (index >= static_cast<int>(BandList.size())) {
740 		tQSL_Error = TQSL_ARGUMENT_ERROR;
741 		tqslTrace("tqsl_getBand", "init_band arg error - index %d", index);
742 		return 1;
743 	}
744 	*name = BandList[index].name.c_str();
745 	if (spectrum)
746 		*spectrum = BandList[index].spectrum.c_str();
747 	if (low)
748 		*low = BandList[index].low;
749 	if (high)
750 		*high = BandList[index].high;
751 	return 0;
752 }
753 
754 static int
init_mode()755 init_mode() {
756 	if (ModeList.size() > 0)
757 		return 0;
758 	XMLElement modes;
759 	if (tqsl_get_xml_config_section("modes", modes)) {
760 		tqslTrace("init_mode", "Error from tqsl_get_xml_config_section %d", tQSL_Error);
761 		return 1;
762 	}
763 	XMLElement config_mode;
764 	bool ok = modes.getFirstElement("mode", config_mode);
765 	while (ok) {
766 		Mode m;
767 		m.mode = config_mode.getText();
768 		m.group = config_mode.getAttribute("group").first;
769 		ModeList.push_back(m);
770 		ok = modes.getNextElement(config_mode);
771 	}
772 	sort(ModeList.begin(), ModeList.end());
773 	return 0;
774 }
775 
776 static int
init_propmode()777 init_propmode() {
778 	if (PropModeList.size() > 0)
779 		return 0;
780 	XMLElement propmodes;
781 	if (tqsl_get_xml_config_section("propmodes", propmodes)) {
782 		tqslTrace("init_propmode", "Error getting config section %d", tQSL_Error);
783 		return 1;
784 	}
785 	XMLElement config_mode;
786 	bool ok = propmodes.getFirstElement("propmode", config_mode);
787 	while (ok) {
788 		PropMode p;
789 		p.descrip = config_mode.getText();
790 		p.name = config_mode.getAttribute("name").first;
791 		PropModeList.push_back(p);
792 		ok = propmodes.getNextElement(config_mode);
793 	}
794 	sort(PropModeList.begin(), PropModeList.end());
795 	return 0;
796 }
797 
798 static int
init_satellite()799 init_satellite() {
800 	if (SatelliteList.size() > 0)
801 		return 0;
802 	XMLElement satellites;
803 	if (tqsl_get_xml_config_section("satellites", satellites)) {
804 		tqslTrace("init_satellite", "Error getting config section %d", tQSL_Error);
805 		return 1;
806 	}
807 	XMLElement config_sat;
808 	bool ok = satellites.getFirstElement("satellite", config_sat);
809 	while (ok) {
810 		Satellite s;
811 		s.descrip = config_sat.getText();
812 		s.name = config_sat.getAttribute("name").first;
813 		tQSL_Date d;
814 		if (!tqsl_initDate(&d, config_sat.getAttribute("startDate").first.c_str()))
815 			s.start = d;
816 		if (!tqsl_initDate(&d, config_sat.getAttribute("endDate").first.c_str()))
817 			s.end = d;
818 		SatelliteList.push_back(s);
819 		ok = satellites.getNextElement(config_sat);
820 	}
821 	sort(SatelliteList.begin(), SatelliteList.end());
822 	return 0;
823 }
824 
825 DLLEXPORT int CALLCONVENTION
tqsl_getNumMode(int * number)826 tqsl_getNumMode(int *number) {
827 	if (tqsl_init())
828 		return 1;
829 	if (number == NULL) {
830 		tqslTrace("tqsl_getNumMode", "Argument error, number = 0x%lx", number);
831 		tQSL_Error = TQSL_ARGUMENT_ERROR;
832 		return 1;
833 	}
834 	if (init_mode()) {
835 		tqslTrace("tqsl_getNumMode", "init_mode error %d", tQSL_Error);
836 		return 1;
837 	}
838 	*number = ModeList.size();
839 	return 0;
840 }
841 
842 DLLEXPORT int CALLCONVENTION
tqsl_getMode(int index,const char ** mode,const char ** group)843 tqsl_getMode(int index, const char **mode, const char **group) {
844 	if (index < 0 || mode == NULL) {
845 		tqslTrace("tqsl_getMode", "Arg error index=%d, mode=0x%lx, group=0x%lx", index, mode, group);
846 		tQSL_Error = TQSL_ARGUMENT_ERROR;
847 		return 1;
848 	}
849 	if (init_mode()) {
850 		tqslTrace("tqsl_getMode", "init_mode error %d", tQSL_Error);
851 		return 1;
852 	}
853 	if (index >= static_cast<int>(ModeList.size())) {
854 		tqslTrace("tqsl_getMode", "Argument error: %d", index);
855 		tQSL_Error = TQSL_ARGUMENT_ERROR;
856 		return 1;
857 	}
858 	*mode = ModeList[index].mode.c_str();
859 	if (group)
860 		*group = ModeList[index].group.c_str();
861 	return 0;
862 }
863 
864 DLLEXPORT int CALLCONVENTION
tqsl_getNumDXCCEntity(int * number)865 tqsl_getNumDXCCEntity(int *number) {
866 	if (number == NULL) {
867 		tqslTrace("tqsl_getNumDXCCEntity", "Arg error - number=null");
868 		tQSL_Error = TQSL_ARGUMENT_ERROR;
869 		return 1;
870 	}
871 	if (init_dxcc()) {
872 		tqslTrace("tqsl_getNumDXCCEntity", "init_dxcc error %d", tQSL_Error);
873 		return 1;
874 	}
875 	*number = DXCCList.size();
876 	return 0;
877 }
878 
879 DLLEXPORT int CALLCONVENTION
tqsl_getDXCCEntity(int index,int * number,const char ** name)880 tqsl_getDXCCEntity(int index, int *number, const char **name) {
881 	if (index < 0 || name == NULL || number == NULL) {
882 		tqslTrace("tqsl_getDXCCEntity", "arg error index=%d, number = 0x%lx, name=0x%lx", index, number, name);
883 		tQSL_Error = TQSL_ARGUMENT_ERROR;
884 		return 1;
885 	}
886 	if (init_dxcc()) {
887 		tqslTrace("tqsl_getDXCCEntity", "init_dxcc error %d", tQSL_Error);
888 		return 1;
889 	}
890 	if (index >= static_cast<int>(DXCCList.size())) {
891 		tQSL_Error = TQSL_ARGUMENT_ERROR;
892 		tqslTrace("tqsl_getDXCCEntity", "index range %d", index);
893 		return 1;
894 	}
895 	*number = DXCCList[index].first;
896 	*name = DXCCList[index].second.c_str();
897 	return 0;
898 }
899 
900 
901 DLLEXPORT int CALLCONVENTION
tqsl_getDXCCEntityName(int number,const char ** name)902 tqsl_getDXCCEntityName(int number, const char **name) {
903 	if (name == NULL) {
904 		tqslTrace("tqsl_getDXCCEntityName", "Name=null");
905 		tQSL_Error = TQSL_ARGUMENT_ERROR;
906 		return 1;
907 	}
908 	if (init_dxcc()) {
909 		tqslTrace("tqsl_getDXCCEntityName", "init_dxcc error %d", tQSL_Error);
910 		return 1;
911 	}
912 	IntMap::const_iterator it;
913 	it = DXCCMap.find(number);
914 	if (it == DXCCMap.end()) {
915 		tQSL_Error = TQSL_NAME_NOT_FOUND;
916 		return 1;
917 	}
918 	*name = it->second.c_str();
919 	return 0;
920 }
921 
922 DLLEXPORT int CALLCONVENTION
tqsl_getDXCCZoneMap(int number,const char ** zonemap)923 tqsl_getDXCCZoneMap(int number, const char **zonemap) {
924 	if (zonemap == NULL) {
925 		tqslTrace("tqsl_getDXCCZoneMap", "zonemap ptr null");
926 		tQSL_Error = TQSL_ARGUMENT_ERROR;
927 		return 1;
928 	}
929 	if (init_dxcc()) {
930 		tqslTrace("tqsl_getDXCCZoneMap", "init_dxcc error %d", tQSL_Error);
931 		return 1;
932 	}
933 	IntMap::const_iterator it;
934 	it = DXCCZoneMap.find(number);
935 	if (it == DXCCZoneMap.end()) {
936 		tQSL_Error = TQSL_NAME_NOT_FOUND;
937 		return 1;
938 	}
939 	const char *map = it->second.c_str();
940 	if (!map || map[0] == '\0')
941 		*zonemap = NULL;
942 	else
943 		*zonemap = map;
944 	return 0;
945 }
946 
947 DLLEXPORT int CALLCONVENTION
tqsl_getDXCCStartDate(int number,tQSL_Date * d)948 tqsl_getDXCCStartDate(int number, tQSL_Date *d) {
949 	if (d == NULL) {
950 		tqslTrace("tqsl_getDXCCStartDate", "date ptr null");
951 		tQSL_Error = TQSL_ARGUMENT_ERROR;
952 		return 1;
953 	}
954 	if (init_dxcc()) {
955 		tqslTrace("tqsl_getDXCCStartDate", "init_dxcc error %d", tQSL_Error);
956 		return 1;
957 	}
958 	DateMap::const_iterator it;
959 	it = DXCCStartMap.find(number);
960 	if (it == DXCCStartMap.end()) {
961 		tQSL_Error = TQSL_NAME_NOT_FOUND;
962 		return 1;
963 	}
964 	tQSL_Date newdate = it->second;
965 	*d = newdate;
966 	return 0;
967 }
968 
969 DLLEXPORT int CALLCONVENTION
tqsl_getDXCCEndDate(int number,tQSL_Date * d)970 tqsl_getDXCCEndDate(int number, tQSL_Date *d) {
971 	if (d == NULL) {
972 		tqslTrace("tqsl_getDXCCEndDate", "date ptr null");
973 		tQSL_Error = TQSL_ARGUMENT_ERROR;
974 		return 1;
975 	}
976 	if (init_dxcc()) {
977 		tqslTrace("tqsl_getDXCCEndDate", "init_dxcc error %d", tQSL_Error);
978 		return 1;
979 	}
980 	DateMap::const_iterator it;
981 	it = DXCCEndMap.find(number);
982 	if (it == DXCCEndMap.end()) {
983 		tQSL_Error = TQSL_NAME_NOT_FOUND;
984 		return 1;
985 	}
986 	tQSL_Date newdate = it->second;
987 	*d = newdate;
988 	return 0;
989 }
990 
991 DLLEXPORT int CALLCONVENTION
tqsl_getNumPropagationMode(int * number)992 tqsl_getNumPropagationMode(int *number) {
993 	if (tqsl_init())
994 		return 1;
995 	if (number == NULL) {
996 		tqslTrace("tqsl_getNumPropagationMode", "number=null");
997 		tQSL_Error = TQSL_ARGUMENT_ERROR;
998 		return 1;
999 	}
1000 	if (init_propmode()) {
1001 		tqslTrace("tqsl_getNumPropagationMode", "init_propmode error %d", tQSL_Error);
1002 		return 1;
1003 	}
1004 	*number = PropModeList.size();
1005 	return 0;
1006 }
1007 
1008 DLLEXPORT int CALLCONVENTION
tqsl_getPropagationMode(int index,const char ** name,const char ** descrip)1009 tqsl_getPropagationMode(int index, const char **name, const char **descrip) {
1010 	if (index < 0 || name == NULL) {
1011 		tqslTrace("tqsl_getPropagationMode", "arg error index=%d name=0x%lx descrip=0x%lx", index, name, descrip);
1012 		tQSL_Error = TQSL_ARGUMENT_ERROR;
1013 		return 1;
1014 	}
1015 	if (init_propmode()) {
1016 		tqslTrace("tqsl_getPropagationMode", "init_propmode error %d",  tQSL_Error);
1017 		return 1;
1018 	}
1019 	if (index >= static_cast<int>(PropModeList.size())) {
1020 		tqslTrace("tqsl_getPropagationMode", "index out of range: %d", index);
1021 		tQSL_Error = TQSL_ARGUMENT_ERROR;
1022 		return 1;
1023 	}
1024 	*name = PropModeList[index].name.c_str();
1025 	if (descrip)
1026 		*descrip = PropModeList[index].descrip.c_str();
1027 	return 0;
1028 }
1029 
1030 DLLEXPORT int CALLCONVENTION
tqsl_getNumSatellite(int * number)1031 tqsl_getNumSatellite(int *number) {
1032 	if (tqsl_init())
1033 		return 1;
1034 	if (number == NULL) {
1035 		tqslTrace("tqsl_getNumSatellite", "arg error number = null");
1036 		tQSL_Error = TQSL_ARGUMENT_ERROR;
1037 		return 1;
1038 	}
1039 	if (init_satellite()) {
1040 		tqslTrace("tqsl_getNumSatellite", "init_satellite error %d", tQSL_Error);
1041 		return 1;
1042 	}
1043 	*number = SatelliteList.size();
1044 	return 0;
1045 }
1046 
1047 DLLEXPORT int CALLCONVENTION
tqsl_getSatellite(int index,const char ** name,const char ** descrip,tQSL_Date * start,tQSL_Date * end)1048 tqsl_getSatellite(int index, const char **name, const char **descrip,
1049 	tQSL_Date *start, tQSL_Date *end) {
1050 	if (index < 0 || name == NULL) {
1051 		tqslTrace("tqsl_getSatellite", "arg error index=%d name=0x%lx", index, name);
1052 		tQSL_Error = TQSL_ARGUMENT_ERROR;
1053 		return 1;
1054 	}
1055 	if (init_satellite()) {
1056 		tqslTrace("tqsl_getSatellite", "init_satellite error %d", tQSL_Error);
1057 		return 1;
1058 	}
1059 	if (index >= static_cast<int>(SatelliteList.size())) {
1060 		tqslTrace("tqsl_getSatellite", "index error %d", index);
1061 		tQSL_Error = TQSL_ARGUMENT_ERROR;
1062 		return 1;
1063 	}
1064 	*name = SatelliteList[index].name.c_str();
1065 	if (descrip)
1066 		*descrip = SatelliteList[index].descrip.c_str();
1067 	if (start)
1068 		*start = SatelliteList[index].start;
1069 	if (end)
1070 		*end = SatelliteList[index].end;
1071 	return 0;
1072 }
1073 
1074 static int
init_cabrillo_map()1075 init_cabrillo_map() {
1076 	if (tqsl_cabrillo_map.size() > 0)
1077 		return 0;
1078 	XMLElement cabrillo_map;
1079 	if (tqsl_get_xml_config_section("cabrillomap", cabrillo_map)) {
1080 		tqslTrace("init_cabrillo_map", "get_xml_config_section error %d", tQSL_Error);
1081 		return 1;
1082 	}
1083 	XMLElement cabrillo_item;
1084 	bool ok = cabrillo_map.getFirstElement("cabrillocontest", cabrillo_item);
1085 	while (ok) {
1086 		if (cabrillo_item.getText() != "" && strtol(cabrillo_item.getAttribute("field").first.c_str(), NULL, 10) > TQSL_MIN_CABRILLO_MAP_FIELD)
1087 			tqsl_cabrillo_map[cabrillo_item.getText()] =
1088 				make_pair(strtol(cabrillo_item.getAttribute("field").first.c_str(), NULL, 10)-1,
1089 					(cabrillo_item.getAttribute("type").first == "VHF") ? TQSL_CABRILLO_VHF : TQSL_CABRILLO_HF);
1090 		ok = cabrillo_map.getNextElement(cabrillo_item);
1091 	}
1092 	return 0;
1093 }
1094 
1095 DLLEXPORT int CALLCONVENTION
tqsl_clearCabrilloMap()1096 tqsl_clearCabrilloMap() {
1097 	tqslTrace("tqsl_clearCabrilloMap", NULL);
1098 	tqsl_cabrillo_user_map.clear();
1099 	return 0;
1100 }
1101 
1102 DLLEXPORT int CALLCONVENTION
tqsl_setCabrilloMapEntry(const char * contest,int field,int contest_type)1103 tqsl_setCabrilloMapEntry(const char *contest, int field, int contest_type) {
1104 	if (contest == NULL || field <= TQSL_MIN_CABRILLO_MAP_FIELD ||
1105 		(contest_type != TQSL_CABRILLO_HF && contest_type != TQSL_CABRILLO_VHF)) {
1106 		tqslTrace("tqsl_setCabrilloMapEntry", "arg error contest=0x%lx field = %d", contest, field);
1107 		tQSL_Error = TQSL_ARGUMENT_ERROR;
1108 		return 1;
1109 	}
1110 	tqsl_cabrillo_user_map[string_toupper(contest)] = make_pair(field-1, contest_type);
1111 	return 0;
1112 }
1113 
1114 DLLEXPORT int CALLCONVENTION
tqsl_getCabrilloMapEntry(const char * contest,int * fieldnum,int * contest_type)1115 tqsl_getCabrilloMapEntry(const char *contest, int *fieldnum, int *contest_type) {
1116 	if (contest == NULL || fieldnum == NULL) {
1117 		tqslTrace("tqsl_getCabrilloMapEntry", "arg error contest=0x%lx fieldnum = 0x%lx", contest, fieldnum);
1118 		tQSL_Error = TQSL_ARGUMENT_ERROR;
1119 		return 1;
1120 	}
1121 	if (init_cabrillo_map()) {
1122 		tqslTrace("tqsl_getCabrilloMapEntry", "init_cabrillo_map errror %d", tQSL_Error);
1123 		return 1;
1124 	}
1125 	map<string, pair<int, int> >::iterator it;
1126 	if ((it = tqsl_cabrillo_user_map.find(string_toupper(contest))) == tqsl_cabrillo_user_map.end()) {
1127 		if ((it = tqsl_cabrillo_map.find(string_toupper(contest))) == tqsl_cabrillo_map.end()) {
1128 			*fieldnum = 0;
1129 			return 0;
1130 		}
1131 	}
1132 	*fieldnum = it->second.first + 1;
1133 	if (contest_type)
1134 		*contest_type = it->second.second;
1135 	return 0;
1136 }
1137 
1138 static int
init_adif_map()1139 init_adif_map() {
1140 	if (tqsl_adif_map.size() > 0)
1141 		return 0;
1142 	XMLElement adif_map;
1143 	if (tqsl_get_xml_config_section("adifmap", adif_map)) {
1144 		tqslTrace("init_adif_map", "tqsl_get_xml_config_section error %d", tQSL_Error);
1145 		return 1;
1146 	}
1147 	XMLElement adif_item;
1148 	bool ok = adif_map.getFirstElement("adifmode", adif_item);
1149 	while (ok) {
1150 		string adifmode = adif_item.getAttribute("adif-mode").first;
1151 		string submode = adif_item.getAttribute("adif-submode").first;
1152 		// Prefer the "mode=" attribute of the mode definition, else get the item value.
1153 		string gabbi = adif_item.getAttribute("mode").first;
1154 		string melem = adif_item.getText();
1155 
1156 		if (adifmode == "") { 		// Handle entries with just a mode element
1157 			adifmode = melem;
1158 		}
1159 		if (gabbi != "") {		// There should always be one
1160 			if (adifmode != "") {
1161 				tqsl_adif_map[adifmode] = gabbi;
1162 			}
1163 			// Map this gabbi mode from submode
1164 			if (submode != "" && submode != adifmode) {
1165 				tqsl_adif_map[submode] = gabbi;
1166 			}
1167 			if (melem != "" && melem != adifmode) {
1168 				tqsl_adif_map[melem] = gabbi;
1169 			}
1170 			// Add a mode%submode lookup too
1171 			if (adifmode != "" && submode != "") {
1172 				tqsl_adif_map[adifmode + "%" + submode] = gabbi;
1173 			}
1174 		}
1175 		ok = adif_map.getNextElement(adif_item);
1176 	}
1177 	return 0;
1178 }
1179 
1180 DLLEXPORT int CALLCONVENTION
tqsl_clearADIFModes()1181 tqsl_clearADIFModes() {
1182 	tqsl_adif_map.clear();
1183 	return 0;
1184 }
1185 
1186 DLLEXPORT int CALLCONVENTION
tqsl_setADIFMode(const char * adif_item,const char * mode)1187 tqsl_setADIFMode(const char *adif_item, const char *mode) {
1188 	if (adif_item == NULL || mode == NULL) {
1189 		tqslTrace("tqsl_setADIFMode", "arg error adif_item=0x%lx mode=0x%lx", adif_item, mode);
1190 		tQSL_Error = TQSL_ARGUMENT_ERROR;
1191 		return 1;
1192 	}
1193 	if (init_adif_map()) {
1194 		tQSL_Error = TQSL_CUSTOM_ERROR;
1195 		strncpy(tQSL_CustomError, "TQSL Configuration file invalid - ADIF map invalid",
1196 			sizeof tQSL_CustomError);
1197 		tqslTrace("tqslSetADIFMode", "Error %s", tQSL_CustomError);
1198 		return 1;
1199 	}
1200 	string umode = string_toupper(mode);
1201 	tqsl_adif_map[string_toupper(adif_item)] = umode;
1202 	return 0;
1203 }
1204 
1205 DLLEXPORT int CALLCONVENTION
tqsl_getADIFMode(const char * adif_item,char * mode,int nmode)1206 tqsl_getADIFMode(const char *adif_item, char *mode, int nmode) {
1207 	if (adif_item == NULL || mode == NULL) {
1208 		tqslTrace("tqsl_getADIFMode", "arg error adif_item=0x%lx, mode=0x%lx", adif_item, mode);
1209 		tQSL_Error = TQSL_ARGUMENT_ERROR;
1210 		return 1;
1211 	}
1212 	if (init_adif_map()) {
1213 		tQSL_Error = TQSL_CUSTOM_ERROR;
1214 		strncpy(tQSL_CustomError, "TQSL Configuration file invalid - ADIF map invalid",
1215 			sizeof tQSL_CustomError);
1216 		tqslTrace("tqsl_getADIFMode", "init_adif error %s", tQSL_CustomError);
1217 		return 1;
1218 	}
1219 	string orig = adif_item;
1220 	orig = string_toupper(orig);
1221 	string amode;
1222 	if (tqsl_adif_map.find(orig) != tqsl_adif_map.end()) {
1223 		amode = tqsl_adif_map[orig];
1224 	} else {
1225 		tQSL_Error = TQSL_NAME_NOT_FOUND;
1226 		return 1;
1227 	}
1228 
1229 	if (nmode < static_cast<int>(amode.length())+1) {
1230 		tqslTrace("tqsl_getAdifMode", "bufer error %s %s", nmode, amode.length());
1231 		tQSL_Error = TQSL_BUFFER_ERROR;
1232 		return 1;
1233 	}
1234 	strncpy(mode, amode.c_str(), nmode);
1235 	return 0;
1236 }
1237 
1238 static int
init_loc_maps()1239 init_loc_maps() {
1240 	if (tqsl_field_map.size() > 0)
1241 		return 0;
1242 	XMLElement config_pages;
1243 	if (tqsl_get_xml_config_section("locpages", config_pages)) {
1244 		tqslTrace("init_loc_maps", "get_xml_config_section error %d", tQSL_Error);
1245 		return 1;
1246 	}
1247 	XMLElement config_page;
1248 	tqsl_page_map.clear();
1249 	bool ok;
1250 	for (ok = config_pages.getFirstElement("page", config_page); ok; ok = config_pages.getNextElement(config_page)) {
1251 		pair <string, bool> Id = config_page.getAttribute("Id");
1252 		int page_num = strtol(Id.first.c_str(), NULL, 10);
1253 		if (!Id.second || page_num < 1) {	// Must have the Id!
1254 			tQSL_Error = TQSL_CUSTOM_ERROR;
1255 			strncpy(tQSL_CustomError, "TQSL Configuration file invalid - page missing ID",
1256 				sizeof tQSL_CustomError);
1257 			tqslTrace("init_loc_maps", "error %s", tQSL_CustomError);
1258 			return 1;
1259 		}
1260 		tqsl_page_map[page_num] = config_page;
1261 	}
1262 
1263 	XMLElement config_fields;
1264 	if (tqsl_get_xml_config_section("locfields", config_fields)) {
1265 		tqslTrace("init_loc_maps", "get_xml_config_section locfields error %d", tQSL_Error);
1266 		return 1;
1267 	}
1268 	XMLElement config_field;
1269 	for (ok = config_fields.getFirstElement("field", config_field); ok; ok = config_fields.getNextElement(config_field)) {
1270 		pair <string, bool> Id = config_field.getAttribute("Id");
1271 		if (!Id.second) {	// Must have the Id!
1272 			tQSL_Error = TQSL_CUSTOM_ERROR;
1273 			strncpy(tQSL_CustomError, "TQSL Configuration file invalid - field missing ID",
1274 				sizeof tQSL_CustomError);
1275 			tqslTrace("init_loc_maps", "config field error %s", tQSL_CustomError);
1276 			return 1;
1277 		}
1278 		tqsl_field_map[Id.first] = config_field;
1279 	}
1280 
1281 	return 0;
1282 }
1283 
inMap(int cqvalue,int ituvalue,bool cqz,bool ituz,const char * map)1284 static bool inMap(int cqvalue, int ituvalue, bool cqz, bool ituz, const char *map) {
1285 /*
1286  * Parse the zone map and return true if the value is a valid zone number
1287  * The maps are colon-separated number pairs, with a list of pairs comma separated.
1288  */
1289 	int cq, itu;
1290 	bool result = false;
1291 
1292 	// No map or empty string -> all match
1293 	if (!map || map[0] == '\0') {
1294 		return true;
1295 	}
1296 
1297 	char *mapcopy = strdup(map);
1298 	char *mapPart = strtok(mapcopy, ",");
1299 	while (mapPart) {
1300 		sscanf(mapPart, "%d:%d", &itu, &cq);
1301 		if (cqz && ituz) {
1302 			if ((cq == cqvalue || cqvalue == 0) && (itu == ituvalue || ituvalue == 0)) {
1303 				result = true;
1304 				break;
1305 			}
1306 		} else if (cqz && (cq == cqvalue || cqvalue == 0)) {
1307 			result = true;
1308 			break;
1309 		} else if (ituz && (itu == ituvalue || ituvalue == 0)) {
1310 			result = true;
1311 			break;
1312 		}
1313 		mapPart = strtok(NULL, ",");
1314 	}
1315 	free(mapcopy);
1316 	return result;
1317 }
1318 
1319 static int
_ent_cmp(const void * a,const void * b)1320 _ent_cmp(const void *a, const void *b) {
1321 	return strcasecmp(((struct _dxcc_entity *)a)->name, ((struct _dxcc_entity *)b)->name);
1322 }
1323 
1324 static TQSL_LOCATION_FIELD *
get_location_field(int page,const string & gabbi,TQSL_LOCATION * loc)1325 get_location_field(int page, const string& gabbi, TQSL_LOCATION *loc) {
1326 	if (page == 0)
1327 		page = loc->page;
1328 	for (; page > 0; page = loc->pagelist[page-1].prev) {
1329 		TQSL_LOCATION_FIELDLIST& fl = loc->pagelist[page-1].fieldlist;
1330 		for (int j = 0; j < static_cast<int>(fl.size()); j++) {
1331 			if (fl[j].gabbi_name == gabbi)
1332 				return &(fl[j]);
1333 		}
1334 	}
1335 	return 0;
1336 }
1337 
1338 
1339 static int
update_page(int page,TQSL_LOCATION * loc)1340 update_page(int page, TQSL_LOCATION *loc) {
1341 	TQSL_LOCATION_PAGE& p = loc->pagelist[page-1];
1342 	int dxcc;
1343 	int current_entity = -1;
1344 	int loaded_cqz = -1;
1345 	int loaded_ituz = -1;
1346 	tqslTrace("update_page", "page=%d, loc=0x%lx", page, loc);
1347 	for (int i = 0; i < static_cast<int>(p.fieldlist.size()); i++) {
1348 		TQSL_LOCATION_FIELD& field = p.fieldlist[i];
1349 		field.changed = false;
1350 		if (field.gabbi_name == "CALL") {
1351 			if (field.items.size() == 0 || loc->newflags) {
1352 				// Build list of call signs from available certs
1353 				field.changed = true;
1354 				field.items.clear();
1355 				loc->newflags = false;
1356 				field.flags = TQSL_LOCATION_FIELD_SELNXT;	// Must be selected
1357 				p.hash.clear();
1358 				tQSL_Cert *certlist;
1359 				int ncerts;
1360 				tqsl_selectCertificates(&certlist, &ncerts, 0, 0, 0, 0, loc->cert_flags);
1361 				for (int i = 0; i < ncerts; i++) {
1362 					char callsign[40];
1363 					tqsl_getCertificateCallSign(certlist[i], callsign, sizeof callsign);
1364 					tqsl_getCertificateDXCCEntity(certlist[i], &dxcc);
1365 					char ibuf[10];
1366 					snprintf(ibuf, sizeof ibuf, "%d", dxcc);
1367 					bool found = false;
1368 					// Only add a given DXCC entity to a call once.
1369 					map<string, vector<string> >::iterator call_p;
1370 					for (call_p = p.hash.begin(); call_p != p.hash.end(); call_p++) {
1371 						if (call_p->first == callsign && call_p->second[0] == ibuf) {
1372 							found = true;
1373 							break;
1374 						}
1375 					}
1376 					if (!found)
1377 						p.hash[callsign].push_back(ibuf);
1378 					tqsl_freeCertificate(certlist[i]);
1379 				}
1380 				free(certlist);
1381 				// Fill the call sign list
1382 				map<string, vector<string> >::iterator call_p;
1383 				field.idx = 0;
1384 				TQSL_LOCATION_ITEM none;
1385 				none.text = "[None]";
1386 				field.items.push_back(none);
1387 				for (call_p = p.hash.begin(); call_p != p.hash.end(); call_p++) {
1388 					TQSL_LOCATION_ITEM item;
1389 					item.text = call_p->first;
1390 					if (item.text == field.cdata)
1391 						field.idx = static_cast<int>(field.items.size());
1392 					field.items.push_back(item);
1393 				}
1394 				if (field.idx == 0 && field.items.size() == 2) {
1395 					field.idx = 1;
1396 				}
1397 				if (field.idx >= 0) {
1398 					field.cdata = field.items[field.idx].text;
1399 				}
1400 			}
1401 		} else if (field.gabbi_name == "DXCC") {
1402 			// Note: Expects CALL to be field 0 of this page.
1403 			string call = p.fieldlist[0].cdata;
1404 			if (field.items.size() == 0 || call != field.dependency) {
1405 				// rebuild list
1406 				field.changed = true;
1407 				init_dxcc();
1408 				int olddxcc = strtol(field.cdata.c_str(), NULL, 10);
1409 				field.items.clear();
1410 				field.idx = 0;
1411 #ifdef DXCC_TEST
1412 				const char *dxcc_test = getenv("TQSL_DXCC");
1413 				if (dxcc_test) {
1414 					vector<string> &entlist = p.hash[call];
1415 					char *parse_dxcc = strdup(dxcc_test);
1416 					char *cp = strtok(parse_dxcc, ",");
1417 					while (cp) {
1418 						if (find(entlist.begin(), entlist.end(), string(cp)) == entlist.end())
1419 							entlist.push_back(cp);
1420 						cp = strtok(0, ",");
1421 					}
1422 					free(parse_dxcc);
1423 				}
1424 #endif
1425 				if (call == "[None]") {
1426 					int i;
1427 					if (!_ent_init) {
1428 						num_entities = DXCCMap.size();
1429 						entity_list = new struct _dxcc_entity[num_entities];
1430 						IntMap::const_iterator it;
1431 						for (it = DXCCMap.begin(), i = 0; it != DXCCMap.end(); it++, i++) {
1432 							entity_list[i].number = it->first;
1433 							entity_list[i].name = it->second.c_str();
1434 							entity_list[i].zonemap = DXCCZoneMap[it->first].c_str();
1435 							entity_list[i].start = DXCCStartMap[it->first];
1436 							entity_list[i].end = DXCCEndMap[it->first];
1437 						}
1438 						qsort(entity_list, num_entities, sizeof(struct _dxcc_entity), &_ent_cmp);
1439 						_ent_init = true;
1440 					}
1441 					for (i = 0; i < num_entities; i++) {
1442 						TQSL_LOCATION_ITEM item;
1443 						item.ivalue = entity_list[i].number;
1444 						char buf[10];
1445 						snprintf(buf, sizeof buf, "%d", item.ivalue);
1446 						item.text = buf;
1447 						item.label = entity_list[i].name;
1448 						item.zonemap = entity_list[i].zonemap;
1449 						if (item.ivalue == olddxcc)
1450 							field.idx = field.items.size();
1451 						field.items.push_back(item);
1452 					}
1453 					field.idx = 0;
1454 				} else {
1455 					vector<string>::iterator ip;
1456 					// This iterator walks the list of DXCC entities associated
1457 					// with this callsign
1458 					for (ip = p.hash[call].begin(); ip != p.hash[call].end(); ip++) {
1459 						TQSL_LOCATION_ITEM item;
1460 						item.text = *ip;
1461 						item.ivalue = strtol(ip->c_str(), NULL, 10);
1462 						IntMap::iterator dxcc_it = DXCCMap.find(item.ivalue);
1463 						if (dxcc_it != DXCCMap.end()) {
1464 							item.label = dxcc_it->second;
1465 							item.zonemap = DXCCZoneMap[item.ivalue];
1466 						}
1467 						if (item.ivalue == olddxcc)
1468 							field.idx = field.items.size();
1469 						field.items.push_back(item);
1470 					}
1471 				}
1472 				if (field.items.size() > 0)
1473 					field.cdata = field.items[field.idx].text;
1474 				field.dependency = call;
1475 			} // rebuild list
1476 		} else {
1477 			if (tqsl_field_map.find(field.gabbi_name) == tqsl_field_map.end()) {
1478 				// Shouldn't happen!
1479 				tQSL_Error = TQSL_CUSTOM_ERROR;
1480 				strncpy(tQSL_CustomError, "TQSL Configuration file invalid - field map mismatch.",
1481 					sizeof tQSL_CustomError);
1482 				tqslTrace("update_page", "field map error %s", field.gabbi_name.c_str());
1483 				return 1;
1484 			}
1485 			XMLElement config_field = tqsl_field_map.find(field.gabbi_name)->second;
1486 			pair<string, bool> attr = config_field.getAttribute("dependsOn");
1487 			if (attr.first != "") {
1488 				// Items list depends on other field
1489 				TQSL_LOCATION_FIELD *fp = get_location_field(page, attr.first, loc);
1490 				if (fp) {
1491 					// Found the dependency field. Now find the enums to use
1492 					string val = fp->cdata;
1493 					if (fp->items.size() > 0)
1494 						val = fp->items[fp->idx].text;
1495 					if (val == field.dependency)
1496 						continue;
1497 					field.dependency = val;
1498 					field.changed = true;
1499 					field.items.clear();
1500 					XMLElement enumlist;
1501 					bool ok = config_field.getFirstElement("enums", enumlist);
1502 					while (ok) {
1503 						pair<string, bool> dependency = enumlist.getAttribute("dependency");
1504 						if (dependency.second && dependency.first == val) {
1505 							if (!(field.flags & TQSL_LOCATION_FIELD_MUSTSEL)) {
1506 								TQSL_LOCATION_ITEM item;
1507 								item.label = "[None]";
1508 								field.items.push_back(item);
1509 							}
1510 							XMLElement enumitem;
1511 							bool iok = enumlist.getFirstElement("enum", enumitem);
1512 							while (iok) {
1513 								TQSL_LOCATION_ITEM item;
1514 								item.text = enumitem.getAttribute("value").first;
1515 								item.label = enumitem.getText();
1516 								item.zonemap = enumitem.getAttribute("zonemap").first;
1517 								field.items.push_back(item);
1518 								iok = enumlist.getNextElement(enumitem);
1519 							}
1520 						}
1521 						ok = config_field.getNextElement(enumlist);
1522 					} // enum loop
1523 				} else {
1524 					tQSL_Error = TQSL_CUSTOM_ERROR;
1525 					strncpy(tQSL_CustomError, "TQSL Configuration file invalid - dependent field not found.",
1526 						sizeof tQSL_CustomError);
1527 					tqslTrace("update_page", "error %s", tQSL_CustomError);
1528 					return 1;
1529 				}
1530 			} else {
1531 				// No dependencies
1532 				TQSL_LOCATION_FIELD *ent = get_location_field(page, "DXCC", loc);
1533 				current_entity = strtol(ent->cdata.c_str(), NULL, 10);
1534 				bool cqz = field.gabbi_name == "CQZ";
1535 				bool ituz = field.gabbi_name == "ITUZ";
1536 				if (field.items.size() == 0 || (cqz && current_entity != loaded_cqz) || (ituz && current_entity != loaded_ituz)) {
1537 					XMLElement enumlist;
1538 					if (config_field.getFirstElement("enums", enumlist)) {
1539 						field.items.clear();
1540 						field.changed = true;
1541 						if (!(field.flags & TQSL_LOCATION_FIELD_MUSTSEL)) {
1542 							TQSL_LOCATION_ITEM item;
1543 							item.label = "[None]";
1544 							field.items.push_back(item);
1545 						}
1546 						XMLElement enumitem;
1547 						bool iok = enumlist.getFirstElement("enum", enumitem);
1548 						while (iok) {
1549 							TQSL_LOCATION_ITEM item;
1550 							item.text = enumitem.getAttribute("value").first;
1551 							item.label = enumitem.getText();
1552 							item.zonemap = enumitem.getAttribute("zonemap").first;
1553 							field.items.push_back(item);
1554 							iok = enumlist.getNextElement(enumitem);
1555 						}
1556 					} else {
1557 						// No enums supplied
1558 						int ftype = strtol(config_field.getAttribute("intype").first.c_str(), NULL, 10);
1559 						if (ftype == TQSL_LOCATION_FIELD_LIST || ftype == TQSL_LOCATION_FIELD_DDLIST) {
1560 							// This a list field
1561 							int lower = strtol(config_field.getAttribute("lower").first.c_str(), NULL, 10);
1562 							int upper = strtol(config_field.getAttribute("upper").first.c_str(), NULL, 10);
1563 							const char *zoneMap;
1564 							/* Get the map */
1565 							if (tqsl_getDXCCZoneMap(current_entity, &zoneMap)) {
1566 								zoneMap = NULL;
1567 							}
1568 							if (upper < lower) {
1569 								tQSL_Error = TQSL_CUSTOM_ERROR;
1570 								strncpy(tQSL_CustomError, "TQSL Configuration file invalid - field range order incorrect.",
1571 									sizeof tQSL_CustomError);
1572 								tqslTrace("update_page", "error %s", tQSL_CustomError);
1573 								return 1;
1574 							}
1575 							field.items.clear();
1576 							field.changed = true;
1577 							if (cqz)
1578 								loaded_cqz = current_entity;
1579 							if (ituz)
1580 								loaded_ituz = current_entity;
1581 							if (!(field.flags & TQSL_LOCATION_FIELD_MUSTSEL)) {
1582 								TQSL_LOCATION_ITEM item;
1583 								item.label = "[None]";
1584 								field.items.push_back(item);
1585 							}
1586 							char buf[40];
1587 							for (int j = lower; j <= upper; j++) {
1588 								if (!zoneMap || inMap(j, j, cqz, ituz, zoneMap)) {
1589 									snprintf(buf, sizeof buf, "%d", j);
1590 									TQSL_LOCATION_ITEM item;
1591 									item.text = buf;
1592 									item.ivalue = j;
1593 									field.items.push_back(item);
1594 								}
1595 							}
1596 						} // intype != TEXT
1597 					} // enums supplied
1598 				} // itemlist not empty and current entity
1599 			} // no dependencies
1600 		} // field name not CALL|DXCC
1601 	} // field loop
1602 
1603 	/* Sanity check zones */
1604 	bool zonesok = true;
1605 	// Try for subdivision info first
1606 	string zone_error = "";
1607 	TQSL_LOCATION_FIELD *state = get_location_field(page, "US_STATE", loc);
1608 	if (state) {
1609 		zone_error = "Invalid zone selections for state";
1610 	} else {
1611 		state = get_location_field(page, "CA_PROVINCE", loc);
1612 		if (state) {
1613 			zone_error = "Invalid zone selections for province";
1614 		} else {
1615 			state = get_location_field(page, "RU_OBLAST", loc);
1616 			if (state) {
1617 				zone_error = "Invalid zone selections for oblast";
1618 			} else {
1619 				// If no subdivision, use entity.
1620 				state = get_location_field(page, "DXCC", loc);
1621 				zone_error = "Invalid zone selections for DXCC entity";
1622 			}
1623 		}
1624 	}
1625 
1626 	if (state && state->idx >=0 && state->items.size() > 0) {
1627 		TQSL_LOCATION_FIELD *cqz = get_location_field(page, "CQZ", loc);
1628 		TQSL_LOCATION_FIELD *ituz = get_location_field(page, "ITUZ", loc);
1629 		string szm = state->items[state->idx].zonemap;
1630 		const char* stateZoneMap = szm.c_str();
1631 		int currentCQ = cqz->idata;
1632 		int currentITU = ituz->idata;
1633 
1634 		if (!inMap(currentCQ, currentITU, true, true, stateZoneMap)) {
1635 			zonesok = false;
1636 		}
1637 		TQSL_LOCATION_FIELD *zerr = get_location_field(page, "ZERR", loc);
1638 		if (zerr) {
1639 			if(!zonesok) {
1640 				zerr->cdata = zone_error;
1641 			} else {
1642 				zerr->cdata = "";
1643 			}
1644 		}
1645 	}
1646 	p.complete = true;
1647 	return 0;
1648 }
1649 
1650 static int
make_page(TQSL_LOCATION_PAGELIST & pagelist,int page_num)1651 make_page(TQSL_LOCATION_PAGELIST& pagelist, int page_num) {
1652 	if (init_loc_maps()) {
1653 		tqslTrace("make_page", "init_loc_maps error %d", tQSL_Error);
1654 		return 1;
1655 	}
1656 	if (tqsl_page_map.find(page_num) == tqsl_page_map.end()) {
1657 		tQSL_Error = TQSL_CUSTOM_ERROR;
1658 		strncpy(tQSL_CustomError, "TQSL Configuration file invalid - page reference could not be found.",
1659 			sizeof tQSL_CustomError);
1660 		tqslTrace("make_page", "Error %d %s", page_num, tQSL_CustomError);
1661 		return 1;
1662 	}
1663 
1664 	TQSL_LOCATION_PAGE p;
1665 	pagelist.push_back(p);
1666 
1667 	XMLElement& config_page = tqsl_page_map[page_num];
1668 
1669 	pagelist.back().prev = strtol(config_page.getAttribute("follows").first.c_str(), NULL, 10);
1670 	XMLElement config_pageField;
1671 	bool field_ok = config_page.getFirstElement("pageField", config_pageField);
1672 	while (field_ok) {
1673 		string field_name = config_pageField.getText();
1674 		if (field_name == "" || tqsl_field_map.find(field_name) == tqsl_field_map.end()) {
1675 			tQSL_Error = TQSL_CUSTOM_ERROR;
1676 			strncpy(tQSL_CustomError, "TQSL Configuration file invalid - page references undefined field.",
1677 				sizeof tQSL_CustomError);
1678 			tqslTrace("make_page", "Error %s", tQSL_CustomError);
1679 			return 1;
1680 		}
1681 		XMLElement& config_field = tqsl_field_map[field_name];
1682 		TQSL_LOCATION_FIELD loc_field(
1683 			field_name,
1684 			config_field.getAttribute("label").first.c_str(),
1685 			(config_field.getAttribute("type").first == "C") ? TQSL_LOCATION_FIELD_CHAR : TQSL_LOCATION_FIELD_INT,
1686 			strtol(config_field.getAttribute("len").first.c_str(), NULL, 10),
1687 			strtol(config_field.getAttribute("intype").first.c_str(), NULL, 10),
1688 			strtol(config_field.getAttribute("flags").first.c_str(), NULL, 10)
1689 		);	// NOLINT(whitespace/parens)
1690 		pagelist.back().fieldlist.push_back(loc_field);
1691 		field_ok = config_page.getNextElement(config_pageField);
1692 	}
1693 	return 0;
1694 }
1695 
1696 DLLEXPORT int CALLCONVENTION
tqsl_initStationLocationCapture(tQSL_Location * locp)1697 tqsl_initStationLocationCapture(tQSL_Location *locp) {
1698 	if (tqsl_init())
1699 		return 1;
1700 	if (locp == NULL) {
1701 		tqslTrace("tqsl_initStationLocationCapture", "Arg error locp=null");
1702 		tQSL_Error = TQSL_ARGUMENT_ERROR;
1703 		return 1;
1704 	}
1705 	TQSL_LOCATION *loc = new TQSL_LOCATION;
1706 	*locp = loc;
1707 	if (init_loc_maps()) {
1708 		tqslTrace("tqsl_initStationLocationCapture", "init_loc_maps error %d", tQSL_Error);
1709 		return 1;
1710 	}
1711 	map<int, XMLElement>::iterator pit;
1712 	for (pit = tqsl_page_map.begin(); pit != tqsl_page_map.end(); pit++) {
1713 		if (make_page(loc->pagelist, pit->first)) {
1714 			tqslTrace("tqsl_initStationLocationCapture", "make_page error %d", tQSL_Error);
1715 			return 1;
1716 		}
1717 	}
1718 
1719 	loc->page = 1;
1720 	if (update_page(1, loc)) {
1721 		tqslTrace("tqsl_initStationLocationCapture", "updatePage error %d", tQSL_Error);
1722 		return 1;
1723 	}
1724 	return 0;
1725 }
1726 
1727 DLLEXPORT int CALLCONVENTION
tqsl_endStationLocationCapture(tQSL_Location * locp)1728 tqsl_endStationLocationCapture(tQSL_Location *locp) {
1729 	if (tqsl_init())
1730 		return 1;
1731 	if (locp == NULL) {
1732 		tQSL_Error = TQSL_ARGUMENT_ERROR;
1733 		tqslTrace("tqsl_endStationLocationCapture", "arg error locp=NULL");
1734 		return 1;
1735 	}
1736 	if (*locp == 0)
1737 		return 0;
1738 	if (CAST_TQSL_LOCATION(*locp)->sentinel == 0x5445)
1739 		delete CAST_TQSL_LOCATION(*locp);
1740 	*locp = 0;
1741 	return 0;
1742 }
1743 
1744 DLLEXPORT int CALLCONVENTION
tqsl_updateStationLocationCapture(tQSL_Location locp)1745 tqsl_updateStationLocationCapture(tQSL_Location locp) {
1746 	TQSL_LOCATION *loc;
1747 	if (!(loc = check_loc(locp))) {
1748 		tqslTrace("tqsl_updateStationLocationCapture", "check_loc error %d", tQSL_Error);
1749 		return 1;
1750 	}
1751 //	TQSL_LOCATION_PAGE &p = loc->pagelist[loc->page-1];
1752 	return update_page(loc->page, loc);
1753 }
1754 
1755 DLLEXPORT int CALLCONVENTION
tqsl_getNumStationLocationCapturePages(tQSL_Location locp,int * npages)1756 tqsl_getNumStationLocationCapturePages(tQSL_Location locp, int *npages) {
1757 	TQSL_LOCATION *loc;
1758 	if (!(loc = check_loc(locp))) {
1759 		tqslTrace("tqsl_getNumStationLocationCapturePages", "check_loc error %d", tQSL_Error);
1760 		return 1;
1761 	}
1762 	if (npages == NULL) {
1763 		tQSL_Error = TQSL_ARGUMENT_ERROR;
1764 		tqslTrace("tqsl_getNumStationLocationCapturePages", "arg error npages=NULL");
1765 		return 1;
1766 	}
1767 	*npages = loc->pagelist.size();
1768 	return 0;
1769 }
1770 
1771 DLLEXPORT int CALLCONVENTION
tqsl_getStationLocationCapturePage(tQSL_Location locp,int * page)1772 tqsl_getStationLocationCapturePage(tQSL_Location locp, int *page) {
1773 	TQSL_LOCATION *loc;
1774 	if (!(loc = check_loc(locp))) {
1775 		tqslTrace("tqsl_getStationLocationCapturePage", "check_loc error %d", tQSL_Error);
1776 		return 1;
1777 	}
1778 	if (page == NULL) {
1779 		tqslTrace("tqsl_getStationLocationCapturePage", "arg error page=NULL");
1780 		tQSL_Error = TQSL_ARGUMENT_ERROR;
1781 		return 1;
1782 	}
1783 	*page = loc->page;
1784 	return 0;
1785 }
1786 
1787 DLLEXPORT int CALLCONVENTION
tqsl_setStationLocationCapturePage(tQSL_Location locp,int page)1788 tqsl_setStationLocationCapturePage(tQSL_Location locp, int page) {
1789 	TQSL_LOCATION *loc;
1790 	if (!(loc = check_loc(locp))) {
1791 		tqslTrace("tqsl_setStationLocationCapturePage", "check_loc error %d", tQSL_Error);
1792 		return 1;
1793 	}
1794 	if (page < 1 || page > static_cast<int>(loc->pagelist.size())) {
1795 		tqslTrace("tqsl_setStationLocationCapturePage", "Page %d out of range", page);
1796 		tQSL_Error = TQSL_ARGUMENT_ERROR;
1797 		return 1;
1798 	}
1799 	loc->page = page;
1800 	return 0;
1801 }
1802 
1803 DLLEXPORT int CALLCONVENTION
tqsl_setStationLocationCertFlags(tQSL_Location locp,int flags)1804 tqsl_setStationLocationCertFlags(tQSL_Location locp, int flags) {
1805 	TQSL_LOCATION *loc;
1806 	if (!(loc = check_loc(locp))) {
1807 		tqslTrace("tqsl_setStationLocationCertFlags", "check_loc error %d", tQSL_Error);
1808 		return 1;
1809 	}
1810 	if (loc->cert_flags != flags) {
1811 		loc->cert_flags = flags;
1812 		loc->newflags = true;
1813 		loc->page = 1;
1814 		if (update_page(1, loc)) {
1815 			tqslTrace("tqsl_setStationLocationCertFlags", "update_page error %d", tQSL_Error);
1816 			return 1;
1817 		}
1818 	}
1819 	return 0;
1820 }
1821 
1822 
1823 static int
find_next_page(TQSL_LOCATION * loc)1824 find_next_page(TQSL_LOCATION *loc) {
1825 	// Set next page based on page dependencies
1826 	TQSL_LOCATION_PAGE& p = loc->pagelist[loc->page-1];
1827 	map<int, XMLElement>::iterator pit;
1828 	p.next = 0;
1829 	for (pit = tqsl_page_map.begin(); pit != tqsl_page_map.end(); pit++) {
1830 		if (strtol(pit->second.getAttribute("follows").first.c_str(), NULL, 10) == loc->page) {
1831 			string dependsOn = pit->second.getAttribute("dependsOn").first;
1832 			string dependency = pit->second.getAttribute("dependency").first;
1833 			if (dependsOn == "") {
1834 				p.next = pit->first;
1835 				break;
1836 			}
1837 			TQSL_LOCATION_FIELD *fp = get_location_field(0, dependsOn, loc);
1838 			//if (fp->idx>=fp->items.size()) { cerr<<"!! " __FILE__ "(" << __LINE__ << "): Was going to index out of fp->items"<<endl; }
1839 			//else {
1840 			if (static_cast<int>(fp->items.size()) > fp->idx && fp->idx >= 0 && fp->items[fp->idx].text == dependency) {
1841 				p.next = pit->first;
1842 				break;	// Found next page
1843 			//}
1844 			}
1845 		}
1846 	}
1847 	return 0;
1848 }
1849 
1850 DLLEXPORT int CALLCONVENTION
tqsl_nextStationLocationCapture(tQSL_Location locp)1851 tqsl_nextStationLocationCapture(tQSL_Location locp) {
1852 	TQSL_LOCATION *loc;
1853 	if (!(loc = check_loc(locp))) {
1854 		tqslTrace("tqsl_nextStationLocationCapture", "check_loc error %d", tQSL_Error);
1855 		return 1;
1856 	}
1857 	if (find_next_page(loc))
1858 		return 0;
1859 	TQSL_LOCATION_PAGE &p = loc->pagelist[loc->page-1];
1860 	if (p.next > 0)
1861 		loc->page = p.next;
1862 	update_page(loc->page, loc);
1863 	return 0;
1864 }
1865 
1866 DLLEXPORT int CALLCONVENTION
tqsl_prevStationLocationCapture(tQSL_Location locp)1867 tqsl_prevStationLocationCapture(tQSL_Location locp) {
1868 	TQSL_LOCATION *loc;
1869 	if (!(loc = check_loc(locp))) {
1870 		tqslTrace("tqsl_prevStationLocationCapture", "check_loc error %d", tQSL_Error);
1871 		return 1;
1872 	}
1873 	TQSL_LOCATION_PAGE &p = loc->pagelist[loc->page-1];
1874 	if (p.prev > 0)
1875 		loc->page = p.prev;
1876 	return 0;
1877 }
1878 
1879 DLLEXPORT int CALLCONVENTION
tqsl_hasNextStationLocationCapture(tQSL_Location locp,int * rval)1880 tqsl_hasNextStationLocationCapture(tQSL_Location locp, int *rval) {
1881 	TQSL_LOCATION *loc;
1882 	if (!(loc = check_loc(locp))) {
1883 		tqslTrace("tqsl_hasNextStationLocationCapture", "check_loc error %d", tQSL_Error);
1884 		return 1;
1885 	}
1886 	if (rval == NULL) {
1887 		tqslTrace("tqsl_hasNextStationLocationCapture", "Arg error rval=NULL");
1888 		tQSL_Error = TQSL_ARGUMENT_ERROR;
1889 		return 1;
1890 	}
1891 	if (find_next_page(loc)) {
1892 		tqslTrace("tqsl_hasNextStationLocationCapture", "find_next_page error %d", tQSL_Error);
1893 		return 1;
1894 	}
1895 	*rval = (loc->pagelist[loc->page-1].next > 0);
1896 	return 0;
1897 }
1898 
1899 DLLEXPORT int CALLCONVENTION
tqsl_hasPrevStationLocationCapture(tQSL_Location locp,int * rval)1900 tqsl_hasPrevStationLocationCapture(tQSL_Location locp, int *rval) {
1901 	TQSL_LOCATION *loc;
1902 	if (!(loc = check_loc(locp))) {
1903 		tqslTrace("tqsl_hasPrevStationLocationCapture", "check_loc error %d", tQSL_Error);
1904 		return 1;
1905 	}
1906 	if (rval == NULL) {
1907 		tqslTrace("tqsl_hasPrevStationLocationCapture", "arg error rval=NULL");
1908 		tQSL_Error = TQSL_ARGUMENT_ERROR;
1909 		return 1;
1910 	}
1911 	*rval = (loc->pagelist[loc->page-1].prev > 0);
1912 	return 0;
1913 }
1914 
1915 DLLEXPORT int CALLCONVENTION
tqsl_getNumLocationField(tQSL_Location locp,int * numf)1916 tqsl_getNumLocationField(tQSL_Location locp, int *numf) {
1917 	TQSL_LOCATION *loc;
1918 	if (!(loc = check_loc(locp))) {
1919 		tqslTrace("tqsl_getNumLocationField", "check_loc error %d", tQSL_Error);
1920 		return 1;
1921 	}
1922 	if (numf == NULL) {
1923 		tqslTrace("tqsl_getNumLocationField", "arg error numf=NULL");
1924 		tQSL_Error = TQSL_ARGUMENT_ERROR;
1925 		return 1;
1926 	}
1927 	TQSL_LOCATION_FIELDLIST &fl = loc->pagelist[loc->page-1].fieldlist;
1928 	*numf = fl.size();
1929 	return 0;
1930 }
1931 
1932 DLLEXPORT int CALLCONVENTION
tqsl_getLocationFieldDataLabelSize(tQSL_Location locp,int field_num,int * rval)1933 tqsl_getLocationFieldDataLabelSize(tQSL_Location locp, int field_num, int *rval) {
1934 	TQSL_LOCATION *loc;
1935 	if (!(loc = check_loc(locp))) {
1936 		tqslTrace("tqsl_getLocationFieldDataLabelSize", "check_loc error %d", tQSL_Error);
1937 		return 1;
1938 	}
1939 	TQSL_LOCATION_FIELDLIST &fl = loc->pagelist[loc->page-1].fieldlist;
1940 	if (rval == NULL || field_num < 0 || field_num >= static_cast<int>(fl.size())) {
1941 		tqslTrace("tqsl_getLocationFieldDataLabelSize", "arg error rval=0x%lx, field_num=%d", rval, field_num);
1942 		tQSL_Error = TQSL_ARGUMENT_ERROR;
1943 		return 1;
1944 	}
1945 	*rval = fl[field_num].label.size()+1;
1946 	return 0;
1947 }
1948 
1949 DLLEXPORT int CALLCONVENTION
tqsl_getLocationFieldDataLabel(tQSL_Location locp,int field_num,char * buf,int bufsiz)1950 tqsl_getLocationFieldDataLabel(tQSL_Location locp, int field_num, char *buf, int bufsiz) {
1951 	TQSL_LOCATION *loc;
1952 	if (!(loc = check_loc(locp))) {
1953 		tqslTrace("tqsl_getLocationFieldDataLabel", "check_loc error %d", tQSL_Error);
1954 		return 1;
1955 	}
1956 	TQSL_LOCATION_FIELDLIST &fl = loc->pagelist[loc->page-1].fieldlist;
1957 	if (buf == NULL || field_num < 0 || field_num >= static_cast<int>(fl.size())) {
1958 		tqslTrace("tqsl_getLocationFieldDataLabel", "arg error buf=0x%lx, field_num=%d", buf, field_num);
1959 		tQSL_Error = TQSL_ARGUMENT_ERROR;
1960 		return 1;
1961 	}
1962 	strncpy(buf, fl[field_num].label.c_str(), bufsiz);
1963 	buf[bufsiz-1] = 0;
1964 	return 0;
1965 }
1966 
1967 DLLEXPORT int CALLCONVENTION
tqsl_getLocationFieldDataGABBISize(tQSL_Location locp,int field_num,int * rval)1968 tqsl_getLocationFieldDataGABBISize(tQSL_Location locp, int field_num, int *rval) {
1969 	TQSL_LOCATION *loc;
1970 	if (!(loc = check_loc(locp))) {
1971 		tqslTrace("tqsl_getLocationFieldDataGABBISize", "check_loc error %d", tQSL_Error);
1972 		return 1;
1973 	}
1974 	TQSL_LOCATION_FIELDLIST &fl = loc->pagelist[loc->page-1].fieldlist;
1975 	if (rval == NULL || field_num < 0 || field_num >= static_cast<int>(fl.size())) {
1976 		tqslTrace("tqsl_getLocationFieldDataGABBISize", "arg error rval=0x%lx, field_num=%d", rval, field_num);
1977 		tQSL_Error = TQSL_ARGUMENT_ERROR;
1978 		return 1;
1979 	}
1980 	*rval = fl[field_num].gabbi_name.size()+1;
1981 	return 0;
1982 }
1983 
1984 DLLEXPORT int CALLCONVENTION
tqsl_getLocationFieldDataGABBI(tQSL_Location locp,int field_num,char * buf,int bufsiz)1985 tqsl_getLocationFieldDataGABBI(tQSL_Location locp, int field_num, char *buf, int bufsiz) {
1986 	TQSL_LOCATION *loc;
1987 	if (!(loc = check_loc(locp))) {
1988 		tqslTrace("tqsl_getLocationFieldDataGABBI", "check_loc error %d", tQSL_Error);
1989 		return 1;
1990 	}
1991 	TQSL_LOCATION_FIELDLIST &fl = loc->pagelist[loc->page-1].fieldlist;
1992 	if (buf == NULL || field_num < 0 || field_num >= static_cast<int>(fl.size())) {
1993 		tqslTrace("tqsl_getLocationFieldDataGABBI", "arg error buf=0x%lx, field_num=%d", buf, field_num);
1994 		tQSL_Error = TQSL_ARGUMENT_ERROR;
1995 		return 1;
1996 	}
1997 	strncpy(buf, fl[field_num].gabbi_name.c_str(), bufsiz);
1998 	buf[bufsiz-1] = 0;
1999 	return 0;
2000 }
2001 
2002 DLLEXPORT int CALLCONVENTION
tqsl_getLocationFieldInputType(tQSL_Location locp,int field_num,int * type)2003 tqsl_getLocationFieldInputType(tQSL_Location locp, int field_num, int *type) {
2004 	TQSL_LOCATION *loc;
2005 	if (!(loc = check_loc(locp))) {
2006 		tqslTrace("tqsl_getLocationFieldInputType", "check_loc error %d", tQSL_Error);
2007 		return 1;
2008 	}
2009 	TQSL_LOCATION_FIELDLIST &fl = loc->pagelist[loc->page-1].fieldlist;
2010 	if (type == NULL || field_num < 0 || field_num >= static_cast<int>(fl.size())) {
2011 		tqslTrace("tqsl_getLocationFieldInputType", "arg error type=0x%lx, field_num=%d", type, field_num);
2012 		tQSL_Error = TQSL_ARGUMENT_ERROR;
2013 		return 1;
2014 	}
2015 	*type = fl[field_num].input_type;
2016 	return 0;
2017 }
2018 
2019 DLLEXPORT int CALLCONVENTION
tqsl_getLocationFieldChanged(tQSL_Location locp,int field_num,int * changed)2020 tqsl_getLocationFieldChanged(tQSL_Location locp, int field_num, int *changed) {
2021 	TQSL_LOCATION *loc;
2022 	if (!(loc = check_loc(locp))) {
2023 		tqslTrace("tqsl_getLocationFieldChanged", "check_loc error %d", tQSL_Error);
2024 		return 1;
2025 	}
2026 	TQSL_LOCATION_FIELDLIST &fl = loc->pagelist[loc->page-1].fieldlist;
2027 	if (changed == NULL || field_num < 0 || field_num >= static_cast<int>(fl.size())) {
2028 		tqslTrace("tqsl_getLocationFieldChanged", "arg error changed=0x%lx, field_num=%d", changed, field_num);
2029 		tQSL_Error = TQSL_ARGUMENT_ERROR;
2030 		return 1;
2031 	}
2032 	*changed = fl[field_num].changed;
2033 	return 0;
2034 }
2035 
2036 DLLEXPORT int CALLCONVENTION
tqsl_getLocationFieldDataType(tQSL_Location locp,int field_num,int * type)2037 tqsl_getLocationFieldDataType(tQSL_Location locp, int field_num, int *type) {
2038 	TQSL_LOCATION *loc;
2039 	if (!(loc = check_loc(locp))) {
2040 		tqslTrace("tqsl_getLocationFieldDataType", "check_loc error %d", tQSL_Error);
2041 		return 1;
2042 	}
2043 	TQSL_LOCATION_FIELDLIST &fl = loc->pagelist[loc->page-1].fieldlist;
2044 	if (type == NULL || field_num < 0 || field_num >= static_cast<int>(fl.size())) {
2045 		tqslTrace("tqsl_getLocationFieldDataType", "arg error type=0x%lx, field_num=%d", type, field_num);
2046 		tQSL_Error = TQSL_ARGUMENT_ERROR;
2047 		return 1;
2048 	}
2049 	*type = fl[field_num].data_type;
2050 	return 0;
2051 }
2052 
2053 DLLEXPORT int CALLCONVENTION
tqsl_getLocationFieldFlags(tQSL_Location locp,int field_num,int * flags)2054 tqsl_getLocationFieldFlags(tQSL_Location locp, int field_num, int *flags) {
2055 	TQSL_LOCATION *loc;
2056 	if (!(loc = check_loc(locp))) {
2057 		tqslTrace("tqsl_getLocationFieldFlags", "check_loc error %d", tQSL_Error);
2058 		return 1;
2059 	}
2060 	TQSL_LOCATION_FIELDLIST &fl = loc->pagelist[loc->page-1].fieldlist;
2061 	if (flags == NULL || field_num < 0 || field_num >= static_cast<int>(fl.size())) {
2062 		tqslTrace("tqsl_getLocationFieldFlags", "arg error flags=0x%lx, field_num=%d", flags, field_num);
2063 		tQSL_Error = TQSL_ARGUMENT_ERROR;
2064 		return 1;
2065 	}
2066 	*flags = fl[field_num].flags;
2067 	return 0;
2068 }
2069 
2070 DLLEXPORT int CALLCONVENTION
tqsl_getLocationFieldDataLength(tQSL_Location locp,int field_num,int * rval)2071 tqsl_getLocationFieldDataLength(tQSL_Location locp, int field_num, int *rval) {
2072 	TQSL_LOCATION *loc;
2073 	if (!(loc = check_loc(locp))) {
2074 		tqslTrace("tqsl_getLocationFieldDataLength", "check_loc error %d", tQSL_Error);
2075 		return 1;
2076 	}
2077 	TQSL_LOCATION_FIELDLIST &fl = loc->pagelist[loc->page-1].fieldlist;
2078 	if (rval == NULL || field_num < 0 || field_num >= static_cast<int>(fl.size())) {
2079 		tqslTrace("tqsl_getLocationFieldDataLength", "arg error rval=0x%lx, field_num=%d", rval, field_num);
2080 		tQSL_Error = TQSL_ARGUMENT_ERROR;
2081 		return 1;
2082 	}
2083 	*rval = fl[field_num].data_len;
2084 	return 0;
2085 }
2086 
2087 DLLEXPORT int CALLCONVENTION
tqsl_getLocationFieldCharData(tQSL_Location locp,int field_num,char * buf,int bufsiz)2088 tqsl_getLocationFieldCharData(tQSL_Location locp, int field_num, char *buf, int bufsiz) {
2089 	TQSL_LOCATION *loc;
2090 	if (!(loc = check_loc(locp))) {
2091 		tqslTrace("tqsl_getLocationFieldCharData", "check_loc error %d", tQSL_Error);
2092 		return 1;
2093 	}
2094 	TQSL_LOCATION_FIELDLIST &fl = loc->pagelist[loc->page-1].fieldlist;
2095 	if (buf == NULL || field_num < 0 || field_num >= static_cast<int>(fl.size())) {
2096 		tqslTrace("tqsl_getLocationFieldCharData", "arg errror buf=0x%lx, field_num=%d", buf, field_num);
2097 		tQSL_Error = TQSL_ARGUMENT_ERROR;
2098 		return 1;
2099 	}
2100 	if (fl[field_num].flags & TQSL_LOCATION_FIELD_UPPER)
2101 		strncpy(buf, string_toupper(fl[field_num].cdata).c_str(), bufsiz);
2102 	else
2103 		strncpy(buf, fl[field_num].cdata.c_str(), bufsiz);
2104 	buf[bufsiz-1] = 0;
2105 	return 0;
2106 }
2107 
2108 DLLEXPORT int CALLCONVENTION
tqsl_getLocationFieldIntData(tQSL_Location locp,int field_num,int * dat)2109 tqsl_getLocationFieldIntData(tQSL_Location locp, int field_num, int *dat) {
2110 	TQSL_LOCATION *loc;
2111 	if (!(loc = check_loc(locp))) {
2112 		tqslTrace("tqsl_getLocationFieldIntData", "check_loc error %d", tQSL_Error);
2113 		return 1;
2114 	}
2115 	TQSL_LOCATION_FIELDLIST &fl = loc->pagelist[loc->page-1].fieldlist;
2116 	if (dat == NULL || field_num < 0 || field_num >= static_cast<int>(fl.size())) {
2117 		tqslTrace("tqsl_getLocationFieldIntData", "arg error dat=0x%lx, field_num=%d", dat, field_num);
2118 		tQSL_Error = TQSL_ARGUMENT_ERROR;
2119 		return 1;
2120 	}
2121 	*dat = fl[field_num].idata;
2122 	return 0;
2123 }
2124 
2125 DLLEXPORT int CALLCONVENTION
tqsl_getLocationFieldIndex(tQSL_Location locp,int field_num,int * dat)2126 tqsl_getLocationFieldIndex(tQSL_Location locp, int field_num, int *dat) {
2127 	TQSL_LOCATION *loc;
2128 	if (!(loc = check_loc(locp))) {
2129 		tqslTrace("tqsl_getLocationFieldIndex", "check_loc error %d", tQSL_Error);
2130 		return 1;
2131 	}
2132 	TQSL_LOCATION_FIELDLIST &fl = loc->pagelist[loc->page-1].fieldlist;
2133 	if (dat == NULL || field_num < 0 || field_num >= static_cast<int>(fl.size())) {
2134 		tqslTrace("tqsl_getLocationFieldIndex", "arg error dat=0x%lx, field_num=%d", dat, field_num);
2135 		tQSL_Error = TQSL_ARGUMENT_ERROR;
2136 		return 1;
2137 	}
2138 	if (fl[field_num].input_type != TQSL_LOCATION_FIELD_DDLIST
2139 		&& fl[field_num].input_type != TQSL_LOCATION_FIELD_LIST) {
2140 		tqslTrace("tqsl_getLocationFieldIndex", "arg error input type mismatch");
2141 		tQSL_Error = TQSL_ARGUMENT_ERROR;
2142 		return 1;
2143 	}
2144 	*dat = fl[field_num].idx;
2145 	return 0;
2146 }
2147 
2148 DLLEXPORT int CALLCONVENTION
tqsl_setLocationFieldCharData(tQSL_Location locp,int field_num,const char * buf)2149 tqsl_setLocationFieldCharData(tQSL_Location locp, int field_num, const char *buf) {
2150 	TQSL_LOCATION *loc;
2151 	if (!(loc = check_loc(locp))) {
2152 		tqslTrace("tqsl_setLocationFieldCharData", "check_loc error %d", tQSL_Error);
2153 		return 1;
2154 	}
2155 	TQSL_LOCATION_FIELDLIST &fl = loc->pagelist[loc->page-1].fieldlist;
2156 	if (buf == NULL || field_num < 0 || field_num >= static_cast<int>(fl.size())) {
2157 		tqslTrace("tqsl_setLocationFieldCharData", "arg error buf=0x%lx, field_num=%d", buf, field_num);
2158 		tQSL_Error = TQSL_ARGUMENT_ERROR;
2159 		return 1;
2160 	}
2161 	fl[field_num].cdata = string(buf).substr(0, fl[field_num].data_len);
2162 	if (fl[field_num].flags & TQSL_LOCATION_FIELD_UPPER)
2163 		fl[field_num].cdata = string_toupper(fl[field_num].cdata);
2164 
2165 
2166 	if (fl[field_num].input_type == TQSL_LOCATION_FIELD_DDLIST
2167 		|| fl[field_num].input_type == TQSL_LOCATION_FIELD_LIST) {
2168 		if (fl[field_num].cdata == "") {
2169 			fl[field_num].idx = 0;
2170 			fl[field_num].idata = fl[field_num].items[0].ivalue;
2171 		} else {
2172 			for (int i = 0; i < static_cast<int>(fl[field_num].items.size()); i++) {
2173 				if (fl[field_num].items[i].text == fl[field_num].cdata) {
2174 					fl[field_num].idx = i;
2175 					fl[field_num].idata = fl[field_num].items[i].ivalue;
2176 					break;
2177 				}
2178 			}
2179 		}
2180 	}
2181 	return 0;
2182 }
2183 
2184 /* Set the field's index. For pick lists, this is the index into
2185  * 'items'. In that case, also set the field's data to the picked value.
2186  */
2187 DLLEXPORT int CALLCONVENTION
tqsl_setLocationFieldIndex(tQSL_Location locp,int field_num,int dat)2188 tqsl_setLocationFieldIndex(tQSL_Location locp, int field_num, int dat) {
2189 	TQSL_LOCATION *loc;
2190 	if (!(loc = check_loc(locp))) {
2191 		tqslTrace("tqsl_setLocationFieldIndex", "check_loc error %d", tQSL_Error);
2192 		return 1;
2193 	}
2194 	TQSL_LOCATION_FIELDLIST &fl = loc->pagelist[loc->page-1].fieldlist;
2195 	if (field_num < 0 || field_num >= static_cast<int>(fl.size())) {
2196 		tqslTrace("tqsl_setLocationFieldIndex", "arg error field_num=%d, dat=%d", field_num, dat);
2197 		tQSL_Error = TQSL_ARGUMENT_ERROR;
2198 		return 1;
2199 	}
2200 	fl[field_num].idx = dat;
2201 	if (fl[field_num].input_type == TQSL_LOCATION_FIELD_DDLIST
2202 		|| fl[field_num].input_type == TQSL_LOCATION_FIELD_LIST) {
2203 		if (dat >= 0 && dat < static_cast<int>(fl[field_num].items.size())) {
2204 			fl[field_num].idx = dat;
2205 			fl[field_num].cdata = fl[field_num].items[dat].text;
2206 			fl[field_num].idata = fl[field_num].items[dat].ivalue;
2207 		} else {
2208 			tqslTrace("tqsl_setLocationFieldIndex", "arg error field_num=%d", field_num);
2209 			tQSL_Error = TQSL_ARGUMENT_ERROR;
2210 			return 1;
2211 		}
2212 	}
2213 	return 0;
2214 }
2215 
2216 /* Set the field's integer data.
2217  */
2218 DLLEXPORT int CALLCONVENTION
tqsl_setLocationFieldIntData(tQSL_Location locp,int field_num,int dat)2219 tqsl_setLocationFieldIntData(tQSL_Location locp, int field_num, int dat) {
2220 	TQSL_LOCATION *loc;
2221 	if (!(loc = check_loc(locp))) {
2222 		tqslTrace("tqsl_setLocationFieldIntData", "check_loc error %d", tQSL_Error);
2223 		return 1;
2224 	}
2225 	TQSL_LOCATION_FIELDLIST &fl = loc->pagelist[loc->page-1].fieldlist;
2226 	if (field_num < 0 || field_num >= static_cast<int>(fl.size())) {
2227 		tqslTrace("tqsl_setLocationFieldIntData", "arg error field_num=%d, dat=%d", field_num, dat);
2228 		tQSL_Error = TQSL_ARGUMENT_ERROR;
2229 		return 1;
2230 	}
2231 	fl[field_num].idata = dat;
2232 	return 0;
2233 }
2234 
2235 /* For pick lists, this is the index into
2236  * 'items'. In that case, also set the field's char data to the picked value.
2237  */
2238 DLLEXPORT int CALLCONVENTION
tqsl_getNumLocationFieldListItems(tQSL_Location locp,int field_num,int * rval)2239 tqsl_getNumLocationFieldListItems(tQSL_Location locp, int field_num, int *rval) {
2240 	TQSL_LOCATION *loc;
2241 	if (!(loc = check_loc(locp))) {
2242 		tqslTrace("tqsl_getNumLocationFieldListItems", "check_loc error %d", tQSL_Error);
2243 		return 1;
2244 	}
2245 	if (rval == NULL) {
2246 		tqslTrace("tqsl_getNumLocationFieldListItems", "arg error rval=NULL");
2247 		tQSL_Error = TQSL_ARGUMENT_ERROR;
2248 		return 1;
2249 	}
2250 	TQSL_LOCATION_FIELDLIST &fl = loc->pagelist[loc->page-1].fieldlist;
2251 	*rval = fl[field_num].items.size();
2252 	return 0;
2253 }
2254 
2255 DLLEXPORT int CALLCONVENTION
tqsl_getLocationFieldListItem(tQSL_Location locp,int field_num,int item_idx,char * buf,int bufsiz)2256 tqsl_getLocationFieldListItem(tQSL_Location locp, int field_num, int item_idx, char *buf, int bufsiz) {
2257 	TQSL_LOCATION *loc;
2258 	if (!(loc = check_loc(locp))) {
2259 		tqslTrace("tqsl_getLocationFieldListItem", "check_loc error %d", tQSL_Error);
2260 		return 1;
2261 	}
2262 	TQSL_LOCATION_FIELDLIST &fl = loc->pagelist[loc->page-1].fieldlist;
2263 	if (buf == NULL || field_num < 0 || field_num >= static_cast<int>(fl.size())
2264 		|| (fl[field_num].input_type != TQSL_LOCATION_FIELD_LIST
2265 		&& fl[field_num].input_type != TQSL_LOCATION_FIELD_DDLIST)) {
2266 		tqslTrace("tqsl_getLocationFieldListItem", "arg error buf=0x%lx, field_num=%d", buf, field_num);
2267 		tQSL_Error = TQSL_ARGUMENT_ERROR;
2268 		return 1;
2269 	}
2270 	if (item_idx < 0 || item_idx >= static_cast<int>(fl[field_num].items.size())) {
2271 		tqslTrace("tqsl_getLocationFieldListItem", "arg error item_idx=%d", item_idx);
2272 		tQSL_Error = TQSL_ARGUMENT_ERROR;
2273 		return 1;
2274 	}
2275 	string& str = (fl[field_num].items[item_idx].label == "")
2276 		? fl[field_num].items[item_idx].text
2277 		: fl[field_num].items[item_idx].label;
2278 	strncpy(buf, str.c_str(), bufsiz);
2279 	buf[bufsiz - 1] = '\0';
2280 	return 0;
2281 }
2282 
2283 static string
tqsl_station_data_filename(bool deleted=false)2284 tqsl_station_data_filename(bool deleted = false) {
2285 	const char *f;
2286 	if (deleted)
2287 		f = "station_data_trash";
2288 	else
2289 		f = "station_data";
2290 
2291 	string s = tQSL_BaseDir;
2292 #ifdef _WIN32
2293 	s += "\\";
2294 #else
2295 	s += "/";
2296 #endif
2297 	s += f;
2298 	return s;
2299 }
2300 
2301 static int
tqsl_load_station_data(XMLElement & xel,bool deleted=false)2302 tqsl_load_station_data(XMLElement &xel, bool deleted = false) {
2303 	int status = xel.parseFile(tqsl_station_data_filename(deleted).c_str());
2304 	tqslTrace("tqsl_load_station_data", "file %s parse status %d", tqsl_station_data_filename(deleted).c_str(), status);
2305 	if (status) {
2306 		if (errno == ENOENT) {		// If there's no file, no error.
2307 			tqslTrace("tqsl_load_station_data", "File does not exist");
2308 			return 0;
2309 		}
2310 		strncpy(tQSL_ErrorFile, tqsl_station_data_filename(deleted).c_str(), sizeof tQSL_ErrorFile);
2311 		if (status == XML_PARSE_SYSTEM_ERROR) {
2312 			tQSL_Error = TQSL_FILE_SYSTEM_ERROR;
2313 			tQSL_Errno = errno;
2314 			tqslTrace("tqsl_load_station_data", "parse error, errno=%d", tQSL_Errno);
2315 		} else {
2316 			tqslTrace("tqsl_load_station_data", "syntax error");
2317 			tQSL_Error = TQSL_FILE_SYNTAX_ERROR;
2318 		}
2319 		return 1;
2320 	}
2321 	return status;
2322 }
2323 
2324 static int
tqsl_dump_station_data(XMLElement & xel,bool deleted=false)2325 tqsl_dump_station_data(XMLElement &xel, bool deleted = false) {
2326 	ofstream out;
2327 	string fn = tqsl_station_data_filename(deleted);
2328 
2329 	out.exceptions(ios::failbit | ios::eofbit | ios::badbit);
2330 	try {
2331 #ifdef _WIN32
2332 		wchar_t* wfn = utf8_to_wchar(fn.c_str());
2333 		out.open(wfn);
2334 		free_wchar(wfn);
2335 #else
2336 		out.open(fn.c_str());
2337 #endif
2338 		out << xel << endl;
2339 		out.close();
2340 	}
2341 	catch(exception& x) {
2342 		tQSL_Error = TQSL_CUSTOM_ERROR;
2343 		snprintf(tQSL_CustomError, sizeof tQSL_CustomError,
2344 				"Unable to save new station location file (%s): %s/%s",
2345 				fn.c_str(), x.what(), strerror(errno));
2346 		tqslTrace("tqsl_dump_station_data", "file error %s %s", fn.c_str(), tQSL_CustomError);
2347 		return 1;
2348 	}
2349 	return 0;
2350 }
2351 
2352 static int
tqsl_load_loc(TQSL_LOCATION * loc,XMLElementList::iterator ep,bool ignoreZones)2353 tqsl_load_loc(TQSL_LOCATION *loc, XMLElementList::iterator ep, bool ignoreZones) {
2354 	bool exists;
2355 	loc->page = 1;
2356 	loc->data_errors[0] = '\0';
2357 	int bad_ituz = 0;
2358 	int bad_cqz = 0;
2359 	tqslTrace("tqsl_load_loc", NULL);
2360 	while(1) {
2361 		TQSL_LOCATION_PAGE& page = loc->pagelist[loc->page-1];
2362 		for (int fidx = 0; fidx < static_cast<int>(page.fieldlist.size()); fidx++) {
2363 			TQSL_LOCATION_FIELD& field = page.fieldlist[fidx];
2364 			if (field.gabbi_name != "") {
2365 				// A field that may exist
2366 				XMLElement el;
2367 				if (ep->second->getFirstElement(field.gabbi_name, el)) {
2368 					field.cdata = el.getText();
2369 					switch (field.input_type) {
2370 						case TQSL_LOCATION_FIELD_DDLIST:
2371 						case TQSL_LOCATION_FIELD_LIST:
2372 							exists = false;
2373 							for (int i = 0; i < static_cast<int>(field.items.size()); i++) {
2374 								string cp = field.items[i].text;
2375 								int q = strcasecmp(field.cdata.c_str(), cp.c_str());
2376 								if (q == 0) {
2377 									field.idx = i;
2378 									field.cdata = cp;
2379 									field.idata = field.items[i].ivalue;
2380 									exists = true;
2381 									break;
2382 								}
2383 							}
2384 							if (!exists) {
2385 								if (field.gabbi_name == "CQZ")
2386 									bad_cqz = strtol(field.cdata.c_str(), NULL, 10);
2387 								else if (field.gabbi_name == "ITUZ")
2388 									bad_ituz = strtol(field.cdata.c_str(), NULL, 10);
2389 								else if (field.gabbi_name == "CALL" || field.gabbi_name == "DXCC")
2390 									field.idx = -1;
2391 							}
2392 							break;
2393 						case TQSL_LOCATION_FIELD_TEXT:
2394 							field.cdata = trim(field.cdata);
2395 							if (field.data_type == TQSL_LOCATION_FIELD_INT)
2396 								field.idata = strtol(field.cdata.c_str(), NULL, 10);
2397 							break;
2398 					}
2399 				}
2400 			}
2401 			if (update_page(loc->page, loc))
2402 				return 1;
2403 		}
2404 		int rval;
2405 		if (tqsl_hasNextStationLocationCapture(loc, &rval) || !rval)
2406 			break;
2407 		tqsl_nextStationLocationCapture(loc);
2408 	}
2409 	if (ignoreZones)
2410 		return 0;
2411 	if (bad_cqz && bad_ituz) {
2412 		snprintf(loc->data_errors, sizeof(loc->data_errors),
2413 			"This station location is configured with invalid CQ zone %d and invalid ITU zone %d.", bad_cqz, bad_ituz);
2414 	} else if (bad_cqz) {
2415 		snprintf(loc->data_errors, sizeof(loc->data_errors), "This station location is configured with invalid CQ zone %d.", bad_cqz);
2416 	} else if (bad_ituz) {
2417 		snprintf(loc->data_errors, sizeof(loc->data_errors), "This station location is configured with invalid ITU zone %d.", bad_ituz);
2418 	}
2419 	tqslTrace("tqsl_load_loc", "data_errors=%s", loc->data_errors);
2420 	return 0;
2421 }
2422 
2423 DLLEXPORT int CALLCONVENTION
tqsl_getStationDataEnc(tQSL_StationDataEnc * sdata)2424 tqsl_getStationDataEnc(tQSL_StationDataEnc *sdata) {
2425 	char *dbuf = NULL;
2426 	size_t dlen = 0;
2427 	gzFile in = NULL;
2428 #ifdef _WIN32
2429 	wchar_t *fn = utf8_to_wchar(tqsl_station_data_filename().c_str());
2430 	int fd = _wopen(fn, _O_RDONLY|_O_BINARY);
2431 	free_wchar(fn);
2432 	if (fd != -1)
2433 		in = gzdopen(fd, "rb");
2434 #else
2435 	in = gzopen(tqsl_station_data_filename().c_str(), "rb");
2436 #endif
2437 
2438 	if (!in) {
2439 		if (errno == ENOENT) {
2440 			*sdata = NULL;
2441 			tqslTrace("tqsl_getStationDataEnc", "File %s does not exist", tqsl_station_data_filename().c_str());
2442 			return 0;
2443 		}
2444 		tQSL_Error = TQSL_SYSTEM_ERROR;
2445 		tQSL_Errno = errno;
2446 		strncpy(tQSL_ErrorFile, tqsl_station_data_filename().c_str(), sizeof tQSL_ErrorFile);
2447 		tqslTrace("tqsl_getStationDataEnc", "File %s open error %s", tqsl_station_data_filename().c_str(), strerror(tQSL_Error)
2448 );
2449 		return 1;
2450 	}
2451 	char buf[2048];
2452 	int rcount;
2453 	while ((rcount = gzread(in, buf, sizeof buf)) > 0) {
2454 		dlen += rcount;
2455 	}
2456 	dbuf = reinterpret_cast<char *>(malloc(dlen + 2));
2457 	if (!dbuf) {
2458 		tqslTrace("tqsl_getStationDataEnc", "memory allocation error %d", dlen+2);
2459 		return 1;
2460 	}
2461 	*sdata = dbuf;
2462 
2463 	gzrewind(in);
2464 	while ((rcount = gzread(in, dbuf, sizeof buf)) > 0) {
2465 		dbuf += rcount;
2466 	}
2467 	*dbuf = '\0';
2468 	gzclose(in);
2469 	return 0;
2470 }
2471 
2472 DLLEXPORT int CALLCONVENTION
tqsl_freeStationDataEnc(tQSL_StationDataEnc sdata)2473 	tqsl_freeStationDataEnc(tQSL_StationDataEnc sdata) {
2474 	if (sdata)
2475 		free(sdata);
2476 	return 0; //can never fail
2477 }
2478 
2479 DLLEXPORT int CALLCONVENTION
tqsl_mergeStationLocations(const char * locdata)2480 tqsl_mergeStationLocations(const char *locdata) {
2481 	XMLElement new_data;
2482 	XMLElement old_data;
2483 	XMLElement new_top_el;
2484 	XMLElement old_top_el;
2485 	vector<string> locnames;
2486 
2487 	tqslTrace("tqsl_mergeStationLocations", NULL);
2488 	// Load the current station data
2489 	if (tqsl_load_station_data(old_top_el)) {
2490 		tqslTrace("tqsl_mergeStationLocations", "error loading station data");
2491 		return 1;
2492 	}
2493 	// Parse the data to be merged
2494 	new_top_el.parseString(locdata);
2495 
2496 	if (!new_top_el.getFirstElement(new_data))
2497 		new_data.setElementName("StationDataFile");
2498 
2499 	if (!old_top_el.getFirstElement(old_data))
2500 		old_data.setElementName("StationDataFile");
2501 
2502 	// Build a list of existing station locations
2503 	XMLElementList& namelist = old_data.getElementList();
2504 	XMLElementList::iterator nameiter;
2505 	XMLElement locname;
2506 	for (nameiter = namelist.find("StationData"); nameiter != namelist.end(); nameiter++) {
2507 		if (nameiter->first != "StationData")
2508 			break;
2509 		pair<string, bool> rval = nameiter->second->getAttribute("name");
2510 		if (rval.second) {
2511 			locnames.push_back(rval.first);
2512 		}
2513 	}
2514 
2515 	// Iterate the new locations
2516 	XMLElementList& ellist = new_data.getElementList();
2517 	XMLElementList::iterator ep;
2518 	old_data.setPretext(old_data.getPretext() + "  ");
2519 	for (ep = ellist.find("StationData"); ep != ellist.end(); ep++) {
2520 		if (ep->first != "StationData")
2521 			break;
2522 		pair<string, bool> rval = ep->second->getAttribute("name");
2523 		bool found = false;
2524 		if (rval.second) {
2525 			for (size_t j = 0; j < locnames.size(); j++) {
2526 				if (locnames[j] == rval.first) {
2527 					found = true;
2528 					break;
2529 				}
2530 			}
2531 		}
2532 		if (!found) {
2533 			// Add this one to the station data file
2534 			XMLElement *newtop = new XMLElement("StationData");
2535 			newtop->setPretext("\n  ");
2536 			newtop->setAttribute("name", rval.first);
2537 			newtop->setText("\n  ");
2538 			XMLElement el;
2539 			bool elok = ep->second->getFirstElement(el);
2540 			while (elok) {
2541 				XMLElement *sub = new XMLElement;
2542 				sub->setPretext(newtop->getPretext() + "  ");
2543 				sub->setElementName(el.getElementName());
2544 				sub->setText(el.getText());
2545 				newtop->addElement(sub);
2546 				elok = ep->second->getNextElement(el);
2547 			}
2548 			old_data.addElement(newtop);
2549 			old_data.setText("\n");
2550 		}
2551 	}
2552 	return tqsl_dump_station_data(old_data);
2553 }
2554 
2555 // Move a station location to or from the trash
2556 static int
tqsl_move_station_location(const char * name,bool fromtrash)2557 tqsl_move_station_location(const char *name, bool fromtrash) {
2558 	tqslTrace("tqsl_move_station_location", "name=%s, fromtrash=%d", name, fromtrash);
2559 	XMLElement from_top_el;
2560 	XMLElement to_top_el;
2561 
2562 	if (tqsl_load_station_data(from_top_el, fromtrash)) {
2563 		tqslTrace("tqsl_move_station_location", "error %d loading data", tQSL_Error);
2564 		return 1;
2565 	}
2566 
2567 	if (tqsl_load_station_data(to_top_el, !fromtrash)) {
2568 		tqslTrace("tqsl_move_station_location", "error %d loading data", tQSL_Error);
2569 		return 1;
2570 	}
2571 
2572 	XMLElement from_sfile;
2573 	XMLElement to_sfile;
2574 	if (!from_top_el.getFirstElement(from_sfile))
2575 		from_sfile.setElementName("StationDataFile");
2576 
2577 	if (!to_top_el.getFirstElement(to_sfile))
2578 		to_sfile.setElementName("StationDataFile");
2579 
2580 	XMLElementList& from_ellist = from_sfile.getElementList();
2581 	XMLElementList::iterator from_ep;
2582 	for (from_ep = from_ellist.find("StationData"); from_ep != from_ellist.end(); from_ep++) {
2583 		if (from_ep->first != "StationData")
2584 			break;
2585 		pair<string, bool> from_rval = from_ep->second->getAttribute("name");
2586 		if (from_rval.second && !strcasecmp(from_rval.first.c_str(), name)) {
2587 			// Match, move it.
2588 			// First, delete any old backup for this station location
2589 			XMLElementList& to_ellist = to_sfile.getElementList();
2590 			XMLElementList::iterator to_ep;
2591 			for (to_ep = to_ellist.find("StationData"); to_ep != to_ellist.end(); to_ep++) {
2592 				if (to_ep->first != "StationData")
2593 					break;
2594 				pair<string, bool> to_rval = to_ep->second->getAttribute("name");
2595 				if (to_rval.second && !strcasecmp(to_rval.first.c_str(), name)) {
2596 					to_ellist.erase(to_ep);
2597 					break;
2598 				}
2599 			}
2600 			// Now add it to the target
2601 			XMLElement *newtop = new XMLElement("StationData");
2602 			newtop->setPretext("\n  ");
2603 			newtop->setAttribute("name", from_rval.first);
2604 			newtop->setText("\n  ");
2605 			XMLElement el;
2606 			bool elok = from_ep->second->getFirstElement(el);
2607 			while (elok) {
2608 				XMLElement *sub = new XMLElement;
2609 				sub->setPretext(newtop->getPretext() + "  ");
2610 				sub->setElementName(el.getElementName());
2611 				sub->setText(el.getText());
2612 				newtop->addElement(sub);
2613 				elok = from_ep->second->getNextElement(el);
2614 			}
2615 			to_sfile.addElement(newtop);
2616 			to_sfile.setText("\n");
2617 			tqsl_dump_station_data(to_sfile, !fromtrash);
2618 			from_ellist.erase(from_ep);
2619 			return tqsl_dump_station_data(from_sfile, fromtrash);
2620 		}
2621 	}
2622 	tqslTrace("tqsl_move_station_location", "location not found");
2623 	tQSL_Error = TQSL_LOCATION_NOT_FOUND;
2624 	return 1;
2625 }
2626 
2627 DLLEXPORT int CALLCONVENTION
tqsl_deleteStationLocation(const char * name)2628 tqsl_deleteStationLocation(const char *name) {
2629 	tqslTrace("tqsl_deleteStationLocation", "name=%s", name);
2630 
2631 	return tqsl_move_station_location(name, false);
2632 }
2633 
2634 DLLEXPORT int CALLCONVENTION
tqsl_restoreStationLocation(const char * name)2635 tqsl_restoreStationLocation(const char *name) {
2636 	tqslTrace("tqsl_restoreStationLocation", "name=%s", name);
2637 
2638 	return tqsl_move_station_location(name, true);
2639 }
2640 
2641 DLLEXPORT int CALLCONVENTION
tqsl_getStationLocation(tQSL_Location * locp,const char * name)2642 tqsl_getStationLocation(tQSL_Location *locp, const char *name) {
2643 	if (tqsl_initStationLocationCapture(locp)) {
2644 		tqslTrace("tqsl_getStationLocation", "name=%s error=%d", name, tQSL_Error);
2645 		return 1;
2646 	}
2647 	TQSL_LOCATION *loc;
2648 	if (!(loc = check_loc(*locp))) {
2649 		tqslTrace("tqsl_getStationLocation", "loc error %d", tQSL_Error);
2650 		return 1;
2651 	}
2652 	loc->name = name;
2653 	XMLElement top_el;
2654 	if (tqsl_load_station_data(top_el)) {
2655 		tqslTrace("tqsl_getStationLocation", "load station data error %d", tQSL_Error);
2656 		return 1;
2657 	}
2658 	XMLElement sfile;
2659 	if (!top_el.getFirstElement(sfile))
2660 		sfile.setElementName("StationDataFile");
2661 
2662 	XMLElementList& ellist = sfile.getElementList();
2663 
2664 	bool exists = false;
2665 	XMLElementList::iterator ep;
2666 	for (ep = ellist.find("StationData"); ep != ellist.end(); ep++) {
2667 		if (ep->first != "StationData")
2668 			break;
2669 		pair<string, bool> rval = ep->second->getAttribute("name");
2670 		if (rval.second && !strcasecmp(trim(rval.first).c_str(), trim(loc->name).c_str())) {
2671 			exists = true;
2672 			break;
2673 		}
2674 	}
2675 	if (!exists) {
2676 		tQSL_Error = TQSL_LOCATION_NOT_FOUND;
2677 		tqslTrace("tqsl_getStationLocation", "location %s does not exist", name);
2678 		return 1;
2679 	}
2680 	return tqsl_load_loc(loc, ep, false);
2681 }
2682 
2683 DLLEXPORT int CALLCONVENTION
tqsl_getStationLocationErrors(tQSL_Location locp,char * buf,int bufsiz)2684 tqsl_getStationLocationErrors(tQSL_Location locp, char *buf, int bufsiz) {
2685 	TQSL_LOCATION *loc;
2686 	if (!(loc = check_loc(locp))) {
2687 		tqslTrace("tqsl_getStationLocation", "loc error %d", tQSL_Error);
2688 		return 1;
2689 	}
2690 	if (buf == NULL) {
2691 		tQSL_Error = TQSL_ARGUMENT_ERROR;
2692 		tqslTrace("tqsl_getStationLocation", "buf = NULL");
2693 		return 1;
2694 	}
2695 	strncpy(buf, loc->data_errors, bufsiz);
2696 	buf[bufsiz-1] = 0;
2697 	return 0;
2698 }
2699 
2700 DLLEXPORT int CALLCONVENTION
tqsl_getNumStationLocations(tQSL_Location locp,int * nloc)2701 tqsl_getNumStationLocations(tQSL_Location locp, int *nloc) {
2702 	TQSL_LOCATION *loc;
2703 	if (!(loc = check_loc(locp))) {
2704 		tqslTrace("tqsl_getNumStationLocations", "loc error %d", tQSL_Error);
2705 		return 1;
2706 	}
2707 	if (nloc == NULL) {
2708 		tqslTrace("tqsl_getNumStationLocations", "arg error nloc=NULL");
2709 		tQSL_Error = TQSL_ARGUMENT_ERROR;
2710 		return 1;
2711 	}
2712 	loc->names.clear();
2713 	XMLElement top_el;
2714 	if (tqsl_load_station_data(top_el)) {
2715 		tqslTrace("tqsl_getNumStationLocations", "error %d loading station data", tQSL_Error);
2716 		return 1;
2717 	}
2718 	XMLElement sfile;
2719 	if (top_el.getFirstElement(sfile)) {
2720 		XMLElement sd;
2721 		bool ok = sfile.getFirstElement("StationData", sd);
2722 		while (ok && sd.getElementName() == "StationData") {
2723 			pair<string, bool> name = sd.getAttribute("name");
2724 			if (name.second) {
2725 				XMLElement xc;
2726 				string call;
2727 				if (sd.getFirstElement("CALL", xc))
2728 					call = xc.getText();
2729 				loc->names.push_back(TQSL_NAME(name.first, call));
2730 			}
2731 			ok = sfile.getNextElement(sd);
2732 		}
2733 	}
2734 	*nloc = loc->names.size();
2735 	return 0;
2736 }
2737 
2738 DLLEXPORT int CALLCONVENTION
tqsl_getStationLocationName(tQSL_Location locp,int idx,char * buf,int bufsiz)2739 tqsl_getStationLocationName(tQSL_Location locp, int idx, char *buf, int bufsiz) {
2740 	TQSL_LOCATION *loc;
2741 	if (!(loc = check_loc(locp))) {
2742 		tqslTrace("tqsl_getStationLocationName", "loc error %d", tQSL_Error);
2743 		return 1;
2744 	}
2745 	if (buf == NULL || idx < 0 || idx >= static_cast<int>(loc->names.size())) {
2746 		tqslTrace("tqsl_getStationLocationName", "arg error buf=0x%lx, idx=%d", buf, idx);
2747 		tQSL_Error = TQSL_ARGUMENT_ERROR;
2748 		return 1;
2749 	}
2750 	strncpy(buf, loc->names[idx].name.c_str(), bufsiz);
2751 	buf[bufsiz-1] = 0;
2752 	return 0;
2753 }
2754 
2755 DLLEXPORT int CALLCONVENTION
tqsl_getStationLocationCallSign(tQSL_Location locp,int idx,char * buf,int bufsiz)2756 tqsl_getStationLocationCallSign(tQSL_Location locp, int idx, char *buf, int bufsiz) {
2757 	TQSL_LOCATION *loc;
2758 	if (!(loc = check_loc(locp))) {
2759 		tqslTrace("tqsl_getStationLocationCallSign", "loc error %d", tQSL_Error);
2760 		return 1;
2761 	}
2762 	if (buf == NULL || idx < 0 || idx >= static_cast<int>(loc->names.size())) {
2763 		tqslTrace("tqsl_getStationLocationCallSign", "arg error buf=0x%lx, idx=%d", buf, idx);
2764 		tQSL_Error = TQSL_ARGUMENT_ERROR;
2765 		return 1;
2766 	}
2767 	strncpy(buf, loc->names[idx].call.c_str(), bufsiz);
2768 	buf[bufsiz-1] = 0;
2769 	return 0;
2770 }
2771 
2772 DLLEXPORT int CALLCONVENTION
tqsl_getStationLocationField(tQSL_Location locp,const char * name,char * namebuf,int bufsize)2773 tqsl_getStationLocationField(tQSL_Location locp, const char *name, char *namebuf, int bufsize) {
2774 	int old_page;
2775 	TQSL_LOCATION *loc;
2776 	if (!(loc = check_loc(locp))) {
2777 		tqslTrace("tqsl_getStationLocationField", "loc error %d", tQSL_Error);
2778 		return 1;
2779 	}
2780 	if (name == NULL || namebuf == NULL) {
2781 		tqslTrace("tqsl_getStationLocationField", "arg error name=0x%lx, namebuf=0x%lx", name, namebuf);
2782 		tQSL_Error = TQSL_ARGUMENT_ERROR;
2783 		return 1;
2784 	}
2785 	if (tqsl_getStationLocationCapturePage(loc, &old_page)) {
2786 		tqslTrace("tqsl_getStationLocationField", "get cap page error %d", tQSL_Error);
2787 		return 1;
2788 	}
2789 	string find = name;
2790 
2791 	tqsl_setStationLocationCapturePage(loc, 1);
2792 	do {
2793 		int numf;
2794 		if (tqsl_getNumLocationField(loc, &numf)) {
2795 			tqslTrace("tqsl_getStationLocationField", "erro getting num fields %d", tQSL_Error);
2796 			return 1;
2797 		}
2798 		for (int i = 0; i < numf; i++) {
2799 			TQSL_LOCATION_FIELD& field = loc->pagelist[loc->page-1].fieldlist[i];
2800 			if (find == field.gabbi_name) {	// Found it
2801 				switch (field.input_type) {
2802 					case TQSL_LOCATION_FIELD_DDLIST:
2803 					case TQSL_LOCATION_FIELD_LIST:
2804 						if (field.data_type == TQSL_LOCATION_FIELD_INT) {
2805 							char numbuf[20];
2806 							if (static_cast<int>(field.items.size()) <= field.idx) {
2807 								strncpy(namebuf, field.cdata.c_str(), bufsize);
2808 							} else if (field.idx == 0 && field.items[field.idx].label == "[None]") {
2809 								strncpy(namebuf, "", bufsize);
2810 							} else {
2811 								snprintf(numbuf, sizeof numbuf, "%d", field.items[field.idx].ivalue);
2812 								strncpy(namebuf, numbuf, bufsize);
2813 							}
2814 						} else if (field.idx < 0 || field.idx >= static_cast<int>(field.items.size())) {
2815 							// Allow CALL to not be in the items list
2816 							if (field.idx == -1 && i == 0)
2817 								strncpy(namebuf, field.cdata.c_str(), bufsize);
2818 							else
2819 								strncpy(namebuf, "", bufsize);
2820 						} else {
2821 							if (field.items[field.idx].label == "") {
2822 								strncpy(namebuf, field.items[field.idx].text.c_str(), bufsize);
2823 							} else {
2824 								strncpy(namebuf, field.items[field.idx].label.c_str(), bufsize);
2825 							}
2826 						}
2827 						break;
2828 					case TQSL_LOCATION_FIELD_TEXT:
2829 						field.cdata = trim(field.cdata);
2830 						if (field.flags & TQSL_LOCATION_FIELD_UPPER)
2831 							field.cdata = string_toupper(field.cdata);
2832 						strncpy(namebuf, field.cdata.c_str(), bufsize);
2833 						break;
2834 				}
2835 				goto done;
2836 			}
2837 		}
2838 		int rval;
2839 		if (tqsl_hasNextStationLocationCapture(loc, &rval) || !rval)
2840 			break;
2841 		if (tqsl_nextStationLocationCapture(loc)) {
2842 			tqslTrace("tqsl_getStationLocationField", "error in nextStationLocationCapture %d", tQSL_Error);
2843 			return 1;
2844 		}
2845 	} while (1);
2846 	strncpy(namebuf, "", bufsize);		// Did not find it
2847  done:
2848 	tqsl_setStationLocationCapturePage(loc, old_page);
2849 	return 0;
2850 }
2851 
2852 static int
tqsl_location_to_xml(TQSL_LOCATION * loc,XMLElement & sd)2853 tqsl_location_to_xml(TQSL_LOCATION *loc, XMLElement& sd) {
2854 	int old_page;
2855 	if (tqsl_getStationLocationCapturePage(loc, &old_page)) {
2856 		tqslTrace("tqsl_location_to_xml",  "get_sta_loc_cap_page error %d", tQSL_Error);
2857 		return 1;
2858 	}
2859 	tqsl_setStationLocationCapturePage(loc, 1);
2860 	do {
2861 		int numf;
2862 		if (tqsl_getNumLocationField(loc, &numf)) {
2863 			tqslTrace("tqsl_location_to_xml", "get num loc field error %d", tQSL_Error);
2864 			return 1;
2865 		}
2866 		for (int i = 0; i < numf; i++) {
2867 			TQSL_LOCATION_FIELD& field = loc->pagelist[loc->page-1].fieldlist[i];
2868 			XMLElement *fd = new XMLElement;
2869 			fd->setPretext(sd.getPretext() + "  ");
2870 			fd->setElementName(field.gabbi_name);
2871 			switch (field.input_type) {
2872 				case TQSL_LOCATION_FIELD_DDLIST:
2873 				case TQSL_LOCATION_FIELD_LIST:
2874 					if (field.idx < 0 || field.idx >= static_cast<int>(field.items.size())) {
2875 						fd->setText("");
2876 						if (field.gabbi_name == "CALL") {
2877 							fd->setText("NONE");
2878 						}
2879 					} else if (field.data_type == TQSL_LOCATION_FIELD_INT) {
2880 						char numbuf[20];
2881 						snprintf(numbuf, sizeof numbuf, "%d", field.items[field.idx].ivalue);
2882 						fd->setText(numbuf);
2883 					} else {
2884 						fd->setText(field.items[field.idx].text);
2885 					}
2886 					break;
2887 				case TQSL_LOCATION_FIELD_TEXT:
2888 					field.cdata = trim(field.cdata);
2889 					if (field.flags & TQSL_LOCATION_FIELD_UPPER)
2890 						field.cdata = string_toupper(field.cdata);
2891 					fd->setText(field.cdata);
2892 					break;
2893 			}
2894 			if (strcmp(fd->getText().c_str(), ""))
2895 				sd.addElement(fd);
2896 		}
2897 		int rval;
2898 		if (tqsl_hasNextStationLocationCapture(loc, &rval) || !rval)
2899 			break;
2900 		if (tqsl_nextStationLocationCapture(loc))
2901 			return 1;
2902 	} while (1);
2903 	tqsl_setStationLocationCapturePage(loc, old_page);
2904 	return 0;
2905 }
2906 
2907 DLLEXPORT int CALLCONVENTION
tqsl_setStationLocationCaptureName(tQSL_Location locp,const char * name)2908 tqsl_setStationLocationCaptureName(tQSL_Location locp, const char *name) {
2909 	TQSL_LOCATION *loc;
2910 	if (!(loc = check_loc(locp))) {
2911 		tqslTrace("tqsl_setStationLocationCaptureName", "loc error %d", tQSL_Error);
2912 		return 1;
2913 	}
2914 	if (name == NULL) {
2915 		tqslTrace("tqsl_setStationLocationCaptureName", "arg error name=null");
2916 		tQSL_Error = TQSL_ARGUMENT_ERROR;
2917 		return 1;
2918 	}
2919 	loc->name = name;
2920 	return 0;
2921 }
2922 
2923 DLLEXPORT int CALLCONVENTION
tqsl_getStationLocationCaptureName(tQSL_Location locp,char * namebuf,int bufsize)2924 tqsl_getStationLocationCaptureName(tQSL_Location locp, char *namebuf, int bufsize) {
2925 	TQSL_LOCATION *loc;
2926 	if (!(loc = check_loc(locp))) {
2927 		tqslTrace("tqsl_getStationLocationCaptureName", "loc error %d", tQSL_Error);
2928 		return 1;
2929 	}
2930 	if (namebuf == NULL) {
2931 		tqslTrace("tqsl_getStationLocationCaptureName", "arg error namebuf=null");
2932 		tQSL_Error = TQSL_ARGUMENT_ERROR;
2933 		return 1;
2934 	}
2935 	strncpy(namebuf, loc->name.c_str(), bufsize);
2936 	namebuf[bufsize-1] = 0;
2937 	return 0;
2938 }
2939 
2940 
2941 DLLEXPORT int CALLCONVENTION
tqsl_saveStationLocationCapture(tQSL_Location locp,int overwrite)2942 tqsl_saveStationLocationCapture(tQSL_Location locp, int overwrite) {
2943 	TQSL_LOCATION *loc;
2944 	if (!(loc = check_loc(locp))) {
2945 		tqslTrace("tqsl_saveStationLocationCaptureName", "loc error %d", tQSL_Error);
2946 		return 1;
2947 	}
2948 	if (loc->name == "") {
2949 		tqslTrace("tqsl_saveStationLocationCaptureName", "name empty");
2950 		tQSL_Error = TQSL_EXPECTED_NAME;
2951 		return 1;
2952 	}
2953 	XMLElement top_el;
2954 	if (tqsl_load_station_data(top_el)) {
2955 		tqslTrace("tqsl_saveStationLocationCaptureName", "error %d loading station data", tQSL_Error);
2956 		return 1;
2957 	}
2958 	XMLElement sfile;
2959 	if (!top_el.getFirstElement(sfile))
2960 		sfile.setElementName("StationDataFile");
2961 
2962 	XMLElementList& ellist = sfile.getElementList();
2963 	bool exists = false;
2964 	XMLElementList::iterator ep;
2965 	for (ep = ellist.find("StationData"); ep != ellist.end(); ep++) {
2966 		if (ep->first != "StationData")
2967 			break;
2968 		pair<string, bool> rval = ep->second->getAttribute("name");
2969 		if (rval.second && !strcasecmp(rval.first.c_str(), loc->name.c_str())) {
2970 			exists = true;
2971 			break;
2972 		}
2973 	}
2974 	if (exists && !overwrite) {
2975 		tqslTrace("tqsl_saveStationLocationCaptureName", "exists, no overwrite");
2976 		tQSL_Error = TQSL_NAME_EXISTS;
2977 		return 1;
2978 	}
2979 	XMLElement *sd = new XMLElement("StationData");
2980 	sd->setPretext("\n  ");
2981 	if (tqsl_location_to_xml(loc, *sd)) {
2982 		tqslTrace("tqsl_saveStationLocationCaptureName", "error in loc_to_xml %d", tQSL_Error);
2983 		return 1;
2984 	}
2985 	sd->setAttribute("name", loc->name);
2986 	sd->setText("\n  ");
2987 
2988 	// If 'exists', ep points to the existing station record
2989 	if (exists)
2990 		ellist.erase(ep);
2991 
2992 	sfile.addElement(sd);
2993 	sfile.setText("\n");
2994 	return tqsl_dump_station_data(sfile);
2995 }
2996 
2997 
2998 DLLEXPORT int CALLCONVENTION
tqsl_signQSORecord(tQSL_Cert cert,tQSL_Location locp,TQSL_QSO_RECORD * rec,unsigned char * sig,int * siglen)2999 tqsl_signQSORecord(tQSL_Cert cert, tQSL_Location locp, TQSL_QSO_RECORD *rec, unsigned char *sig, int *siglen) {
3000 	TQSL_LOCATION *loc;
3001 	if (!(loc = check_loc(locp, false))) {
3002 		tqslTrace("tqsl_signQSORecord", "loc error %d", tQSL_Error);
3003 		return 1;
3004 	}
3005 	if (make_sign_data(loc)) {
3006 		tqslTrace("tqsl_signQSORecord", "error %d making sign data", tQSL_Error);
3007 		return 1;
3008 	}
3009 	XMLElement specfield;
3010 	bool ok = tCONTACT_sign.getFirstElement(specfield);
3011 	string rec_sign_data = loc->signdata;
3012 	while (ok) {
3013 		string eln = specfield.getElementName();
3014 		const char *elname = eln.c_str();
3015 		const char *value = 0;
3016 		char buf[100];
3017 		if (!strcmp(elname, "CALL")) {
3018 			value = rec->callsign;
3019 		} else if (!strcmp(elname, "BAND")) {
3020 			value = rec->band;
3021 		} else if (!strcmp(elname, "BAND_RX")) {
3022 			value = rec->rxband;
3023 		} else if (!strcmp(elname, "MODE")) {
3024 			value = rec->mode;
3025 		} else if (!strcmp(elname, "FREQ")) {
3026 			value = rec->freq;
3027 		} else if (!strcmp(elname, "FREQ_RX")) {
3028 			value = rec->rxfreq;
3029 		} else if (!strcmp(elname, "PROP_MODE")) {
3030 			value = rec->propmode;
3031 		} else if (!strcmp(elname, "SAT_NAME")) {
3032 			value = rec->satname;
3033 		} else if (!strcmp(elname, "QSO_DATE")) {
3034 			if (tqsl_isDateValid(&(rec->date)))
3035 				value = tqsl_convertDateToText(&(rec->date), buf, sizeof buf);
3036 		} else if (!strcmp(elname, "QSO_TIME")) {
3037 			if (tqsl_isTimeValid(&(rec->time)))
3038 				value = tqsl_convertTimeToText(&(rec->time), buf, sizeof buf);
3039 		} else {
3040 			tQSL_Error = TQSL_CUSTOM_ERROR;
3041 			snprintf(tQSL_CustomError, sizeof tQSL_CustomError,
3042 				"Unknown field in signing specification: %s", elname);
3043 			tqslTrace("tqsl_signQSORecord", "field err %s", tQSL_CustomError);
3044 			return 1;
3045 		}
3046 		if (value == 0 || value[0] == 0) {
3047 			pair<string, bool> attr = specfield.getAttribute("required");
3048 			if (attr.second && strtol(attr.first.c_str(), NULL, 10)) {
3049 				string err = specfield.getElementName() + " field required by signature specification not found";
3050 				tQSL_Error = TQSL_CUSTOM_ERROR;
3051 				strncpy(tQSL_CustomError, err.c_str(), sizeof tQSL_CustomError);
3052 				tqslTrace("tqsl_signQSORecord", "val err %s", tQSL_CustomError);
3053 				return 1;
3054 			}
3055 		} else {
3056 			string v(value);
3057 			rec_sign_data += trim(v);
3058 		}
3059 		ok = tCONTACT_sign.getNextElement(specfield);
3060 	}
3061 	return tqsl_signDataBlock(cert, (const unsigned char *)rec_sign_data.c_str(), rec_sign_data.size(), sig, siglen);
3062 }
3063 
3064 DLLEXPORT const char* CALLCONVENTION
tqsl_getGABBItCERT(tQSL_Cert cert,int uid)3065 tqsl_getGABBItCERT(tQSL_Cert cert, int uid) {
3066 	static string s;
3067 
3068 	s = "";
3069 	char buf[3000];
3070 	if (tqsl_getCertificateEncoded(cert, buf, sizeof buf))
3071 		return 0;
3072 	char *cp = strstr(buf, "-----END CERTIFICATE-----");
3073 	if (cp)
3074 		*cp = 0;
3075 	if ((cp = strstr(buf, "\n")))
3076 		cp++;
3077 	else
3078 		cp = buf;
3079 	s = "<Rec_Type:5>tCERT\n";
3080 	char sbuf[10], lbuf[40];
3081 	snprintf(sbuf, sizeof sbuf, "%d", uid);
3082 	snprintf(lbuf, sizeof lbuf, "<CERT_UID:%d>%s\n", static_cast<int>(strlen(sbuf)), sbuf);
3083 	s += lbuf;
3084 	snprintf(lbuf, sizeof lbuf, "<CERTIFICATE:%d>", static_cast<int>(strlen(cp)));
3085 	s += lbuf;
3086 	s += cp;
3087 	s += "<eor>\n";
3088 	return s.c_str(); //KC2YWE 1/26 - dangerous but might work since s is static
3089 }
3090 
3091 DLLEXPORT const char* CALLCONVENTION
tqsl_getGABBItSTATION(tQSL_Location locp,int uid,int certuid)3092 tqsl_getGABBItSTATION(tQSL_Location locp, int uid, int certuid) {
3093 	TQSL_LOCATION *loc;
3094 	if (!(loc = check_loc(locp, false))) {
3095 		tqslTrace("tqsl_getGABBItSTATION", "loc error %d", tQSL_Error);
3096 		return 0;
3097 	}
3098 	unsigned char *buf = 0;
3099 	int bufsiz = 0;
3100 	loc->tSTATION = "<Rec_Type:8>tSTATION\n";
3101 	char sbuf[10], lbuf[40];
3102 	snprintf(sbuf, sizeof sbuf, "%d", uid);
3103 	snprintf(lbuf, sizeof lbuf, "<STATION_UID:%d>%s\n", static_cast<int>(strlen(sbuf)), sbuf);
3104 	loc->tSTATION += lbuf;
3105 	snprintf(sbuf, sizeof sbuf, "%d", certuid);
3106 	snprintf(lbuf, sizeof lbuf, "<CERT_UID:%d>%s\n", static_cast<int>(strlen(sbuf)), sbuf);
3107 	loc->tSTATION += lbuf;
3108 	int old_page = loc->page;
3109 	tqsl_setStationLocationCapturePage(loc, 1);
3110 	do {
3111 		TQSL_LOCATION_PAGE& p = loc->pagelist[loc->page-1];
3112 		for (int i = 0; i < static_cast<int>(p.fieldlist.size()); i++) {
3113 			TQSL_LOCATION_FIELD& f = p.fieldlist[i];
3114 			string s;
3115 			if (f.input_type == TQSL_LOCATION_FIELD_BADZONE)	// Don't output these to tSTATION
3116 				continue;
3117 
3118 			if (f.input_type == TQSL_LOCATION_FIELD_DDLIST || f.input_type == TQSL_LOCATION_FIELD_LIST) {
3119 				if (f.idx < 0 || f.idx >= static_cast<int>(f.items.size())) {
3120 					s = "";
3121 				} else {
3122 					s = f.items[f.idx].text;
3123 				}
3124 			} else if (f.data_type == TQSL_LOCATION_FIELD_INT) {
3125 				char buf[20];
3126 				snprintf(buf, sizeof buf, "%d", f.idata);
3127 				s = buf;
3128 			} else {
3129 				s = f.cdata;
3130 			}
3131 			if (s.size() == 0)
3132 				continue;
3133 			int wantsize = s.size() + f.gabbi_name.size() + 20;
3134 			if (buf == 0 || bufsiz < wantsize) {
3135 				if (buf != 0)
3136 					delete[] buf;
3137 				buf = new unsigned char[wantsize];
3138 				bufsiz = wantsize;
3139 			}
3140 			if (tqsl_adifMakeField(f.gabbi_name.c_str(), 0, (unsigned char *)s.c_str(), s.size(), buf, bufsiz)) {
3141 				delete[] buf;
3142 				return 0;
3143 			}
3144 			loc->tSTATION += (const char *)buf;
3145 			loc->tSTATION += "\n";
3146 		}
3147 		int rval;
3148 		if (tqsl_hasNextStationLocationCapture(loc, &rval) || !rval)
3149 			break;
3150 		tqsl_nextStationLocationCapture(loc);
3151 	} while (1);
3152 	tqsl_setStationLocationCapturePage(loc, old_page);
3153 	if (buf != 0)
3154 		delete[] buf;
3155 	loc->tSTATION += "<eor>\n";
3156 	return loc->tSTATION.c_str();
3157 }
3158 
3159 DLLEXPORT const char* CALLCONVENTION
tqsl_getGABBItCONTACTData(tQSL_Cert cert,tQSL_Location locp,TQSL_QSO_RECORD * qso,int stationuid,char * signdata,int sdlen)3160 tqsl_getGABBItCONTACTData(tQSL_Cert cert, tQSL_Location locp, TQSL_QSO_RECORD *qso, int stationuid, char* signdata, int sdlen) {
3161 	TQSL_LOCATION *loc;
3162 	if (!(loc = check_loc(locp, false))) {
3163 		tqslTrace("tqsl_getGABBItCONTACTData", "loc error %d", tQSL_Error);
3164 		return 0;
3165 	}
3166 	if (make_sign_data(loc)) {
3167 		tqslTrace("tqsl_getGABBItCONTACTData", "make_sign_data error %d", tQSL_Error);
3168 		return 0;
3169 	}
3170 	XMLElement specfield;
3171 	bool ok = tCONTACT_sign.getFirstElement(specfield);
3172 	string rec_sign_data = loc->signdata;
3173 	loc->qso_details = "";
3174 	while(ok) {
3175 		string en = specfield.getElementName();
3176 		const char *elname = en.c_str();
3177 		const char *value = 0;
3178 		char buf[100];
3179 		if (!strcmp(elname, "CALL")) {
3180 			value = qso->callsign;
3181 		} else if (!strcmp(elname, "BAND")) {
3182 			value = qso->band;
3183 		} else if (!strcmp(elname, "BAND_RX")) {
3184 			value = qso->rxband;
3185 		} else if (!strcmp(elname, "MODE")) {
3186 			value = qso->mode;
3187 		} else if (!strcmp(elname, "FREQ")) {
3188 			value = qso->freq;
3189 		} else if (!strcmp(elname, "FREQ_RX")) {
3190 			value = qso->rxfreq;
3191 		} else if (!strcmp(elname, "PROP_MODE")) {
3192 			value = qso->propmode;
3193 		} else if (!strcmp(elname, "SAT_NAME")) {
3194 			value = qso->satname;
3195 		} else if (!strcmp(elname, "QSO_DATE")) {
3196 			if (tqsl_isDateValid(&(qso->date)))
3197 				value = tqsl_convertDateToText(&(qso->date), buf, sizeof buf);
3198 		} else if (!strcmp(elname, "QSO_TIME")) {
3199 			if (tqsl_isTimeValid(&(qso->time)))
3200 				value = tqsl_convertTimeToText(&(qso->time), buf, sizeof buf);
3201 		} else {
3202 			tQSL_Error = TQSL_CUSTOM_ERROR;
3203 			snprintf(tQSL_CustomError, sizeof tQSL_CustomError,
3204 				"Unknown field in signing specification: %s", elname);
3205 			tqslTrace("tqsl_getGABBItCONTACTData", "field err %s", tQSL_CustomError);
3206 			return 0;
3207 		}
3208 		if (value == 0 || value[0] == 0) {
3209 			pair<string, bool> attr = specfield.getAttribute("required");
3210 			if (attr.second && strtol(attr.first.c_str(), NULL, 10)) {
3211 				string err = specfield.getElementName() + " field required by signature specification not found";
3212 				tQSL_Error = TQSL_CUSTOM_ERROR;
3213 				strncpy(tQSL_CustomError, err.c_str(), sizeof tQSL_CustomError);
3214 				tqslTrace("tqsl_getGABBItCONTACTData", "field err %s", tQSL_CustomError);
3215 				return 0;
3216 			}
3217 		} else {
3218 			string v(value);
3219 			rec_sign_data += trim(v);
3220 			loc->qso_details += trim(v);
3221 		}
3222 		ok = tCONTACT_sign.getNextElement(specfield);
3223 	}
3224 	unsigned char sig[129];
3225 	int siglen = sizeof sig;
3226 	rec_sign_data = string_toupper(rec_sign_data);
3227 	if (tqsl_signDataBlock(cert, (const unsigned char *)rec_sign_data.c_str(), rec_sign_data.size(), sig, &siglen))
3228 		return 0;
3229 	char b64[512];
3230 	if (tqsl_encodeBase64(sig, siglen, b64, sizeof b64))
3231 		return 0;
3232 	loc->tCONTACT = "<Rec_Type:8>tCONTACT\n";
3233 	char sbuf[10], lbuf[40];
3234 	snprintf(sbuf, sizeof sbuf, "%d", stationuid);
3235 	snprintf(lbuf, sizeof lbuf, "<STATION_UID:%d>%s\n", static_cast<int>(strlen(sbuf)), sbuf);
3236 	loc->tCONTACT += lbuf;
3237 	char buf[256];
3238 	tqsl_adifMakeField("CALL", 0, (const unsigned char *)qso->callsign, -1, (unsigned char *)buf, sizeof buf);
3239 	loc->tCONTACT += buf;
3240 	loc->tCONTACT += "\n";
3241 	tqsl_adifMakeField("BAND", 0, (const unsigned char *)qso->band, -1, (unsigned char *)buf, sizeof buf);
3242 	loc->tCONTACT += buf;
3243 	loc->tCONTACT += "\n";
3244 	tqsl_adifMakeField("MODE", 0, (const unsigned char *)qso->mode, -1, (unsigned char *)buf, sizeof buf);
3245 	loc->tCONTACT += buf;
3246 	loc->tCONTACT += "\n";
3247 	// Optional fields
3248 	if (qso->freq[0] != 0) {
3249 		tqsl_adifMakeField("FREQ", 0, (const unsigned char *)qso->freq, -1, (unsigned char *)buf, sizeof buf);
3250 		loc->tCONTACT += buf;
3251 		loc->tCONTACT += "\n";
3252 	}
3253 	if (qso->rxfreq[0] != 0) {
3254 		tqsl_adifMakeField("FREQ_RX", 0, (const unsigned char *)qso->rxfreq, -1, (unsigned char *)buf, sizeof buf);
3255 		loc->tCONTACT += buf;
3256 		loc->tCONTACT += "\n";
3257 	}
3258 	if (qso->propmode[0] != 0) {
3259 		tqsl_adifMakeField("PROP_MODE", 0, (const unsigned char *)qso->propmode, -1, (unsigned char *)buf, sizeof buf);
3260 		loc->tCONTACT += buf;
3261 		loc->tCONTACT += "\n";
3262 	}
3263 	if (qso->satname[0] != 0) {
3264 		tqsl_adifMakeField("SAT_NAME", 0, (const unsigned char *)qso->satname, -1, (unsigned char *)buf, sizeof buf);
3265 		loc->tCONTACT += buf;
3266 		loc->tCONTACT += "\n";
3267 	}
3268 	if (qso->rxband[0] != 0) {
3269 		tqsl_adifMakeField("BAND_RX", 0, (const unsigned char *)qso->rxband, -1, (unsigned char *)buf, sizeof buf);
3270 		loc->tCONTACT += buf;
3271 		loc->tCONTACT += "\n";
3272 	}
3273 	// Date and Time
3274 	char date_buf[40] = "";
3275 	tqsl_convertDateToText(&(qso->date), date_buf, sizeof date_buf);
3276 	tqsl_adifMakeField("QSO_DATE", 0, (const unsigned char *)date_buf, -1, (unsigned char *)buf, sizeof buf);
3277 	loc->tCONTACT += buf;
3278 	loc->tCONTACT += "\n";
3279 	date_buf[0] = 0;
3280 	tqsl_convertTimeToText(&(qso->time), date_buf, sizeof date_buf);
3281 	tqsl_adifMakeField("QSO_TIME", 0, (const unsigned char *)date_buf, -1, (unsigned char *)buf, sizeof buf);
3282 	loc->tCONTACT += buf;
3283 	loc->tCONTACT += "\n";
3284 	tqsl_adifMakeField(loc->sigspec.c_str(), '6', (const unsigned char *)b64, -1, (unsigned char *)buf, sizeof buf);
3285 	loc->tCONTACT += buf;
3286 	// Signature
3287 	tqsl_adifMakeField("SIGNDATA", 0, (const unsigned char *)rec_sign_data.c_str(), -1, (unsigned char *)buf, sizeof buf);
3288 	loc->tCONTACT += buf;
3289 	loc->tCONTACT += "\n";
3290 	loc->tCONTACT += "<eor>\n";
3291 	if (signdata)
3292 		strncpy(signdata, rec_sign_data.c_str(), sdlen);
3293 	return loc->tCONTACT.c_str();
3294 }
3295 
3296 DLLEXPORT const char* CALLCONVENTION
tqsl_getGABBItCONTACT(tQSL_Cert cert,tQSL_Location locp,TQSL_QSO_RECORD * qso,int stationuid)3297 tqsl_getGABBItCONTACT(tQSL_Cert cert, tQSL_Location locp, TQSL_QSO_RECORD *qso, int stationuid) {
3298 	return tqsl_getGABBItCONTACTData(cert, locp, qso, stationuid, NULL, 0);
3299 }
3300 
3301 
3302 DLLEXPORT int CALLCONVENTION
tqsl_getLocationCallSign(tQSL_Location locp,char * buf,int bufsiz)3303 tqsl_getLocationCallSign(tQSL_Location locp, char *buf, int bufsiz) {
3304 	TQSL_LOCATION *loc;
3305 	if (!(loc = check_loc(locp, false))) {
3306 		tqslTrace("tqsl_getLocationCallSign", "loc error %d", tQSL_Error);
3307 		return 1;
3308 	}
3309 	if (buf == NULL || bufsiz <= 0) {
3310 		tqslTrace("tqsl_getLocationCallSign", "arg error buf=0x%lx, bufsiz=%d", buf, bufsiz);
3311 		tQSL_Error = TQSL_ARGUMENT_ERROR;
3312 		return 1;
3313 	}
3314 	TQSL_LOCATION_PAGE& p = loc->pagelist[0];
3315 	for (int i = 0; i < static_cast<int>(p.fieldlist.size()); i++) {
3316 		TQSL_LOCATION_FIELD f = p.fieldlist[i];
3317 		if (f.gabbi_name == "CALL") {
3318 			strncpy(buf, f.cdata.c_str(), bufsiz);
3319 			buf[bufsiz-1] = 0;
3320 			if (static_cast<int>(f.cdata.size()) >= bufsiz) {
3321 				tqslTrace("tqsl_getLocationCallSign", "buf error req=%d avail=%d", static_cast<int>(f.cdata.size()), bufsiz);
3322 				tQSL_Error = TQSL_BUFFER_ERROR;
3323 				return 1;
3324 			}
3325 			return 0;
3326 		}
3327 	}
3328 	tQSL_Error = TQSL_CALL_NOT_FOUND;
3329 	return 1;
3330 }
3331 
3332 DLLEXPORT int CALLCONVENTION
tqsl_setLocationCallSign(tQSL_Location locp,const char * buf)3333 tqsl_setLocationCallSign(tQSL_Location locp, const char *buf) {
3334 	TQSL_LOCATION *loc;
3335 	if (!(loc = check_loc(locp, false))) {
3336 		tqslTrace("tqsl_setLocationCallSign", "loc error %d", tQSL_Error);
3337 		return 1;
3338 	}
3339 	if (buf == NULL) {
3340 		tqslTrace("tqsl_setLocationCallSign", "arg error buf=null");
3341 		tQSL_Error = TQSL_ARGUMENT_ERROR;
3342 		return 1;
3343 	}
3344 	TQSL_LOCATION_PAGE& p = loc->pagelist[0];
3345 	for (int i = 0; i < static_cast<int>(p.fieldlist.size()); i++) {
3346 		TQSL_LOCATION_FIELD f = p.fieldlist[i];
3347 		if (f.gabbi_name == "CALL") {
3348 			for (int j = 0; j < static_cast<int>(f.items.size()); j++) {
3349 				if (f.items[j].text == buf) {
3350 					loc->pagelist[0].fieldlist[i].idx = j;
3351 					loc->pagelist[0].fieldlist[i].cdata = buf;
3352 					break;
3353 				}
3354 			}
3355 			return 0;
3356 		}
3357 	}
3358 	tQSL_Error = TQSL_CALL_NOT_FOUND;
3359 	return 1;
3360 }
3361 
3362 DLLEXPORT int CALLCONVENTION
tqsl_getLocationDXCCEntity(tQSL_Location locp,int * dxcc)3363 tqsl_getLocationDXCCEntity(tQSL_Location locp, int *dxcc) {
3364 	TQSL_LOCATION *loc;
3365 	if (!(loc = check_loc(locp, false))) {
3366 		tqslTrace("tqsl_getLocationDXCCEntity", "loc error %d", tQSL_Error);
3367 		return 1;
3368 	}
3369 	if (dxcc == NULL) {
3370 		tqslTrace("tqsl_getLocationDXCCEntity", "arg err dxcc=null");
3371 		tQSL_Error = TQSL_ARGUMENT_ERROR;
3372 		return 1;
3373 	}
3374 	TQSL_LOCATION_PAGE& p = loc->pagelist[0];
3375 	for (int i = 0; i < static_cast<int>(p.fieldlist.size()); i++) {
3376 		TQSL_LOCATION_FIELD f = p.fieldlist[i];
3377 		if (f.gabbi_name == "DXCC") {
3378 			if (f.idx < 0 || f.idx >= static_cast<int>(f.items.size()))
3379 				break;	// No matching DXCC entity
3380 			*dxcc = f.items[f.idx].ivalue;
3381 			return 0;
3382 		}
3383 	}
3384 	tqslTrace("tqsl_getLocationDXCCEntity", "name not found");
3385 	tQSL_Error = TQSL_NAME_NOT_FOUND;
3386 	return 1;
3387 }
3388 
3389 DLLEXPORT int CALLCONVENTION
tqsl_getNumProviders(int * n)3390 tqsl_getNumProviders(int *n) {
3391 	if (n == NULL) {
3392 		tqslTrace("tqsl_getNumProviders", "arg error n=null");
3393 		tQSL_Error = TQSL_ARGUMENT_ERROR;
3394 		return 1;
3395 	}
3396 	vector<TQSL_PROVIDER> plist;
3397 	if (tqsl_load_provider_list(plist)) {
3398 		tqslTrace("tqsl_getNumProviders", "error loading providers %d", tQSL_Error);
3399 		return 1;
3400 	}
3401 	if (plist.size() == 0) {
3402 		tqslTrace("tqsl_getNumProviders", "prov not found");
3403 		tQSL_Error = TQSL_PROVIDER_NOT_FOUND;
3404 		return 1;
3405 	}
3406 	*n = plist.size();
3407 	return 0;
3408 }
3409 
3410 DLLEXPORT int CALLCONVENTION
tqsl_getProvider(int idx,TQSL_PROVIDER * provider)3411 tqsl_getProvider(int idx, TQSL_PROVIDER *provider) {
3412 	if (provider == NULL || idx < 0) {
3413 		tqslTrace("tqsl_getProvider", "arg error provider=0x%lx, idx=%d", provider, idx);
3414 		tQSL_Error = TQSL_ARGUMENT_ERROR;
3415 		return 1;
3416 	}
3417 	vector<TQSL_PROVIDER> plist;
3418 	if (tqsl_load_provider_list(plist)) {
3419 		tqslTrace("tqsl_getProvider", "err %d loading list", tQSL_Error);
3420 		return 1;
3421 	}
3422 	if (idx >= static_cast<int>(plist.size())) {
3423 		tqslTrace("tqsl_getProvider", "prov not found");
3424 		tQSL_Error = TQSL_PROVIDER_NOT_FOUND;
3425 		return 1;
3426 	}
3427 	*provider = plist[idx];
3428 	return 0;
3429 }
3430 
3431 DLLEXPORT int CALLCONVENTION
tqsl_importTQSLFile(const char * file,int (* cb)(int type,const char *,void *),void * userdata)3432 tqsl_importTQSLFile(const char *file, int(*cb)(int type, const char *, void *), void *userdata) {
3433 	bool foundcerts = false;
3434 	tQSL_ImportCall[0] = '\0';
3435 	tQSL_ImportSerial = 0;
3436 	int rval = 0;
3437 
3438 	if (file == NULL) {
3439 		tqslTrace("tqsl_importTQSLFile", "file=NULL");
3440 		tQSL_Error = TQSL_ARGUMENT_ERROR;
3441 		return 1;
3442 	}
3443 	XMLElement topel;
3444 	int status = topel.parseFile(file);
3445 	if (status) {
3446 		strncpy(tQSL_ErrorFile, file, sizeof tQSL_ErrorFile);
3447 		if (status == XML_PARSE_SYSTEM_ERROR) {
3448 			tQSL_Error = TQSL_FILE_SYSTEM_ERROR;
3449 			tQSL_Errno = errno;
3450 			tqslTrace("tqsl_importTQSLFile", "system error file=%s err=%s", file, strerror(tQSL_Errno));
3451 		} else {
3452 			tQSL_Error = TQSL_FILE_SYNTAX_ERROR;
3453 			tqslTrace("tqsl_importTQSLFile", "file %s syntax error", file);
3454 		}
3455 		return 1;
3456 	}
3457 	XMLElement tqsldata;
3458 	if (!topel.getFirstElement("tqsldata", tqsldata)) {
3459 		strncpy(tQSL_ErrorFile, file, sizeof tQSL_ErrorFile);
3460 		tQSL_Error = TQSL_FILE_SYNTAX_ERROR;
3461 		return 1;
3462 	}
3463 	XMLElement section;
3464 	bool stat = tqsldata.getFirstElement("tqslcerts", section);
3465 	if (stat) {
3466 		XMLElement cert;
3467 		bool cstat = section.getFirstElement("rootcert", cert);
3468 		while (cstat) {
3469 			foundcerts = true;
3470 			if (tqsl_import_cert(cert.getText().c_str(), ROOTCERT, cb, userdata)) {
3471 				tqslTrace("tqsl_importTQSLFile", "duplicate root cert");
3472 			}
3473 			cstat = section.getNextElement(cert);
3474 		}
3475 		cstat = section.getFirstElement("cacert", cert);
3476 		while (cstat) {
3477 			foundcerts = true;
3478 			if (tqsl_import_cert(cert.getText().c_str(), CACERT, cb, userdata)) {
3479 				tqslTrace("tqsl_importTQSLFile", "duplicate ca cert");
3480 			}
3481 			cstat = section.getNextElement(cert);
3482 		}
3483 		cstat = section.getFirstElement("usercert", cert);
3484 		while (cstat) {
3485 			foundcerts = true;
3486 			if (tqsl_import_cert(cert.getText().c_str(), USERCERT, cb, userdata)) {
3487 				tqslTrace("tqsl_importTQSLFile", "error importing user cert");
3488 				tQSL_Error = TQSL_CERT_ERROR;
3489 				rval = 1;
3490 			}
3491 			cstat = section.getNextElement(cert);
3492 		}
3493 	}
3494 	// If any of the user certificates failed import, return the error status.
3495 	if (rval) {
3496 		return rval;
3497 	}
3498 
3499 	stat = tqsldata.getFirstElement("tqslconfig", section);
3500 	if (stat) {
3501 		// Check to make sure we aren't overwriting newer version
3502 		int major = strtol(section.getAttribute("majorversion").first.c_str(), NULL, 10);
3503 		int minor = strtol(section.getAttribute("minorversion").first.c_str(), NULL, 10);
3504 		int curmajor, curminor;
3505 		if (tqsl_getConfigVersion(&curmajor, &curminor)) {
3506 			tqslTrace("tqsl_importTQSLFile", "Get config ver error %d", tQSL_Error);
3507 			return 1;
3508 		}
3509 		if (major < curmajor) {
3510 			if (foundcerts) {
3511 				tqslTrace("tqsl_importTQSLFile", "Suppressing update from V%d.%d to V%d.%d", curmajor, curminor, major, minor);
3512 				return rval;
3513 			}
3514 			tQSL_Error = TQSL_CUSTOM_ERROR;
3515 			snprintf(tQSL_CustomError, sizeof tQSL_CustomError,
3516 				"This configuration file (V%d.%d) is older than the currently installed one (V%d.%d). It will not be installed.",
3517 						major, minor, curmajor, curminor);
3518 			tqslTrace("tqsl_importTQSLFile", "Config update error: %s", tQSL_CustomError);
3519 			return 1;
3520 		}
3521 		if (major == curmajor) {
3522 			if (minor == curminor) {		// Same rev as already installed
3523 				tqslTrace("tqsl_importTQSLFile", "Suppressing update from V%d.%d to V%d.%d", curmajor, curminor, major, minor);
3524 				return rval;
3525 			}
3526 			if (minor < curminor) {
3527 				if (foundcerts) {
3528 					tqslTrace("tqsl_importTQSLFile", "Suppressing update from V%d.%d to V%d.%d", curmajor, curminor, major, minor);
3529 					return rval;
3530 				}
3531 				tQSL_Error = TQSL_CUSTOM_ERROR;
3532 				snprintf(tQSL_CustomError, sizeof tQSL_CustomError,
3533 					"This configuration file (V%d.%d) is older than the currently installed one (V%d.%d). It will not be installed.",
3534 							major, minor, curmajor, curminor);
3535 				tqslTrace("tqsl_importTQSLFile", "Config update error: %s", tQSL_CustomError);
3536 				return rval;
3537 		 	}
3538 		}
3539 		// Save the configuration file
3540 		ofstream out;
3541 #ifdef _WIN32
3542 		string fn = string(tQSL_BaseDir) + "\\config.xml";
3543 #else
3544 		string fn = string(tQSL_BaseDir) + "/config.xml";
3545 #endif
3546 		out.exceptions(ios::failbit | ios::eofbit | ios::badbit);
3547 		try {
3548 #ifdef _WIN32
3549 			wchar_t *wfn = utf8_to_wchar(fn.c_str());
3550 			out.open(wfn);
3551 			free_wchar(wfn);
3552 #else
3553 			out.open(fn.c_str());
3554 #endif
3555 			out << section << endl;
3556 			out.close();
3557 		}
3558 		catch(exception& x) {
3559 			tQSL_Error = TQSL_CUSTOM_ERROR;
3560 			snprintf(tQSL_CustomError, sizeof tQSL_CustomError,
3561 				"Error writing new configuration file (%s): %s/%s",
3562 				fn.c_str(), x.what(), strerror(errno));
3563 			tqslTrace("tqsl_importTQSLFile", "I/O error: %s", tQSL_CustomError);
3564 			if (cb)
3565 				return (*cb)(TQSL_CERT_CB_RESULT | TQSL_CERT_CB_ERROR | TQSL_CERT_CB_CONFIG,
3566 					fn.c_str(), userdata);
3567 			if (tQSL_Error == 0) {
3568 				tQSL_Error = TQSL_CERT_ERROR;
3569 			}
3570 			return 1;
3571 		}
3572 		// Clear stored config data to force re-reading new config
3573 		tqsl_xml_config.clear();
3574 		DXCCMap.clear();
3575 		DXCCList.clear();
3576 		BandList.clear();
3577 		ModeList.clear();
3578 		tqsl_page_map.clear();
3579 		tqsl_field_map.clear();
3580 		tqsl_adif_map.clear();
3581 		tqsl_cabrillo_map.clear();
3582 		string version = "Configuration V" + section.getAttribute("majorversion").first + "."
3583 			+ section.getAttribute("minorversion").first + "\n" + fn;
3584 		if (cb) {
3585 			int cbret = (*cb)(TQSL_CERT_CB_RESULT | TQSL_CERT_CB_LOADED | TQSL_CERT_CB_CONFIG,
3586 				version.c_str(), userdata);
3587 			if (cbret || rval) {
3588 				if (tQSL_Error == 0) {
3589 					tQSL_Error = TQSL_CERT_ERROR;
3590 				}
3591 				return 1;
3592 			}
3593 		}
3594 	}
3595 	if (rval && tQSL_Error == 0) {
3596 		tQSL_Error = TQSL_CERT_ERROR;
3597 	}
3598 	return rval;
3599 }
3600 
3601 /*
3602  * Get the first user certificate from a .tq6 file
3603 */
3604 DLLEXPORT int CALLCONVENTION
tqsl_getSerialFromTQSLFile(const char * file,long * serial)3605 tqsl_getSerialFromTQSLFile(const char *file, long *serial) {
3606 	XMLElement topel;
3607 	if (file == NULL || serial == NULL) {
3608 		tqslTrace("tqsl_getSerialFromTQSLFile", "Arg error file=0x%lx, serial=0x%lx", file, serial);
3609 		tQSL_Error = TQSL_ARGUMENT_ERROR;
3610 		return 1;
3611 	}
3612 	int status =  topel.parseFile(file);
3613 	if (status) {
3614 		strncpy(tQSL_ErrorFile, file, sizeof tQSL_ErrorFile);
3615 		if (status == XML_PARSE_SYSTEM_ERROR) {
3616 			tQSL_Error = TQSL_FILE_SYSTEM_ERROR;
3617 			tQSL_Errno = errno;
3618 			tqslTrace("tqsl_getSerialFromTQSLFile", "parse error %d, error %s", tQSL_Error, strerror(tQSL_Errno));
3619 		} else {
3620 			tQSL_Error = TQSL_FILE_SYNTAX_ERROR;
3621 			tqslTrace("tqsl_getSerialFromTQSLFile", "parse syntax error %d", tQSL_Error);
3622 		}
3623 		return 1;
3624 	}
3625 	XMLElement tqsldata;
3626 	if (!topel.getFirstElement("tqsldata", tqsldata)) {
3627 		strncpy(tQSL_ErrorFile, file, sizeof tQSL_ErrorFile);
3628 		tqslTrace("tqsl_getSerialFromTQSLFile", "parse syntax error %d", tQSL_Error);
3629 		tQSL_Error = TQSL_FILE_SYNTAX_ERROR;
3630 		return 1;
3631 	}
3632 	XMLElement section;
3633 	bool stat = tqsldata.getFirstElement("tqslcerts", section);
3634 	if (stat) {
3635 		XMLElement cert;
3636 		bool cstat = section.getFirstElement("usercert", cert);
3637 		if (cstat) {
3638 			if (tqsl_get_pem_serial(cert.getText().c_str(), serial)) {
3639 				strncpy(tQSL_ErrorFile, file, sizeof tQSL_ErrorFile);
3640 				tqslTrace("tqsl_getSerialFromTQSLFile", "parse syntax error %d", tQSL_Error);
3641 				tQSL_Error = TQSL_FILE_SYNTAX_ERROR;
3642 				return 1;
3643 			}
3644 			return 0;
3645 		}
3646 	}
3647 	tqslTrace("tqsl_getSerialFromTQSLFile", "no usercert in file %s", file);
3648 	return 1;
3649 }
3650 
3651 DLLEXPORT int CALLCONVENTION
tqsl_getDeletedStationLocations(char *** locp,int * nloc)3652 tqsl_getDeletedStationLocations(char ***locp, int *nloc) {
3653 	if (locp == NULL) {
3654 		tqslTrace("tqsl_getDeletedStationLocations", "arg error locp=NULL");
3655 		tQSL_Error = TQSL_ARGUMENT_ERROR;
3656 		return 1;
3657 	}
3658 	if (nloc == NULL) {
3659 		tqslTrace("tqsl_getDeletedStationLocations", "arg error nloc=NULL");
3660 		tQSL_Error = TQSL_ARGUMENT_ERROR;
3661 		return 1;
3662 	}
3663 	*locp = NULL;
3664 	*nloc = 0;
3665 
3666 	vector<string> namelist;
3667 
3668 	XMLElement top_el;
3669 	if (tqsl_load_station_data(top_el, true)) {
3670 		tqslTrace("tqsl_getDeletedStationLocations", "error %d loading station data", tQSL_Error);
3671 		return 1;
3672 	}
3673 	XMLElement sfile;
3674 	if (top_el.getFirstElement(sfile)) {
3675 		XMLElement sd;
3676 		bool ok = sfile.getFirstElement("StationData", sd);
3677 		while (ok && sd.getElementName() == "StationData") {
3678 			pair<string, bool> name = sd.getAttribute("name");
3679 			if (name.second) {
3680 				namelist.push_back(name.first);
3681 			}
3682 			ok = sfile.getNextElement(sd);
3683 		}
3684 	}
3685 	*nloc = namelist.size();
3686 	if (*nloc == 0) {
3687 		*locp = NULL;
3688 		return 0;
3689 	}
3690 	*locp = reinterpret_cast<char **>(calloc(*nloc, sizeof(**locp)));
3691 	vector<string>::iterator it;
3692 	char **p = *locp;
3693 	for (it = namelist.begin(); it != namelist.end(); it++) {
3694 		*p++ = strdup((*it).c_str());
3695 	}
3696 	return 0;
3697 }
3698 
3699 DLLEXPORT void CALLCONVENTION
tqsl_freeDeletedLocationList(char ** list,int nloc)3700 tqsl_freeDeletedLocationList(char** list, int nloc) {
3701 	if (!list) return;
3702 	for (int i = 0; i < nloc; i++)
3703 		if (list[i]) free(list[i]);
3704 	if (list) free(list);
3705 }
3706 
3707 DLLEXPORT int CALLCONVENTION
tqsl_getLocationQSODetails(tQSL_Location locp,char * buf,int buflen)3708 tqsl_getLocationQSODetails(tQSL_Location locp, char *buf, int buflen) {
3709 	TQSL_LOCATION *loc;
3710 	if (!(loc = check_loc(locp, false))) {
3711 		tqslTrace("tqsl_getLocationDXCCEntity", "loc error %d", tQSL_Error);
3712 		return 1;
3713 	}
3714 	if (buf == NULL) {
3715 		tqslTrace("tqsl_getLocationQSODetails", "Argument error, buf = 0x%lx", buf);
3716 		tQSL_Error = TQSL_ARGUMENT_ERROR;
3717 		return 1;
3718 	}
3719 	strncpy(buf, loc->qso_details.c_str(), buflen);
3720 	return 0;
3721 }
3722 
3723 DLLEXPORT int CALLCONVENTION
tqsl_getLocationStationDetails(tQSL_Location locp,char * buf,int buflen)3724 tqsl_getLocationStationDetails(tQSL_Location locp, char *buf, int buflen) {
3725 	TQSL_LOCATION *loc;
3726 	if (!(loc = check_loc(locp, false))) {
3727 		tqslTrace("tqsl_getLocationStationDetails", "loc error %d", tQSL_Error);
3728 		return 1;
3729 	}
3730 	if (buf == NULL) {
3731 		tqslTrace("tqsl_getLocationStationDetails", "Argument error, buf = 0x%lx", buf);
3732 		tQSL_Error = TQSL_ARGUMENT_ERROR;
3733 		return 1;
3734 	}
3735 	strncpy(buf, loc->loc_details.c_str(), buflen);
3736 	return 0;
3737 }
3738