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 <rim(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